{"version":3,"file":"static/js/main.0abe397c.js","mappings":"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BAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,CAAC9Y,MAAM,MAAI,MAClB,gBAAC8Y,GAAM,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","sources":["webGL/Canvas2DtoWebGL.js","webGL/gl-matrix-min.js","webGL/litegl.js","webGL/litegraph.js","webGL/litescene.js","webpack://qci-react-stack-web-front-end/./src/webGL/Canvas2DtoWebGL.js?95c2","webpack://qci-react-stack-web-front-end/./src/webGL/gl-matrix-min.js?867b","webpack://qci-react-stack-web-front-end/./src/webGL/litegl.js?fbf8","webpack://qci-react-stack-web-front-end/./src/webGL/litegraph.js?bef3","webpack://qci-react-stack-web-front-end/./src/webGL/litescene.js?e37b","utils/Globals.ts","utils/HistoryUtil.ts","consts/Routes.ts","redux/actions/VersionActions.ts","redux/getStore.ts","api/ApiResultHandler.ts","utils/Guid.ts","utils/FileDownload.ts","api/BaseApi.ts","api/EnterpriseApiService.ts","models/EnterpriseDTO.ts","models/SiteDTO.ts","consts/Role.ts","api/AccountApiService.ts","consts/PermissionNames.ts","models/UserSecurity.ts","redux/actions/UserSessionActions.ts","redux/reducers/UserSessionReducer.ts","redux/reducers/VersionReducer.ts","redux/reducers/RootReducer.ts","redux/Store.ts","utils/CurrentUser.ts","utils/RoleUtil.ts","api/BinApiService.ts","consts/ErrorPriority.ts","consts/BoardGroup.ts","consts/HeaterMode.ts","consts/FormConstants.ts","pages/shared/downloadLogsByRange.ts","utils/deriveAutomationType.ts","consts/DryingForecast.ts","app/theme.ts","pages/dashboard/BinStatusPage/FilingForecast.tsx","pages/admin/RestartModuleButton.tsx","pages/dashboard/BinStatusPage/UnloadForm.tsx","pages/features/usePermissions.tsx","pages/dashboard/BinStatusPage/LoadTables.tsx","pages/dashboard/BinStatusPage/FanCard.tsx","consts/FanOffReason.ts","queries/BinDTOContext.tsx","consts/OperatingMode.ts","pages/features/shared.tsx","consts/ManualFanMode.ts","pages/features/CustomSpin.tsx","pages/features/WeatherMonitorForm.tsx","consts/RhSensorEnum.ts","consts/TemperatureSensorEnum.ts","pages/shared/binDTOUtils.ts","consts/HardwareYear.ts","pages/features/WeatherMonitorAntdOnly.tsx","utils/formatting.ts","pages/dashboard/ManualFanSettingsModal.tsx","queries/BinInfoContext.tsx","consts/DeviceConnectionState.ts","pages/dashboard/BinOfflineIndicator.tsx","api/ShivversService.ts","api/AdminApiService.ts","api/UserApiService.ts","pages/shared/PhoneInput.tsx","api/RoleApiService.ts","pages/shared/StatePicker.tsx","pages/users/PremierUserList.tsx","pages/users/UserList.tsx","pages/binOverview/PremierSpreadsheet.tsx","pages/binOverview/BinCommander.tsx","pages/dashboard/Camera/LiveStreaming.tsx","pages/dashboard/Camera/CameraImage.tsx","consts/CompressorOffReason.ts","pages/dashboard/BinStatusPage/CompressorControls.tsx","consts/GrainType.ts","queries/useUploadBinStateToAzure.ts","pages/dashboard/BinStatusPage/ForceNewBinStateUploadContext.tsx","pages/dashboard/BinStatusPage/DryLayerAdjust.tsx","pages/dashboard/BinStatusPage/ModeHelpTexts.tsx","pages/shared/UploadFile.tsx","pages/shared/RoutineList.tsx","consts/RoutineType.ts","pages/dashboard/BinStatusPage/NewValveDisplay.tsx","pages/dashboard/BinStatusPage/ManualRoutineControl.tsx","pages/dashboard/charts/shared.ts","consts/AutomationType.ts","pages/dashboard/charts/modehistoryAndFanChangeHistory.ts","pages/dashboard/charts/Co2Chart.tsx","pages/dashboard/charts/plugins/annotationOverlapFixPlugin.ts","pages/dashboard/charts/AmbientPlenumChart.tsx","pages/dashboard/charts/LayerMCHistoryChart.tsx","pages/dashboard/charts/LayerTemperatureHistoryChart.tsx","pages/dashboard/charts/PlenumPressureChart.tsx","pages/dashboard/charts/TargetLayerChart.tsx","pages/dashboard/charts/TemperatureCableChart.tsx","pages/dashboard/charts/MoistureRemovalRHChart.tsx","pages/dashboard/charts/MoistureRemovalAHChart.tsx","pages/dashboard/charts/RangeFinderChart.tsx","pages/dashboard/charts/DryingDashboard.tsx","pages/dashboard/charts/FanAndHeaterChart.tsx","pages/dashboard/charts/csv/core.ts","pages/dashboard/charts/csv/CsvButton.tsx","pages/dashboard/charts/FillLevelChartTab.tsx","pages/dashboard/charts/csv/useGrainTypeCsv.ts","pages/dashboard/charts/IncomingAirConditionsChartTab.tsx","pages/dashboard/charts/MoistureCableChartTab.tsx","pages/dashboard/charts/TemperatureCableChartTab.tsx","pages/dashboard/charts/Co2ChartTab.tsx","pages/dashboard/charts/CompressorChart.tsx","pages/dashboard/charts/CompressorChartTab.tsx","pages/dashboard/BinStatusPage/ChartsTab.tsx","pages/dashboard/BinStatusPage/HotspotIndicator.tsx","pages/dashboard/BinStatusPage/BatchStartTag.tsx","pages/dashboard/BinStatusPage/features/IndividualSensorTable/dataTransformation.ts","pages/dashboard/BinStatusPage/features/IndividualSensorTable/TemperatureCableTable.tsx","pages/dashboard/BinStatusPage/features/shared.ts","pages/dashboard/BinStatusPage/features/IndividualSensorTable/shared.ts","pages/dashboard/BinStatusPage/features/IndividualSensorTable/MoistureCableCard.tsx","pages/dashboard/BinStatusPage/BinStatsPage.tsx","queries/useBinStateFromAzure.ts","consts/HeaterOffReason.ts","pages/dashboard/BinStatusPage/HeaterControls.tsx","pages/dashboard/BinStatusPage/ResetFanRuntime.tsx","pages/dashboard/BinStatusPage/BinStatusCard.tsx","pages/dashboard/BinStatusPage/BinSettingsModal.tsx","pages/dashboard/BinStatusPage/SensorActuatorControl.tsx","pages/dashboard/BinStatusPage/DeviceLogsRangeForm.tsx","pages/dashboard/BinStatusPage/LogViewer.tsx","pages/dashboard/BinStatusPage/AdminControlsCard.tsx","pages/dashboard/BinStatusPage/BinVisualThree.tsx","pages/dashboard/HomeScreen/Canvas.tsx","pages/features/Premier/shared.ts","pages/features/Premier/Overview.tsx","pages/features/Premier/ShivversTag.tsx","pages/features/Premier/DashboardCardShivversPremier.tsx","pages/features/PremierPlus/shared.ts","queries/useGetPremierPlusBinDto.ts","components/CustomTag.tsx","components/Premier/PremierPlus/PremierPlusOverview.tsx","components/BinDetails.tsx","queries/useBinStateFromDevice.ts","queries/useGetBinByIDQuery.ts","pages/features/PremierPlus/FanControl.tsx","pages/features/PremierPlus/MoveGrainControl.tsx","pages/features/PremierPlus/ControlPanel.tsx","pages/features/PremierPlus/PremierPlusDashboard.tsx","pages/dashboard/BinStatusPage/BinStatsPageParent.tsx","pages/dashboard/HomeScreen/GrowerSelection.tsx","components/GoogleMaps/types.ts","components/GoogleMaps/UndoRedo.ts","components/GoogleMaps/UndoAllControl.tsx","components/GoogleMaps/MapControl.tsx","components/GoogleMaps/useDrawingManager.tsx","components/GoogleMaps/MapHandler.tsx","components/GoogleMaps/PlaceAutoComplete.tsx","components/GoogleMaps/GoogleMapWithPinDrop.tsx","queries/useGetAllGrowersQuery.ts","components/RegisterProductModal.tsx","pages/dashboard/HomeScreen/Home.tsx","utils/RouteErrors.tsx","pages/dashboard/HomeScreen/Dashboard.tsx","models/GrowerDTO.ts","pages/users/ExternalUserEdit.tsx","pages/admin/GrowerAdminstration.tsx","pages/admin/TextUpdateV2.tsx","utils/enumUtils.ts","pages/admin/DeviceProfileTable.tsx","consts/SensorType.ts","consts/IotModel.ts","queries/useGetAllPCBsQuery.ts","queries/useGetAllAzureIoTEdgeDevicesQuery.ts","queries/useAllBinsQuery.ts","pages/admin/UpdateDeviceV3.tsx","queries/useGetBinAssemblySubmittalQuery.ts","pages/admin/AddBoardLookup.tsx","pages/admin/AddSoftwareLookup.tsx","queries/useGetAllSoftwareLookupsQuery.ts","pages/admin/CreateUpdateBinV2.tsx","queries/useGetBinsQuery.ts","pages/admin/IoTDevices.tsx","pages/admin/AdminTabs.tsx","pages/shared/GlobalBreadcrumb.tsx","pages/admin/Admin.tsx","models/LoginDTO.ts","pages/login/Login.tsx","pages/UnexpectedError.tsx","layouts/PublicLayout.tsx","pages/shared/FormattedPhone.tsx","pages/shared/FormattedAddress.tsx","pages/users/UserDetail.tsx","pages/users/Users.tsx","pages/login/RequestResetPassword.tsx","pages/login/ResetPassword.tsx","sideNav/SideNav.tsx","pages/users/UserAlertSettings.tsx","pages/dashboard/BinStatusPage/AlertsHistoryTable.tsx","layouts/FullscreenLayout.tsx","pages/shared/RouteWithLayout.tsx","pages/Unauthorized.tsx","pages/shared/WithAuthorization.tsx","models/BinInfoDTO.ts","pages/dashboard/SiteDetailsScreen/MoreSiteDetail.tsx","pages/dashboard/BinStatusPage/BinTestPage.tsx","pages/ChangePasswordForm.tsx","pages/Profile.tsx","pages/settings/SettingsTabs.tsx","pages/settings/settings.tsx","pages/NotFoundComponent.tsx","pages/features/Premier/useMachineStatusAnnotations.ts","pages/features/Premier/MoistureDisplay.tsx","pages/features/Premier/TapeStrip.tsx","pages/features/Premier/SettingsTable.tsx","pages/features/Premier/DryerSettingsText.tsx","pages/features/Premier/ChartStatusLegend.tsx","pages/features/Premier/SettingsEdit.tsx","pages/features/Premier/PremierStatsPage.tsx","pages/dashboard/BinStatusPage/BinStatsShivversPageParent.tsx","app/App.tsx","registerServiceWorker.ts","index.tsx"],"sourcesContent":["module.exports = \"//replaces the Canvas2D functions by WebGL functions, the behaviour is not 100% the same but it kind of works in many cases\\r\\n//not all functions have been implemented\\r\\n\\r\\nif(typeof(GL) == \\\"undefined\\\")\\r\\n\\tthrow(\\\"litegl.js must be included to use enableWebGLCanvas\\\");\\r\\n\\r\\nfunction enableWebGLCanvas( canvas, options )\\r\\n{\\r\\n\\tvar gl;\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\t// Detect if canvas is WebGL enabled and get context if possible\\r\\n\\tif(!canvas.is_webgl)\\r\\n\\t{\\r\\n\\t\\toptions.canvas = canvas;\\r\\n\\t\\toptions.alpha = true;\\r\\n\\t\\toptions.stencil = true;\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tgl = GL.create(options);\\r\\n\\t\\t} catch(e) {\\r\\n\\t\\t\\tconsole.log(\\\"This canvas cannot be used as WebGL, maybe WebGL is not supported or this canvas has already a 2D context associated\\\");\\r\\n\\t\\t\\tgl = canvas.getContext(\\\"2d\\\", options);\\r\\n\\t\\t\\treturn gl;\\r\\n \\t}\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tgl = canvas.gl;\\r\\n\\r\\n\\r\\n\\t// Return if canvas is already canvas2DtoWebGL enabled\\r\\n\\tif(canvas.canvas2DtoWebGL_enabled)\\r\\n\\t\\treturn gl;\\r\\n\\r\\n\\t//settings\\r\\n\\tvar curveSubdivisions = 50;\\r\\n\\tvar max_points = 10000; //max amount of vertex allowed to have in a single primitive\\r\\n\\tvar max_characters = 1000; //max amount of characters allowed to have in a single fillText\\r\\n\\r\\n\\t//flag it for future uses\\r\\n\\tcanvas.canvas2DtoWebGL_enabled = true;\\r\\n\\r\\n\\tvar prev_gl = null;\\r\\n\\r\\n\\tvar ctx = canvas.ctx = gl;\\r\\n\\tctx.WebGLCanvas = {};\\r\\n\\tvar white = vec4.fromValues(1,1,1,1);\\r\\n\\r\\n\\t//reusing same buffer\\r\\n\\tvar global_index = 0;\\r\\n\\tvar global_vertices = new Float32Array( max_points * 3 );\\r\\n\\tvar global_mesh = new GL.Mesh();\\r\\n\\tvar global_buffer = global_mesh.createVertexBuffer(\\\"vertices\\\", null, null, global_vertices, gl.STREAM_DRAW );\\r\\n\\tvar quad_mesh = GL.Mesh.getScreenQuad();\\r\\n\\tvar circle_mesh = GL.Mesh.circle({size:1});\\r\\n\\tvar extra_projection = mat4.create();\\r\\n\\tvar stencil_enabled = false;\\r\\n\\tvar anisotropic = options.anisotropic !== undefined ? options.anisotropic : 2;\\r\\n\\r\\n\\tvar uniforms = {\\r\\n\\t\\tu_texture: 0\\r\\n\\t};\\r\\n\\r\\n\\tvar extra_macros = {};\\r\\n\\tif(options.allow3D)\\r\\n\\t\\textra_macros.EXTRA_PROJECTION = \\\"\\\";\\r\\n\\r\\n\\t//used to store font atlas textures (images are not stored here)\\r\\n\\tvar textures = gl.WebGLCanvas.textures_atlas = {};\\r\\n\\tgl.WebGLCanvas.clearAtlas = function()\\r\\n\\t{\\r\\n\\t\\ttextures = gl.WebGLCanvas.textures_atlas = {};\\r\\n\\t}\\r\\n\\r\\n\\tvar vertex_shader = null;\\r\\n\\tvar\\tflat_shader = null;\\r\\n\\tvar\\ttexture_shader = null;\\r\\n\\tvar flat_primitive_shader = null;\\r\\n\\tvar textured_transform_shader = null;\\r\\n\\tvar textured_primitive_shader = null;\\r\\n\\tvar gradient_primitive_shader = null;\\r\\n\\tvar\\tpoint_text_shader = null;\\r\\n\\r\\n\\tgl.WebGLCanvas.set3DMatrix = function(matrix)\\r\\n\\t{\\r\\n\\t\\tif(!matrix)\\r\\n\\t\\t\\tmat4.identity( extra_projection );\\r\\n\\t\\telse\\r\\n\\t\\t\\textra_projection.set( matrix );\\r\\n\\t\\tif(extra_macros.EXTRA_PROJECTION == null)\\r\\n\\t\\t{\\r\\n\\t\\t\\textra_macros.EXTRA_PROJECTION = \\\"\\\";\\r\\n\\t\\t\\tcompileShaders();\\r\\n\\t\\t\\tuniforms.u_projection = extra_projection;\\r\\n\\t\\t}\\r\\n\\t\\tuniforms.u_projection_enabled = !!matrix;\\r\\n\\t}\\r\\n\\r\\n\\tcompileShaders();\\r\\n\\r\\n\\tfunction compileShaders()\\r\\n\\t{\\r\\n\\t\\tvertex_shader = \\\"\\\\n\\\\\\r\\n\\t\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform mat3 u_transform;\\\\n\\\\\\r\\n\\t\\t\\t\\t#ifdef EXTRA_PROJECTION\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tuniform bool u_projection_enabled;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tuniform mat4 u_projection;\\\\n\\\\\\r\\n\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\tvarying float v_visible;\\\\n\\\\\\r\\n\\t\\t\\t\\tvoid main() { \\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 pos = a_vertex;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tv_visible = pos.z;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tpos = u_transform * vec3(pos.xy,1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tpos.z = 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t#ifdef EXTRA_PROJECTION\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tif(u_projection_enabled)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\t\\tgl_Position = u_projection * vec4(pos.xy,0.0,1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\t\\treturn;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t//normalize\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tpos.x = (2.0 * pos.x / u_viewport.x) - 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tpos.y = -((2.0 * pos.y / u_viewport.y) - 1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tgl_Position = vec4(pos, 1.0); \\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\t\\\";\\r\\n\\r\\n\\t\\tvertex_shader2 = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec2 a_coord;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_position;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_size;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat3 u_transform;\\\\n\\\\\\r\\n\\t\\t\\t#ifdef EXTRA_PROJECTION\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform bool u_projection_enabled;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform mat4 u_projection;\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\tvoid main() { \\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 pos = vec3(u_position + vec2(a_coord.x,1.0 - a_coord.y) * u_size, 1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tv_coord = a_coord; \\\\n\\\\\\r\\n\\t\\t\\t\\tpos = u_transform * pos;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.z = 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t#ifdef EXTRA_PROJECTION\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(u_projection_enabled)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tgl_Position = u_projection * vec4(pos.xy,0.0,1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\t//normalize\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.x = (2.0 * pos.x / u_viewport.x) - 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.y = -((2.0 * pos.y / u_viewport.y) - 1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = vec4(pos, 1.0); \\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\t\\t//flat_shader = new GL.Shader( GL.Shader.QUAD_VERTEX_SHADER, GL.Shader.FLAT_FRAGMENT_SHADER );\\r\\n\\t\\t//texture_shader = new GL.Shader( GL.Shader.QUAD_VERTEX_SHADER, GL.Shader.SCREEN_COLORED_FRAGMENT_SHADER );\\r\\n\\r\\n\\t\\tflat_shader = new GL.Shader(vertex_shader2,\\\"\\\\n\\\\\\r\\n\\t\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tgl_FragColor = u_color;\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\", extra_macros );\\r\\n\\r\\n\\t\\ttexture_shader = new GL.Shader(vertex_shader2,\\\"\\\\n\\\\\\r\\n\\t\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tgl_FragColor = u_color * texture2D( u_texture, v_coord );\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\", extra_macros );\\r\\n\\r\\n\\r\\n\\t\\tflat_primitive_shader = new GL.Shader(vertex_shader,\\\"\\\\n\\\\\\r\\n\\t\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\t\\tvarying float v_visible;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif (v_visible == 0.0)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tdiscard;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tgl_FragColor = u_color;\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\", extra_macros );\\r\\n\\r\\n\\t\\ttextured_transform_shader = new GL.Shader(GL.Shader.QUAD_VERTEX_SHADER,\\\"\\\\n\\\\\\r\\n\\t\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec4 u_texture_transform;\\\\n\\\\\\r\\n\\t\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 uv = v_coord * u_texture_transform.zw + vec2(u_texture_transform.x,0.0);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tuv.y = uv.y - u_texture_transform.y + (1.0 - u_texture_transform.w);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tuv = clamp(uv,vec2(0.0),vec2(1.0));\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tgl_FragColor = u_color * texture2D(u_texture, uv);\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\", extra_macros );\\r\\n\\r\\n\\t\\ttextured_primitive_shader = new GL.Shader(vertex_shader,\\\"\\\\n\\\\\\r\\n\\t\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\t\\tvarying float v_visible;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec4 u_texture_transform;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform mat3 u_itransform;\\\\n\\\\\\r\\n\\t\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 pos = (u_itransform * vec3( gl_FragCoord.s, u_viewport.y - gl_FragCoord.t,1.0)).xy;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tpos *= vec2( (u_viewport.x * u_texture_transform.z), (u_viewport.y * u_texture_transform.w) );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 uv = fract(pos / u_viewport) + u_texture_transform.xy;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tuv.y = 1.0 - uv.y;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tgl_FragColor = u_color * texture2D( u_texture, uv);\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\", extra_macros );\\r\\n\\r\\n\\t\\tgradient_primitive_shader = new GL.Shader(vertex_shader,\\\"\\\\n\\\\\\r\\n\\t\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\t\\tvarying float v_visible;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec4 u_gradient;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform mat3 u_itransform;\\\\n\\\\\\r\\n\\t\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 pos = (u_itransform * vec3( gl_FragCoord.s, u_viewport.y - gl_FragCoord.t,1.0)).xy;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t//vec2 pos = vec2( gl_FragCoord.s, u_viewport.y - gl_FragCoord.t);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 AP = pos - u_gradient.xy;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 AB = u_gradient.zw - u_gradient.xy;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tfloat dotAPAB = dot(AP,AB);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tfloat dotABAB = dot(AB,AB);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tfloat x = dotAPAB / dotABAB;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 uv = vec2( x, 0.0 );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tgl_FragColor = u_color * texture2D( u_texture, uv );\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\", extra_macros );\\r\\n\\r\\n\\t\\t//used for text\\r\\n\\t\\tvar POINT_TEXT_VERTEX_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec2 a_coord;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat3 u_transform;\\\\n\\\\\\r\\n\\t\\t\\t#ifdef EXTRA_PROJECTION\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform bool u_projection_enabled;\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform mat4 u_projection;\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_pointSize;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() { \\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 pos = a_vertex;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos = u_transform * pos;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.z = 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t#ifdef EXTRA_PROJECTION\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(u_projection_enabled)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tgl_Position = u_projection * vec4(pos.xy,0.0,1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\t//normalize\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.x = (2.0 * pos.x / u_viewport.x) - 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.y = -((2.0 * pos.y / u_viewport.y) - 1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = vec4(pos, 1.0); \\\\n\\\\\\r\\n\\t\\t\\t\\tgl_PointSize = ceil(u_pointSize);\\\\n\\\\\\r\\n\\t\\t\\t\\tv_coord = a_coord;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n\\t\\tvar POINT_TEXT_FRAGMENT_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_iCharSize;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_pointSize;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_angle_sincos;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 uv = vec2(1.0 - gl_PointCoord.s, gl_PointCoord.t);\\\\n\\\\\\r\\n\\t\\t\\t\\tuv = vec2( ((uv.y - 0.5) * u_angle_sincos.y - (uv.x - 0.5) * u_angle_sincos.x) + 0.5, ((uv.x - 0.5) * u_angle_sincos.y + (uv.y - 0.5) * u_angle_sincos.x) + 0.5);\\\\n\\\\\\r\\n\\t\\t\\t\\tuv = v_coord - uv * u_iCharSize + vec2(u_iCharSize*0.5);\\\\n\\\\\\r\\n\\t\\t\\t\\tuv.y = 1.0 - uv.y;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = vec4(u_color.xyz, u_color.a * texture2D(u_texture, uv, -1.0 ).a);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n\\t\\tpoint_text_shader = new GL.Shader( POINT_TEXT_VERTEX_SHADER, POINT_TEXT_FRAGMENT_SHADER, extra_macros );\\r\\n\\t};\\r\\n\\r\\n\\tctx.createImageShader = function(code)\\r\\n\\t{\\r\\n\\t\\treturn new GL.Shader( GL.Shader.QUAD_VERTEX_SHADER,\\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_texture_transform;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 uv = v_coord * u_texture_transform.zw + vec2(u_texture_transform.x,0.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tuv.y = uv.y - u_texture_transform.y + (1.0 - u_texture_transform.w);\\\\n\\\\\\r\\n\\t\\t\\t\\tuv = clamp(uv,vec2(0.0),vec2(1.0));\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = u_color * texture2D(u_texture, uv);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\\"+code+\\\";\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\", extra_macros );\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\t//some people may reuse it\\r\\n\\tctx.WebGLCanvas.vertex_shader = vertex_shader;\\r\\n\\r\\n\\t//STACK and TRANSFORM\\r\\n\\tctx._matrix = mat3.create();\\r\\n\\tvar tmp_mat3 = mat3.create();\\r\\n\\tvar tmp_vec2 = vec2.create();\\r\\n\\tvar tmp_vec4 = vec4.create();\\r\\n\\tvar tmp_vec4b = vec4.create();\\r\\n\\tvar tmp_vec2b = vec2.create();\\r\\n\\tctx._stack = [];\\r\\n\\tvar global_angle = 0;\\r\\n\\tvar viewport = ctx.viewport_data.subarray(2,4);\\r\\n\\r\\n\\tctx.translate = function(x,y)\\r\\n\\t{\\r\\n\\t\\ttmp_vec2[0] = x;\\r\\n\\t\\ttmp_vec2[1] = y;\\r\\n\\t\\tmat3.translate( this._matrix, this._matrix, tmp_vec2 );\\r\\n\\t}\\r\\n\\r\\n\\tctx.rotate = function(angle)\\r\\n\\t{\\r\\n\\t\\tmat3.rotate( this._matrix, this._matrix, angle );\\r\\n\\t\\tglobal_angle += angle;\\r\\n\\t}\\r\\n\\r\\n\\tctx.scale = function(x,y)\\r\\n\\t{\\r\\n\\t\\ttmp_vec2[0] = x;\\r\\n\\t\\ttmp_vec2[1] = y;\\r\\n\\t\\tmat3.scale( this._matrix, this._matrix, tmp_vec2 );\\r\\n\\t}\\r\\n\\r\\n\\t//own method to reset internal stuff\\r\\n\\tctx.resetTransform = function()\\r\\n\\t{\\r\\n\\t\\t//reset transform\\r\\n\\t\\tgl._stack.length = 0;\\r\\n\\t\\tthis._matrix.set([1,0,0, 0,1,0, 0,0,1]);\\r\\n\\t\\tglobal_angle = 0;\\r\\n\\t}\\r\\n\\r\\n\\tctx.save = function() {\\r\\n\\t\\tif(this._stack.length < 32)\\r\\n\\t\\t\\tthis._stack.push( mat3.clone( this._matrix ) );\\r\\n\\t}\\r\\n\\r\\n\\tctx.restore = function() {\\r\\n\\t\\tif(this._stack.length)\\r\\n\\t\\t\\tthis._matrix.set( this._stack.pop() );\\r\\n\\t\\telse\\r\\n\\t\\t\\tmat3.identity( this._matrix );\\r\\n\\t\\tglobal_angle = Math.atan2( this._matrix[3], this._matrix[4] ); //use up vector\\r\\n\\t\\tif(\\tstencil_enabled )\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.enable( gl.STENCIL_TEST );\\r\\n\\t\\t\\tgl.clearStencil( 0x0 );\\r\\n\\t\\t\\tgl.clear( gl.STENCIL_BUFFER_BIT );\\r\\n\\t\\t\\tgl.disable( gl.STENCIL_TEST );\\r\\n\\t\\t\\tstencil_enabled = false;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tctx.transform = function(a,b,c,d,e,f) {\\r\\n\\t\\tvar m = tmp_mat3;\\r\\n\\r\\n m[0] = a; m[1] = b; m[2] = 0; m[3] = c; m[4] = d; m[5] = 0; m[6] = e; m[7] = f; m[8] = 1; //fix\\r\\n\\t\\t//m[0] = a; m[1] = c;\\tm[2] = e; m[3] = b;\\tm[4] = d; m[5] = f;\\tm[6] = 0; m[7] = 0;\\tm[8] = 1;\\r\\n\\r\\n\\t\\tmat3.multiply( this._matrix, this._matrix, m );\\r\\n\\t\\tglobal_angle = Math.atan2( this._matrix[0], this._matrix[1] );\\r\\n\\t}\\r\\n\\r\\n\\tctx.setTransform = function(a,b,c,d,e,f) {\\r\\n\\t\\tvar m = this._matrix;\\r\\n\\r\\n\\t\\tm[0] = a; m[1] = b; m[2] = 0; m[3] = c; m[4] = d; m[5] = 0; m[6] = e; m[7] = f; m[8] = 1; //fix\\r\\n\\t\\t//m[0] = a; m[1] = c;\\tm[2] = e; m[3] = b;\\tm[4] = d; m[5] = f;\\tm[6] = 0; m[7] = 0;\\tm[8] = 1;\\r\\n\\r\\n\\t\\t//this._matrix.set([a,c,e,b,d,f,0,0,1]);\\r\\n\\t\\tglobal_angle = Math.atan2( this._matrix[0], this._matrix[1] );\\r\\n\\t}\\r\\n\\r\\n\\t//Images\\r\\n\\tvar last_uid = 1;\\r\\n\\r\\n\\t//textures are stored inside images, so as long as the image exist in memory, the texture will exist\\r\\n\\tfunction getTexture( img )\\r\\n\\t{\\r\\n\\t\\tvar tex = null;\\r\\n\\t\\tif(img.constructor === GL.Texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(img._context_id == gl.context_id)\\r\\n\\t\\t\\t\\treturn img;\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\t//same image could be used in several contexts\\r\\n\\t\\t\\tif(!img.gl)\\r\\n\\t\\t\\t\\timg.gl = {};\\r\\n\\r\\n\\t\\t\\t//Regular image\\r\\n\\t\\t\\tif(img.src)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar wrap = gl.REPEAT;\\r\\n\\r\\n\\t\\t\\t\\ttex = img.gl[ gl.context_id ];\\r\\n\\t\\t\\t\\tif(tex)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif( img.mustUpdate )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\ttex.uploadData( img );\\r\\n\\t\\t\\t\\t\\t\\timg.mustUpdate = false;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\treturn tex;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\treturn img.gl[ gl.context_id ] = GL.Texture.fromImage(img, { magFilter: gl.LINEAR, minFilter: gl.LINEAR_MIPMAP_LINEAR, wrap: wrap, ignore_pot:true, premultipliedAlpha: true, anisotropic: anisotropic } );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse //probably a canvas\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttex = img.gl[ gl.context_id ];\\r\\n\\t\\t\\t\\tif(tex)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif( img.mustUpdate )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\ttex.uploadData( img );\\r\\n\\t\\t\\t\\t\\t\\timg.mustUpdate = false;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\treturn tex;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\treturn img.gl[ gl.context_id ] = GL.Texture.fromImage(img, { minFilter: gl.LINEAR, magFilter: gl.LINEAR, anisotropic: anisotropic });\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\t//it supports all versions of drawImage (3 params, 5 params or 9 params)\\r\\n\\t//it allows to pass a shader, otherwise it uses texture_shader (code is GL.Shader.SCREEN_COLORED_FRAGMENT_SHADER)\\r\\n\\tctx.drawImage = function( img, x, y, w, h, shader )\\r\\n\\t{\\r\\n\\t\\tif(!img)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar img_width = img.videoWidth || img.width;\\r\\n\\t\\tvar img_height = img.videoHeight || img.height;\\r\\n\\t\\t\\t\\r\\n\\t\\tif(img_width == 0 || img_height == 0) \\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar tex = getTexture(img);\\r\\n\\t\\tif(!tex)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(arguments.length == 9) //img, sx,sy,sw,sh, x,y,w,h\\r\\n\\t\\t{\\r\\n\\t\\t\\ttmp_vec4b.set([x/img_width,y/img_height,w/img_width,h/img_height]);\\r\\n\\t\\t\\tx = arguments[5];\\r\\n\\t\\t\\ty = arguments[6];\\r\\n\\t\\t\\tw = arguments[7];\\r\\n\\t\\t\\th = arguments[8];\\r\\n\\t\\t\\tshader = textured_transform_shader;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\ttmp_vec4b.set([0,0,1,1]); //reset texture transform\\r\\n\\r\\n\\t\\ttmp_vec2[0] = x; tmp_vec2[1] = y;\\r\\n\\t\\ttmp_vec2b[0] = w === undefined ? tex.width : w;\\r\\n\\t\\ttmp_vec2b[1] = h === undefined ? tex.height : h;\\r\\n\\r\\n\\t\\ttex.bind(0);\\r\\n\\t\\tif(tex !== img) //only apply the imageSmoothingEnabled if we are dealing with images, not textures\\r\\n\\t\\t\\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.imageSmoothingEnabled ? gl.LINEAR : gl.NEAREST );\\r\\n\\r\\n\\t\\tif(!this.tintImages)\\r\\n\\t\\t{\\r\\n\\t\\t\\ttmp_vec4[0] = tmp_vec4[1] = tmp_vec4[2] = 1.0;\\ttmp_vec4[3] = this._globalAlpha;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tuniforms.u_color = this.tintImages ? this._fillcolor : tmp_vec4;\\r\\n\\t\\tuniforms.u_position = tmp_vec2;\\r\\n\\t\\tuniforms.u_size = tmp_vec2b;\\r\\n\\t\\tuniforms.u_transform = this._matrix;\\r\\n\\t\\tuniforms.u_texture_transform = tmp_vec4b;\\r\\n\\t\\tuniforms.u_viewport = viewport;\\r\\n\\r\\n\\t\\tshader = shader || texture_shader;\\r\\n\\r\\n\\t\\tshader.uniforms( uniforms ).draw(quad_mesh);\\r\\n\\t\\textra_projection[14] -= 0.001;\\r\\n\\t}\\r\\n\\r\\n\\tctx.createPattern = function( img )\\r\\n\\t{\\r\\n\\t\\treturn getTexture( img );\\r\\n\\t}\\r\\n\\r\\n\\t//to create gradients\\r\\n\\tfunction WebGLCanvasGradient(x,y,x2,y2)\\r\\n\\t{\\r\\n\\t\\tthis.id = (ctx._last_gradient_id++) % ctx._max_gradients;\\r\\n\\t\\tthis.points = new Float32Array([x,y,x2,y2]);\\r\\n\\t\\tthis.stops = [];\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t}\\r\\n\\r\\n\\t//to avoid creating textures all the time\\r\\n\\tctx._last_gradient_id = 0;\\r\\n\\tctx._max_gradients = 16;\\r\\n\\tctx._gradients_pool = [];\\r\\n\\r\\n\\tWebGLCanvasGradient.prototype.addColorStop = function( pos, color )\\r\\n\\t{\\r\\n\\t\\tvar final_color = hexColorToRGBA( color );\\r\\n\\t\\tvar v = new Uint8Array(4);\\r\\n\\t\\tv[0] = Math.clamp( final_color[0], 0,1 ) * 255;\\r\\n\\t\\tv[1] = Math.clamp( final_color[1], 0,1 ) * 255;\\r\\n\\t\\tv[2] = Math.clamp( final_color[2], 0,1 ) * 255;\\r\\n\\t\\tv[3] = Math.clamp( final_color[3], 0,1 ) * 255;\\r\\n\\t\\tthis.stops.push( [ pos, v ]);\\r\\n\\t\\tthis.stops.sort( function(a,b) {return (a[0] > b[0]) ? 1 : ((b[0] > a[0]) ? -1 : 0);} );\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t}\\r\\n\\r\\n\\tWebGLCanvasGradient.prototype.toTexture = function()\\r\\n\\t{\\r\\n\\t\\t//create a texture from the pool\\r\\n\\t\\tif(!this._texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(this.id != -1)\\r\\n\\t\\t\\t\\tthis._texture = ctx._gradients_pool[ this.id ];\\r\\n\\t\\t\\tif(!this._texture)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._texture = new GL.Texture( 128,1, { format: gl.RGBA, magFilter: gl.LINEAR, wrap: gl.CLAMP_TO_EDGE, minFilter: gl.NEAREST });\\r\\n\\t\\t\\t\\tif(this.id != -1)\\r\\n\\t\\t\\t\\t\\tctx._gradients_pool[ this.id ] = this._texture;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tif(!this._must_update)\\r\\n\\t\\t\\treturn this._texture;\\r\\n\\t\\tthis._must_update = false;\\r\\n\\t\\tif(this.stops.length < 1)\\r\\n\\t\\t\\treturn this._texture; //no stops\\r\\n\\t\\tif(this.stops.length < 2)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._texture.fill( this.stops[0][1] );\\r\\n\\t\\t\\treturn this._texture; //one color\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//fill buffer\\r\\n\\t\\tvar index = 0;\\r\\n\\t\\tvar current = this.stops[index];\\r\\n\\t\\tvar next = this.stops[index+1];\\r\\n\\r\\n\\t\\tvar buffer = new Uint8Array(128*4);\\r\\n\\t\\tfor(var i = 0; i < 128; i+=1)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar color = buffer.subarray( i*4, i*4+4 );\\r\\n\\t\\t\\tvar t = i/128;\\r\\n\\t\\t\\tif( current[0] > t )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(index == 0)\\r\\n\\t\\t\\t\\t\\tcolor.set( current[1] );\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tindex+=1;\\t\\t\\t\\t\\t\\t\\r\\n\\t\\t\\t\\t\\tcurrent = this.stops[index];\\r\\n\\t\\t\\t\\t\\tnext = this.stops[index+1];\\r\\n\\t\\t\\t\\t\\tif(!next)\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\ti-=1;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if( current[0] <= t && t < next[0] )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar f = (t - current[0]) / (next[0] - current[0]);\\r\\n\\t\\t\\t\\tvec4.lerp( color, current[1], next[1], f );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if( next[0] <= t )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tindex+=1;\\t\\t\\t\\t\\t\\t\\r\\n\\t\\t\\t\\tcurrent = this.stops[index];\\r\\n\\t\\t\\t\\tnext = this.stops[index+1];\\r\\n\\t\\t\\t\\tif(!next)\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\ti-=1;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\t//fill the remaining\\r\\n\\t\\tif(i<128)\\r\\n\\t\\t\\tfor(var j = i; j < 128; j+=1)\\r\\n\\t\\t\\t\\tbuffer.set( current[1], j*4 );\\r\\n\\t\\tthis._texture.uploadData( buffer );\\r\\n\\t\\treturn this._texture;\\r\\n\\t}\\r\\n\\r\\n\\tctx.createLinearGradient = function( x,y, x2, y2 )\\r\\n\\t{\\r\\n\\t\\treturn new WebGLCanvasGradient(x,y,x2,y2);\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\t//Primitives\\r\\n\\r\\n\\tctx.beginPath = function()\\r\\n\\t{\\r\\n\\t\\tglobal_index = 0;\\r\\n\\t}\\r\\n\\r\\n\\tctx.closePath = function()\\r\\n\\t{\\r\\n\\t\\tif(global_index < 3)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tglobal_vertices[ global_index ] = global_vertices[0];\\r\\n\\t\\tglobal_vertices[ global_index + 1] = global_vertices[1];\\r\\n\\t\\tglobal_vertices[ global_index + 2] = 1;\\r\\n\\t\\tglobal_index += 3;\\r\\n\\t}\\r\\n\\r\\n\\tctx.moveTo = function(x,y)\\r\\n\\t{\\r\\n\\t\\t//not the first line\\r\\n\\t\\tif(global_index == 0)\\r\\n\\t\\t{\\r\\n\\t\\t\\tglobal_vertices[ global_index ] = x;\\r\\n\\t\\t\\tglobal_vertices[ global_index + 1] = y;\\r\\n\\t\\t\\tglobal_vertices[ global_index + 2] = 1;\\r\\n\\t\\t\\tglobal_index += 3;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tglobal_vertices[ global_index ] = global_vertices[ global_index - 3];\\r\\n\\t\\t\\tglobal_vertices[ global_index + 1] = global_vertices[ global_index - 2];\\r\\n\\t\\t\\tglobal_vertices[ global_index + 2] = 0;\\r\\n\\t\\t\\tglobal_index += 3;\\r\\n\\t\\t\\tglobal_vertices[ global_index ] = x;\\r\\n\\t\\t\\tglobal_vertices[ global_index + 1] = y;\\r\\n\\t\\t\\tglobal_vertices[ global_index + 2] = 0;\\r\\n\\t\\t\\tglobal_index += 3;\\r\\n\\t\\t}\\r\\n\\r\\n\\t}\\r\\n\\r\\n\\tctx.lineTo = function(x,y)\\r\\n\\t{\\r\\n\\t\\tglobal_vertices[ global_index ] = x;\\r\\n\\t\\tglobal_vertices[ global_index + 1] = y;\\r\\n\\t\\tglobal_vertices[ global_index + 2] = 1;\\r\\n\\t\\tglobal_index += 3;\\r\\n\\t}\\r\\n\\r\\n\\tctx.bezierCurveTo = function(m1x,m1y, m2x,m2y, ex,ey)\\r\\n\\t{\\r\\n\\t\\tif(global_index < 3)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar last = [ global_vertices[ global_index - 3 ], global_vertices[ global_index - 2 ] ];\\r\\n\\t\\tcp = [ last, [m1x, m1y], [m2x, m2y], [ex, ey] ];\\r\\n\\t\\tfor(var i = 0; i <= curveSubdivisions; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar t = i/curveSubdivisions;\\r\\n\\t\\t\\tvar ax, bx, cx;\\r\\n\\t\\t\\tvar ay, by, cy;\\r\\n\\t\\t\\tvar tSquared, tCubed;\\r\\n\\r\\n\\t\\t\\t/* cálculo de los coeficientes polinomiales */\\r\\n\\t\\t\\tcx = 3.0 * (cp[1][0] - cp[0][0]);\\r\\n\\t\\t\\tbx = 3.0 * (cp[2][0] - cp[1][0]) - cx;\\r\\n\\t\\t\\tax = cp[3][0] - cp[0][0] - cx - bx;\\r\\n\\r\\n\\t\\t\\tcy = 3.0 * (cp[1][1] - cp[0][1]);\\r\\n\\t\\t\\tby = 3.0 * (cp[2][1] - cp[1][1]) - cy;\\r\\n\\t\\t\\tay = cp[3][1] - cp[0][1] - cy - by;\\r\\n\\r\\n\\t\\t\\t/* calculate the curve point at parameter value t */\\r\\n\\t\\t\\ttSquared = t * t;\\r\\n\\t\\t\\ttCubed = tSquared * t;\\r\\n\\r\\n\\t\\t\\tvar x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0][0];\\r\\n\\t\\t\\tvar y = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0][1];\\r\\n\\t\\t\\tglobal_vertices[ global_index ] = x;\\r\\n\\t\\t\\tglobal_vertices[ global_index + 1] = y;\\r\\n\\t\\t\\tglobal_vertices[ global_index + 2] = 1;\\r\\n\\t\\t\\tglobal_index += 3;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tctx.quadraticCurveTo = function(mx,my,ex,ey)\\r\\n\\t{\\r\\n\\t\\tif(global_index < 3)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar sx = global_vertices[ global_index - 3 ];\\r\\n\\t\\tvar sy = global_vertices[ global_index - 2 ];\\r\\n\\t\\t\\r\\n\\t\\tfor(var i = 0; i <= curveSubdivisions; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar f = i/curveSubdivisions;\\r\\n\\t\\t\\tvar nf = 1-f;\\r\\n\\r\\n\\t\\t\\tvar m1x = sx * nf + mx * f;\\r\\n\\t\\t\\tvar m1y = sy * nf + my * f;\\r\\n\\r\\n\\t\\t\\tvar m2x = mx * nf + ex * f;\\r\\n\\t\\t\\tvar m2y = my * nf + ey * f;\\r\\n\\r\\n\\t\\t\\tglobal_vertices[ global_index ] = m1x * nf + m2x * f;\\r\\n\\t\\t\\tglobal_vertices[ global_index + 1] = m1y * nf + m2y * f;\\r\\n\\t\\t\\tglobal_vertices[ global_index + 2] = 1;\\r\\n\\t\\t\\tglobal_index += 3;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\tctx.fill = function()\\r\\n\\t{\\r\\n\\t\\tif(global_index < 9)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//update buffer\\r\\n\\t\\tglobal_buffer.uploadRange(0, global_index * 4); //4 bytes per float\\r\\n\\t\\tuniforms.u_viewport = viewport;\\r\\n\\t\\tvar shader = flat_primitive_shader;\\r\\n\\r\\n\\t\\t//first the shadow\\r\\n\\t\\tif( this._shadowcolor[3] > 0.0 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tuniforms.u_color = this._shadowcolor;\\r\\n\\t\\t\\tthis.save();\\r\\n\\t\\t\\tthis.translate( this.shadowOffsetX, this.shadowOffsetY );\\r\\n\\t\\t\\tshader.uniforms(uniforms).drawRange(global_mesh, gl.TRIANGLE_FAN, 0, global_index / 3);\\r\\n\\t\\t\\tthis.restore();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tuniforms.u_color = this._fillcolor;\\r\\n\\t\\tuniforms.u_transform = this._matrix;\\r\\n\\r\\n\\t\\tvar fill_style = this._fillStyle;\\r\\n\\r\\n\\t\\tif( fill_style.constructor === WebGLCanvasGradient ) //gradient\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar grad = fill_style;\\r\\n\\t\\t\\tvar tex = grad.toTexture();\\r\\n\\t\\t\\tuniforms.u_color = [1,1,1, this.globalAlpha]; \\r\\n\\t\\t\\tuniforms.u_gradient = grad.points; \\r\\n\\t\\t\\tuniforms.u_texture = 0;\\r\\n\\t\\t\\tuniforms.u_itransform = mat3.invert( tmp_mat3, this._matrix );\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tshader = gradient_primitive_shader;\\r\\n\\t\\t}\\r\\n\\t\\telse if( fill_style.constructor === GL.Texture ) //pattern\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar tex = fill_style;\\r\\n\\t\\t\\tuniforms.u_color = [1,1,1, this.globalAlpha]; \\r\\n\\t\\t\\tuniforms.u_texture = 0;\\r\\n\\t\\t\\ttmp_vec4.set([0,0,1/tex.width, 1/tex.height]);\\r\\n\\t\\t\\tuniforms.u_texture_transform = tmp_vec4;\\r\\n\\t\\t\\tuniforms.u_itransform = mat3.invert( tmp_mat3, this._matrix );\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tshader = textured_primitive_shader;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//render\\r\\n\\t\\tshader.uniforms(uniforms).drawRange(global_mesh, gl.TRIANGLE_FAN, 0, global_index / 3);\\r\\n\\t\\textra_projection[14] -= 0.001;\\r\\n\\t}\\r\\n\\r\\n\\t//basic stroke using gl.LINES\\r\\n\\tctx.strokeThin = function()\\r\\n\\t{\\r\\n\\t\\tif(global_index < 6)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//update buffer\\r\\n\\t\\tglobal_buffer.uploadRange(0, global_index * 4); //4 bytes per float\\r\\n\\t\\t//global_buffer.upload( gl.STREAM_DRAW );\\r\\n\\r\\n\\t\\tgl.setLineWidth( this.lineWidth );\\r\\n\\t\\tuniforms.u_color = this._strokecolor;\\r\\n\\t\\tuniforms.u_transform = this._matrix;\\r\\n\\t\\tuniforms.u_viewport = viewport;\\r\\n\\t\\tflat_primitive_shader.uniforms(uniforms).drawRange(global_mesh, gl.LINE_STRIP, 0, global_index / 3);\\r\\n\\t}\\r\\n\\r\\n\\t//advanced stroke (it takes width into account)\\r\\n\\tvar lines_vertices = new Float32Array( max_points * 3 );\\r\\n\\tvar lines_mesh = new GL.Mesh();\\r\\n\\tvar lines_buffer = lines_mesh.createVertexBuffer(\\\"vertices\\\", null, null, lines_vertices, gl.STREAM_DRAW );\\r\\n\\r\\n\\tctx.stroke = function()\\r\\n\\t{\\r\\n\\t\\tif(global_index < 6)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\ttmp_vec2[0] = this._matrix[0];\\r\\n\\t\\ttmp_vec2[1] = this._matrix[1];\\r\\n\\r\\n\\t\\tif( (this.lineWidth * vec2.length(tmp_vec2)) <= 1.0 )\\r\\n\\t\\t\\treturn this.strokeThin();\\r\\n\\r\\n\\t\\tvar num_points = global_index / 3;\\r\\n\\t\\tvar vertices = lines_vertices;\\r\\n\\t\\tvar l = global_index;\\r\\n\\t\\tvar line_width = this.lineWidth * 0.5;\\r\\n\\r\\n\\t\\tvar points = global_vertices;\\r\\n\\r\\n\\t\\tvar delta_x = 0;\\r\\n\\t\\tvar delta_y = 0;\\r\\n\\t\\tvar prev_delta_x = 0;\\r\\n\\t\\tvar prev_delta_y = 0;\\r\\n\\t\\tvar average_x = 0;\\r\\n\\t\\tvar average_y = 0;\\r\\n\\t\\tvar first_delta_x = 0;\\r\\n\\t\\tvar first_delta_y = 0;\\r\\n\\r\\n\\t\\tif(points[0] == points[ global_index - 3 ] && points[1] == points[ global_index - 2 ])\\r\\n\\t\\t{\\r\\n\\t\\t\\tdelta_x = points[ global_index - 3 ] - points[ global_index - 6 ];\\r\\n\\t\\t\\tdelta_y = points[ global_index - 2 ] - points[ global_index - 5 ];\\r\\n\\t\\t\\tvar dist = Math.sqrt( delta_x*delta_x + delta_y*delta_y );\\r\\n\\t\\t\\tif(dist != 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tdelta_x = (delta_x / dist);\\r\\n\\t\\t\\t\\tdelta_y = (delta_y / dist);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar i, pos = 0;\\r\\n\\t\\tfor(i = 0; i < l-3; i+=3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tprev_delta_x = delta_x;\\r\\n\\t\\t\\tprev_delta_y = delta_y;\\r\\n\\r\\n\\t\\t\\tdelta_x = points[i+3] - points[i];\\r\\n\\t\\t\\tdelta_y = points[i+4] - points[i+1];\\r\\n\\t\\t\\tvar dist = Math.sqrt( delta_x*delta_x + delta_y*delta_y );\\r\\n\\t\\t\\tif(dist != 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tdelta_x = (delta_x / dist);\\r\\n\\t\\t\\t\\tdelta_y = (delta_y / dist);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif(i == 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfirst_delta_x = delta_x;\\r\\n\\t\\t\\t\\tfirst_delta_y = delta_y;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\taverage_x = delta_x + prev_delta_x;\\r\\n\\t\\t\\taverage_y = delta_y + prev_delta_y;\\r\\n\\r\\n\\t\\t\\tvar dist = Math.sqrt( average_x*average_x + average_y*average_y );\\r\\n\\t\\t\\tif(dist != 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\taverage_x = (average_x / dist);\\r\\n\\t\\t\\t\\taverage_y = (average_y / dist);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvertices[ pos+0 ] = points[i] - average_y * line_width;\\r\\n\\t\\t\\tvertices[ pos+1 ] = points[i+1] + average_x * line_width;\\r\\n\\t\\t\\tvertices[ pos+2 ] = 1;\\r\\n\\t\\t\\tvertices[ pos+3 ] = points[i] + average_y * line_width;\\r\\n\\t\\t\\tvertices[ pos+4 ] = points[i+1] - average_x * line_width;\\r\\n\\t\\t\\tvertices[ pos+5 ] = 1;\\r\\n\\r\\n\\t\\t\\tpos += 6;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//final points are tricky\\r\\n\\t\\tif(points[0] == points[ global_index - 3 ] && points[1] == points[ global_index - 2 ])\\r\\n\\t\\t{\\r\\n\\t\\t\\taverage_x = delta_x + first_delta_x;\\r\\n\\t\\t\\taverage_y = delta_y + first_delta_y;\\r\\n\\t\\t\\tvar dist = Math.sqrt( average_x*average_x + average_y*average_y );\\r\\n\\t\\t\\tif(dist != 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\taverage_x = (average_x / dist);\\r\\n\\t\\t\\t\\taverage_y = (average_y / dist);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tvertices[ pos+0 ] = points[i] - average_y * line_width;\\r\\n\\t\\t\\tvertices[ pos+1 ] = points[i+1] + average_x * line_width;\\r\\n\\t\\t\\tvertices[ pos+2 ] = 1;\\r\\n\\t\\t\\tvertices[ pos+3 ] = points[i] + average_y * line_width;\\r\\n\\t\\t\\tvertices[ pos+4 ] = points[i+1] - average_x * line_width;\\r\\n\\t\\t\\tvertices[ pos+5 ] = 1;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar dist = Math.sqrt( delta_x*delta_x + delta_y*delta_y );\\r\\n\\t\\t\\tif(dist != 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\taverage_x = (delta_x / dist);\\r\\n\\t\\t\\t\\taverage_y = (delta_y / dist);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvertices[ pos+0 ] = points[i] - (average_y - average_x) * line_width;\\r\\n\\t\\t\\tvertices[ pos+1 ] = points[i+1] + (average_x + average_y) * line_width;\\r\\n\\t\\t\\tvertices[ pos+2 ] = 1;\\r\\n\\t\\t\\tvertices[ pos+3 ] = points[i] + (average_y + average_x) * line_width;\\r\\n\\t\\t\\tvertices[ pos+4 ] = points[i+1] - (average_x - average_y) * line_width;\\r\\n\\t\\t\\tvertices[ pos+5 ] = 1;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tpos += 6;\\r\\n\\r\\n\\t\\tlines_buffer.upload(gl.STREAM_DRAW);\\r\\n\\t\\tlines_buffer.uploadRange(0, pos * 4); //4 bytes per float\\r\\n\\r\\n\\t\\tuniforms.u_transform = this._matrix;\\r\\n\\t\\tuniforms.u_viewport = viewport;\\r\\n\\r\\n\\t\\t//first the shadow\\r\\n\\t\\tif( this._shadowcolor[3] > 0.0 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tuniforms.u_color = this._shadowcolor;\\r\\n\\t\\t\\tthis.save();\\r\\n\\t\\t\\tthis.translate( this.shadowOffsetX, this.shadowOffsetY );\\r\\n\\t\\t\\tflat_primitive_shader.uniforms(uniforms).drawRange(global_mesh, gl.TRIANGLE_STRIP, 0, pos / 3);\\r\\n\\t\\t\\tthis.restore();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//gl.setLineWidth( this.lineWidth );\\r\\n\\t\\tuniforms.u_color = this._strokecolor;\\r\\n\\t\\tflat_primitive_shader.uniforms( uniforms ).drawRange(lines_mesh, gl.TRIANGLE_STRIP, 0, pos / 3 );\\r\\n\\t\\textra_projection[14] -= 0.001;\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\tctx.rect = function(x,y,w,h)\\r\\n\\t{\\r\\n\\t\\tglobal_vertices[ global_index ] = x;\\r\\n\\t\\tglobal_vertices[ global_index + 1] = y;\\r\\n\\t\\tglobal_vertices[ global_index + 2] = 1;\\r\\n\\r\\n\\t\\tglobal_vertices[ global_index + 3] = x+w;\\r\\n\\t\\tglobal_vertices[ global_index + 4] = y;\\r\\n\\t\\tglobal_vertices[ global_index + 5] = 1;\\r\\n\\r\\n\\t\\tglobal_vertices[ global_index + 6] = x+w;\\r\\n\\t\\tglobal_vertices[ global_index + 7] = y+h;\\r\\n\\t\\tglobal_vertices[ global_index + 8] = 1;\\r\\n\\r\\n\\t\\tglobal_vertices[ global_index + 9] = x;\\r\\n\\t\\tglobal_vertices[ global_index + 10] = y+h;\\r\\n\\t\\tglobal_vertices[ global_index + 11] = 1;\\r\\n\\r\\n\\t\\tglobal_vertices[ global_index + 12] = x;\\r\\n\\t\\tglobal_vertices[ global_index + 13] = y;\\r\\n\\t\\tglobal_vertices[ global_index + 14] = 1;\\r\\n\\r\\n\\t\\tglobal_index += 15;\\r\\n\\t}\\r\\n\\r\\n\\tctx.roundRect = function (x, y, w, h, radius, radius_low)\\r\\n\\t{\\r\\n\\t\\tif ( radius === undefined )\\r\\n\\t\\t\\tradius = 5;\\r\\n\\t\\tif(radius_low === undefined)\\r\\n\\t\\t\\tradius_low = radius;\\r\\n\\t\\tvar gv = global_vertices;\\r\\n\\t\\tvar gi = global_index;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < 10; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar ang = (i/10)*Math.PI*0.5;\\r\\n\\t\\t\\tgv[ gi ] = x+radius*(1.0 - Math.cos(ang));\\r\\n\\t\\t\\tgv[ gi + 1] = y+radius*(1.0 - Math.sin(ang));\\r\\n\\t\\t\\tgv[ gi + 2] = 1;\\r\\n\\t\\t\\tgi += 3;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tgv[ gi + 0] = x+radius; gv[ gi + 1] = y; gv[ gi + 2] = 1;\\r\\n\\t\\tgv[ gi + 3] = x+w-radius; gv[ gi + 4] = y; gv[ gi + 5] = 1;\\r\\n\\t\\tgi += 6;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < 10; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar ang = (i/10)*Math.PI*0.5;\\r\\n\\t\\t\\tgv[ gi + 0] = x+w-radius*(1.0 - Math.sin(ang));\\r\\n\\t\\t\\tgv[ gi + 1] = y+radius*(1.0 - Math.cos(ang));\\r\\n\\t\\t\\tgv[ gi + 2] = 1;\\r\\n\\t\\t\\tgi += 3;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tgv[ gi + 0] = x+w; gv[ gi + 1] = y+radius; gv[ gi + 2] = 1;\\r\\n\\t\\tgv[ gi + 3] = x+w; gv[ gi + 4] = y+h-radius_low; gv[ gi + 5] = 1;\\r\\n\\t\\tgi += 6;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < 10; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar ang = (i/10)*Math.PI*0.5;\\r\\n\\t\\t\\tgv[ gi + 0] = x+w-radius_low*(1.0 - Math.cos(ang));\\r\\n\\t\\t\\tgv[ gi + 1] = y+h-radius_low*(1.0 - Math.sin(ang));\\r\\n\\t\\t\\tgv[ gi + 2] = 1;\\r\\n\\t\\t\\tgi += 3;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tgv[ gi + 0] = x+w-radius_low; gv[ gi + 1] = y+h; gv[ gi + 2] = 1;\\r\\n\\t\\tgv[ gi + 3] = x+radius_low; gv[ gi + 4] = y+h; gv[ gi + 5] = 1;\\r\\n\\t\\tgi += 6;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < 10; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar ang = (i/10)*Math.PI*0.5;\\r\\n\\t\\t\\tgv[ gi + 0] = x+radius_low*(1.0 - Math.sin(ang));\\r\\n\\t\\t\\tgv[ gi + 1] = y+h-radius_low*(1.0 - Math.cos(ang));\\r\\n\\t\\t\\tgv[ gi + 2] = 1;\\r\\n\\t\\t\\tgi += 3;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tgv[ gi + 0] = x; gv[ gi + 1] = y+radius; gv[ gi + 2] = 1;\\r\\n\\t\\tglobal_index = gi + 3;\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\tctx.arc = function(x,y,radius, start_ang, end_ang)\\r\\n\\t{\\r\\n\\t\\tnum = Math.ceil(radius*2*this._matrix[0]+1);\\r\\n\\t\\tif(num<1)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tstart_ang = start_ang === undefined ? 0 : start_ang;\\r\\n\\t\\tend_ang = end_ang === undefined ? Math.PI * 2 : end_ang;\\r\\n\\r\\n\\t\\tvar delta = (end_ang - start_ang) / num;\\r\\n\\r\\n\\t\\tfor(var i = 0; i <= num; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar f = start_ang + i*delta;\\r\\n\\t\\t\\tthis.lineTo(x + Math.cos(f) * radius, y + Math.sin(f) * radius);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tctx.strokeRect = function(x,y,w,h)\\r\\n\\t{\\r\\n\\t\\tthis.beginPath();\\r\\n\\t\\tthis.rect(x,y,w,h);//[x,y,1, x+w,y,1, x+w,y+h,1, x,y+h,1, x,y,1 ];\\r\\n\\t\\tthis.stroke();\\r\\n\\t}\\r\\n\\t\\r\\n\\tctx.fillRect = function(x,y,w,h)\\r\\n\\t{\\r\\n\\t\\tglobal_index = 0;\\r\\n\\r\\n\\t\\t//fill using a gradient or pattern\\r\\n\\t\\tif( this._fillStyle.constructor == GL.Texture || this._fillStyle.constructor === WebGLCanvasGradient )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.beginPath();\\r\\n\\t\\t\\tthis.rect(x,y,w,h);\\r\\n\\t\\t\\tthis.fill();\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tuniforms.u_color = this._fillcolor;\\r\\n\\t\\ttmp_vec2[0] = x; tmp_vec2[1] = y;\\r\\n\\t\\ttmp_vec2b[0] = w; tmp_vec2b[1] = h;\\r\\n\\t\\tuniforms.u_position = tmp_vec2;\\r\\n\\t\\tuniforms.u_size = tmp_vec2b;\\r\\n\\t\\tuniforms.u_transform = this._matrix;\\r\\n\\t\\tuniforms.u_viewport = viewport;\\r\\n\\t\\tflat_shader.uniforms(uniforms).draw(quad_mesh);\\r\\n\\t\\textra_projection[14] -= 0.001;\\r\\n\\t}\\r\\n\\r\\n\\t//other functions\\r\\n\\tctx.clearRect = function(x,y,w,h)\\r\\n\\t{\\r\\n\\t\\tif(x != 0 || y != 0 || w != canvas.width || h != canvas.height )\\r\\n\\t\\t\\tgl.scissor(x,y,w,h);\\r\\n\\r\\n\\t\\t//gl.clearColor( 0.0,0.0,0.0,0.0 );\\r\\n\\t\\tgl.clear( gl.COLOR_BUFFER_BIT );\\r\\n\\t\\tvar v = gl.viewport_data;\\r\\n\\t\\tgl.scissor(v[0],v[1],v[2],v[3]);\\r\\n\\t}\\r\\n\\r\\n\\tctx.fillCircle = function(x,y,r)\\r\\n\\t{\\r\\n\\t\\tglobal_index = 0;\\r\\n\\r\\n\\t\\t//fill using a gradient or pattern\\r\\n\\t\\tif( this._fillStyle.constructor == GL.Texture || this._fillStyle.constructor === WebGLCanvasGradient )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.beginPath();\\r\\n\\t\\t\\tthis.arc(x,y,r,0,Math.PI*2);\\r\\n\\t\\t\\tthis.fill();\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tuniforms.u_color = this._fillcolor;\\r\\n\\t\\ttmp_vec2[0] = x; tmp_vec2[1] = y;\\r\\n\\t\\ttmp_vec2b[0] = r; tmp_vec2b[1] = r;\\r\\n\\t\\tuniforms.u_position = tmp_vec2;\\r\\n\\t\\tuniforms.u_size = tmp_vec2b;\\r\\n\\t\\tuniforms.u_transform = this._matrix;\\r\\n\\t\\tuniforms.u_viewport = viewport\\r\\n\\t\\tflat_shader.uniforms(uniforms).draw(circle_mesh);\\r\\n\\t\\textra_projection[14] -= 0.001;\\r\\n\\t}\\r\\n\\t\\r\\n\\tctx.clip = function()\\r\\n\\t{\\r\\n\\t\\tgl.colorMask(false, false, false, false);\\r\\n\\t\\tgl.depthMask(false);\\r\\n\\t\\t\\r\\n\\t\\t//fill stencil buffer\\r\\n\\t\\tgl.enable( gl.STENCIL_TEST );\\r\\n\\t\\tgl.stencilFunc( gl.ALWAYS, 1, 0xFF );\\r\\n\\t\\tgl.stencilOp( gl.KEEP, gl.KEEP, gl.REPLACE ); //TODO using INCR we could allow 8 stencils \\r\\n\\t\\t\\r\\n\\t\\tthis.fill();\\r\\n\\r\\n\\t\\tstencil_enabled = true;\\t\\t\\r\\n\\t\\tgl.colorMask(true, true, true, true);\\r\\n\\t\\tgl.depthMask(true);\\r\\n\\t\\tgl.stencilFunc( gl.EQUAL, 1, 0xFF );\\r\\n\\t}\\r\\n\\r\\n\\t//control funcs: used to set flags at the beginning and the end of the render\\r\\n\\tctx.start2D = function()\\r\\n\\t{\\r\\n\\t\\tprev_gl = window.gl;\\r\\n\\t\\twindow.gl = this;\\r\\n\\t\\tvar gl = this;\\r\\n\\r\\n\\t\\t//viewport[0] = gl.viewport_data[2];\\r\\n\\t\\t//viewport[1] = gl.viewport_data[3];\\r\\n\\t\\tgl.disable( gl.CULL_FACE );\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.disable( gl.STENCIL_TEST );\\r\\n\\t\\tgl.enable( gl.BLEND );\\r\\n\\t\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );\\r\\n\\t\\tgl.lineWidth = 1;\\r\\n\\t\\tglobal_index = 0;\\r\\n\\t\\tmat4.identity( extra_projection );\\r\\n\\t}\\r\\n\\r\\n\\tctx.finish2D = function()\\r\\n\\t{\\r\\n\\t\\tglobal_index = 0;\\r\\n\\t\\tgl.lineWidth = 1;\\r\\n\\t\\twindow.gl = prev_gl;\\r\\n\\t\\tgl.disable( gl.STENCIL_TEST );\\r\\n\\t}\\r\\n\\r\\n\\t/*\\r\\n\\tvar max_triangle_characters = 64;\\r\\n\\tvar triangle = new Float32Array([-1,1,0, -1,-1,0, 1,-1,0, -1,1,0, 1,-1,0, 1,1,0]);\\r\\n\\tvar triangle_text_vertices = new Float32Array( max_triangle_characters * 6 * 3 );\\r\\n\\tvar triangle_text_mesh = new GL.Mesh();\\r\\n\\tvar triangle_text_vertices_buffer = triangle_text_mesh.createVertexBuffer(\\\"vertices\\\", null, null, triangle_text_vertices );\\r\\n\\tvar tv = triangle_text_vertices;\\r\\n\\tfor(var i = 0; i < triangle_text_vertices.length; i += 6*3)\\r\\n\\t{\\r\\n\\t\\ttv.set(triangle, i);\\r\\n\\t\\ttv[2] = tv[5] = tv[8] = tv[11] = t[14] = t[17] = i / (6*3);\\r\\n\\t}\\r\\n\\ttriangle_text_vertices_buffer.upload();\\r\\n\\r\\n\\tvar TRIANGLE_TEXT_VERTEX_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\t#define MAX_CHARS 64;\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_charPos[ MAX_CHARS ];\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_charCoord[ MAX_CHARS ];\\\\n\\\\\\r\\n\\t\\t\\tuniform mat3 u_transform;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_pointSize;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() { \\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 pos = a_vertex;\\\\n\\\\\\r\\n\\t\\t\\t\\tv_coord = a_vertex * 0.5 + vec2(0.5);\\\\n\\\\\\r\\n\\t\\t\\t\\tint char_index = (int)pos.z;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.z = 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.xz = pos.xz * u_pointSize + u_charPos[char_index];\\\\n\\\\\\r\\n\\t\\t\\t\\tpos = u_transform * pos;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.z = 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t//normalize\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.x = (2.0 * pos.x / u_viewport.x) - 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.y = -((2.0 * pos.y / u_viewport.y) - 1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = vec4(pos, 1.0); \\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n\\tvar TRIANGLE_TEXT_FRAGMENT_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_iCharSize;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_pointSize;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 uv = vec2(1.0 - gl_PointCoord.s, 1.0 - gl_PointCoord.t);\\\\n\\\\\\r\\n\\t\\t\\t\\tuv = v_coord - uv * u_iCharSize + vec2(u_iCharSize*0.5);\\\\n\\\\\\r\\n\\t\\t\\t\\tuv.y = 1.0 - uv.y;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = vec4(u_color.xyz, u_color.a * texture2D(u_texture, uv, -1.0 ).a);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\t*/\\r\\n\\r\\n\\t//text rendering\\r\\n\\tvar point_text_vertices = new Float32Array( max_characters * 3 );\\r\\n\\tvar point_text_coords = new Float32Array( max_characters * 2 );\\r\\n\\tvar point_text_mesh = new GL.Mesh();\\r\\n\\tvar point_text_vertices_buffer = point_text_mesh.createVertexBuffer(\\\"vertices\\\", null, null, point_text_vertices, gl.STREAM_DRAW );\\r\\n\\tvar point_text_coords_buffer = point_text_mesh.createVertexBuffer(\\\"coords\\\", null, null, point_text_coords, gl.STREAM_DRAW );\\r\\n\\r\\n\\tctx.fillText = ctx.strokeText = function(text,startx,starty)\\r\\n\\t{\\r\\n\\t\\tif(text === null || text === undefined)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(text.constructor !== String)\\r\\n\\t\\t\\ttext = String(text);\\r\\n\\r\\n\\t\\tvar atlas = createFontAtlas.call( this, this._font_family, this._font_mode );\\r\\n\\t\\tvar info = atlas.info;\\r\\n\\r\\n\\t\\tvar points = point_text_vertices;\\r\\n\\t\\tvar coords = point_text_coords;\\r\\n\\t\\tvar point_size = this._font_size * 1.1;\\r\\n\\r\\n\\t\\tif(point_size < 1)\\r\\n\\t\\t\\tpoint_size = 1;\\r\\n\\t\\tvar char_size = info.char_size;\\r\\n\\r\\n\\t\\tvar x = 0;\\r\\n\\t\\tvar y = 0;\\r\\n\\t\\tvar l = text.length;\\r\\n\\t\\tvar spacing = point_size * info.spacing / info.char_size - 1 ;\\r\\n\\t\\tvar kernings = info.kernings;\\r\\n\\t\\tvar scale_factor = info.font_size / this._font_size;\\r\\n\\r\\n\\t\\tvar vertices_index = 0, coords_index = 0;\\r\\n\\t\\t\\r\\n\\t\\tfor(var i = 0; i < l; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar char_code = text.charCodeAt(i);\\r\\n\\t\\t\\tvar c = info[ char_code ]; //info\\r\\n\\t\\t\\tif(!c)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(text.charCodeAt(i) == 10) //break line\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tx = 0;\\r\\n\\t\\t\\t\\t\\ty -= point_size;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tx += point_size * 0.5;\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar kern = kernings[ text[i] ];\\r\\n\\t\\t\\tif(i == 0)\\r\\n\\t\\t\\t\\tx -= point_size * kern[\\\"nwidth\\\"] * 0.25;\\r\\n\\r\\n\\r\\n\\t\\t\\tpoints[vertices_index+0] = startx + x + point_size * 0.5;\\r\\n\\t\\t\\tpoints[vertices_index+1] = starty + y - point_size * 0.25;\\r\\n\\t\\t\\tpoints[vertices_index+2] = 1;\\r\\n\\t\\t\\tvertices_index += 3;\\r\\n\\r\\n\\t\\t\\tcoords[coords_index+0] = c[1];\\r\\n\\t\\t\\tcoords[coords_index+1] = c[2];\\r\\n\\t\\t\\tcoords_index += 2;\\r\\n\\r\\n\\t\\t\\tvar pair_kern = kern[ text[i+1] ];\\r\\n\\t\\t\\tif(!pair_kern)\\r\\n\\t\\t\\t\\tx += point_size * info.space;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tx += point_size * pair_kern;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar offset = 0;\\r\\n\\t\\tif(this.textAlign == \\\"right\\\")\\r\\n\\t\\t\\toffset = x + point_size * 0.5;\\r\\n\\t\\telse if(this.textAlign == \\\"center\\\")\\r\\n\\t\\t\\toffset = x * 0.5;\\r\\n\\t\\tif(offset)\\r\\n\\t\\t\\tfor(var i = 0; i < points.length; i += 3)\\r\\n\\t\\t\\t\\tpoints[i] -= offset;\\r\\n\\t\\t\\r\\n\\t\\t//render\\r\\n\\t\\tatlas.bind(0);\\r\\n\\r\\n\\t\\t//var mesh = GL.Mesh.load({ vertices: points, coords: coords });\\r\\n\\t\\tpoint_text_vertices_buffer.uploadRange(0,vertices_index*4);\\r\\n\\t\\tpoint_text_coords_buffer.uploadRange(0,coords_index*4);\\r\\n\\r\\n\\t\\tuniforms.u_color = this._fillcolor;\\r\\n\\t\\tuniforms.u_pointSize = point_size * vec2.length( this._matrix );\\r\\n\\t\\tuniforms.u_iCharSize = info.char_size / atlas.width;\\r\\n\\t\\tuniforms.u_transform = this._matrix;\\r\\n\\t\\tuniforms.u_viewport = viewport;\\r\\n\\t\\tif(!uniforms.u_angle_sincos)\\r\\n\\t\\t\\tuniforms.u_angle_sincos = vec2.create();\\r\\n\\r\\n\\t\\tuniforms.u_angle_sincos[1] = Math.sin(-global_angle);\\r\\n\\t\\tuniforms.u_angle_sincos[0] = -Math.cos(-global_angle);\\r\\n\\t\\t//uniforms.u_angle_sincos[0] = Math.sin(-global_angle);\\r\\n\\t\\t//uniforms.u_angle_sincos[1] = Math.cos(-global_angle);\\r\\n\\r\\n\\t\\tpoint_text_shader.uniforms(uniforms).drawRange(point_text_mesh, gl.POINTS, 0, vertices_index / 3 );\\r\\n\\r\\n\\t\\treturn { x:x, y:y };\\r\\n\\t}\\r\\n\\r\\n\\tctx.measureText = function(text)\\r\\n\\t{\\r\\n\\t\\tvar atlas = createFontAtlas.call( this, this._font_family, this._font_mode );\\r\\n\\t\\tvar info = atlas.info;\\r\\n\\t\\tvar point_size = Math.ceil( this._font_size * 1.1 );\\r\\n\\t\\tvar spacing = point_size * atlas.info.spacing / atlas.info.char_size - 1 ;\\r\\n\\t\\treturn { width: text.length * spacing, height: point_size };\\r\\n\\t}\\r\\n\\r\\n\\tfunction createFontAtlas( fontname, fontmode, force )\\r\\n\\t{\\r\\n\\t\\tfontname = fontname || \\\"monospace\\\";\\r\\n\\t\\tfontmode = fontmode || \\\"normal\\\";\\r\\n\\r\\n\\t\\tvar now = getTime();\\r\\n\\r\\n\\t\\tvar imageSmoothingEnabled = this.imageSmoothingEnabled;\\r\\n\\t\\tvar useInternationalFont = enableWebGLCanvas.useInternationalFont;\\r\\n\\r\\n\\t\\tvar canvas_size = 1024;\\r\\n\\r\\n\\t\\tvar texture_name = \\\":font_\\\" + fontname + \\\":\\\" + fontmode + \\\":\\\" + useInternationalFont;\\r\\n\\r\\n\\t\\tvar texture = textures[texture_name];\\r\\n\\t\\tif(texture && !force)\\r\\n\\t\\t\\treturn texture;\\r\\n\\r\\n\\t\\tvar max_ascii_code = 200;\\r\\n\\t\\tvar chars_per_row = 10;\\r\\n\\r\\n\\t\\tif(useInternationalFont) //more characters\\r\\n\\t\\t{\\r\\n\\t\\t\\tmax_ascii_code = 400;\\r\\n\\t\\t\\tchars_per_row = 20;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar char_size = (canvas_size / chars_per_row)|0;\\r\\n\\t\\tvar font_size = (char_size * 0.95)|0;\\r\\n\\r\\n\\t\\tvar canvas = createCanvas(canvas_size,canvas_size);\\r\\n\\t\\t//document.body.appendChild(canvas); //debug\\r\\n\\t\\tvar ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tctx.fillStyle = \\\"white\\\";\\r\\n\\t\\tctx.imageSmoothingEnabled = this.imageSmoothingEnabled;\\r\\n\\t\\t//ctx.fillRect(0,0,canvas.width,canvas.height);\\r\\n\\t\\tctx.clearRect(0,0,canvas.width,canvas.height);\\r\\n\\t\\tctx.font = fontmode + \\\" \\\" + font_size + \\\"px \\\" + fontname;\\r\\n\\t\\tctx.textAlign = \\\"center\\\";\\r\\n\\t\\tvar x = 0;\\r\\n\\t\\tvar y = 0;\\r\\n\\t\\tvar xoffset = 0.5, yoffset = font_size * -0.3;\\r\\n\\t\\tvar info = {\\r\\n\\t\\t\\tfont_size: font_size,\\r\\n\\t\\t\\tchar_size: char_size, //in pixels\\r\\n\\t\\t\\tspacing: char_size * 0.6, //in pixels\\r\\n\\t\\t\\tspace: (ctx.measureText(\\\" \\\").width / font_size)\\r\\n\\t\\t};\\r\\n\\t\\t\\r\\n\\t\\tyoffset += enableWebGLCanvas.fontOffsetY * char_size;\\r\\n\\r\\n\\t\\t//compute individual char width (WARNING: measureText is very slow)\\r\\n\\t\\tvar kernings = info.kernings = {};\\r\\n\\t\\tfor(var i = 33; i < max_ascii_code; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar character = String.fromCharCode(i);\\r\\n\\t\\t\\tvar char_width = ctx.measureText(character).width;\\r\\n\\t\\t\\tvar char_info = { width: char_width, nwidth: char_width / font_size };\\r\\n\\t\\t\\tkernings[character] = char_info;\\r\\n\\t\\t}\\r\\n\\t\\t\\r\\n\\t\\tvar clip = true; //clip every character: debug\\r\\n\\r\\n\\t\\t//paint characters in atlas\\r\\n\\t\\tfor(var i = 33; i < max_ascii_code; ++i)//valid characters from 33 to max_ascii_code\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar character = String.fromCharCode(i);\\r\\n\\t\\t\\tvar kerning = kernings[ character ];\\r\\n\\t\\t\\tif( kerning && kerning.width ) //has some visual info\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tinfo[i] = [character, (x + char_size*0.5)/canvas.width, (y + char_size*0.5) / canvas.height];\\r\\n\\t\\t\\t\\tif(clip)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tctx.save();\\r\\n\\t\\t\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\t\\t\\tctx.rect( Math.floor(x)+0.5, Math.floor(y)+0.5, char_size-2, char_size-2 );\\r\\n\\t\\t\\t\\t\\tctx.clip();\\r\\n\\t\\t\\t\\t\\tctx.fillText( character, Math.floor(x+char_size*xoffset), Math.floor(y+char_size+yoffset), char_size );\\r\\n\\t\\t\\t\\t\\tctx.restore();\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tctx.fillText( character, Math.floor(x+char_size*xoffset), Math.floor(y+char_size+yoffset), char_size );\\r\\n\\t\\t\\t\\tx += char_size; //cannot pack chars closer because rendering points, no quads\\r\\n\\t\\t\\t\\tif((x + char_size) > canvas.width)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tx = 0;\\r\\n\\t\\t\\t\\t\\ty += char_size;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif( y + char_size > canvas.height )\\r\\n\\t\\t\\t\\tbreak; //too many characters\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar last_valid_ascii = i; //the last character in the atlas\\r\\n\\r\\n\\t\\t//compute kernings of every char with the rest of chars\\r\\n\\t\\tfor(var i = 33; i < last_valid_ascii; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar character = String.fromCharCode(i);\\r\\n\\t\\t\\tvar kerning = kernings[ character ];\\r\\n\\t\\t\\tvar char_width = kerning.width;\\r\\n\\t\\t\\tfor(var j = 33; j < last_valid_ascii; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar other = String.fromCharCode(j);\\r\\n\\t\\t\\t\\tvar other_width = kernings[other].width; \\r\\n\\t\\t\\t\\tif(!other_width)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tvar total_width = ctx.measureText(character + other).width; //this is painfully slow...\\r\\n\\t\\t\\t\\tkerning[other] = (total_width * 1.45 - char_width - other_width) / font_size;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\ttexture = GL.Texture.fromImage( canvas, { format: gl.ALPHA, magFilter: imageSmoothingEnabled ? gl.LINEAR : gl.NEAREST, minFilter: imageSmoothingEnabled ? gl.NEAREST_MIPMAP_LINEAR : gl.NEAREST, premultiply_alpha: false, anisotropic: 8 } );\\r\\n\\t\\ttexture.info = info; //font generation info\\r\\n\\r\\n\\t\\treturn textures[texture_name] = texture;\\r\\n\\t}\\r\\n\\r\\n\\t//NOT TESTED\\r\\n\\tctx.getImageData = function(x,y,w,h)\\r\\n\\t{\\r\\n\\t\\tvar buffer = new Uint8Array(w*h*4);\\r\\n\\t\\tgl.readPixels(x,y,w,h,gl.RGBA,gl.UNSIGNED_BYTE,buffer);\\r\\n\\t\\treturn { data: buffer, width: w, height: h, resolution: 1 };\\r\\n\\t}\\r\\n\\r\\n\\tctx.putImageData = function( imagedata, x, y )\\r\\n\\t{\\r\\n\\t\\tvar tex = new GL.Texture( imagedata.width, imagedata.height, { filter: gl.NEAREST, pixel_data: imagedata.data } );\\r\\n\\t\\ttex.renderQuad(x,y,tex.width, tex.height);\\r\\n\\t}\\r\\n\\r\\n\\tObject.defineProperty(gl, \\\"fillStyle\\\", {\\r\\n\\t\\tget: function() { return this._fillStyle; },\\r\\n\\t\\tset: function(v) { \\r\\n\\t\\t\\tif(!v)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tthis._fillStyle = v;\\r\\n\\t\\t\\thexColorToRGBA( v, this._fillcolor, this._globalAlpha ); \\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\tObject.defineProperty(gl, \\\"strokeStyle\\\", {\\r\\n\\t\\tget: function() { return this._strokeStyle; },\\r\\n\\t\\tset: function(v) { \\r\\n\\t\\t\\tif(!v)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tthis._strokeStyle = v; \\r\\n\\t\\t\\thexColorToRGBA( v, this._strokecolor, this._globalAlpha );\\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\t//shortcuts\\r\\n\\tObject.defineProperty(gl, \\\"fillColor\\\", {\\r\\n\\t\\tget: function() { return this._fillcolor; },\\r\\n\\t\\tset: function(v) { \\r\\n\\t\\t\\tif(!v)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tthis._fillcolor.set(v);\\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\tObject.defineProperty(gl, \\\"strokeColor\\\", {\\r\\n\\t\\tget: function() { return this._strokecolor; },\\r\\n\\t\\tset: function(v) { \\r\\n\\t\\t\\tif(!v)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tthis._strokecolor.set(v);\\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\tObject.defineProperty(gl, \\\"shadowColor\\\", {\\r\\n\\t\\tget: function() { return this._shadowcolor; },\\r\\n\\t\\tset: function(v) { \\r\\n\\t\\t\\tif(!v)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\thexColorToRGBA( v, this._shadowcolor, this._globalAlpha );\\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\tObject.defineProperty(gl, \\\"globalAlpha\\\", {\\r\\n\\t\\tget: function() { return this._globalAlpha; },\\r\\n\\t\\tset: function(v) { \\r\\n\\t\\t\\tthis._globalAlpha = v; \\r\\n\\t\\t\\tthis._strokecolor[3] = this._fillcolor[3] = v;\\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\tObject.defineProperty(gl, \\\"font\\\", {\\r\\n\\t\\tget: function() { return this._font; },\\r\\n\\t\\tset: function(v) { \\r\\n\\t\\t\\tthis._font = v;\\r\\n\\t\\t\\tvar t = v.split(\\\" \\\");\\r\\n\\t\\t\\tif(t.length == 3)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._font_mode = t[0];\\r\\n\\t\\t\\t\\tthis._font_size = parseFloat(t[1]);\\r\\n\\t\\t\\t\\tif( Number.isNaN( this._font_size ) )\\r\\n\\t\\t\\t\\t\\tthis._font_size = 14;\\r\\n\\t\\t\\t\\tif(this._font_size < 10) \\r\\n\\t\\t\\t\\t\\tthis._font_size = 10;\\r\\n\\t\\t\\t\\tthis._font_family = t[2];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(t.length == 2)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._font_mode = \\\"normal\\\";\\r\\n\\t\\t\\t\\tthis._font_size = parseFloat(t[0]);\\r\\n\\t\\t\\t\\tif( Number.isNaN( this._font_size ) )\\r\\n\\t\\t\\t\\t\\tthis._font_size = 14;\\r\\n\\t\\t\\t\\tif(this._font_size < 10) \\r\\n\\t\\t\\t\\t\\tthis._font_size = 10;\\r\\n\\t\\t\\t\\tthis._font_family = t[1];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._font_mode = \\\"normal\\\";\\r\\n\\t\\t\\t\\tthis._font_family = t[0];\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\tctx._fillcolor = vec4.fromValues(0,0,0,1);\\r\\n\\tctx._strokecolor = vec4.fromValues(0,0,0,1);\\r\\n\\tctx._shadowcolor = vec4.fromValues(0,0,0,0);\\r\\n\\tctx._globalAlpha = 1;\\r\\n\\tctx._font = \\\"14px monospace\\\";\\r\\n\\tctx._font_family = \\\"monospace\\\";\\r\\n\\tctx._font_size = \\\"14px\\\";\\r\\n\\tctx._font_mode = \\\"normal\\\";\\r\\n\\r\\n\\r\\n\\t//STATE\\r\\n\\tctx.strokeStyle = \\\"rgba(0,0,0,1)\\\";\\r\\n\\tctx.fillStyle = \\\"rgba(0,0,0,1)\\\";\\r\\n\\tctx.shadowColor = \\\"transparent\\\";\\r\\n\\tctx.shadowOffsetX = ctx.shadowOffsetY = 0;\\r\\n\\tctx.globalAlpha = 1;\\r\\n\\tctx.setLineWidth = ctx.lineWidth; //save the webgl function\\r\\n\\tctx.lineWidth = 4; //set lineWidth as a number\\r\\n\\tctx.imageSmoothingEnabled = true;\\r\\n\\tctx.tintImages = false; //my own parameter\\r\\n\\r\\n\\r\\n\\t//empty functions: this is used to create null functions in those Canvas2D funcs not implemented here\\r\\n\\tvar names = [\\\"arcTo\\\",\\\"isPointInPath\\\",\\\"createImageData\\\"]; //all functions have been implemented\\r\\n\\tvar null_func = function() {};\\r\\n\\tfor(var i in names)\\r\\n\\t\\tctx[ names[i] ] = null_func;\\r\\n\\r\\n\\treturn ctx;\\r\\n};\\r\\n\\r\\nenableWebGLCanvas.useInternationalFont = false; //render as much characters as possible in the texture atlas\\r\\nenableWebGLCanvas.fontOffsetY = 0; //hack, some fonts need extra offsets, dont know why\"","module.exports = \"/**\\r\\n * @fileoverview gl-matrix - High performance matrix and vector operations\\r\\n * @author Brandon Jones\\r\\n * @author Colin MacKenzie IV\\r\\n * @version 2.2.1\\r\\n */\\r\\n/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved.\\r\\n\\r\\nRedistribution and use in source and binary forms, with or without modification,\\r\\nare permitted provided that the following conditions are met:\\r\\n\\r\\n * Redistributions of source code must retain the above copyright notice, this\\r\\n list of conditions and the following disclaimer.\\r\\n * Redistributions in binary form must reproduce the above copyright notice,\\r\\n this list of conditions and the following disclaimer in the documentation\\r\\n and/or other materials provided with the distribution.\\r\\n\\r\\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \\\"AS IS\\\" AND\\r\\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\\r\\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\\r\\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR\\r\\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\\r\\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\\r\\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\\r\\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\\r\\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\\r\\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */\\r\\n(function(e){\\\"use strict\\\";var t={};typeof exports==\\\"undefined\\\"?typeof define==\\\"function\\\"&&typeof define.amd==\\\"object\\\"&&define.amd?(t.exports={},define(function(){return t.exports})):t.exports=typeof window!=\\\"undefined\\\"?window:e:t.exports=exports,function(e){if(!t)var t=1e-6;if(!n)var n=typeof Float32Array!=\\\"undefined\\\"?Float32Array:Array;if(!r)var r=Math.random;var i={};i.setMatrixArrayType=function(e){n=e},typeof e!=\\\"undefined\\\"&&(e.glMatrix=i);var s=Math.PI/180;i.toRadian=function(e){return e*s};var o={};o.create=function(){var e=new n(2);return e[0]=0,e[1]=0,e},o.clone=function(e){var t=new n(2);return t[0]=e[0],t[1]=e[1],t},o.fromValues=function(e,t){var r=new n(2);return r[0]=e,r[1]=t,r},o.copy=function(e,t){return e[0]=t[0],e[1]=t[1],e},o.set=function(e,t,n){return e[0]=t,e[1]=n,e},o.add=function(e,t,n){return e[0]=t[0]+n[0],e[1]=t[1]+n[1],e},o.subtract=function(e,t,n){return e[0]=t[0]-n[0],e[1]=t[1]-n[1],e},o.sub=o.subtract,o.multiply=function(e,t,n){return e[0]=t[0]*n[0],e[1]=t[1]*n[1],e},o.mul=o.multiply,o.divide=function(e,t,n){return e[0]=t[0]/n[0],e[1]=t[1]/n[1],e},o.div=o.divide,o.min=function(e,t,n){return e[0]=Math.min(t[0],n[0]),e[1]=Math.min(t[1],n[1]),e},o.max=function(e,t,n){return e[0]=Math.max(t[0],n[0]),e[1]=Math.max(t[1],n[1]),e},o.scale=function(e,t,n){return e[0]=t[0]*n,e[1]=t[1]*n,e},o.scaleAndAdd=function(e,t,n,r){return e[0]=t[0]+n[0]*r,e[1]=t[1]+n[1]*r,e},o.distance=function(e,t){var n=t[0]-e[0],r=t[1]-e[1];return Math.sqrt(n*n+r*r)},o.dist=o.distance,o.squaredDistance=function(e,t){var n=t[0]-e[0],r=t[1]-e[1];return n*n+r*r},o.sqrDist=o.squaredDistance,o.length=function(e){var t=e[0],n=e[1];return Math.sqrt(t*t+n*n)},o.len=o.length,o.squaredLength=function(e){var t=e[0],n=e[1];return t*t+n*n},o.sqrLen=o.squaredLength,o.negate=function(e,t){return e[0]=-t[0],e[1]=-t[1],e},o.normalize=function(e,t){var n=t[0],r=t[1],i=n*n+r*r;return i>0&&(i=1/Math.sqrt(i),e[0]=t[0]*i,e[1]=t[1]*i),e},o.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]},o.cross=function(e,t,n){var r=t[0]*n[1]-t[1]*n[0];return e[0]=e[1]=0,e[2]=r,e},o.lerp=function(e,t,n,r){var i=t[0],s=t[1];return e[0]=i+r*(n[0]-i),e[1]=s+r*(n[1]-s),e},o.random=function(e,t){t=t||1;var n=r()*2*Math.PI;return e[0]=Math.cos(n)*t,e[1]=Math.sin(n)*t,e},o.transformMat2=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[2]*i,e[1]=n[1]*r+n[3]*i,e},o.transformMat2d=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[2]*i+n[4],e[1]=n[1]*r+n[3]*i+n[5],e},o.transformMat3=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[3]*i+n[6],e[1]=n[1]*r+n[4]*i+n[7],e},o.transformMat4=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[4]*i+n[12],e[1]=n[1]*r+n[5]*i+n[13],e},o.forEach=function(){var e=o.create();return function(t,n,r,i,s,o){var u,a;n||(n=2),r||(r=0),i?a=Math.min(i*n+r,t.length):a=t.length;for(u=r;u0&&(s=1/Math.sqrt(s),e[0]=t[0]*s,e[1]=t[1]*s,e[2]=t[2]*s),e},u.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]},u.cross=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=n[0],u=n[1],a=n[2];return e[0]=i*a-s*u,e[1]=s*o-r*a,e[2]=r*u-i*o,e},u.lerp=function(e,t,n,r){var i=t[0],s=t[1],o=t[2];return e[0]=i+r*(n[0]-i),e[1]=s+r*(n[1]-s),e[2]=o+r*(n[2]-o),e},u.random=function(e,t){t=t||1;var n=r()*2*Math.PI,i=r()*2-1,s=Math.sqrt(1-i*i)*t;return e[0]=Math.cos(n)*s,e[1]=Math.sin(n)*s,e[2]=i*t,e},u.transformMat4=function(e,t,n){var r=t[0],i=t[1],s=t[2];return e[0]=n[0]*r+n[4]*i+n[8]*s+n[12],e[1]=n[1]*r+n[5]*i+n[9]*s+n[13],e[2]=n[2]*r+n[6]*i+n[10]*s+n[14],e},u.transformMat3=function(e,t,n){var r=t[0],i=t[1],s=t[2];return e[0]=r*n[0]+i*n[3]+s*n[6],e[1]=r*n[1]+i*n[4]+s*n[7],e[2]=r*n[2]+i*n[5]+s*n[8],e},u.transformQuat=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=n[0],u=n[1],a=n[2],f=n[3],l=f*r+u*s-a*i,c=f*i+a*r-o*s,h=f*s+o*i-u*r,p=-o*r-u*i-a*s;return e[0]=l*f+p*-o+c*-a-h*-u,e[1]=c*f+p*-u+h*-o-l*-a,e[2]=h*f+p*-a+l*-u-c*-o,e},u.rotateX=function(e,t,n,r){var i=[],s=[];return i[0]=t[0]-n[0],i[1]=t[1]-n[1],i[2]=t[2]-n[2],s[0]=i[0],s[1]=i[1]*Math.cos(r)-i[2]*Math.sin(r),s[2]=i[1]*Math.sin(r)+i[2]*Math.cos(r),e[0]=s[0]+n[0],e[1]=s[1]+n[1],e[2]=s[2]+n[2],e},u.rotateY=function(e,t,n,r){var i=[],s=[];return i[0]=t[0]-n[0],i[1]=t[1]-n[1],i[2]=t[2]-n[2],s[0]=i[2]*Math.sin(r)+i[0]*Math.cos(r),s[1]=i[1],s[2]=i[2]*Math.cos(r)-i[0]*Math.sin(r),e[0]=s[0]+n[0],e[1]=s[1]+n[1],e[2]=s[2]+n[2],e},u.rotateZ=function(e,t,n,r){var i=[],s=[];return i[0]=t[0]-n[0],i[1]=t[1]-n[1],i[2]=t[2]-n[2],s[0]=i[0]*Math.cos(r)-i[1]*Math.sin(r),s[1]=i[0]*Math.sin(r)+i[1]*Math.cos(r),s[2]=i[2],e[0]=s[0]+n[0],e[1]=s[1]+n[1],e[2]=s[2]+n[2],e},u.forEach=function(){var e=u.create();return function(t,n,r,i,s,o){var u,a;n||(n=3),r||(r=0),i?a=Math.min(i*n+r,t.length):a=t.length;for(u=r;u0&&(o=1/Math.sqrt(o),e[0]=t[0]*o,e[1]=t[1]*o,e[2]=t[2]*o,e[3]=t[3]*o),e},a.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3]},a.lerp=function(e,t,n,r){var i=t[0],s=t[1],o=t[2],u=t[3];return e[0]=i+r*(n[0]-i),e[1]=s+r*(n[1]-s),e[2]=o+r*(n[2]-o),e[3]=u+r*(n[3]-u),e},a.random=function(e,t){return t=t||1,e[0]=r(),e[1]=r(),e[2]=r(),e[3]=r(),a.normalize(e,e),a.scale(e,e,t),e},a.transformMat4=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=t[3];return e[0]=n[0]*r+n[4]*i+n[8]*s+n[12]*o,e[1]=n[1]*r+n[5]*i+n[9]*s+n[13]*o,e[2]=n[2]*r+n[6]*i+n[10]*s+n[14]*o,e[3]=n[3]*r+n[7]*i+n[11]*s+n[15]*o,e},a.transformQuat=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=n[0],u=n[1],a=n[2],f=n[3],l=f*r+u*s-a*i,c=f*i+a*r-o*s,h=f*s+o*i-u*r,p=-o*r-u*i-a*s;return e[0]=l*f+p*-o+c*-a-h*-u,e[1]=c*f+p*-u+h*-o-l*-a,e[2]=h*f+p*-a+l*-u-c*-o,e},a.forEach=function(){var e=a.create();return function(t,n,r,i,s,o){var u,a;n||(n=4),r||(r=0),i?a=Math.min(i*n+r,t.length):a=t.length;for(u=r;u.999999?(r[0]=0,r[1]=0,r[2]=0,r[3]=1,r):(u.cross(e,i,s),r[0]=e[0],r[1]=e[1],r[2]=e[2],r[3]=1+o,p.normalize(r,r))}}(),p.setAxes=function(){var e=c.create();return function(t,n,r,i){return e[0]=r[0],e[3]=r[1],e[6]=r[2],e[1]=i[0],e[4]=i[1],e[7]=i[2],e[2]=-n[0],e[5]=-n[1],e[8]=-n[2],p.normalize(t,p.fromMat3(t,e))}}(),p.clone=a.clone,p.fromValues=a.fromValues,p.copy=a.copy,p.set=a.set,p.identity=function(e){return e[0]=0,e[1]=0,e[2]=0,e[3]=1,e},p.setAxisAngle=function(e,t,n){n*=.5;var r=Math.sin(n);return e[0]=r*t[0],e[1]=r*t[1],e[2]=r*t[2],e[3]=Math.cos(n),e},p.add=a.add,p.multiply=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=t[3],u=n[0],a=n[1],f=n[2],l=n[3];return e[0]=r*l+o*u+i*f-s*a,e[1]=i*l+o*a+s*u-r*f,e[2]=s*l+o*f+r*a-i*u,e[3]=o*l-r*u-i*a-s*f,e},p.mul=p.multiply,p.scale=a.scale,p.rotateX=function(e,t,n){n*=.5;var r=t[0],i=t[1],s=t[2],o=t[3],u=Math.sin(n),a=Math.cos(n);return e[0]=r*a+o*u,e[1]=i*a+s*u,e[2]=s*a-i*u,e[3]=o*a-r*u,e},p.rotateY=function(e,t,n){n*=.5;var r=t[0],i=t[1],s=t[2],o=t[3],u=Math.sin(n),a=Math.cos(n);return e[0]=r*a-s*u,e[1]=i*a+o*u,e[2]=s*a+r*u,e[3]=o*a-i*u,e},p.rotateZ=function(e,t,n){n*=.5;var r=t[0],i=t[1],s=t[2],o=t[3],u=Math.sin(n),a=Math.cos(n);return e[0]=r*a+i*u,e[1]=i*a-r*u,e[2]=s*a+o*u,e[3]=o*a-s*u,e},p.calculateW=function(e,t){var n=t[0],r=t[1],i=t[2];return e[0]=n,e[1]=r,e[2]=i,e[3]=-Math.sqrt(Math.abs(1-n*n-r*r-i*i)),e},p.dot=a.dot,p.lerp=a.lerp,p.slerp=function(e,t,n,r){var i=t[0],s=t[1],o=t[2],u=t[3],a=n[0],f=n[1],l=n[2],c=n[3],h,p,d,v,m;return p=i*a+s*f+o*l+u*c,p<0&&(p=-p,a=-a,f=-f,l=-l,c=-c),1-p>1e-6?(h=Math.acos(p),d=Math.sin(h),v=Math.sin((1-r)*h)/d,m=Math.sin(r*h)/d):(v=1-r,m=r),e[0]=v*i+m*a,e[1]=v*s+m*f,e[2]=v*o+m*l,e[3]=v*u+m*c,e},p.invert=function(e,t){var n=t[0],r=t[1],i=t[2],s=t[3],o=n*n+r*r+i*i+s*s,u=o?1/o:0;return e[0]=-n*u,e[1]=-r*u,e[2]=-i*u,e[3]=s*u,e},p.conjugate=function(e,t){return e[0]=-t[0],e[1]=-t[1],e[2]=-t[2],e[3]=t[3],e},p.length=a.length,p.len=p.length,p.squaredLength=a.squaredLength,p.sqrLen=p.squaredLength,p.normalize=a.normalize,p.fromMat3=function(e,t){var n=t[0]+t[4]+t[8],r;if(n>0)r=Math.sqrt(n+1),e[3]=.5*r,r=.5/r,e[0]=(t[7]-t[5])*r,e[1]=(t[2]-t[6])*r,e[2]=(t[3]-t[1])*r;else{var i=0;t[4]>t[0]&&(i=1),t[8]>t[i*3+i]&&(i=2);var s=(i+1)%3,o=(i+2)%3;r=Math.sqrt(t[i*3+i]-t[s*3+s]-t[o*3+o]+1),e[i]=.5*r,r=.5/r,e[3]=(t[o*3+s]-t[s*3+o])*r,e[s]=(t[s*3+i]+t[i*3+s])*r,e[o]=(t[o*3+i]+t[i*3+o])*r}return e},p.str=function(e){return\\\"quat(\\\"+e[0]+\\\", \\\"+e[1]+\\\", \\\"+e[2]+\\\", \\\"+e[3]+\\\")\\\"},typeof e!=\\\"undefined\\\"&&(e.quat=p)}(t.exports)})(this);\\r\\n\"","module.exports = \"//packer version\\r\\n//litegl.js by Javi Agenjo 2014 @tamat (tamats.com)\\r\\n//forked from lightgl.js by Evan Wallace (madebyevan.com)\\r\\n\\\"use strict\\\";\\r\\n\\r\\n(function(global){\\r\\n\\r\\nvar GL = global.GL = {};\\r\\n\\r\\nif(typeof(glMatrix) == \\\"undefined\\\")\\r\\n\\tthrow(\\\"litegl.js requires gl-matrix to work. It must be included before litegl.\\\");\\r\\n\\r\\n//polyfill\\r\\nglobal.requestAnimationFrame = global.requestAnimationFrame || global.mozRequestAnimationFrame || global.webkitRequestAnimationFrame || function(callback) { setTimeout(callback, 1000 / 60); };\\r\\n\\r\\nGL.blockable_keys = {\\\"Up\\\":true,\\\"Down\\\":true,\\\"Left\\\":true,\\\"Right\\\":true};\\r\\n\\r\\nGL.reverse = null;\\r\\n\\r\\n//some consts\\r\\nGL.LEFT_MOUSE_BUTTON = 1;\\r\\nGL.MIDDLE_MOUSE_BUTTON = 2;\\r\\nGL.RIGHT_MOUSE_BUTTON = 3;\\r\\nGL.last_context_id = 0;\\r\\n\\r\\n\\r\\n//Define WEBGL ENUMS as statics (more to come in WebGL 2)\\r\\n//sometimes we need some gl enums before having the gl context, solution: define them globally because the specs says they are constant)\\r\\n\\r\\nGL.COLOR_BUFFER_BIT = 16384;\\r\\nGL.DEPTH_BUFFER_BIT = 256;\\r\\nGL.STENCIL_BUFFER_BIT = 1024;\\r\\n\\r\\nGL.TEXTURE_2D = 3553;\\r\\nGL.TEXTURE_CUBE_MAP = 34067;\\r\\nGL.TEXTURE_3D = 32879;\\r\\n\\r\\nGL.TEXTURE_MAG_FILTER = 10240;\\r\\nGL.TEXTURE_MIN_FILTER = 10241;\\r\\nGL.TEXTURE_WRAP_S = 10242;\\r\\nGL.TEXTURE_WRAP_T = 10243;\\r\\n\\r\\nGL.BYTE = 5120;\\r\\nGL.UNSIGNED_BYTE = 5121;\\r\\nGL.SHORT = 5122;\\r\\nGL.UNSIGNED_SHORT = 5123;\\r\\nGL.INT = 5124;\\r\\nGL.UNSIGNED_INT = 5125;\\r\\nGL.FLOAT = 5126;\\r\\nGL.HALF_FLOAT_OES = 36193; //webgl 1.0 only\\r\\n\\r\\n//webgl2 formats\\r\\nGL.HALF_FLOAT = 5131; \\r\\nGL.DEPTH_COMPONENT16 = 33189;\\r\\nGL.DEPTH_COMPONENT24 = 33190;\\r\\nGL.DEPTH_COMPONENT32F = 36012;\\r\\n\\r\\nGL.FLOAT_VEC2 = 35664;\\r\\nGL.FLOAT_VEC3 = 35665;\\r\\nGL.FLOAT_VEC4 = 35666;\\r\\nGL.INT_VEC2 = 35667;\\r\\nGL.INT_VEC3 = 35668;\\r\\nGL.INT_VEC4 = 35669;\\r\\nGL.BOOL = 35670;\\r\\nGL.BOOL_VEC2 = 35671;\\r\\nGL.BOOL_VEC3 = 35672;\\r\\nGL.BOOL_VEC4 = 35673;\\r\\nGL.FLOAT_MAT2 = 35674;\\r\\nGL.FLOAT_MAT3 = 35675;\\r\\nGL.FLOAT_MAT4 = 35676;\\r\\n\\r\\n//used to know the amount of data to reserve per uniform\\r\\nGL.TYPE_LENGTH = {};\\r\\nGL.TYPE_LENGTH[ GL.FLOAT ] = GL.TYPE_LENGTH[ GL.INT ] = GL.TYPE_LENGTH[ GL.BYTE ] = GL.TYPE_LENGTH[ GL.BOOL ] = 1;\\r\\nGL.TYPE_LENGTH[ GL.FLOAT_VEC2 ] = GL.TYPE_LENGTH[ GL.INT_VEC2 ] = GL.TYPE_LENGTH[ GL.BOOL_VEC2 ] = 2;\\r\\nGL.TYPE_LENGTH[ GL.FLOAT_VEC3 ] = GL.TYPE_LENGTH[ GL.INT_VEC3 ] = GL.TYPE_LENGTH[ GL.BOOL_VEC3 ] = 3;\\r\\nGL.TYPE_LENGTH[ GL.FLOAT_VEC4 ] = GL.TYPE_LENGTH[ GL.INT_VEC4 ] = GL.TYPE_LENGTH[ GL.BOOL_VEC4 ] = 4;\\r\\nGL.TYPE_LENGTH[ GL.FLOAT_MAT3 ] = 9;\\r\\nGL.TYPE_LENGTH[ GL.FLOAT_MAT4 ] = 16;\\r\\n\\r\\n\\r\\nGL.SAMPLER_2D = 35678;\\r\\nGL.SAMPLER_3D = 35679;\\r\\nGL.SAMPLER_CUBE = 35680;\\r\\n\\r\\nGL.DEPTH_COMPONENT = 6402;\\r\\nGL.ALPHA = 6406;\\r\\nGL.RGB = 6407;\\r\\nGL.RGBA = 6408;\\r\\nGL.LUMINANCE = 6409;\\r\\nGL.LUMINANCE_ALPHA = 6410;\\r\\nGL.DEPTH_STENCIL = 34041;\\r\\nGL.UNSIGNED_INT_24_8_WEBGL = 34042;\\r\\n\\r\\n//webgl2 formats\\r\\nGL.R8 = 33321;\\r\\nGL.R16F = 33325;\\r\\nGL.R32F = 33326;\\r\\nGL.R8UI = 33330;\\r\\nGL.RG8 = 33323;\\r\\nGL.RG16F = 33327;\\r\\nGL.RG32F = 33328;\\r\\nGL.RGB8 = 32849;\\r\\nGL.SRGB8 = 35905;\\r\\nGL.RGB565 = 36194;\\r\\nGL.R11F_G11F_B10F = 35898;\\r\\nGL.RGB9_E5 = 35901;\\r\\nGL.RGB16F = 34843;\\r\\nGL.RGB32F = 34837;\\r\\nGL.RGB8UI = 36221;\\r\\nGL.RGBA8 = 32856;\\r\\nGL.RGB5_A1 = 32855;\\r\\nGL.RGBA16F = 34842;\\r\\nGL.RGBA32F = 34836;\\r\\nGL.RGBA8UI = 36220;\\r\\nGL.RGBA16I = 36232;\\r\\nGL.RGBA16UI = 36214;\\r\\nGL.RGBA32I = 36226;\\r\\nGL.RGBA32UI = 36208;\\r\\n\\r\\nGL.NEAREST = 9728;\\r\\nGL.LINEAR = 9729;\\r\\nGL.NEAREST_MIPMAP_NEAREST = 9984;\\r\\nGL.LINEAR_MIPMAP_NEAREST = 9985;\\r\\nGL.NEAREST_MIPMAP_LINEAR = 9986;\\r\\nGL.LINEAR_MIPMAP_LINEAR = 9987;\\r\\n\\r\\nGL.REPEAT = 10497;\\r\\nGL.CLAMP_TO_EDGE = 33071;\\r\\nGL.MIRRORED_REPEAT = 33648;\\r\\n\\r\\nGL.ZERO = 0;\\r\\nGL.ONE = 1;\\r\\nGL.SRC_COLOR = 768;\\r\\nGL.ONE_MINUS_SRC_COLOR = 769;\\r\\nGL.SRC_ALPHA = 770;\\r\\nGL.ONE_MINUS_SRC_ALPHA = 771;\\r\\nGL.DST_ALPHA = 772;\\r\\nGL.ONE_MINUS_DST_ALPHA = 773;\\r\\nGL.DST_COLOR = 774;\\r\\nGL.ONE_MINUS_DST_COLOR = 775;\\r\\nGL.SRC_ALPHA_SATURATE = 776;\\r\\nGL.CONSTANT_COLOR = 32769;\\r\\nGL.ONE_MINUS_CONSTANT_COLOR = 32770;\\r\\nGL.CONSTANT_ALPHA = 32771;\\r\\nGL.ONE_MINUS_CONSTANT_ALPHA = 32772;\\r\\n\\r\\nGL.VERTEX_SHADER = 35633;\\r\\nGL.FRAGMENT_SHADER = 35632;\\r\\n\\r\\nGL.FRONT = 1028;\\r\\nGL.BACK = 1029;\\r\\nGL.FRONT_AND_BACK = 1032;\\r\\n\\r\\nGL.NEVER = 512;\\r\\nGL.LESS = 513;\\r\\nGL.EQUAL = 514;\\r\\nGL.LEQUAL = 515;\\r\\nGL.GREATER = 516;\\r\\nGL.NOTEQUAL = 517;\\r\\nGL.GEQUAL = 518;\\r\\nGL.ALWAYS = 519;\\r\\n\\r\\nGL.KEEP = 7680;\\r\\nGL.REPLACE = 7681;\\r\\nGL.INCR = 7682;\\r\\nGL.DECR = 7683;\\r\\nGL.INCR_WRAP = 34055;\\r\\nGL.DECR_WRAP = 34056;\\r\\nGL.INVERT = 5386;\\r\\n\\r\\nGL.STREAM_DRAW = 35040;\\r\\nGL.STATIC_DRAW = 35044;\\r\\nGL.DYNAMIC_DRAW = 35048;\\r\\n\\r\\nGL.ARRAY_BUFFER = 34962;\\r\\nGL.ELEMENT_ARRAY_BUFFER = 34963;\\r\\n\\r\\nGL.POINTS = 0;\\r\\nGL.LINES = 1;\\r\\nGL.LINE_LOOP = 2;\\r\\nGL.LINE_STRIP = 3;\\r\\nGL.TRIANGLES = 4;\\r\\nGL.TRIANGLE_STRIP = 5;\\r\\nGL.TRIANGLE_FAN = 6;\\r\\n\\r\\nGL.CW = 2304;\\r\\nGL.CCW = 2305;\\r\\n\\r\\nGL.CULL_FACE = 2884;\\r\\nGL.DEPTH_TEST = 2929;\\r\\nGL.BLEND = 3042;\\r\\n\\r\\nGL.temp_vec3 = vec3.create();\\r\\nGL.temp2_vec3 = vec3.create();\\r\\nGL.temp_vec4 = vec4.create();\\r\\nGL.temp_quat = quat.create();\\r\\nGL.temp_mat3 = mat3.create();\\r\\nGL.temp_mat4 = mat4.create();\\r\\n\\r\\n\\r\\nglobal.DEG2RAD = 0.0174532925;\\r\\nglobal.RAD2DEG = 57.295779578552306;\\r\\nglobal.EPSILON = 0.000001;\\r\\n\\r\\n/**\\r\\n* Tells if one number is power of two (used for textures)\\r\\n* @method isPowerOfTwo\\r\\n* @param {v} number\\r\\n* @return {boolean}\\r\\n*/\\r\\nglobal.isPowerOfTwo = GL.isPowerOfTwo = function isPowerOfTwo(v)\\r\\n{\\r\\n\\treturn ((Math.log(v) / Math.log(2)) % 1) == 0;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Tells if one number is power of two (used for textures)\\r\\n* @method isPowerOfTwo\\r\\n* @param {v} number\\r\\n* @return {boolean}\\r\\n*/\\r\\nglobal.nearestPowerOfTwo = GL.nearestPowerOfTwo = function nearestPowerOfTwo(v)\\r\\n{\\r\\n\\treturn Math.pow(2, Math.round( Math.log( v ) / Math.log(2) ) )\\r\\n}\\r\\n\\r\\n\\r\\n//Global Scope\\r\\n//better array conversion to string for serializing\\r\\nvar typed_arrays = [ Uint8Array, Int8Array, Uint16Array, Int16Array, Uint32Array, Int32Array, Float32Array, Float64Array ];\\r\\nfunction typedToArray(){ \\r\\n\\treturn Array.prototype.slice.call(this);\\r\\n}\\r\\ntyped_arrays.forEach( function(v) { \\r\\n\\tif(!v.prototype.toJSON)\\r\\n\\t\\tObject.defineProperty( v.prototype, \\\"toJSON\\\", {\\r\\n\\t\\t\\tvalue: typedToArray,\\r\\n\\t\\t\\tenumerable: false\\r\\n\\t\\t});\\r\\n});\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Get current time in milliseconds\\r\\n* @method getTime\\r\\n* @return {number}\\r\\n*/\\r\\nif(typeof(performance) != \\\"undefined\\\")\\r\\n global.getTime = performance.now.bind(performance);\\r\\nelse\\r\\n global.getTime = Date.now.bind( Date );\\r\\nGL.getTime = global.getTime;\\r\\n\\r\\n\\r\\nglobal.isFunction = function isFunction(obj) {\\r\\n return !!(obj && obj.constructor && obj.call && obj.apply);\\r\\n}\\r\\n\\r\\nglobal.isArray = function isArray(obj) {\\r\\n return (obj && obj.constructor === Array );\\r\\n //var str = Object.prototype.toString.call(obj);\\r\\n //return str == '[object Array]' || str == '[object Float32Array]';\\r\\n}\\r\\n\\r\\nglobal.isNumber = function isNumber(obj) {\\r\\n return (obj != null && obj.constructor === Number );\\r\\n}\\r\\n\\r\\nglobal.getClassName = function getClassName(obj)\\r\\n{\\r\\n\\tif (!obj)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//from function info, but not standard\\r\\n\\tif(obj.name)\\r\\n\\t\\treturn obj.name;\\r\\n\\r\\n\\t//from sourcecode\\r\\n\\tif(obj.toString) {\\r\\n\\t\\tvar arr = obj.toString().match(\\r\\n\\t\\t\\t/function\\\\s*(\\\\w+)/);\\r\\n\\t\\tif (arr && arr.length == 2) {\\r\\n\\t\\t\\treturn arr[1];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* clone one object recursively, only allows objects containing number,strings,typed-arrays or other objects\\r\\n* @method cloneObject\\r\\n* @param {Object} object \\r\\n* @param {Object} target if omited an empty object is created\\r\\n* @return {Object}\\r\\n*/\\r\\nglobal.cloneObject = GL.cloneObject = function(o, t)\\r\\n{\\r\\n\\tif(o.constructor !== Object)\\r\\n\\t\\tthrow(\\\"cloneObject only can clone pure javascript objects, not classes\\\");\\r\\n\\r\\n\\tt = t || {};\\r\\n\\r\\n\\tfor(var i in o)\\r\\n\\t{\\r\\n\\t\\tvar v = o[i];\\r\\n\\t\\tif(v === null)\\r\\n\\t\\t{\\r\\n\\t\\t\\tt[i] = null;\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tswitch(v.constructor)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase Int8Array:\\r\\n\\t\\t\\tcase Uint8Array:\\r\\n\\t\\t\\tcase Int16Array:\\r\\n\\t\\t\\tcase Uint16Array:\\r\\n\\t\\t\\tcase Int32Array:\\r\\n\\t\\t\\tcase Uint32Array:\\r\\n\\t\\t\\tcase Float32Array:\\r\\n\\t\\t\\tcase Float64Array:\\r\\n\\t\\t\\t\\tt[i] = new v.constructor(v);\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase Boolean:\\r\\n\\t\\t\\tcase Number:\\r\\n\\t\\t\\tcase String:\\r\\n\\t\\t\\t\\tt[i] = v;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase Array:\\r\\n\\t\\t\\t\\tt[i] = v.concat(); //content is not cloned\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase Object:\\r\\n\\t\\t\\t\\tt[i] = GL.cloneObject(v);\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn t;\\r\\n}\\r\\n\\r\\n\\r\\n/* SLOW because accepts booleans\\r\\nfunction isNumber(obj) {\\r\\n var str = Object.prototype.toString.call(obj);\\r\\n return str == '[object Number]' || str == '[object Boolean]';\\r\\n}\\r\\n*/\\r\\n\\r\\n//given a regular expression, a text and a callback, it calls the function every time it finds it\\r\\nglobal.regexMap = function regexMap(regex, text, callback) {\\r\\n var result;\\r\\n while ((result = regex.exec(text)) != null) {\\r\\n callback(result);\\r\\n }\\r\\n}\\r\\n\\r\\nglobal.createCanvas = GL.createCanvas = function createCanvas(width, height) {\\r\\n var canvas = document.createElement('canvas');\\r\\n canvas.width = width;\\r\\n canvas.height = height;\\r\\n return canvas;\\r\\n}\\r\\n\\r\\nglobal.cloneCanvas = GL.cloneCanvas = function cloneCanvas(c) {\\r\\n var canvas = document.createElement('canvas');\\r\\n canvas.width = c.width;\\r\\n canvas.height = c.height;\\r\\n var ctx = canvas.getContext(\\\"2d\\\");\\r\\n ctx.drawImage(c,0,0);\\r\\n return canvas;\\r\\n}\\r\\n\\r\\nif(typeof(Image) != \\\"undefined\\\") //not existing inside workers\\r\\n{\\r\\n\\tImage.prototype.getPixels = function()\\r\\n\\t{\\r\\n\\t\\tvar canvas = document.createElement('canvas');\\r\\n\\t\\tcanvas.width = this.width;\\r\\n\\t\\tcanvas.height = this.height;\\r\\n\\t\\tvar ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tctx.drawImage(this,0,0);\\r\\n\\t\\treturn ctx.getImageData(0, 0, this.width, this.height).data;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//you must pass an object with characters to replace and replace with what {\\\"a\\\":\\\"A\\\",\\\"c\\\":\\\"C\\\"}\\r\\nif(!String.prototype.hasOwnProperty(\\\"replaceAll\\\")) \\r\\n\\tObject.defineProperty(String.prototype, \\\"replaceAll\\\", {\\r\\n\\t\\tvalue: function(words){\\r\\n\\t\\t\\tvar str = this;\\r\\n\\t\\t\\tfor(var i in words)\\r\\n\\t\\t\\t\\tstr = str.split(i).join(words[i]);\\r\\n\\t\\t\\treturn str;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false\\r\\n\\t});\\t\\r\\n\\r\\n/*\\r\\nString.prototype.replaceAll = function(words){\\r\\n\\tvar str = this;\\r\\n\\tfor(var i in words)\\r\\n\\t\\tstr = str.split(i).join(words[i]);\\r\\n return str;\\r\\n};\\r\\n*/\\r\\n\\r\\n//used for hashing keys\\r\\nif(!String.prototype.hasOwnProperty(\\\"hashCode\\\")) \\r\\n\\tObject.defineProperty(String.prototype, \\\"hashCode\\\", {\\r\\n\\t\\tvalue: function(){\\r\\n\\t\\t\\tvar hash = 0, i, c, l;\\r\\n\\t\\t\\tif (this.length == 0) return hash;\\r\\n\\t\\t\\tfor (i = 0, l = this.length; i < l; ++i) {\\r\\n\\t\\t\\t\\tc = this.charCodeAt(i);\\r\\n\\t\\t\\t\\thash = ((hash<<5)-hash)+c;\\r\\n\\t\\t\\t\\thash |= 0; // Convert to 32bit integer\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn hash;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false\\r\\n\\t});\\t\\r\\n\\r\\n//avoid errors when Typed array is expected and regular array is found\\r\\n//Array.prototype.subarray = Array.prototype.slice;\\r\\n//if(!Array.prototype.hasOwnProperty(\\\"subarray\\\"))\\r\\n//\\tObject.defineProperty(Array.prototype, \\\"subarray\\\", { value: Array.prototype.slice, enumerable: false });\\r\\n\\r\\nif(!Array.prototype.hasOwnProperty(\\\"clone\\\"))\\r\\n\\tObject.defineProperty(Array.prototype, \\\"clone\\\", { value: Array.prototype.concat, enumerable: false });\\r\\nif(!Float32Array.prototype.hasOwnProperty(\\\"clone\\\"))\\r\\n\\tObject.defineProperty(Float32Array.prototype, \\\"clone\\\", { value: function() { return new Float32Array(this); }, enumerable: false });\\r\\n\\r\\n\\r\\n// remove all properties on obj, effectively reverting it to a new object (to reduce garbage)\\r\\nglobal.wipeObject = function wipeObject(obj)\\r\\n{\\r\\n for (var p in obj)\\r\\n {\\r\\n if (obj.hasOwnProperty(p))\\r\\n delete obj[p];\\r\\n }\\r\\n};\\r\\n\\r\\n//copy methods from origin to target\\r\\nglobal.extendClass = GL.extendClass = function extendClass( target, origin ) {\\r\\n\\tfor(var i in origin) //copy class properties\\r\\n\\t{\\r\\n\\t\\tif(target.hasOwnProperty(i))\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\ttarget[i] = origin[i];\\r\\n\\t}\\r\\n\\r\\n\\tif(origin.prototype) //copy prototype properties\\r\\n\\t{\\r\\n\\t\\tvar prop_names = Object.getOwnPropertyNames( origin.prototype );\\r\\n\\t\\tfor(var i = 0; i < prop_names.length; ++i) //only enumerables\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar name = prop_names[i];\\r\\n\\t\\t\\t//if(!origin.prototype.hasOwnProperty(name)) \\r\\n\\t\\t\\t//\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(target.prototype.hasOwnProperty(name)) //avoid overwritting existing ones\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//copy getters \\r\\n\\t\\t\\tif(origin.prototype.__lookupGetter__(name))\\r\\n\\t\\t\\t\\ttarget.prototype.__defineGetter__(name, origin.prototype.__lookupGetter__(name));\\r\\n\\t\\t\\telse \\r\\n\\t\\t\\t\\ttarget.prototype[name] = origin.prototype[name];\\r\\n\\r\\n\\t\\t\\t//and setters\\r\\n\\t\\t\\tif(origin.prototype.__lookupSetter__(name))\\r\\n\\t\\t\\t\\ttarget.prototype.__defineSetter__(name, origin.prototype.__lookupSetter__(name));\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif(!target.hasOwnProperty(\\\"superclass\\\")) \\r\\n\\t\\tObject.defineProperty(target, \\\"superclass\\\", {\\r\\n\\t\\t\\tget: function() { return origin },\\r\\n\\t\\t\\tenumerable: false\\r\\n\\t\\t});\\t\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n//simple http request\\r\\nglobal.HttpRequest = GL.request = function HttpRequest( url, params, callback, error, options )\\r\\n{\\r\\n\\tvar async = true;\\r\\n\\tif(options && options.async !== undefined)\\r\\n\\t\\tasync = options.async;\\r\\n\\r\\n\\tif(params)\\r\\n\\t{\\r\\n\\t\\tvar params_str = null;\\r\\n\\t\\tvar params_arr = [];\\r\\n\\t\\tfor(var i in params)\\r\\n\\t\\t\\tparams_arr.push(i + \\\"=\\\" + params[i]);\\r\\n\\t\\tparams_str = params_arr.join(\\\"&\\\");\\r\\n\\t\\turl = url + \\\"?\\\" + params_str;\\r\\n\\t}\\r\\n\\r\\n\\tvar xhr = new XMLHttpRequest();\\r\\n\\txhr.open('GET', url, async);\\r\\n\\txhr.onload = function(e)\\r\\n\\t{\\r\\n\\t\\tvar response = this.response;\\r\\n\\t\\tvar type = this.getResponseHeader(\\\"Content-Type\\\");\\r\\n\\t\\tif(this.status != 200)\\r\\n\\t\\t{\\r\\n\\t\\t\\tLEvent.trigger(xhr,\\\"fail\\\",this.status);\\r\\n\\t\\t\\tif(error)\\r\\n\\t\\t\\t\\terror(this.status);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLEvent.trigger(xhr,\\\"done\\\",this.response);\\r\\n\\t\\tif(callback)\\r\\n\\t\\t\\tcallback(this.response);\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\txhr.onerror = function(err)\\r\\n\\t{\\r\\n\\t\\tLEvent.trigger(xhr,\\\"fail\\\",err);\\r\\n\\t}\\r\\n\\t\\r\\n\\tif(options)\\r\\n\\t{\\r\\n\\t\\tfor(var i in options)\\r\\n\\t\\t\\txhr[i] = options[i];\\r\\n\\t\\tif(options.binary)\\r\\n\\t\\t\\txhr.responseType = \\\"arraybuffer\\\";\\r\\n\\t}\\r\\n\\r\\n\\txhr.send();\\r\\n\\r\\n\\treturn xhr;\\r\\n}\\r\\n\\r\\n//cheap simple promises\\r\\nif( global.XMLHttpRequest )\\r\\n{\\r\\n\\tif( !XMLHttpRequest.prototype.hasOwnProperty(\\\"done\\\") )\\r\\n\\t\\tObject.defineProperty( XMLHttpRequest.prototype, \\\"done\\\", { enumerable: false, value: function(callback)\\r\\n\\t\\t{\\r\\n\\t\\t LEvent.bind(this,\\\"done\\\", function(e,err) { callback(err); } );\\r\\n\\t\\t return this;\\r\\n\\t\\t}});\\r\\n\\r\\n\\tif( !XMLHttpRequest.prototype.hasOwnProperty(\\\"fail\\\") )\\r\\n\\t\\tObject.defineProperty( XMLHttpRequest.prototype, \\\"fail\\\", { enumerable: false, value: function(callback)\\r\\n\\t\\t{\\r\\n\\t\\t LEvent.bind(this,\\\"fail\\\", function(e,err) { callback(err); } );\\r\\n\\t\\t return this;\\r\\n\\t\\t}});\\r\\n}\\r\\n\\r\\nglobal.getFileExtension = function getFileExtension(url)\\r\\n{\\r\\n\\tvar question = url.indexOf(\\\"?\\\");\\r\\n\\tif(question != -1)\\r\\n\\t\\turl = url.substr(0,question);\\r\\n\\tvar point = url.lastIndexOf(\\\".\\\");\\r\\n\\tif(point == -1) \\r\\n\\t\\treturn \\\"\\\";\\r\\n\\treturn url.substr(point+1).toLowerCase();\\r\\n} \\r\\n\\r\\n\\r\\n//allows to pack several (text)files inside one single file (useful for shaders)\\r\\n//every file must start with \\\\filename.ext or /filename.ext\\r\\nglobal.loadFileAtlas = GL.loadFileAtlas = function loadFileAtlas(url, callback, sync)\\r\\n{\\r\\n\\tvar deferred_callback = null;\\r\\n\\r\\n\\tHttpRequest(url, null, function(data) {\\r\\n\\t\\tvar files = GL.processFileAtlas(data); \\r\\n\\t\\tif(callback)\\r\\n\\t\\t\\tcallback(files);\\r\\n\\t\\tif(deferred_callback)\\r\\n\\t\\t\\tdeferred_callback(files);\\r\\n\\t}, alert, sync);\\r\\n\\r\\n\\treturn { done: function(callback) { deferred_callback = callback; } };\\r\\n}\\r\\n\\r\\n//This parses a text file that contains several text files (they are separated by \\\"\\\\filename\\\"), and returns an object with every file separatly\\r\\nglobal.processFileAtlas = GL.processFileAtlas = function(data, skip_trim)\\r\\n{\\r\\n\\tvar lines = data.split(\\\"\\\\n\\\");\\r\\n\\tvar files = {};\\r\\n\\r\\n\\tvar current_file_lines = [];\\r\\n\\tvar current_file_name = \\\"\\\";\\r\\n\\tfor(var i = 0, l = lines.length; i < l; i++)\\r\\n\\t{\\r\\n\\t\\tvar line = skip_trim ? lines[i] : lines[i].trim();\\r\\n\\t\\tif(!line.length)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif( line[0] != \\\"\\\\\\\\\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tcurrent_file_lines.push(line);\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( current_file_lines.length )\\r\\n\\t\\t\\tfiles[ current_file_name ] = current_file_lines.join(\\\"\\\\n\\\");\\r\\n\\t\\tcurrent_file_lines.length = 0;\\r\\n\\t\\tcurrent_file_name = line.substr(1);\\r\\n\\t}\\r\\n\\r\\n\\tif( current_file_lines.length )\\r\\n\\t\\tfiles[ current_file_name ] = current_file_lines.join(\\\"\\\\n\\\");\\r\\n\\r\\n\\treturn files;\\r\\n}\\r\\n\\r\\n\\r\\n/*\\r\\nglobal.halfFloatToFloat = function( h )\\r\\n{\\r\\n\\tfunction convertMantissa(i) {\\r\\n\\t if (i == 0) \\r\\n\\t\\t\\treturn 0\\r\\n\\t\\telse if (i < 1024)\\r\\n\\t\\t{\\r\\n\\t var m = i << 13;\\r\\n\\t\\t\\tvar e = 0;\\r\\n\\t\\t\\twhile (!(m & 0x00800000))\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\te -= 0x00800000\\r\\n\\t\\t\\t\\tm = m << 1\\r\\n\\t\\t\\t}\\r\\n\\t m &= ~0x00800000\\r\\n\\t\\t e += 0x38800000\\r\\n\\t return m | e;\\r\\n\\t\\t}\\r\\n\\t\\treturn 0x38000000 + ((i - 1024) << 13);\\r\\n\\t}\\r\\n\\r\\n\\tfunction convertExponent(i)\\t{\\r\\n\\t\\tif (i == 0)\\r\\n\\t\\t\\treturn 0;\\r\\n\\t\\telse if (i >= 1 && i <= 31)\\r\\n\\t\\t\\treturn i << 23;\\r\\n\\t\\telse if (i == 31)\\r\\n\\t\\t\\treturn 0x47800000;\\r\\n\\t\\telse if (i == 32)\\r\\n\\t\\t\\treturn 0x80000000;\\r\\n\\t\\telse if (i >= 33 && i <= 63)\\r\\n\\t\\t\\treturn 0x80000000 + ((i - 32) << 23);\\r\\n\\t\\treturn 0xC7800000;\\r\\n\\t}\\r\\n\\r\\n\\tfunction convertOffset(i) {\\r\\n\\t if (i == 0 || i == 32)\\r\\n\\t\\t return 0\\r\\n\\t\\treturn 1024;\\r\\n\\t}\\r\\n\\r\\n\\tvar v = convertMantissa( convertOffset( h >> 10) + (h & 0x3ff) ) + convertExponent(h >> 10);\\r\\n\\tvar a = new Uint32Array([v]);\\r\\n\\treturn (new Float32Array(a.buffer))[0]; \\r\\n}\\r\\n*/\\r\\n\\r\\nglobal.typedArrayToArray = function(array)\\r\\n{\\r\\n\\tvar r = [];\\r\\n\\tr.length = array.length;\\r\\n\\tfor(var i = 0; i < array.length; i++)\\r\\n\\t\\tr[i] = array[i];\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\nglobal.RGBToHex = function(r, g, b) { \\r\\n\\tr = Math.min(255, r*255)|0;\\r\\n\\tg = Math.min(255, g*255)|0;\\r\\n\\tb = Math.min(255, b*255)|0;\\r\\n\\treturn \\\"#\\\" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);\\r\\n}\\r\\n\\r\\nglobal.HUEToRGB = function ( p, q, t ){\\r\\n\\tif(t < 0) t += 1;\\r\\n\\tif(t > 1) t -= 1;\\r\\n\\tif(t < 1/6) return p + (q - p) * 6 * t;\\r\\n\\tif(t < 1/2) return q;\\r\\n\\tif(t < 2/3) return p + (q - p) * (2/3 - t) * 6;\\r\\n\\treturn p;\\r\\n}\\r\\n\\r\\nglobal.HSLToRGB = function( h, s, l, out ){\\r\\n\\tvar r, g, b;\\r\\n\\tout = out || vec3.create();\\r\\n\\tif(s == 0){\\r\\n\\t\\tr = g = b = l; // achromatic\\r\\n\\t}else{\\r\\n\\t\\tvar q = l < 0.5 ? l * (1 + s) : l + s - l * s;\\r\\n\\t\\tvar p = 2 * l - q;\\r\\n\\t\\tr = HUEToRGB(p, q, h + 1/3);\\r\\n\\t\\tg = HUEToRGB(p, q, h);\\r\\n\\t\\tb = HUEToRGB(p, q, h - 1/3);\\r\\n\\t}\\r\\n\\tout[0] = r;\\r\\n\\tout[1] = g;\\r\\n\\tout[2] = b;\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nglobal.hexColorToRGBA = (function() {\\r\\n\\t//to change the color: from http://www.w3schools.com/cssref/css_colorsfull.asp\\r\\n\\tvar string_colors = {\\r\\n\\t\\twhite: [1,1,1],\\r\\n\\t\\tblack: [0,0,0],\\r\\n\\t\\tgray: [0.501960813999176, 0.501960813999176, 0.501960813999176],\\r\\n\\t\\tred: [1,0,0],\\r\\n\\t\\torange: [1, 0.6470588445663452, 0],\\r\\n\\t\\tpink: [1, 0.7529411911964417, 0.7960784435272217],\\r\\n\\t\\tgreen: [0, 0.501960813999176, 0],\\r\\n\\t\\tlime: [0,1,0],\\r\\n\\t\\tblue: [0,0,1],\\r\\n\\t\\tviolet: [0.9333333373069763, 0.5098039507865906, 0.9333333373069763],\\r\\n\\t\\tmagenta: [1,0,1],\\r\\n\\t\\tcyan: [0,1,1],\\r\\n\\t\\tyellow: [1,1,0],\\r\\n\\t\\tbrown: [0.6470588445663452, 0.16470588743686676, 0.16470588743686676],\\r\\n\\t\\tsilver: [0.7529411911964417, 0.7529411911964417, 0.7529411911964417],\\r\\n\\t\\tgold: [1, 0.843137264251709, 0],\\r\\n\\t\\ttransparent: [0,0,0,0]\\r\\n\\t};\\r\\n\\r\\n\\treturn function( hex, color, alpha )\\r\\n\\t{\\r\\n\\talpha = (alpha === undefined ? 1 : alpha);\\r\\n\\tcolor = color || new Float32Array(4);\\r\\n\\tcolor[3] = alpha;\\r\\n\\r\\n\\tif(typeof(hex) != \\\"string\\\")\\r\\n\\t\\treturn color;\\r\\n\\r\\n\\r\\n\\t//for those hardcoded colors\\r\\n\\tvar col = string_colors[hex];\\r\\n\\tif( col !== undefined )\\r\\n\\t{\\r\\n\\t\\tcolor.set( col );\\r\\n\\t\\tif(color.length == 3)\\r\\n\\t\\t\\tcolor[3] = alpha;\\r\\n\\t\\telse\\r\\n\\t\\t\\tcolor[3] *= alpha;\\r\\n\\t\\treturn color;\\r\\n\\t}\\r\\n\\r\\n\\t//rgba colors\\r\\n\\tvar pos = hex.indexOf(\\\"rgba(\\\");\\r\\n\\tif(pos != -1)\\r\\n\\t{\\r\\n\\t\\tvar str = hex.substr(5,hex.length-2);\\r\\n\\t\\tstr = str.split(\\\",\\\");\\r\\n\\t\\tcolor[0] = parseInt( str[0] ) / 255;\\r\\n\\t\\tcolor[1] = parseInt( str[1] ) / 255;\\r\\n\\t\\tcolor[2] = parseInt( str[2] ) / 255;\\r\\n\\t\\tcolor[3] = parseFloat( str[3] ) * alpha;\\r\\n\\t\\treturn color;\\r\\n\\t}\\r\\n\\r\\n\\tvar pos = hex.indexOf(\\\"hsla(\\\");\\r\\n\\tif(pos != -1)\\r\\n\\t{\\r\\n\\t\\tvar str = hex.substr(5,hex.length-2);\\r\\n\\t\\tstr = str.split(\\\",\\\");\\r\\n\\t\\tHSLToRGB( parseInt( str[0] ) / 360, parseInt( str[1] ) / 100, parseInt( str[2] ) / 100, color );\\r\\n\\t\\tcolor[3] = parseFloat( str[3] ) * alpha;\\r\\n\\t\\treturn color;\\r\\n\\t}\\r\\n\\r\\n\\tcolor[3] = alpha;\\r\\n\\r\\n\\t//rgb colors\\r\\n\\tvar pos = hex.indexOf(\\\"rgb(\\\");\\r\\n\\tif(pos != -1)\\r\\n\\t{\\r\\n\\t\\tvar str = hex.substr(4,hex.length-2);\\r\\n\\t\\tstr = str.split(\\\",\\\");\\r\\n\\t\\tcolor[0] = parseInt( str[0] ) / 255;\\r\\n\\t\\tcolor[1] = parseInt( str[1] ) / 255;\\r\\n\\t\\tcolor[2] = parseInt( str[2] ) / 255;\\r\\n\\t\\treturn color;\\r\\n\\t}\\r\\n\\r\\n\\tvar pos = hex.indexOf(\\\"hsl(\\\");\\r\\n\\tif(pos != -1)\\r\\n\\t{\\r\\n\\t\\tvar str = hex.substr(4,hex.length-2);\\r\\n\\t\\tstr = str.split(\\\",\\\");\\r\\n\\t\\tHSLToRGB( parseInt( str[0] ) / 360, parseInt( str[1] ) / 100, parseInt( str[2] ) / 100, color );\\r\\n\\t\\treturn color;\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\t//the rest\\r\\n\\t// Expand shorthand form (e.g. \\\"03F\\\") to full form (e.g. \\\"0033FF\\\")\\r\\n\\tvar shorthandRegex = /^#?([a-f\\\\d])([a-f\\\\d])([a-f\\\\d])$/i;\\r\\n\\thex = hex.replace( shorthandRegex, function(m, r, g, b) {\\r\\n\\t\\treturn r + r + g + g + b + b;\\r\\n\\t});\\r\\n\\r\\n\\tvar result = /^#?([a-f\\\\d]{2})([a-f\\\\d]{2})([a-f\\\\d]{2})$/i.exec(hex);\\r\\n\\tif(!result)\\r\\n\\t\\treturn color;\\r\\n\\r\\n\\tcolor[0] = parseInt(result[1], 16) / 255;\\r\\n\\tcolor[1] = parseInt(result[2], 16) / 255;\\r\\n\\tcolor[2] = parseInt(result[3], 16) / 255;\\r\\n\\treturn color;\\r\\n\\t}\\r\\n})();\\r\\n/**\\r\\n * @fileoverview dds - Utilities for loading DDS texture files\\r\\n * @author Brandon Jones\\r\\n * @version 0.1\\r\\n */\\r\\n\\r\\n/*\\r\\n * Copyright (c) 2012 Brandon Jones\\r\\n *\\r\\n * This software is provided 'as-is', without any express or implied\\r\\n * warranty. In no event will the authors be held liable for any damages\\r\\n * arising from the use of this software.\\r\\n *\\r\\n * Permission is granted to anyone to use this software for any purpose,\\r\\n * including commercial applications, and to alter it and redistribute it\\r\\n * freely, subject to the following restrictions:\\r\\n *\\r\\n * 1. The origin of this software must not be misrepresented; you must not\\r\\n * claim that you wrote the original software. If you use this software\\r\\n * in a product, an acknowledgment in the product documentation would be\\r\\n * appreciated but is not required.\\r\\n *\\r\\n * 2. Altered source versions must be plainly marked as such, and must not\\r\\n * be misrepresented as being the original software.\\r\\n *\\r\\n * 3. This notice may not be removed or altered from any source\\r\\n * distribution.\\r\\n */\\r\\n\\r\\nvar DDS = (function () {\\r\\n\\r\\n \\\"use strict\\\";\\r\\n \\r\\n // All values and structures referenced from:\\r\\n // http://msdn.microsoft.com/en-us/library/bb943991.aspx/\\r\\n var DDS_MAGIC = 0x20534444;\\r\\n \\r\\n var DDSD_CAPS = 0x1,\\r\\n DDSD_HEIGHT = 0x2,\\r\\n DDSD_WIDTH = 0x4,\\r\\n DDSD_PITCH = 0x8,\\r\\n DDSD_PIXELFORMAT = 0x1000,\\r\\n DDSD_MIPMAPCOUNT = 0x20000,\\r\\n DDSD_LINEARSIZE = 0x80000,\\r\\n DDSD_DEPTH = 0x800000;\\r\\n\\r\\n var DDSCAPS_COMPLEX = 0x8,\\r\\n DDSCAPS_MIPMAP = 0x400000,\\r\\n DDSCAPS_TEXTURE = 0x1000;\\r\\n \\r\\n var DDSCAPS2_CUBEMAP = 0x200,\\r\\n DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,\\r\\n DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,\\r\\n DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,\\r\\n DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,\\r\\n DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,\\r\\n DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,\\r\\n DDSCAPS2_VOLUME = 0x200000;\\r\\n\\r\\n var DDPF_ALPHAPIXELS = 0x1,\\r\\n DDPF_ALPHA = 0x2,\\r\\n DDPF_FOURCC = 0x4,\\r\\n DDPF_RGB = 0x40,\\r\\n DDPF_YUV = 0x200,\\r\\n DDPF_LUMINANCE = 0x20000;\\r\\n\\r\\n function fourCCToInt32(value) {\\r\\n return value.charCodeAt(0) +\\r\\n (value.charCodeAt(1) << 8) +\\r\\n (value.charCodeAt(2) << 16) +\\r\\n (value.charCodeAt(3) << 24);\\r\\n }\\r\\n\\r\\n function int32ToFourCC(value) {\\r\\n return String.fromCharCode(\\r\\n value & 0xff,\\r\\n (value >> 8) & 0xff,\\r\\n (value >> 16) & 0xff,\\r\\n (value >> 24) & 0xff\\r\\n );\\r\\n }\\r\\n\\r\\n var FOURCC_DXT1 = fourCCToInt32(\\\"DXT1\\\");\\r\\n var FOURCC_DXT3 = fourCCToInt32(\\\"DXT3\\\");\\r\\n var FOURCC_DXT5 = fourCCToInt32(\\\"DXT5\\\");\\r\\n\\r\\n var headerLengthInt = 31; // The header length in 32 bit ints\\r\\n\\r\\n // Offsets into the header array\\r\\n var off_magic = 0;\\r\\n\\r\\n var off_size = 1;\\r\\n var off_flags = 2;\\r\\n var off_height = 3;\\r\\n var off_width = 4;\\r\\n\\r\\n var off_mipmapCount = 7;\\r\\n\\r\\n var off_pfFlags = 20;\\r\\n var off_pfFourCC = 21;\\r\\n var off_caps = 27;\\r\\n \\r\\n // Little reminder for myself where the above values come from\\r\\n /*DDS_PIXELFORMAT {\\r\\n int32 dwSize; // offset: 19\\r\\n int32 dwFlags;\\r\\n char[4] dwFourCC;\\r\\n int32 dwRGBBitCount;\\r\\n int32 dwRBitMask;\\r\\n int32 dwGBitMask;\\r\\n int32 dwBBitMask;\\r\\n int32 dwABitMask; // offset: 26\\r\\n };\\r\\n \\r\\n DDS_HEADER {\\r\\n int32 dwSize; // 1\\r\\n int32 dwFlags;\\r\\n int32 dwHeight;\\r\\n int32 dwWidth;\\r\\n int32 dwPitchOrLinearSize;\\r\\n int32 dwDepth;\\r\\n int32 dwMipMapCount; // offset: 7\\r\\n int32[11] dwReserved1;\\r\\n DDS_PIXELFORMAT ddspf; // offset 19\\r\\n int32 dwCaps; // offset: 27\\r\\n int32 dwCaps2;\\r\\n int32 dwCaps3;\\r\\n int32 dwCaps4;\\r\\n int32 dwReserved2; // offset 31\\r\\n };*/\\r\\n\\r\\n /**\\r\\n * Transcodes DXT into RGB565.\\r\\n * Optimizations:\\r\\n * 1. Use integer math to compute c2 and c3 instead of floating point\\r\\n * math. Specifically:\\r\\n * c2 = 5/8 * c0 + 3/8 * c1\\r\\n * c3 = 3/8 * c0 + 5/8 * c1\\r\\n * This is about a 40% performance improvement. It also appears to\\r\\n * match what hardware DXT decoders do, as the colors produced\\r\\n * by this integer math match what hardware produces, while the\\r\\n * floating point in dxtToRgb565Unoptimized() produce slightly\\r\\n * different colors (for one GPU this was tested on).\\r\\n * 2. Unroll the inner loop. Another ~10% improvement.\\r\\n * 3. Compute r0, g0, b0, r1, g1, b1 only once instead of twice.\\r\\n * Another 10% improvement.\\r\\n * 4. Use a Uint16Array instead of a Uint8Array. Another 10% improvement.\\r\\n * @author Evan Parker\\r\\n * @param {Uint16Array} src The src DXT bits as a Uint16Array.\\r\\n * @param {number} srcByteOffset\\r\\n * @param {number} width\\r\\n * @param {number} height\\r\\n * @return {Uint16Array} dst\\r\\n */\\r\\n function dxtToRgb565(src, src16Offset, width, height) {\\r\\n var c = new Uint16Array(4);\\r\\n var dst = new Uint16Array(width * height);\\r\\n var nWords = (width * height) / 4;\\r\\n var m = 0;\\r\\n var dstI = 0;\\r\\n var i = 0;\\r\\n var r0 = 0, g0 = 0, b0 = 0, r1 = 0, g1 = 0, b1 = 0;\\r\\n \\r\\n var blockWidth = width / 4;\\r\\n var blockHeight = height / 4;\\r\\n for (var blockY = 0; blockY < blockHeight; blockY++) {\\r\\n for (var blockX = 0; blockX < blockWidth; blockX++) {\\r\\n i = src16Offset + 4 * (blockY * blockWidth + blockX);\\r\\n c[0] = src[i];\\r\\n c[1] = src[i + 1];\\r\\n r0 = c[0] & 0x1f;\\r\\n g0 = c[0] & 0x7e0;\\r\\n b0 = c[0] & 0xf800;\\r\\n r1 = c[1] & 0x1f;\\r\\n g1 = c[1] & 0x7e0;\\r\\n b1 = c[1] & 0xf800;\\r\\n // Interpolate between c0 and c1 to get c2 and c3.\\r\\n // Note that we approximate 1/3 as 3/8 and 2/3 as 5/8 for\\r\\n // speed. This also appears to be what the hardware DXT\\r\\n // decoder in many GPUs does :)\\r\\n c[2] = ((5 * r0 + 3 * r1) >> 3)\\r\\n | (((5 * g0 + 3 * g1) >> 3) & 0x7e0)\\r\\n | (((5 * b0 + 3 * b1) >> 3) & 0xf800);\\r\\n c[3] = ((5 * r1 + 3 * r0) >> 3)\\r\\n | (((5 * g1 + 3 * g0) >> 3) & 0x7e0)\\r\\n | (((5 * b1 + 3 * b0) >> 3) & 0xf800);\\r\\n m = src[i + 2];\\r\\n dstI = (blockY * 4) * width + blockX * 4;\\r\\n dst[dstI] = c[m & 0x3];\\r\\n dst[dstI + 1] = c[(m >> 2) & 0x3];\\r\\n dst[dstI + 2] = c[(m >> 4) & 0x3];\\r\\n dst[dstI + 3] = c[(m >> 6) & 0x3];\\r\\n dstI += width;\\r\\n dst[dstI] = c[(m >> 8) & 0x3];\\r\\n dst[dstI + 1] = c[(m >> 10) & 0x3];\\r\\n dst[dstI + 2] = c[(m >> 12) & 0x3];\\r\\n dst[dstI + 3] = c[(m >> 14)];\\r\\n m = src[i + 3];\\r\\n dstI += width;\\r\\n dst[dstI] = c[m & 0x3];\\r\\n dst[dstI + 1] = c[(m >> 2) & 0x3];\\r\\n dst[dstI + 2] = c[(m >> 4) & 0x3];\\r\\n dst[dstI + 3] = c[(m >> 6) & 0x3];\\r\\n dstI += width;\\r\\n dst[dstI] = c[(m >> 8) & 0x3];\\r\\n dst[dstI + 1] = c[(m >> 10) & 0x3];\\r\\n dst[dstI + 2] = c[(m >> 12) & 0x3];\\r\\n dst[dstI + 3] = c[(m >> 14)];\\r\\n }\\r\\n }\\r\\n return dst;\\r\\n }\\r\\n\\r\\n function BGRtoRGB( byteArray )\\r\\n\\t{\\r\\n\\t\\tfor(var j = 0, l = byteArray.length, tmp = 0; j < l; j+=4) //BGR fix\\r\\n\\t\\t{\\r\\n\\t\\t\\ttmp = byteArray[j];\\r\\n\\t\\t\\tbyteArray[j] = byteArray[j+2];\\r\\n\\t\\t\\tbyteArray[j+2] = tmp;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n function flipDXT( width, blockBytes, byteArray )\\r\\n\\t{\\r\\n\\t\\t//TODO\\r\\n\\t\\t//var row = Uint8Array(width);\\r\\n\\t}\\r\\n\\r\\n\\r\\n /**\\r\\n * Parses a DDS file from the given arrayBuffer and uploads it into the currently bound texture\\r\\n *\\r\\n * @param {WebGLRenderingContext} gl WebGL rendering context\\r\\n * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object\\r\\n * @param {TypedArray} arrayBuffer Array Buffer containing the DDS files data\\r\\n * @param {boolean} [loadMipmaps] If false only the top mipmap level will be loaded, otherwise all available mipmaps will be uploaded\\r\\n *\\r\\n * @returns {number} Number of mipmaps uploaded, 0 if there was an error\\r\\n */\\r\\n function uploadDDSLevels(gl, ext, arrayBuffer, loadMipmaps) {\\r\\n var header = new Int32Array(arrayBuffer, 0, headerLengthInt),\\r\\n fourCC, blockBytes, internalFormat,\\r\\n width, height, dataLength, dataOffset, is_cubemap,\\r\\n rgb565Data, byteArray, mipmapCount, i, face;\\r\\n\\r\\n if(header[off_magic] != DDS_MAGIC) {\\r\\n console.error(\\\"Invalid magic number in DDS header\\\");\\r\\n return 0;\\r\\n }\\r\\n \\r\\n if(!header[off_pfFlags] & DDPF_FOURCC) {\\r\\n console.error(\\\"Unsupported format, must contain a FourCC code\\\");\\r\\n return 0;\\r\\n }\\r\\n\\r\\n fourCC = header[off_pfFourCC];\\r\\n switch(fourCC) {\\r\\n case FOURCC_DXT1:\\r\\n blockBytes = 8;\\r\\n internalFormat = ext ? ext.COMPRESSED_RGB_S3TC_DXT1_EXT : null;\\r\\n break;\\r\\n\\r\\n\\t\\t\\t/*\\r\\n case FOURCC_DXT1:\\r\\n blockBytes = 8;\\r\\n internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT1_EXT : null;\\r\\n break;\\r\\n\\t\\t\\t*/\\r\\n\\r\\n case FOURCC_DXT3:\\r\\n blockBytes = 16;\\r\\n internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT3_EXT : null;\\r\\n break;\\r\\n\\r\\n case FOURCC_DXT5:\\r\\n blockBytes = 16;\\r\\n internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT5_EXT : null;\\r\\n break;\\r\\n\\r\\n default:\\r\\n\\t\\t\\t\\tblockBytes = 4;\\r\\n\\t\\t\\t\\tfourCC = null;\\r\\n\\t\\t\\t\\tinternalFormat = gl.RGBA;\\r\\n //console.error(\\\"Unsupported FourCC code:\\\", int32ToFourCC(fourCC), fourCC);\\r\\n //return null;\\r\\n }\\r\\n\\r\\n mipmapCount = 1;\\r\\n if(header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) {\\r\\n mipmapCount = Math.max(1, header[off_mipmapCount]);\\r\\n }\\r\\n\\r\\n width = header[off_width];\\r\\n height = header[off_height];\\r\\n dataOffset = header[off_size] + 4;\\r\\n\\t\\tis_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP);\\r\\n\\r\\n\\t\\tif(is_cubemap)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//console.error(\\\"Cubemaps not supported in DDS\\\");\\r\\n\\t\\t\\t//return null;\\r\\n\\r\\n\\t\\t\\tfor(face = 0; face < 6; ++face)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\twidth = header[off_width];\\r\\n\\t\\t\\t\\theight = header[off_height];\\r\\n\\t\\t\\t\\tfor(var i = 0; i < mipmapCount; ++i) {\\r\\n\\t\\t\\t\\t\\tif(fourCC)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tdataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes;\\r\\n\\t\\t\\t\\t\\t\\tbyteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);\\r\\n\\t\\t\\t\\t\\t\\tflipDXT( width, blockBytes, byteArray );\\r\\n\\t\\t\\t\\t\\t\\tgl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, i, internalFormat, width, height, 0, byteArray);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false );\\r\\n\\t\\t\\t\\t\\t\\tdataLength = width * height * blockBytes;\\r\\n\\t\\t\\t\\t\\t\\tbyteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);\\r\\n\\t\\t\\t\\t\\t\\tBGRtoRGB(byteArray);\\r\\n\\t\\t\\t\\t\\t\\tgl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, i, internalFormat, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, byteArray);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tdataOffset += dataLength;\\r\\n\\t\\t\\t\\t\\twidth *= 0.5;\\r\\n\\t\\t\\t\\t\\theight *= 0.5;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse //2d texture\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(ext) {\\r\\n\\t\\t\\t\\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true );\\r\\n\\t\\t\\t\\tfor(var i = 0; i < mipmapCount; ++i) {\\r\\n\\t\\t\\t\\t\\tif(fourCC)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tdataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes;\\r\\n\\t\\t\\t\\t\\t\\tbyteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);\\r\\n\\t\\t\\t\\t\\t\\tgl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, byteArray);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tdataLength = width * height * blockBytes;\\r\\n\\t\\t\\t\\t\\t\\tbyteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);\\r\\n\\t\\t\\t\\t\\t\\tBGRtoRGB(byteArray);\\r\\n\\t\\t\\t\\t\\t\\tgl.texImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, internalFormat, gl.UNSIGNED_BYTE, byteArray);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tdataOffset += dataLength;\\r\\n\\t\\t\\t\\t\\twidth *= 0.5;\\r\\n\\t\\t\\t\\t\\theight *= 0.5;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tif(fourCC == FOURCC_DXT1) {\\r\\n\\t\\t\\t\\t\\tdataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes;\\r\\n\\t\\t\\t\\t\\tbyteArray = new Uint16Array(arrayBuffer);\\r\\n\\t\\t\\t\\t\\t//Decompress\\r\\n\\t\\t\\t\\t\\trgb565Data = dxtToRgb565(byteArray, dataOffset / 2, width, height);\\r\\n\\t\\t\\t\\t\\tgl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, rgb565Data);\\r\\n\\t\\t\\t\\t\\tif(loadMipmaps) {\\r\\n\\t\\t\\t\\t\\t\\tgl.generateMipmap(gl.TEXTURE_2D);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tconsole.error(\\\"No manual decoder for\\\", int32ToFourCC(fourCC), \\\"and no native support\\\");\\r\\n\\t\\t\\t\\t\\treturn 0;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n return mipmapCount;\\r\\n }\\r\\n\\r\\n /**\\r\\n * Parses a DDS file from the given arrayBuffer and uploads it into the currently bound texture\\r\\n *\\r\\n * @param {WebGLRenderingContext} gl WebGL rendering context\\r\\n * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object\\r\\n * @param {TypedArray} arrayBuffer Array Buffer containing the DDS files data\\r\\n * @param {boolean} [loadMipmaps] If false only the top mipmap level will be loaded, otherwise all available mipmaps will be uploaded\\r\\n *\\r\\n * @returns {number} Number of mipmaps uploaded, 0 if there was an error\\r\\n */\\r\\n function getDDSLevels( arrayBuffer, compressed_not_supported )\\r\\n\\t{\\r\\n var header = new Int32Array(arrayBuffer, 0, headerLengthInt),\\r\\n fourCC, blockBytes, internalFormat,\\r\\n width, height, dataLength, dataOffset, is_cubemap,\\r\\n rgb565Data, byteArray, mipmapCount, i, face;\\r\\n\\r\\n if(header[off_magic] != DDS_MAGIC) {\\r\\n console.error(\\\"Invalid magic number in DDS header\\\");\\r\\n return 0;\\r\\n }\\r\\n \\r\\n if(!header[off_pfFlags] & DDPF_FOURCC) {\\r\\n console.error(\\\"Unsupported format, must contain a FourCC code\\\");\\r\\n return 0;\\r\\n }\\r\\n\\r\\n fourCC = header[off_pfFourCC];\\r\\n switch(fourCC) {\\r\\n case FOURCC_DXT1:\\r\\n blockBytes = 8;\\r\\n internalFormat = \\\"COMPRESSED_RGB_S3TC_DXT1_EXT\\\";\\r\\n break;\\r\\n\\r\\n case FOURCC_DXT3:\\r\\n blockBytes = 16;\\r\\n internalFormat = \\\"COMPRESSED_RGBA_S3TC_DXT3_EXT\\\";\\r\\n break;\\r\\n\\r\\n case FOURCC_DXT5:\\r\\n blockBytes = 16;\\r\\n internalFormat = \\\"COMPRESSED_RGBA_S3TC_DXT5_EXT\\\";\\r\\n break;\\r\\n\\r\\n default:\\r\\n\\t\\t\\t\\tblockBytes = 4;\\r\\n\\t\\t\\t\\tinternalFormat = \\\"RGBA\\\";\\r\\n //console.error(\\\"Unsupported FourCC code:\\\", int32ToFourCC(fourCC), fourCC);\\r\\n //return null;\\r\\n }\\r\\n\\r\\n mipmapCount = 1;\\r\\n if(header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) {\\r\\n mipmapCount = Math.max(1, header[off_mipmapCount]);\\r\\n }\\r\\n\\r\\n width = header[off_width];\\r\\n height = header[off_height];\\r\\n dataOffset = header[off_size] + 4;\\r\\n\\t\\tis_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP);\\r\\n\\r\\n\\t\\tvar buffers = [];\\r\\n\\r\\n\\t\\tif(is_cubemap)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var face = 0; face < 6; ++face)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\twidth = header[off_width];\\r\\n\\t\\t\\t\\theight = header[off_height];\\r\\n\\t\\t\\t\\tfor(var i = 0; i < mipmapCount; ++i)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(fourCC)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tdataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes;\\r\\n\\t\\t\\t\\t\\t\\tbyteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);\\r\\n\\t\\t\\t\\t\\t\\tbuffers.push({ tex: \\\"TEXTURE_CUBE_MAP\\\", face: face, mipmap: i, internalFormat: internalFormat, width: width, height: height, offset: 0, dataOffset: dataOffset, dataLength: dataLength });\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tdataLength = width * height * blockBytes;\\r\\n\\t\\t\\t\\t\\t\\tbyteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);\\r\\n\\t\\t\\t\\t\\t\\tBGRtoRGB(byteArray);\\r\\n\\t\\t\\t\\t\\t\\tbuffers.push({ tex: \\\"TEXTURE_CUBE_MAP\\\", face: face, mipmap: i, internalFormat: internalFormat, width: width, height: height, offset: 0, type: \\\"UNSIGNED_BYTE\\\", dataOffset: dataOffset, dataLength: dataLength });\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tdataOffset += dataLength;\\r\\n\\t\\t\\t\\t\\twidth *= 0.5;\\r\\n\\t\\t\\t\\t\\theight *= 0.5;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse //2d texture\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!compressed_not_supported)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var i = 0; i < mipmapCount; ++i) {\\r\\n\\t\\t\\t\\t\\tdataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes;\\r\\n\\t\\t\\t\\t\\tbyteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);\\r\\n\\t\\t\\t\\t\\t//gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, byteArray);\\r\\n\\t\\t\\t\\t\\tbuffers.push({ tex: \\\"TEXTURE_2D\\\", mipmap: i, internalFormat: internalFormat, width: width, height: height, offset: 0, type: \\\"UNSIGNED_BYTE\\\", dataOffset: dataOffset, dataLength: dataLength });\\r\\n\\t\\t\\t\\t\\tdataOffset += dataLength;\\r\\n\\t\\t\\t\\t\\twidth *= 0.5;\\r\\n\\t\\t\\t\\t\\theight *= 0.5;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tif(fourCC == FOURCC_DXT1)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tdataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes;\\r\\n\\t\\t\\t\\t\\tbyteArray = new Uint16Array(arrayBuffer);\\r\\n\\t\\t\\t\\t\\trgb565Data = dxtToRgb565(byteArray, dataOffset / 2, width, height);\\r\\n\\t\\t\\t\\t\\t//gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, rgb565Data);\\r\\n\\t\\t\\t\\t\\tbuffers.push({ tex: \\\"TEXTURE_2D\\\", mipmap: 0, internalFormat: \\\"RGB\\\", width: width, height: height, offset: 0, format:\\\"RGB\\\", type: \\\"UNSIGNED_SHORT_5_6_5\\\", data: rgb565Data });\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tconsole.error(\\\"No manual decoder for\\\", int32ToFourCC(fourCC), \\\"and no native support\\\");\\r\\n\\t\\t\\t\\t\\treturn 0;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n return buffers;\\r\\n }\\r\\n\\r\\n /**\\r\\n * Creates a texture from the DDS file at the given URL. Simple shortcut for the most common use case\\r\\n *\\r\\n * @param {WebGLRenderingContext} gl WebGL rendering context\\r\\n * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object\\r\\n * @param {string} src URL to DDS file to be loaded\\r\\n * @param {function} [callback] callback to be fired when the texture has finished loading\\r\\n *\\r\\n * @returns {WebGLTexture} New texture that will receive the DDS image data\\r\\n */\\r\\n function loadDDSTextureEx(gl, ext, src, texture, loadMipmaps, callback) {\\r\\n var xhr = new XMLHttpRequest();\\r\\n \\r\\n xhr.open('GET', src, true);\\r\\n xhr.responseType = \\\"arraybuffer\\\";\\r\\n xhr.onload = function() {\\r\\n if(this.status == 200) {\\r\\n\\t\\t\\t\\tvar header = new Int32Array(this.response, 0, headerLengthInt)\\r\\n\\t\\t\\t\\tvar is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP);\\r\\n\\t\\t\\t\\tvar tex_type = is_cubemap ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;\\r\\n gl.bindTexture(tex_type, texture);\\r\\n var mipmaps = uploadDDSLevels(gl, ext, this.response, loadMipmaps);\\r\\n gl.texParameteri(tex_type, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\\r\\n gl.texParameteri(tex_type, gl.TEXTURE_MIN_FILTER, mipmaps > 1 ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);\\r\\n gl.bindTexture(tex_type, null);\\r\\n\\t\\t\\t\\ttexture.texture_type = tex_type;\\r\\n\\t\\t\\t\\ttexture.width = header[off_width];\\r\\n\\t\\t\\t\\ttexture.height = header[off_height];\\r\\n }\\r\\n\\r\\n if(callback) {\\r\\n callback(texture);\\r\\n }\\r\\n };\\r\\n xhr.send(null);\\r\\n\\r\\n return texture;\\r\\n }\\r\\n\\r\\n /**\\r\\n * Creates a texture from the DDS file at the given ArrayBuffer.\\r\\n *\\r\\n * @param {WebGLRenderingContext} gl WebGL rendering context\\r\\n * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object\\r\\n * @param {ArrayBuffer} data containing the DDS file\\r\\n * @param {Texture} texture from GL.Texture\\r\\n * @returns {WebGLTexture} New texture that will receive the DDS image data\\r\\n */\\r\\n function loadDDSTextureFromMemoryEx(gl, ext, data, texture, loadMipmaps) {\\r\\n\\t\\tvar header = new Int32Array(data, 0, headerLengthInt)\\r\\n\\t\\tvar is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP);\\r\\n\\t\\tvar tex_type = is_cubemap ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;\\r\\n\\r\\n\\t\\tvar handler = texture.handler || texture;\\r\\n\\r\\n\\t\\tgl.bindTexture(tex_type, texture.handler);\\r\\n\\r\\n\\t\\t//upload data\\r\\n\\t\\tvar mipmaps = uploadDDSLevels(gl, ext, data, loadMipmaps);\\r\\n\\r\\n\\t\\tgl.texParameteri(tex_type, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\\r\\n\\t\\tgl.texParameteri(tex_type, gl.TEXTURE_MIN_FILTER, mipmaps > 1 ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);\\r\\n if(is_cubemap)\\r\\n {\\r\\n gl.texParameteri(tex_type, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );\\r\\n gl.texParameteri(tex_type, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );\\r\\n }\\r\\n\\r\\n\\t\\tgl.bindTexture(tex_type, null); //unbind\\r\\n\\t\\tif(texture.handler)\\r\\n\\t\\t{\\r\\n\\t\\t\\ttexture.texture_type = tex_type;\\r\\n\\t\\t\\ttexture.width = header[off_width];\\r\\n\\t\\t\\ttexture.height = header[off_height];\\r\\n\\t\\t}\\r\\n return texture;\\r\\n }\\r\\n\\r\\n /**\\r\\n * Extracts the texture info from a DDS file at the given ArrayBuffer.\\r\\n *\\r\\n * @param {ArrayBuffer} data containing the DDS file\\r\\n *\\r\\n * @returns {Object} contains mipmaps and properties\\r\\n */\\r\\n function getDDSTextureFromMemoryEx(data) {\\r\\n\\t\\tvar header = new Int32Array(data, 0, headerLengthInt)\\r\\n\\t\\tvar is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP);\\r\\n\\t\\tvar tex_type = is_cubemap ? \\\"TEXTURE_CUBE_MAP\\\" : \\\"TEXTURE_2D\\\";\\r\\n\\t\\tvar buffers = getDDSLevels(data);\\r\\n\\r\\n\\t\\tvar texture = {\\r\\n\\t\\t\\ttype: tex_type,\\r\\n\\t\\t\\tbuffers: buffers,\\r\\n\\t\\t\\tdata: data,\\r\\n\\t\\t\\twidth: header[off_width],\\r\\n\\t\\t\\theight: header[off_height]\\r\\n\\t\\t};\\r\\n\\r\\n return texture;\\r\\n }\\r\\n\\r\\n /**\\r\\n * Creates a texture from the DDS file at the given URL. Simple shortcut for the most common use case\\r\\n *\\r\\n * @param {WebGLRenderingContext} gl WebGL rendering context\\r\\n * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object\\r\\n * @param {string} src URL to DDS file to be loaded\\r\\n * @param {function} [callback] callback to be fired when the texture has finished loading\\r\\n *\\r\\n * @returns {WebGLTexture} New texture that will receive the DDS image data\\r\\n */\\r\\n function loadDDSTexture(gl, ext, src, callback) {\\r\\n var texture = gl.createTexture();\\r\\n var ext = gl.getExtension(\\\"WEBGL_compressed_texture_s3tc\\\");\\r\\n loadDDSTextureEx(gl, ext, src, texture, true, callback);\\r\\n return texture;\\r\\n }\\r\\n\\r\\n return {\\r\\n dxtToRgb565: dxtToRgb565,\\r\\n uploadDDSLevels: uploadDDSLevels,\\r\\n loadDDSTextureEx: loadDDSTextureEx,\\r\\n loadDDSTexture: loadDDSTexture,\\r\\n\\t\\tloadDDSTextureFromMemoryEx: loadDDSTextureFromMemoryEx,\\r\\n\\t\\tgetDDSTextureFromMemoryEx: getDDSTextureFromMemoryEx\\r\\n };\\r\\n\\r\\n})();\\r\\n\\r\\nif(typeof(global) != \\\"undefined\\\")\\r\\n\\tglobal.DDS = DDS;\\r\\n\\r\\n/* this file adds some extra functions to gl-matrix library */\\r\\nif(typeof(glMatrix) == \\\"undefined\\\")\\r\\n\\tthrow(\\\"You must include glMatrix on your project\\\");\\r\\n\\r\\nMath.clamp = function(v,a,b) { return (a > v ? a : (b < v ? b : v)); }\\r\\n\\r\\nvar V3 = vec3.create;\\r\\nvar M4 = vec3.create;\\r\\n\\r\\n\\r\\nvec3.ZERO = vec3.fromValues(0,0,0);\\r\\nvec3.FRONT = vec3.fromValues(0,0,-1);\\r\\nvec3.UP = vec3.fromValues(0,1,0);\\r\\nvec3.RIGHT = vec3.fromValues(1,0,0);\\r\\n\\r\\nvec2.rotate = function(out,vec,angle_in_rad)\\r\\n{\\r\\n\\tvar x = vec[0], y = vec[1];\\r\\n\\tvar cos = Math.cos(angle_in_rad);\\r\\n\\tvar sin = Math.sin(angle_in_rad);\\r\\n\\tout[0] = x * cos - y * sin;\\r\\n\\tout[1] = x * sin + y * cos;\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nvec3.zero = function(a)\\r\\n{\\r\\n\\ta[0] = a[1] = 0.0;\\r\\n\\treturn a;\\r\\n}\\r\\n\\r\\n//for signed angles\\r\\nvec2.perpdot = function(a,b)\\r\\n{\\r\\n\\treturn a[1] * b[0] + -a[0] * b[1];\\r\\n}\\r\\n\\r\\nvec2.computeSignedAngle = function( a, b )\\r\\n{\\r\\n\\treturn Math.atan2( vec2.perpdot(a,b), vec2.dot(a,b) );\\r\\n}\\r\\n\\r\\nvec2.random = function( vec, scale )\\r\\n{\\r\\n\\tscale = scale || 1.0;\\r\\n\\tvec[0] = Math.random() * scale;\\r\\n\\tvec[1] = Math.random() * scale;\\r\\n\\treturn vec;\\r\\n}\\r\\n\\r\\nvec3.zero = function(a)\\r\\n{\\r\\n\\ta[0] = a[1] = a[2] = 0.0;\\r\\n\\treturn a;\\r\\n}\\r\\n\\r\\nvec3.minValue = function(a)\\r\\n{\\r\\n\\tif(a[0] < a[1] && a[0] < a[2]) return a[0];\\r\\n\\tif(a[1] < a[2]) return a[1];\\r\\n\\treturn a[2];\\r\\n}\\r\\n\\r\\nvec3.maxValue = function(a)\\r\\n{\\r\\n\\tif(a[0] > a[1] && a[0] > a[2]) return a[0];\\r\\n\\tif(a[1] > a[2]) return a[1];\\r\\n\\treturn a[2];\\r\\n}\\r\\n\\r\\nvec3.minValue = function(a)\\r\\n{\\r\\n\\tif(a[0] < a[1] && a[0] < a[2]) return a[0];\\r\\n\\tif(a[1] < a[2]) return a[1];\\r\\n\\treturn a[2];\\r\\n}\\r\\n\\r\\nvec3.addValue = function(out,a,v)\\r\\n{\\r\\n\\tout[0] = a[0] + v;\\r\\n\\tout[1] = a[1] + v;\\r\\n\\tout[2] = a[2] + v;\\r\\n}\\r\\n\\r\\nvec3.subValue = function(out,a,v)\\r\\n{\\r\\n\\tout[0] = a[0] - v;\\r\\n\\tout[1] = a[1] - v;\\r\\n\\tout[2] = a[2] - v;\\r\\n}\\r\\n\\r\\nvec3.toArray = function(vec)\\r\\n{\\r\\n\\treturn [vec[0],vec[1],vec[2]];\\r\\n}\\r\\n\\r\\nvec3.rotateX = function(out,vec,angle_in_rad)\\r\\n{\\r\\n\\tvar y = vec[1], z = vec[2];\\r\\n\\tvar cos = Math.cos(angle_in_rad);\\r\\n\\tvar sin = Math.sin(angle_in_rad);\\r\\n\\r\\n\\tout[0] = vec[0];\\r\\n\\tout[1] = y * cos - z * sin;\\r\\n\\tout[2] = y * sin + z * cos;\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nvec3.rotateY = function(out,vec,angle_in_rad)\\r\\n{\\r\\n\\tvar x = vec[0], z = vec[2];\\r\\n\\tvar cos = Math.cos(angle_in_rad);\\r\\n\\tvar sin = Math.sin(angle_in_rad);\\r\\n\\r\\n\\tout[0] = x * cos - z * sin;\\r\\n\\tout[1] = vec[1];\\r\\n\\tout[2] = x * sin + z * cos;\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nvec3.rotateZ = function(out,vec,angle_in_rad)\\r\\n{\\r\\n\\tvar x = vec[0], y = vec[1];\\r\\n\\tvar cos = Math.cos(angle_in_rad);\\r\\n\\tvar sin = Math.sin(angle_in_rad);\\r\\n\\r\\n\\tout[0] = x * cos - y * sin;\\r\\n\\tout[1] = x * sin + y * cos;\\r\\n\\tout[2] = vec[2];\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nvec3.angle = function( a, b )\\r\\n{\\r\\n\\treturn Math.acos( vec3.dot(a,b) );\\r\\n}\\r\\n\\r\\nvec3.random = function(vec, scale)\\r\\n{\\r\\n\\tscale = scale || 1.0;\\r\\n\\tvec[0] = Math.random() * scale;\\r\\n\\tvec[1] = Math.random() * scale;\\r\\n\\tvec[2] = Math.random() * scale;\\r\\n\\treturn vec;\\r\\n}\\r\\n\\r\\n//converts a polar coordinate (radius, lat, long) to (x,y,z)\\r\\nvec3.polarToCartesian = function(out, v)\\r\\n{\\r\\n\\tvar r = v[0];\\r\\n\\tvar lat = v[1];\\r\\n\\tvar lon = v[2];\\r\\n\\tout[0] = r * Math.cos(lat) * Math.sin(lon);\\r\\n\\tout[1] = r * Math.sin(lat);\\r\\n\\tout[2] = r * Math.cos(lat) * Math.cos(lon);\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nvec3.reflect = function(out, v, n)\\r\\n{\\r\\n\\tvar x = v[0]; var y = v[1]; var z = v[2];\\r\\n\\tvec3.scale( out, n, -2 * vec3.dot(v,n) );\\r\\n\\tout[0] += x;\\r\\n\\tout[1] += y;\\r\\n\\tout[2] += z;\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n/* VEC4 */\\r\\nvec4.random = function(vec, scale)\\r\\n{\\r\\n\\tscale = scale || 1.0;\\r\\n\\tvec[0] = Math.random() * scale;\\r\\n\\tvec[1] = Math.random() * scale;\\r\\n\\tvec[2] = Math.random() * scale;\\r\\n\\tvec[3] = Math.random() * scale;\\t\\r\\n\\treturn vec;\\r\\n}\\r\\n\\r\\nvec4.toArray = function(vec)\\r\\n{\\r\\n\\treturn [vec[0],vec[1],vec[2],vec[3]];\\r\\n}\\r\\n\\r\\n\\r\\n/** MATRIX ********************/\\r\\nmat3.IDENTITY = mat3.create();\\r\\nmat4.IDENTITY = mat4.create();\\r\\n\\r\\nmat4.toArray = function(mat)\\r\\n{\\r\\n\\treturn [mat[0],mat[1],mat[2],mat[3],mat[4],mat[5],mat[6],mat[7],mat[8],mat[9],mat[10],mat[11],mat[12],mat[13],mat[14],mat[15]];\\r\\n}\\r\\n\\r\\nmat4.setUpAndOrthonormalize = function(out, m, up)\\r\\n{\\r\\n\\tif(m != out)\\r\\n\\t\\tmat4.copy(out,m);\\r\\n\\tvar right = out.subarray(0,3);\\r\\n\\tvec3.normalize(out.subarray(4,7),up);\\r\\n\\tvar front = out.subarray(8,11);\\r\\n\\tvec3.cross( right, up, front );\\r\\n\\tvec3.normalize( right, right );\\r\\n\\tvec3.cross( front, right, up );\\r\\n\\tvec3.normalize( front, front );\\r\\n}\\r\\n\\r\\nmat4.multiplyVec3 = function(out, m, a) {\\r\\n var x = a[0], y = a[1], z = a[2];\\r\\n out[0] = m[0] * x + m[4] * y + m[8] * z + m[12];\\r\\n out[1] = m[1] * x + m[5] * y + m[9] * z + m[13];\\r\\n out[2] = m[2] * x + m[6] * y + m[10] * z + m[14];\\r\\n return out;\\r\\n};\\r\\n\\r\\n//from https://github.com/hughsk/from-3d-to-2d/blob/master/index.js\\r\\n//m should be a projection matrix (or a VP or MVP)\\r\\n//projects vector from 3D to 2D and returns the value in normalized screen space\\r\\nmat4.projectVec3 = function(out, m, a)\\r\\n{\\r\\n\\tvar ix = a[0];\\r\\n\\tvar iy = a[1];\\r\\n\\tvar iz = a[2];\\r\\n\\r\\n\\tvar ox = m[0] * ix + m[4] * iy + m[8] * iz + m[12];\\r\\n\\tvar oy = m[1] * ix + m[5] * iy + m[9] * iz + m[13];\\r\\n\\tvar oz = m[2] * ix + m[6] * iy + m[10] * iz + m[14];\\r\\n\\tvar ow = m[3] * ix + m[7] * iy + m[11] * iz + m[15];\\r\\n\\r\\n\\tout[0] = (ox / ow + 1) / 2;\\r\\n\\tout[1] = (oy / ow + 1) / 2;\\r\\n\\tout[2] = (oz / ow + 1) / 2;\\r\\n\\treturn out;\\r\\n};\\r\\n\\r\\n\\r\\n//from https://github.com/hughsk/from-3d-to-2d/blob/master/index.js\\r\\nvec3.project = function(out, vec, mvp, viewport) {\\r\\n\\tviewport = viewport || gl.viewport_data;\\r\\n\\r\\n\\tvar m = mvp;\\r\\n\\r\\n\\tvar ix = vec[0];\\r\\n\\tvar iy = vec[1];\\r\\n\\tvar iz = vec[2];\\r\\n\\r\\n\\tvar ox = m[0] * ix + m[4] * iy + m[8] * iz + m[12];\\r\\n\\tvar oy = m[1] * ix + m[5] * iy + m[9] * iz + m[13];\\r\\n\\tvar oz = m[2] * ix + m[6] * iy + m[10] * iz + m[14];\\r\\n\\tvar ow = m[3] * ix + m[7] * iy + m[11] * iz + m[15];\\r\\n\\r\\n\\tvar projx = (ox / ow + 1) / 2;\\r\\n\\tvar projy = 1 - (oy / ow + 1) / 2;\\r\\n\\tvar projz = (oz / ow + 1) / 2;\\r\\n\\r\\n\\tout[0] = projx * viewport[2] + viewport[0];\\r\\n\\tout[1] = projy * viewport[3] + viewport[1];\\r\\n\\tout[2] = projz; //ow\\r\\n\\treturn out;\\r\\n};\\r\\n\\r\\nvar unprojectMat = mat4.create();\\r\\nvar unprojectVec = vec4.create();\\r\\n\\r\\nvec3.unproject = function (out, vec, viewprojection, viewport) {\\r\\n\\r\\n\\tvar m = unprojectMat;\\r\\n\\tvar v = unprojectVec;\\r\\n\\t\\r\\n\\tv[0] = (vec[0] - viewport[0]) * 2.0 / viewport[2] - 1.0;\\r\\n\\tv[1] = (vec[1] - viewport[1]) * 2.0 / viewport[3] - 1.0;\\r\\n\\tv[2] = 2.0 * vec[2] - 1.0;\\r\\n\\tv[3] = 1.0;\\r\\n\\t\\r\\n\\tif(!mat4.invert(m,viewprojection)) \\r\\n\\t\\treturn null;\\r\\n\\t\\r\\n\\tvec4.transformMat4(v, v, m);\\r\\n\\tif(v[3] === 0.0) \\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tout[0] = v[0] / v[3];\\r\\n\\tout[1] = v[1] / v[3];\\r\\n\\tout[2] = v[2] / v[3];\\r\\n\\t\\r\\n\\treturn out;\\r\\n};\\r\\n\\r\\n//without translation\\r\\nmat4.rotateVec3 = function(out, m, a) {\\r\\n var x = a[0], y = a[1], z = a[2];\\r\\n out[0] = m[0] * x + m[4] * y + m[8] * z;\\r\\n out[1] = m[1] * x + m[5] * y + m[9] * z;\\r\\n out[2] = m[2] * x + m[6] * y + m[10] * z;\\r\\n return out;\\r\\n};\\r\\n\\r\\nmat4.fromTranslationFrontTop = function (out, pos, front, top)\\r\\n{\\r\\n\\tvec3.cross(out.subarray(0,3), front, top);\\r\\n\\tout.set(top,4);\\r\\n\\tout.set(front,8);\\r\\n\\tout.set(pos,12);\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n\\r\\nmat4.translationMatrix = function (v)\\r\\n{\\r\\n\\tvar out = mat4.create();\\r\\n\\tout[12] = v[0];\\r\\n\\tout[13] = v[1];\\r\\n\\tout[14] = v[2];\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nmat4.setTranslation = function (out, v)\\r\\n{\\r\\n\\tout[12] = v[0];\\r\\n\\tout[13] = v[1];\\r\\n\\tout[14] = v[2];\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n\\r\\nmat4.getTranslation = function (out, matrix)\\r\\n{\\r\\n\\tout[0] = matrix[12];\\r\\n\\tout[1] = matrix[13];\\r\\n\\tout[2] = matrix[14];\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n//returns the matrix without rotation\\r\\nmat4.toRotationMat4 = function (out, mat) {\\r\\n\\tmat4.copy(out,mat);\\r\\n\\tout[12] = out[13] = out[14] = 0.0;\\r\\n\\treturn out;\\r\\n};\\r\\n\\r\\nmat4.swapRows = function(out, mat, row, row2)\\r\\n{\\r\\n\\tif(out != mat)\\r\\n\\t{\\r\\n\\t\\tmat4.copy(out, mat);\\r\\n\\t\\tout[4*row] = mat[4*row2];\\r\\n\\t\\tout[4*row+1] = mat[4*row2+1];\\r\\n\\t\\tout[4*row+2] = mat[4*row2+2];\\r\\n\\t\\tout[4*row+3] = mat[4*row2+3];\\r\\n\\t\\tout[4*row2] = mat[4*row];\\r\\n\\t\\tout[4*row2+1] = mat[4*row+1];\\r\\n\\t\\tout[4*row2+2] = mat[4*row+2];\\r\\n\\t\\tout[4*row2+3] = mat[4*row+3];\\r\\n\\t\\treturn out;\\r\\n\\t}\\r\\n\\r\\n\\tvar temp = new Float32Array(matrix.subarray(row*4,row*5));\\r\\n\\tmatrix.set( matrix.subarray(row2*4,row2*5), row*4 );\\r\\n\\tmatrix.set( temp, row2*4 );\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n//used in skinning\\r\\nmat4.scaleAndAdd = function(out, mat, mat2, v)\\r\\n{\\r\\n\\tout[0] = mat[0] + mat2[0] * v; \\tout[1] = mat[1] + mat2[1] * v; \\tout[2] = mat[2] + mat2[2] * v; \\tout[3] = mat[3] + mat2[3] * v;\\r\\n\\tout[4] = mat[4] + mat2[4] * v; \\tout[5] = mat[5] + mat2[5] * v; \\tout[6] = mat[6] + mat2[6] * v; \\tout[7] = mat[7] + mat2[7] * v;\\r\\n\\tout[8] = mat[8] + mat2[8] * v; \\tout[9] = mat[9] + mat2[9] * v; \\tout[10] = mat[10] + mat2[10] * v; \\tout[11] = mat[11] + mat2[11] * v;\\r\\n\\tout[12] = mat[12] + mat2[12] * v; out[13] = mat[13] + mat2[13] * v; \\tout[14] = mat[14] + mat2[14] * v; \\tout[15] = mat[15] + mat2[15] * v;\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nquat.fromAxisAngle = function(axis, rad)\\r\\n{\\r\\n\\tvar out = quat.create();\\r\\n rad = rad * 0.5;\\r\\n var s = Math.sin(rad);\\r\\n out[0] = s * axis[0];\\r\\n out[1] = s * axis[1];\\r\\n out[2] = s * axis[2];\\r\\n out[3] = Math.cos(rad);\\r\\n return out;\\r\\n}\\r\\n\\r\\n/*\\r\\nquat.toEuler = function(out, quat) {\\r\\n\\tvar q = quat;\\r\\n\\tvar heading, attitude, bank;\\r\\n\\r\\n\\tif( (q[0]*q[1] + q[2]*q[3]) == 0.5 )\\r\\n\\t{\\r\\n\\t\\theading = 2 * Math.atan2(q[0],q[3]);\\r\\n\\t\\tbank = 0;\\r\\n\\t\\tattitude = 0; //�?\\r\\n\\t}\\r\\n\\telse if( (q[0]*q[1] + q[2]*q[3]) == 0.5 )\\r\\n\\t{\\r\\n\\t\\theading = -2 * Math.atan2(q[0],q[3]);\\r\\n\\t\\tbank = 0;\\r\\n\\t\\tattitude = 0; //�?\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\theading = Math.atan2( 2*(q[1]*q[3] - q[0]*q[2]) , 1 - 2 * (q[1]*q[1] - q[2]*q[2]) );\\r\\n\\t\\tattitude = Math.asin( 2*(q[0]*q[1] - q[2]*q[3]) );\\r\\n\\t\\tbank = Math.atan2( 2*(q[0]*q[3] - q[1]*q[2]), 1 - 2*(q[0]*q[0] - q[2]*q[2]) );\\r\\n\\t}\\r\\n\\r\\n\\tif(!out)\\r\\n\\t\\tout = vec3.create();\\r\\n\\tvec3.set(out, heading, attitude, bank);\\r\\n\\treturn out;\\r\\n}\\r\\n*/\\r\\n\\r\\n/*\\r\\n//FROM https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles\\r\\n//doesnt work well\\r\\nquat.toEuler = function(out, q)\\r\\n{\\r\\n var yaw = Math.atan2(2*q[0]*q[3] + 2*q[1]*q[2], 1 - 2*q[2]*q[2] - 2*q[3]*q[3]);\\r\\n var pitch = Math.asin(2*q[0]*q[2] - 2*q[3]*q[1]);\\r\\n var roll = Math.atan2(2*q[0]*q[1] + 2*q[2]*q[3], 1 - 2*q[1]*q[1] - 2*q[2]*q[2]);\\r\\n\\tif(!out)\\r\\n\\t\\tout = vec3.create();\\r\\n\\tvec3.set(out, yaw, pitch, roll);\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nquat.fromEuler = function(out, vec) {\\r\\n\\tvar yaw = vec[0];\\r\\n\\tvar pitch = vec[1];\\r\\n\\tvar roll = vec[2];\\r\\n\\r\\n\\tvar C1 = Math.cos(yaw*0.5);\\r\\n\\tvar C2 = Math.cos(pitch*0.5);\\r\\n\\tvar C3 = Math.cos(roll*0.5);\\r\\n\\tvar S1 = Math.sin(yaw*0.5);\\r\\n\\tvar S2 = Math.sin(pitch*0.5);\\r\\n\\tvar S3 = Math.sin(roll*0.5);\\r\\n\\r\\n\\tvar x = C1*C2*C3 + S1*S2*S3;\\r\\n\\tvar y = S1*C2*C3 - C1*S2*S3;\\r\\n\\tvar z = C1*S2*C3 + S1*C2*S3;\\r\\n\\tvar w = C1*C2*S3 - S1*S2*C3;\\r\\n\\r\\n\\tquat.set(out, x,y,z,w );\\r\\n\\tquat.normalize(out,out); //necessary?\\r\\n\\treturn out;\\r\\n}\\r\\n*/\\r\\n\\r\\nquat.toEuler = function(out, q)\\r\\n{\\r\\n var heading = Math.atan2(2*q[1]*q[3] - 2*q[0]*q[2], 1 - 2*q[1]*q[1] - 2*q[2]*q[2]);\\r\\n var attitude = Math.asin(2*q[0]*q[1] + 2*q[2]*q[3]);\\r\\n var bank = Math.atan2(2*q[0]*q[3] - 2*q[1]*q[2], 1 - 2*q[0]*q[0] - 2*q[2]*q[2]);\\r\\n\\tif(!out)\\r\\n\\t\\tout = vec3.create();\\r\\n\\tvec3.set(out, heading, attitude, bank);\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nquat.fromEuler = function(out, vec) {\\r\\n\\tvar heading = vec[0];\\r\\n\\tvar attitude = vec[1];\\r\\n\\tvar bank = vec[2];\\r\\n\\r\\n\\tvar C1 = Math.cos(heading); //yaw\\r\\n\\tvar C2 = Math.cos(attitude); //pitch\\r\\n\\tvar C3 = Math.cos(bank); //roll\\r\\n\\tvar S1 = Math.sin(heading);\\r\\n\\tvar S2 = Math.sin(attitude);\\r\\n\\tvar S3 = Math.sin(bank);\\r\\n\\r\\n\\tvar w = Math.sqrt(1.0 + C1 * C2 + C1*C3 - S1 * S2 * S3 + C2*C3) * 0.5;\\r\\n\\tif(w == 0.0)\\r\\n\\t{\\r\\n\\t\\tw = 0.000001;\\r\\n\\t\\t//quat.set(out, 0,0,0,1 );\\r\\n\\t\\t//return out;\\r\\n\\t}\\r\\n\\r\\n\\tvar x = (C2 * S3 + C1 * S3 + S1 * S2 * C3) / (4.0 * w);\\r\\n\\tvar y = (S1 * C2 + S1 * C3 + C1 * S2 * S3) / (4.0 * w);\\r\\n\\tvar z = (-S1 * S3 + C1 * S2 * C3 + S2) /(4.0 * w);\\r\\n\\tquat.set(out, x,y,z,w );\\r\\n\\tquat.normalize(out,out);\\r\\n\\treturn out;\\r\\n};\\r\\n\\r\\n\\r\\n//not tested\\r\\nquat.fromMat4 = function(out,m)\\r\\n{\\r\\n\\tvar trace = m[0] + m[5] + m[10];\\r\\n\\tif ( trace > 0.0 )\\r\\n\\t{\\r\\n\\t\\tvar s = Math.sqrt( trace + 1.0 );\\r\\n\\t\\tout[3] = s * 0.5;//w\\r\\n\\t\\tvar recip = 0.5 / s;\\r\\n\\t\\tout[0] = ( m[9] - m[6] ) * recip; //2,1 1,2\\r\\n\\t\\tout[1] = ( m[2] - m[8] ) * recip; //0,2 2,0\\r\\n\\t\\tout[2] = ( m[4] - m[1] ) * recip; //1,0 0,1\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tvar i = 0;\\r\\n\\t\\tif( m[5] > m[0] )\\r\\n\\t\\t i = 1;\\r\\n\\t\\tif( m[10] > m[i*4+i] )\\r\\n\\t\\t i = 2;\\r\\n\\t\\tvar j = ( i + 1 ) % 3;\\r\\n\\t\\tvar k = ( j + 1 ) % 3;\\r\\n\\t\\tvar s = Math.sqrt( m[i*4+i] - m[j*4+j] - m[k*4+k] + 1.0 );\\r\\n\\t\\tout[i] = 0.5 * s;\\r\\n\\t\\tvar recip = 0.5 / s;\\r\\n\\t\\tout[3] = ( m[k*4+j] - m[j*4+k] ) * recip;//w\\r\\n\\t\\tout[j] = ( m[j*4+i] + m[i*4+j] ) * recip;\\r\\n\\t\\tout[k] = ( m[k*4+i] + m[i*4+k] ) * recip;\\r\\n\\t}\\r\\n\\tquat.normalize(out,out);\\r\\n}\\r\\n\\r\\n//col according to common matrix notation, here are stored as rows\\r\\nvec3.getMat3Column = function(out, m, index )\\r\\n{\\r\\n\\tout[0] = m[index*3];\\r\\n\\tout[1] = m[index*3 + 1];\\r\\n\\tout[2] = m[index*3 + 2];\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nmat3.setColumn = function(out, v, index )\\r\\n{\\r\\n\\tout[index*3] = v[0];\\r\\n\\tout[index*3+1] = v[1];\\r\\n\\tout[index*3+2] = v[2];\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n\\r\\n//http://matthias-mueller-fischer.ch/publications/stablePolarDecomp.pdf\\r\\n//reusing the previous quaternion as an indicator to keep perpendicularity\\r\\nquat.fromMat3AndQuat = (function(){\\r\\n\\tvar temp_mat3 = mat3.create();\\r\\n\\tvar temp_quat = quat.create();\\r\\n\\tvar Rcol0 = vec3.create();\\r\\n\\tvar Rcol1 = vec3.create();\\r\\n\\tvar Rcol2 = vec3.create();\\r\\n\\tvar Acol0 = vec3.create();\\r\\n\\tvar Acol1 = vec3.create();\\r\\n\\tvar Acol2 = vec3.create();\\r\\n\\tvar RAcross0 = vec3.create();\\r\\n\\tvar RAcross1 = vec3.create();\\r\\n\\tvar RAcross2 = vec3.create();\\r\\n\\tvar omega = vec3.create();\\r\\n\\tvar axis = mat3.create();\\r\\n\\r\\n\\treturn function( q, A, max_iter )\\r\\n\\t{\\r\\n\\t\\tmax_iter = max_iter || 25;\\r\\n\\t\\tfor (var iter = 0; iter < max_iter; ++iter)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar R = mat3.fromQuat( temp_mat3, q );\\r\\n\\t\\t\\tvec3.getMat3Column(Rcol0,R,0);\\r\\n\\t\\t\\tvec3.getMat3Column(Rcol1,R,1);\\r\\n\\t\\t\\tvec3.getMat3Column(Rcol2,R,2);\\r\\n\\t\\t\\tvec3.getMat3Column(Acol0,A,0);\\r\\n\\t\\t\\tvec3.getMat3Column(Acol1,A,1);\\r\\n\\t\\t\\tvec3.getMat3Column(Acol2,A,2);\\r\\n\\t\\t\\tvec3.cross( RAcross0, Rcol0, Acol0 );\\r\\n\\t\\t\\tvec3.cross( RAcross1, Rcol1, Acol1 );\\r\\n\\t\\t\\tvec3.cross( RAcross2, Rcol2, Acol2 );\\r\\n\\t\\t\\tvec3.add( omega, RAcross0, RAcross1 );\\r\\n\\t\\t\\tvec3.add( omega, omega, RAcross2 );\\r\\n\\t\\t\\tvar d = 1.0 / Math.abs( vec3.dot(Rcol0,Acol0) + vec3.dot(Rcol1,Acol1) + vec3.dot(Rcol2,Acol2) ) + 1.0e-9;\\r\\n\\t\\t\\tvec3.scale( omega, omega, d );\\r\\n\\t\\t\\tvar w = vec3.length(omega);\\r\\n\\t\\t\\tif (w < 1.0e-9)\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tvec3.scale(omega,omega,1/w); //normalize\\r\\n\\t\\t\\tquat.setAxisAngle( temp_quat, omega, w );\\r\\n\\t\\t\\tquat.mul( q, temp_quat, q );\\r\\n\\t\\t\\tquat.normalize(q,q);\\r\\n\\t\\t}\\r\\n\\t\\treturn q;\\r\\n\\t};\\r\\n})();\\r\\n\\r\\n//http://number-none.com/product/IK%20with%20Quaternion%20Joint%20Limits/\\r\\nquat.rotateToFrom = (function(){ \\r\\n\\tvar tmp = vec3.create();\\r\\n\\treturn function(out, v1, v2)\\r\\n\\t{\\r\\n\\t\\tout = out || quat.create();\\r\\n\\t\\tvar axis = vec3.cross(tmp, v1, v2);\\r\\n\\t\\tvar dot = vec3.dot(v1, v2);\\r\\n\\t\\tif( dot < -1 + 0.01){\\r\\n\\t\\t\\tout[0] = 0;\\r\\n\\t\\t\\tout[1] = 1; \\r\\n\\t\\t\\tout[2] = 0; \\r\\n\\t\\t\\tout[3] = 0; \\r\\n\\t\\t\\treturn out;\\r\\n\\t\\t}\\r\\n\\t\\tout[0] = axis[0] * 0.5;\\r\\n\\t\\tout[1] = axis[1] * 0.5; \\r\\n\\t\\tout[2] = axis[2] * 0.5; \\r\\n\\t\\tout[3] = (1 + dot) * 0.5; \\r\\n\\t\\tquat.normalize(out, out); \\r\\n\\t\\treturn out; \\r\\n\\t}\\r\\n})();\\r\\n\\r\\nquat.lookAt = (function(){ \\r\\n\\tvar axis = vec3.create();\\r\\n\\t\\r\\n\\treturn function( out, forwardVector, up )\\r\\n\\t{\\r\\n\\t\\tvar dot = vec3.dot( vec3.FRONT, forwardVector );\\r\\n\\r\\n\\t\\tif ( Math.abs( dot - (-1.0)) < 0.000001 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tout.set( vec3.UP );\\r\\n\\t\\t\\tout[3] = Math.PI;\\r\\n\\t\\t\\treturn out;\\r\\n\\t\\t}\\r\\n\\t\\tif ( Math.abs(dot - 1.0) < 0.000001 )\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn quat.identity( out );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar rotAngle = Math.acos( dot );\\r\\n\\t\\tvec3.cross( axis, vec3.FRONT, forwardVector );\\r\\n\\t\\tvec3.normalize( axis, axis );\\r\\n\\t\\tquat.setAxisAngle( out, axis, rotAngle );\\r\\n\\t\\treturn out;\\r\\n\\t}\\r\\n})();\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* @namespace GL\\r\\n*/\\r\\n\\r\\n/**\\r\\n* Indexer used to reuse vertices among a mesh\\r\\n* @class Indexer\\r\\n* @constructor\\r\\n*/\\r\\nGL.Indexer = function Indexer() {\\r\\n this.unique = [];\\r\\n this.indices = [];\\r\\n this.map = {};\\r\\n}\\r\\nGL.Indexer.prototype = {\\r\\n\\tadd: function(obj) {\\r\\n var key = JSON.stringify(obj);\\r\\n if (!(key in this.map)) {\\r\\n this.map[key] = this.unique.length;\\r\\n this.unique.push(obj);\\r\\n }\\r\\n return this.map[key];\\r\\n }\\r\\n};\\r\\n\\r\\n/**\\r\\n* A data buffer to be stored in the GPU\\r\\n* @class Buffer\\r\\n* @constructor\\r\\n* @param {Number} target gl.ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER\\r\\n* @param {ArrayBufferView} data the data in typed-array format\\r\\n* @param {number} spacing number of numbers per component (3 per vertex, 2 per uvs...), default 3\\r\\n* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW \\r\\n*/\\r\\nGL.Buffer = function Buffer( target, data, spacing, stream_type, gl ) {\\r\\n\\tif(GL.debug)\\r\\n\\t\\tconsole.log(\\\"GL.Buffer created\\\");\\r\\n\\r\\n\\tif(gl !== null)\\r\\n\\t\\tgl = gl || global.gl;\\r\\n\\tthis.gl = gl;\\r\\n\\r\\n\\tthis.buffer = null; //webgl buffer\\r\\n\\tthis.target = target; //GL.ARRAY_BUFFER, GL.ELEMENT_ARRAY_BUFFER\\r\\n\\tthis.attribute = null; //name of the attribute in the shader (\\\"a_vertex\\\",\\\"a_normal\\\",\\\"a_coord\\\",...)\\r\\n\\r\\n\\t//optional\\r\\n\\tthis.data = data;\\r\\n\\tthis.spacing = spacing || 3;\\r\\n\\r\\n\\tif(this.data && this.gl)\\r\\n\\t\\tthis.upload(stream_type);\\r\\n}\\r\\n\\r\\n/**\\r\\n* binds the buffer to a attrib location\\r\\n* @method bind\\r\\n* @param {number} location the location of the shader (from shader.attributes[ name ])\\r\\n*/\\r\\nGL.Buffer.prototype.bind = function( location, gl )\\r\\n{\\r\\n\\tgl = gl || this.gl;\\r\\n\\r\\n\\tgl.bindBuffer( gl.ARRAY_BUFFER, this.buffer );\\r\\n\\tgl.enableVertexAttribArray( location );\\r\\n\\tgl.vertexAttribPointer( location, this.spacing, this.buffer.gl_type, false, 0, 0);\\r\\n}\\r\\n\\r\\n/**\\r\\n* unbinds the buffer from an attrib location\\r\\n* @method unbind\\r\\n* @param {number} location the location of the shader\\r\\n*/\\r\\nGL.Buffer.prototype.unbind = function( location, gl )\\r\\n{\\r\\n\\tgl = gl || this.gl;\\r\\n\\tgl.disableVertexAttribArray( location );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Applies an action to every vertex in this buffer\\r\\n* @method forEach\\r\\n* @param {function} callback to be called for every vertex (or whatever is contained in the buffer)\\r\\n*/\\r\\nGL.Buffer.prototype.forEach = function(callback)\\r\\n{\\r\\n\\tvar d = this.data;\\r\\n\\tfor (var i = 0, s = this.spacing, l = d.length; i < l; i += s)\\r\\n\\t{\\r\\n\\t\\tcallback(d.subarray(i,i+s),i);\\r\\n\\t}\\r\\n\\treturn this; //to concatenate\\r\\n}\\r\\n\\r\\n/**\\r\\n* Applies a mat4 transform to every triplets in the buffer (assuming they are points)\\r\\n* No upload is performed (to ensure efficiency in case there are several operations performed)\\r\\n* @method applyTransform\\r\\n* @param {mat4} mat\\r\\n*/\\r\\nGL.Buffer.prototype.applyTransform = function(mat)\\r\\n{\\r\\n\\tvar d = this.data;\\r\\n\\tfor (var i = 0, s = this.spacing, l = d.length; i < l; i += s)\\r\\n\\t{\\r\\n\\t\\tvar v = d.subarray(i,i+s);\\r\\n\\t\\tvec3.transformMat4(v,v,mat);\\r\\n\\t}\\r\\n\\treturn this; //to concatenate\\r\\n}\\r\\n\\r\\n/**\\r\\n* Uploads the buffer data (stored in this.data) to the GPU\\r\\n* @method upload\\r\\n* @param {number} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW \\r\\n*/\\r\\nGL.Buffer.prototype.upload = function( stream_type ) { //default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW )\\r\\n\\tvar spacing = this.spacing || 3; //default spacing\\t\\r\\n\\tvar gl = this.gl;\\r\\n\\tif(!gl)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this.data)\\r\\n\\t\\tthrow(\\\"No data supplied\\\");\\r\\n\\r\\n\\tvar data = this.data;\\r\\n\\tif(!data.buffer)\\r\\n\\t\\tthrow(\\\"Buffers must be typed arrays\\\");\\r\\n\\r\\n\\t//I store some stuff inside the WebGL buffer instance, it is supported\\r\\n\\tthis.buffer = this.buffer || gl.createBuffer();\\r\\n\\tif(!this.buffer)\\r\\n\\t\\treturn; //if the context is lost...\\r\\n\\r\\n\\tthis.buffer.length = data.length;\\r\\n\\tthis.buffer.spacing = spacing;\\r\\n\\r\\n\\t//store the data format\\r\\n\\tswitch( data.constructor )\\r\\n\\t{\\r\\n\\t\\tcase Int8Array: this.buffer.gl_type = gl.BYTE; break;\\r\\n\\t\\tcase Uint8ClampedArray: \\r\\n\\t\\tcase Uint8Array: this.buffer.gl_type = gl.UNSIGNED_BYTE; break;\\r\\n\\t\\tcase Int16Array: this.buffer.gl_type = gl.SHORT; break;\\r\\n\\t\\tcase Uint16Array: this.buffer.gl_type = gl.UNSIGNED_SHORT; break;\\r\\n\\t\\tcase Int32Array: this.buffer.gl_type = gl.INT; break;\\r\\n\\t\\tcase Uint32Array: this.buffer.gl_type = gl.UNSIGNED_INT; break;\\r\\n\\t\\tcase Float32Array: this.buffer.gl_type = gl.FLOAT; break;\\r\\n\\t\\tdefault: throw(\\\"unsupported buffer type\\\");\\r\\n\\t}\\r\\n\\r\\n\\tif(this.target == gl.ARRAY_BUFFER && ( this.buffer.gl_type == gl.INT || this.buffer.gl_type == gl.UNSIGNED_INT ))\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"WebGL does not support UINT32 or INT32 as vertex buffer types, converting to FLOAT\\\");\\r\\n\\t\\tthis.buffer.gl_type = gl.FLOAT;\\r\\n\\t\\tdata = new Float32Array(data);\\r\\n\\t}\\r\\n\\r\\n\\tgl.bindBuffer(this.target, this.buffer);\\r\\n\\tgl.bufferData(this.target, data , stream_type || this.stream_type || gl.STATIC_DRAW);\\r\\n};\\r\\n//legacy\\r\\nGL.Buffer.prototype.compile = GL.Buffer.prototype.upload;\\r\\n\\r\\n\\r\\n/**\\r\\n* Assign data to buffer and uploads it (it allows range)\\r\\n* @method setData\\r\\n* @param {ArrayBufferView} data in Float32Array format usually\\r\\n* @param {number} offset offset in bytes\\r\\n*/\\r\\nGL.Buffer.prototype.setData = function( data, offset )\\r\\n{\\r\\n\\tif(!data.buffer)\\r\\n\\t\\tthrow(\\\"Data must be typed array\\\");\\r\\n\\toffset = offset || 0;\\r\\n\\r\\n\\tif(!this.data)\\r\\n\\t{\\r\\n\\t\\tthis.data = data;\\r\\n\\t\\tthis.upload();\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\telse if( this.data.length < data.length )\\r\\n\\t\\tthrow(\\\"buffer is not big enough, you cannot set data to a smaller buffer\\\");\\r\\n\\r\\n\\tif(this.data != data)\\r\\n\\t{\\r\\n\\t\\tif(this.data.length == data.length)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.data.set( data );\\r\\n\\t\\t\\tthis.upload();\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//upload just part of it\\r\\n\\t\\tvar new_data_view = new Uint8Array( data.buffer, data.buffer.byteOffset, data.buffer.byteLength );\\r\\n\\t\\tvar data_view = new Uint8Array( this.data.buffer );\\r\\n\\t\\tdata_view.set( new_data_view, offset );\\r\\n\\t\\tthis.uploadRange( offset, new_data_view.length );\\r\\n\\t}\\r\\n\\r\\n};\\r\\n\\r\\n\\r\\n/**\\r\\n* Uploads part of the buffer data (stored in this.data) to the GPU\\r\\n* @method uploadRange\\r\\n* @param {number} start offset in bytes\\r\\n* @param {number} size sizes in bytes\\r\\n*/\\r\\nGL.Buffer.prototype.uploadRange = function(start, size)\\r\\n{\\r\\n\\tif(!this.data)\\r\\n\\t\\tthrow(\\\"No data stored in this buffer\\\");\\r\\n\\r\\n\\tvar data = this.data;\\r\\n\\tif(!data.buffer)\\r\\n\\t\\tthrow(\\\"Buffers must be typed arrays\\\");\\r\\n\\r\\n\\t//cut fragment to upload (no way to avoid GC here, no function to specify the size in WebGL 1.0, but there is one in WebGL 2.0)\\r\\n\\tvar view = new Uint8Array( this.data.buffer, start, size );\\r\\n\\r\\n\\tvar gl = this.gl;\\r\\n\\tgl.bindBuffer(this.target, this.buffer);\\r\\n\\tgl.bufferSubData(this.target, start, view );\\r\\n};\\r\\n\\r\\n/**\\r\\n* Clones one buffer (it allows to share the same data between both buffers)\\r\\n* @method clone\\r\\n* @param {boolean} share if you want that both buffers share the same data (default false)\\r\\n* return {GL.Buffer} buffer cloned\\r\\n*/\\r\\nGL.Buffer.prototype.clone = function(share)\\r\\n{\\r\\n\\tvar buffer = new GL.Buffer();\\r\\n\\tif(share)\\r\\n\\t{\\r\\n\\t\\tfor(var i in this)\\r\\n\\t\\t\\tbuffer[i] = this[i];\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tif(this.target)\\r\\n\\t\\t\\tbuffer.target = this.target;\\r\\n\\t\\tif(this.gl)\\r\\n\\t\\t\\tbuffer.gl = this.gl;\\r\\n\\t\\tif(this.spacing)\\r\\n\\t\\t\\tbuffer.spacing = this.spacing;\\r\\n\\t\\tif(this.data) //clone data\\r\\n\\t\\t{\\r\\n\\t\\t\\tbuffer.data = new global[ this.data.constructor ]( this.data );\\r\\n\\t\\t\\tbuffer.upload();\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn buffer;\\r\\n}\\r\\n\\r\\n\\r\\nGL.Buffer.prototype.toJSON = function()\\r\\n{\\r\\n\\tif(!this.data)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"cannot serialize a mesh without data\\\");\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\treturn {\\r\\n\\t\\tdata_type: getClassName(this.data),\\r\\n\\t\\tdata: this.data.toJSON(),\\r\\n\\t\\ttarget: this.target,\\r\\n\\t\\tattribute: this.attribute,\\r\\n\\t\\tspacing: this.spacing\\r\\n\\t};\\r\\n}\\r\\n\\r\\nGL.Buffer.prototype.fromJSON = function(o)\\r\\n{\\r\\n\\tvar data_type = global[ o.data_type ] || Float32Array;\\r\\n\\tthis.data = new data_type( o.data ); //cloned\\r\\n\\tthis.target = o.target;\\r\\n\\tthis.spacing = o.spacing || 3;\\r\\n\\tthis.attribute = o.attribute;\\r\\n\\tthis.upload( GL.STATIC_DRAW );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Deletes the content from the GPU and destroys the handler\\r\\n* @method delete\\r\\n*/\\r\\nGL.Buffer.prototype.delete = function()\\r\\n{\\r\\n\\tvar gl = this.gl;\\r\\n\\tgl.deleteBuffer( this.buffer );\\r\\n\\tthis.buffer = null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Base class for meshes, it wraps several buffers and some global info like the bounding box\\r\\n* @class Mesh\\r\\n* @param {Object} vertexBuffers object with all the vertex streams\\r\\n* @param {Object} indexBuffers object with all the indices streams\\r\\n* @param {Object} options\\r\\n* @param {WebGLContext} gl [Optional] gl context where to create the mesh\\r\\n* @constructor\\r\\n*/\\r\\nglobal.Mesh = GL.Mesh = function Mesh( vertexbuffers, indexbuffers, options, gl )\\r\\n{\\r\\n\\tif(GL.debug)\\r\\n\\t\\tconsole.log(\\\"GL.Mesh created\\\");\\r\\n\\r\\n\\tif( gl !== null )\\r\\n\\t{\\r\\n\\t\\tgl = gl || global.gl;\\r\\n\\t\\tthis.gl = gl;\\r\\n\\t}\\r\\n\\r\\n\\t//used to avoid problems with resources moving between different webgl context\\r\\n\\tthis._context_id = gl.context_id; \\r\\n\\r\\n\\tthis.vertexBuffers = {};\\r\\n\\tthis.indexBuffers = {};\\r\\n\\r\\n\\t//here you can store extra info, like groups, which is an array of { name, start, length, material }\\r\\n\\tthis.info = {\\r\\n\\t\\tgroups: []\\r\\n\\t}; \\r\\n\\tthis._bounding = BBox.create(); //here you can store a AABB in BBox format\\r\\n\\r\\n\\tif(vertexbuffers || indexbuffers)\\r\\n\\t\\tthis.addBuffers( vertexbuffers, indexbuffers, options ? options.stream_type : null );\\r\\n\\r\\n\\tif(options)\\r\\n\\t\\tfor(var i in options)\\r\\n\\t\\t\\tthis[i] = options[i];\\r\\n};\\r\\n\\r\\nMesh.common_buffers = {\\r\\n\\t\\\"vertices\\\": { spacing:3, attribute: \\\"a_vertex\\\"},\\r\\n\\t\\\"vertices2D\\\": { spacing:2, attribute: \\\"a_vertex2D\\\"},\\r\\n\\t\\\"normals\\\": { spacing:3, attribute: \\\"a_normal\\\"},\\r\\n\\t\\\"coords\\\": { spacing:2, attribute: \\\"a_coord\\\"},\\r\\n\\t\\\"coords1\\\": { spacing:2, attribute: \\\"a_coord1\\\"},\\r\\n\\t\\\"coords2\\\": { spacing:2, attribute: \\\"a_coord2\\\"},\\r\\n\\t\\\"colors\\\": { spacing:4, attribute: \\\"a_color\\\"}, \\r\\n\\t\\\"tangents\\\": { spacing:3, attribute: \\\"a_tangent\\\"},\\r\\n\\t\\\"bone_indices\\\": { spacing:4, attribute: \\\"a_bone_indices\\\", type: Uint8Array },\\r\\n\\t\\\"weights\\\": { spacing:4, attribute: \\\"a_weights\\\"},\\r\\n\\t\\\"extra\\\": { spacing:1, attribute: \\\"a_extra\\\"},\\r\\n\\t\\\"extra2\\\": { spacing:2, attribute: \\\"a_extra2\\\"},\\r\\n\\t\\\"extra3\\\": { spacing:3, attribute: \\\"a_extra3\\\"},\\r\\n\\t\\\"extra4\\\": { spacing:4, attribute: \\\"a_extra4\\\"}\\r\\n};\\r\\n\\r\\nMesh.default_datatype = Float32Array;\\r\\n\\r\\nObject.defineProperty( Mesh.prototype, \\\"bounding\\\", {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tif(!v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(v.length < 13)\\r\\n\\t\\t\\tthrow(\\\"Bounding must use the BBox bounding format of 13 floats: center, halfsize, min, max, radius\\\");\\r\\n\\t\\tthis._bounding.set(v);\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._bounding;\\r\\n\\t}\\r\\n});\\r\\n\\r\\n/**\\r\\n* Adds buffer to mesh\\r\\n* @method addBuffer\\r\\n* @param {string} name\\r\\n* @param {Buffer} buffer \\r\\n*/\\r\\n\\r\\nMesh.prototype.addBuffer = function(name, buffer)\\r\\n{\\r\\n\\tif(buffer.target == gl.ARRAY_BUFFER)\\r\\n\\t\\tthis.vertexBuffers[name] = buffer;\\r\\n\\telse\\r\\n\\t\\tthis.indexBuffers[name] = buffer;\\r\\n\\r\\n\\tif(!buffer.attribute)\\r\\n\\t{\\r\\n\\t\\tvar info = GL.Mesh.common_buffers[name];\\r\\n\\t\\tif(info)\\r\\n\\t\\t\\tbuffer.attribute = info.attribute;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Adds vertex and indices buffers to a mesh\\r\\n* @method addBuffers\\r\\n* @param {Object} vertexBuffers object with all the vertex streams\\r\\n* @param {Object} indexBuffers object with all the indices streams\\r\\n* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW )\\r\\n*/\\r\\nMesh.prototype.addBuffers = function( vertexbuffers, indexbuffers, stream_type )\\r\\n{\\r\\n\\tvar num_vertices = 0;\\r\\n\\r\\n\\tif(this.vertexBuffers[\\\"vertices\\\"])\\r\\n\\t\\tnum_vertices = this.vertexBuffers[\\\"vertices\\\"].data.length / 3;\\r\\n\\r\\n\\tfor(var i in vertexbuffers)\\r\\n\\t{\\r\\n\\t\\tvar data = vertexbuffers[i];\\r\\n\\t\\tif(!data) \\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t\\r\\n\\t\\tif( data.constructor == GL.Buffer )\\r\\n\\t\\t{\\r\\n\\t\\t\\tdata = data.data;\\r\\n\\t\\t}\\r\\n\\t\\telse if( typeof(data[0]) != \\\"number\\\") //linearize: (transform Arrays in typed arrays)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar newdata = [];\\r\\n\\t\\t\\tfor (var j = 0, chunk = 10000; j < data.length; j += chunk) {\\r\\n\\t\\t\\t newdata = Array.prototype.concat.apply(newdata, data.slice(j, j + chunk));\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tdata = newdata;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar stream_info = GL.Mesh.common_buffers[i];\\r\\n\\r\\n\\t\\t//cast to typed float32 if no type is specified\\r\\n\\t\\tif(data.constructor === Array)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar datatype = GL.Mesh.default_datatype;\\r\\n\\t\\t\\tif(stream_info && stream_info.type)\\r\\n\\t\\t\\t\\tdatatype = stream_info.type;\\r\\n\\t\\t\\tdata = new datatype( data );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//compute spacing\\r\\n\\t\\tif(i == \\\"vertices\\\")\\r\\n\\t\\t\\tnum_vertices = data.length / 3;\\r\\n\\t\\tvar spacing = data.length / num_vertices;\\r\\n\\t\\tif(stream_info && stream_info.spacing)\\r\\n\\t\\t\\tspacing = stream_info.spacing;\\r\\n\\r\\n\\t\\t//add and upload\\r\\n\\t\\tvar attribute = \\\"a_\\\" + i;\\r\\n\\t\\tif(stream_info && stream_info.attribute)\\r\\n\\t\\t\\tattribute = stream_info.attribute;\\r\\n\\t\\r\\n\\t\\tif( this.vertexBuffers[i] )\\r\\n\\t\\t\\tthis.updateVertexBuffer( i, attribute, spacing, data, stream_type );\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.createVertexBuffer( i, attribute, spacing, data, stream_type );\\r\\n\\t}\\r\\n\\r\\n\\tif(indexbuffers)\\r\\n\\t\\tfor(var i in indexbuffers)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar data = indexbuffers[i];\\r\\n\\t\\t\\tif(!data) continue;\\r\\n\\r\\n\\t\\t\\tif( data.constructor == GL.Buffer )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tdata = data.data;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif( typeof(data[0]) != \\\"number\\\") //linearize\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnewdata = [];\\r\\n\\t\\t\\t\\tfor (var i = 0, chunk = 10000; i < data.length; i += chunk) {\\r\\n\\t\\t\\t\\t newdata = Array.prototype.concat.apply(newdata, data.slice(i, i + chunk));\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tdata = newdata;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//cast to typed\\r\\n\\t\\t\\tif(data.constructor === Array)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar datatype = Uint16Array;\\r\\n\\t\\t\\t\\tif(num_vertices > 256*256)\\r\\n\\t\\t\\t\\t\\tdatatype = Uint32Array;\\r\\n\\t\\t\\t\\tdata = new datatype( data );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tthis.createIndexBuffer( i, data );\\r\\n\\t\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Creates a new empty buffer and attachs it to this mesh\\r\\n* @method createVertexBuffer\\r\\n* @param {String} name \\\"vertices\\\",\\\"normals\\\"...\\r\\n* @param {String} attribute name of the stream in the shader \\\"a_vertex\\\",\\\"a_normal\\\",... [optional, if omitted is used the common_buffers]\\r\\n* @param {number} spacing components per vertex [optional, if ommited is used the common_buffers, if not found then uses 3 ]\\r\\n* @param {ArrayBufferView} buffer_data the data in typed array format [optional, if ommited it created an empty array of getNumVertices() * spacing]\\r\\n* @param {enum} stream_type [optional, default = gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW ) ]\\r\\n*/\\r\\n\\r\\nMesh.prototype.createVertexBuffer = function( name, attribute, buffer_spacing, buffer_data, stream_type ) {\\r\\n\\r\\n\\tvar common = GL.Mesh.common_buffers[name]; //generic info about a buffer with the same name\\r\\n\\r\\n\\tif (!attribute && common)\\r\\n\\t\\tattribute = common.attribute;\\r\\n\\r\\n\\tif (!attribute)\\r\\n\\t\\tthrow(\\\"Buffer added to mesh without attribute name\\\");\\r\\n\\r\\n\\tif (!buffer_spacing && common)\\r\\n\\t{\\r\\n\\t\\tif(common && common.spacing)\\r\\n\\t\\t\\tbuffer_spacing = common.spacing;\\r\\n\\t\\telse\\r\\n\\t\\t\\tbuffer_spacing = 3;\\r\\n\\t}\\r\\n\\r\\n\\tif(!buffer_data)\\r\\n\\t{\\r\\n\\t\\tvar num = this.getNumVertices();\\r\\n\\t\\tif(!num)\\r\\n\\t\\t\\tthrow(\\\"Cannot create an empty buffer in a mesh without vertices (vertices are needed to know the size)\\\");\\r\\n\\t\\tbuffer_data = new (GL.Mesh.default_datatype)(num * buffer_spacing);\\r\\n\\t}\\r\\n\\r\\n\\tif(!buffer_data.buffer)\\r\\n\\t\\tthrow(\\\"Buffer data MUST be typed array\\\");\\r\\n\\r\\n\\t//used to ensure the buffers are held in the same gl context as the mesh\\r\\n\\tvar buffer = this.vertexBuffers[name] = new GL.Buffer( gl.ARRAY_BUFFER, buffer_data, buffer_spacing, stream_type, this.gl );\\r\\n\\tbuffer.name = name;\\r\\n\\tbuffer.attribute = attribute;\\r\\n\\r\\n\\treturn buffer;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Updates a vertex buffer \\r\\n* @method updateVertexBuffer\\r\\n* @param {String} name the name of the buffer\\r\\n* @param {String} attribute the name of the attribute in the shader\\r\\n* @param {number} spacing number of numbers per component (3 per vertex, 2 per uvs...), default 3\\r\\n* @param {*} data the array with all the data\\r\\n* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW \\r\\n*/\\r\\nMesh.prototype.updateVertexBuffer = function( name, attribute, buffer_spacing, buffer_data, stream_type ) {\\r\\n\\tvar buffer = this.vertexBuffers[name];\\r\\n\\tif(!buffer)\\r\\n\\t{\\r\\n\\t\\tconsole.log(\\\"buffer not found: \\\",name);\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tif(!buffer_data.length)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tbuffer.attribute = attribute;\\r\\n\\tbuffer.spacing = buffer_spacing;\\r\\n\\tbuffer.data = buffer_data;\\r\\n\\tbuffer.upload( stream_type );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Removes a vertex buffer from the mesh\\r\\n* @method removeVertexBuffer\\r\\n* @param {String} name \\\"vertices\\\",\\\"normals\\\"...\\r\\n* @param {Boolean} free if you want to remove the data from the GPU\\r\\n*/\\r\\nMesh.prototype.removeVertexBuffer = function(name, free) {\\r\\n\\tvar buffer = this.vertexBuffers[name];\\r\\n\\tif(!buffer)\\r\\n\\t\\treturn;\\r\\n\\tif(free)\\r\\n\\t\\tbuffer.delete();\\r\\n\\tdelete this.vertexBuffers[name];\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a vertex buffer\\r\\n* @method getVertexBuffer\\r\\n* @param {String} name of vertex buffer\\r\\n* @return {Buffer} the buffer\\r\\n*/\\r\\nMesh.prototype.getVertexBuffer = function(name)\\r\\n{\\r\\n\\treturn this.vertexBuffers[name];\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Creates a new empty index buffer and attachs it to this mesh\\r\\n* @method createIndexBuffer\\r\\n* @param {String} name \\r\\n* @param {Typed array} data \\r\\n* @param {enum} stream_type gl.STATIC_DRAW, gl.DYNAMIC_DRAW, gl.STREAM_DRAW\\r\\n*/\\r\\nMesh.prototype.createIndexBuffer = function(name, buffer_data, stream_type) {\\r\\n\\t//(target, data, spacing, stream_type, gl)\\r\\n\\r\\n\\t//cast to typed\\r\\n\\tif(buffer_data.constructor === Array)\\r\\n\\t{\\r\\n\\t\\tvar datatype = Uint16Array;\\r\\n\\t\\tvar vertices = this.vertexBuffers[\\\"vertices\\\"];\\r\\n\\t\\tif(vertices)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar num_vertices = vertices.data.length / 3;\\r\\n\\t\\t\\tif(num_vertices > 256*256)\\r\\n\\t\\t\\t\\tdatatype = Uint32Array;\\r\\n\\t\\t\\tbuffer_data = new datatype( buffer_data );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar buffer = this.indexBuffers[name] = new GL.Buffer(gl.ELEMENT_ARRAY_BUFFER, buffer_data, 0, stream_type, this.gl );\\r\\n\\treturn buffer;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a vertex buffer\\r\\n* @method getBuffer\\r\\n* @param {String} name of vertex buffer\\r\\n* @return {Buffer} the buffer\\r\\n*/\\r\\nMesh.prototype.getBuffer = function(name)\\r\\n{\\r\\n\\treturn this.vertexBuffers[name];\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a index buffer\\r\\n* @method getIndexBuffer\\r\\n* @param {String} name of index buffer\\r\\n* @return {Buffer} the buffer\\r\\n*/\\r\\nMesh.prototype.getIndexBuffer = function(name)\\r\\n{\\r\\n\\treturn this.indexBuffers[name];\\r\\n}\\r\\n\\r\\n/**\\r\\n* Removes an index buffer from the mesh\\r\\n* @method removeIndexBuffer\\r\\n* @param {String} name \\\"vertices\\\",\\\"normals\\\"...\\r\\n* @param {Boolean} free if you want to remove the data from the GPU\\r\\n*/\\r\\nMesh.prototype.removeIndexBuffer = function(name, free) {\\r\\n\\tvar buffer = this.indexBuffers[name];\\r\\n\\tif(!buffer)\\r\\n\\t\\treturn;\\r\\n\\tif(free)\\r\\n\\t\\tbuffer.delete();\\r\\n\\tdelete this.indexBuffers[name];\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Uploads data inside buffers to VRAM.\\r\\n* @method upload\\r\\n* @param {number} buffer_type gl.STATIC_DRAW, gl.DYNAMIC_DRAW, gl.STREAM_DRAW\\r\\n*/\\r\\nMesh.prototype.upload = function(buffer_type) {\\r\\n\\tfor (var attribute in this.vertexBuffers) {\\r\\n\\t\\tvar buffer = this.vertexBuffers[attribute];\\r\\n\\t\\t//buffer.data = this[buffer.name];\\r\\n\\t\\tbuffer.upload(buffer_type);\\r\\n\\t}\\r\\n\\r\\n\\tfor (var name in this.indexBuffers) {\\r\\n\\t\\tvar buffer = this.indexBuffers[name];\\r\\n\\t\\t//buffer.data = this[name];\\r\\n\\t\\tbuffer.upload();\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//LEGACY, plz remove\\r\\nMesh.prototype.compile = Mesh.prototype.upload;\\r\\n\\r\\n\\r\\nMesh.prototype.deleteBuffers = function()\\r\\n{\\r\\n\\tfor(var i in this.vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar buffer = this.vertexBuffers[i];\\r\\n\\t\\tbuffer.delete();\\r\\n\\t}\\r\\n\\tthis.vertexBuffers = {};\\r\\n\\r\\n\\tfor(var i in this.indexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar buffer = this.indexBuffers[i];\\r\\n\\t\\tbuffer.delete();\\r\\n\\t}\\r\\n\\tthis.indexBuffers = {};\\r\\n}\\r\\n\\r\\nMesh.prototype.delete = Mesh.prototype.deleteBuffers;\\r\\n\\r\\nMesh.prototype.bindBuffers = function( shader )\\r\\n{\\r\\n\\t// enable attributes as necessary.\\r\\n\\tfor (var name in this.vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar buffer = this.vertexBuffers[ name ];\\r\\n\\t\\tvar attribute = buffer.attribute || name;\\r\\n\\t\\tvar location = shader.attributes[ attribute ];\\r\\n\\t\\tif (location == null || !buffer.buffer) \\r\\n\\t\\t\\tcontinue; \\r\\n\\t\\tgl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer);\\r\\n\\t\\tgl.enableVertexAttribArray(location);\\r\\n\\t\\tgl.vertexAttribPointer(location, buffer.buffer.spacing, buffer.buffer.gl_type, false, 0, 0);\\r\\n\\t}\\r\\n}\\r\\n\\r\\nMesh.prototype.unbindBuffers = function( shader )\\r\\n{\\r\\n\\t// disable attributes\\r\\n\\tfor (var name in this.vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar buffer = this.vertexBuffers[ name ];\\r\\n\\t\\tvar attribute = buffer.attribute || name;\\r\\n\\t\\tvar location = shader.attributes[ attribute ];\\r\\n\\t\\tif (location == null || !buffer.buffer)\\r\\n\\t\\t\\tcontinue; //ignore this buffer\\r\\n\\t\\tgl.disableVertexAttribArray( shader.attributes[attribute] );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Creates a clone of the mesh, the datarrays are cloned too\\r\\n* @method clone\\r\\n*/\\r\\nMesh.prototype.clone = function( gl )\\r\\n{\\r\\n\\tvar gl = gl || global.gl;\\r\\n\\tvar vbs = {};\\r\\n\\tvar ibs = {};\\r\\n\\r\\n\\tfor(var i in this.vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar b = this.vertexBuffers[i];\\r\\n\\t\\tvbs[i] = new b.data.constructor( b.data ); //clone\\r\\n\\t}\\r\\n\\tfor(var i in this.indexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar b = this.indexBuffers[i];\\r\\n\\t\\tibs[i] = new b.data.constructor( b.data ); //clone\\r\\n\\t}\\r\\n\\r\\n\\treturn new GL.Mesh( vbs, ibs, undefined, gl );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Creates a clone of the mesh, but the data-arrays are shared between both meshes (useful for sharing a mesh between contexts)\\r\\n* @method clone\\r\\n*/\\r\\nMesh.prototype.cloneShared = function( gl )\\r\\n{\\r\\n\\tvar gl = gl || global.gl;\\r\\n\\treturn new GL.Mesh( this.vertexBuffers, this.indexBuffers, undefined, gl );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Creates an object with the info of the mesh (useful to transfer to workers)\\r\\n* @method toObject\\r\\n*/\\r\\nMesh.prototype.toObject = function()\\r\\n{\\r\\n\\tvar vbs = {};\\r\\n\\tvar ibs = {};\\r\\n\\r\\n\\tfor(var i in this.vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar b = this.vertexBuffers[i];\\r\\n\\t\\tvbs[i] = { \\r\\n\\t\\t\\tspacing: b.spacing,\\r\\n\\t\\t\\tdata: new b.data.constructor( b.data ) //clone\\r\\n\\t\\t}; \\r\\n\\t}\\r\\n\\tfor(var i in this.indexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar b = this.indexBuffers[i];\\r\\n\\t\\tibs[i] = { \\r\\n\\t\\t\\tdata: new b.data.constructor( b.data ) //clone\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn { \\r\\n\\t\\tvertexBuffers: vbs, \\r\\n\\t\\tindexBuffers: ibs,\\r\\n\\t\\tinfo: this.info ? cloneObject( this.info ) : null,\\r\\n\\t\\tbounding: this._bounding.toJSON()\\r\\n\\t};\\r\\n}\\r\\n\\r\\n\\r\\nMesh.prototype.toJSON = function()\\r\\n{\\r\\n\\tvar r = {\\r\\n\\t\\tvertexBuffers: {},\\r\\n\\t\\tindexBuffers: {},\\r\\n\\t\\tinfo: this.info ? cloneObject( this.info ) : null,\\r\\n\\t\\tbounding: this._bounding.toJSON() \\r\\n\\t};\\r\\n\\r\\n\\tfor(var i in this.vertexBuffers)\\r\\n\\t\\tr.vertexBuffers[i] = this.vertexBuffers[i].toJSON();\\r\\n\\r\\n\\tfor(var i in this.indexBuffers)\\r\\n\\t\\tr.indexBuffers[i] = this.indexBuffers[i].toJSON();\\r\\n\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\nMesh.prototype.fromJSON = function(o)\\r\\n{\\r\\n\\tthis.vertexBuffers = {};\\r\\n\\tthis.indexBuffers = {};\\r\\n\\r\\n\\tfor(var i in o.vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tif(!o.vertexBuffers[i])\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar buffer = new GL.Buffer();\\r\\n\\t\\tbuffer.fromJSON( o.vertexBuffers[i] );\\r\\n\\t\\tif(!buffer.attribute && GL.Mesh.common_buffers[i])\\r\\n\\t\\t\\tbuffer.attribute = GL.Mesh.common_buffers[i].attribute;\\r\\n\\t\\tthis.vertexBuffers[i] = buffer;\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i in o.indexBuffers)\\r\\n\\t{\\r\\n\\t\\tif(!o.indexBuffers[i])\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar buffer = new GL.Buffer();\\r\\n\\t\\tbuffer.fromJSON( o.indexBuffers[i] );\\r\\n\\t\\tthis.indexBuffers[i] = buffer;\\r\\n\\t}\\r\\n\\r\\n\\tif(o.info)\\r\\n\\t\\tthis.info = cloneObject( o.info );\\r\\n\\tif(o.bounding)\\r\\n\\t\\tthis.bounding = o.bounding; //setter does the job\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Computes some data about the mesh\\r\\n* @method generateMetadata\\r\\n*/\\r\\nMesh.prototype.generateMetadata = function()\\r\\n{\\r\\n\\tvar metadata = {};\\r\\n\\r\\n\\tvar vertices = this.vertexBuffers[\\\"vertices\\\"].data;\\r\\n\\tvar triangles = this.indexBuffers[\\\"triangles\\\"].data;\\r\\n\\r\\n\\tmetadata.vertices = vertices.length / 3;\\r\\n\\tif(triangles)\\r\\n\\t\\tmetadata.faces = triangles.length / 3;\\r\\n\\telse\\r\\n\\t\\tmetadata.faces = vertices.length / 9;\\r\\n\\r\\n\\tmetadata.indexed = !!this.metadata.faces;\\r\\n\\tthis.metadata = metadata;\\r\\n}\\r\\n\\r\\n//never tested\\r\\n/*\\r\\nMesh.prototype.draw = function(shader, mode, range_start, range_length)\\r\\n{\\r\\n\\tif(range_length == 0) return;\\r\\n\\r\\n\\t// Create and enable attribute pointers as necessary.\\r\\n\\tvar length = 0;\\r\\n\\tfor (var attribute in this.vertexBuffers) {\\r\\n\\t var buffer = this.vertexBuffers[attribute];\\r\\n\\t var location = shader.attributes[attribute] ||\\r\\n\\t\\tgl.getAttribLocation(shader.program, attribute);\\r\\n\\t if (location == -1 || !buffer.buffer) continue;\\r\\n\\t shader.attributes[attribute] = location;\\r\\n\\t gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer);\\r\\n\\t gl.enableVertexAttribArray(location);\\r\\n\\t gl.vertexAttribPointer(location, buffer.buffer.spacing, gl.FLOAT, false, 0, 0);\\r\\n\\t length = buffer.buffer.length / buffer.buffer.spacing;\\r\\n\\t}\\r\\n\\r\\n\\t//range rendering\\r\\n\\tvar offset = 0;\\r\\n\\tif(arguments.length > 3) //render a polygon range\\r\\n\\t\\toffset = range_start * (this.indexBuffer ? this.indexBuffer.constructor.BYTES_PER_ELEMENT : 1); //in bytes (Uint16 == 2 bytes)\\r\\n\\r\\n\\tif(arguments.length > 4)\\r\\n\\t\\tlength = range_length;\\r\\n\\telse if (this.indexBuffer)\\r\\n\\t\\tlength = this.indexBuffer.buffer.length - offset;\\r\\n\\r\\n\\t// Disable unused attribute pointers.\\r\\n\\tfor (var attribute in shader.attributes) {\\r\\n\\t if (!(attribute in this.vertexBuffers)) {\\r\\n\\t\\tgl.disableVertexAttribArray(shader.attributes[attribute]);\\r\\n\\t }\\r\\n\\t}\\r\\n\\r\\n\\t// Draw the geometry.\\r\\n\\tif (length && (!this.indexBuffer || indexBuffer.buffer)) {\\r\\n\\t if (this.indexBuffer) {\\r\\n\\t\\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer.buffer);\\r\\n\\t\\tgl.drawElements(mode, length, gl.UNSIGNED_SHORT, offset);\\r\\n\\t } else {\\r\\n\\t\\tgl.drawArrays(mode, offset, length);\\r\\n\\t }\\r\\n\\t}\\r\\n\\r\\n\\treturn this;\\r\\n}\\r\\n*/\\r\\n\\r\\n/**\\r\\n* Creates a new index stream with wireframe \\r\\n* @method computeWireframe\\r\\n*/\\r\\nMesh.prototype.computeWireframe = function() {\\r\\n\\tvar index_buffer = this.indexBuffers[\\\"triangles\\\"];\\r\\n\\r\\n\\tvar vertices = this.vertexBuffers[\\\"vertices\\\"].data;\\r\\n\\tvar num_vertices = (vertices.length/3);\\r\\n\\r\\n\\tif(!index_buffer) //unindexed\\r\\n\\t{\\r\\n\\t\\tvar num_triangles = num_vertices / 3;\\r\\n\\t\\tvar buffer = num_vertices > 256*256 ? new Uint32Array( num_triangles * 6 ) : new Uint16Array( num_triangles * 6 );\\r\\n\\t\\tfor(var i = 0; i < num_vertices; i += 3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tbuffer[i*2] = i;\\r\\n\\t\\t\\tbuffer[i*2+1] = i+1;\\r\\n\\t\\t\\tbuffer[i*2+2] = i+1;\\r\\n\\t\\t\\tbuffer[i*2+3] = i+2;\\r\\n\\t\\t\\tbuffer[i*2+4] = i+2;\\r\\n\\t\\t\\tbuffer[i*2+5] = i;\\r\\n\\t\\t}\\r\\n\\r\\n\\t}\\r\\n\\telse //indexed\\r\\n\\t{\\r\\n\\t\\tvar data = index_buffer.data;\\r\\n\\r\\n\\t\\tvar indexer = new GL.Indexer();\\r\\n\\t\\tfor (var i = 0; i < data.length; i+=3) {\\r\\n\\t\\t var t = data.subarray(i,i+3);\\r\\n\\t\\t for (var j = 0; j < t.length; j++) {\\r\\n\\t\\t\\tvar a = t[j], b = t[(j + 1) % t.length];\\r\\n\\t\\t\\tindexer.add([Math.min(a, b), Math.max(a, b)]);\\r\\n\\t\\t }\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//linearize\\r\\n\\t\\tvar unique = indexer.unique;\\r\\n\\t\\tvar buffer = num_vertices > 256*256 ? new Uint32Array( unique.length * 2 ) : new Uint16Array( unique.length * 2 );\\r\\n\\t\\tfor(var i = 0, l = unique.length; i < l; ++i)\\r\\n\\t\\t\\tbuffer.set(unique[i],i*2);\\r\\n\\t}\\r\\n\\r\\n\\t//create stream\\r\\n\\tthis.createIndexBuffer('wireframe', buffer);\\r\\n\\treturn this;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Multiplies every normal by -1 and uploads it\\r\\n* @method flipNormals\\r\\n* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW)\\r\\n*/\\r\\nMesh.prototype.flipNormals = function( stream_type ) {\\r\\n\\tvar normals_buffer = this.vertexBuffers[\\\"normals\\\"];\\r\\n\\tif(!normals_buffer)\\r\\n\\t\\treturn;\\r\\n\\tvar data = normals_buffer.data;\\r\\n\\tvar l = data.length;\\r\\n\\tfor(var i = 0; i < l; ++i)\\r\\n\\t\\tdata[i] *= -1;\\r\\n\\tnormals_buffer.upload( stream_type );\\r\\n\\r\\n\\t//reverse indices too\\r\\n\\tif( !this.indexBuffers[\\\"triangles\\\"] )\\r\\n\\t\\tthis.computeIndices(); //create indices\\r\\n\\r\\n\\tvar triangles_buffer = this.indexBuffers[\\\"triangles\\\"];\\r\\n\\tvar data = triangles_buffer.data;\\r\\n\\tvar l = data.length;\\r\\n\\tfor(var i = 0; i < l; i += 3)\\r\\n\\t{\\r\\n\\t\\tvar tmp = data[i];\\r\\n\\t\\tdata[i] = data[i+1];\\r\\n\\t\\tdata[i+1] = tmp;\\r\\n\\t\\t//the [i+2] stays the same\\r\\n\\t}\\r\\n\\ttriangles_buffer.upload( stream_type );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Compute indices for a mesh where vertices are shared\\r\\n* @method computeIndices\\r\\n*/\\r\\nMesh.prototype.computeIndices = function() {\\r\\n\\r\\n\\t//cluster by distance\\r\\n\\tvar new_vertices = [];\\r\\n\\tvar new_normals = [];\\r\\n\\tvar new_coords = [];\\r\\n\\r\\n\\tvar indices = [];\\r\\n\\r\\n\\tvar old_vertices_buffer = this.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tvar old_normals_buffer = this.vertexBuffers[\\\"normals\\\"];\\r\\n\\tvar old_coords_buffer = this.vertexBuffers[\\\"coords\\\"];\\r\\n\\r\\n\\tvar old_vertices_data = old_vertices_buffer.data;\\r\\n\\r\\n\\tvar old_normals_data = null;\\r\\n\\tif( old_normals_buffer )\\r\\n\\t\\told_normals_data = old_normals_buffer.data;\\r\\n\\r\\n\\tvar old_coords_data = null;\\r\\n\\tif( old_coords_buffer )\\r\\n\\t\\told_coords_data = old_coords_buffer.data;\\r\\n\\r\\n\\r\\n\\tvar indexer = {};\\r\\n\\r\\n\\tvar l = old_vertices_data.length / 3;\\r\\n\\tfor(var i = 0; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar v = old_vertices_data.subarray( i*3,(i+1)*3 );\\r\\n\\t\\tvar key = (v[0] * 1000)|0;\\r\\n\\r\\n\\t\\t//search in new_vertices\\r\\n\\t\\tvar j = 0;\\r\\n\\t\\tvar candidates = indexer[key];\\r\\n\\t\\tif(candidates)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar l2 = candidates.length;\\r\\n\\t\\t\\tfor(; j < l2; j++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar v2 = new_vertices[ candidates[j] ];\\r\\n\\t\\t\\t\\t//same vertex\\r\\n\\t\\t\\t\\tif( vec3.sqrDist( v, v2 ) < 0.01 )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tindices.push(j);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t/*\\r\\n\\t\\tvar l2 = new_vertices.length;\\r\\n\\t\\tfor(var j = 0; j < l2; j++)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//same vertex\\r\\n\\t\\t\\tif( vec3.sqrDist( v, new_vertices[j] ) < 0.001 )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tindices.push(j);\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\t*/\\r\\n\\r\\n\\t\\tif(candidates && j != l2)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tvar index = j;\\r\\n\\t\\tnew_vertices.push(v);\\r\\n\\t\\tif( indexer[ key ] )\\r\\n\\t\\t\\tindexer[ key ].push( index );\\r\\n\\t\\telse\\r\\n\\t\\t\\tindexer[ key ] = [ index ];\\r\\n\\r\\n\\t\\tif(old_normals_data)\\r\\n\\t\\t\\tnew_normals.push( old_normals_data.subarray(i*3, (i+1)*3) );\\r\\n\\t\\tif(old_coords_data)\\r\\n\\t\\t\\tnew_coords.push( old_coords_data.subarray(i*2, (i+1)*2) );\\r\\n\\t\\tindices.push(index);\\r\\n\\t}\\r\\n\\r\\n\\tthis.vertexBuffers = {}; //erase all\\r\\n\\r\\n\\t//new buffers\\r\\n\\tthis.createVertexBuffer( 'vertices', GL.Mesh.common_buffers[\\\"vertices\\\"].attribute, 3, linearizeArray( new_vertices ) );\\t\\r\\n\\tif(old_normals_data)\\r\\n\\t\\tthis.createVertexBuffer( 'normals', GL.Mesh.common_buffers[\\\"normals\\\"].attribute, 3, linearizeArray( new_normals ) );\\t\\r\\n\\tif(old_coords_data)\\r\\n\\t\\tthis.createVertexBuffer( 'coords', GL.Mesh.common_buffers[\\\"coords\\\"].attribute, 2, linearizeArray( new_coords ) );\\t\\r\\n\\r\\n\\tthis.createIndexBuffer( \\\"triangles\\\", indices );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Breaks the indices\\r\\n* @method explodeIndices\\r\\n*/\\r\\nMesh.prototype.explodeIndices = function( buffer_name ) {\\r\\n\\r\\n\\tbuffer_name = buffer_name || \\\"triangles\\\";\\r\\n\\r\\n\\tvar indices_buffer = this.getIndexBuffer( buffer_name );\\r\\n\\tif(!indices_buffer)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar indices = indices_buffer.data;\\r\\n\\r\\n\\tvar new_buffers = {};\\r\\n\\tfor(var i in this.vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar info = GL.Mesh.common_buffers[i];\\r\\n\\t\\tnew_buffers[i] = new (info.type || Float32Array)( info.spacing * indices.length );\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i = 0, l = indices.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar index = indices[i];\\r\\n\\t\\tfor(var j in this.vertexBuffers)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar buffer = this.vertexBuffers[j];\\r\\n\\t\\t\\tvar info = GL.Mesh.common_buffers[j];\\r\\n\\t\\t\\tvar spacing = buffer.spacing || info.spacing;\\r\\n\\t\\t\\tvar new_buffer = new_buffers[j];\\r\\n\\t\\t\\tnew_buffer.set( buffer.data.subarray( index*spacing, index*spacing + spacing ), i*spacing );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t/*\\r\\n\\t//cluster by distance\\r\\n\\tvar new_vertices = new Float32Array(indices.length * 3);\\r\\n\\tvar new_normals = null;\\r\\n\\tvar new_coords = null;\\r\\n\\r\\n\\tvar old_vertices_buffer = this.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tvar old_vertices = old_vertices_buffer.data;\\r\\n\\r\\n\\tvar old_normals_buffer = this.vertexBuffers[\\\"normals\\\"];\\r\\n\\tvar old_normals = null;\\r\\n\\tif(old_normals_buffer)\\r\\n\\t{\\r\\n\\t\\told_normals = old_normals_buffer.data;\\r\\n\\t\\tnew_normals = new Float32Array(indices.length * 3);\\r\\n\\t}\\r\\n\\r\\n\\tvar old_coords_buffer = this.vertexBuffers[\\\"coords\\\"];\\r\\n\\tvar old_coords = null;\\r\\n\\tif( old_coords_buffer )\\r\\n\\t{\\r\\n\\t\\told_coords = old_coords_buffer.data;\\r\\n\\t\\tnew_coords = new Float32Array(indices.length * 2);\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i = 0, l = indices.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar index = indices[i];\\r\\n\\t\\tnew_vertices.set( old_vertices.subarray( index*3, index*3 + 3 ), i*3 );\\r\\n\\t\\tif(old_normals)\\r\\n\\t\\t\\tnew_normals.set( old_normals.subarray( index*3, index*3 + 3 ), i*3 );\\r\\n\\t\\tif(old_coords)\\r\\n\\t\\t\\tnew_coords.set( old_coords.subarray( index*2, index*2 + 2 ), i*2 );\\r\\n\\t}\\r\\n\\r\\n\\t//erase all\\r\\n\\tthis.vertexBuffers = {}; \\r\\n\\r\\n\\t//new buffers\\r\\n\\tthis.createVertexBuffer( 'vertices', GL.Mesh.common_buffers[\\\"vertices\\\"].attribute, 3, new_vertices );\\t\\r\\n\\tif(new_normals)\\r\\n\\t\\tthis.createVertexBuffer( 'normals', GL.Mesh.common_buffers[\\\"normals\\\"].attribute, 3, new_normals );\\t\\r\\n\\tif(new_coords)\\r\\n\\t\\tthis.createVertexBuffer( 'coords', GL.Mesh.common_buffers[\\\"coords\\\"].attribute, 2, new_coords );\\t\\r\\n\\t*/\\r\\n\\r\\n\\tfor(var i in new_buffers)\\r\\n\\t{\\r\\n\\t\\tvar old = this.vertexBuffers[i];\\r\\n\\t\\tthis.createVertexBuffer( i, old.attribute, old.spacing, new_buffers[i] );\\t\\r\\n\\t}\\r\\n\\r\\n\\tdelete this.indexBuffers[ buffer_name ];\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Creates a stream with the normals\\r\\n* @method computeNormals\\r\\n* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW)\\r\\n*/\\r\\nMesh.prototype.computeNormals = function( stream_type ) {\\r\\n\\tvar vertices_buffer = this.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tif(!vertices_buffer)\\r\\n\\t\\treturn console.error(\\\"Cannot compute normals of a mesh without vertices\\\");\\r\\n\\r\\n\\tvar vertices = this.vertexBuffers[\\\"vertices\\\"].data;\\r\\n\\tvar num_vertices = vertices.length / 3;\\r\\n\\r\\n\\t//create because it is faster than filling it with zeros\\r\\n\\tvar normals = new Float32Array( vertices.length );\\r\\n\\r\\n\\tvar triangles = null;\\r\\n\\tif(this.indexBuffers[\\\"triangles\\\"])\\r\\n\\t\\ttriangles = this.indexBuffers[\\\"triangles\\\"].data;\\r\\n\\r\\n\\tvar temp = GL.temp_vec3;\\r\\n\\tvar temp2 = GL.temp2_vec3;\\r\\n\\r\\n\\tvar i1,i2,i3,v1,v2,v3,n1,n2,n3;\\r\\n\\r\\n\\t//compute the plane normal\\r\\n\\tvar l = triangles ? triangles.length : vertices.length;\\r\\n\\tfor (var a = 0; a < l; a+=3)\\r\\n\\t{\\r\\n\\t\\tif(triangles)\\r\\n\\t\\t{\\r\\n\\t\\t\\ti1 = triangles[a];\\r\\n\\t\\t\\ti2 = triangles[a+1];\\r\\n\\t\\t\\ti3 = triangles[a+2];\\r\\n\\r\\n\\t\\t\\tv1 = vertices.subarray(i1*3,i1*3+3);\\r\\n\\t\\t\\tv2 = vertices.subarray(i2*3,i2*3+3);\\r\\n\\t\\t\\tv3 = vertices.subarray(i3*3,i3*3+3);\\r\\n\\r\\n\\t\\t\\tn1 = normals.subarray(i1*3,i1*3+3);\\r\\n\\t\\t\\tn2 = normals.subarray(i2*3,i2*3+3);\\r\\n\\t\\t\\tn3 = normals.subarray(i3*3,i3*3+3);\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tv1 = vertices.subarray(a*3,a*3+3);\\r\\n\\t\\t\\tv2 = vertices.subarray(a*3+3,a*3+6);\\r\\n\\t\\t\\tv3 = vertices.subarray(a*3+6,a*3+9);\\r\\n\\r\\n\\t\\t\\tn1 = normals.subarray(a*3,a*3+3);\\r\\n\\t\\t\\tn2 = normals.subarray(a*3+3,a*3+6);\\r\\n\\t\\t\\tn3 = normals.subarray(a*3+6,a*3+9);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvec3.sub( temp, v2, v1 );\\r\\n\\t\\tvec3.sub( temp2, v3, v1 );\\r\\n\\t\\tvec3.cross( temp, temp, temp2 );\\r\\n\\t\\tvec3.normalize(temp,temp);\\r\\n\\r\\n\\t\\t//save\\r\\n\\t\\tvec3.add( n1, n1, temp );\\r\\n\\t\\tvec3.add( n2, n2, temp );\\r\\n\\t\\tvec3.add( n3, n3, temp );\\r\\n\\t}\\r\\n\\r\\n\\t//normalize if vertices are shared\\r\\n\\tif(triangles)\\r\\n\\tfor (var a = 0, l = normals.length; a < l; a+=3)\\r\\n\\t{\\r\\n\\t\\tvar n = normals.subarray(a,a+3);\\r\\n\\t\\tvec3.normalize(n,n);\\r\\n\\t}\\r\\n\\r\\n\\tvar normals_buffer = this.vertexBuffers[\\\"normals\\\"];\\r\\n\\r\\n\\tif(normals_buffer)\\r\\n\\t{\\r\\n\\t\\tnormals_buffer.data = normals;\\r\\n\\t\\tnormals_buffer.upload( stream_type );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\treturn this.createVertexBuffer('normals', GL.Mesh.common_buffers[\\\"normals\\\"].attribute, 3, normals );\\r\\n\\treturn normals_buffer;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Creates a new stream with the tangents\\r\\n* @method computeTangents\\r\\n*/\\r\\nMesh.prototype.computeTangents = function()\\r\\n{\\r\\n\\tvar vertices_buffer = this.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tif(!vertices_buffer)\\r\\n\\t\\treturn console.error(\\\"Cannot compute tangents of a mesh without vertices\\\");\\r\\n\\r\\n\\tvar normals_buffer = this.vertexBuffers[\\\"normals\\\"];\\r\\n\\tif(!normals_buffer)\\r\\n\\t\\treturn console.error(\\\"Cannot compute tangents of a mesh without normals\\\");\\r\\n\\r\\n\\tvar uvs_buffer = this.vertexBuffers[\\\"coords\\\"];\\r\\n\\tif(!uvs_buffer)\\r\\n\\t\\treturn console.error(\\\"Cannot compute tangents of a mesh without uvs\\\");\\r\\n\\r\\n\\tvar triangles_buffer = this.indexBuffers[\\\"triangles\\\"];\\r\\n\\tif(!triangles_buffer)\\r\\n\\t\\treturn console.error(\\\"Cannot compute tangents of a mesh without indices\\\");\\r\\n\\r\\n\\tvar vertices = vertices_buffer.data;\\r\\n\\tvar normals = normals_buffer.data;\\r\\n\\tvar uvs = uvs_buffer.data;\\r\\n\\tvar triangles = triangles_buffer.data;\\r\\n\\r\\n\\tif(!vertices || !normals || !uvs) return;\\r\\n\\r\\n\\tvar num_vertices = vertices.length / 3;\\r\\n\\r\\n\\tvar tangents = new Float32Array(num_vertices * 4);\\r\\n\\t\\r\\n\\t//temporary (shared)\\r\\n\\tvar tan1 = new Float32Array(num_vertices*3*2);\\r\\n\\tvar tan2 = tan1.subarray(num_vertices*3);\\r\\n\\r\\n\\tvar a,l;\\r\\n\\tvar sdir = vec3.create();\\r\\n\\tvar tdir = vec3.create();\\r\\n\\tvar temp = vec3.create();\\r\\n\\tvar temp2 = vec3.create();\\r\\n\\r\\n\\tfor (a = 0, l = triangles.length; a < l; a+=3)\\r\\n\\t{\\r\\n\\t\\tvar i1 = triangles[a];\\r\\n\\t\\tvar i2 = triangles[a+1];\\r\\n\\t\\tvar i3 = triangles[a+2];\\r\\n\\r\\n\\t\\tvar v1 = vertices.subarray(i1*3,i1*3+3);\\r\\n\\t\\tvar v2 = vertices.subarray(i2*3,i2*3+3);\\r\\n\\t\\tvar v3 = vertices.subarray(i3*3,i3*3+3);\\r\\n\\r\\n\\t\\tvar w1 = uvs.subarray(i1*2,i1*2+2);\\r\\n\\t\\tvar w2 = uvs.subarray(i2*2,i2*2+2);\\r\\n\\t\\tvar w3 = uvs.subarray(i3*2,i3*2+2);\\r\\n\\r\\n\\t\\tvar x1 = v2[0] - v1[0];\\r\\n\\t\\tvar x2 = v3[0] - v1[0];\\r\\n\\t\\tvar y1 = v2[1] - v1[1];\\r\\n\\t\\tvar y2 = v3[1] - v1[1];\\r\\n\\t\\tvar z1 = v2[2] - v1[2];\\r\\n\\t\\tvar z2 = v3[2] - v1[2];\\r\\n\\r\\n\\t\\tvar s1 = w2[0] - w1[0];\\r\\n\\t\\tvar s2 = w3[0] - w1[0];\\r\\n\\t\\tvar t1 = w2[1] - w1[1];\\r\\n\\t\\tvar t2 = w3[1] - w1[1];\\r\\n\\r\\n\\t\\tvar r;\\r\\n\\t\\tvar den = (s1 * t2 - s2 * t1);\\r\\n\\t\\tif ( Math.abs(den) < 0.000000001 )\\r\\n\\t\\t r = 0.0;\\r\\n\\t\\telse\\r\\n\\t\\t r = 1.0 / den;\\r\\n\\r\\n\\t\\tvec3.copy(sdir, [(t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r] );\\r\\n\\t\\tvec3.copy(tdir, [(s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r] );\\r\\n\\r\\n\\t\\tvec3.add( tan1.subarray( i1*3, i1*3+3), tan1.subarray( i1*3, i1*3+3), sdir);\\r\\n\\t\\tvec3.add( tan1.subarray( i2*3, i2*3+3), tan1.subarray( i2*3, i2*3+3), sdir);\\r\\n\\t\\tvec3.add( tan1.subarray( i3*3, i3*3+3), tan1.subarray( i3*3, i3*3+3), sdir);\\r\\n\\r\\n\\t\\tvec3.add( tan2.subarray( i1*3, i1*3+3), tan2.subarray( i1*3, i1*3+3), tdir);\\r\\n\\t\\tvec3.add( tan2.subarray( i2*3, i2*3+3), tan2.subarray( i2*3, i2*3+3), tdir);\\r\\n\\t\\tvec3.add( tan2.subarray( i3*3, i3*3+3), tan2.subarray( i3*3, i3*3+3), tdir);\\r\\n\\t}\\r\\n\\r\\n\\tfor (a = 0, l = vertices.length; a < l; a+=3)\\r\\n\\t{\\r\\n\\t\\tvar n = normals.subarray(a,a+3);\\r\\n\\t\\tvar t = tan1.subarray(a,a+3);\\r\\n\\r\\n\\t\\t// Gram-Schmidt orthogonalize\\r\\n\\t\\tvec3.subtract(temp, t, vec3.scale(temp, n, vec3.dot(n, t) ) );\\r\\n\\t\\tvec3.normalize(temp,temp);\\r\\n\\r\\n\\t\\t// Calculate handedness\\r\\n\\t\\tvar w = ( vec3.dot( vec3.cross(temp2, n, t), tan2.subarray(a,a+3) ) < 0.0) ? -1.0 : 1.0;\\r\\n\\t\\ttangents.set([temp[0], temp[1], temp[2], w],(a/3)*4);\\r\\n\\t}\\r\\n\\r\\n\\tthis.createVertexBuffer('tangents', Mesh.common_buffers[\\\"tangents\\\"].attribute, 4, tangents );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Creates texture coordinates using a triplanar aproximation\\r\\n* @method computeTextureCoordinates\\r\\n*/\\r\\nMesh.prototype.computeTextureCoordinates = function( stream_type )\\r\\n{\\r\\n\\tvar vertices_buffer = this.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tif(!vertices_buffer)\\r\\n\\t\\treturn console.error(\\\"Cannot compute uvs of a mesh without vertices\\\");\\r\\n\\r\\n\\tthis.explodeIndices( \\\"triangles\\\" );\\r\\n\\r\\n\\tvar vertices = vertices_buffer.data;\\r\\n\\tvar num_vertices = vertices.length / 3;\\r\\n\\r\\n\\tvar uvs_buffer = this.vertexBuffers[\\\"coords\\\"];\\r\\n\\tvar uvs = new Float32Array( num_vertices * 2 );\\r\\n\\r\\n\\tvar triangles_buffer = this.indexBuffers[\\\"triangles\\\"];\\r\\n\\tvar triangles = null;\\r\\n\\tif( triangles_buffer )\\r\\n\\t\\ttriangles = triangles_buffer.data;\\r\\n\\r\\n\\tvar plane_normal = vec3.create();\\r\\n\\tvar side1 = vec3.create();\\r\\n\\tvar side2 = vec3.create();\\r\\n\\r\\n\\tvar bbox = this.getBoundingBox();\\r\\n\\tvar bboxcenter = BBox.getCenter( bbox );\\r\\n\\tvar bboxhs = vec3.create();\\r\\n\\tbboxhs.set( BBox.getHalfsize( bbox ) ); //careful, this is a reference\\r\\n\\tvec3.scale( bboxhs, bboxhs, 2 );\\r\\n\\r\\n\\tvar num = triangles ? triangles.length : vertices.length/3;\\r\\n\\r\\n\\tfor (var a = 0; a < num; a+=3)\\r\\n\\t{\\r\\n\\t\\tif(triangles)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar i1 = triangles[a];\\r\\n\\t\\t\\tvar i2 = triangles[a+1];\\r\\n\\t\\t\\tvar i3 = triangles[a+2];\\r\\n\\r\\n\\t\\t\\tvar v1 = vertices.subarray(i1*3,i1*3+3);\\r\\n\\t\\t\\tvar v2 = vertices.subarray(i2*3,i2*3+3);\\r\\n\\t\\t\\tvar v3 = vertices.subarray(i3*3,i3*3+3);\\r\\n\\r\\n\\t\\t\\tvar uv1 = uvs.subarray(i1*2,i1*2+2);\\r\\n\\t\\t\\tvar uv2 = uvs.subarray(i2*2,i2*2+2);\\r\\n\\t\\t\\tvar uv3 = uvs.subarray(i3*2,i3*2+2);\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar v1 = vertices.subarray((a)*3,(a)*3+3);\\r\\n\\t\\t\\tvar v2 = vertices.subarray((a+1)*3,(a+1)*3+3);\\r\\n\\t\\t\\tvar v3 = vertices.subarray((a+2)*3,(a+2)*3+3);\\r\\n\\r\\n\\t\\t\\tvar uv1 = uvs.subarray((a)*2,(a)*2+2);\\r\\n\\t\\t\\tvar uv2 = uvs.subarray((a+1)*2,(a+1)*2+2);\\r\\n\\t\\t\\tvar uv3 = uvs.subarray((a+2)*2,(a+2)*2+2);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvec3.sub(side1, v1, v2 );\\r\\n\\t\\tvec3.sub(side2, v1, v3 );\\r\\n\\t\\tvec3.cross( plane_normal, side1, side2 );\\r\\n\\t\\t//vec3.normalize( plane_normal, plane_normal ); //not necessary\\r\\n\\r\\n\\t\\tplane_normal[0] = Math.abs( plane_normal[0] );\\r\\n\\t\\tplane_normal[1] = Math.abs( plane_normal[1] );\\r\\n\\t\\tplane_normal[2] = Math.abs( plane_normal[2] );\\r\\n\\r\\n\\t\\tif( plane_normal[0] > plane_normal[1] && plane_normal[0] > plane_normal[2])\\r\\n\\t\\t{\\r\\n\\t\\t\\t//X\\r\\n\\t\\t\\tuv1[0] = (v1[2] - bboxcenter[2]) / bboxhs[2];\\r\\n\\t\\t\\tuv1[1] = (v1[1] - bboxcenter[1]) / bboxhs[1];\\r\\n\\t\\t\\tuv2[0] = (v2[2] - bboxcenter[2]) / bboxhs[2];\\r\\n\\t\\t\\tuv2[1] = (v2[1] - bboxcenter[1]) / bboxhs[1];\\r\\n\\t\\t\\tuv3[0] = (v3[2] - bboxcenter[2]) / bboxhs[2];\\r\\n\\t\\t\\tuv3[1] = (v3[1] - bboxcenter[1]) / bboxhs[1];\\r\\n\\t\\t}\\r\\n\\t\\telse if ( plane_normal[1] > plane_normal[2])\\r\\n\\t\\t{\\r\\n\\t\\t\\t//Y\\r\\n\\t\\t\\tuv1[0] = (v1[0] - bboxcenter[0]) / bboxhs[0];\\r\\n\\t\\t\\tuv1[1] = (v1[2] - bboxcenter[2]) / bboxhs[2];\\r\\n\\t\\t\\tuv2[0] = (v2[0] - bboxcenter[0]) / bboxhs[0];\\r\\n\\t\\t\\tuv2[1] = (v2[2] - bboxcenter[2]) / bboxhs[2];\\r\\n\\t\\t\\tuv3[0] = (v3[0] - bboxcenter[0]) / bboxhs[0];\\r\\n\\t\\t\\tuv3[1] = (v3[2] - bboxcenter[2]) / bboxhs[2];\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\t//Z\\r\\n\\t\\t\\tuv1[0] = (v1[0] - bboxcenter[0]) / bboxhs[0];\\r\\n\\t\\t\\tuv1[1] = (v1[1] - bboxcenter[1]) / bboxhs[1];\\r\\n\\t\\t\\tuv2[0] = (v2[0] - bboxcenter[0]) / bboxhs[0];\\r\\n\\t\\t\\tuv2[1] = (v2[1] - bboxcenter[1]) / bboxhs[1];\\r\\n\\t\\t\\tuv3[0] = (v3[0] - bboxcenter[0]) / bboxhs[0];\\r\\n\\t\\t\\tuv3[1] = (v3[1] - bboxcenter[1]) / bboxhs[1];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif(uvs_buffer)\\r\\n\\t{\\r\\n\\t\\tuvs_buffer.data = uvs;\\r\\n\\t\\tuvs_buffer.upload( stream_type );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tthis.createVertexBuffer('coords', Mesh.common_buffers[\\\"coords\\\"].attribute, 2, uvs );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Computes the number of vertices\\r\\n* @method getVertexNumber\\r\\n*/\\r\\nMesh.prototype.getNumVertices = function() {\\r\\n\\tvar b = this.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tif(!b)\\r\\n\\t\\treturn 0;\\r\\n\\treturn b.data.length / b.spacing;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Computes the number of triangles (takes into account indices)\\r\\n* @method getNumTriangles\\r\\n*/\\r\\nMesh.prototype.getNumTriangles = function() {\\r\\n\\tvar indices_buffer = this.getIndexBuffer(\\\"triangles\\\");\\r\\n\\tif(!indices_buffer)\\r\\n\\t\\treturn this.getNumVertices() / 3;\\r\\n\\treturn indices_buffer.data.length / 3;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Computes bounding information\\r\\n* @method Mesh.computeBoundingBox\\r\\n* @param {typed Array} vertices array containing all the vertices\\r\\n* @param {BBox} bb where to store the bounding box\\r\\n* @param {Array} mask [optional] to specify which vertices must be considered when creating the bbox, used to create BBox of a submesh\\r\\n*/\\r\\nMesh.computeBoundingBox = function( vertices, bb, mask ) {\\r\\n\\r\\n\\tif(!vertices)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar start = 0;\\r\\n\\r\\n\\tif(mask)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < mask.length; ++i)\\r\\n\\t\\t\\tif( mask[i] )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tstart = i;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\tif(start == mask.length)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"mask contains only zeros, no vertices marked\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar min = vec3.clone( vertices.subarray( start*3, start*3 + 3) );\\r\\n\\tvar max = vec3.clone( vertices.subarray( start*3, start*3 + 3) );\\r\\n\\tvar v;\\r\\n\\r\\n\\tfor(var i = start*3; i < vertices.length; i+=3)\\r\\n\\t{\\r\\n\\t\\tif( mask && !mask[i/3] )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tv = vertices.subarray(i,i+3);\\r\\n\\t\\tvec3.min( min,v, min);\\r\\n\\t\\tvec3.max( max,v, max);\\r\\n\\t}\\r\\n\\r\\n\\tif( isNaN(min[0]) || isNaN(min[1]) || isNaN(min[2]) ||\\r\\n\\t\\tisNaN(max[0]) || isNaN(max[1]) || isNaN(max[2]) )\\r\\n\\t{\\r\\n\\t\\tmin[0] = min[1] = min[2] = 0;\\r\\n\\t\\tmax[0] = max[1] = max[2] = 0;\\r\\n\\t\\tconsole.warn(\\\"Warning: GL.Mesh has NaN values in vertices\\\");\\r\\n\\t}\\r\\n\\r\\n\\tvar center = vec3.add( vec3.create(), min,max );\\r\\n\\tvec3.scale( center, center, 0.5);\\r\\n\\tvar half_size = vec3.subtract( vec3.create(), max, center );\\r\\n\\r\\n\\treturn BBox.setCenterHalfsize( bb || BBox.create(), center, half_size );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the bounding box, if it is not computed, then computes it\\r\\n* @method getBoundingBox\\r\\n* @return {BBox} bounding box\\r\\n*/\\r\\nMesh.prototype.getBoundingBox = function()\\r\\n{\\r\\n\\tif(this._bounding)\\r\\n\\t\\treturn this._bounding;\\r\\n\\r\\n\\tthis.updateBoundingBox();\\r\\n\\treturn this._bounding;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Update bounding information of this mesh\\r\\n* @method updateBoundingBox\\r\\n*/\\r\\nMesh.prototype.updateBoundingBox = function() {\\r\\n\\tvar vertices = this.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tif(!vertices)\\r\\n\\t\\treturn;\\r\\n\\tGL.Mesh.computeBoundingBox( vertices.data, this._bounding );\\r\\n\\tif(this.info && this.info.groups && this.info.groups.length)\\r\\n\\t\\tthis.computeGroupsBoundingBoxes();\\r\\n}\\r\\n\\r\\n/**\\r\\n* Update bounding information for every group submesh\\r\\n* @method computeGroupsBoundingBoxes\\r\\n*/\\r\\nMesh.prototype.computeGroupsBoundingBoxes = function()\\r\\n{\\r\\n\\tvar indices = null;\\r\\n\\tvar indices_buffer = this.getIndexBuffer(\\\"triangles\\\");\\r\\n\\tif( indices_buffer )\\r\\n\\t\\tindices = indices_buffer.data;\\r\\n\\r\\n\\tvar vertices_buffer = this.getVertexBuffer(\\\"vertices\\\");\\r\\n\\tif(!vertices_buffer)\\r\\n\\t\\treturn false;\\r\\n\\tvar vertices = vertices_buffer.data;\\r\\n\\tif(!vertices.length)\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\tvar groups = this.info.groups;\\r\\n\\tfor(var i = 0; i < groups.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar group = groups[i];\\r\\n\\t\\tgroup.bounding = group.bounding || BBox.create();\\r\\n\\t\\tvar submesh_vertices = null;\\r\\n\\t\\tif( indices )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mask = new Uint8Array( vertices.length / 3 );\\r\\n\\t\\t\\tvar s = group.start;\\r\\n\\t\\t\\tfor( var j = 0, l = group.length; j < l; j += 3 )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tmask[ indices[s+j] ] = 1;\\r\\n\\t\\t\\t\\tmask[ indices[s+j+1] ] = 1;\\r\\n\\t\\t\\t\\tmask[ indices[s+j+2] ] = 1;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tGL.Mesh.computeBoundingBox( vertices, group.bounding, mask );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tsubmesh_vertices = vertices.subarray( group.start * 3, ( group.start + group.length) * 3 );\\r\\n\\t\\t\\tGL.Mesh.computeBoundingBox( submesh_vertices, group.bounding );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* forces a bounding box to be set\\r\\n* @method setBoundingBox\\r\\n* @param {vec3} center center of the bounding box\\r\\n* @param {vec3} half_size vector from the center to positive corner\\r\\n*/\\r\\nMesh.prototype.setBoundingBox = function( center, half_size ) {\\r\\n\\tBBox.setCenterHalfsize( this._bounding, center, half_size );\\t\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Remove all local memory from the streams (leaving it only in the VRAM) to save RAM\\r\\n* @method freeData\\r\\n*/\\r\\nMesh.prototype.freeData = function()\\r\\n{\\r\\n\\tfor (var attribute in this.vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tthis.vertexBuffers[attribute].data = null;\\r\\n\\t\\tdelete this[ this.vertexBuffers[attribute].name ]; //delete from the mesh itself\\r\\n\\t}\\r\\n\\tfor (var name in this.indexBuffers)\\r\\n\\t{\\r\\n\\t\\tthis.indexBuffers[name].data = null;\\r\\n\\t\\tdelete this[ this.indexBuffers[name].name ]; //delete from the mesh itself\\r\\n\\t}\\r\\n}\\r\\n\\r\\nMesh.prototype.configure = function( o, options )\\r\\n{\\r\\n\\tvar vertex_buffers = {};\\r\\n\\tvar index_buffers = {};\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\tfor(var j in o)\\r\\n\\t{\\r\\n\\t\\tif(!o[j])\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif(j == \\\"vertexBuffers\\\" || j == \\\"vertex_buffers\\\") //HACK: legacy code\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(i in o[j])\\r\\n\\t\\t\\t\\tvertex_buffers[i] = o[j][i];\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\t\\t\\r\\n\\t\\tif(j == \\\"indexBuffers\\\" || j == \\\"index_buffers\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(i in o[j])\\r\\n\\t\\t\\t\\tindex_buffers[i] = o[j][i];\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(j == \\\"indices\\\" || j == \\\"lines\\\" || j == \\\"wireframe\\\" || j == \\\"triangles\\\")\\r\\n\\t\\t\\tindex_buffers[j] = o[j];\\r\\n\\t\\telse if( GL.Mesh.common_buffers[j])\\r\\n\\t\\t\\tvertex_buffers[j] = o[j];\\r\\n\\t\\telse //global data like bounding, info of groups, etc\\r\\n\\t\\t{\\r\\n\\t\\t\\toptions[j] = o[j];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tthis.addBuffers( vertex_buffers, index_buffers, options.stream_type );\\r\\n\\r\\n\\tfor(var i in options)\\r\\n\\t\\tthis[i] = options[i];\\t\\t\\r\\n\\r\\n\\tif(!options.bounding)\\r\\n\\t\\tthis.updateBoundingBox();\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the amount of memory used by this mesh in bytes (sum of all buffers)\\r\\n* @method getMemory\\r\\n* @return {number} bytes\\r\\n*/\\r\\nMesh.prototype.totalMemory = function()\\r\\n{\\r\\n\\tvar num = 0|0;\\r\\n\\r\\n\\tfor (var name in this.vertexBuffers)\\r\\n\\t\\tnum += this.vertexBuffers[name].data.buffer.byteLength;\\r\\n\\tfor (var name in this.indexBuffers)\\r\\n\\t\\tnum += this.indexBuffers[name].data.buffer.byteLength;\\r\\n\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\nMesh.prototype.slice = function(start, length)\\r\\n{\\r\\n\\tvar new_vertex_buffers = {};\\r\\n\\r\\n\\tvar indices_buffer = this.indexBuffers[\\\"triangles\\\"];\\r\\n\\tif(!indices_buffer)\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"splice in not indexed not supported yet\\\");\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tvar indices = indices_buffer.data;\\r\\n\\r\\n\\tvar new_triangles = [];\\r\\n\\tvar reindex = new Int32Array( indices.length );\\r\\n\\treindex.fill(-1);\\r\\n\\r\\n\\tvar end = start + length;\\r\\n\\tif(end >= indices.length)\\r\\n\\t\\tend = indices.length;\\r\\n\\r\\n\\tvar last_index = 0;\\r\\n\\tfor(var j = start; j < end; ++j)\\r\\n\\t{\\r\\n\\t\\tvar index = indices[j];\\r\\n\\t\\tif( reindex[index] != -1 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tnew_triangles.push(reindex[index]);\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//new vertex\\r\\n\\t\\tvar new_index = last_index++;\\r\\n\\t\\treindex[index] = new_index;\\r\\n\\t\\tnew_triangles.push(new_index);\\r\\n\\r\\n\\t\\tfor( var i in this.vertexBuffers )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar buffer = this.vertexBuffers[i];\\r\\n\\t\\t\\tvar data = buffer.data;\\r\\n\\t\\t\\tvar spacing = buffer.spacing;\\r\\n\\t\\t\\tif(!new_vertex_buffers[i])\\r\\n\\t\\t\\t\\tnew_vertex_buffers[i] = [];\\r\\n\\t\\t\\tvar new_buffer = new_vertex_buffers[i];\\r\\n\\t\\t\\tfor(var k = 0; k < spacing; ++k)\\r\\n\\t\\t\\t\\tnew_buffer.push( data[k + index*spacing] );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar new_mesh = new GL.Mesh( new_vertex_buffers, {triangles: new_triangles}, null,gl);\\r\\n\\tnew_mesh.updateBoundingBox();\\r\\n\\treturn new_mesh;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* returns a low poly version of the mesh that takes much less memory (but breaks tiling of uvs and smoothing groups)\\r\\n* @method simplify\\r\\n* @return {Mesh} simplified mesh\\r\\n*/\\r\\nMesh.prototype.simplify = function()\\r\\n{\\r\\n\\t//compute bounding box\\r\\n\\tvar bb = this.getBoundingBox();\\r\\n\\tvar min = BBox.getMin( bb );\\r\\n\\tvar halfsize = BBox.getHalfsize( bb );\\r\\n\\tvar range = vec3.scale( vec3.create(), halfsize, 2 );\\r\\n\\r\\n\\tvar newmesh = new GL.Mesh();\\r\\n\\tvar temp = vec3.create();\\r\\n\\r\\n\\tfor(var i in this.vertexBuffers)\\r\\n\\t{\\r\\n\\t\\t//take every vertex and normalize it to the bounding box\\r\\n\\t\\tvar buffer = this.vertexBuffers[i];\\r\\n\\t\\tvar data = buffer.data;\\r\\n\\r\\n\\t\\tvar new_data = new Float32Array( data.length );\\r\\n\\r\\n\\t\\tif(i == \\\"vertices\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var j = 0, l = data.length; j < l; j+=3 )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar v = data.subarray(j,j+3);\\r\\n\\t\\t\\t\\tvec3.sub( temp, v, min );\\r\\n\\t\\t\\t\\tvec3.div( temp, temp, range );\\r\\n\\t\\t\\t\\ttemp[0] = Math.round(temp[0] * 256) / 256;\\r\\n\\t\\t\\t\\ttemp[1] = Math.round(temp[1] * 256) / 256;\\r\\n\\t\\t\\t\\ttemp[2] = Math.round(temp[2] * 256) / 256;\\r\\n\\t\\t\\t\\tvec3.mul( temp, temp, range );\\r\\n\\t\\t\\t\\tvec3.add( temp, temp, min );\\r\\n\\t\\t\\t\\tnew_data.set( temp, j );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tnewmesh.addBuffer();\\r\\n\\t}\\r\\n\\r\\n\\t//search for repeated vertices\\r\\n\\t\\t//compute the average normal and coord\\r\\n\\t//reindex the triangles\\r\\n\\t//return simplified mesh\\t\\r\\n}\\r\\n\\r\\n/**\\r\\n* Static method for the class Mesh to create a mesh from a list of common streams\\r\\n* @method Mesh.load\\r\\n* @param {Object} buffers object will all the buffers\\r\\n* @param {Object} options [optional]\\r\\n* @param {Mesh} output_mesh [optional] mesh to store the mesh, otherwise is created\\r\\n* @param {WebGLContext} gl [optional] if omitted, the global.gl is used\\r\\n*/\\r\\nMesh.load = function( buffers, options, output_mesh, gl ) {\\r\\n\\toptions = options || {};\\r\\n\\tif(options.no_gl)\\r\\n\\t\\tgl = null;\\r\\n\\tvar mesh = output_mesh || new GL.Mesh(null,null,null,gl);\\r\\n\\tmesh.configure( buffers, options );\\r\\n\\treturn mesh;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a mesh with all the meshes merged (you can apply transforms individually to every buffer)\\r\\n* @method Mesh.mergeMeshes\\r\\n* @param {Array} meshes array containing object like { mesh:, matrix:, texture_matrix: }\\r\\n* @param {Object} options { only_data: to get the mesh data without uploading it }\\r\\n* @return {GL.Mesh|Object} the mesh in GL.Mesh format or Object format (if options.only_data is true)\\r\\n*/\\r\\nMesh.mergeMeshes = function( meshes, options )\\r\\n{\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\tvar vertex_buffers = {};\\r\\n\\tvar index_buffers = {};\\r\\n\\tvar offsets = {}; //tells how many positions indices must be offseted\\r\\n\\tvar vertex_offsets = [];\\r\\n\\tvar current_vertex_offset = 0;\\r\\n\\tvar groups = [];\\r\\n\\r\\n\\t//vertex buffers\\r\\n\\t//compute size\\r\\n\\tfor(var i = 0; i < meshes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar mesh_info = meshes[i];\\r\\n\\t\\tvar mesh = mesh_info.mesh;\\r\\n\\t\\tvar offset = current_vertex_offset;\\r\\n\\t\\tvertex_offsets.push( offset );\\r\\n\\t\\tvar length = mesh.vertexBuffers[\\\"vertices\\\"].data.length / 3;\\r\\n\\t\\tcurrent_vertex_offset += length;\\r\\n\\r\\n\\t\\tfor(var j in mesh.vertexBuffers)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!vertex_buffers[j])\\r\\n\\t\\t\\t\\tvertex_buffers[j] = mesh.vertexBuffers[j].data.length;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tvertex_buffers[j] += mesh.vertexBuffers[j].data.length;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var j in mesh.indexBuffers)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!index_buffers[j])\\r\\n\\t\\t\\t\\tindex_buffers[j] = mesh.indexBuffers[j].data.length;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tindex_buffers[j] += mesh.indexBuffers[j].data.length;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//groups\\r\\n\\t\\tvar group = {\\r\\n\\t\\t\\tname: \\\"mesh_\\\" + i,\\r\\n\\t\\t\\tstart: offset,\\r\\n\\t\\t\\tlength: length,\\r\\n\\t\\t\\tmaterial: \\\"\\\"\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tgroups.push( group );\\r\\n\\t}\\r\\n\\r\\n\\t//allocate\\r\\n\\tfor(var j in vertex_buffers)\\r\\n\\t{\\r\\n\\t\\tvar datatype = options[j];\\r\\n\\t\\tif(datatype === null)\\r\\n\\t\\t{\\r\\n\\t\\t\\tdelete vertex_buffers[j];\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!datatype)\\r\\n\\t\\t\\tdatatype = Float32Array;\\r\\n\\r\\n\\t\\tvertex_buffers[j] = new datatype( vertex_buffers[j] );\\r\\n\\t\\toffsets[j] = 0;\\r\\n\\t}\\r\\n\\r\\n\\tfor(var j in index_buffers)\\r\\n\\t{\\r\\n\\t\\tindex_buffers[j] = new Uint32Array( index_buffers[j] );\\r\\n\\t\\toffsets[j] = 0;\\r\\n\\t}\\r\\n\\r\\n\\t//store\\r\\n\\tfor(var i = 0; i < meshes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar mesh_info = meshes[i];\\r\\n\\t\\tvar mesh = mesh_info.mesh;\\r\\n\\t\\tvar offset = 0;\\r\\n\\t\\tvar length = 0;\\r\\n\\r\\n\\t\\tfor(var j in mesh.vertexBuffers)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!vertex_buffers[j])\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(j == \\\"vertices\\\")\\r\\n\\t\\t\\t\\tlength = mesh.vertexBuffers[j].data.length / 3;\\r\\n\\r\\n\\t\\t\\tvertex_buffers[j].set( mesh.vertexBuffers[j].data, offsets[j] );\\r\\n\\r\\n\\t\\t\\t//apply transform\\r\\n\\t\\t\\tif(mesh_info[ j + \\\"_matrix\\\"] )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar matrix = mesh_info[ j + \\\"_matrix\\\" ];\\r\\n\\t\\t\\t\\tif(matrix.length == 16)\\r\\n\\t\\t\\t\\t\\tapply_transform( vertex_buffers[j], offsets[j], mesh.vertexBuffers[j].data.length, matrix )\\r\\n\\t\\t\\t\\telse if(matrix.length == 9)\\r\\n\\t\\t\\t\\t\\tapply_transform2D( vertex_buffers[j], offsets[j], mesh.vertexBuffers[j].data.length, matrix )\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\toffsets[j] += mesh.vertexBuffers[j].data.length;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var j in mesh.indexBuffers)\\r\\n\\t\\t{\\r\\n\\t\\t\\tindex_buffers[j].set( mesh.indexBuffers[j].data, offsets[j] );\\r\\n\\t\\t\\tapply_offset( index_buffers[j], offsets[j], mesh.indexBuffers[j].data.length, vertex_offsets[i] );\\r\\n\\t\\t\\toffsets[j] += mesh.indexBuffers[j].data.length;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//useful functions\\r\\n\\tfunction apply_transform( array, start, length, matrix )\\r\\n\\t{\\r\\n\\t\\tvar l = start + length;\\r\\n\\t\\tfor(var i = start; i < l; i+=3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar v = array.subarray(i,i+3);\\r\\n\\t\\t\\tvec3.transformMat4( v, v, matrix );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tfunction apply_transform2D( array, start, length, matrix )\\r\\n\\t{\\r\\n\\t\\tvar l = start + length;\\r\\n\\t\\tfor(var i = start; i < l; i+=2)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar v = array.subarray(i,i+2);\\r\\n\\t\\t\\tvec2.transformMat3( v, v, matrix );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tfunction apply_offset( array, start, length, offset )\\r\\n\\t{\\r\\n\\t\\tvar l = start + length;\\r\\n\\t\\tfor(var i = start; i < l; ++i)\\r\\n\\t\\t\\tarray[i] += offset;\\r\\n\\t}\\r\\n\\r\\n\\tvar extra = { info: { groups: groups } };\\r\\n\\r\\n\\t//return\\r\\n\\tif( typeof(gl) != \\\"undefined\\\" || options.only_data )\\r\\n\\t\\treturn new GL.Mesh( vertex_buffers,index_buffers, extra );\\r\\n\\treturn { \\r\\n\\t\\tvertexBuffers: vertex_buffers, \\r\\n\\t\\tindexBuffers: index_buffers, \\r\\n\\t\\tinfo: { groups: groups } \\r\\n\\t};\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n//Here we store all basic mesh parsers (OBJ, STL) and encoders\\r\\nMesh.parsers = {};\\r\\nMesh.encoders = {};\\r\\nMesh.binary_file_formats = {}; //extensions that must be downloaded in binary\\r\\nMesh.compressors = {}; //used to compress binary meshes\\r\\nMesh.decompressors = {}; //used to decompress binary meshes\\r\\n\\r\\n/**\\r\\n* Returns am empty mesh and loads a mesh and parses it using the Mesh.parsers, by default only OBJ is supported\\r\\n* @method Mesh.fromOBJ\\r\\n* @param {Array} meshes array containing all the meshes\\r\\n*/\\r\\nMesh.fromURL = function(url, on_complete, gl, options)\\r\\n{\\r\\n\\toptions = options || {};\\r\\n\\tgl = gl || global.gl;\\r\\n\\t\\r\\n\\tvar mesh = new GL.Mesh(undefined,undefined,undefined,gl);\\r\\n\\tmesh.ready = false;\\r\\n\\r\\n\\tvar pos = url.lastIndexOf(\\\".\\\");\\r\\n\\tvar extension = url.substr(pos+1).toLowerCase();\\r\\n\\toptions.binary = Mesh.binary_file_formats[ extension ];\\r\\n\\r\\n\\tHttpRequest( url, null, function(data) {\\r\\n\\t\\tmesh.parse( data, extension );\\r\\n\\t\\tdelete mesh[\\\"ready\\\"];\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete.call(mesh,mesh, url);\\r\\n\\t}, function(err){\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete(null);\\r\\n\\t}, options );\\r\\n\\treturn mesh;\\r\\n}\\r\\n\\r\\n/**\\r\\n* given some data an information about the format, it search for a parser in Mesh.parsers and tries to extract the mesh information\\r\\n* Only obj supported now\\r\\n* @method parse\\r\\n* @param {*} data could be string or ArrayBuffer\\r\\n* @param {String} format parser file format name (p.e. \\\"obj\\\")\\r\\n* @return {?} depending on the parser\\r\\n*/\\r\\nMesh.prototype.parse = function( data, format )\\r\\n{\\r\\n\\tformat = format.toLowerCase();\\r\\n\\tvar parser = GL.Mesh.parsers[ format ];\\r\\n\\tif(parser)\\r\\n\\t\\treturn parser.call(null, data, {mesh: this});\\r\\n\\tthrow(\\\"GL.Mesh.parse: no parser found for format \\\" + format );\\r\\n}\\r\\n\\r\\n/**\\r\\n* It returns the mesh data encoded in the format specified\\r\\n* Only obj supported now\\r\\n* @method encode\\r\\n* @param {String} format to encode the data to (p.e. \\\"obj\\\")\\r\\n* @return {?} String with the info\\r\\n*/\\r\\nMesh.prototype.encode = function( format, options )\\r\\n{\\r\\n\\tformat = format.toLowerCase();\\r\\n\\tvar encoder = GL.Mesh.encoders[ format ];\\r\\n\\tif(encoder)\\r\\n\\t\\treturn encoder.call(null, this, options );\\r\\n\\tthrow(\\\"GL.Mesh.encode: no encoder found for format \\\" + format );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a shared mesh containing a quad to be used when rendering to the screen\\r\\n* Reusing the same quad helps not filling the memory\\r\\n* @method getScreenQuad\\r\\n* @return {GL.Mesh} the screen quad\\r\\n*/\\r\\nMesh.getScreenQuad = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar mesh = gl.meshes[\\\":screen_quad\\\"];\\r\\n\\tif(mesh)\\r\\n\\t\\treturn mesh;\\r\\n\\r\\n\\tvar vertices = new Float32Array([0,0,0, 1,1,0, 0,1,0, 0,0,0, 1,0,0, 1,1,0 ]);\\r\\n\\tvar coords = new Float32Array([0,0, 1,1, 0,1, 0,0, 1,0, 1,1 ]);\\r\\n\\tmesh = new GL.Mesh({ vertices: vertices, coords: coords}, undefined, undefined, gl);\\r\\n\\treturn gl.meshes[\\\":screen_quad\\\"] = mesh;\\r\\n}\\r\\n\\r\\nfunction linearizeArray( array, typed_array_class )\\r\\n{\\r\\n\\tif(array.constructor === typed_array_class)\\r\\n\\t\\treturn array;\\r\\n\\tif(array.constructor !== Array)\\r\\n\\t{\\r\\n\\t\\ttyped_array_class = typed_array_class || Float32Array;\\r\\n\\t\\treturn new typed_array_class(array);\\r\\n\\t}\\r\\n\\r\\n\\ttyped_array_class = typed_array_class || Float32Array;\\r\\n\\tvar components = array[0].length;\\r\\n\\tvar size = array.length * components;\\r\\n\\tvar buffer = new typed_array_class(size);\\r\\n\\r\\n\\tfor (var i=0; i < array.length;++i)\\r\\n\\t\\tfor(var j=0; j < components; ++j)\\r\\n\\t\\t\\tbuffer[i*components + j] = array[i][j];\\r\\n\\treturn buffer;\\r\\n}\\r\\n\\r\\nGL.linearizeArray = linearizeArray;\\r\\n\\r\\n/* BINARY MESHES */\\r\\n//Add some functions to the classes in LiteGL to allow store in binary\\r\\nGL.Mesh.EXTENSION = \\\"wbin\\\";\\r\\nGL.Mesh.enable_wbin_compression = true;\\r\\n\\r\\n//this is used when a mesh is dynamic and constantly changes\\r\\nfunction DynamicMesh( size, normals, coords, colors, gl )\\r\\n{\\r\\n\\tsize = size || 1024;\\r\\n\\r\\n\\tif(GL.debug)\\r\\n\\t\\tconsole.log(\\\"GL.Mesh created\\\");\\r\\n\\r\\n\\tif( gl !== null )\\r\\n\\t{\\r\\n\\t\\tgl = gl || global.gl;\\r\\n\\t\\tthis.gl = gl;\\r\\n\\t}\\r\\n\\r\\n\\t//used to avoid problems with resources moving between different webgl context\\r\\n\\tthis._context_id = gl.context_id; \\r\\n\\r\\n\\tthis.vertexBuffers = {};\\r\\n\\tthis.indexBuffers = {};\\r\\n\\r\\n\\t//here you can store extra info, like groups, which is an array of { name, start, length, material }\\r\\n\\tthis.info = {\\r\\n\\t\\tgroups: []\\r\\n\\t}; \\r\\n\\tthis._bounding = BBox.create(); //here you can store a AABB in BBox format\\r\\n\\r\\n\\tthis.resize( size );\\r\\n}\\r\\n\\r\\nDynamicMesh.DEFAULT_NORMAL = vec3.fromValues(0,1,0);\\r\\nDynamicMesh.DEFAULT_COORD = vec2.fromValues(0.5,0.5);\\r\\nDynamicMesh.DEFAULT_COLOR = vec4.fromValues(1,1,1,1);\\r\\n\\r\\nDynamicMesh.prototype.resize = function( size )\\r\\n{\\r\\n\\tvar buffers = {};\\r\\n\\r\\n\\tthis._vertex_data = new Float32Array( size * 3 );\\r\\n\\tbuffers.vertices = this._vertex_data;\\r\\n\\r\\n\\tif( normals )\\r\\n\\t\\tbuffers.normals = this._normal_data = new Float32Array( size * 3 );\\r\\n\\tif( coords )\\r\\n\\t\\tbuffers.coords = this._coord_data = new Float32Array( size * 2 );\\r\\n\\tif( colors )\\r\\n\\t\\tbuffers.colors = this._color_data = new Float32Array( size * 4 );\\r\\n\\r\\n\\tthis.addBuffers( buffers );\\r\\n\\r\\n\\tthis.current_pos = 0;\\r\\n\\tthis.max_size = size;\\r\\n\\tthis._must_update = true;\\r\\n}\\r\\n\\r\\nDynamicMesh.prototype.clear = function()\\r\\n{\\r\\n\\tthis.current_pos = 0;\\r\\n}\\r\\n\\r\\nDynamicMesh.prototype.addPoint = function( vertex, normal, coord, color )\\r\\n{\\r\\n\\tif (pos >= this.max_size)\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"DynamicMesh: not enough space, reserve more\\\");\\r\\n\\t\\treturn false;\\r\\n\\t}\\r\\n\\tvar pos = this.current_pos++;\\r\\n\\r\\n\\tthis._vertex_data.set( vertex, pos*3 );\\r\\n\\r\\n\\tif(this._normal_data)\\r\\n\\t\\tthis._normal_data.set( normal || DynamicMesh.DEFAULT_NORMAL, pos*3 );\\r\\n\\tif(this._coord_data)\\r\\n\\t\\tthis._coord_data.set( coord || DynamicMesh.DEFAULT_COORD, pos*2 );\\r\\n\\tif(this._color_data)\\r\\n\\t\\tthis._color_data.set( color || DynamicMesh.DEFAULT_COLOR, pos*4 );\\r\\n\\r\\n\\tthis._must_update = true;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\nDynamicMesh.prototype.update = function( force )\\r\\n{\\r\\n\\tif(!this._must_update && !force)\\r\\n\\t\\treturn this.current_pos;\\r\\n\\tthis._must_update = false;\\r\\n\\r\\n\\tthis.getBuffer(\\\"vertices\\\").upload( gl.STREAM_DRAW );\\r\\n\\tif(this._normal_data)\\r\\n\\t\\tthis.getBuffer(\\\"normal\\\").upload( gl.STREAM_DRAW );\\r\\n\\tif(this._coord_data)\\r\\n\\t\\tthis.getBuffer(\\\"coord\\\").upload( gl.STREAM_DRAW );\\r\\n\\tif(this._color_data)\\r\\n\\t\\tthis.getBuffer(\\\"color\\\").upload( gl.STREAM_DRAW );\\r\\n\\treturn this.current_pos;\\r\\n}\\r\\n\\r\\nextendClass( DynamicMesh, Mesh );\\r\\n\\r\\n/**\\r\\n* @class Mesh\\r\\n*/\\r\\n\\r\\n/**\\r\\n* Returns a planar mesh (you can choose how many subdivisions)\\r\\n* @method Mesh.plane\\r\\n* @param {Object} options valid options: detail, detailX, detailY, size, width, heigth, xz (horizontal plane)\\r\\n*/\\r\\nMesh.plane = function(options, gl) {\\r\\n\\toptions = options || {};\\r\\n\\toptions.triangles = [];\\r\\n\\tvar mesh = {};\\r\\n\\tvar detailX = options.detailX || options.detail || 1;\\r\\n\\tvar detailY = options.detailY || options.detail || 1;\\r\\n\\tvar width = options.width || options.size || 1;\\r\\n\\tvar height = options.height || options.size || 1;\\r\\n\\tvar xz = options.xz;\\r\\n\\twidth *= 0.5;\\r\\n\\theight *= 0.5;\\r\\n\\r\\n\\tvar triangles = [];\\r\\n\\tvar vertices = [];\\r\\n\\tvar coords = [];\\r\\n\\tvar normals = [];\\r\\n\\r\\n\\tvar N = vec3.fromValues(0,0,1);\\r\\n\\tif(xz) \\r\\n\\t\\tN.set([0,1,0]);\\r\\n\\r\\n\\tfor (var y = 0; y <= detailY; y++) {\\r\\n\\t\\tvar t = y / detailY;\\r\\n\\t\\tfor (var x = 0; x <= detailX; x++) {\\r\\n\\t\\t var s = x / detailX;\\r\\n\\t\\t if(xz)\\r\\n\\t\\t\\t vertices.push((2 * s - 1) * width, 0, -(2 * t - 1) * height);\\r\\n\\t\\t else\\r\\n\\t\\t\\t vertices.push((2 * s - 1) * width, (2 * t - 1) * height, 0);\\r\\n\\t\\t coords.push(s, t);\\r\\n\\t\\t normals.push(N[0],N[1],N[2]);\\r\\n\\t\\t if (x < detailX && y < detailY) {\\r\\n\\t\\t\\tvar i = x + y * (detailX + 1);\\r\\n\\t\\t\\tif(xz) //horizontal\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttriangles.push(i + 1, i + detailX + 1, i);\\r\\n\\t\\t\\t\\ttriangles.push(i + 1, i + detailX + 2, i + detailX + 1);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse //vertical\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttriangles.push(i, i + 1, i + detailX + 1);\\r\\n\\t\\t\\t\\ttriangles.push(i + detailX + 1, i + 1, i + detailX + 2);\\r\\n\\t\\t\\t}\\r\\n\\t\\t }\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar bounding = BBox.fromCenterHalfsize( [0,0,0], xz ? [width,0,height] : [width,height,0] );\\r\\n\\tvar mesh_info = {vertices:vertices, normals: normals, coords: coords, triangles: triangles };\\r\\n\\treturn GL.Mesh.load( mesh_info, { bounding: bounding }, gl);\\r\\n};\\r\\n\\r\\n/**\\r\\n* Returns a 2D Mesh (be careful, stream is vertices2D, used for 2D engines )\\r\\n* @method Mesh.plane2D\\r\\n*/\\r\\nMesh.plane2D = function(options, gl) {\\r\\n\\tvar vertices = new Float32Array([-1,1, 1,-1, 1,1, -1,1, -1,-1, 1,-1]);\\r\\n\\tvar coords = new Float32Array([0,1, 1,0, 1,1, 0,1, 0,0, 1,0]);\\r\\n\\r\\n\\tif(options && options.size)\\r\\n\\t{\\r\\n\\t\\tvar s = options.size * 0.5;\\r\\n\\t\\tfor(var i = 0; i < vertices.length; ++i)\\r\\n\\t\\t\\tvertices[i] *= s;\\r\\n\\t}\\r\\n\\treturn new GL.Mesh( {vertices2D: vertices, coords: coords },null,gl );\\r\\n};\\r\\n\\r\\n/**\\r\\n* Returns a point mesh \\r\\n* @method Mesh.point\\r\\n* @param {Object} options no options\\r\\n*/\\r\\nMesh.point = function(options) {\\r\\n\\treturn new GL.Mesh( {vertices: [0,0,0]} );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a cube mesh \\r\\n* @method Mesh.cube\\r\\n* @param {Object} options valid options: size \\r\\n*/\\r\\nMesh.cube = function(options, gl) {\\r\\n\\toptions = options || {};\\r\\n\\tvar halfsize = (options.size || 1) * 0.5;\\r\\n\\r\\n\\tvar buffers = {};\\r\\n\\t//[[-1,1,-1],[-1,-1,+1],[-1,1,1],[-1,1,-1],[-1,-1,-1],[-1,-1,+1],[1,1,-1],[1,1,1],[1,-1,+1],[1,1,-1],[1,-1,+1],[1,-1,-1],[-1,1,1],[1,-1,1],[1,1,1],[-1,1,1],[-1,-1,1],[1,-1,1],[-1,1,-1],[1,1,-1],[1,-1,-1],[-1,1,-1],[1,-1,-1],[-1,-1,-1],[-1,1,-1],[1,1,1],[1,1,-1],[-1,1,-1],[-1,1,1],[1,1,1],[-1,-1,-1],[1,-1,-1],[1,-1,1],[-1,-1,-1],[1,-1,1],[-1,-1,1]]\\r\\n\\tbuffers.vertices = new Float32Array([-1,1,-1,-1,-1,+1, -1,1,1,-1,1,-1, -1,-1,-1,-1,-1,+1, 1,1,-1,1,1,1,1,-1,+1,1,1,-1,1,-1,+1,1,-1,-1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,-1,-1,1,-1,-1,1,1,1,1,1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,1]);\\r\\n\\tfor(var i = 0, l = buffers.vertices.length; i < l; ++i)\\r\\n\\t\\tbuffers.vertices[i] *= halfsize;\\r\\n\\r\\n\\t//[[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0]]\\r\\n\\t//[[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0]];\\r\\n\\tbuffers.normals = new Float32Array([-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0]);\\r\\n\\tbuffers.coords = new Float32Array([0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0]);\\r\\n\\r\\n\\tif(options.wireframe)\\r\\n\\t\\tbuffers.wireframe = new Uint16Array([0,2, 2,5, 5,4, 4,0, 6,7, 7,10, 10,11, 11,6, 0,6, 2,7, 5,10, 4,11 ]);\\r\\n\\toptions.bounding = BBox.fromCenterHalfsize( [0,0,0], [halfsize,halfsize,halfsize] );\\r\\n\\treturn GL.Mesh.load(buffers, options, gl);\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns a cube mesh of a given size\\r\\n* @method Mesh.cube\\r\\n* @param {Object} options valid options: size, sizex, sizey, sizez\\r\\n*/\\r\\nMesh.box = function(options, gl) {\\r\\n\\toptions = options || {};\\r\\n\\tvar sizex = options.sizex || 1;\\r\\n\\tvar sizey = options.sizey || 1;\\r\\n\\tvar sizez = options.sizez || 1;\\r\\n\\tsizex *= 0.5;\\r\\n\\tsizey *= 0.5;\\r\\n\\tsizez *= 0.5;\\r\\n\\r\\n\\tvar buffers = {};\\r\\n\\t//[[-1,1,-1],[-1,-1,+1],[-1,1,1],[-1,1,-1],[-1,-1,-1],[-1,-1,+1],[1,1,-1],[1,1,1],[1,-1,+1],[1,1,-1],[1,-1,+1],[1,-1,-1],[-1,1,1],[1,-1,1],[1,1,1],[-1,1,1],[-1,-1,1],[1,-1,1],[-1,1,-1],[1,1,-1],[1,-1,-1],[-1,1,-1],[1,-1,-1],[-1,-1,-1],[-1,1,-1],[1,1,1],[1,1,-1],[-1,1,-1],[-1,1,1],[1,1,1],[-1,-1,-1],[1,-1,-1],[1,-1,1],[-1,-1,-1],[1,-1,1],[-1,-1,1]]\\r\\n\\tbuffers.vertices = new Float32Array([-1,1,-1,-1,-1,+1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,+1,1,1,-1,1,1,1,1,-1,+1,1,1,-1,1,-1,+1,1,-1,-1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,-1,-1,1,-1,-1,1,1,1,1,1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,1]);\\r\\n\\t//for(var i in options.vertices) for(var j in options.vertices[i]) options.vertices[i][j] *= size;\\r\\n\\tfor(var i = 0, l = buffers.vertices.length; i < l; i+=3) \\r\\n\\t{\\r\\n\\t\\tbuffers.vertices[i] *= sizex;\\r\\n\\t\\tbuffers.vertices[i+1] *= sizey;\\r\\n\\t\\tbuffers.vertices[i+2] *= sizez;\\r\\n\\t}\\r\\n\\r\\n\\t//[[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0]]\\r\\n\\t//[[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0]];\\r\\n\\tbuffers.normals = new Float32Array([-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0]);\\r\\n\\tbuffers.coords = new Float32Array([0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0]);\\r\\n\\r\\n\\tif(options.wireframe)\\r\\n\\t\\tbuffers.wireframe = new Uint16Array([0,2, 2,5, 5,4, 4,0, 6,7, 7,10, 10,11, 11,6, 0,6, 2,7, 5,10, 4,11 ]);\\r\\n\\r\\n\\toptions.bounding = BBox.fromCenterHalfsize( [0,0,0], [sizex,sizey,sizez] );\\r\\n\\r\\n\\treturn GL.Mesh.load(buffers, options, gl);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a circle mesh \\r\\n* @method Mesh.circle\\r\\n* @param {Object} options valid options: size,radius, xz = in xz plane, otherwise xy plane\\r\\n*/\\r\\nMesh.circle = function( options, gl ) {\\r\\n\\toptions = options || {};\\r\\n\\tvar size = options.size || options.radius || 1;\\r\\n\\tvar slices = Math.ceil(options.slices || 24);\\r\\n\\tvar xz = options.xz || false;\\r\\n\\tvar empty = options.empty || false;\\r\\n\\tif(slices < 3) slices = 3;\\r\\n\\tvar delta = (2 * Math.PI) / slices;\\r\\n\\r\\n\\tvar center = vec3.create();\\r\\n\\tvar A = vec3.create();\\r\\n\\tvar N = vec3.fromValues(0,0,1);\\r\\n\\tvar uv_center = vec2.fromValues(0.5,0.5);\\r\\n\\tvar uv = vec2.create();\\r\\n\\r\\n\\tif(xz) N.set([0,1,0]);\\r\\n\\r\\n\\tvar index = xz ? 2 : 1;\\r\\n\\r\\n\\tvar vertices = new Float32Array(3 * (slices + 1));\\r\\n\\tvar normals = new Float32Array(3 * (slices + 1));\\r\\n\\tvar coords = new Float32Array(2 * (slices + 1));\\r\\n\\tvar triangles = null;\\r\\n\\r\\n\\t//the center is always the same\\r\\n\\tvertices.set(center, 0);\\r\\n\\tnormals.set(N, 0);\\r\\n\\tcoords.set(uv_center, 0);\\r\\n\\r\\n\\tvar sin = 0;\\r\\n\\tvar cos = 0;\\r\\n\\r\\n\\t//compute vertices\\r\\n\\tfor(var i = 0; i < slices; ++i )\\r\\n\\t{\\r\\n\\t\\tsin = Math.sin( delta * i );\\r\\n\\t\\tcos = Math.cos( delta * i );\\r\\n\\r\\n\\t\\tA[0] = sin * size;\\r\\n\\t\\tA[index] = cos * size;\\r\\n\\t\\tuv[0] = sin * 0.5 + 0.5;\\r\\n\\t\\tuv[1] = cos * 0.5 + 0.5;\\r\\n\\t\\tvertices.set(A, i * 3 + 3);\\r\\n\\t\\tnormals.set(N, i * 3 + 3);\\r\\n\\t\\tcoords.set(uv, i * 2 + 2);\\r\\n\\t}\\r\\n\\r\\n\\tif(empty)\\r\\n\\t{\\r\\n\\t\\tvertices = vertices.subarray(3);\\r\\n\\t\\tnormals = vertices.subarray(3);\\r\\n\\t\\tcoords = vertices.subarray(2);\\r\\n\\t\\ttriangles = null;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tvar triangles = new Uint16Array(3 * slices);\\r\\n\\t\\tvar offset = 2;\\r\\n\\t\\tvar offset2 = 1;\\r\\n\\t\\tif(xz)\\r\\n\\t\\t{\\r\\n\\t\\t\\toffset = 1;\\r\\n\\t\\t\\toffset2 = 2;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//compute indices\\r\\n\\t\\tfor(var i = 0; i < slices-1; ++i )\\r\\n\\t\\t{\\r\\n\\t\\t\\ttriangles[i*3] = 0;\\r\\n\\t\\t\\ttriangles[i*3+1] = i+offset;\\r\\n\\t\\t\\ttriangles[i*3+2] = i+offset2;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\ttriangles[i*3] = 0;\\r\\n\\t\\tif(xz)\\r\\n\\t\\t{\\r\\n\\t\\t\\ttriangles[i*3+1] = i+1;\\r\\n\\t\\t\\ttriangles[i*3+2] = 1;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\ttriangles[i*3+1] = 1;\\r\\n\\t\\t\\ttriangles[i*3+2] = i+1;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\toptions.bounding = BBox.fromCenterHalfsize( [0,0,0], xz ? [size,0,size] : [size,size,0] );\\r\\n\\r\\n\\tvar buffers = {vertices: vertices, normals: normals, coords: coords, triangles: triangles};\\r\\n\\r\\n\\tif(options.wireframe)\\r\\n\\t{\\r\\n\\t\\tvar wireframe = new Uint16Array(slices*2);\\r\\n\\t\\tfor(var i = 0; i < slices; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\twireframe[i*2] = i;\\r\\n\\t\\t\\twireframe[i*2+1] = i+1;\\r\\n\\t\\t}\\r\\n\\t\\twireframe[0] = slices;\\r\\n\\t\\tbuffers.wireframe = wireframe;\\r\\n\\t}\\r\\n\\r\\n\\treturn GL.Mesh.load( buffers, options, gl );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a cube mesh \\r\\n* @method Mesh.cylinder\\r\\n* @param {Object} options valid options: radius, height, subdivisions \\r\\n*/\\r\\nMesh.cylinder = function( options, gl ) {\\r\\n\\toptions = options || {};\\r\\n\\tvar radius = options.radius || options.size || 1;\\r\\n\\tvar height = options.height || options.size || 2;\\r\\n\\tvar subdivisions = options.subdivisions || 64;\\r\\n\\r\\n\\tvar vertices = new Float32Array(subdivisions * 6 * 3 * 2 );\\r\\n\\tvar normals = new Float32Array(subdivisions * 6 * 3 * 2 );\\r\\n\\tvar coords = new Float32Array(subdivisions * 6 * 2 * 2 );\\r\\n\\t//not indexed because caps have different normals and uvs so...\\r\\n\\r\\n\\tvar delta = 2*Math.PI / subdivisions;\\r\\n\\tvar normal = null;\\r\\n\\tfor(var i = 0; i < subdivisions; ++i)\\r\\n\\t{\\r\\n\\t\\tvar angle = i * delta;\\r\\n\\r\\n\\t\\tnormal = [ Math.sin(angle), 0, Math.cos(angle)];\\r\\n\\t\\tvertices.set([ normal[0]*radius, height*0.5, normal[2]*radius], i*6*3);\\r\\n\\t\\tnormals.set(normal, i*6*3 );\\r\\n\\t\\tcoords.set([i/subdivisions,1], i*6*2 );\\r\\n\\r\\n\\t\\tnormal = [ Math.sin(angle), 0, Math.cos(angle)];\\r\\n\\t\\tvertices.set([ normal[0]*radius, height*-0.5, normal[2]*radius], i*6*3 + 3);\\r\\n\\t\\tnormals.set(normal, i*6*3 + 3);\\r\\n\\t\\tcoords.set([i/subdivisions,0], i*6*2 + 2);\\r\\n\\r\\n\\t\\tnormal = [ Math.sin(angle+delta), 0, Math.cos(angle+delta)];\\r\\n\\t\\tvertices.set([ normal[0]*radius, height*-0.5, normal[2]*radius], i*6*3 + 6);\\r\\n\\t\\tnormals.set(normal, i*6*3 + 6);\\r\\n\\t\\tcoords.set([(i+1)/subdivisions,0], i*6*2 + 4);\\r\\n\\r\\n\\t\\tnormal = [ Math.sin(angle+delta), 0, Math.cos(angle+delta)];\\r\\n\\t\\tvertices.set([ normal[0]*radius, height*0.5, normal[2]*radius], i*6*3 + 9);\\r\\n\\t\\tnormals.set(normal, i*6*3 + 9);\\r\\n\\t\\tcoords.set([(i+1)/subdivisions,1], i*6*2 + 6);\\r\\n\\r\\n\\t\\tnormal = [ Math.sin(angle), 0, Math.cos(angle)];\\r\\n\\t\\tvertices.set([ normal[0]*radius, height*0.5, normal[2]*radius], i*6*3 + 12);\\r\\n\\t\\tnormals.set(normal, i*6*3 + 12);\\r\\n\\t\\tcoords.set([i/subdivisions,1], i*6*2 + 8);\\r\\n\\r\\n\\t\\tnormal = [ Math.sin(angle+delta), 0, Math.cos(angle+delta)];\\r\\n\\t\\tvertices.set([ normal[0]*radius, height*-0.5, normal[2]*radius], i*6*3 + 15);\\r\\n\\t\\tnormals.set(normal, i*6*3 + 15);\\r\\n\\t\\tcoords.set([(i+1)/subdivisions,0], i*6*2 + 10);\\r\\n\\t}\\r\\n\\r\\n\\tvar pos = i*6*3;\\r\\n\\tvar pos_uv = i*6*2;\\r\\n\\tvar caps_start = pos;\\r\\n\\r\\n\\t//caps\\r\\n\\tif( options.caps === false )\\r\\n\\t{\\r\\n\\t\\t//finalize arrays\\r\\n\\t\\tvertices = vertices.subarray(0,pos);\\r\\n\\t\\tnormals = normals.subarray(0,pos);\\r\\n\\t\\tcoords = coords.subarray(0,pos_uv);\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tvar top_center = vec3.fromValues(0,height*0.5,0);\\r\\n\\t\\tvar bottom_center = vec3.fromValues(0,height*-0.5,0);\\r\\n\\t\\tvar up = vec3.fromValues(0,1,0);\\r\\n\\t\\tvar down = vec3.fromValues(0,-1,0);\\r\\n\\t\\tfor(var i = 0; i < subdivisions; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar angle = i * delta;\\r\\n\\r\\n\\t\\t\\tvar uv = vec3.fromValues( Math.sin(angle), 0, Math.cos(angle) );\\r\\n\\t\\t\\tvar uv2 = vec3.fromValues( Math.sin(angle+delta), 0, Math.cos(angle+delta) );\\r\\n\\r\\n\\t\\t\\tvertices.set([ uv[0]*radius, height*0.5, uv[2]*radius], pos + i*6*3);\\r\\n\\t\\t\\tnormals.set(up, pos + i*6*3 );\\r\\n\\t\\t\\tcoords.set( [ -uv[0] * 0.5 + 0.5,uv[2] * 0.5 + 0.5], pos_uv + i*6*2 );\\r\\n\\r\\n\\t\\t\\tvertices.set([ uv2[0]*radius, height*0.5, uv2[2]*radius], pos + i*6*3 + 3);\\r\\n\\t\\t\\tnormals.set(up, pos + i*6*3 + 3 );\\r\\n\\t\\t\\tcoords.set( [ -uv2[0] * 0.5 + 0.5,uv2[2] * 0.5 + 0.5], pos_uv + i*6*2 + 2 );\\r\\n\\r\\n\\t\\t\\tvertices.set( top_center, pos + i*6*3 + 6 );\\r\\n\\t\\t\\tnormals.set(up, pos + i*6*3 + 6);\\r\\n\\t\\t\\tcoords.set([0.5,0.5], pos_uv + i*6*2 + 4);\\r\\n\\t\\t\\t\\r\\n\\t\\t\\t//bottom\\r\\n\\t\\t\\tvertices.set([ uv2[0]*radius, height*-0.5, uv2[2]*radius], pos + i*6*3 + 9);\\r\\n\\t\\t\\tnormals.set(down, pos + i*6*3 + 9);\\r\\n\\t\\t\\tcoords.set( [ uv2[0] * 0.5 + 0.5,uv2[2] * 0.5 + 0.5], pos_uv + i*6*2 + 6);\\r\\n\\r\\n\\t\\t\\tvertices.set([ uv[0]*radius, height*-0.5, uv[2]*radius], pos + i*6*3 + 12);\\r\\n\\t\\t\\tnormals.set(down, pos + i*6*3 + 12 );\\r\\n\\t\\t\\tcoords.set( [ uv[0] * 0.5 + 0.5,uv[2] * 0.5 + 0.5], pos_uv + i*6*2 + 8 );\\r\\n\\r\\n\\t\\t\\tvertices.set( bottom_center, pos + i*6*3 + 15 );\\r\\n\\t\\t\\tnormals.set( down, pos + i*6*3 + 15);\\r\\n\\t\\t\\tcoords.set( [0.5,0.5], pos_uv + i*6*2 + 10);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar buffers = {\\r\\n\\t\\tvertices: vertices,\\r\\n\\t\\tnormals: normals,\\r\\n\\t\\tcoords: coords\\r\\n\\t}\\r\\n\\toptions.bounding = BBox.fromCenterHalfsize( [0,0,0], [radius,height*0.5,radius] );\\r\\n\\toptions.info = { groups: [] };\\r\\n\\r\\n\\tif(options.caps !== false)\\r\\n\\t{\\r\\n\\t\\toptions.info.groups.push({ name:\\\"side\\\", start: 0, length: caps_start / 3});\\r\\n\\t\\toptions.info.groups.push({ name:\\\"caps\\\", start: caps_start / 3, length: (vertices.length - caps_start) / 3});\\r\\n\\t}\\r\\n\\r\\n\\treturn Mesh.load( buffers, options, gl );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a cone mesh \\r\\n* @method Mesh.cone\\r\\n* @param {Object} options valid options: radius, height, subdivisions \\r\\n*/\\r\\nMesh.cone = function( options, gl ) {\\r\\n\\toptions = options || {};\\r\\n\\tvar radius = options.radius || options.size || 1;\\r\\n\\tvar height = options.height || options.size || 2;\\r\\n\\tvar subdivisions = options.subdivisions || 64;\\r\\n\\r\\n\\tvar vertices = new Float32Array(subdivisions * 3 * 3 * 2);\\r\\n\\tvar normals = new Float32Array(subdivisions * 3 * 3 * 2);\\r\\n\\tvar coords = new Float32Array(subdivisions * 2 * 3 * 2);\\r\\n\\t//not indexed because caps have different normals and uvs so...\\r\\n\\r\\n\\tvar delta = 2*Math.PI / subdivisions;\\r\\n\\tvar normal = null;\\r\\n\\tvar normal_y = radius / height;\\r\\n\\tvar up = [0,1,0];\\r\\n\\r\\n\\tfor(var i = 0; i < subdivisions; ++i)\\r\\n\\t{\\r\\n\\t\\tvar angle = i * delta;\\r\\n\\r\\n\\t\\tnormal = [ Math.sin(angle+delta*0.5), normal_y, Math.cos(angle+delta*0.5)];\\r\\n\\t\\tvec3.normalize(normal,normal);\\r\\n\\t\\t//normal = up;\\r\\n\\t\\tvertices.set([ 0, height, 0] , i*6*3);\\r\\n\\t\\tnormals.set(normal, i*6*3 );\\r\\n\\t\\tcoords.set([i/subdivisions,1], i*6*2 );\\r\\n\\r\\n\\t\\tnormal = [ Math.sin(angle), normal_y, Math.cos(angle)];\\r\\n\\t\\tvertices.set([ normal[0]*radius, 0, normal[2]*radius], i*6*3 + 3);\\r\\n\\t\\tvec3.normalize(normal,normal);\\r\\n\\t\\tnormals.set(normal, i*6*3 + 3);\\r\\n\\t\\tcoords.set([i/subdivisions,0], i*6*2 + 2);\\r\\n\\r\\n\\t\\tnormal = [ Math.sin(angle+delta), normal_y, Math.cos(angle+delta)];\\r\\n\\t\\tvertices.set([ normal[0]*radius, 0, normal[2]*radius], i*6*3 + 6);\\r\\n\\t\\tvec3.normalize(normal,normal);\\r\\n\\t\\tnormals.set(normal, i*6*3 + 6);\\r\\n\\t\\tcoords.set([(i+1)/subdivisions,0], i*6*2 + 4);\\r\\n\\t}\\r\\n\\r\\n\\tvar pos = 0;//i*3*3;\\r\\n\\tvar pos_uv = 0;//i*3*2;\\r\\n\\r\\n\\t//cap\\r\\n\\tvar bottom_center = vec3.fromValues(0,0,0);\\r\\n\\tvar down = vec3.fromValues(0,-1,0);\\r\\n\\tfor(var i = 0; i < subdivisions; ++i)\\r\\n\\t{\\r\\n\\t\\tvar angle = i * delta;\\r\\n\\r\\n\\t\\tvar uv = vec3.fromValues( Math.sin(angle), 0, Math.cos(angle) );\\r\\n\\t\\tvar uv2 = vec3.fromValues( Math.sin(angle+delta), 0, Math.cos(angle+delta) );\\r\\n\\r\\n\\t\\t//bottom\\r\\n\\t\\tvertices.set([ uv2[0]*radius, 0, uv2[2]*radius], pos + i*6*3 + 9);\\r\\n\\t\\tnormals.set(down, pos + i*6*3 + 9);\\r\\n\\t\\tcoords.set( [ uv2[0] * 0.5 + 0.5,uv2[2] * 0.5 + 0.5], pos_uv + i*6*2 + 6);\\r\\n\\r\\n\\t\\tvertices.set([ uv[0]*radius, 0, uv[2]*radius], pos + i*6*3 + 12);\\r\\n\\t\\tnormals.set(down, pos + i*6*3 + 12 );\\r\\n\\t\\tcoords.set( [ uv[0] * 0.5 + 0.5,uv[2] * 0.5 + 0.5], pos_uv + i*6*2 + 8 );\\r\\n\\r\\n\\t\\tvertices.set( bottom_center, pos + i*6*3 + 15 );\\r\\n\\t\\tnormals.set( down, pos + i*6*3 + 15);\\r\\n\\t\\tcoords.set( [0.5,0.5], pos_uv + i*6*2 + 10);\\r\\n\\t}\\r\\n\\r\\n\\tvar buffers = {\\r\\n\\t\\tvertices: vertices,\\r\\n\\t\\tnormals: normals,\\r\\n\\t\\tcoords: coords\\r\\n\\t}\\r\\n\\toptions.bounding = BBox.fromCenterHalfsize( [0,height*0.5,0], [radius,height*0.5,radius] );\\r\\n\\r\\n\\treturn Mesh.load( buffers, options, gl );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a sphere mesh \\r\\n* @method Mesh.sphere\\r\\n* @param {Object} options valid options: radius, lat, long, subdivisions, hemi\\r\\n*/\\r\\nMesh.sphere = function( options, gl ) {\\r\\n\\toptions = options || {};\\r\\n\\tvar radius = options.radius || options.size || 1;\\r\\n\\tvar latitudeBands = options.lat || options.subdivisions || 16;\\r\\n\\tvar longitudeBands = options[\\\"long\\\"] || options.subdivisions || 16;\\r\\n\\r\\n\\tvar vertexPositionData = new Float32Array( (latitudeBands+1)*(longitudeBands+1)*3 );\\r\\n\\tvar normalData = new Float32Array( (latitudeBands+1)*(longitudeBands+1)*3 );\\r\\n\\tvar textureCoordData = new Float32Array( (latitudeBands+1)*(longitudeBands+1)*2 );\\r\\n\\tvar indexData = new Uint16Array( latitudeBands*longitudeBands*6 );\\r\\n\\tvar latRange = options.hemi ? Math.PI * 0.5 : Math.PI;\\r\\n\\r\\n\\tvar i = 0, iuv = 0;\\r\\n\\tfor (var latNumber = 0; latNumber <= latitudeBands; latNumber++)\\r\\n\\t{\\r\\n\\t\\tvar theta = latNumber * latRange / latitudeBands;\\r\\n\\t\\tvar sinTheta = Math.sin(theta);\\r\\n\\t\\tvar cosTheta = Math.cos(theta);\\r\\n\\r\\n\\t\\tfor (var longNumber = 0; longNumber <= longitudeBands; longNumber++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar phi = longNumber * 2 * Math.PI / longitudeBands;\\r\\n\\t\\t\\tvar sinPhi = Math.sin(phi);\\r\\n\\t\\t\\tvar cosPhi = Math.cos(phi);\\r\\n\\r\\n\\t\\t\\tvar x = cosPhi * sinTheta;\\r\\n\\t\\t\\tvar y = cosTheta;\\r\\n\\t\\t\\tvar z = sinPhi * sinTheta;\\r\\n\\t\\t\\tvar u = 1- (longNumber / longitudeBands);\\r\\n\\t\\t\\tvar v = (1 - latNumber / latitudeBands);\\r\\n\\r\\n\\t\\t\\tvertexPositionData.set([radius * x,radius * y,radius * z],i);\\r\\n\\t\\t\\tnormalData.set([x,y,z],i);\\r\\n\\t\\t\\ttextureCoordData.set([u,v], iuv );\\r\\n\\t\\t\\ti += 3;\\r\\n\\t\\t\\tiuv += 2;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\ti=0;\\r\\n\\tfor (var latNumber = 0; latNumber < latitudeBands; latNumber++)\\r\\n\\t{\\r\\n\\t\\tfor (var longNumber = 0; longNumber < longitudeBands; longNumber++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar first = (latNumber * (longitudeBands + 1)) + longNumber;\\r\\n\\t\\t\\tvar second = first + longitudeBands + 1;\\r\\n\\r\\n\\t\\t\\tindexData.set([second,first,first + 1], i);\\r\\n\\t\\t\\tindexData.set([second + 1,second,first + 1], i+3);\\r\\n\\t\\t\\ti += 6;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar buffers = {\\r\\n\\t\\tvertices: vertexPositionData,\\r\\n\\t\\tnormals: normalData,\\r\\n\\t\\tcoords: textureCoordData,\\r\\n\\t\\ttriangles: indexData\\r\\n\\t};\\r\\n\\r\\n\\tif(options.wireframe)\\r\\n\\t{\\r\\n\\t\\tvar wireframe = new Uint16Array(longitudeBands*latitudeBands*4);\\r\\n\\t\\tvar pos = 0;\\r\\n\\t\\tfor(var i = 0; i < latitudeBands; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var j = 0; j < longitudeBands; j++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\twireframe[pos] = i*(longitudeBands+1) + j;\\r\\n\\t\\t\\t\\twireframe[pos + 1] = i*(longitudeBands+1) + j + 1;\\r\\n\\t\\t\\t\\tpos += 2;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\twireframe[pos - longitudeBands*2] = i*(longitudeBands+1) + j;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var i = 0; i < longitudeBands; i++)\\r\\n\\t\\tfor(var j = 0; j < latitudeBands; j++)\\r\\n\\t\\t{\\r\\n\\t\\t\\twireframe[pos] = j*(longitudeBands+1) + i;\\r\\n\\t\\t\\twireframe[pos + 1] = (j+1)*(longitudeBands+1) + i;\\r\\n\\t\\t\\tpos += 2;\\r\\n\\t\\t}\\r\\n\\t\\tbuffers.wireframe = wireframe;\\r\\n\\t}\\r\\n\\r\\n\\tif(options.hemi)\\r\\n\\t\\toptions.bounding = BBox.fromCenterHalfsize( [0,radius*0.5,0], [radius,radius*0.5,radius], radius );\\r\\n\\telse\\r\\n\\t\\toptions.bounding = BBox.fromCenterHalfsize( [0,0,0], [radius,radius,radius], radius );\\r\\n\\treturn GL.Mesh.load( buffers, options, gl );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a grid mesh (must be rendered using gl.LINES)\\r\\n* @method Mesh.grid\\r\\n* @param {Object} options valid options: size, lines\\r\\n*/\\r\\nMesh.grid = function( options, gl )\\r\\n{\\r\\n\\toptions = options || {};\\r\\n\\tvar num_lines = options.lines || 11;\\r\\n\\tif(num_lines < 0) \\r\\n\\t\\tnum_lines = 1;\\r\\n\\tvar size = options.size || 10;\\r\\n\\r\\n\\tvar vertexPositionData = new Float32Array( num_lines*2*2*3 );\\r\\n\\tvar hsize = size * 0.5;\\r\\n\\tvar pos = 0;\\r\\n\\tvar x = -hsize;\\r\\n\\tvar delta = size / (num_lines-1);\\r\\n\\r\\n\\tfor(var i = 0; i < num_lines; i++)\\r\\n\\t{\\r\\n\\t\\tvertexPositionData[ pos ] = x;\\r\\n\\t\\tvertexPositionData[ pos+2 ] = -hsize;\\r\\n\\t\\tvertexPositionData[ pos+3 ] = x;\\r\\n\\t\\tvertexPositionData[ pos+5 ] = hsize;\\r\\n\\r\\n\\t\\tvertexPositionData[ pos+6 ] = hsize;\\r\\n\\t\\tvertexPositionData[ pos+8 ] = x\\r\\n\\t\\tvertexPositionData[ pos+9 ] = -hsize;\\r\\n\\t\\tvertexPositionData[ pos+11 ] = x\\r\\n\\r\\n\\t\\tx += delta;\\r\\n\\t\\tpos += 12;\\r\\n\\t}\\r\\n\\r\\n\\treturn new GL.Mesh({vertices: vertexPositionData}, options, gl );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns a icosahedron mesh (useful to create spheres by subdivision)\\r\\n* @method Mesh.icosahedron\\r\\n* @param {Object} options valid options: radius, subdivisions (max: 6)\\r\\n*/\\r\\nMesh.icosahedron = function( options, gl ) {\\r\\n\\toptions = options || {};\\r\\n\\tvar radius = options.radius || options.size || 1;\\r\\n\\tvar subdivisions = options.subdivisions === undefined ? 0 : options.subdivisions;\\r\\n\\tif(subdivisions > 6) //dangerous\\r\\n\\t\\tsubdivisions = 6;\\r\\n\\r\\n\\tvar t = (1.0 + Math.sqrt(5)) / 2.0;\\r\\n\\tvar vertices = [-1,t,0, 1,t,0, -1,-t,0, 1,-t,0,\\r\\n\\t\\t\\t\\t\\t0,-1,t, 0,1,t, 0,-1,-t, 0,1,-t,\\r\\n\\t\\t\\t\\t\\tt,0,-1, t,0,1, -t,0,-1, -t,0,1];\\r\\n\\tvar normals = [];\\r\\n\\tvar coords = [];\\r\\n\\tvar indices = [0,11,5, 0,5,1, 0,1,7, 0,7,10, 0,10,11, 1,5,9, 5,11,4, 11,10,2, 10,7,6, 7,1,8, 3,9,4, 3,4,2, 3,2,6, 3,6,8, 3,8,9, 4,9,5, 2,4,11, 6,2,10, 8,6,7, 9,8,1 ];\\r\\n\\r\\n\\t//normalize\\r\\n\\tvar l = vertices.length;\\r\\n\\tfor(var i = 0; i < l; i+=3)\\r\\n\\t{\\r\\n\\t\\tvar mod = Math.sqrt( vertices[i]*vertices[i] + vertices[i+1]*vertices[i+1] + vertices[i+2]*vertices[i+2] );\\r\\n\\t\\tvar normalx = vertices[i] / mod;\\r\\n\\t\\tvar normaly = vertices[i+1] / mod;\\r\\n\\t\\tvar normalz = vertices[i+2] / mod;\\r\\n\\t\\tnormals.push( normalx, normaly, normalz );\\r\\n\\t\\tcoords.push( Math.atan2( normalx, normalz ), Math.acos( normaly ) );\\r\\n\\t\\tvertices[i] *= radius/mod;\\r\\n\\t\\tvertices[i+1] *= radius/mod;\\r\\n\\t\\tvertices[i+2] *= radius/mod;\\r\\n\\t}\\r\\n\\r\\n\\tvar middles = {};\\r\\n\\r\\n\\t//A,B = index of vertex in vertex array\\r\\n\\tfunction middlePoint( A, B )\\r\\n\\t{\\r\\n\\t\\tvar key = indices[A] < indices[B] ? indices[A] + \\\":\\\"+indices[B] : indices[B]+\\\":\\\"+indices[A];\\r\\n\\t\\tvar r = middles[key];\\r\\n\\t\\tif(r)\\r\\n\\t\\t\\treturn r;\\r\\n\\t\\tvar index = vertices.length / 3;\\r\\n\\t\\tvertices.push(( vertices[ indices[A]*3] + vertices[ indices[B]*3 ]) * 0.5,\\r\\n\\t\\t\\t\\t\\t(vertices[ indices[A]*3+1] + vertices[ indices[B]*3+1 ]) * 0.5,\\r\\n\\t\\t\\t\\t\\t(vertices[ indices[A]*3+2] + vertices[ indices[B]*3+2 ]) * 0.5);\\r\\n\\r\\n\\t\\tvar mod = Math.sqrt( vertices[index*3]*vertices[index*3] + vertices[index*3+1]*vertices[index*3+1] + vertices[index*3+2]*vertices[index*3+2] );\\r\\n\\t\\tvar normalx = vertices[index*3] / mod;\\r\\n\\t\\tvar normaly = vertices[index*3+1] / mod;\\r\\n\\t\\tvar normalz = vertices[index*3+2] / mod;\\r\\n\\t\\tnormals.push( normalx, normaly, normalz );\\r\\n\\t\\tcoords.push( (Math.atan2( normalx, normalz ) / Math.PI) * 0.5, (Math.acos( normaly ) / Math.PI) );\\r\\n\\t\\tvertices[index*3] *= radius/mod;\\r\\n\\t\\tvertices[index*3+1] *= radius/mod;\\r\\n\\t\\tvertices[index*3+2] *= radius/mod;\\r\\n\\r\\n\\t\\tmiddles[key] = index;\\r\\n\\t\\treturn index;\\r\\n\\t}\\r\\n\\r\\n\\tfor (var iR = 0; iR < subdivisions; ++iR )\\r\\n\\t{\\r\\n\\t\\tvar new_indices = [];\\r\\n\\t\\tvar l = indices.length;\\r\\n\\t\\tfor(var i = 0; i < l; i+=3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar MA = middlePoint( i, i+1 );\\r\\n\\t\\t\\tvar MB = middlePoint( i+1, i+2);\\r\\n\\t\\t\\tvar MC = middlePoint( i+2, i);\\r\\n\\t\\t\\tnew_indices.push(indices[i], MA, MC);\\r\\n\\t\\t\\tnew_indices.push(indices[i+1], MB, MA);\\r\\n\\t\\t\\tnew_indices.push(indices[i+2], MC, MB);\\r\\n\\t\\t\\tnew_indices.push(MA, MB, MC);\\r\\n\\t\\t}\\r\\n\\t\\tindices = new_indices;\\r\\n\\t}\\r\\n\\r\\n\\toptions.bounding = BBox.fromCenterHalfsize( [0,0,0], [radius,radius,radius], radius );\\r\\n\\r\\n\\treturn new GL.Mesh.load({vertices: vertices, coords: coords, normals: normals, triangles: indices},options,gl);\\r\\n}\\r\\n/**\\r\\n* @namespace GL\\r\\n*/\\r\\n\\r\\n/**\\r\\n* Texture class to upload images to the GPU, default is gl.TEXTURE_2D, gl.RGBA of gl.UNSIGNED_BYTE with filters set to gl.LINEAR and wrap to gl.CLAMP_TO_EDGE
\\r\\n\\tThere is a list of options
\\r\\n\\t==========================
\\r\\n\\t- texture_type: gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP, default gl.TEXTURE_2D
\\r\\n\\t- format: gl.RGB, gl.RGBA, gl.DEPTH_COMPONENT, default gl.RGBA
\\r\\n\\t- type: gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT, gl.HALF_FLOAT_OES, gl.FLOAT, default gl.UNSIGNED_BYTE
\\r\\n\\t- filter: filtering for mag and min: gl.NEAREST or gl.LINEAR, default gl.NEAREST
\\r\\n\\t- magFilter: magnifying filter: gl.NEAREST, gl.LINEAR, default gl.NEAREST
\\r\\n\\t- minFilter: minifying filter: gl.NEAREST, gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR, default gl.NEAREST
\\r\\n\\t- wrap: texture wrapping: gl.CLAMP_TO_EDGE, gl.REPEAT, gl.MIRROR, default gl.CLAMP_TO_EDGE (also accepts wrapT and wrapS for separate settings)
\\r\\n\\t- pixel_data: ArrayBufferView with the pixel data to upload to the texture, otherwise the texture will be black (if cubemaps then pass an array[6] with the data for every face)
\\r\\n\\t- premultiply_alpha : multiply the color by the alpha value when uploading, default FALSE
\\r\\n\\t- no_flip : do not flip in Y, default TRUE
\\r\\n\\t- anisotropic : number of anisotropic fetches, default 0
\\r\\n\\r\\n\\tcheck for more info about formats: https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D\\r\\n\\r\\n* @class Texture\\r\\n* @param {number} width texture width (any supported but Power of Two allows to have mipmaps), 0 means no memory reserved till its filled\\r\\n* @param {number} height texture height (any supported but Power of Two allows to have mipmaps), 0 means no memory reserved till its filled\\r\\n* @param {Object} options Check the list in the description\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nglobal.Texture = GL.Texture = function Texture( width, height, options, gl ) {\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\t//used to avoid problems with resources moving between different webgl context\\r\\n\\tgl = gl || global.gl;\\r\\n\\tthis.gl = gl;\\r\\n\\tthis._context_id = gl.context_id; \\r\\n\\r\\n\\t//round sizes\\r\\n\\twidth = parseInt(width); \\r\\n\\theight = parseInt(height);\\r\\n\\r\\n\\tif(GL.debug)\\r\\n\\t\\tconsole.log(\\\"GL.Texture created: \\\",width,height);\\r\\n\\r\\n\\t//create texture handler\\r\\n\\tthis.handler = gl.createTexture();\\r\\n\\r\\n\\t//set settings\\r\\n\\tthis.width = width;\\r\\n\\tthis.height = height;\\r\\n\\tif(options.depth) //for texture_3d\\r\\n\\t\\tthis.depth = options.depth; \\r\\n\\tthis.texture_type = options.texture_type || gl.TEXTURE_2D; //or gl.TEXTURE_CUBE_MAP\\r\\n\\tthis.format = options.format || Texture.DEFAULT_FORMAT; //gl.RGBA (if gl.DEPTH_COMPONENT remember type: gl.UNSIGNED_SHORT)\\r\\n\\tthis.internalFormat = options.internalFormat; //LUMINANCE, and weird formats with bits\\r\\n\\tthis.type = options.type || Texture.DEFAULT_TYPE; //gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT, gl.FLOAT or gl.HALF_FLOAT_OES (or gl.HIGH_PRECISION_FORMAT which could be half or float)\\r\\n\\tthis.magFilter = options.magFilter || options.filter || Texture.DEFAULT_MAG_FILTER;\\r\\n\\tthis.minFilter = options.minFilter || options.filter || Texture.DEFAULT_MIN_FILTER;\\r\\n\\tthis.wrapS = options.wrap || options.wrapS || Texture.DEFAULT_WRAP_S; \\r\\n\\tthis.wrapT = options.wrap || options.wrapT || Texture.DEFAULT_WRAP_T;\\r\\n\\tthis.data = null; //where the data came from\\r\\n\\r\\n\\t//precompute the max amount of texture units\\r\\n\\tif(!Texture.MAX_TEXTURE_IMAGE_UNITS)\\r\\n\\t\\tTexture.MAX_TEXTURE_IMAGE_UNITS = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );\\r\\n\\r\\n\\tthis.has_mipmaps = false;\\r\\n\\r\\n\\tif( this.format == gl.DEPTH_COMPONENT && gl.webgl_version == 1 && !gl.extensions[\\\"WEBGL_depth_texture\\\"] )\\r\\n\\t\\tthrow(\\\"Depth Texture not supported\\\");\\r\\n\\tif( this.type == gl.FLOAT && !gl.extensions[\\\"OES_texture_float\\\"] && gl.webgl_version == 1 )\\r\\n\\t\\tthrow(\\\"Float Texture not supported\\\");\\r\\n\\tif( this.type == gl.HALF_FLOAT_OES)\\r\\n\\t{\\r\\n\\t\\tif( !gl.extensions[\\\"OES_texture_half_float\\\"] && gl.webgl_version == 1 )\\r\\n\\t\\t\\tthrow(\\\"Half Float Texture extension not supported.\\\");\\r\\n\\t\\telse if( gl.webgl_version > 1 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"using HALF_FLOAT_OES in WebGL2 is deprecated, suing HALF_FLOAT instead\\\");\\r\\n\\t\\t\\tthis.type = this.format == gl.RGB ? gl.RGB16F : gl.RGBA16F;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\tif( (!isPowerOfTwo(this.width) || !isPowerOfTwo(this.height)) && //non power of two\\r\\n\\t\\t( (this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR) || //uses mipmaps\\r\\n\\t\\t(this.wrapS != gl.CLAMP_TO_EDGE || this.wrapT != gl.CLAMP_TO_EDGE) ) ) //uses wrap\\r\\n\\t{\\r\\n\\t\\tif(!options.ignore_pot)\\r\\n\\t\\t\\tthrow(\\\"Cannot use texture-wrap or mipmaps in Non-Power-of-Two textures\\\");\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.minFilter = this.magFilter = gl.LINEAR;\\r\\n\\t\\t\\tthis.wrapS = this.wrapT = gl.CLAMP_TO_EDGE;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//empty textures are allowed to be created\\r\\n\\tif(!width || !height)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//because sometimes the internal format is not so obvious\\r\\n\\tif(!this.internalFormat)\\r\\n\\t\\tthis.computeInternalFormat();\\r\\n\\r\\n\\t//this is done because in some cases the user binds a texture to slot 0 and then creates a new one, which overrides slot 0\\r\\n\\tgl.activeTexture( gl.TEXTURE0 + Texture.MAX_TEXTURE_IMAGE_UNITS - 1);\\r\\n\\t//I use an invalid gl enum to say this texture is a depth texture, ugly, I know...\\r\\n\\tgl.bindTexture( this.texture_type, this.handler);\\r\\n\\tgl.texParameteri( this.texture_type, gl.TEXTURE_MAG_FILTER, this.magFilter );\\r\\n\\tgl.texParameteri( this.texture_type, gl.TEXTURE_MIN_FILTER, this.minFilter );\\r\\n\\tgl.texParameteri( this.texture_type, gl.TEXTURE_WRAP_S, this.wrapS );\\r\\n\\tgl.texParameteri( this.texture_type, gl.TEXTURE_WRAP_T, this.wrapT );\\r\\n\\r\\n\\tif(options.anisotropic && gl.extensions[\\\"EXT_texture_filter_anisotropic\\\"])\\r\\n\\t\\tgl.texParameterf( GL.TEXTURE_2D, gl.extensions[\\\"EXT_texture_filter_anisotropic\\\"].TEXTURE_MAX_ANISOTROPY_EXT, options.anisotropic);\\r\\n\\r\\n\\tvar type = this.type;\\r\\n\\tvar pixel_data = options.pixel_data;\\r\\n\\tif(pixel_data && !pixel_data.buffer)\\r\\n\\t{\\r\\n\\t\\tif( this.texture_type == GL.TEXTURE_CUBE_MAP )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(pixel_data[0].constructor === Number) //special case, specify just one face and copy it\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tpixel_data = toTypedArray( pixel_data );\\r\\n\\t\\t\\t\\tpixel_data = [pixel_data,pixel_data,pixel_data,pixel_data,pixel_data,pixel_data]; \\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tfor(var i = 0; i < pixel_data.length; ++i)\\r\\n\\t\\t\\t\\t\\tpixel_data[i] = toTypedArray( pixel_data[i] );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tpixel_data = toTypedArray( pixel_data );\\r\\n\\t\\tthis.data = pixel_data;\\r\\n\\t}\\r\\n\\r\\n\\tfunction toTypedArray( data )\\r\\n\\t{\\r\\n\\t\\tif(data.constructor !== Array)\\r\\n\\t\\t\\treturn data;\\r\\n\\t\\tif( type == GL.FLOAT)\\r\\n\\t\\t\\treturn new Float32Array( data );\\r\\n\\t\\tif( type == GL.HALF_FLOAT_OES)\\r\\n\\t\\t\\treturn new Uint16Array( data );\\r\\n\\t\\treturn new Uint8Array( data );\\r\\n\\t}\\r\\n\\r\\n\\t//gl.TEXTURE_1D is not supported by WebGL...\\r\\n\\r\\n\\t//here we create all **********************************\\r\\n\\tif(this.texture_type == GL.TEXTURE_2D)\\r\\n\\t{\\r\\n\\t\\t//create the texture\\r\\n\\t\\tgl.texImage2D( GL.TEXTURE_2D, 0, this.internalFormat, width, height, 0, this.format, this.type, pixel_data || null );\\r\\n\\r\\n\\t\\t//generate empty mipmaps (necessary?)\\r\\n\\t\\tif ( GL.isPowerOfTwo(width) && GL.isPowerOfTwo(height) && options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.generateMipmap( this.texture_type );\\r\\n\\t\\t\\tthis.has_mipmaps = true;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(this.texture_type == GL.TEXTURE_CUBE_MAP)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < 6; ++i)\\r\\n\\t\\t\\tgl.texImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, this.internalFormat, this.width, this.height, 0, this.format, this.type, pixel_data ? pixel_data[i] : null );\\r\\n\\t}\\r\\n\\telse if(this.texture_type == GL.TEXTURE_3D)\\r\\n\\t{\\r\\n\\t\\tif(this.gl.webgl_version == 1)\\r\\n\\t\\t\\tthrow(\\\"TEXTURE_3D not supported in WebGL 1. Enable WebGL 2 in the context by passing webgl2:true to the context\\\");\\r\\n\\t\\tif(!options.depth)\\r\\n\\t\\t\\tthrow(\\\"3d texture depth must be set in the options.depth\\\");\\r\\n\\t\\tgl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); //standard does not allow this flags for 3D textures\\r\\n\\t\\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false );\\r\\n\\t\\tgl.texImage3D( GL.TEXTURE_3D, 0, this.internalFormat, width, height, options.depth, 0, this.format, this.type, pixel_data || null );\\r\\n\\t}\\r\\n\\tgl.bindTexture(this.texture_type, null); //disable\\r\\n\\tgl.activeTexture(gl.TEXTURE0);\\r\\n}\\r\\n\\r\\nTexture.DEFAULT_TYPE = GL.UNSIGNED_BYTE;\\r\\nTexture.DEFAULT_FORMAT = GL.RGBA;\\r\\nTexture.DEFAULT_MAG_FILTER = GL.LINEAR;\\r\\nTexture.DEFAULT_MIN_FILTER = GL.LINEAR;\\r\\nTexture.DEFAULT_WRAP_S = GL.CLAMP_TO_EDGE;\\r\\nTexture.DEFAULT_WRAP_T = GL.CLAMP_TO_EDGE;\\r\\nTexture.EXTENSION = \\\"png\\\"; //used when saving it to file\\r\\n\\r\\n//used for render to FBOs\\r\\nTexture.framebuffer = null;\\r\\nTexture.renderbuffer = null;\\r\\nTexture.loading_color = new Uint8Array([0,0,0,0]);\\r\\nTexture.use_renderbuffer_pool = true; //should improve performance\\r\\n\\r\\n//because usually you dont want to specify the internalFormat, this tries to guess it from its format\\r\\n//check https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html for more info\\r\\nTexture.prototype.computeInternalFormat = function()\\r\\n{\\r\\n\\tthis.internalFormat = this.format; //default\\r\\n\\r\\n\\t//automatic selection of internal format for depth textures to avoid problems between webgl1 and 2\\r\\n\\tif( this.format == GL.DEPTH_COMPONENT )\\r\\n\\t{\\r\\n\\t\\tthis.minFilter = this.magFilter = GL.NEAREST;\\r\\n\\r\\n\\t\\tif( gl.webgl_version == 2 ) \\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.type == GL.UNSIGNED_SHORT )\\r\\n\\t\\t\\t\\tthis.internalFormat = GL.DEPTH_COMPONENT16;\\r\\n\\t\\t\\telse if( this.type == GL.UNSIGNED_INT )\\r\\n\\t\\t\\t\\tthis.internalFormat = GL.DEPTH_COMPONENT24;\\r\\n\\t\\t\\telse if( this.type == GL.FLOAT )\\r\\n\\t\\t\\t\\tthis.internalFormat = GL.DEPTH_COMPONENT32F;\\r\\n\\t\\t\\telse \\r\\n\\t\\t\\t\\tthrow(\\\"unsupported type for a depth texture\\\");\\r\\n\\t\\t}\\r\\n\\t\\telse if( gl.webgl_version == 1 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.type == GL.FLOAT )\\r\\n\\t\\t\\t\\tthrow(\\\"WebGL 1.0 does not support float depth textures\\\");\\r\\n\\t\\t\\tthis.internalFormat = GL.DEPTH_COMPONENT;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if( this.format == gl.RGBA )\\r\\n\\t{\\r\\n\\t\\tif( gl.webgl_version == 2 ) \\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.type == GL.FLOAT )\\r\\n\\t\\t\\t\\tthis.internalFormat = GL.RGBA32F;\\r\\n\\t\\t\\telse if( this.type == GL.HALF_FLOAT )\\r\\n\\t\\t\\t\\tthis.internalFormat = GL.RGBA16F;\\r\\n\\t\\t\\telse if( this.type == GL.HALF_FLOAT_OES )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"webgl 2 does not use HALF_FLOAT_OES, converting to HALF_FLOAT\\\")\\r\\n\\t\\t\\t\\tthis.type = GL.HALF_FLOAT;\\r\\n\\t\\t\\t\\tthis.internalFormat = GL.RGBA16F;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\telse if( this.type == GL.UNSIGNED_SHORT )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.internalFormat = GL.RGBA16UI;\\r\\n\\t\\t\\t\\tthis.format = gl.RGBA_INTEGER;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if( this.type == GL.UNSIGNED_INT )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.internalFormat = GL.RGBA32UI;\\r\\n\\t\\t\\t\\tthis.format = gl.RGBA_INTEGER;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t*/\\r\\n\\t\\t}\\r\\n\\t\\telse if( gl.webgl_version == 1 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.type == GL.HALF_FLOAT )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"webgl 1 does not use HALF_FLOAT, converting to HALF_FLOAT_OES\\\")\\r\\n\\t\\t\\t\\tthis.type = GL.HALF_FLOAT_OES;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Free the texture memory from the GPU, sets the texture handler to null\\r\\n* @method delete\\r\\n*/\\r\\nTexture.prototype.delete = function()\\r\\n{\\r\\n\\tgl.deleteTexture( this.handler );\\r\\n\\tthis.handler = null;\\r\\n}\\r\\n\\r\\nTexture.prototype.getProperties = function()\\r\\n{\\r\\n\\treturn {\\r\\n\\t\\twidth: this.width,\\r\\n\\t\\theight: this.height,\\r\\n\\t\\ttype: this.type,\\r\\n\\t\\tformat: this.format,\\r\\n\\t\\ttexture_type: this.texture_type,\\r\\n\\t\\tmagFilter: this.magFilter,\\r\\n\\t\\tminFilter: this.minFilter,\\r\\n\\t\\twrapS: this.wrapS,\\r\\n\\t\\twrapT: this.wrapT\\r\\n\\t};\\r\\n}\\r\\n\\r\\nTexture.prototype.hasSameProperties = function(t)\\r\\n{\\r\\n\\tif(!t)\\r\\n\\t\\treturn false;\\r\\n\\treturn t.width == this.width && \\r\\n\\t\\tt.height == this.height &&\\r\\n\\t\\tt.type == this.type &&\\r\\n\\t\\tt.format == this.format &&\\r\\n\\t\\tt.texture_type == this.texture_type;\\r\\n}\\r\\n\\r\\nTexture.prototype.hasSameSize = function(t)\\r\\n{\\r\\n\\tif(!t)\\r\\n\\t\\treturn false;\\r\\n\\treturn t.width == this.width && t.height == this.height;\\r\\n}\\r\\n//textures cannot be stored in JSON\\r\\nTexture.prototype.toJSON = function()\\r\\n{\\r\\n\\treturn \\\"\\\";\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns if depth texture is supported by the GPU\\r\\n* @method isDepthSupported\\r\\n* @return {Boolean} true if supported\\r\\n*/\\r\\nTexture.isDepthSupported = function()\\r\\n{\\r\\n\\treturn gl.extensions[\\\"WEBGL_depth_texture\\\"] != null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Binds the texture to one texture unit\\r\\n* @method bind\\r\\n* @param {number} unit texture unit\\r\\n* @return {number} returns the texture unit\\r\\n*/\\r\\nTexture.prototype.bind = function( unit ) {\\r\\n\\tif(unit == undefined)\\r\\n\\t\\tunit = 0;\\r\\n\\tvar gl = this.gl;\\r\\n\\r\\n\\t//TODO: if the texture is not uploaded, must be upload now\\r\\n\\r\\n\\t//bind\\r\\n\\tgl.activeTexture(gl.TEXTURE0 + unit);\\r\\n\\tgl.bindTexture( this.texture_type, this.handler );\\r\\n\\treturn unit;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Unbinds the texture \\r\\n* @method unbind\\r\\n* @param {number} unit texture unit\\r\\n* @return {number} returns the texture unit\\r\\n*/\\r\\nTexture.prototype.unbind = function(unit) {\\r\\n\\tif(unit === undefined)\\r\\n\\t\\tunit = 0;\\r\\n\\tvar gl = this.gl;\\r\\n\\tgl.activeTexture(gl.TEXTURE0 + unit );\\r\\n\\tgl.bindTexture(this.texture_type, null);\\r\\n}\\r\\n\\r\\n\\r\\nTexture.prototype.setParameter = function(param,value) {\\r\\n\\tthis.bind(0);\\r\\n\\tthis.gl.texParameteri( this.texture_type, param, value );\\r\\n\\tswitch(param)\\r\\n\\t{\\r\\n\\t\\tcase this.gl.TEXTURE_MAG_FILTER: this.magFilter = value; break;\\r\\n\\t\\tcase this.gl.TEXTURE_MIN_FILTER: this.minFilter = value; break;\\r\\n\\t\\tcase this.gl.TEXTURE_WRAP_S: this.wrapS = value; break;\\r\\n\\t\\tcase this.gl.TEXTURE_WRAP_T: this.wrapT = value; break;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Unbinds the texture \\r\\n* @method Texture.setUploadOptions\\r\\n* @param {Object} options a list of options to upload the texture\\r\\n* - premultiply_alpha : multiply the color by the alpha value, default FALSE\\r\\n* - no_flip : do not flip in Y, default TRUE\\r\\n*/\\r\\nTexture.setUploadOptions = function(options, gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\r\\n\\tif(options) //options that are not stored in the texture should be passed again to avoid reusing unknown state\\r\\n\\t{\\r\\n\\t\\tgl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !!(options.premultiply_alpha) );\\r\\n\\t\\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !(options.no_flip) );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tgl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false );\\r\\n\\t\\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true );\\r\\n\\t}\\r\\n\\tgl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Given an Image/Canvas/Video it uploads it to the GPU\\r\\n* @method uploadImage\\r\\n* @param {Image} img\\r\\n* @param {Object} options [optional] upload options (premultiply_alpha, no_flip)\\r\\n*/\\r\\nTexture.prototype.uploadImage = function( image, options )\\r\\n{\\r\\n\\tthis.bind();\\r\\n\\tvar gl = this.gl;\\r\\n\\tif(!image)\\r\\n\\t\\tthrow(\\\"uploadImage parameter must be Image\\\");\\r\\n\\r\\n\\tTexture.setUploadOptions(options, gl);\\r\\n\\r\\n\\ttry {\\r\\n\\t\\tgl.texImage2D( gl.TEXTURE_2D, 0, this.format, this.format, this.type, image );\\r\\n\\t\\tthis.width = image.videoWidth || image.width;\\r\\n\\t\\tthis.height = image.videoHeight || image.height;\\r\\n\\t\\tthis.data = image;\\r\\n\\t} catch (e) {\\r\\n\\t\\tif (location.protocol == 'file:') {\\r\\n\\t\\t\\tthrow 'image not loaded for security reasons (serve this page over \\\"http://\\\" instead)';\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tthrow 'image not loaded for security reasons (image must originate from the same ' +\\r\\n\\t\\t\\t'domain as this page or use Cross-Origin Resource Sharing)';\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//TODO: add expand transparent pixels option\\r\\n\\r\\n\\t//generate mipmaps\\r\\n\\tif (this.minFilter && this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR) {\\r\\n\\t\\tgl.generateMipmap(this.texture_type);\\r\\n\\t\\tthis.has_mipmaps = true;\\r\\n\\t}\\r\\n\\tgl.bindTexture(this.texture_type, null); //disable\\r\\n}\\r\\n\\r\\n/**\\r\\n* Uploads data to the GPU (data must have the appropiate size)\\r\\n* @method uploadData\\r\\n* @param {ArrayBuffer} data\\r\\n* @param {Object} options [optional] upload options (premultiply_alpha, no_flip, cubemap_face, mipmap_level)\\r\\n*/\\r\\nTexture.prototype.uploadData = function( data, options, skip_mipmaps )\\r\\n{\\r\\n\\toptions = options || {};\\r\\n\\tif(!data)\\r\\n\\t\\tthrow(\\\"no data passed\\\");\\r\\n\\tvar gl = this.gl;\\r\\n\\tthis.bind();\\r\\n\\tTexture.setUploadOptions(options, gl);\\r\\n\\tvar mipmap_level = options.mipmap_level || 0;\\r\\n\\tvar width = this.width;\\r\\n\\tvar height = this.height;\\r\\n\\twidth = width >> mipmap_level; \\r\\n\\theight = height >> mipmap_level;\\r\\n\\tvar internal_format = this.internalFormat || this.format;\\r\\n\\r\\n\\tif( this.type == GL.HALF_FLOAT_OES && data.constructor === Float32Array )\\r\\n\\t\\tconsole.warn(\\\"cannot uploadData to a HALF_FLOAT texture from a Float32Array, must be Uint16Array. To upload it we recomment to create a FLOAT texture, upload data there and copy to your HALF_FLOAT.\\\");\\r\\n\\r\\n\\tif( this.texture_type == GL.TEXTURE_2D )\\r\\n\\t{\\r\\n\\t\\tif(gl.webgl_version == 1)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(data.buffer && data.buffer.constructor == ArrayBuffer)\\r\\n\\t\\t\\t\\tgl.texImage2D(this.texture_type, mipmap_level, internal_format, width, height, 0, this.format, this.type, data);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.texImage2D(this.texture_type, mipmap_level, internal_format, this.format, this.type, data);\\r\\n\\t\\t}\\r\\n\\t\\telse if(gl.webgl_version == 2) //webgl forces to use width and height\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(data.buffer && data.buffer.constructor == ArrayBuffer)\\r\\n\\t\\t\\t\\tgl.texImage2D(this.texture_type, mipmap_level, internal_format, width, height, 0, this.format, this.type, data);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.texImage2D(this.texture_type, mipmap_level, internal_format, width, height, 0, this.format, this.type, data);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if( this.texture_type == GL.TEXTURE_3D )\\r\\n\\t{\\r\\n\\t\\tgl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); //standard does not allow this flags for 3D textures\\r\\n\\t\\tgl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false );\\r\\n\\t\\tgl.texImage3D( this.texture_type, mipmap_level, internal_format, width, height, this.depth >> mipmap_level, 0, this.format, this.type, data);\\r\\n\\t}\\r\\n\\telse if( this.texture_type == GL.TEXTURE_CUBE_MAP )\\r\\n\\t\\tgl.texImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + (options.cubemap_face || 0), mipmap_level, internal_format, width, height, 0, this.format, this.type, data);\\r\\n\\telse\\r\\n\\t\\tthrow(\\\"cannot uploadData for this texture type\\\");\\r\\n\\r\\n\\tthis.data = data; //should I clone it?\\r\\n\\r\\n\\tif (!skip_mipmaps && this.minFilter && this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR) {\\r\\n\\t\\tgl.generateMipmap(this.texture_type);\\r\\n\\t\\tthis.has_mipmaps = true;\\r\\n\\t}\\r\\n\\tgl.bindTexture(this.texture_type, null); //disable\\r\\n}\\r\\n\\r\\n//When creating cubemaps this is helpful\\r\\n\\r\\n/*THIS WORKS old\\r\\nTexture.cubemap_camera_parameters = [\\r\\n\\t{ type:\\\"posX\\\", dir: vec3.fromValues(-1,0,0), \\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(0,0,-1) },\\r\\n\\t{ type:\\\"negX\\\", dir: vec3.fromValues(1,0,0),\\t\\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(0,0,1) },\\r\\n\\t{ type:\\\"posY\\\", dir: vec3.fromValues(0,-1,0), \\tup: vec3.fromValues(0,0,-1), right: vec3.fromValues(1,0,0) },\\r\\n\\t{ type:\\\"negY\\\", dir: vec3.fromValues(0,1,0),\\t\\tup: vec3.fromValues(0,0,1),\\tright: vec3.fromValues(-1,0,0) },\\r\\n\\t{ type:\\\"posZ\\\", dir: vec3.fromValues(0,0,-1), \\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(1,0,0) },\\r\\n\\t{ type:\\\"negZ\\\", dir: vec3.fromValues(0,0,1),\\t\\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(-1,0,0) }\\r\\n];\\r\\n*/\\r\\n\\r\\n//THIS works\\r\\nTexture.cubemap_camera_parameters = [\\r\\n\\t{ type:\\\"posX\\\", dir: vec3.fromValues(1,0,0), \\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(0,0,-1) },\\r\\n\\t{ type:\\\"negX\\\", dir: vec3.fromValues(-1,0,0),\\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(0,0,1) },\\r\\n\\t{ type:\\\"posY\\\", dir: vec3.fromValues(0,1,0), \\tup: vec3.fromValues(0,0,-1), right: vec3.fromValues(1,0,0) },\\r\\n\\t{ type:\\\"negY\\\", dir: vec3.fromValues(0,-1,0),\\tup: vec3.fromValues(0,0,1),\\tright: vec3.fromValues(1,0,0) },\\r\\n\\t{ type:\\\"posZ\\\", dir: vec3.fromValues(0,0,1), \\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(1,0,0) },\\r\\n\\t{ type:\\\"negZ\\\", dir: vec3.fromValues(0,0,-1),\\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(-1,0,0) }\\r\\n];\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Render to texture using FBO, just pass the callback to a rendering function and the content of the texture will be updated\\r\\n* If the texture is a cubemap, the callback will be called six times, once per face, the number of the face is passed as a second parameter\\r\\n* for further info about how to set up the propper cubemap camera, check the GL.Texture.cubemap_camera_parameters with the direction and up vector for every face.\\r\\n*\\r\\n* Keep in mind that it tries to reuse the last renderbuffer for the depth, and if it cannot (different size) it creates a new one (throwing the old)\\r\\n* @method drawTo\\r\\n* @param {Function} callback function that does all the rendering inside this texture\\r\\n*/\\r\\nTexture.prototype.drawTo = function(callback, params)\\r\\n{\\r\\n\\tvar gl = this.gl;\\r\\n\\r\\n\\t//if(this.format == gl.DEPTH_COMPONENT)\\r\\n\\t//\\tthrow(\\\"cannot use drawTo in depth textures, use Texture.drawToColorAndDepth\\\");\\r\\n\\r\\n\\tvar v = gl.getViewport();\\r\\n\\tvar now = GL.getTime();\\r\\n\\r\\n\\tvar old_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING );\\r\\n\\r\\n\\tvar framebuffer = gl._framebuffer = gl._framebuffer || gl.createFramebuffer();\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, framebuffer );\\r\\n\\r\\n\\t//this code allows to reuse old renderbuffers instead of creating and destroying them for every frame\\r\\n\\tvar renderbuffer = null;\\r\\n\\r\\n\\tif( Texture.use_renderbuffer_pool ) //create a renderbuffer pool\\r\\n\\t{\\r\\n\\t\\tif(!gl._renderbuffers_pool)\\r\\n\\t\\t\\tgl._renderbuffers_pool = {};\\r\\n\\t\\t//generate unique key for this renderbuffer\\r\\n\\t\\tvar key = this.width + \\\":\\\" + this.height;\\r\\n\\r\\n\\t\\t//reuse or create new one\\r\\n\\t\\tif( gl._renderbuffers_pool[ key ] ) //Reuse old\\r\\n\\t\\t{\\r\\n\\t\\t\\trenderbuffer = gl._renderbuffers_pool[ key ];\\r\\n\\t\\t\\trenderbuffer.time = now;\\r\\n\\t\\t\\tgl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\t//create temporary buffer\\r\\n\\t\\t\\tgl._renderbuffers_pool[ key ] = renderbuffer = gl.createRenderbuffer();\\r\\n\\t\\t\\trenderbuffer.time = now;\\r\\n\\t\\t\\trenderbuffer.width = this.width;\\r\\n\\t\\t\\trenderbuffer.height = this.height;\\r\\n\\t\\t\\tgl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer );\\r\\n\\r\\n\\t\\t\\t//destroy after one minute \\r\\n\\t\\t\\tsetTimeout( inner_check_destroy.bind(renderbuffer), 1000*60 );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\trenderbuffer = gl._renderbuffer = gl._renderbuffer || gl.createRenderbuffer();\\r\\n\\t\\trenderbuffer.width = this.width;\\r\\n\\t\\trenderbuffer.height = this.height;\\r\\n\\t\\tgl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer );\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\t//bind render buffer for depth or color\\r\\n\\tif( this.format === gl.DEPTH_COMPONENT )\\r\\n\\t\\tgl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, this.width, this.height);\\r\\n\\telse\\r\\n\\t\\tgl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);\\r\\n\\r\\n\\r\\n\\t//clears memory from unused buffer\\r\\n\\tfunction inner_check_destroy()\\r\\n\\t{\\r\\n\\t\\tif( GL.getTime() - this.time >= 1000*60 )\\r\\n\\t\\t{\\r\\n\\t\\t\\t//console.log(\\\"Buffer cleared\\\");\\r\\n\\t\\t\\tgl.deleteRenderbuffer( gl._renderbuffers_pool[ key ] );\\r\\n\\t\\t\\tdelete gl._renderbuffers_pool[ key ];\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tsetTimeout( inner_check_destroy.bind(this), 1000*60 );\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\t//create to store depth\\r\\n\\t/*\\r\\n\\tif (this.width != renderbuffer.width || this.height != renderbuffer.height ) {\\r\\n\\t renderbuffer.width = this.width;\\r\\n\\t renderbuffer.height = this.height;\\r\\n\\t gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\tgl.viewport(0, 0, this.width, this.height);\\r\\n\\r\\n\\t//if(gl._current_texture_drawto)\\r\\n\\t//\\tthrow(\\\"Texture.drawTo: Cannot use drawTo from inside another drawTo\\\");\\r\\n\\r\\n\\tgl._current_texture_drawto = this;\\r\\n\\tgl._current_fbo_color = framebuffer;\\r\\n\\tgl._current_fbo_depth = renderbuffer;\\r\\n\\r\\n\\tif(this.texture_type == gl.TEXTURE_2D)\\r\\n\\t{\\r\\n\\t\\tif( this.format !== gl.DEPTH_COMPONENT )\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.handler, 0 );\\r\\n\\t\\t\\tgl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer );\\r\\n\\t\\t\\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.handler, 0);\\r\\n\\t\\t}\\r\\n\\t\\tcallback(this, params);\\r\\n\\t}\\r\\n\\telse if(this.texture_type == gl.TEXTURE_CUBE_MAP)\\r\\n\\t{\\r\\n\\t\\t//bind the fixed ones out of the loop to save calls\\r\\n\\t\\tif( this.format !== gl.DEPTH_COMPONENT )\\r\\n\\t\\t\\tgl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer );\\r\\n\\r\\n\\t\\t//for every face of the cubemap\\r\\n\\t\\tfor(var i = 0; i < 6; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.format !== gl.DEPTH_COMPONENT )\\r\\n\\t\\t\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, this.handler, 0);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, this.handler, 0 );\\r\\n\\t\\t\\tcallback(this,i, params);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tthis.data = null;\\r\\n\\r\\n\\tgl._current_texture_drawto = null;\\r\\n\\tgl._current_fbo_color = null;\\r\\n\\tgl._current_fbo_depth = null;\\r\\n\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, old_fbo );\\r\\n\\tgl.bindRenderbuffer(gl.RENDERBUFFER, null);\\r\\n\\tgl.viewport(v[0], v[1], v[2], v[3]);\\r\\n\\r\\n\\treturn this;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Static version of drawTo meant to be used with several buffers\\r\\n* @method drawToColorAndDepth\\r\\n* @param {Texture} color_texture\\r\\n* @param {Texture} depth_texture\\r\\n* @param {Function} callback\\r\\n*/\\r\\nTexture.drawTo = function( color_textures, callback, depth_texture )\\r\\n{\\r\\n\\tvar w = -1,\\r\\n\\t\\th = -1,\\r\\n\\t\\ttype = null;\\r\\n\\r\\n\\tif(!color_textures && !depth_texture)\\r\\n\\t\\tthrow(\\\"Textures missing in drawTo\\\");\\r\\n\\r\\n\\tif(color_textures && color_textures.length)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < color_textures.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar t = color_textures[i];\\r\\n\\t\\t\\tif(w == -1) \\r\\n\\t\\t\\t\\tw = t.width;\\r\\n\\t\\t\\telse if(w != t.width)\\r\\n\\t\\t\\t\\tthrow(\\\"Cannot use Texture.drawTo if textures have different dimensions\\\");\\r\\n\\t\\t\\tif(h == -1) \\r\\n\\t\\t\\t\\th = t.height;\\r\\n\\t\\t\\telse if(h != t.height)\\r\\n\\t\\t\\t\\tthrow(\\\"Cannot use Texture.drawTo if textures have different dimensions\\\");\\r\\n\\t\\t\\tif(type == null) //first one defines the type\\r\\n\\t\\t\\t\\ttype = t.type;\\r\\n\\t\\t\\telse if (type != t.type)\\r\\n\\t\\t\\t\\tthrow(\\\"Cannot use Texture.drawTo if textures have different data type, all must have the same type\\\");\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tw = depth_texture.width;\\r\\n\\t\\th = depth_texture.height;\\r\\n\\t}\\r\\n\\r\\n\\tvar ext = gl.extensions[\\\"WEBGL_draw_buffers\\\"];\\r\\n\\tif(!ext && color_textures && color_textures.length > 1)\\r\\n\\t\\tthrow(\\\"Rendering to several textures not supported\\\");\\r\\n\\r\\n\\tvar v = gl.getViewport();\\r\\n\\tgl._framebuffer = gl._framebuffer || gl.createFramebuffer();\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, gl._framebuffer );\\r\\n\\r\\n\\tgl.viewport( 0, 0, w, h );\\r\\n\\r\\n\\tvar renderbuffer = null;\\r\\n\\tif( depth_texture && depth_texture.format !== gl.DEPTH_COMPONENT || depth_texture.type != gl.UNSIGNED_INT )\\r\\n\\t\\tthrow(\\\"Depth texture must be of format: gl.DEPTH_COMPONENT and type: gl.UNSIGNED_INT\\\");\\r\\n\\r\\n\\tif( depth_texture )\\r\\n\\t{\\r\\n\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0);\\r\\n\\t}\\r\\n\\telse //create a temporary depth renderbuffer\\r\\n\\t{\\r\\n\\t\\t//create renderbuffer for depth\\r\\n\\t\\trenderbuffer = gl._renderbuffer = gl._renderbuffer || gl.createRenderbuffer();\\r\\n\\t\\trenderbuffer.width = w;\\r\\n\\t\\trenderbuffer.height = h;\\r\\n\\t\\tgl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer );\\r\\n\\t\\tgl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h);\\r\\n\\r\\n\\t\\tgl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer );\\r\\n\\t}\\r\\n\\r\\n\\tif( color_textures )\\r\\n\\t{\\r\\n\\t\\tvar order = []; //draw_buffers request the use of an array with the order of the attachments\\r\\n\\t\\tfor(var i = 0; i < color_textures.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar t = color_textures[i];\\r\\n\\t\\t\\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, t.handler, 0);\\r\\n\\t\\t\\torder.push( gl.COLOR_ATTACHMENT0 + i );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(color_textures.length > 1)\\r\\n\\t\\t\\text.drawBuffersWEBGL( order );\\r\\n\\t}\\r\\n\\telse //create temporary color render buffer\\r\\n\\t{\\r\\n\\t\\tvar color_renderbuffer = this._color_renderbuffer = this._color_renderbuffer || gl.createRenderbuffer();\\r\\n\\t\\tcolor_renderbuffer.width = w;\\r\\n\\t\\tcolor_renderbuffer.height = h;\\r\\n\\r\\n\\t\\tgl.bindRenderbuffer( gl.RENDERBUFFER, color_renderbuffer );\\r\\n\\t\\tgl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, w, h );\\r\\n\\r\\n\\t\\tgl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color_renderbuffer );\\r\\n\\t}\\r\\n\\r\\n\\tvar complete = gl.checkFramebufferStatus( gl.FRAMEBUFFER );\\r\\n\\tif(complete !== gl.FRAMEBUFFER_COMPLETE)\\r\\n\\t\\tthrow(\\\"FBO not complete: \\\" + complete);\\r\\n\\r\\n\\tcallback();\\r\\n\\r\\n\\t//clear data\\r\\n\\tif(color_textures.length)\\r\\n\\t\\tfor(var i = 0; i < color_textures.length; ++i)\\r\\n\\t\\t\\tcolor_textures[i].data = null;\\r\\n\\r\\n\\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\\r\\n\\r\\n\\tgl.viewport(v[0], v[1], v[2], v[3]);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Similar to drawTo but it also stores the depth in a depth texture\\r\\n* @method drawToColorAndDepth\\r\\n* @param {Texture} color_texture\\r\\n* @param {Texture} depth_texture\\r\\n* @param {Function} callback\\r\\n*/\\r\\nTexture.drawToColorAndDepth = function( color_texture, depth_texture, callback ) {\\r\\n\\tvar gl = color_texture.gl; //static function\\r\\n\\r\\n\\tif(depth_texture.width != color_texture.width || depth_texture.height != color_texture.height)\\r\\n\\t\\tthrow(\\\"Different size between color texture and depth texture\\\");\\r\\n\\r\\n\\tvar v = gl.getViewport();\\r\\n\\r\\n\\tgl._framebuffer = gl._framebuffer || gl.createFramebuffer();\\r\\n\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, gl._framebuffer);\\r\\n\\r\\n\\tgl.viewport(0, 0, color_texture.width, color_texture.height);\\r\\n\\r\\n\\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, color_texture.handler, 0);\\r\\n\\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0);\\r\\n\\r\\n\\tcallback();\\r\\n\\r\\n\\tcolor_texture.data = null;\\r\\n\\tdepth_texture.data = null;\\r\\n\\r\\n\\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\\r\\n\\r\\n\\tgl.viewport(v[0], v[1], v[2], v[3]);\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Copy content of one texture into another\\r\\n* TODO: check using copyTexImage2D\\r\\n* @method copyTo\\r\\n* @param {GL.Texture} target_texture\\r\\n* @param {GL.Shader} [shader=null] optional shader to apply while copying\\r\\n* @param {Object} [uniforms=null] optional uniforms for the shader\\r\\n*/\\r\\nTexture.prototype.copyTo = function( target_texture, shader, uniforms ) {\\r\\n\\tvar that = this;\\r\\n\\tvar gl = this.gl;\\r\\n\\r\\n\\t//save state\\r\\n\\tvar previous_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING );\\r\\n\\tvar viewport = gl.getViewport(); \\r\\n\\r\\n\\tif(!shader)\\r\\n\\t\\tshader = this.texture_type == gl.TEXTURE_2D ? GL.Shader.getScreenShader() : GL.Shader.getCubemapCopyShader();\\r\\n\\r\\n\\t//render\\r\\n\\tgl.disable( gl.BLEND );\\r\\n\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\tif(shader && uniforms)\\r\\n\\t\\tshader.uniforms( uniforms );\\r\\n\\r\\n\\t//reuse fbo\\r\\n\\tvar fbo = gl.__copy_fbo;\\r\\n\\tif(!fbo)\\r\\n\\t\\tfbo = gl.__copy_fbo = gl.createFramebuffer();\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, fbo );\\r\\n\\r\\n\\tgl.viewport(0,0,target_texture.width, target_texture.height);\\r\\n\\tif(this.texture_type == gl.TEXTURE_2D)\\r\\n\\t{\\r\\n\\t\\tif(this.format !== gl.DEPTH_COMPONENT && this.format !== gl.DEPTH_STENCIL )\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target_texture.handler, 0);\\r\\n\\t\\t\\tthis.toViewport( shader );\\r\\n\\t\\t}\\r\\n\\t\\telse //copying a depth texture is harder\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar color_renderbuffer = gl._color_renderbuffer = gl._color_renderbuffer || gl.createRenderbuffer();\\r\\n\\t\\t\\tvar w = color_renderbuffer.width = target_texture.width;\\r\\n\\t\\t\\tvar h = color_renderbuffer.height = target_texture.height;\\r\\n\\t\\t\\t\\r\\n\\t\\t\\t//attach color render buffer\\r\\n\\t\\t\\tgl.bindRenderbuffer( gl.RENDERBUFFER, color_renderbuffer );\\r\\n\\t\\t\\tgl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, w, h );\\r\\n\\t\\t\\tgl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color_renderbuffer );\\r\\n\\r\\n\\t\\t\\t//attach depth texture\\r\\n\\t\\t\\tvar attachment_point = target_texture.format == gl.DEPTH_STENCIL ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT;\\r\\n\\t\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, attachment_point, gl.TEXTURE_2D, target_texture.handler, 0);\\r\\n\\r\\n\\t\\t\\tvar complete = gl.checkFramebufferStatus( gl.FRAMEBUFFER );\\r\\n\\t\\t\\tif(complete !== gl.FRAMEBUFFER_COMPLETE)\\r\\n\\t\\t\\t\\tthrow(\\\"FBO not complete: \\\" + complete);\\r\\n\\r\\n\\t\\t\\t//enable depth test?\\r\\n\\t\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\t\\tgl.depthFunc( gl.ALWAYS );\\r\\n\\t\\t\\tgl.colorMask( false,false,false,false );\\r\\n\\t\\t\\t//call shader that overwrites depth values\\r\\n\\t\\t\\tshader = GL.Shader.getCopyDepthShader();\\r\\n\\t\\t\\tthis.toViewport( shader );\\r\\n\\t\\t\\tgl.colorMask( true,true,true,true );\\r\\n\\t\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\t\\tgl.depthFunc( gl.LEQUAL );\\r\\n\\t\\t\\tgl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null );\\r\\n\\t\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, attachment_point, gl.TEXTURE_2D, null, 0);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(this.texture_type == gl.TEXTURE_CUBE_MAP)\\r\\n\\t{\\r\\n\\t\\tshader.uniforms({u_texture: 0});\\r\\n\\t\\tvar rot_matrix = GL.temp_mat3;\\r\\n\\t\\tfor(var i = 0; i < 6; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, target_texture.handler, 0);\\r\\n\\t\\t\\tvar face_info = GL.Texture.cubemap_camera_parameters[ i ];\\r\\n\\t\\t\\tmat3.identity( rot_matrix );\\r\\n\\t\\t\\trot_matrix.set( face_info.right, 0 );\\r\\n\\t\\t\\trot_matrix.set( face_info.up, 3 );\\r\\n\\t\\t\\trot_matrix.set( face_info.dir, 6 );\\r\\n\\t\\t\\t//mat3.invert(rot_matrix,rot_matrix);\\r\\n\\t\\t\\tthis.toViewport( shader,{ u_rotation: rot_matrix });\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\t\\r\\n\\t//restore previous state\\r\\n\\tgl.setViewport(viewport); //restore viewport\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, previous_fbo ); //restore fbo\\r\\n\\r\\n\\t//generate mipmaps when needed\\r\\n\\tif (target_texture.minFilter && target_texture.minFilter != gl.NEAREST && target_texture.minFilter != gl.LINEAR) {\\r\\n\\t\\ttarget_texture.bind();\\r\\n\\t\\tgl.generateMipmap(target_texture.texture_type);\\r\\n\\t\\ttarget_texture.has_mipmaps = true;\\r\\n\\t}\\r\\n\\r\\n\\ttarget_texture.data = null;\\r\\n\\tgl.bindTexture( target_texture.texture_type, null ); //disable\\r\\n\\treturn this;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Similar to CopyTo, but more specific, only for color texture_2D. It doesnt change the blend flag\\r\\n* @method blit\\r\\n* @param {GL.Texture} target_texture\\r\\n* @param {GL.Shader} [shader=null] optional shader to apply while copying\\r\\n* @param {Object} [uniforms=null] optional uniforms for the shader\\r\\n*/\\r\\nTexture.prototype.blit = (function(){ \\r\\n\\tvar viewport = new Float32Array(4);\\t\\r\\n\\t\\r\\n\\treturn function( target_texture, shader, uniforms ) {\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tvar gl = this.gl;\\r\\n\\r\\n\\t\\tif ( this.texture_type != gl.TEXTURE_2D || this.format === gl.DEPTH_COMPONENT || this.format === gl.DEPTH_STENCIL )\\r\\n\\t\\t\\tthrow(\\\"blit only support TEXTURE_2D of RGB or RGBA. use copyTo instead\\\");\\r\\n\\r\\n\\t\\t//save state\\r\\n\\t\\tvar previous_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING );\\r\\n\\t\\tviewport.set( gl.viewport_data ); \\r\\n\\r\\n\\t\\tshader = shader || GL.Shader.getScreenShader();\\r\\n\\t\\tif(shader && uniforms)\\r\\n\\t\\t\\tshader.uniforms( uniforms );\\r\\n\\r\\n\\t\\t//reuse fbo\\r\\n\\t\\tvar fbo = gl.__copy_fbo;\\r\\n\\t\\tif(!fbo)\\r\\n\\t\\t\\tfbo = gl.__copy_fbo = gl.createFramebuffer();\\r\\n\\t\\tgl.bindFramebuffer( gl.FRAMEBUFFER, fbo );\\r\\n\\r\\n\\t\\tgl.viewport(0,0,target_texture.width, target_texture.height);\\r\\n\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target_texture.handler, 0);\\r\\n\\r\\n\\t\\tthis.bind(0);\\r\\n\\t\\tshader.draw( GL.Mesh.getScreenQuad(), gl.TRIANGLES );\\r\\n\\t\\t\\r\\n\\t\\t//restore previous state\\r\\n\\t\\tgl.setViewport(viewport); //restore viewport\\r\\n\\t\\tgl.bindFramebuffer( gl.FRAMEBUFFER, previous_fbo ); //restore fbo\\r\\n\\r\\n\\t\\ttarget_texture.data = null;\\r\\n\\t\\tgl.bindTexture( target_texture.texture_type, null ); //disable\\r\\n\\t\\treturn this;\\r\\n\\t}\\r\\n})();\\r\\n\\r\\n/**\\r\\n* Render texture in a quad to full viewport size\\r\\n* @method toViewport\\r\\n* @param {Shader} shader to apply, otherwise a default textured shader is applied [optional]\\r\\n* @param {Object} uniforms for the shader if needed [optional]\\r\\n*/\\r\\nTexture.prototype.toViewport = function(shader, uniforms)\\r\\n{\\r\\n\\tshader = shader || Shader.getScreenShader();\\r\\n\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\tthis.bind(0);\\r\\n\\t//shader.uniforms({u_texture: 0}); //never changes\\r\\n\\tif(uniforms)\\r\\n\\t\\tshader.uniforms(uniforms);\\r\\n\\tshader.draw( mesh, gl.TRIANGLES );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Fills the texture with a constant color (uses gl.clear)\\r\\n* @method fill\\r\\n* @param {vec4} color rgba\\r\\n* @param {boolean} skip_mipmaps if true the mipmaps wont be updated\\r\\n*/\\r\\nTexture.prototype.fill = function(color, skip_mipmaps )\\r\\n{\\r\\n\\tvar old_color = gl.getParameter( gl.COLOR_CLEAR_VALUE );\\r\\n\\tgl.clearColor( color[0], color[1], color[2], color[3] );\\r\\n\\tthis.drawTo( function() {\\r\\n\\t\\tgl.clear( gl.COLOR_BUFFER_BIT );\\t\\r\\n\\t});\\r\\n\\tgl.clearColor( old_color[0], old_color[1], old_color[2], old_color[3] );\\r\\n\\r\\n\\tif (!skip_mipmaps && this.minFilter && this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR ) {\\r\\n\\t\\tthis.bind();\\r\\n\\t\\tgl.generateMipmap( this.texture_type );\\r\\n\\t\\tthis.has_mipmaps = true;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Render texture in a quad of specified area\\r\\n* @method renderQuad\\r\\n* @param {number} x\\r\\n* @param {number} y\\r\\n* @param {number} width\\r\\n* @param {number} height\\r\\n*/\\r\\nTexture.prototype.renderQuad = (function() {\\r\\n\\t//static variables: less garbage\\r\\n\\tvar identity = mat3.create();\\r\\n\\tvar pos = vec2.create();\\r\\n\\tvar size = vec2.create();\\r\\n\\tvar white = vec4.fromValues(1,1,1,1);\\r\\n\\r\\n\\treturn (function(x,y,w,h, shader, uniforms)\\r\\n\\t{\\r\\n\\t\\tpos[0] = x;\\tpos[1] = y;\\r\\n\\t\\tsize[0] = w; size[1] = h;\\r\\n\\r\\n\\t\\tshader = shader || Shader.getQuadShader(this.gl);\\r\\n\\t\\tvar mesh = Mesh.getScreenQuad(this.gl);\\r\\n\\t\\tthis.bind(0);\\r\\n\\t\\tshader.uniforms({u_texture: 0, u_position: pos, u_color: white, u_size: size, u_viewport: gl.viewport_data.subarray(2,4), u_transform: identity });\\r\\n\\t\\tif(uniforms)\\r\\n\\t\\t\\tshader.uniforms(uniforms);\\r\\n\\t\\tshader.draw( mesh, gl.TRIANGLES );\\r\\n\\t});\\r\\n})();\\r\\n\\r\\n\\r\\n/**\\r\\n* Applies a blur filter of 5x5 pixels to the texture (be careful using it, it is slow)\\r\\n* @method applyBlur\\r\\n* @param {Number} offsetx scalar that multiplies the offset when fetching pixels horizontally (default 1)\\r\\n* @param {Number} offsety scalar that multiplies the offset when fetching pixels vertically (default 1)\\r\\n* @param {Number} intensity scalar that multiplies the result (default 1)\\r\\n* @param {Texture} output_texture [optional] if not passed the output is the own texture\\r\\n* @param {Texture} temp_texture blur needs a temp texture, if not supplied it will use the temporary textures pool\\r\\n* @return {Texture} returns the temp_texture in case you want to reuse it\\r\\n*/\\r\\nTexture.prototype.applyBlur = function( offsetx, offsety, intensity, output_texture, temp_texture )\\r\\n{\\r\\n\\tvar that = this;\\r\\n\\tvar gl = this.gl;\\r\\n\\tif(offsetx === undefined)\\r\\n\\t\\toffsetx = 1;\\r\\n\\tif(offsety === undefined)\\r\\n\\t\\toffsety = 1;\\r\\n\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\tgl.disable( gl.BLEND );\\r\\n\\toutput_texture = output_texture || this;\\r\\n\\tvar is_temp = !temp_texture;\\r\\n\\r\\n\\t//if(this === output_texture && this.texture_type === gl.TEXTURE_CUBE_MAP )\\r\\n\\t//\\tthrow(\\\"cannot use applyBlur in a texture with itself when blurring a CUBE_MAP\\\");\\r\\n\\tif(temp_texture === output_texture)\\r\\n\\t\\tthrow(\\\"cannot use applyBlur in a texture using as temporary itself\\\");\\r\\n\\r\\n\\tif(output_texture && this.texture_type !== output_texture.texture_type )\\r\\n\\t\\tthrow(\\\"cannot use applyBlur with textures of different texture_type\\\");\\r\\n\\r\\n\\t//if(this.width != output_texture.width || this.height != output_texture.height)\\r\\n\\t//\\tthrow(\\\"cannot use applyBlur with an output texture of different size, it doesnt work\\\");\\r\\n\\r\\n\\t//save state\\r\\n\\tvar current_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING );\\r\\n\\tvar viewport = gl.getViewport(); \\r\\n\\r\\n\\t//reuse fbo\\r\\n\\tvar fbo = gl.__copy_fbo;\\r\\n\\tif(!fbo)\\r\\n\\t\\tfbo = gl.__copy_fbo = gl.createFramebuffer();\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, fbo );\\r\\n\\tgl.viewport(0,0, this.width, this.height);\\r\\n\\r\\n\\tif( this.texture_type === gl.TEXTURE_2D )\\r\\n\\t{\\r\\n\\t\\tvar shader = GL.Shader.getBlurShader();\\r\\n\\r\\n\\t\\tif(!temp_texture)\\r\\n\\t\\t\\ttemp_texture = GL.Texture.getTemporary( this.width, this.height, this );\\r\\n\\r\\n\\t\\t//horizontal blur\\r\\n\\t\\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, temp_texture.handler, 0);\\r\\n\\t\\tthis.toViewport( shader, {u_texture: 0, u_intensity: intensity, u_offset: [0, offsety / this.height ] });\\r\\n\\r\\n\\t\\t//vertical blur\\r\\n\\t\\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, output_texture.handler, 0);\\r\\n\\t\\tgl.viewport(0,0,output_texture.width, output_texture.height);\\r\\n\\t\\ttemp_texture.toViewport( shader, {u_intensity: intensity, u_offset: [offsetx / temp_texture.width, 0] });\\r\\n\\r\\n\\t\\tif(is_temp)\\r\\n\\t\\t\\tGL.Texture.releaseTemporary( temp_texture );\\r\\n\\t}\\r\\n\\telse if( this.texture_type === gl.TEXTURE_CUBE_MAP )\\r\\n\\t{\\r\\n\\t\\t//var weights = new Float32Array([ 0.16/0.98, 0.15/0.98, 0.12/0.98, 0.09/0.98, 0.05/0.98 ]);\\r\\n\\t\\t//var weights = new Float32Array([ 0.05/0.98, 0.09/0.98, 0.12/0.98, 0.15/0.98, 0.16/0.98, 0.15/0.98, 0.12/0.98, 0.09/0.98, 0.05/0.98, 0.0 ]); //extra 0 to avoid mat3\\r\\n\\t\\tvar shader = GL.Shader.getCubemapBlurShader();\\r\\n\\t\\tshader.uniforms({u_texture: 0, u_intensity: intensity, u_offset: [ offsetx / this.width, offsety / this.height ] });\\r\\n\\t\\tthis.bind(0);\\r\\n\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\tmesh.bindBuffers( shader );\\r\\n\\t\\tshader.bind();\\r\\n\\r\\n\\t\\tvar destination = null;\\r\\n\\t\\t\\r\\n\\t\\tif(!temp_texture && output_texture == this) //we need a temporary texture\\r\\n\\t\\t\\tdestination = temp_texture = GL.Texture.getTemporary( output_texture.width, output_texture.height, output_texture );\\r\\n\\t\\telse\\r\\n\\t\\t\\tdestination = output_texture; //blur directly to output texture\\r\\n\\r\\n\\t\\tvar rot_matrix = GL.temp_mat3;\\r\\n\\t\\tfor(var i = 0; i < 6; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, destination.handler, 0);\\r\\n\\t\\t\\tvar face_info = GL.Texture.cubemap_camera_parameters[ i ];\\r\\n\\t\\t\\tmat3.identity(rot_matrix);\\r\\n\\t\\t\\trot_matrix.set( face_info.right, 0 );\\r\\n\\t\\t\\trot_matrix.set( face_info.up, 3 );\\r\\n\\t\\t\\trot_matrix.set( face_info.dir, 6 );\\r\\n\\t\\t\\t//mat3.invert(rot_matrix,rot_matrix);\\r\\n\\t\\t\\tshader._setUniform( \\\"u_rotation\\\", rot_matrix );\\r\\n\\t\\t\\tgl.drawArrays( gl.TRIANGLES, 0, 6 );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tmesh.unbindBuffers( shader );\\r\\n\\r\\n\\t\\tif(temp_texture) //copy back \\r\\n\\t\\t\\ttemp_texture.copyTo( output_texture );\\r\\n\\r\\n\\t\\tif(temp_texture && is_temp) //release temp\\r\\n\\t\\t\\tGL.Texture.releaseTemporary( temp_texture );\\r\\n\\t}\\r\\n\\r\\n\\t//restore previous state\\r\\n\\tgl.setViewport(viewport); //restore viewport\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, current_fbo ); //restore fbo\\r\\n\\r\\n\\toutput_texture.data = null;\\r\\n\\r\\n\\t//generate mipmaps when needed\\r\\n\\tif (output_texture.minFilter && output_texture.minFilter != gl.NEAREST && output_texture.minFilter != gl.LINEAR) {\\r\\n\\t\\toutput_texture.bind();\\r\\n\\t\\tgl.generateMipmap(output_texture.texture_type);\\r\\n\\t\\toutput_texture.has_mipmaps = true;\\r\\n\\t}\\r\\n\\r\\n\\tgl.bindTexture( output_texture.texture_type, null ); //disable\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Loads and uploads a texture from a url\\r\\n* @method Texture.fromURL\\r\\n* @param {String} url\\r\\n* @param {Object} options\\r\\n* @param {Function} on_complete\\r\\n* @return {Texture} the texture\\r\\n*/\\r\\nTexture.fromURL = function( url, options, on_complete, gl ) {\\r\\n\\tgl = gl || global.gl;\\r\\n\\r\\n\\toptions = options || {};\\r\\n\\toptions = Object.create(options); //creates a new options using the old one as prototype\\r\\n\\r\\n\\tvar texture = options.texture || new GL.Texture(1, 1, options, gl);\\r\\n\\r\\n\\tif(url.length < 64)\\r\\n\\t\\ttexture.url = url;\\r\\n\\ttexture.bind();\\r\\n\\tvar default_color = options.temp_color || Texture.loading_color;\\r\\n\\t//Texture.setUploadOptions(options);\\r\\n\\tgl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);\\r\\n\\tvar temp_color = options.type == gl.FLOAT ? new Float32Array(default_color) : new Uint8Array(default_color);\\r\\n\\tgl.texImage2D( gl.TEXTURE_2D, 0, texture.format, texture.width, texture.height, 0, texture.format, texture.type, temp_color );\\r\\n\\tgl.bindTexture( texture.texture_type, null ); //disable\\r\\n\\ttexture.ready = false;\\r\\n\\r\\n\\tvar ext = null;\\r\\n\\tif( options.extension ) //to force format\\r\\n\\t\\text = options.extension;\\r\\n\\r\\n\\tif(!ext && url.length < 512) //avoid base64 urls\\r\\n\\t{\\r\\n\\t\\tvar base = url;\\r\\n\\t\\tvar pos = url.indexOf(\\\"?\\\");\\r\\n\\t\\tif(pos != -1)\\r\\n\\t\\t\\tbase = url.substr(0,pos);\\r\\n\\t\\tpos = base.lastIndexOf(\\\".\\\");\\r\\n\\t\\tif(pos != -1)\\r\\n\\t\\t\\text = base.substr(pos+1).toLowerCase();\\r\\n\\t}\\r\\n\\r\\n\\tif( ext == \\\"dds\\\")\\r\\n\\t{\\r\\n\\t\\tvar ext = gl.getExtension(\\\"WEBKIT_WEBGL_compressed_texture_s3tc\\\") || gl.getExtension(\\\"WEBGL_compressed_texture_s3tc\\\");\\r\\n\\t\\tvar new_texture = new GL.Texture(0,0, options, gl);\\r\\n\\t\\tDDS.loadDDSTextureEx(gl, ext, url, new_texture.handler, true, function(t) {\\r\\n\\t\\t\\ttexture.texture_type = t.texture_type;\\r\\n\\t\\t\\ttexture.handler = t;\\r\\n\\t\\t\\tdelete texture[\\\"ready\\\"]; //texture.ready = true;\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\ton_complete(texture, url);\\r\\n\\t\\t});\\r\\n\\t}\\r\\n\\telse if( ext == \\\"tga\\\" )\\r\\n\\t{\\r\\n\\t\\tHttpRequest( url, null, function(data) {\\r\\n\\t\\t\\tvar img_data = GL.Texture.parseTGA(data);\\r\\n\\t\\t\\tif(!img_data)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\toptions.texture = texture;\\r\\n\\t\\t\\tif(img_data.format == \\\"RGB\\\")\\r\\n\\t\\t\\t\\ttexture.format = gl.RGB;\\r\\n\\t\\t\\ttexture = GL.Texture.fromMemory( img_data.width, img_data.height, img_data.pixels, options );\\r\\n\\t\\t\\tdelete texture[\\\"ready\\\"]; //texture.ready = true;\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\ton_complete( texture, url );\\r\\n\\t\\t},null,{ binary: true });\\r\\n\\t}\\r\\n\\telse //png,jpg,webp,...\\r\\n\\t{\\r\\n\\t\\tvar image = new Image();\\r\\n\\t\\timage.src = url;\\r\\n\\t\\tvar that = this;\\r\\n\\t\\timage.onload = function()\\r\\n\\t\\t{\\r\\n\\t\\t\\toptions.texture = texture;\\r\\n\\t\\t\\tGL.Texture.fromImage(this, options);\\r\\n\\t\\t\\tdelete texture[\\\"ready\\\"]; //texture.ready = true;\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\ton_complete(texture, url);\\r\\n\\t\\t}\\r\\n\\t\\timage.onerror = function()\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\ton_complete(null);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn texture;\\r\\n};\\r\\n\\r\\nTexture.parseTGA = function(data)\\r\\n{\\r\\n\\tif(!data || data.constructor !== ArrayBuffer)\\r\\n\\t\\tthrow( \\\"TGA: data must be ArrayBuffer\\\");\\r\\n\\tdata = new Uint8Array(data);\\r\\n\\tvar TGAheader = new Uint8Array( [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0] );\\r\\n\\tvar TGAcompare = data.subarray(0,12);\\r\\n\\tfor(var i = 0; i < TGAcompare.length; i++)\\r\\n\\t\\tif(TGAheader[i] != TGAcompare[i])\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"TGA header is not valid\\\");\\r\\n\\t\\t\\treturn null; //not a TGA\\r\\n\\t\\t}\\r\\n\\r\\n\\tvar header = data.subarray(12,18);\\r\\n\\tvar img = {};\\r\\n\\timg.width = header[1] * 256 + header[0];\\r\\n\\timg.height = header[3] * 256 + header[2];\\r\\n\\timg.bpp = header[4];\\r\\n\\timg.bytesPerPixel = img.bpp / 8;\\r\\n\\timg.imageSize = img.width * img.height * img.bytesPerPixel;\\r\\n\\timg.pixels = data.subarray(18,18+img.imageSize);\\r\\n\\timg.pixels = new Uint8Array( img.pixels ); \\t//clone\\r\\n\\tif(\\t(header[5] & (1<<4)) == 0) //hack, needs swap\\r\\n\\t{\\r\\n\\t\\t//TGA comes in BGR format so we swap it, this is slooooow\\r\\n\\t\\tfor(var i = 0; i < img.imageSize; i+= img.bytesPerPixel)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar temp = img.pixels[i];\\r\\n\\t\\t\\timg.pixels[i] = img.pixels[i+2];\\r\\n\\t\\t\\timg.pixels[i+2] = temp;\\r\\n\\t\\t}\\r\\n\\t\\theader[5] |= 1<<4; //mark as swaped\\r\\n\\t\\timg.format = img.bpp == 32 ? \\\"RGBA\\\" : \\\"RGB\\\";\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\timg.format = img.bpp == 32 ? \\\"RGBA\\\" : \\\"RGB\\\";\\r\\n\\t//some extra bytes to avoid alignment problems\\r\\n\\t//img.pixels = new Uint8Array( img.imageSize + 14);\\r\\n\\t//img.pixels.set( data.subarray(18,18+img.imageSize), 0);\\r\\n\\timg.flipY = true;\\r\\n\\t//img.format = img.bpp == 32 ? \\\"BGRA\\\" : \\\"BGR\\\";\\r\\n\\t//trace(\\\"TGA info: \\\" + img.width + \\\"x\\\" + img.height );\\r\\n\\treturn img;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Create a texture from an Image\\r\\n* @method Texture.fromImage\\r\\n* @param {Image} image\\r\\n* @param {Object} options\\r\\n* @return {Texture} the texture\\r\\n*/\\r\\nTexture.fromImage = function( image, options ) {\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\tvar texture = options.texture || new GL.Texture( image.width, image.height, options);\\r\\n\\ttexture.uploadImage( image, options );\\r\\n\\r\\n\\ttexture.bind();\\r\\n\\tgl.texParameteri(texture.texture_type, gl.TEXTURE_MAG_FILTER, texture.magFilter );\\r\\n\\tgl.texParameteri(texture.texture_type, gl.TEXTURE_MIN_FILTER, texture.minFilter );\\r\\n\\tgl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_S, texture.wrapS );\\r\\n\\tgl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_T, texture.wrapT );\\r\\n\\r\\n\\tif (GL.isPowerOfTwo(texture.width) && GL.isPowerOfTwo(texture.height) )\\r\\n\\t{\\r\\n\\t\\tif( options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR)\\r\\n\\t\\t{\\r\\n\\t\\t\\ttexture.bind();\\r\\n\\t\\t\\tgl.generateMipmap(texture.texture_type);\\r\\n\\t\\t\\ttexture.has_mipmaps = true;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\t//no mipmaps supported\\r\\n\\t\\tgl.texParameteri(texture.texture_type, gl.TEXTURE_MIN_FILTER, GL.LINEAR );\\r\\n\\t\\tgl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE );\\r\\n\\t\\tgl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE );\\r\\n\\t\\ttexture.has_mipmaps = false;\\r\\n\\t}\\r\\n\\tgl.bindTexture(texture.texture_type, null); //disable\\r\\n\\ttexture.data = image;\\r\\n\\tif(options.keep_image)\\r\\n\\t\\ttexture.img = image;\\r\\n\\treturn texture;\\r\\n};\\r\\n\\r\\n/**\\r\\n* Create a texture from a Video\\r\\n* @method Texture.fromVideo\\r\\n* @param {Video} video\\r\\n* @param {Object} options\\r\\n* @return {Texture} the texture\\r\\n*/\\r\\nTexture.fromVideo = function(video, options) {\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\tvar texture = options.texture || new GL.Texture(video.videoWidth, video.videoHeight, options);\\r\\n\\ttexture.bind();\\r\\n\\ttexture.uploadImage( video, options );\\r\\n\\tif (options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) {\\r\\n\\t\\ttexture.bind();\\r\\n\\t\\tgl.generateMipmap(texture.texture_type);\\r\\n\\t\\ttexture.has_mipmaps = true;\\r\\n\\t\\ttexture.data = video;\\r\\n\\t}\\r\\n\\tgl.bindTexture(texture.texture_type, null); //disable\\r\\n\\treturn texture;\\r\\n};\\r\\n\\r\\n/**\\r\\n* Create a clone of a texture\\r\\n* @method Texture.fromTexture\\r\\n* @param {Texture} old_texture\\r\\n* @param {Object} options\\r\\n* @return {Texture} the texture\\r\\n*/\\r\\nTexture.fromTexture = function( old_texture, options) {\\r\\n\\toptions = options || {};\\r\\n\\tvar texture = new GL.Texture( old_texture.width, old_texture.height, options );\\r\\n\\told_texture.copyTo( texture );\\r\\n\\treturn texture;\\r\\n};\\r\\n\\r\\nTexture.prototype.clone = function( options )\\r\\n{\\r\\n\\tvar old_options = this.getProperties();\\r\\n\\tif(options)\\r\\n\\t\\tfor(var i in options)\\r\\n\\t\\t\\told_options[i] = options[i];\\r\\n\\treturn Texture.fromTexture( this, old_options);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Create a texture from an ArrayBuffer containing the pixels\\r\\n* @method Texture.fromTexture\\r\\n* @param {number} width\\r\\n* @param {number} height\\r\\n* @param {ArrayBuffer} pixels\\r\\n* @param {Object} options\\r\\n* @return {Texture} the texture\\r\\n*/\\r\\nTexture.fromMemory = function( width, height, pixels, options) //format in options as format\\r\\n{\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\tvar texture = options.texture || new GL.Texture(width, height, options);\\r\\n\\tTexture.setUploadOptions(options);\\r\\n\\ttexture.bind();\\r\\n\\r\\n\\tif(pixels.constructor === Array)\\r\\n\\t{\\r\\n\\t\\tif(options.type == gl.FLOAT)\\r\\n\\t\\t\\tpixels = new Float32Array( pixels );\\r\\n\\t\\telse if(options.type == GL.HALF_FLOAT || options.type == GL.HALF_FLOAT_OES) \\r\\n\\t\\t\\tpixels = new Uint16Array( pixels ); //gl.UNSIGNED_SHORT_4_4_4_4 is only for texture that are SHORT per pixel, not per channel!\\r\\n\\t\\telse \\r\\n\\t\\t\\tpixels = new Uint8Array( pixels );\\r\\n\\t}\\r\\n\\r\\n\\tgl.texImage2D( gl.TEXTURE_2D, 0, texture.format, width, height, 0, texture.format, texture.type, pixels );\\r\\n\\ttexture.width = width;\\r\\n\\ttexture.height = height;\\r\\n\\ttexture.data = pixels;\\r\\n\\tif (options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) {\\r\\n\\t\\tgl.generateMipmap(gl.TEXTURE_2D);\\r\\n\\t\\ttexture.has_mipmaps = true;\\r\\n\\t}\\r\\n\\tgl.bindTexture(texture.texture_type, null); //disable\\r\\n\\treturn texture;\\r\\n};\\r\\n\\r\\n/**\\r\\n* Create a texture from an ArrayBuffer containing the pixels\\r\\n* @method Texture.fromDDSInMemory\\r\\n* @param {ArrayBuffer} DDS data\\r\\n* @param {Object} options\\r\\n* @return {Texture} the texture\\r\\n*/\\r\\nTexture.fromDDSInMemory = function(data, options) //format in options as format\\r\\n{\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\tvar texture = options.texture || new GL.Texture(0, 0, options);\\r\\n\\tGL.Texture.setUploadOptions(options);\\r\\n\\ttexture.bind();\\r\\n\\r\\n\\tvar ext = gl.getExtension(\\\"WEBKIT_WEBGL_compressed_texture_s3tc\\\") || gl.getExtension(\\\"WEBGL_compressed_texture_s3tc\\\");\\r\\n\\tDDS.loadDDSTextureFromMemoryEx(gl, ext, data, texture, true );\\r\\n\\r\\n\\tgl.bindTexture(texture.texture_type, null); //disable\\r\\n\\treturn texture;\\r\\n};\\r\\n\\r\\n/**\\r\\n* Create a generative texture from a shader ( must GL.Shader.getScreenShader as reference for the shader )\\r\\n* @method Texture.fromShader\\r\\n* @param {number} width\\r\\n* @param {number} height\\r\\n* @param {Shader} shader\\r\\n* @param {Object} options\\r\\n* @return {Texture} the texture\\r\\n*/\\r\\nTexture.fromShader = function(width, height, shader, options) {\\r\\n\\toptions = options || {};\\r\\n\\t\\r\\n\\tvar texture = new GL.Texture( width, height, options );\\r\\n\\t//copy content\\r\\n\\ttexture.drawTo(function() {\\r\\n\\t\\tgl.disable( gl.BLEND ); \\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.disable( gl.CULL_FACE );\\r\\n\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\tshader.draw( mesh );\\r\\n\\t});\\r\\n\\r\\n\\treturn texture;\\r\\n};\\r\\n\\r\\n/**\\r\\n* Create a cubemap texture from a set of 6 images\\r\\n* @method Texture.cubemapFromImages\\r\\n* @param {Array} images\\r\\n* @param {Object} options\\r\\n* @return {Texture} the texture\\r\\n*/\\r\\nTexture.cubemapFromImages = function(images, options) {\\r\\n\\toptions = options || {};\\r\\n\\tif(images.length != 6)\\r\\n\\t\\tthrow \\\"missing images to create cubemap\\\";\\r\\n\\r\\n\\tvar width = images[0].width;\\r\\n\\tvar height = images[0].height;\\r\\n\\toptions.texture_type = gl.TEXTURE_CUBE_MAP;\\r\\n\\r\\n\\tvar texture = null;\\r\\n\\t\\r\\n\\tif(options.texture)\\r\\n\\t{\\r\\n\\t\\ttexture = options.texture;\\r\\n\\t\\ttexture.width = width;\\r\\n\\t\\ttexture.height = height;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\ttexture = new GL.Texture( width, height, options );\\r\\n\\r\\n\\tTexture.setUploadOptions(options);\\r\\n\\ttexture.bind();\\r\\n\\r\\n\\ttry {\\r\\n\\r\\n\\t\\tfor(var i = 0; i < 6; i++)\\r\\n\\t\\t\\tgl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, texture.format, texture.format, texture.type, images[i]);\\r\\n\\t\\ttexture.data = images;\\r\\n\\t} catch (e) {\\r\\n\\t\\tif (location.protocol == 'file:') {\\r\\n\\t\\t throw 'image not loaded for security reasons (serve this page over \\\"http://\\\" instead)';\\r\\n\\t\\t} else {\\r\\n\\t\\t throw 'image not loaded for security reasons (image must originate from the same ' +\\r\\n\\t\\t\\t'domain as this page or use Cross-Origin Resource Sharing)';\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\tif (options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) {\\r\\n\\t\\tgl.generateMipmap(gl.TEXTURE_CUBE_MAP);\\r\\n\\t\\ttexture.has_mipmaps = true;\\r\\n\\t}\\r\\n\\r\\n\\ttexture.unbind();\\r\\n\\treturn texture;\\r\\n};\\r\\n\\r\\n/**\\r\\n* Create a cubemap texture from a single image that contains all six images \\r\\n* If it is a cross, it must be horizontally aligned, and options.is_cross must be equal to the column where the top and bottom are located (usually 1 or 2)\\r\\n* otherwise it assumes the 6 images are arranged vertically, in the order of OpenGL: +X, -X, +Y, -Y, +Z, -Z\\r\\n* @method Texture.cubemapFromImage\\r\\n* @param {Image} image\\r\\n* @param {Object} options\\r\\n* @return {Texture} the texture\\r\\n*/\\r\\nTexture.cubemapFromImage = function( image, options ) {\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\tif(image.width != (image.height / 6) && image.height % 6 != 0 && !options.faces && !options.is_polar )\\r\\n\\t{\\r\\n\\t\\tconsole.error( \\\"Cubemap image not valid, only 1x6 (vertical) or 6x3 (cross) formats. Check size:\\\", image.width, image.height );\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tvar width = image.width;\\r\\n\\tvar height = image.height;\\r\\n\\r\\n\\tif(options.is_polar)\\r\\n\\t{\\r\\n\\t\\tvar size = options.size || GL.nearestPowerOfTwo( image.height );\\r\\n\\t\\tvar temp_tex = GL.Texture.fromImage( image, { ignore_pot:true, wrap: gl.REPEAT, filter: gl.LINEAR } );\\r\\n\\t\\tvar cubemap = new GL.Texture( size, size, { texture_type: gl.TEXTURE_CUBE_MAP, format: gl.RGBA });\\r\\n\\t\\tif(options.texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar old_tex = options.texture;\\r\\n\\t\\t\\tfor(var i in cubemap)\\r\\n\\t\\t\\t\\told_tex[i] = cubemap[i];\\r\\n\\t\\t\\tcubemap = old_tex;\\r\\n\\t\\t}\\r\\n\\t\\tvar rot_matrix = mat3.create();\\r\\n\\t\\tvar uniforms = { u_texture:0, u_rotation: rot_matrix };\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\tvar shader = GL.Shader.getPolarToCubemapShader();\\r\\n\\t\\tcubemap.drawTo(function(t,i){\\r\\n\\t\\t\\tvar face_info = GL.Texture.cubemap_camera_parameters[ i ];\\r\\n\\t\\t\\tmat3.identity( rot_matrix );\\r\\n\\t\\t\\trot_matrix.set( face_info.right, 0 );\\r\\n\\t\\t\\trot_matrix.set( face_info.up, 3 );\\r\\n\\t\\t\\trot_matrix.set( face_info.dir, 6 );\\r\\n\\t\\t\\ttemp_tex.toViewport( shader, uniforms );\\r\\n\\t\\t});\\r\\n\\t\\tif(options.keep_image)\\r\\n\\t\\t\\tcubemap.img = image;\\r\\n\\t\\treturn cubemap;\\r\\n\\t}\\r\\n\\telse if(options.is_cross !== undefined)\\r\\n\\t{\\r\\n\\t\\toptions.faces = Texture.generateCubemapCrossFacesInfo(image.width, options.is_cross);\\r\\n\\t\\twidth = height = image.width / 4;\\r\\n\\t}\\r\\n\\telse if(options.faces)\\r\\n\\t{\\r\\n\\t\\twidth = options.width || options.faces[0].width;\\r\\n\\t\\theight = options.height || options.faces[0].height;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\theight /= 6;\\r\\n\\r\\n\\tif(width != height)\\r\\n\\t{\\r\\n\\t\\tconsole.log(\\\"Texture not valid, width and height for every face must be square\\\");\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tvar size = width;\\r\\n\\toptions.no_flip = true;\\r\\n\\r\\n\\tvar images = [];\\r\\n\\tfor(var i = 0; i < 6; i++)\\r\\n\\t{\\r\\n\\t\\tvar canvas = createCanvas( size, size );\\r\\n\\t\\tvar ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tif(options.faces)\\r\\n\\t\\t\\tctx.drawImage(image, options.faces[i].x, options.faces[i].y, options.faces[i].width || size, options.faces[i].height || size, 0,0, size, size );\\r\\n\\t\\telse\\r\\n\\t\\t\\tctx.drawImage(image, 0, height*i, width, height, 0,0, size, size );\\r\\n\\t\\timages.push(canvas);\\r\\n\\t\\t//document.body.appendChild(canvas); //debug\\r\\n\\t}\\r\\n\\r\\n\\tvar texture = Texture.cubemapFromImages(images, options);\\r\\n\\tif(options.keep_image)\\r\\n\\t\\ttexture.img = image;\\r\\n\\treturn texture;\\r\\n};\\r\\n\\r\\n/**\\r\\n* Given the width and the height of an image, and in which column is the top and bottom sides of the cubemap, it gets the info to pass to Texture.cubemapFromImage in options.faces\\r\\n* @method Texture.generateCubemapCrossFaces\\r\\n* @param {number} width of the CROSS image (not the side image)\\r\\n* @param {number} column the column where the top and the bottom is located\\r\\n* @return {Object} object to pass to Texture.cubemapFromImage in options.faces\\r\\n*/\\r\\nTexture.generateCubemapCrossFacesInfo = function(width, column)\\r\\n{\\r\\n\\tif(column === undefined)\\r\\n\\t\\tcolumn = 1;\\r\\n\\tvar s = width / 4;\\r\\n\\r\\n\\treturn [\\r\\n\\t\\t{ x: 2*s, y: s, width: s, height: s }, //+x\\r\\n\\t\\t{ x: 0, y: s, width: s, height: s }, //-x\\r\\n\\t\\t{ x: column*s, y: 0, width: s, height: s }, //+y\\r\\n\\t\\t{ x: column*s, y: 2*s, width: s, height: s }, //-y\\r\\n\\t\\t{ x: s, y: s, width: s, height: s }, //+z\\r\\n\\t\\t{ x: 3*s, y: s, width: s, height: s } //-z\\r\\n\\t];\\r\\n}\\r\\n\\r\\n/**\\r\\n* Create a cubemap texture from a single image url that contains the six images\\r\\n* if it is a cross, it must be horizontally aligned, and options.is_cross must be equal to the column where the top and bottom are located (usually 1 or 2)\\r\\n* otherwise it assumes the 6 images are arranged vertically.\\r\\n* @method Texture.cubemapFromURL\\r\\n* @param {Image} image\\r\\n* @param {Object} options\\r\\n* @param {Function} on_complete callback\\r\\n* @return {Texture} the texture\\r\\n*/\\r\\nTexture.cubemapFromURL = function( url, options, on_complete ) {\\r\\n\\toptions = options || {};\\r\\n\\toptions = Object.create(options); //creates a new options using the old one as prototype\\r\\n\\toptions.texture_type = gl.TEXTURE_CUBE_MAP;\\r\\n\\tvar texture = options.texture || new GL.Texture(1, 1, options);\\r\\n\\r\\n\\ttexture.bind();\\r\\n\\tTexture.setUploadOptions(options);\\r\\n\\tvar default_color = options.temp_color || [0,0,0,255];\\r\\n\\tvar temp_color = options.type == gl.FLOAT ? new Float32Array(default_color) : new Uint8Array(default_color);\\r\\n\\r\\n\\tfor(var i = 0; i < 6; i++)\\r\\n\\t\\tgl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, texture.format, 1, 1, 0, texture.format, texture.type, temp_color);\\r\\n\\tgl.bindTexture(texture.texture_type, null); //disable\\r\\n\\ttexture.ready = false;\\r\\n\\r\\n\\tvar image = new Image();\\r\\n\\timage.src = url;\\r\\n\\tvar that = this;\\r\\n\\timage.onload = function()\\r\\n\\t{\\r\\n\\t\\toptions.texture = texture;\\r\\n\\t\\ttexture = GL.Texture.cubemapFromImage(this, options);\\r\\n\\t\\tif(texture)\\r\\n\\t\\t\\tdelete texture[\\\"ready\\\"]; //texture.ready = true;\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete(texture);\\r\\n\\t}\\r\\n\\r\\n\\treturn texture;\\t\\r\\n};\\r\\n\\r\\n/**\\r\\n* returns an ArrayBuffer with the pixels in the texture, they are fliped in Y\\r\\n* Warn: If cubemap it only returns the pixels of the first face! use getCubemapPixels instead\\r\\n* @method getPixels\\r\\n* @param {number} cubemap_face [optional] the index of the cubemap face to read (ignore if texture_2D)\\r\\n* @param {number} mipmap level [optional, default is 0]\\r\\n* @return {ArrayBuffer} the data ( Uint8Array, Uint16Array or Float32Array )\\r\\n*/\\r\\nTexture.prototype.getPixels = function( cubemap_face, mipmap_level )\\r\\n{\\r\\n\\tmipmap_level = mipmap_level || 0;\\r\\n\\tvar gl = this.gl;\\r\\n\\tvar v = gl.getViewport();\\r\\n\\tvar old_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING );\\r\\n\\r\\n\\tif(this.format == gl.DEPTH_COMPONENT)\\r\\n\\t\\tthrow(\\\"cannot use getPixels in depth textures\\\");\\r\\n\\r\\n\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\r\\n\\t//reuse fbo\\r\\n\\tvar fbo = gl.__copy_fbo;\\r\\n\\tif(!fbo)\\r\\n\\t\\tfbo = gl.__copy_fbo = gl.createFramebuffer();\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, fbo );\\r\\n\\r\\n\\tvar buffer = null;\\r\\n\\r\\n\\tvar width = this.width >> mipmap_level;\\r\\n\\tvar height = this.height >> mipmap_level;\\r\\n\\tgl.viewport(0, 0, width, height);\\r\\n\\r\\n\\tif(this.texture_type == gl.TEXTURE_2D)\\r\\n\\t\\tgl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.handler, mipmap_level);\\r\\n\\telse if(this.texture_type == gl.TEXTURE_CUBE_MAP)\\r\\n\\t\\tgl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + (cubemap_face || 0), this.handler, mipmap_level);\\r\\n\\r\\n\\tvar channels = this.format == gl.RGB ? 3 : 4;\\r\\n\\tchannels = 4; //WEBGL DOES NOT SUPPORT READING 3 CHANNELS ONLY, YET...\\r\\n\\tvar type = this.type;\\r\\n\\t//type = gl.UNSIGNED_BYTE; //WEBGL DOES NOT SUPPORT READING FLOAT seems, YET... 23/5/18 now it seems it does now\\r\\n\\r\\n\\tif(type == gl.UNSIGNED_BYTE)\\r\\n\\t\\tbuffer = new Uint8Array( width * height * channels );\\r\\n\\telse if(type == GL.HALF_FLOAT || type == GL.HALF_FLOAT_OES) //previously half float couldnot be read\\r\\n\\t\\tbuffer = new Uint16Array( width * height * channels ); //gl.UNSIGNED_SHORT_4_4_4_4 is only for texture that are SHORT per pixel, not per channel!\\r\\n\\telse \\r\\n\\t\\tbuffer = new Float32Array( width * height * channels );\\r\\n\\r\\n\\tgl.readPixels( 0,0, width, height, channels == 3 ? gl.RGB : gl.RGBA, type, buffer ); //NOT SUPPORTED FLOAT or RGB BY WEBGL YET\\r\\n\\r\\n\\t//restore\\r\\n\\tgl.bindFramebuffer(gl.FRAMEBUFFER, old_fbo );\\r\\n\\tgl.viewport(v[0], v[1], v[2], v[3]);\\r\\n\\treturn buffer;\\r\\n}\\r\\n\\r\\n/**\\r\\n* uploads some pixels to the texture (see uploadData method for more options)\\r\\n* @method setPixels\\r\\n* @param {ArrayBuffer} data gl.UNSIGNED_BYTE or gl.FLOAT data\\r\\n* @param {Boolean} no_flip do not flip in Y \\r\\n* @param {Boolean} skip_mipmaps do not update mipmaps when possible\\r\\n* @param {Number} cubemap_face if the texture is a cubemap, which face\\r\\n*/\\r\\nTexture.prototype.setPixels = function( data, no_flip, skip_mipmaps, cubemap_face )\\r\\n{\\r\\n\\tvar options = { no_flip: no_flip };\\r\\n\\tif(cubemap_face)\\r\\n\\t\\toptions.cubemap_face = cubemap_face;\\r\\n\\tthis.uploadData( data, options, skip_mipmaps );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns an array with six arrays containing the pixels of every cubemap face\\r\\n* @method getCubemapPixels\\r\\n* @return {Array} the array that has 6 typed arrays containing the pixels \\r\\n*/\\r\\nTexture.prototype.getCubemapPixels = function()\\r\\n{\\r\\n\\tif(this.texture_type !== gl.TEXTURE_CUBE_MAP)\\r\\n\\t\\tthrow(\\\"this texture is not a cubemap\\\");\\r\\n\\treturn [ this.getPixels(0),\\tthis.getPixels(1), this.getPixels(2), this.getPixels(3), this.getPixels(4), this.getPixels(5) ];\\r\\n}\\r\\n\\r\\n/**\\r\\n* fills a cubemap given an array with typed arrays containing the pixels of 6 faces\\r\\n* @method setCubemapPixels\\r\\n* @param {Array} data array that has 6 typed arrays containing the pixels \\r\\n* @param {bool} noflip if pixels should not be flipped according to Y\\r\\n*/\\r\\nTexture.prototype.setCubemapPixels = function( data_array, no_flip )\\r\\n{\\r\\n\\tif(this.texture_type !== gl.TEXTURE_CUBE_MAP)\\r\\n\\t\\tthrow(\\\"this texture is not a cubemap, it should be created with { texture_type: gl.TEXTURE_CUBE_MAP }\\\");\\r\\n\\tfor(var i = 0; i < 6; ++i)\\r\\n\\t\\tthis.setPixels( data_array[i], no_flip, i != 5, i );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Copy texture content to a canvas\\r\\n* @method toCanvas\\r\\n* @param {Canvas} canvas must have the same size, if different the canvas will be resized\\r\\n* @param {boolean} flip_y optional, flip vertically\\r\\n* @param {Number} max_size optional, if it is supplied the canvas wont be bigger of max_size (the image will be scaled down)\\r\\n*/\\r\\nTexture.prototype.toCanvas = function( canvas, flip_y, max_size )\\r\\n{\\r\\n\\tmax_size = max_size || 8192;\\r\\n\\tvar gl = this.gl;\\r\\n\\r\\n\\tvar w = Math.min( this.width, max_size );\\r\\n\\tvar h = Math.min( this.height, max_size );\\r\\n\\r\\n\\t//cross\\r\\n\\tif(this.texture_type == gl.TEXTURE_CUBE_MAP)\\r\\n\\t{\\r\\n\\t\\tw = w * 4;\\r\\n\\t\\th = h * 3;\\r\\n\\t}\\r\\n\\r\\n\\tcanvas = canvas || createCanvas( w, h );\\r\\n\\tif(canvas.width != w) \\r\\n\\t\\tcanvas.width = w;\\r\\n\\tif(canvas.height != h)\\r\\n\\t\\tcanvas.height = h;\\r\\n\\r\\n\\tvar buffer = null;\\r\\n\\tif(this.texture_type == gl.TEXTURE_2D )\\r\\n\\t{\\r\\n\\t\\tif(this.width != w || this.height != h || this.type != gl.UNSIGNED_BYTE) //resize image to fit the canvas\\r\\n\\t\\t{\\r\\n\\t\\t\\t//create a temporary texture\\r\\n\\t\\t\\tvar temp = new GL.Texture(w,h,{ format: gl.RGBA, filter: gl.NEAREST });\\r\\n\\t\\t\\tthis.copyTo( temp );\\t\\r\\n\\t\\t\\tbuffer = temp.getPixels();\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tbuffer = this.getPixels();\\r\\n\\r\\n\\t\\tvar ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tvar pixels = ctx.getImageData(0,0,w,h);\\r\\n\\t\\tpixels.data.set( buffer );\\r\\n\\t\\tctx.putImageData(pixels,0,0);\\r\\n\\r\\n\\t\\tif(flip_y)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar temp = createCanvas(w,h);\\r\\n\\t\\t\\tvar temp_ctx = temp.getContext(\\\"2d\\\");\\r\\n\\t\\t\\ttemp_ctx.translate(0,temp.height);\\r\\n\\t\\t\\ttemp_ctx.scale(1,-1);\\r\\n\\t\\t\\ttemp_ctx.drawImage( canvas, 0, 0, temp.width, temp.height );\\r\\n\\t\\t\\tctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);\\r\\n\\t\\t\\tctx.drawImage( temp, 0, 0 );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(this.texture_type == gl.TEXTURE_CUBE_MAP )\\r\\n\\t{\\r\\n\\t\\tvar temp_canvas = createCanvas( this.width, this.height );\\r\\n\\t\\tvar temp_ctx = temp_canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tvar info = GL.Texture.generateCubemapCrossFacesInfo( canvas.width, 1 );\\r\\n\\t\\tvar ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tctx.fillStyle = \\\"black\\\";\\r\\n\\t\\tctx.fillRect(0,0,canvas.width, canvas.height );\\r\\n\\r\\n\\t\\tvar cubemap = this;\\r\\n\\t\\tif(this.type != gl.UNSIGNED_BYTE) //convert pixels to uint8 as it is the only supported format by the canvas\\r\\n\\t\\t{\\r\\n\\t\\t\\t//create a temporary texture\\r\\n\\t\\t\\tcubemap = new GL.Texture( this.width, this.height, { format: gl.RGBA, texture_type: gl.TEXTURE_CUBE_MAP, filter: gl.NEAREST, type: gl.UNSIGNED_BYTE });\\r\\n\\t\\t\\tthis.copyTo( cubemap );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var i = 0; i < 6; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pixels = temp_ctx.getImageData(0,0, temp_canvas.width, temp_canvas.height );\\r\\n\\t\\t\\tbuffer = cubemap.getPixels(i);\\r\\n\\t\\t\\tpixels.data.set( buffer );\\r\\n\\t\\t\\ttemp_ctx.putImageData(pixels,0,0);\\r\\n\\t\\t\\tctx.drawImage( temp_canvas, info[i].x, info[i].y, temp_canvas.width, temp_canvas.height );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn canvas;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* returns the texture file in binary format \\r\\n* @method toBinary\\r\\n* @param {Boolean} flip_y\\r\\n* @return {ArrayBuffer} the arraybuffer of the file containing the image\\r\\n*/\\r\\nTexture.binary_extension = \\\"png\\\";\\r\\nTexture.prototype.toBinary = function(flip_y, type)\\r\\n{\\r\\n\\t//dump to canvas\\r\\n\\tvar canvas = this.toCanvas(null,flip_y);\\r\\n\\t//use the slow method (because its sync)\\r\\n\\tvar data = canvas.toDataURL( type );\\r\\n\\tvar index = data.indexOf(\\\",\\\");\\r\\n\\tvar base64_data = data.substr(index+1);\\r\\n\\tvar binStr = atob( base64_data );\\r\\n\\tvar len = binStr.length,\\r\\n\\tarr = new Uint8Array(len);\\r\\n\\tfor (var i=0; i 0 )\\r\\n\\t\\tconsole.warn(\\\"this texture is already in the textures pool\\\");\\r\\n\\r\\n\\tvar pool = gl._texture_pool;\\r\\n\\tif(!pool)\\r\\n\\t\\tpool = gl._texture_pool = [];\\r\\n\\ttex._pool = getTime();\\r\\n\\tpool.push( tex );\\r\\n\\r\\n\\t//do not store too much textures in the textures pool\\r\\n\\tif( pool.length > 20 )\\r\\n\\t{\\r\\n\\t\\tpool.sort( function(a,b) { return b._pool - a._pool } ); //sort by time\\r\\n\\t\\t//pool.sort( function(a,b) { return a._key - b._key } ); //sort by size\\r\\n\\t\\tvar tex = pool.pop(); //free the last one\\r\\n\\t\\ttex._pool = 0;\\r\\n\\t\\ttex.delete();\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//returns the next power of two bigger than size\\r\\nTexture.nextPOT = function( size )\\r\\n{\\r\\n\\treturn Math.pow( 2, Math.ceil( Math.log(size) / Math.log(2) ) );\\r\\n}\\r\\n\\r\\n/** \\r\\n* FBO for FrameBufferObjects, FBOs are used to store the render inside one or several textures \\r\\n* Supports multibuffer and depthbuffer texture, useful for deferred rendering\\r\\n* @namespace GL\\r\\n* @class FBO\\r\\n* @param {Array} color_textures an array containing the color textures, if not supplied a render buffer will be used\\r\\n* @param {GL.Texture} depth_texture the depth texture, if not supplied a render buffer will be used\\r\\n* @param {Bool} stencil create a stencil buffer?\\r\\n* @constructor\\r\\n*/\\r\\nfunction FBO( textures, depth_texture, stencil, gl )\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tthis.gl = gl;\\r\\n\\tthis._context_id = gl.context_id; \\r\\n\\r\\n\\tif(textures && textures.constructor !== Array)\\r\\n\\t\\tthrow(\\\"FBO textures must be an Array\\\");\\r\\n\\r\\n\\tthis.handler = null;\\r\\n\\tthis.width = -1;\\r\\n\\tthis.height = -1;\\r\\n\\tthis.color_textures = [];\\r\\n\\tthis.depth_texture = null;\\r\\n\\tthis.stencil = !!stencil;\\r\\n\\r\\n\\tthis._stencil_enabled = false;\\r\\n\\tthis._num_binded_textures = 0;\\r\\n\\r\\n\\t//assign textures\\r\\n\\tif((textures && textures.length) || depth_texture)\\r\\n\\t\\tthis.setTextures( textures, depth_texture );\\r\\n\\r\\n\\t//save state\\r\\n\\tthis._old_fbo_handler = null;\\r\\n\\tthis._old_viewport = new Float32Array(4);\\r\\n}\\r\\n\\r\\nGL.FBO = FBO;\\r\\n\\r\\n/**\\r\\n* Changes the textures binded to this FBO\\r\\n* @method setTextures\\r\\n* @param {Array} color_textures an array containing the color textures, if not supplied a render buffer will be used\\r\\n* @param {GL.Texture} depth_texture the depth texture, if not supplied a render buffer will be used\\r\\n* @param {Boolean} skip_disable it doenst try to go back to the previous FBO enabled in case there was one\\r\\n*/\\r\\nFBO.prototype.setTextures = function( color_textures, depth_texture, skip_disable )\\r\\n{\\r\\n\\t//test depth\\r\\n\\tif( depth_texture && depth_texture.constructor === GL.Texture )\\r\\n\\t{\\r\\n\\t\\tif( depth_texture.format !== GL.DEPTH_COMPONENT && \\r\\n\\t\\t\\tdepth_texture.format !== GL.DEPTH_STENCIL && \\r\\n\\t\\t\\tdepth_texture.format !== GL.DEPTH_COMPONENT16 && \\r\\n\\t\\t\\tdepth_texture.format !== GL.DEPTH_COMPONENT24 &&\\r\\n\\t\\t\\tdepth_texture.format !== GL.DEPTH_COMPONENT32F )\\r\\n\\t\\t\\tthrow(\\\"FBO Depth texture must be of format: gl.DEPTH_COMPONENT, gl.DEPTH_STENCIL or gl.DEPTH_COMPONENT16/24/32F (only in webgl2)\\\");\\r\\n\\r\\n\\t\\tif( depth_texture.type != GL.UNSIGNED_SHORT && \\r\\n\\t\\t\\tdepth_texture.type != GL.UNSIGNED_INT && \\r\\n\\t\\t\\tdepth_texture.type != GL.UNSIGNED_INT_24_8_WEBGL &&\\r\\n\\t\\t\\tdepth_texture.type != GL.FLOAT)\\r\\n\\t\\t\\tthrow(\\\"FBO Depth texture must be of type: gl.UNSIGNED_SHORT, gl.UNSIGNED_INT, gl.UNSIGNED_INT_24_8_WEBGL\\\");\\r\\n\\t}\\r\\n\\r\\n\\t//test if is already binded\\r\\n\\tvar same = this.depth_texture == depth_texture;\\r\\n\\tif( same && color_textures )\\r\\n\\t{\\r\\n\\t\\tif( color_textures.constructor !== Array )\\r\\n\\t\\t\\tthrow(\\\"FBO: color_textures parameter must be an array containing all the textures to be binded in the color\\\");\\r\\n\\t\\tif( color_textures.length == this.color_textures.length )\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i = 0; i < color_textures.length; ++i)\\r\\n\\t\\t\\t\\tif( color_textures[i] != this.color_textures[i] )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tsame = false;\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tsame = false;\\r\\n\\t}\\r\\n\\r\\n\\tif(this._stencil_enabled !== this.stencil)\\r\\n\\t\\tsame = false;\\r\\n\\t\\t\\r\\n\\tif(same)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//copy textures in place\\r\\n\\tthis.color_textures.length = color_textures ? color_textures.length : 0;\\r\\n\\tif(color_textures)\\r\\n\\t\\tfor(var i = 0; i < color_textures.length; ++i)\\r\\n\\t\\t\\tthis.color_textures[i] = color_textures[i];\\r\\n\\tthis.depth_texture = depth_texture;\\r\\n\\r\\n\\t//update GPU FBO\\r\\n\\tthis.update( skip_disable );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Updates the FBO with the new set of textures and buffers\\r\\n* @method update\\r\\n* @param {Boolean} skip_disable it doenst try to go back to the previous FBO enabled in case there was one\\r\\n*/\\r\\nFBO.prototype.update = function( skip_disable )\\r\\n{\\r\\n\\t//save state to restore afterwards\\r\\n\\tthis._old_fbo_handler = gl.getParameter( gl.FRAMEBUFFER_BINDING );\\r\\n\\r\\n\\tif(!this.handler)\\r\\n\\t\\tthis.handler = gl.createFramebuffer();\\r\\n\\r\\n\\tvar w = -1,\\r\\n\\t\\th = -1,\\r\\n\\t\\ttype = null;\\r\\n\\r\\n\\tvar color_textures = this.color_textures;\\r\\n\\tvar depth_texture = this.depth_texture;\\r\\n\\r\\n\\t//compute the W and H (and check they have the same size)\\r\\n\\tif(color_textures && color_textures.length)\\r\\n\\t\\tfor(var i = 0; i < color_textures.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar t = color_textures[i];\\r\\n\\t\\t\\tif(t.constructor !== GL.Texture)\\r\\n\\t\\t\\t\\tthrow(\\\"FBO can only bind instances of GL.Texture\\\");\\r\\n\\t\\t\\tif(w == -1) \\r\\n\\t\\t\\t\\tw = t.width;\\r\\n\\t\\t\\telse if(w != t.width)\\r\\n\\t\\t\\t\\tthrow(\\\"Cannot bind textures with different dimensions\\\");\\r\\n\\t\\t\\tif(h == -1) \\r\\n\\t\\t\\t\\th = t.height;\\r\\n\\t\\t\\telse if(h != t.height)\\r\\n\\t\\t\\t\\tthrow(\\\"Cannot bind textures with different dimensions\\\");\\r\\n\\t\\t\\tif(type == null) //first one defines the type\\r\\n\\t\\t\\t\\ttype = t.type;\\r\\n\\t\\t\\telse if (type != t.type)\\r\\n\\t\\t\\t\\tthrow(\\\"Cannot bind textures to a FBO with different pixel formats\\\");\\r\\n\\t\\t\\tif (t.texture_type != gl.TEXTURE_2D)\\r\\n\\t\\t\\t\\tthrow(\\\"Cannot bind a Cubemap to a FBO\\\");\\r\\n\\t\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tw = depth_texture.width;\\r\\n\\t\\th = depth_texture.height;\\r\\n\\t}\\r\\n\\r\\n\\tthis.width = w;\\r\\n\\tthis.height = h;\\r\\n\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, this.handler );\\r\\n\\r\\n\\t//draw_buffers allow to have more than one color texture binded in a FBO\\r\\n\\tvar ext = gl.extensions[\\\"WEBGL_draw_buffers\\\"];\\r\\n\\tif( gl.webgl_version == 1 && !ext && color_textures && color_textures.length > 1)\\r\\n\\t\\tthrow(\\\"Rendering to several textures not supported by your browser\\\");\\r\\n\\r\\n\\tvar target = gl.webgl_version == 1 ? gl.FRAMEBUFFER : gl.DRAW_FRAMEBUFFER;\\r\\n\\r\\n\\t//detach anything bindede\\r\\n\\tgl.framebufferRenderbuffer( target, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null );\\r\\n\\tgl.framebufferRenderbuffer( target, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, null );\\r\\n\\t//detach color too?\\r\\n\\r\\n\\t//bind a buffer for the depth\\r\\n\\tif( depth_texture && depth_texture.constructor === GL.Texture )\\r\\n\\t{\\r\\n\\t\\tif(gl.webgl_version == 1 && !gl.extensions[\\\"WEBGL_depth_texture\\\"] )\\r\\n\\t\\t\\tthrow(\\\"Rendering to depth texture not supported by your browser\\\");\\r\\n\\r\\n\\t\\tif(this.stencil && depth_texture.format !== gl.DEPTH_STENCIL )\\r\\n\\t\\t\\tconsole.warn(\\\"Stencil cannot be enabled if there is a depth texture with a DEPTH_STENCIL format\\\");\\r\\n\\r\\n\\t\\tif( depth_texture.format == gl.DEPTH_STENCIL )\\r\\n\\t\\t\\tgl.framebufferTexture2D( target, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0);\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.framebufferTexture2D( target, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0);\\r\\n\\t}\\r\\n\\telse //create a renderbuffer to store depth\\r\\n\\t{\\r\\n\\t\\tvar depth_renderbuffer = null;\\r\\n\\t\\t\\r\\n\\t\\t//allows to reuse a renderbuffer between FBOs\\r\\n\\t\\tif( depth_texture && depth_texture.constructor === WebGLRenderbuffer && depth_texture.width == w && depth_texture.height == h ) \\r\\n\\t\\t\\tdepth_renderbuffer = this._depth_renderbuffer = depth_texture;\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\t//create one\\r\\n\\t\\t\\tdepth_renderbuffer = this._depth_renderbuffer = this._depth_renderbuffer || gl.createRenderbuffer();\\r\\n\\t\\t\\tdepth_renderbuffer.width = w;\\r\\n\\t\\t\\tdepth_renderbuffer.height = h;\\r\\n\\t\\t}\\r\\n\\t\\t\\r\\n\\t\\tgl.bindRenderbuffer( gl.RENDERBUFFER, depth_renderbuffer );\\r\\n\\t\\tif(this.stencil)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_STENCIL, w, h );\\r\\n\\t\\t\\tgl.framebufferRenderbuffer( target, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depth_renderbuffer );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h );\\r\\n\\t\\t\\tgl.framebufferRenderbuffer( target, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depth_renderbuffer );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//bind buffers for the colors\\r\\n\\tif(color_textures && color_textures.length)\\r\\n\\t{\\r\\n\\t\\tthis.order = []; //draw_buffers request the use of an array with the order of the attachments\\r\\n\\t\\tfor(var i = 0; i < color_textures.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar t = color_textures[i];\\r\\n\\r\\n\\t\\t\\t//not a bug, gl.COLOR_ATTACHMENT0 + i because COLOR_ATTACHMENT is sequential numbers\\r\\n\\t\\t\\tgl.framebufferTexture2D( target, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, t.handler, 0 );\\r\\n\\t\\t\\tthis.order.push( gl.COLOR_ATTACHMENT0 + i );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse //create renderbuffer to store color\\r\\n\\t{\\r\\n\\t\\tvar color_renderbuffer = this._color_renderbuffer = this._color_renderbuffer || gl.createRenderbuffer();\\r\\n\\t\\tcolor_renderbuffer.width = w;\\r\\n\\t\\tcolor_renderbuffer.height = h;\\r\\n\\t\\tgl.bindRenderbuffer( gl.RENDERBUFFER, color_renderbuffer );\\r\\n\\t\\tgl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, w, h );\\r\\n\\t\\tgl.framebufferRenderbuffer( target, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color_renderbuffer );\\r\\n\\t}\\r\\n\\r\\n\\t//detach old ones (only if is reusing a FBO with a different set of textures)\\r\\n\\tvar num = color_textures ? color_textures.length : 0;\\r\\n\\tfor(var i = num; i < this._num_binded_textures; ++i)\\r\\n\\t\\tgl.framebufferTexture2D( target, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, null, 0);\\r\\n\\tthis._num_binded_textures = num;\\r\\n\\r\\n\\tthis._stencil_enabled = this.stencil;\\r\\n\\r\\n\\t/* does not work, must be used with the depth_stencil\\r\\n\\tif(this.stencil && !depth_texture)\\r\\n\\t{\\r\\n\\t\\tvar stencil_buffer = this._stencil_buffer = this._stencil_buffer || gl.createRenderbuffer();\\r\\n\\t\\tstencil_buffer.width = w;\\r\\n\\t\\tstencil_buffer.height = h;\\r\\n\\t\\tgl.bindRenderbuffer( gl.RENDERBUFFER, stencil_buffer );\\r\\n\\t\\tgl.renderbufferStorage( gl.RENDERBUFFER, gl.STENCIL_INDEX8, w, h);\\r\\n\\t\\tgl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencil_buffer );\\r\\n\\t\\tthis._stencil_enabled = true;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tthis._stencil_buffer = null;\\r\\n\\t\\tthis._stencil_enabled = false;\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\t//when using more than one texture you need to use the multidraw extension\\r\\n\\tif(color_textures && color_textures.length > 1)\\r\\n\\t{\\r\\n\\t\\tif( ext )\\r\\n\\t\\t\\text.drawBuffersWEBGL( this.order );\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.drawBuffers( this.order );\\r\\n\\t}\\r\\n\\r\\n\\t//check completion\\r\\n\\tvar complete = gl.checkFramebufferStatus( target );\\r\\n\\tif(complete !== gl.FRAMEBUFFER_COMPLETE) //36054: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT\\r\\n\\t\\tthrow(\\\"FBO not complete: \\\" + complete);\\r\\n\\r\\n\\t//restore state\\r\\n\\tgl.bindTexture(gl.TEXTURE_2D, null);\\r\\n\\tgl.bindRenderbuffer(gl.RENDERBUFFER, null);\\r\\n\\tif(!skip_disable)\\r\\n\\t\\tgl.bindFramebuffer( target, this._old_fbo_handler );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Enables this FBO (from now on all the render will be stored in the textures attached to this FBO\\r\\n* It stores the previous viewport to restore it afterwards, and changes it to full FBO size\\r\\n* @method bind\\r\\n* @param {boolean} keep_old keeps the previous FBO is one was attached to restore it afterwards\\r\\n*/\\r\\nFBO.prototype.bind = function( keep_old )\\r\\n{\\r\\n\\tif(!this.color_textures.length && !this.depth_texture)\\r\\n\\t\\tthrow(\\\"FBO: no textures attached to FBO\\\");\\r\\n\\tthis._old_viewport.set( gl.viewport_data );\\r\\n\\r\\n\\tif(keep_old)\\r\\n\\t\\tthis._old_fbo_handler = gl.getParameter( gl.FRAMEBUFFER_BINDING );\\r\\n\\telse\\r\\n\\t\\tthis._old_fbo_handler = null;\\r\\n\\r\\n\\tif(this._old_fbo_handler != this.handler )\\r\\n\\t\\tgl.bindFramebuffer( gl.FRAMEBUFFER, this.handler );\\r\\n\\r\\n\\t//mark them as in use in the FBO\\r\\n\\tfor(var i = 0; i < this.color_textures.length; ++i)\\r\\n\\t\\tthis.color_textures[i]._in_current_fbo = true;\\r\\n\\tif(this.depth_texture)\\r\\n\\t\\tthis.depth_texture._in_current_fbo = true;\\r\\n\\r\\n\\tgl.viewport( 0,0, this.width, this.height );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Disables this FBO, if it was binded with keep_old then the old FBO is enabled, otherwise it will render to the screen\\r\\n* Restores viewport to previous\\r\\n* @method unbind\\r\\n*/\\r\\nFBO.prototype.unbind = function()\\r\\n{\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, this._old_fbo_handler );\\r\\n\\tthis._old_fbo_handler = null;\\r\\n\\tgl.setViewport( this._old_viewport );\\r\\n\\r\\n\\t//mark the textures as no longer in use\\r\\n\\tfor(var i = 0; i < this.color_textures.length; ++i)\\r\\n\\t\\tthis.color_textures[i]._in_current_fbo = false;\\r\\n\\tif(this.depth_texture)\\r\\n\\t\\tthis.depth_texture._in_current_fbo = false;\\r\\n}\\r\\n\\r\\n//binds another FBO without switch back to previous (faster)\\r\\nFBO.prototype.switchTo = function( next_fbo )\\r\\n{\\r\\n\\tnext_fbo._old_fbo_handler = this._old_fbo_handler;\\r\\n\\tnext_fbo._old_viewport.set( this._old_viewport );\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, next_fbo.handler );\\r\\n\\tthis._old_fbo_handler = null;\\r\\n\\tgl.viewport( 0,0, this.width, this.height );\\r\\n\\r\\n\\t//mark the textures as no longer in use\\r\\n\\tfor(var i = 0; i < this.color_textures.length; ++i)\\r\\n\\t\\tthis.color_textures[i]._in_current_fbo = false;\\r\\n\\tif(this.depth_texture)\\r\\n\\t\\tthis.depth_texture._in_current_fbo = false;\\r\\n\\r\\n\\t//mark them as in use in the FBO\\r\\n\\tfor(var i = 0; i < next_fbo.color_textures.length; ++i)\\r\\n\\t\\tnext_fbo.color_textures[i]._in_current_fbo = true;\\r\\n\\tif(next_fbo.depth_texture)\\r\\n\\t\\tnext_fbo.depth_texture._in_current_fbo = true;\\r\\n}\\r\\n\\r\\nFBO.prototype.delete = function()\\r\\n{\\r\\n\\tgl.deleteFramebuffer( this.handler );\\r\\n\\tthis.handler = null;\\r\\n}\\r\\n\\r\\n//WebGL 1.0 support for certaing FBOs is not very clear and can crash sometimes\\r\\nFBO.supported = {};\\r\\n//type: gl.FLOAT, format: gl.RGBA\\r\\nFBO.testSupport = function( type, format ) {\\r\\n\\tvar name = type +\\\":\\\" + format;\\r\\n\\tif( FBO.supported[ name ] != null )\\r\\n\\t\\treturn FBO.supported[ name ];\\r\\n\\r\\n\\tvar tex = new GL.Texture(1,1,{ format: format, type: type });\\r\\n\\ttry\\r\\n\\t{\\r\\n\\t\\tvar fbo = new GL.FBO([tex]);\\r\\n\\t}\\r\\n\\tcatch (err)\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"This browser WEBGL implementation doesn't support this FBO format: \\\" + GL.reverse[type] + \\\" \\\" + GL.reverse[format] );\\r\\n\\t\\treturn FBO.supported[ name ] = false;\\r\\n\\t}\\r\\n\\tFBO.supported[ name ] = true;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* @namespace GL\\r\\n*/\\r\\n\\r\\n/**\\r\\n* Shader class to upload programs to the GPU\\r\\n* @class Shader\\r\\n* @constructor\\r\\n* @param {String} vertexSource (it also allows to pass a compiled vertex shader)\\r\\n* @param {String} fragmentSource (it also allows to pass a compiled fragment shader)\\r\\n* @param {Object} macros (optional) precompiler macros to be applied when compiling\\r\\n*/\\r\\nglobal.Shader = GL.Shader = function Shader( vertexSource, fragmentSource, macros )\\r\\n{\\r\\n\\tif(GL.debug)\\r\\n\\t\\tconsole.log(\\\"GL.Shader created\\\");\\r\\n\\r\\n\\tif( !vertexSource || !fragmentSource )\\r\\n\\t\\tthrow(\\\"GL.Shader source code parameter missing\\\");\\r\\n\\r\\n\\t//used to avoid problems with resources moving between different webgl context\\r\\n\\tthis._context_id = global.gl.context_id; \\r\\n\\tvar gl = this.gl = global.gl;\\r\\n\\r\\n\\t//expand macros\\r\\n\\tvar extra_code = Shader.expandMacros( macros );\\r\\n\\r\\n\\tvar final_vertexSource = vertexSource.constructor === String ? Shader.injectCode( extra_code, vertexSource, gl ) : vertexSource;\\r\\n\\tvar final_fragmentSource = fragmentSource.constructor === String ? Shader.injectCode( extra_code, fragmentSource, gl ) : fragmentSource;\\r\\n\\r\\n\\tthis.program = gl.createProgram();\\r\\n\\r\\n\\tvar vs = vertexSource.constructor === String ? GL.Shader.compileSource( gl.VERTEX_SHADER, final_vertexSource ) : vertexSource;\\r\\n\\tvar fs = fragmentSource.constructor === String ? GL.Shader.compileSource( gl.FRAGMENT_SHADER, final_fragmentSource ) : fragmentSource;\\r\\n\\r\\n\\tgl.attachShader( this.program, vs, gl );\\r\\n\\tgl.attachShader( this.program, fs, gl );\\r\\n\\tgl.linkProgram(this.program);\\r\\n\\tif (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {\\r\\n\\t\\tthrow 'link error: ' + gl.getProgramInfoLog(this.program);\\r\\n\\t}\\r\\n\\r\\n\\tthis.vs_shader = vs;\\r\\n\\tthis.fs_shader = fs;\\r\\n\\r\\n\\t//Extract info from the shader\\r\\n\\tthis.attributes = {}; \\r\\n\\tthis.uniformInfo = {};\\r\\n\\tthis.samplers = {};\\r\\n\\r\\n\\t//extract info about the shader to speed up future processes\\r\\n\\tthis.extractShaderInfo();\\r\\n}\\r\\n\\r\\nShader.expandMacros = function(macros)\\r\\n{\\r\\n\\tvar extra_code = \\\"\\\"; //add here preprocessor directives that should be above everything\\r\\n\\tif(macros)\\r\\n\\t\\tfor(var i in macros)\\r\\n\\t\\t\\textra_code += \\\"#define \\\" + i + \\\" \\\" + (macros[i] ? macros[i] : \\\"\\\") + \\\"\\\\n\\\";\\r\\n\\treturn extra_code;\\r\\n}\\r\\n\\r\\n//this is done to avoid problems with the #version which must be in the first line\\r\\nShader.injectCode = function( inject_code, code, gl )\\r\\n{\\r\\n\\tvar index = code.indexOf(\\\"\\\\n\\\");\\r\\n\\tvar version = ( gl ? \\\"#define WEBGL\\\" + gl.webgl_version + \\\"\\\\n\\\" : \\\"\\\");\\r\\n\\tvar first_line = code.substr(0,index).trim();\\r\\n\\tif( first_line.indexOf(\\\"#version\\\") == -1 )\\r\\n\\t\\treturn version + inject_code + code;\\r\\n\\treturn first_line + \\\"\\\\n\\\" + version + inject_code + code.substr(index);\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Compiles one single shader source (could be gl.VERTEX_SHADER or gl.FRAGMENT_SHADER) and returns the webgl shader handler \\r\\n* Used internaly to compile the vertex and fragment shader.\\r\\n* It throws an exception if there is any error in the code\\r\\n* @method Shader.compileSource\\r\\n* @param {Number} type could be gl.VERTEX_SHADER or gl.FRAGMENT_SHADER\\r\\n* @param {String} source the source file to compile\\r\\n* @return {WebGLShader} the handler from webgl\\r\\n*/\\r\\nShader.compileSource = function( type, source, gl, shader )\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tshader = shader || gl.createShader(type);\\r\\n\\tgl.shaderSource(shader, source);\\r\\n\\tgl.compileShader(shader);\\r\\n\\tif (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\\r\\n\\t\\tthrow (type == gl.VERTEX_SHADER ? \\\"Vertex\\\" : \\\"Fragment\\\") + ' shader compile error: ' + gl.getShaderInfoLog(shader);\\r\\n\\t}\\r\\n\\treturn shader;\\r\\n}\\r\\n\\r\\nShader.parseError = function( error_str, vs_code, fs_code )\\r\\n{\\r\\n\\tif(!error_str)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar t = error_str.split(\\\" \\\");\\r\\n\\tvar nums = t[5].split(\\\":\\\");\\r\\n\\r\\n\\treturn {\\r\\n\\t\\ttype: t[0],\\r\\n\\t\\tline_number: parseInt( nums[1] ),\\r\\n\\t\\tline_pos: parseInt( nums[0] ),\\r\\n\\t\\tline_code: ( t[0] == \\\"Fragment\\\" ? fs_code : vs_code ).split(\\\"\\\\n\\\")[ parseInt( nums[1] ) ],\\r\\n\\t\\terr: error_str\\r\\n\\t};\\r\\n}\\r\\n\\r\\n/**\\r\\n* It updates the code inside one shader\\r\\n* @method updateShader\\r\\n* @param {String} vertexSource \\r\\n* @param {String} fragmentSource \\r\\n* @param {Object} macros [optional]\\r\\n*/\\r\\nShader.prototype.updateShader = function( vertexSource, fragmentSource, macros )\\r\\n{\\r\\n\\tvar gl = this.gl || global.gl;\\r\\n\\r\\n\\t//expand macros\\r\\n\\tvar extra_code = Shader.expandMacros( macros );\\r\\n\\r\\n\\tif(!this.program)\\r\\n\\t\\tthis.program = gl.createProgram();\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tgl.detachShader( this.program, this.vs_shader );\\r\\n\\t\\tgl.detachShader( this.program, this.fs_shader );\\r\\n\\t}\\r\\n\\r\\n\\tvar extra_code = Shader.expandMacros( macros );\\r\\n\\r\\n\\tvar final_vertexSource = vertexSource.constructor === String ? Shader.injectCode( extra_code, vertexSource, gl ) : vertexSource;\\r\\n\\tvar final_fragmentSource = fragmentSource.constructor === String ? Shader.injectCode( extra_code, fragmentSource, gl ) : fragmentSource;\\r\\n\\r\\n\\tvar vs = vertexSource.constructor === String ? GL.Shader.compileSource( gl.VERTEX_SHADER, final_vertexSource ) : vertexSource;\\r\\n\\tvar fs = fragmentSource.constructor === String ? GL.Shader.compileSource( gl.FRAGMENT_SHADER, final_fragmentSource ) : fragmentSource;\\r\\n\\r\\n\\tgl.attachShader( this.program, vs, gl );\\r\\n\\tgl.attachShader( this.program, fs, gl );\\r\\n\\tgl.linkProgram( this.program );\\r\\n\\tif (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {\\r\\n\\t\\tthrow 'link error: ' + gl.getProgramInfoLog( this.program );\\r\\n\\t}\\r\\n\\r\\n\\t//store shaders separated\\r\\n\\tthis.vs_shader = vs;\\r\\n\\tthis.fs_shader = fs;\\r\\n\\r\\n\\t//Extract info from the shader\\r\\n\\tthis.attributes = {}; \\r\\n\\tthis.uniformInfo = {};\\r\\n\\tthis.samplers = {};\\r\\n\\r\\n\\t//extract info about the shader to speed up future processes\\r\\n\\tthis.extractShaderInfo();\\r\\n}\\r\\n\\r\\n/**\\r\\n* It extract all the info about the compiled shader program, all the info about uniforms and attributes.\\r\\n* This info is stored so it works faster during rendering.\\r\\n* @method extractShaderInfo\\r\\n*/\\r\\n\\r\\n\\r\\nShader.prototype.extractShaderInfo = function()\\r\\n{\\r\\n\\tvar gl = this.gl;\\r\\n\\t\\r\\n\\tvar l = gl.getProgramParameter( this.program, gl.ACTIVE_UNIFORMS );\\r\\n\\r\\n\\t//extract uniforms info\\r\\n\\tfor(var i = 0; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar data = gl.getActiveUniform( this.program, i);\\r\\n\\t\\tif(!data) break;\\r\\n\\r\\n\\t\\tvar uniformName = data.name;\\r\\n\\r\\n\\t\\t//arrays have uniformName[0], strip the [] (also data.size tells you if it is an array)\\r\\n\\t\\tvar pos = uniformName.indexOf(\\\"[\\\"); \\r\\n\\t\\tif(pos != -1)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pos2 = uniformName.indexOf(\\\"].\\\"); //leave array of structs though\\r\\n\\t\\t\\tif(pos2 == -1)\\r\\n\\t\\t\\t\\tuniformName = uniformName.substr(0,pos);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//store texture samplers\\r\\n\\t\\tif(data.type == gl.SAMPLER_2D || data.type == gl.SAMPLER_CUBE || data.type == GL.SAMPLER_3D)\\r\\n\\t\\t\\tthis.samplers[ uniformName ] = data.type;\\r\\n\\t\\t\\r\\n\\t\\t//get which function to call when uploading this uniform\\r\\n\\t\\tvar func = Shader.getUniformFunc(data);\\r\\n\\t\\tvar is_matrix = false;\\r\\n\\t\\tif(data.type == gl.FLOAT_MAT2 || data.type == gl.FLOAT_MAT3 || data.type == gl.FLOAT_MAT4)\\r\\n\\t\\t\\tis_matrix = true;\\r\\n\\t\\tvar type_length = GL.TYPE_LENGTH[ data.type ] || 1;\\r\\n\\r\\n\\t\\t//save the info so the user doesnt have to specify types when uploading data to the shader\\r\\n\\t\\tthis.uniformInfo[ uniformName ] = { \\r\\n\\t\\t\\ttype: data.type,\\r\\n\\t\\t\\tfunc: func,\\r\\n\\t\\t\\tsize: data.size,\\r\\n\\t\\t\\ttype_length: type_length,\\r\\n\\t\\t\\tis_matrix: is_matrix,\\r\\n\\t\\t\\tloc: gl.getUniformLocation(this.program, uniformName),\\r\\n\\t\\t\\tdata: new Float32Array( type_length * data.size ) //prealloc space to assign uniforms that are not typed\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\t//extract attributes info\\r\\n\\tfor(var i = 0, l = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES); i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar data = gl.getActiveAttrib( this.program, i);\\r\\n\\t\\tif(!data)\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tvar func = Shader.getUniformFunc(data);\\r\\n\\t\\tvar type_length = GL.TYPE_LENGTH[ data.type ] || 1;\\r\\n\\t\\tthis.uniformInfo[ data.name ] = { \\r\\n\\t\\t\\ttype: data.type,\\r\\n\\t\\t\\tfunc: func,\\r\\n\\t\\t\\ttype_length: type_length,\\r\\n\\t\\t\\tsize: data.size,\\r\\n\\t\\t\\tloc: null \\r\\n\\t\\t}; //gl.getAttribLocation( this.program, data.name )\\r\\n\\t\\tthis.attributes[ data.name ] = gl.getAttribLocation(this.program, data.name );\\t\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns if this shader has a uniform with the given name\\r\\n* @method hasUniform\\r\\n* @param {String} name name of the uniform\\r\\n* @return {Boolean}\\r\\n*/\\r\\nShader.prototype.hasUniform = function(name)\\r\\n{\\r\\n\\treturn this.uniformInfo[name];\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns if this shader has an attribute with the given name\\r\\n* @method hasAttribute\\r\\n* @param {String} name name of the attribute\\r\\n* @return {Boolean}\\r\\n*/\\r\\nShader.prototype.hasAttribute = function(name)\\r\\n{\\r\\n\\treturn this.attributes[name];\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Tells you which function to call when uploading a uniform according to the data type in the shader\\r\\n* Used internally from extractShaderInfo to optimize calls \\r\\n* @method Shader.getUniformFunc\\r\\n* @param {Object} data info about the uniform\\r\\n* @return {Function}\\r\\n*/\\r\\nShader.getUniformFunc = function( data )\\r\\n{\\r\\n\\tvar func = null;\\r\\n\\tswitch (data.type)\\r\\n\\t{\\r\\n\\t\\tcase GL.FLOAT: \\t\\t\\r\\n\\t\\t\\tif(data.size == 1)\\r\\n\\t\\t\\t\\tfunc = gl.uniform1f; \\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tfunc = gl.uniform1fv; \\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GL.FLOAT_MAT2: func = gl.uniformMatrix2fv; break;\\r\\n\\t\\tcase GL.FLOAT_MAT3:\\tfunc = gl.uniformMatrix3fv; break;\\r\\n\\t\\tcase GL.FLOAT_MAT4:\\tfunc = gl.uniformMatrix4fv; break;\\r\\n\\t\\tcase GL.FLOAT_VEC2: func = gl.uniform2fv; break;\\r\\n\\t\\tcase GL.FLOAT_VEC3: func = gl.uniform3fv; break;\\r\\n\\t\\tcase GL.FLOAT_VEC4: func = gl.uniform4fv; break;\\r\\n\\r\\n\\t\\tcase GL.UNSIGNED_INT: \\r\\n\\t\\tcase GL.INT: \\t \\r\\n\\t\\t\\tif(data.size == 1)\\r\\n\\t\\t\\t\\tfunc = gl.uniform1i; \\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tfunc = gl.uniform1iv; \\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GL.INT_VEC2: func = gl.uniform2iv; break;\\r\\n\\t\\tcase GL.INT_VEC3: func = gl.uniform3iv; break;\\r\\n\\t\\tcase GL.INT_VEC4: func = gl.uniform4iv; break;\\r\\n\\r\\n\\t\\tcase GL.SAMPLER_2D:\\r\\n\\t\\tcase GL.SAMPLER_3D:\\r\\n\\t\\tcase GL.SAMPLER_CUBE:\\r\\n\\t\\t\\tfunc = gl.uniform1i; break;\\r\\n\\t\\tdefault: func = gl.uniform1f; break;\\r\\n\\t}\\t\\r\\n\\treturn func;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Create a shader from two urls. While the system is fetching the two urls, the shader contains a dummy shader that renders black.\\r\\n* @method Shader.fromURL\\r\\n* @param {String} vs_path the url to the vertex shader\\r\\n* @param {String} fs_path the url to the fragment shader\\r\\n* @param {Function} on_complete [Optional] a callback to call once the shader is ready.\\r\\n* @return {Shader}\\r\\n*/\\r\\nShader.fromURL = function( vs_path, fs_path, on_complete )\\r\\n{\\r\\n\\t//create simple shader first\\r\\n\\tvar vs_code = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tattribute mat4 u_mvp;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() { \\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = u_mvp * vec4(a_vertex,1.0); \\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\tvar fs_code = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\t\\r\\n\\tvar shader = new GL.Shader(vs_code, fs_code);\\r\\n\\tshader.ready = false;\\r\\n\\r\\n\\tvar true_vs = null;\\r\\n\\tvar true_fs = null;\\r\\n\\r\\n\\tHttpRequest( vs_path, null, function(vs_code) {\\r\\n\\t\\ttrue_vs = vs_code;\\r\\n\\t\\tif(true_fs)\\r\\n\\t\\t\\tcompileShader();\\r\\n\\t});\\r\\n\\r\\n\\tHttpRequest( fs_path, null, function(fs_code) {\\r\\n\\t\\ttrue_fs = fs_code;\\r\\n\\t\\tif(true_vs)\\r\\n\\t\\t\\tcompileShader();\\r\\n\\t});\\r\\n\\r\\n\\tfunction compileShader()\\r\\n\\t{\\r\\n\\t\\tvar true_shader = new GL.Shader(true_vs, true_fs);\\r\\n\\t\\tfor(var i in true_shader)\\r\\n\\t\\t\\tshader[i] = true_shader[i];\\r\\n\\t\\tshader.ready = true;\\r\\n\\t}\\r\\n\\r\\n\\treturn shader;\\r\\n}\\r\\n\\r\\n/**\\r\\n* enables the shader (calls useProgram)\\r\\n* @method bind\\r\\n*/\\r\\nShader.prototype.bind = function()\\r\\n{\\r\\n\\tvar gl = this.gl;\\r\\n\\tgl.useProgram( this.program );\\r\\n\\tgl._current_shader = this;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the location of a uniform or attribute\\r\\n* @method getLocation\\r\\n* @param {String} name\\r\\n* @return {WebGLUniformLocation} location\\r\\n*/\\r\\nShader.prototype.getLocation = function( name )\\r\\n{\\r\\n\\tvar info = this.uniformInfo[name];\\r\\n\\tif(info)\\r\\n\\t\\treturn this.uniformInfo[name].loc;\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Uploads a set of uniforms to the Shader. You dont need to specify types, they are infered from the shader info.\\r\\n* @method uniforms\\r\\n* @param {Object} uniforms\\r\\n*/\\r\\nShader._temp_uniform = new Float32Array(16);\\r\\n\\r\\nShader.prototype.uniforms = function(uniforms) {\\r\\n\\tvar gl = this.gl;\\r\\n\\tgl.useProgram(this.program);\\r\\n\\tgl._current_shader = this;\\r\\n\\r\\n\\tfor (var name in uniforms)\\r\\n\\t{\\r\\n\\t\\tvar info = this.uniformInfo[ name ];\\r\\n\\t\\tif (!info)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tthis._setUniform( name, uniforms[name] );\\r\\n\\t\\t//this.setUniform( name, uniforms[name] );\\r\\n\\t\\t//this._assing_uniform(uniforms, name, gl );\\r\\n\\t}\\r\\n\\r\\n\\treturn this;\\r\\n}//uniforms\\r\\n\\r\\nShader.prototype.uniformsArray = function(array) {\\r\\n\\tvar gl = this.gl;\\r\\n\\tgl.useProgram( this.program );\\r\\n\\tgl._current_shader = this;\\r\\n\\r\\n\\tfor(var i = 0, l = array.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar uniforms = array[i];\\r\\n\\t\\tfor (var name in uniforms)\\r\\n\\t\\t\\tthis._setUniform( name, uniforms[name] );\\r\\n\\t\\t\\t//this._assing_uniform(uniforms, name, gl );\\r\\n\\t}\\r\\n\\r\\n\\treturn this;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Uploads a uniform to the Shader. You dont need to specify types, they are infered from the shader info. Shader must be binded!\\r\\n* @method setUniform\\r\\n* @param {string} name\\r\\n* @param {*} value\\r\\n*/\\r\\nShader.prototype.setUniform = (function(){\\r\\n\\r\\n\\treturn (function(name, value)\\r\\n\\t{\\r\\n\\t\\tif(\\tthis.gl._current_shader != this )\\r\\n\\t\\t\\tthis.bind();\\r\\n\\r\\n\\t\\tvar info = this.uniformInfo[name];\\r\\n\\t\\tif (!info)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(info.loc === null)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(value == null) //strict?\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(value.constructor === Array)\\r\\n\\t\\t{\\r\\n\\t\\t\\tinfo.data.set( value );\\r\\n\\t\\t\\tvalue = info.data;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(info.is_matrix)\\r\\n\\t\\t\\tinfo.func.call( this.gl, info.loc, false, value );\\r\\n\\t\\telse\\r\\n\\t\\t\\tinfo.func.call( this.gl, info.loc, value );\\r\\n\\t});\\r\\n})();\\r\\n\\r\\n//skips enabling shader\\r\\nShader.prototype._setUniform = (function(){\\r\\n\\r\\n\\treturn (function(name, value)\\r\\n\\t{\\r\\n\\t\\tvar info = this.uniformInfo[ name ];\\r\\n\\t\\tif (!info)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(info.loc === null)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//if(info.loc.constructor !== Function)\\r\\n\\t\\t//\\treturn;\\r\\n\\r\\n\\t\\tif(value == null) \\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(value.constructor === Array)\\r\\n\\t\\t{\\r\\n\\t\\t\\tinfo.data.set( value );\\r\\n\\t\\t\\tvalue = info.data;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(info.is_matrix)\\r\\n\\t\\t\\tinfo.func.call( this.gl, info.loc, false, value );\\r\\n\\t\\telse\\r\\n\\t\\t\\tinfo.func.call( this.gl, info.loc, value );\\r\\n\\t});\\r\\n})();\\r\\n\\r\\n/**\\r\\n* Renders a mesh using this shader, remember to use the function uniforms before to enable the shader\\r\\n* @method draw\\r\\n* @param {Mesh} mesh\\r\\n* @param {number} mode could be gl.LINES, gl.POINTS, gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN\\r\\n* @param {String} index_buffer_name the name of the index buffer, if not provided triangles will be assumed\\r\\n*/\\r\\nShader.prototype.draw = function( mesh, mode, index_buffer_name ) {\\r\\n\\tindex_buffer_name = index_buffer_name === undefined ? (mode == gl.LINES ? 'lines' : 'triangles') : index_buffer_name;\\r\\n\\tthis.drawBuffers( mesh.vertexBuffers,\\r\\n\\t index_buffer_name ? mesh.indexBuffers[ index_buffer_name ] : null,\\r\\n\\t arguments.length < 2 ? gl.TRIANGLES : mode);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Renders a range of a mesh using this shader\\r\\n* @method drawRange\\r\\n* @param {Mesh} mesh\\r\\n* @param {number} mode could be gl.LINES, gl.POINTS, gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN\\r\\n* @param {number} start first primitive to render\\r\\n* @param {number} length number of primitives to render\\r\\n* @param {String} index_buffer_name the name of the index buffer, if not provided triangles will be assumed\\r\\n*/\\r\\nShader.prototype.drawRange = function(mesh, mode, start, length, index_buffer_name )\\r\\n{\\r\\n\\tindex_buffer_name = index_buffer_name === undefined ? (mode == gl.LINES ? 'lines' : 'triangles') : index_buffer_name;\\r\\n\\r\\n\\tthis.drawBuffers( mesh.vertexBuffers,\\r\\n\\t index_buffer_name ? mesh.indexBuffers[ index_buffer_name ] : null,\\r\\n\\t mode, start, length);\\r\\n}\\r\\n\\r\\n/**\\r\\n* render several buffers with a given index buffer\\r\\n* @method drawBuffers\\r\\n* @param {Object} vertexBuffers an object containing all the buffers\\r\\n* @param {IndexBuffer} indexBuffer\\r\\n* @param {number} mode could be gl.LINES, gl.POINTS, gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN\\r\\n* @param {number} range_start first primitive to render\\r\\n* @param {number} range_length number of primitives to render\\r\\n*/\\r\\n\\r\\n//this two variables are a hack to avoid memory allocation on drawCalls\\r\\nvar temp_attribs_array = new Uint8Array(16);\\r\\nvar temp_attribs_array_zero = new Uint8Array(16); //should be filled with zeros always\\r\\n\\r\\nShader.prototype.drawBuffers = function( vertexBuffers, indexBuffer, mode, range_start, range_length )\\r\\n{\\r\\n\\tif(range_length == 0)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar gl = this.gl;\\r\\n\\r\\n\\tgl.useProgram(this.program); //this could be removed assuming every shader is called with some uniforms \\r\\n\\r\\n\\t// enable attributes as necessary.\\r\\n\\tvar length = 0;\\r\\n\\tvar attribs_in_use = temp_attribs_array; //hack to avoid garbage\\r\\n\\tattribs_in_use.set( temp_attribs_array_zero ); //reset\\r\\n\\r\\n\\tfor (var name in vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar buffer = vertexBuffers[name];\\r\\n\\t\\tvar attribute = buffer.attribute || name;\\r\\n\\t\\t//precompute attribute locations in shader\\r\\n\\t\\tvar location = this.attributes[attribute];// || gl.getAttribLocation(this.program, attribute);\\r\\n\\r\\n\\t\\tif (location == null || !buffer.buffer) //-1 changed for null\\r\\n\\t\\t\\tcontinue; //ignore this buffer\\r\\n\\r\\n\\t\\tattribs_in_use[location] = 1; //mark it as used\\r\\n\\r\\n\\t\\t//this.attributes[attribute] = location;\\r\\n\\t\\tgl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer);\\r\\n\\t\\tgl.enableVertexAttribArray(location);\\r\\n\\r\\n\\t\\tgl.vertexAttribPointer(location, buffer.buffer.spacing, buffer.buffer.gl_type, false, 0, 0);\\r\\n\\t\\tlength = buffer.buffer.length / buffer.buffer.spacing;\\r\\n\\t}\\r\\n\\r\\n\\t//range rendering\\r\\n\\tvar offset = 0; //in bytes\\r\\n\\tif(range_start > 0) //render a polygon range\\r\\n\\t\\toffset = range_start; //in bytes (Uint16 == 2 bytes)\\r\\n\\r\\n\\tif (indexBuffer)\\r\\n\\t\\tlength = indexBuffer.buffer.length - offset;\\r\\n\\r\\n\\tif(range_length > 0 && range_length < length) //to avoid problems\\r\\n\\t\\tlength = range_length;\\r\\n\\r\\n\\tvar BYTES_PER_ELEMENT = (indexBuffer && indexBuffer.data) ? indexBuffer.data.constructor.BYTES_PER_ELEMENT : 1;\\r\\n\\toffset *= BYTES_PER_ELEMENT;\\r\\n\\r\\n\\t// Force to disable buffers in this shader that are not in this mesh\\r\\n\\tfor (var attribute in this.attributes)\\r\\n\\t{\\r\\n\\t\\tvar location = this.attributes[attribute];\\r\\n\\t\\tif (!(attribs_in_use[location])) {\\r\\n\\t\\t\\tgl.disableVertexAttribArray(this.attributes[attribute]);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// Draw the geometry.\\r\\n\\tif (length && (!indexBuffer || indexBuffer.buffer)) {\\r\\n\\t if (indexBuffer) {\\r\\n\\t\\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buffer);\\r\\n\\t\\tgl.drawElements( mode, length, indexBuffer.buffer.gl_type, offset); //gl.UNSIGNED_SHORT\\r\\n\\t\\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);\\r\\n\\t } else {\\r\\n\\t\\tgl.drawArrays(mode, offset, length);\\r\\n\\t }\\r\\n\\t}\\r\\n\\r\\n\\treturn this;\\r\\n}\\r\\n\\r\\nShader._instancing_arrays = [];\\r\\n\\r\\nShader.prototype.drawInstanced = function( mesh, primitive, indices, instanced_uniforms, range_start, range_length, num_intances )\\r\\n{\\r\\n\\tif(range_length === 0)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//bind buffers\\r\\n\\tvar gl = this.gl;\\r\\n\\r\\n\\tif( gl.webgl_version == 1 && !gl.extensions.ANGLE_instanced_arrays )\\r\\n\\t\\tthrow(\\\"instancing not supported\\\");\\r\\n\\r\\n\\tgl.useProgram(this.program); //this could be removed assuming every shader is called with some uniforms \\r\\n\\r\\n\\t// enable attributes as necessary.\\r\\n\\tvar length = 0;\\r\\n\\tvar attribs_in_use = temp_attribs_array; //hack to avoid garbage\\r\\n\\tattribs_in_use.set( temp_attribs_array_zero ); //reset\\r\\n\\r\\n\\tvar vertexBuffers = mesh.vertexBuffers;\\r\\n\\r\\n\\tfor (var name in vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar buffer = vertexBuffers[name];\\r\\n\\t\\tvar attribute = buffer.attribute || name;\\r\\n\\t\\t//precompute attribute locations in shader\\r\\n\\t\\tvar location = this.attributes[attribute];// || gl.getAttribLocation(this.program, attribute);\\r\\n\\r\\n\\t\\tif (location == null || !buffer.buffer) //-1 changed for null\\r\\n\\t\\t\\tcontinue; //ignore this buffer\\r\\n\\r\\n\\t\\tattribs_in_use[location] = 1; //mark it as used\\r\\n\\r\\n\\t\\t//this.attributes[attribute] = location;\\r\\n\\t\\tgl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer);\\r\\n\\t\\tgl.enableVertexAttribArray(location);\\r\\n\\r\\n\\t\\tgl.vertexAttribPointer(location, buffer.buffer.spacing, buffer.buffer.gl_type, false, 0, 0);\\r\\n\\t\\tlength = buffer.buffer.length / buffer.buffer.spacing;\\r\\n\\t}\\r\\n\\r\\n\\tvar indexBuffer = indices ? mesh.getIndexBuffer( indices ) : null;\\r\\n\\r\\n\\t//range rendering\\r\\n\\tvar offset = 0; //in bytes\\r\\n\\tif(range_start > 0) //render a polygon range\\r\\n\\t\\toffset = range_start; \\r\\n\\r\\n\\tif (indexBuffer)\\r\\n\\t\\tlength = indexBuffer.buffer.length - offset;\\r\\n\\r\\n\\tif(range_length > 0 && range_length < length) //to avoid problems\\r\\n\\t\\tlength = range_length;\\r\\n\\r\\n\\tvar BYTES_PER_ELEMENT = (indexBuffer && indexBuffer.data) ? indexBuffer.data.constructor.BYTES_PER_ELEMENT : 1;\\r\\n\\toffset *= BYTES_PER_ELEMENT;\\r\\n\\r\\n\\t// Force to disable buffers in this shader that are not in this mesh\\r\\n\\tfor (var attribute in this.attributes)\\r\\n\\t{\\r\\n\\t\\tvar location = this.attributes[attribute];\\r\\n\\t\\tif (!(attribs_in_use[location])) {\\r\\n\\t\\t\\tgl.disableVertexAttribArray(this.attributes[attribute]);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar ext = gl.extensions.ANGLE_instanced_arrays;\\r\\n\\tvar batch_length = 0;\\r\\n\\r\\n\\t//pack the instanced uniforms\\r\\n\\tvar index = 0;\\r\\n\\tfor(var uniform in instanced_uniforms)\\r\\n\\t{\\r\\n\\t\\tvar values = instanced_uniforms[ uniform ];\\r\\n\\t\\tbatch_length = values.length;\\r\\n\\t\\tvar uniformLocation = this.attributes[ uniform ];\\r\\n\\t\\tif( uniformLocation == null )\\r\\n\\t\\t\\treturn; //not found\\r\\n\\t\\tvar element_size = 0;\\r\\n\\t\\tvar total_size = 0;\\r\\n\\t\\tif( values.constructor === Array )\\r\\n\\t\\t{\\r\\n\\t\\t\\telement_size = values[0].constructor === Number ? 1 : values[0].length;\\r\\n\\t\\t\\ttotal_size = element_size * values.length;\\r\\n\\t\\t}\\r\\n\\t\\telse //typed array\\r\\n\\t\\t{\\r\\n\\t\\t\\telement_size = this.uniformInfo[ uniform ].type_length;\\r\\n\\t\\t\\ttotal_size = values.length;\\r\\n\\t\\t\\tbatch_length = total_size / element_size;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar data_array = Shader._instancing_arrays[ index ];\\r\\n\\t\\tif( !data_array || data_array.data.length < total_size )\\r\\n\\t\\t\\tdata_array = Shader._instancing_arrays[ index ] = { data: new Float32Array( total_size ), buffer: gl.createBuffer() };\\r\\n\\t\\tdata_array.uniform = uniform;\\r\\n\\t\\tdata_array.element_size = element_size;\\r\\n\\t\\tif( values.constructor === Array )\\r\\n\\t\\t\\tfor(var j = 0; j < values.length; ++j)\\r\\n\\t\\t\\t\\tdata_array.data.set( values[j], j*element_size ); //flatten array\\r\\n\\t\\telse\\r\\n\\t\\t\\tdata_array.data.set( values ); //copy\\r\\n\\t\\tgl.bindBuffer( gl.ARRAY_BUFFER, data_array.buffer );\\r\\n\\t\\tgl.bufferData( gl.ARRAY_BUFFER, data_array.data, gl.STREAM_DRAW );\\r\\n\\r\\n\\t\\tif(element_size == 16) //mat4\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var k = 0; k < 4; ++k)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tgl.enableVertexAttribArray( uniformLocation+k );\\r\\n\\t\\t\\t\\tgl.vertexAttribPointer( uniformLocation+k, 4, gl.FLOAT , false, 16*4, k*4*4 ); //4 bytes per float\\r\\n\\t\\t\\t\\tif( ext ) //webgl 1\\r\\n\\t\\t\\t\\t\\text.vertexAttribDivisorANGLE( uniformLocation+k, 1 ); // This makes it instanced!\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tgl.vertexAttribDivisor( uniformLocation+k, 1 ); // This makes it instanced!\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse //others\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.enableVertexAttribArray( uniformLocation );\\r\\n\\t\\t\\tgl.vertexAttribPointer( uniformLocation, element_size, gl.FLOAT, false, element_size*4, 0 ); //4 bytes per float, 0 offset\\r\\n\\t\\t\\tif( ext ) //webgl 1\\r\\n\\t\\t\\t\\text.vertexAttribDivisorANGLE( uniformLocation, 1 ); // This makes it instanced!\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.vertexAttribDivisor( uniformLocation, 1 ); // This makes it instanced!\\r\\n\\t\\t}\\r\\n\\t\\tindex+=1;\\r\\n\\t}\\r\\n\\r\\n\\tif( num_intances )\\r\\n\\t\\tbatch_length = num_intances;\\r\\n\\r\\n\\tif( ext ) //webgl 1.0\\r\\n\\t{\\r\\n\\t\\tif(indexBuffer)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buffer);\\r\\n\\t\\t\\text.drawElementsInstancedANGLE( primitive, length, indexBuffer.buffer.gl_type, offset, batch_length );\\r\\n\\t\\t\\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\text.drawArraysInstancedANGLE( primitive, offset, length, batch_length);\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tif(indexBuffer)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buffer);\\r\\n\\t\\t\\tgl.drawElementsInstanced( primitive, length, indexBuffer.buffer.gl_type, offset, batch_length );\\r\\n\\t\\t\\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.drawArraysInstanced( primitive, offset, length, batch_length);\\r\\n\\t}\\r\\n\\r\\n\\t//disable instancing buffers\\r\\n\\tfor(var i = 0; i < index; ++i)\\r\\n\\t{\\r\\n\\t\\tvar info = Shader._instancing_arrays[ i ];\\r\\n\\t\\tvar uniformLocation = this.attributes[ info.uniform ];\\r\\n\\t\\tvar element_size = info.element_size;\\r\\n\\t\\tif( element_size == 16) //mat4\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var k = 0; k < 4; ++k)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tgl.disableVertexAttribArray( uniformLocation+k );\\r\\n\\t\\t\\t\\tif( ext ) //webgl 1\\r\\n\\t\\t\\t\\t\\text.vertexAttribDivisorANGLE( uniformLocation+k, 0 );\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tgl.vertexAttribDivisor( uniformLocation+k, 0 ); \\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse //others\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.enableVertexAttribArray( uniformLocation );\\r\\n\\t\\t\\tif( ext ) //webgl 1\\r\\n\\t\\t\\t\\text.vertexAttribDivisorANGLE( uniformLocation, 0 );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.vertexAttribDivisor( uniformLocation, 0 );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn this;\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Given a source code with the directive #import it expands it inserting the code using Shader.files to fetch for import files.\\r\\n* Warning: Imports are evaluated only the first inclusion, the rest are ignored to avoid double inclusion of functions\\r\\n* Also, imports cannot have other imports inside.\\r\\n* @method Shader.expandImports\\r\\n* @param {String} code the source code\\r\\n* @param {Object} files [Optional] object with files to import from (otherwise Shader.files is used)\\r\\n* @return {String} the code with the lines #import removed and replaced by the code\\r\\n*/\\r\\nShader.expandImports = function(code, files)\\r\\n{\\r\\n\\tfiles = files || Shader.files;\\r\\n\\r\\n\\tvar already_imported = {}; //avoid to import two times the same code\\r\\n\\tif( !files )\\r\\n\\t\\tthrow(\\\"Shader.files not initialized, assign files there\\\");\\r\\n\\r\\n\\tvar replace_import = function(v)\\r\\n\\t{\\r\\n\\t\\tvar token = v.split(\\\"\\\\\\\"\\\");\\r\\n\\t\\tvar id = token[1];\\r\\n\\t\\tif( already_imported[id] )\\r\\n\\t\\t\\treturn \\\"//already imported: \\\" + id + \\\"\\\\n\\\";\\r\\n\\t\\tvar file = files[id];\\r\\n\\t\\talready_imported[ id ] = true;\\r\\n\\t\\tif(file)\\r\\n\\t\\t\\treturn file + \\\"\\\\n\\\";\\r\\n\\t\\treturn \\\"//import code not found: \\\" + id + \\\"\\\\n\\\";\\r\\n\\t}\\r\\n\\r\\n\\t//return code.replace(/#import\\\\s+\\\\\\\"(\\\\w+)\\\\\\\"\\\\s*\\\\n/g, replace_import );\\r\\n\\treturn code.replace(/#import\\\\s+\\\\\\\"([a-zA-Z0-9_\\\\.]+)\\\\\\\"\\\\s*\\\\n/g, replace_import );\\r\\n}\\r\\n\\r\\nShader.dumpErrorToConsole = function(err, vscode, fscode)\\r\\n{\\r\\n\\tconsole.error(err);\\r\\n\\tvar msg = err.msg;\\r\\n\\tvar code = null;\\r\\n\\tif(err.indexOf(\\\"Fragment\\\") != -1)\\r\\n\\t\\tcode = fscode;\\r\\n\\telse\\r\\n\\t\\tcode = vscode;\\r\\n\\r\\n\\tvar lines = code.split(\\\"\\\\n\\\");\\r\\n\\tfor(var i in lines)\\r\\n\\t\\tlines[i] = i + \\\"| \\\" + lines[i];\\r\\n\\r\\n\\tconsole.groupCollapsed(\\\"Shader code\\\");\\r\\n\\tconsole.log( lines.join(\\\"\\\\n\\\") );\\r\\n\\tconsole.groupEnd();\\r\\n}\\r\\n\\r\\nShader.convertTo100 = function(code,type)\\r\\n{\\r\\n\\t//in VERTEX\\r\\n\\t\\t//change in for attribute\\r\\n\\t\\t//change out for varying\\r\\n\\t\\t//add #extension GL_OES_standard_derivatives\\r\\n\\t//in FRAGMENT\\r\\n\\t\\t//change in for varying \\r\\n\\t\\t//remove out vec4 _gl_FragColor\\r\\n\\t\\t//rename _gl_FragColor for gl_FragColor\\r\\n\\t//in both\\r\\n\\t\\t//change #version 300 es for #version 100\\r\\n\\t\\t//replace 'texture(' for 'texture2D('\\r\\n}\\r\\n\\r\\n\\r\\nShader.convertTo300 = function(code,type)\\r\\n{\\r\\n\\t//in VERTEX\\r\\n\\t\\t//change attribute for in\\r\\n\\t\\t//change varying for out\\r\\n\\t\\t//remove #extension GL_OES_standard_derivatives\\r\\n\\t//in FRAGMENT\\r\\n\\t\\t//change varying for in\\r\\n\\t\\t//rename gl_FragColor for _gl_FragColor\\r\\n\\t\\t//rename gl_FragData[0] for _gl_FragColor\\r\\n\\t\\t//add out vec4 _gl_FragColor\\r\\n\\t//in both\\r\\n\\t\\t//replace texture2D for texture\\r\\n}\\r\\n\\r\\n//helps to check if a variable value is valid to an specific uniform in a shader\\r\\nShader.validateValue = function( value, uniform_info )\\r\\n{\\r\\n\\tif(value === null || value === undefined)\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\tswitch (uniform_info.type)\\r\\n\\t{\\r\\n\\t\\t//used to validate shaders\\r\\n\\t\\tcase GL.INT: \\r\\n\\t\\tcase GL.FLOAT: \\r\\n\\t\\tcase GL.SAMPLER_2D: \\r\\n\\t\\tcase GL.SAMPLER_CUBE: \\r\\n\\t\\t\\treturn isNumber(value);\\r\\n\\t\\tcase GL.INT_VEC2: \\r\\n\\t\\tcase GL.FLOAT_VEC2:\\r\\n\\t\\t\\treturn value.length === 2;\\r\\n\\t\\tcase GL.INT_VEC3: \\r\\n\\t\\tcase GL.FLOAT_VEC3:\\r\\n\\t\\t\\treturn value.length === 3;\\r\\n\\t\\tcase GL.INT_VEC4: \\r\\n\\t\\tcase GL.FLOAT_VEC4:\\r\\n\\t\\tcase GL.FLOAT_MAT2:\\r\\n\\t\\t\\t return value.length === 4;\\r\\n\\t\\tcase GL.FLOAT_MAT3:\\r\\n\\t\\t\\t return value.length === 8;\\r\\n\\t\\tcase GL.FLOAT_MAT4:\\r\\n\\t\\t\\t return value.length === 16;\\r\\n\\t}\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n//**************** SHADERS ***********************************\\r\\n\\r\\nShader.DEFAULT_VERTEX_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_normal;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec2 a_coord;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_position;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_normal;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_model;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_mvp;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tv_position = (u_model * vec4(a_vertex,1.0)).xyz;\\\\n\\\\\\r\\n\\t\\t\\t\\tv_normal = (u_model * vec4(a_normal,0.0)).xyz;\\\\n\\\\\\r\\n\\t\\t\\t\\tv_coord = a_coord;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = u_mvp * vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\nShader.SCREEN_VERTEX_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec2 a_coord;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() { \\\\n\\\\\\r\\n\\t\\t\\t\\tv_coord = a_coord; \\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = vec4(a_coord * 2.0 - 1.0, 0.0, 1.0); \\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\nShader.SCREEN_FRAGMENT_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n//used in createFX\\r\\nShader.SCREEN_FRAGMENT_FX = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\t#ifdef FX_UNIFORMS\\\\n\\\\\\r\\n\\t\\t\\t\\tFX_UNIFORMS\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 uv = v_coord;\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D(u_texture, uv);\\\\n\\\\\\r\\n\\t\\t\\t\\t#ifdef FX_CODE\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tFX_CODE ;\\\\n\\\\\\r\\n\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\nShader.SCREEN_COLORED_FRAGMENT_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = u_color * texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\nShader.BLEND_FRAGMENT_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture2;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_factor;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = mix( texture2D(u_texture, v_coord), texture2D(u_texture2, v_coord), u_factor);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n//used to paint quads\\r\\nShader.QUAD_VERTEX_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec2 a_coord;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_position;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_size;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat3 u_transform;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() { \\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 pos = vec3(u_position + vec2(a_coord.x,1.0 - a_coord.y) * u_size, 1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tv_coord = a_coord; \\\\n\\\\\\r\\n\\t\\t\\t\\tpos = u_transform * pos;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.z = 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t//normalize\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.x = (2.0 * pos.x / u_viewport.x) - 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.y = -((2.0 * pos.y / u_viewport.y) - 1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = vec4(pos, 1.0); \\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\nShader.QUAD_FRAGMENT_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = u_color * texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n//used to render partially a texture\\r\\nShader.QUAD2_FRAGMENT_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_texture_area;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t vec2 uv = vec2( mix(u_texture_area.x, u_texture_area.z, v_coord.x), 1.0 - mix(u_texture_area.w, u_texture_area.y, v_coord.y) );\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = u_color * texture2D(u_texture, uv);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\nShader.PRIMITIVE2D_VERTEX_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat3 u_transform;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() { \\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 pos = a_vertex;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos = u_transform * pos;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.z = 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t//normalize\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.x = (2.0 * pos.x / u_viewport.x) - 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tpos.y = -((2.0 * pos.y / u_viewport.y) - 1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = vec4(pos, 1.0); \\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\nShader.FLAT_VERTEX_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_mvp;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() { \\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = u_mvp * vec4(a_vertex,1.0); \\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\nShader.FLAT_FRAGMENT_SHADER = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = u_color;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\nShader.SCREEN_FLAT_FRAGMENT_SHADER = Shader.FLAT_FRAGMENT_SHADER; //legacy\\r\\n\\r\\n\\r\\n/**\\r\\n* Allows to create a simple shader meant to be used to process a texture, instead of having to define the generic Vertex & Fragment Shader code\\r\\n* @method Shader.createFX\\r\\n* @param {string} code string containg code, like \\\"color = color * 2.0;\\\"\\r\\n* @param {string} [uniforms=null] string containg extra uniforms, like \\\"uniform vec3 u_pos;\\\"\\r\\n*/\\r\\nShader.createFX = function(code, uniforms, shader)\\r\\n{\\r\\n\\t//remove comments\\r\\n\\tcode = GL.Shader.removeComments( code, true ); //remove comments and breaklines to avoid problems with the macros\\r\\n\\tvar macros = {\\r\\n\\t\\tFX_CODE: code,\\r\\n\\t\\tFX_UNIFORMS: uniforms || \\\"\\\"\\r\\n\\t}\\r\\n\\tif(!shader)\\r\\n\\t\\treturn new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, GL.Shader.SCREEN_FRAGMENT_FX, macros );\\r\\n\\tshader.updateShader( GL.Shader.SCREEN_VERTEX_SHADER, GL.Shader.SCREEN_FRAGMENT_FX, macros );\\r\\n\\treturn shader;\\r\\n}\\r\\n\\r\\nShader.removeComments = function(code, one_line)\\r\\n{\\r\\n\\tif(!code)\\r\\n\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\tvar rx = /(\\\\/\\\\*([^*]|[\\\\r\\\\n]|(\\\\*+([^*\\\\/]|[\\\\r\\\\n])))*\\\\*+\\\\/)|(\\\\/\\\\/.*)/g;\\r\\n\\tvar code = code.replace( rx ,\\\"\\\");\\r\\n\\tvar lines = code.split(\\\"\\\\n\\\");\\r\\n\\tvar result = [];\\r\\n\\tfor(var i = 0; i < lines.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar line = lines[i]; \\r\\n\\t\\tvar pos = line.indexOf(\\\"//\\\");\\r\\n\\t\\tif(pos != -1)\\r\\n\\t\\t\\tline = lines[i].substr(0,pos);\\r\\n\\t\\tline = line.trim();\\r\\n\\t\\tif(line.length)\\r\\n\\t\\t\\tresult.push(line);\\r\\n\\t}\\r\\n\\treturn result.join( one_line ? \\\"\\\" : \\\"\\\\n\\\" );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Renders a fullscreen quad with this shader applied\\r\\n* @method toViewport\\r\\n* @param {object} uniforms\\r\\n*/\\r\\nShader.prototype.toViewport = function(uniforms)\\r\\n{\\r\\n\\tvar mesh = GL.Mesh.getScreenQuad();\\r\\n\\tif(uniforms)\\r\\n\\t\\tthis.uniforms(uniforms);\\r\\n\\tthis.draw( mesh );\\r\\n}\\r\\n\\r\\n//Now some common shaders everybody needs\\r\\n\\r\\n/**\\r\\n* Returns a shader ready to render a textured quad in fullscreen, use with Mesh.getScreenQuad() mesh\\r\\n* shader params: sampler2D u_texture\\r\\n* @method Shader.getScreenShader\\r\\n*/\\r\\nShader.getScreenShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":screen\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\tshader = gl.shaders[\\\":screen\\\"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.SCREEN_FRAGMENT_SHADER );\\r\\n\\treturn shader.uniforms({u_texture:0}); //do it the first time so I dont have to do it every time\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a shader ready to render a flat color quad in fullscreen, use with Mesh.getScreenQuad() mesh\\r\\n* shader params: vec4 u_color\\r\\n* @method Shader.getFlatScreenShader\\r\\n*/\\r\\nShader.getFlatScreenShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":flat_screen\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\tshader = gl.shaders[\\\":flat_screen\\\"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.FLAT_FRAGMENT_SHADER );\\r\\n\\treturn shader.uniforms({u_color:[1,1,1,1]}); //do it the first time so I dont have to do it every time\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a shader ready to render a colored textured quad in fullscreen, use with Mesh.getScreenQuad() mesh\\r\\n* shader params vec4 u_color and sampler2D u_texture\\r\\n* @method Shader.getColoredScreenShader\\r\\n*/\\r\\nShader.getColoredScreenShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":colored_screen\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\tshader = gl.shaders[\\\":colored_screen\\\"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.SCREEN_COLORED_FRAGMENT_SHADER );\\r\\n\\treturn shader.uniforms({u_texture:0, u_color: vec4.fromValues(1,1,1,1) }); //do it the first time so I dont have to do it every time\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a shader ready to render a quad with transform, use with Mesh.getScreenQuad() mesh\\r\\n* shader must have: u_position, u_size, u_viewport, u_transform (mat3)\\r\\n* @method Shader.getQuadShader\\r\\n*/\\r\\nShader.getQuadShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":quad\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\treturn gl.shaders[\\\":quad\\\"] = new GL.Shader( Shader.QUAD_VERTEX_SHADER, Shader.QUAD_FRAGMENT_SHADER );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a shader ready to render part of a texture into the viewport\\r\\n* shader must have: u_position, u_size, u_viewport, u_transform, u_texture_area (vec4)\\r\\n* @method Shader.getPartialQuadShader\\r\\n*/\\r\\nShader.getPartialQuadShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":quad2\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\treturn gl.shaders[\\\":quad2\\\"] = new GL.Shader( Shader.QUAD_VERTEX_SHADER, Shader.QUAD2_FRAGMENT_SHADER );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a shader that blends two textures\\r\\n* shader must have: u_factor, u_texture, u_texture2\\r\\n* @method Shader.getBlendShader\\r\\n*/\\r\\nShader.getBlendShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":blend\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\treturn gl.shaders[\\\":blend\\\"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.BLEND_FRAGMENT_SHADER );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a shader used to apply gaussian blur to one texture in one axis (you should use it twice to get a gaussian blur)\\r\\n* shader params are: vec2 u_offset, float u_intensity\\r\\n* @method Shader.getBlurShader\\r\\n*/\\r\\nShader.getBlurShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":blur\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\r\\n\\tvar shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,\\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_offset;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_intensity;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t vec4 sum = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t\\t sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\\\\n\\\\\\r\\n\\t\\t\\t sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\\\\n\\\\\\r\\n\\t\\t\\t sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\\\\n\\\\\\r\\n\\t\\t\\t sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\\\\n\\\\\\r\\n\\t\\t\\t sum += texture2D(u_texture, v_coord) * 0.16/0.98;\\\\n\\\\\\r\\n\\t\\t\\t sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\\\\n\\\\\\r\\n\\t\\t\\t sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\\\\n\\\\\\r\\n\\t\\t\\t sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\\\\n\\\\\\r\\n\\t\\t\\t sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = u_intensity * sum;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\");\\r\\n\\treturn gl.shaders[\\\":blur\\\"] = shader;\\r\\n}\\r\\n\\r\\n//shader to copy a depth texture into another one\\r\\nShader.getCopyDepthShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":copy_depth\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\r\\n\\tvar shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,\\\"\\\\n\\\\\\r\\n\\t\\t\\t#extension GL_EXT_frag_depth : enable\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t gl_FragDepthEXT = texture2D( u_texture, v_coord ).x;\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = vec4(1.0);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\");\\r\\n\\treturn gl.shaders[\\\":copy_depth\\\"] = shader;\\r\\n}\\r\\n\\r\\nShader.getCubemapShowShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":show_cubemap\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\r\\n\\tvar shader = new GL.Shader( Shader.DEFAULT_VERTEX_SHADER,\\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_normal;\\\\n\\\\\\r\\n\\t\\t\\tuniform samplerCube u_texture;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = textureCube( u_texture, v_normal );\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\");\\r\\n\\tshader.uniforms({u_texture:0});\\r\\n\\treturn gl.shaders[\\\":show_cubemap\\\"] = shader;\\r\\n}\\r\\n\\r\\n//shader to copy a cubemap into another \\r\\nShader.getPolarToCubemapShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":polar_to_cubemap\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\r\\n\\tvar shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,\\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat3 u_rotation;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 uv = vec2( v_coord.x, 1.0 - v_coord.y );\\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 dir = normalize( vec3( uv - vec2(0.5), 0.5 ));\\\\n\\\\\\r\\n\\t\\t\\t\\tdir = u_rotation * dir;\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat u = atan(dir.x,dir.z) / 6.28318531;\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat v = (asin(dir.y) / 1.57079633) * 0.5 + 0.5;\\\\n\\\\\\r\\n\\t\\t\\t\\tu = mod(u,1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tv = mod(v,1.0);\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = texture2D( u_texture, vec2(u,v) );\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\");\\r\\n\\treturn gl.shaders[\\\":polar_to_cubemap\\\"] = shader;\\r\\n}\\r\\n\\r\\n\\r\\n//shader to copy a cubemap into another \\r\\nShader.getCubemapCopyShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":copy_cubemap\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\r\\n\\tvar shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,\\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform samplerCube u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat3 u_rotation;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 uv = vec2( v_coord.x, 1.0 - v_coord.y );\\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 dir = vec3( uv - vec2(0.5), 0.5 );\\\\n\\\\\\r\\n\\t\\t\\t\\tdir = u_rotation * dir;\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = textureCube( u_texture, dir );\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\");\\r\\n\\treturn gl.shaders[\\\":copy_cubemap\\\"] = shader;\\r\\n}\\r\\n\\r\\n//shader to blur a cubemap\\r\\nShader.getCubemapBlurShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":blur_cubemap\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\r\\n\\tvar shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,\\\"\\\\n\\\\\\r\\n\\t\\t\\t#ifndef NUM_SAMPLES\\\\n\\\\\\r\\n\\t\\t\\t\\t#define NUM_SAMPLES 4\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform samplerCube u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat3 u_rotation;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_offset;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_intensity;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 sum = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 uv = vec2( v_coord.x, 1.0 - v_coord.y ) - vec2(0.5);\\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 dir = vec3(0.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tfor( int x = -2; x <= 2; x++ )\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tfor( int y = -2; y <= 2; y++ )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tdir.xy = uv + vec2( u_offset.x * float(x), u_offset.y * float(y)) * 0.5;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tdir.z = 0.5;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tdir = u_rotation * dir;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tcolor = textureCube( u_texture, dir );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tcolor.xyz = color.xyz * color.xyz;/*linearize*/\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tsum += color;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\tsum /= 25.0;\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = vec4( sqrt( sum.xyz ), sum.w ) ;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\");\\r\\n\\treturn gl.shaders[\\\":blur_cubemap\\\"] = shader;\\r\\n}\\r\\n\\r\\n//shader to do FXAA (antialiasing)\\r\\nShader.FXAA_FUNC = \\\"\\\\n\\\\\\r\\n\\tuniform vec2 u_viewportSize;\\\\n\\\\\\r\\n\\tuniform vec2 u_iViewportSize;\\\\n\\\\\\r\\n\\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\\\\n\\\\\\r\\n\\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\\\\n\\\\\\r\\n\\t#define FXAA_SPAN_MAX 8.0\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\\\\n\\\\\\r\\n\\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tvec4 color = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t/*vec2 u_iViewportSize = vec2(1.0 / u_viewportSize.x, 1.0 / u_viewportSize.y);*/\\\\n\\\\\\r\\n\\t\\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * u_iViewportSize).xyz;\\\\n\\\\\\r\\n\\t\\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * u_iViewportSize).xyz;\\\\n\\\\\\r\\n\\t\\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * u_iViewportSize).xyz;\\\\n\\\\\\r\\n\\t\\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * u_iViewportSize).xyz;\\\\n\\\\\\r\\n\\t\\tvec3 rgbM = texture2D(tex, fragCoord * u_iViewportSize).xyz;\\\\n\\\\\\r\\n\\t\\tvec3 luma = vec3(0.299, 0.587, 0.114);\\\\n\\\\\\r\\n\\t\\tfloat lumaNW = dot(rgbNW, luma);\\\\n\\\\\\r\\n\\t\\tfloat lumaNE = dot(rgbNE, luma);\\\\n\\\\\\r\\n\\t\\tfloat lumaSW = dot(rgbSW, luma);\\\\n\\\\\\r\\n\\t\\tfloat lumaSE = dot(rgbSE, luma);\\\\n\\\\\\r\\n\\t\\tfloat lumaM = dot(rgbM, luma);\\\\n\\\\\\r\\n\\t\\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\\\\n\\\\\\r\\n\\t\\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvec2 dir;\\\\n\\\\\\r\\n\\t\\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\\\\n\\\\\\r\\n\\t\\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\\\\n\\\\\\r\\n\\t\\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * u_iViewportSize;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * u_iViewportSize + dir * (1.0 / 3.0 - 0.5)).xyz + \\\\n\\\\\\r\\n\\t\\t\\ttexture2D(tex, fragCoord * u_iViewportSize + dir * (2.0 / 3.0 - 0.5)).xyz);\\\\n\\\\\\r\\n\\t\\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * u_iViewportSize + dir * -0.5).xyz + \\\\n\\\\\\r\\n\\t\\t\\ttexture2D(tex, fragCoord * u_iViewportSize + dir * 0.5).xyz);\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\treturn vec4(rgbA,1.0);\\\\n\\\\\\r\\n\\t\\tfloat lumaB = dot(rgbB, luma);\\\\n\\\\\\r\\n\\t\\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\\\\n\\\\\\r\\n\\t\\t\\tcolor = vec4(rgbA, 1.0);\\\\n\\\\\\r\\n\\t\\telse\\\\n\\\\\\r\\n\\t\\t\\tcolor = vec4(rgbB, 1.0);\\\\n\\\\\\r\\n\\t\\treturn color;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\n/**\\r\\n* Returns a shader to apply FXAA antialiasing\\r\\n* params are vec2 u_viewportSize, vec2 u_iViewportSize or you can call shader.setup()\\r\\n* @method Shader.getFXAAShader\\r\\n*/\\r\\nShader.getFXAAShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":fxaa\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\r\\n\\tvar shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,\\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\t\\\" + Shader.FXAA_FUNC + \\\"\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = applyFXAA( u_texture, v_coord * u_viewportSize) ;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\");\\r\\n\\r\\n\\tvar viewport = vec2.fromValues( gl.viewport_data[2], gl.viewport_data[3] );\\r\\n\\tvar iviewport = vec2.fromValues( 1/gl.viewport_data[2], 1/gl.viewport_data[3] );\\r\\n\\r\\n\\tshader.setup = function() {\\r\\n\\t\\tviewport[0] = gl.viewport_data[2];\\r\\n\\t\\tviewport[1] = gl.viewport_data[3];\\r\\n\\t\\tiviewport[0] = 1/gl.viewport_data[2];\\r\\n\\t\\tiviewport[1] = 1/gl.viewport_data[3];\\r\\n\\t\\tthis.uniforms({ u_viewportSize: viewport, u_iViewportSize: iviewport });\\t\\r\\n\\t}\\r\\n\\treturn gl.shaders[\\\":fxaa\\\"] = shader;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a flat shader (useful to render lines)\\r\\n* @method Shader.getFlatShader\\r\\n*/\\r\\nShader.getFlatShader = function(gl)\\r\\n{\\r\\n\\tgl = gl || global.gl;\\r\\n\\tvar shader = gl.shaders[\\\":flat\\\"];\\r\\n\\tif(shader)\\r\\n\\t\\treturn shader;\\r\\n\\r\\n\\tvar shader = new GL.Shader( Shader.FLAT_VERTEX_SHADER,Shader.FLAT_FRAGMENT_SHADER);\\r\\n\\tshader.uniforms({u_color:[1,1,1,1]});\\r\\n\\treturn gl.shaders[\\\":flat\\\"] = shader;\\r\\n}\\r\\n\\r\\n/**\\r\\n* The global scope that contains all the classes from LiteGL and also all the enums of WebGL so you dont need to create a context to use the values.\\r\\n* @class GL\\r\\n*/\\r\\n\\r\\n/**\\r\\n* creates a new WebGL context (it can create the canvas or use an existing one)\\r\\n* @method create\\r\\n* @param {Object} options supported are: \\r\\n* - width\\r\\n* - height\\r\\n* - canvas\\r\\n* - container (string or element)\\r\\n* @return {WebGLRenderingContext} webgl context with all the extra functions (check gl in the doc for more info)\\r\\n*/\\r\\nGL.create = function(options) {\\r\\n\\toptions = options || {};\\r\\n\\tvar canvas = null;\\r\\n\\tif(options.canvas)\\r\\n\\t{\\r\\n\\t\\tif(typeof(options.canvas) == \\\"string\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tcanvas = document.getElementById( options.canvas );\\r\\n\\t\\t\\tif(!canvas) throw(\\\"Canvas element not found: \\\" + options.canvas );\\r\\n\\t\\t}\\r\\n\\t\\telse \\r\\n\\t\\t\\tcanvas = options.canvas;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tvar root = null;\\r\\n\\t\\tif(options.container)\\r\\n\\t\\t\\troot = options.container.constructor === String ? document.querySelector( options.container ) : options.container;\\r\\n\\t\\tif(root && !options.width)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar rect = root.getBoundingClientRect();\\r\\n\\t\\t\\toptions.width = rect.width;\\r\\n\\t\\t\\toptions.height = rect.height;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tcanvas = createCanvas( options.width || 800, options.height || 600 );\\r\\n\\t\\tif(root)\\r\\n\\t\\t\\troot.appendChild(canvas);\\r\\n\\t}\\r\\n\\r\\n\\tif (!('alpha' in options)) options.alpha = false;\\r\\n\\r\\n\\r\\n\\t/**\\r\\n\\t* the webgl context returned by GL.create, its a WebGLRenderingContext with some extra methods added\\r\\n\\t* @class gl\\r\\n\\t*/\\r\\n\\tvar gl = null;\\r\\n\\r\\n\\tvar seq = null;\\r\\n\\tif(options.version == 2)\\t\\r\\n\\t\\tseq = ['webgl2','experimental-webgl2'];\\r\\n\\telse if(options.version == 1 || options.version === undefined) //default\\r\\n\\t\\tseq = ['webgl','experimental-webgl'];\\r\\n\\telse if(options.version === 0) //latest\\r\\n\\t\\tseq = ['webgl2','experimental-webgl2','webgl','experimental-webgl'];\\r\\n\\r\\n\\tif(!seq)\\r\\n\\t\\tthrow 'Incorrect WebGL version, must be 1 or 2';\\r\\n\\r\\n\\tvar context_options = {\\r\\n\\t\\talpha: options.alpha === undefined ? true : options.alpha,\\r\\n\\t\\tdepth: options.depth === undefined ? true : options.depth,\\r\\n\\t\\tstencil: options.stencil === undefined ? true : options.stencil,\\r\\n\\t\\tantialias: options.antialias === undefined ? true : options.antialias,\\r\\n\\t\\tpremultipliedAlpha: options.premultipliedAlpha === undefined ? true : options.premultipliedAlpha,\\r\\n\\t\\tpreserveDrawingBuffer: options.preserveDrawingBuffer === undefined ? true : options.preserveDrawingBuffer\\r\\n\\t};\\r\\n\\r\\n\\tfor(var i = 0; i < seq.length; ++i)\\r\\n\\t{\\r\\n\\t\\ttry { gl = canvas.getContext( seq[i], context_options ); } catch (e) {}\\r\\n\\t\\tif(gl)\\r\\n\\t\\t\\tbreak;\\r\\n\\t}\\r\\n\\r\\n\\tif (!gl)\\r\\n\\t{\\r\\n\\t\\tif( canvas.getContext( \\\"webgl\\\" ) )\\r\\n\\t\\t\\tthrow 'WebGL supported but not with those parameters';\\r\\n\\t\\tthrow 'WebGL not supported';\\r\\n\\t}\\r\\n\\r\\n\\t//context globals\\r\\n\\tgl.webgl_version = gl.constructor.name === \\\"WebGL2RenderingContext\\\" ? 2 : 1;\\r\\n\\tglobal.gl = gl;\\r\\n\\tcanvas.is_webgl = true;\\r\\n\\tcanvas.gl = gl;\\r\\n\\tgl.context_id = this.last_context_id++;\\r\\n\\r\\n\\t//get some common extensions for webgl 1\\r\\n\\tgl.extensions = {};\\r\\n\\tgl.extensions[\\\"OES_standard_derivatives\\\"] = gl.derivatives_supported = gl.getExtension('OES_standard_derivatives') || false;\\r\\n\\tgl.extensions[\\\"WEBGL_depth_texture\\\"] = gl.getExtension(\\\"WEBGL_depth_texture\\\") || gl.getExtension(\\\"WEBKIT_WEBGL_depth_texture\\\") || gl.getExtension(\\\"MOZ_WEBGL_depth_texture\\\");\\r\\n\\tgl.extensions[\\\"OES_element_index_uint\\\"] = gl.getExtension(\\\"OES_element_index_uint\\\");\\r\\n\\tgl.extensions[\\\"WEBGL_draw_buffers\\\"] = gl.getExtension(\\\"WEBGL_draw_buffers\\\");\\r\\n\\tgl.extensions[\\\"EXT_shader_texture_lod\\\"] = gl.getExtension(\\\"EXT_shader_texture_lod\\\");\\r\\n\\tgl.extensions[\\\"EXT_sRGB\\\"] = gl.getExtension(\\\"EXT_sRGB\\\");\\r\\n\\tgl.extensions[\\\"EXT_texture_filter_anisotropic\\\"] = gl.getExtension(\\\"EXT_texture_filter_anisotropic\\\") || gl.getExtension(\\\"WEBKIT_EXT_texture_filter_anisotropic\\\") || gl.getExtension(\\\"MOZ_EXT_texture_filter_anisotropic\\\");\\r\\n\\tgl.extensions[\\\"EXT_frag_depth\\\"] = gl.getExtension(\\\"EXT_frag_depth\\\") || gl.getExtension(\\\"WEBKIT_EXT_frag_depth\\\") || gl.getExtension(\\\"MOZ_EXT_frag_depth\\\");\\r\\n\\tgl.extensions[\\\"WEBGL_lose_context\\\"] = gl.getExtension(\\\"WEBGL_lose_context\\\") || gl.getExtension(\\\"WEBKIT_WEBGL_lose_context\\\") || gl.getExtension(\\\"MOZ_WEBGL_lose_context\\\");\\r\\n\\tgl.extensions[\\\"ANGLE_instanced_arrays\\\"] = gl.getExtension(\\\"ANGLE_instanced_arrays\\\");\\r\\n\\tgl.extensions[\\\"disjoint_timer_query\\\"] = gl.getExtension(\\\"EXT_disjoint_timer_query\\\");\\r\\n\\r\\n\\t//for float textures\\r\\n\\tgl.extensions[\\\"OES_texture_float_linear\\\"] = gl.getExtension(\\\"OES_texture_float_linear\\\");\\r\\n\\tif(gl.extensions[\\\"OES_texture_float_linear\\\"])\\r\\n\\t\\tgl.extensions[\\\"OES_texture_float\\\"] = gl.getExtension(\\\"OES_texture_float\\\");\\r\\n\\tgl.extensions[\\\"EXT_color_buffer_float\\\"] = gl.getExtension(\\\"EXT_color_buffer_float\\\");\\r\\n\\r\\n\\t//for half float textures in webgl 1 require extension\\r\\n\\tgl.extensions[\\\"OES_texture_half_float_linear\\\"] = gl.getExtension(\\\"OES_texture_half_float_linear\\\");\\r\\n\\tif(gl.extensions[\\\"OES_texture_half_float_linear\\\"])\\r\\n\\t\\tgl.extensions[\\\"OES_texture_half_float\\\"] = gl.getExtension(\\\"OES_texture_half_float\\\");\\r\\n\\r\\n\\tif( gl.webgl_version == 1 )\\r\\n\\t\\tgl.HIGH_PRECISION_FORMAT = gl.extensions[\\\"OES_texture_half_float\\\"] ? GL.HALF_FLOAT_OES : (gl.extensions[\\\"OES_texture_float\\\"] ? GL.FLOAT : GL.UNSIGNED_BYTE); //because Firefox dont support half float\\r\\n\\telse\\r\\n\\t\\tgl.HIGH_PRECISION_FORMAT = GL.HALF_FLOAT_OES;\\r\\n\\r\\n\\tgl.max_texture_units = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);\\r\\n\\r\\n\\t//viewport hack to retrieve it without using getParameter (which is slow and generates garbage)\\r\\n\\tif(!gl._viewport_func)\\r\\n\\t{\\r\\n\\t\\tgl._viewport_func = gl.viewport;\\r\\n\\t\\tgl.viewport_data = new Float32Array([0,0,gl.canvas.width,gl.canvas.height]); //32000 max viewport, I guess its fine\\r\\n\\t\\tgl.viewport = function(a,b,c,d) { var v = this.viewport_data; v[0] = a|0; v[1] = b|0; v[2] = c|0; v[3] = d|0; this._viewport_func(a,b,c,d); }\\r\\n\\t\\tgl.getViewport = function(v) { \\r\\n\\t\\t\\tif(v) { v[0] = gl.viewport_data[0]; v[1] = gl.viewport_data[1]; v[2] = gl.viewport_data[2]; v[3] = gl.viewport_data[3]; return v; }\\r\\n\\t\\t\\treturn new Float32Array( gl.viewport_data );\\r\\n\\t\\t};\\r\\n\\t\\tgl.setViewport = function( v, flip_y ) {\\r\\n\\t\\t\\tgl.viewport_data.set(v);\\r\\n\\t\\t\\tif(flip_y)\\r\\n\\t\\t\\t\\tgl.viewport_data[1] = this.drawingBufferHeight-v[1]-v[3];\\r\\n\\t\\t\\tthis._viewport_func(v[0],gl.viewport_data[1],v[2],v[3]);\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tconsole.warn(\\\"Creating LiteGL context over the same canvas twice\\\");\\r\\n\\r\\n\\t//reverse names helper (assuming no names repeated)\\r\\n\\tif(!GL.reverse)\\r\\n\\t{\\r\\n\\t\\tGL.reverse = {}; \\r\\n\\t\\tfor(var i in gl)\\r\\n\\t\\t\\tif( gl[i] && gl[i].constructor === Number )\\r\\n\\t\\t\\t\\tGL.reverse[ gl[i] ] = i;\\r\\n\\t}\\r\\n\\t\\r\\n\\t//just some checks\\r\\n\\tif(typeof(glMatrix) == \\\"undefined\\\")\\r\\n\\t\\tthrow(\\\"glMatrix not found, LiteGL requires glMatrix to be included\\\");\\r\\n\\r\\n\\tvar last_click_time = 0;\\r\\n\\r\\n\\t//some global containers, use them to reuse assets\\r\\n\\tgl.shaders = {};\\r\\n\\tgl.textures = {};\\r\\n\\tgl.meshes = {};\\r\\n\\r\\n\\t/**\\r\\n\\t* sets this context as the current global gl context (in case you have more than one)\\r\\n\\t* @method makeCurrent\\r\\n\\t*/\\r\\n\\tgl.makeCurrent = function()\\r\\n\\t{\\r\\n\\t\\tglobal.gl = this;\\r\\n\\t}\\r\\n\\r\\n\\t/**\\r\\n\\t* executes callback inside this webgl context\\r\\n\\t* @method execute\\r\\n\\t* @param {Function} callback\\r\\n\\t*/\\r\\n\\tgl.execute = function(callback)\\r\\n\\t{\\r\\n\\t\\tvar old_gl = global.gl;\\r\\n\\t\\tglobal.gl = this;\\r\\n\\t\\tcallback();\\r\\n\\t\\tglobal.gl = old_gl;\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\t/**\\r\\n\\t* Launch animation loop (calls gl.onupdate and gl.ondraw every frame)\\r\\n\\t* example: gl.ondraw = function(){ ... } or gl.onupdate = function(dt) { ... }\\r\\n\\t* @method animate\\r\\n\\t*/\\r\\n\\tgl.animate = function(v) {\\r\\n\\t\\tif(v === false)\\r\\n\\t\\t{\\r\\n\\t\\t\\tglobal.cancelAnimationFrame( this._requestFrame_id );\\r\\n\\t\\t\\tthis._requestFrame_id = null;\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar post = global.requestAnimationFrame;\\r\\n\\t\\tvar time = getTime();\\r\\n\\t\\tvar context = this;\\r\\n\\r\\n\\t\\t//loop only if browser tab visible\\r\\n\\t\\tfunction loop() {\\r\\n\\t\\t\\tif(gl.destroyed) //to stop rendering once it is destroyed\\r\\n\\t\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t\\tcontext._requestFrame_id = post(loop); //do it first, in case it crashes\\r\\n\\r\\n\\t\\t\\tvar now = getTime();\\r\\n\\t\\t\\tvar dt = (now - time) * 0.001;\\r\\n\\t\\t\\tif(context.mouse)\\r\\n\\t\\t\\t\\tcontext.mouse.last_buttons = context.mouse.buttons;\\r\\n\\t\\t\\tif (context.onupdate) \\r\\n\\t\\t\\t\\tcontext.onupdate(dt);\\r\\n\\t\\t\\tLEvent.trigger( context, \\\"update\\\", dt);\\r\\n\\t\\t\\tif (context.ondraw)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//make sure the ondraw is called using this gl context (in case there is more than one)\\r\\n\\t\\t\\t\\tvar old_gl = global.gl;\\r\\n\\t\\t\\t\\tglobal.gl = context;\\r\\n\\t\\t\\t\\t//call ondraw\\r\\n\\t\\t\\t\\tcontext.ondraw();\\r\\n\\t\\t\\t\\tLEvent.trigger(context,\\\"draw\\\");\\r\\n\\t\\t\\t\\t//restore old context\\r\\n\\t\\t\\t\\tglobal.gl = old_gl;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\ttime = now;\\r\\n\\t\\t}\\r\\n\\t\\tthis._requestFrame_id = post(loop); //launch main loop\\r\\n\\t}\\t\\r\\n\\r\\n\\t//store binded to be able to remove them if destroyed\\r\\n\\t/*\\r\\n\\tvar _binded_events = [];\\r\\n\\tfunction addEvent(object, type, callback)\\r\\n\\t{\\r\\n\\t\\t_binded_events.push(object,type,callback);\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\t/**\\r\\n\\t* Destroy this WebGL context (removes also the Canvas from the DOM)\\r\\n\\t* @method destroy\\r\\n\\t*/\\r\\n\\tgl.destroy = function() {\\r\\n\\t\\t//unbind global events\\r\\n\\t\\tif(onkey_handler)\\r\\n\\t\\t{\\r\\n\\t\\t\\tdocument.removeEventListener(\\\"keydown\\\", onkey_handler );\\r\\n\\t\\t\\tdocument.removeEventListener(\\\"keyup\\\", onkey_handler );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(this.canvas.parentNode)\\r\\n\\t\\t\\tthis.canvas.parentNode.removeChild(this.canvas);\\r\\n\\t\\tthis.destroyed = true;\\r\\n\\t\\tif(global.gl == this)\\r\\n\\t\\t\\tglobal.gl = null;\\r\\n\\t}\\r\\n\\r\\n\\tvar mouse = gl.mouse = {\\r\\n\\t\\tbuttons: 0, //this should always be up-to-date with mouse state\\r\\n\\t\\tlast_buttons: 0, //button state in the previous frame\\r\\n\\t\\tleft_button: false,\\r\\n\\t\\tmiddle_button: false,\\r\\n\\t\\tright_button: false,\\r\\n\\t\\tposition: new Float32Array(2),\\r\\n\\t\\tx:0, //in canvas coordinates\\r\\n\\t\\ty:0,\\r\\n\\t\\tdeltax: 0,\\r\\n\\t\\tdeltay: 0,\\r\\n\\t\\tclientx:0, //in client coordinates\\r\\n\\t\\tclienty:0,\\r\\n\\t\\tisInsideRect: function(x,y,w,h, flip_y )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mouse_y = this.y;\\r\\n\\t\\t\\tif(flip_y)\\r\\n\\t\\t\\t\\tmouse_y = gl.canvas.height - mouse_y;\\r\\n\\t\\t\\tif( this.x > x && this.x < x + w &&\\r\\n\\t\\t\\t\\tmouse_y > y && mouse_y < y + h)\\r\\n\\t\\t\\t\\treturn true;\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t},\\r\\n\\r\\n\\t\\t/**\\r\\n\\t\\t* returns true if button num is pressed (where num could be GL.LEFT_MOUSE_BUTTON, GL.RIGHT_MOUSE_BUTTON, GL.MIDDLE_MOUSE_BUTTON\\r\\n\\t\\t* @method captureMouse\\r\\n\\t\\t* @param {boolean} capture_wheel capture also the mouse wheel\\r\\n\\t\\t*/\\r\\n\\t\\tisButtonPressed: function(num)\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn this.buttons & (1< 1)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t switch(e.type)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase \\\"touchstart\\\": type = \\\"mousedown\\\"; break;\\r\\n\\t\\t\\tcase \\\"touchmove\\\": type = \\\"mousemove\\\"; break; \\r\\n\\t\\t\\tcase \\\"touchend\\\": type = \\\"mouseup\\\"; break;\\r\\n\\t\\t\\tdefault: return;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar simulatedEvent = document.createEvent(\\\"MouseEvent\\\");\\r\\n\\t\\tsimulatedEvent.initMouseEvent(type, true, true, window, 1,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t first.screenX, first.screenY,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t first.clientX, first.clientY, false,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t false, false, false, 0/*left*/, null);\\r\\n\\t\\tsimulatedEvent.originalEvent = simulatedEvent;\\r\\n\\t\\tsimulatedEvent.is_touch = true;\\r\\n\\t\\tfirst.target.dispatchEvent( simulatedEvent );\\r\\n\\t\\te.preventDefault();\\r\\n\\t}\\r\\n\\r\\n\\tfunction ongesture(e)\\r\\n\\t{\\r\\n\\t\\te.eventType = e.type;\\r\\n\\r\\n\\t\\tif(gl.ongesture && gl.ongesture(e) === false )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif( LEvent.trigger( gl, e.type, e ) === false )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\te.preventDefault();\\r\\n\\t}\\r\\n\\r\\n\\tvar keys = gl.keys = {};\\r\\n\\r\\n\\t/**\\r\\n\\t* Tells the system to capture key events on the canvas. This will trigger onkey\\r\\n\\t* @method captureKeys\\r\\n\\t* @param {boolean} prevent_default prevent default behaviour (like scroll on the web, etc)\\r\\n\\t* @param {boolean} only_canvas only caches keyboard events if they happen when the canvas is in focus\\r\\n\\t*/\\r\\n\\tvar onkey_handler = null;\\r\\n\\tgl.captureKeys = function( prevent_default, only_canvas ) {\\r\\n\\t\\tif(onkey_handler) \\r\\n\\t\\t\\treturn;\\r\\n\\t\\tgl.keys = {};\\r\\n\\r\\n\\t\\tvar target = only_canvas ? gl.canvas : document;\\r\\n\\r\\n\\t\\tdocument.addEventListener(\\\"keydown\\\", inner );\\r\\n\\t\\tdocument.addEventListener(\\\"keyup\\\", inner );\\r\\n\\t\\tfunction inner(e) { onkey(e, prevent_default); }\\r\\n\\t\\tonkey_handler = inner;\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\r\\n\\tfunction onkey(e, prevent_default)\\r\\n\\t{\\r\\n\\t\\te.eventType = e.type; //type cannot be overwritten, so I make a clone to allow me to overwrite\\r\\n\\r\\n\\t\\tvar target_element = e.target.nodeName.toLowerCase();\\r\\n\\t\\tif(target_element === \\\"input\\\" || target_element === \\\"textarea\\\" || target_element === \\\"select\\\")\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\te.character = String.fromCharCode(e.keyCode).toLowerCase();\\r\\n\\t\\tvar prev_state = false;\\r\\n\\t\\tvar key = GL.mapKeyCode(e.keyCode);\\r\\n\\t\\tif(!key) //this key doesnt look like an special key\\r\\n\\t\\t\\tkey = e.character;\\r\\n\\r\\n\\t\\t//regular key\\r\\n\\t\\tif (!e.altKey && !e.ctrlKey && !e.metaKey) {\\r\\n\\t\\t\\tif (key) \\r\\n\\t\\t\\t\\tgl.keys[key] = e.type == \\\"keydown\\\";\\r\\n\\t\\t\\tprev_state = gl.keys[e.keyCode];\\r\\n\\t\\t\\tgl.keys[e.keyCode] = e.type == \\\"keydown\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//avoid repetition if key stays pressed\\r\\n\\t\\tif(prev_state != gl.keys[e.keyCode])\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(e.type == \\\"keydown\\\" && gl.onkeydown) \\r\\n\\t\\t\\t\\tgl.onkeydown(e);\\r\\n\\t\\t\\telse if(e.type == \\\"keyup\\\" && gl.onkeyup) \\r\\n\\t\\t\\t\\tgl.onkeyup(e);\\r\\n\\t\\t\\tLEvent.trigger(gl, e.type, e);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(gl.onkey)\\r\\n\\t\\t\\tgl.onkey(e);\\r\\n\\r\\n\\t\\tif(prevent_default && (e.isChar || GL.blockable_keys[e.keyIdentifier || e.key ]) )\\r\\n\\t\\t\\te.preventDefault();\\r\\n\\t}\\r\\n\\r\\n\\t//gamepads\\r\\n\\tgl.gamepads = null;\\r\\n\\t/*\\r\\n\\tfunction onButton(e, pressed)\\r\\n\\t{\\r\\n\\t\\tconsole.log(e);\\r\\n\\t\\tif(pressed && gl.onbuttondown)\\r\\n\\t\\t\\tgl.onbuttondown(e);\\r\\n\\t\\telse if(!pressed && gl.onbuttonup)\\r\\n\\t\\t\\tgl.onbuttonup(e);\\r\\n\\t\\tif(gl.onbutton)\\r\\n\\t\\t\\tgl.onbutton(e);\\r\\n\\t\\tLEvent.trigger(gl, pressed ? \\\"buttondown\\\" : \\\"buttonup\\\", e );\\r\\n\\t}\\r\\n\\tfunction onGamepad(e)\\r\\n\\t{\\r\\n\\t\\tconsole.log(e);\\r\\n\\t\\tif(gl.ongamepad) \\r\\n\\t\\t\\tgl.ongamepad(e);\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\t/**\\r\\n\\t* Tells the system to capture gamepad events on the canvas. \\r\\n\\t* @method captureGamepads\\r\\n\\t*/\\r\\n\\tgl.captureGamepads = function()\\r\\n\\t{\\r\\n\\t\\tvar getGamepads = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; \\r\\n\\t\\tif(!getGamepads) return;\\r\\n\\t\\tthis.gamepads = getGamepads.call(navigator);\\r\\n\\r\\n\\t\\t//only in firefox, so I cannot rely on this\\r\\n\\t\\t/*\\r\\n\\t\\twindow.addEventListener(\\\"gamepadButtonDown\\\", function(e) { onButton(e, true); }, false);\\r\\n\\t\\twindow.addEventListener(\\\"MozGamepadButtonDown\\\", function(e) { onButton(e, true); }, false);\\r\\n\\t\\twindow.addEventListener(\\\"WebkitGamepadButtonDown\\\", function(e) { onButton(e, true); }, false);\\r\\n\\t\\twindow.addEventListener(\\\"gamepadButtonUp\\\", function(e) { onButton(e, false); }, false);\\r\\n\\t\\twindow.addEventListener(\\\"MozGamepadButtonUp\\\", function(e) { onButton(e, false); }, false);\\r\\n\\t\\twindow.addEventListener(\\\"WebkitGamepadButtonUp\\\", function(e) { onButton(e, false); }, false);\\r\\n\\t\\twindow.addEventListener(\\\"gamepadconnected\\\", onGamepad, false);\\r\\n\\t\\twindow.addEventListener(\\\"gamepaddisconnected\\\", onGamepad, false);\\r\\n\\t\\t*/\\r\\n\\r\\n\\t}\\r\\n\\r\\n\\t/**\\r\\n\\t* returns the detected gamepads on the system\\r\\n\\t* @method getGamepads\\r\\n\\t* @param {bool} skip_mapping if set to true it returns the basic gamepad, otherwise it returns a class with mapping info to XBOX controller\\r\\n\\t*/\\r\\n\\tgl.getGamepads = function(skip_mapping)\\r\\n\\t{\\r\\n\\t\\t//gamepads\\r\\n\\t\\tvar getGamepads = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; \\r\\n\\t\\tif(!getGamepads)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar gamepads = getGamepads.call(navigator);\\r\\n\\t\\tif(!this.gamepads)\\r\\n\\t\\t\\tthis.gamepads = [];\\r\\n\\r\\n\\t\\t//iterate to generate events\\r\\n\\t\\tfor(var i = 0; i < 4; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar gamepad = gamepads[i]; //current state\\r\\n\\r\\n\\t\\t\\tif(gamepad && !skip_mapping)\\r\\n\\t\\t\\t\\taddGamepadXBOXmapping(gamepad);\\r\\n\\r\\n\\t\\t\\t//launch connected gamepads events\\r\\n\\t\\t\\tif(gamepad && !gamepad.prev_buttons)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tgamepad.prev_buttons = new Uint8Array(32);\\r\\n\\t\\t\\t\\tvar event = new CustomEvent(\\\"gamepadconnected\\\");\\r\\n\\t\\t\\t\\tevent.eventType = event.type;\\r\\n\\t\\t\\t\\tevent.gamepad = gamepad;\\r\\n\\t\\t\\t\\tif(this.ongamepadconnected)\\r\\n\\t\\t\\t\\t\\tthis.ongamepadconnected(event);\\r\\n\\t\\t\\t\\tLEvent.trigger(gl,\\\"gamepadconnected\\\",event);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\telse if(old_gamepad && !gamepad)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar event = new CustomEvent(\\\"gamepaddisconnected\\\");\\r\\n\\t\\t\\t\\tevent.eventType = event.type;\\r\\n\\t\\t\\t\\tevent.gamepad = old_gamepad;\\r\\n\\t\\t\\t\\tif(this.ongamepaddisconnected)\\r\\n\\t\\t\\t\\t\\tthis.ongamepaddisconnected(event);\\r\\n\\t\\t\\t\\tLEvent.trigger(gl,\\\"gamepaddisconnected\\\",event);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t*/\\r\\n\\r\\n\\t\\t\\t//seek buttons changes to trigger events\\r\\n\\t\\t\\tif(gamepad)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var j = 0; j < gamepad.buttons.length; ++j)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar button = gamepad.buttons[j];\\r\\n\\t\\t\\t\\t\\tbutton.was_pressed = false;\\r\\n\\t\\t\\t\\t\\tif( button.pressed && !gamepad.prev_buttons[j] )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tbutton.was_pressed = true;\\r\\n\\t\\t\\t\\t\\t\\tvar event = new CustomEvent(\\\"gamepadButtonDown\\\");\\r\\n\\t\\t\\t\\t\\t\\tevent.eventType = event.type;\\r\\n\\t\\t\\t\\t\\t\\tevent.button = button;\\r\\n\\t\\t\\t\\t\\t\\tevent.which = j;\\r\\n\\t\\t\\t\\t\\t\\tevent.gamepad = gamepad;\\r\\n\\t\\t\\t\\t\\t\\tif(gl.onbuttondown)\\r\\n\\t\\t\\t\\t\\t\\t\\tgl.onbuttondown(event);\\r\\n\\t\\t\\t\\t\\t\\tLEvent.trigger(gl,\\\"buttondown\\\",event);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse if( !button.pressed && gamepad.prev_buttons[j] )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar event = new CustomEvent(\\\"gamepadButtonUp\\\");\\r\\n\\t\\t\\t\\t\\t\\tevent.eventType = event.type;\\r\\n\\t\\t\\t\\t\\t\\tevent.button = button;\\r\\n\\t\\t\\t\\t\\t\\tevent.which = j;\\r\\n\\t\\t\\t\\t\\t\\tevent.gamepad = gamepad;\\r\\n\\t\\t\\t\\t\\t\\tif(gl.onbuttondown)\\r\\n\\t\\t\\t\\t\\t\\t\\tgl.onbuttondown(event);\\r\\n\\t\\t\\t\\t\\t\\tLEvent.trigger(gl,\\\"buttonup\\\",event);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tgamepad.prev_buttons[j] = button.pressed ? 1 : 0;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tthis.gamepads = gamepads;\\r\\n\\t\\treturn gamepads;\\r\\n\\t}\\r\\n\\r\\n\\tfunction addGamepadXBOXmapping(gamepad)\\r\\n\\t{\\r\\n\\t\\t//xbox controller mapping\\r\\n\\t\\tvar xbox = gamepad.xbox || { axes:[], buttons:{}, hat: \\\"\\\"};\\r\\n\\t\\txbox.axes[\\\"lx\\\"] = gamepad.axes[0];\\r\\n\\t\\txbox.axes[\\\"ly\\\"] = gamepad.axes[1];\\r\\n\\t\\txbox.axes[\\\"rx\\\"] = gamepad.axes[2];\\r\\n\\t\\txbox.axes[\\\"ry\\\"] = gamepad.axes[3];\\r\\n\\t\\txbox.axes[\\\"triggers\\\"] = gamepad.axes[4];\\r\\n\\r\\n\\t\\tfor(var i = 0; i < gamepad.buttons.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tswitch(i) //I use a switch to ensure that a player with another gamepad could play\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase 0: xbox.buttons[\\\"a\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 1: xbox.buttons[\\\"b\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 2: xbox.buttons[\\\"x\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 3: xbox.buttons[\\\"y\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 4: xbox.buttons[\\\"lb\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 5: xbox.buttons[\\\"rb\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 6: xbox.buttons[\\\"lt\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 7: xbox.buttons[\\\"rt\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 8: xbox.buttons[\\\"back\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 9: xbox.buttons[\\\"start\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 10: xbox.buttons[\\\"ls\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 11: xbox.buttons[\\\"rs\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tcase 12: if( gamepad.buttons[i].pressed) xbox.hat += \\\"up\\\"; break;\\r\\n\\t\\t\\t\\tcase 13: if( gamepad.buttons[i].pressed) xbox.hat += \\\"down\\\"; break;\\r\\n\\t\\t\\t\\tcase 14: if( gamepad.buttons[i].pressed) xbox.hat += \\\"left\\\"; break;\\r\\n\\t\\t\\t\\tcase 15: if( gamepad.buttons[i].pressed) xbox.hat += \\\"right\\\"; break;\\r\\n\\t\\t\\t\\tcase 16: xbox.buttons[\\\"home\\\"] = gamepad.buttons[i].pressed; break;\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tgamepad.xbox = xbox;\\r\\n\\t}\\r\\n\\r\\n\\t/**\\r\\n\\t* launches de canvas in fullscreen mode\\r\\n\\t* @method fullscreen\\r\\n\\t*/\\r\\n\\tgl.fullscreen = function()\\r\\n\\t{\\r\\n\\t\\tvar canvas = this.canvas;\\r\\n\\t\\tif(canvas.requestFullScreen)\\r\\n\\t\\t\\tcanvas.requestFullScreen();\\r\\n\\t\\telse if(canvas.webkitRequestFullScreen)\\r\\n\\t\\t\\tcanvas.webkitRequestFullScreen();\\r\\n\\t\\telse if(canvas.mozRequestFullScreen)\\r\\n\\t\\t\\tcanvas.mozRequestFullScreen();\\r\\n\\t\\telse\\r\\n\\t\\t\\tconsole.error(\\\"Fullscreen not supported\\\");\\r\\n\\t}\\r\\n\\r\\n\\t/**\\r\\n\\t* returns a canvas with a snapshot of an area\\r\\n\\t* this is safer than using the canvas itself due to internals of webgl\\r\\n\\t* @method snapshot\\r\\n\\t* @param {Number} startx viewport x coordinate\\r\\n\\t* @param {Number} starty viewport y coordinate from bottom\\r\\n\\t* @param {Number} areax viewport area width\\r\\n\\t* @param {Number} areay viewport area height\\r\\n\\t* @return {Canvas} canvas\\r\\n\\t*/\\r\\n\\tgl.snapshot = function(startx, starty, areax, areay, skip_reverse)\\r\\n\\t{\\r\\n\\t\\tvar canvas = createCanvas(areax,areay);\\r\\n\\t\\tvar ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tvar pixels = ctx.getImageData(0,0,canvas.width,canvas.height);\\r\\n\\r\\n\\t\\tvar buffer = new Uint8Array(areax * areay * 4);\\r\\n\\t\\tgl.readPixels(startx, starty, canvas.width, canvas.height, gl.RGBA,gl.UNSIGNED_BYTE, buffer);\\r\\n\\r\\n\\t\\tpixels.data.set( buffer );\\r\\n\\t\\tctx.putImageData(pixels,0,0);\\r\\n\\r\\n\\t\\tif(skip_reverse)\\r\\n\\t\\t\\treturn canvas;\\r\\n\\r\\n\\t\\t//flip image \\r\\n\\t\\tvar final_canvas = createCanvas(areax,areay);\\r\\n\\t\\tvar ctx = final_canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tctx.translate(0,areay);\\r\\n\\t\\tctx.scale(1,-1);\\r\\n\\t\\tctx.drawImage(canvas,0,0);\\r\\n\\r\\n\\t\\treturn final_canvas;\\r\\n\\t}\\r\\n\\r\\n\\t//from https://webgl2fundamentals.org/webgl/lessons/webgl1-to-webgl2.html\\r\\n\\tfunction getAndApplyExtension( gl, name ) {\\r\\n\\t\\tvar ext = gl.getExtension(name);\\r\\n\\t\\tif (!ext) {\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t}\\r\\n\\t\\tvar suffix = name.split(\\\"_\\\")[0];\\r\\n\\t\\tvar prefix = suffix = '_';\\r\\n\\t\\tvar suffixRE = new RegExp(suffix + '$');\\r\\n\\t\\tvar prefixRE = new RegExp('^' + prefix);\\r\\n\\t\\tfor (var key in ext) {\\r\\n\\t\\t\\tvar val = ext[key];\\r\\n\\t\\t\\tif (typeof(val) === 'function') {\\r\\n\\t\\t\\t\\t// remove suffix (eg: bindVertexArrayOES -> bindVertexArray)\\r\\n\\t\\t\\t\\tvar unsuffixedKey = key.replace(suffixRE, '');\\r\\n\\t\\t\\t\\tif (key.substing)\\r\\n\\t\\t\\t\\t\\tgl[unprefixedKey] = ext[key].bind(ext);\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tvar unprefixedKey = key.replace(prefixRE, '');\\r\\n\\t\\t\\t\\tgl[unprefixedKey] = ext[key];\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\t//mini textures manager\\r\\n\\tvar loading_textures = {};\\r\\n\\t/**\\r\\n\\t* returns a texture and caches it inside gl.textures[]\\r\\n\\t* @method loadTexture\\r\\n\\t* @param {String} url\\r\\n\\t* @param {Object} options (same options as when creating a texture)\\r\\n\\t* @param {Function} callback function called once the texture is loaded\\r\\n\\t* @return {Texture} texture\\r\\n\\t*/\\r\\n\\tgl.loadTexture = function(url, options, on_load)\\r\\n\\t{\\r\\n\\t\\tif(this.textures[ url ])\\r\\n\\t\\t\\treturn this.textures[url];\\r\\n\\r\\n\\t\\tif( loading_textures[url] )\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar img = new Image();\\r\\n\\t\\timg.url = url;\\r\\n\\t\\timg.onload = function()\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar texture = GL.Texture.fromImage(this, options);\\r\\n\\t\\t\\ttexture.img = this;\\r\\n\\t\\t\\tgl.textures[this.url] = texture;\\r\\n\\t\\t\\tdelete loading_textures[this.url];\\r\\n\\t\\t\\tif(on_load)\\r\\n\\t\\t\\t\\ton_load(texture);\\r\\n\\t\\t} \\r\\n\\t\\timg.src = url;\\r\\n\\t\\tloading_textures[url] = true;\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\t/**\\r\\n\\t* draws a texture to the viewport\\r\\n\\t* @method drawTexture\\r\\n\\t* @param {Texture} texture\\r\\n\\t* @param {number} x in viewport coordinates \\r\\n\\t* @param {number} y in viewport coordinates \\r\\n\\t* @param {number} w in viewport coordinates \\r\\n\\t* @param {number} h in viewport coordinates \\r\\n\\t* @param {number} tx texture x in texture coordinates\\r\\n\\t* @param {number} ty texture y in texture coordinates\\r\\n\\t* @param {number} tw texture width in texture coordinates\\r\\n\\t* @param {number} th texture height in texture coordinates\\r\\n\\t*/\\r\\n\\tgl.drawTexture = (function() {\\r\\n\\t\\t//static variables: less garbage\\r\\n\\t\\tvar identity = mat3.create();\\r\\n\\t\\tvar pos = vec2.create();\\r\\n\\t\\tvar size = vec2.create();\\r\\n\\t\\tvar area = vec4.create();\\r\\n\\t\\tvar white = vec4.fromValues(1,1,1,1);\\r\\n\\t\\tvar viewport = vec2.create();\\r\\n\\t\\tvar _uniforms = {u_texture: 0, u_position: pos, u_color: white, u_size: size, u_texture_area: area, u_viewport: viewport, u_transform: identity };\\r\\n\\r\\n\\t\\treturn (function(texture, x,y, w,h, tx,ty, tw,th, shader, uniforms)\\r\\n\\t\\t{\\r\\n\\t\\t\\tpos[0] = x;\\tpos[1] = y;\\r\\n\\t\\t\\tif(w === undefined)\\r\\n\\t\\t\\t\\tw = texture.width;\\r\\n\\t\\t\\tif(h === undefined)\\r\\n\\t\\t\\t\\th = texture.height;\\r\\n\\t\\t\\tsize[0] = w;\\r\\n\\t\\t\\tsize[1] = h;\\r\\n\\r\\n\\t\\t\\tif(tx === undefined) tx = 0;\\r\\n\\t\\t\\tif(ty === undefined) ty = 0;\\r\\n\\t\\t\\tif(tw === undefined) tw = texture.width;\\r\\n\\t\\t\\tif(th === undefined) th = texture.height;\\r\\n\\r\\n\\t\\t\\tarea[0] = tx / texture.width;\\r\\n\\t\\t\\tarea[1] = ty / texture.height;\\r\\n\\t\\t\\tarea[2] = (tx + tw) / texture.width;\\r\\n\\t\\t\\tarea[3] = (ty + th) / texture.height;\\r\\n\\r\\n\\t\\t\\tviewport[0] = this.viewport_data[2];\\r\\n\\t\\t\\tviewport[1] = this.viewport_data[3];\\r\\n\\r\\n\\t\\t\\tshader = shader || Shader.getPartialQuadShader(this);\\r\\n\\t\\t\\tvar mesh = Mesh.getScreenQuad(this);\\r\\n\\t\\t\\ttexture.bind(0);\\r\\n\\t\\t\\tshader.uniforms( _uniforms );\\r\\n\\t\\t\\tif( uniforms )\\r\\n\\t\\t\\t\\tshader.uniforms( uniforms );\\r\\n\\t\\t\\tshader.draw( mesh, gl.TRIANGLES );\\r\\n\\t\\t});\\r\\n\\t})();\\r\\n\\r\\n\\tgl.canvas.addEventListener(\\\"webglcontextlost\\\", function(e) {\\r\\n\\t\\te.preventDefault();\\r\\n\\t\\tgl.context_lost = true;\\r\\n\\t\\tif(gl.onlosecontext)\\r\\n\\t\\t\\tgl.onlosecontext(e);\\r\\n\\t}, false);\\r\\n\\r\\n\\t/**\\r\\n\\t* use it to reset the the initial gl state\\r\\n\\t* @method gl.reset\\r\\n\\t*/\\r\\n\\tgl.reset = function()\\r\\n\\t{\\r\\n\\t\\t//viewport\\r\\n\\t\\tgl.viewport(0,0, this.canvas.width, this.canvas.height );\\r\\n\\r\\n\\t\\t//flags\\r\\n\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\tgl.disable( gl.CULL_FACE );\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.frontFace( gl.CCW );\\r\\n\\r\\n\\t\\t//texture\\r\\n\\t\\tgl._current_texture_drawto = null;\\r\\n\\t\\tgl._current_fbo_color = null;\\r\\n\\t\\tgl._current_fbo_depth = null;\\r\\n\\t}\\r\\n\\r\\n\\tgl.dump = function()\\r\\n\\t{\\r\\n\\t\\tconsole.log(\\\"userAgent: \\\", navigator.userAgent );\\r\\n\\t\\tconsole.log(\\\"Supported extensions:\\\");\\r\\n\\t\\tvar extensions = gl.getSupportedExtensions();\\r\\n\\t\\tconsole.log( extensions.join(\\\",\\\") );\\r\\n\\t\\tvar info = [ \\\"VENDOR\\\", \\\"VERSION\\\", \\\"MAX_VERTEX_ATTRIBS\\\", \\\"MAX_VARYING_VECTORS\\\", \\\"MAX_VERTEX_UNIFORM_VECTORS\\\", \\\"MAX_VERTEX_TEXTURE_IMAGE_UNITS\\\", \\\"MAX_FRAGMENT_UNIFORM_VECTORS\\\", \\\"MAX_TEXTURE_SIZE\\\", \\\"MAX_TEXTURE_IMAGE_UNITS\\\" ];\\r\\n\\t\\tconsole.log(\\\"WebGL info:\\\");\\r\\n\\t\\tfor(var i in info)\\r\\n\\t\\t\\tconsole.log(\\\" * \\\" + info[i] + \\\": \\\" + gl.getParameter( gl[info[i]] ));\\r\\n\\t\\tconsole.log(\\\"*************************************************\\\")\\r\\n\\t}\\r\\n\\r\\n\\t//Reset state\\r\\n\\tgl.reset();\\r\\n\\r\\n\\t//Return\\r\\n\\treturn gl;\\r\\n}\\r\\n\\r\\nGL.mapKeyCode = function(code)\\r\\n{\\r\\n\\tvar named = {\\r\\n\\t\\t8: 'BACKSPACE',\\r\\n\\t\\t9: 'TAB',\\r\\n\\t\\t13: 'ENTER',\\r\\n\\t\\t16: 'SHIFT',\\r\\n\\t\\t17: 'CTRL',\\r\\n\\t\\t27: 'ESCAPE',\\r\\n\\t\\t32: 'SPACE',\\r\\n\\t\\t37: 'LEFT',\\r\\n\\t\\t38: 'UP',\\r\\n\\t\\t39: 'RIGHT',\\r\\n\\t\\t40: 'DOWN'\\r\\n\\t};\\r\\n\\treturn named[code] || (code >= 65 && code <= 90 ? String.fromCharCode(code) : null);\\r\\n}\\r\\n\\r\\n//add useful info to the event\\r\\nGL.dragging = false;\\r\\nGL.last_pos = [0,0];\\r\\n\\r\\n//adds extra info to the MouseEvent (coordinates in canvas axis, deltas and button state)\\r\\nGL.augmentEvent = function(e, root_element)\\r\\n{\\r\\n\\tvar offset_left = 0;\\r\\n\\tvar offset_top = 0;\\r\\n\\tvar b = null;\\r\\n\\r\\n\\troot_element = root_element || e.target || gl.canvas;\\r\\n\\tb = root_element.getBoundingClientRect();\\r\\n\\t\\t\\r\\n\\te.mousex = e.clientX - b.left;\\r\\n\\te.mousey = e.clientY - b.top;\\r\\n\\te.canvasx = e.mousex;\\r\\n\\te.canvasy = b.height - e.mousey;\\r\\n\\te.deltax = 0;\\r\\n\\te.deltay = 0;\\r\\n\\t\\r\\n\\tif(e.type == \\\"mousedown\\\")\\r\\n\\t{\\r\\n\\t\\tthis.dragging = true;\\r\\n\\t\\tgl.mouse.buttons |= (1 << e.which); //enable\\r\\n\\t}\\r\\n\\telse if (e.type == \\\"mousemove\\\")\\r\\n\\t{\\r\\n\\t}\\r\\n\\telse if (e.type == \\\"mouseup\\\")\\r\\n\\t{\\r\\n\\t\\tgl.mouse.buttons = gl.mouse.buttons & ~(1 << e.which);\\r\\n\\t\\tif(gl.mouse.buttons == 0)\\r\\n\\t\\t\\tthis.dragging = false;\\r\\n\\t}\\r\\n\\r\\n\\tif( e.movementX !== undefined && !GL.isMobile() ) //pointer lock (mobile gives always zero)\\r\\n\\t{\\r\\n\\t\\t//console.log( e.movementX )\\r\\n\\t\\te.deltax = e.movementX;\\r\\n\\t\\te.deltay = e.movementY;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\te.deltax = e.mousex - this.last_pos[0];\\r\\n\\t\\te.deltay = e.mousey - this.last_pos[1];\\r\\n\\t}\\r\\n\\tthis.last_pos[0] = e.mousex;\\r\\n\\tthis.last_pos[1] = e.mousey;\\r\\n\\r\\n\\t//insert info in event\\r\\n\\te.dragging = this.dragging;\\r\\n\\te.buttons_mask = gl.mouse.buttons;\\r\\n\\te.leftButton = !!(gl.mouse.buttons & (1<= 0; --j) //iterate backwards to avoid problems after removing\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( array[j][1] != target_instance || (callback && callback !== array[j][0]) ) \\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tarray.splice(j,1);//remove\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Unbinds all callbacks associated to one specific event from this instance\\r\\n\\t* @method LEvent.unbindAll\\r\\n\\t* @param {Object} instance where the events are binded\\r\\n\\t* @param {String} event name of the event you want to remove all binds\\r\\n\\t**/\\r\\n\\tunbindAllEvent: function( instance, event_type )\\r\\n\\t{\\r\\n\\t\\tif(!instance) \\r\\n\\t\\t\\tthrow(\\\"cannot unbind events in null\\\");\\r\\n\\r\\n\\t\\tvar events = instance.__levents;\\r\\n\\t\\tif(!events)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tdelete events[ event_type ];\\r\\n\\t\\tif( instance.onLEventUnbindAll )\\r\\n\\t\\t\\tinstance.onLEventUnbindAll( event_type, target_instance, callback );\\r\\n\\t\\treturn;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Tells if there is a binded callback that matches the criteria\\r\\n\\t* @method LEvent.isBind\\r\\n\\t* @param {Object} instance where the are the events binded\\r\\n\\t* @param {String} event_name string defining the event name\\r\\n\\t* @param {function} callback the callback\\r\\n\\t* @param {Object} target_instance [Optional] instance binded to callback\\r\\n\\t**/\\r\\n\\tisBind: function( instance, event_type, callback, target_instance )\\r\\n\\t{\\r\\n\\t\\tif(!instance)\\r\\n\\t\\t\\tthrow(\\\"LEvent cannot have null as instance\\\");\\r\\n\\r\\n\\t\\tvar events = instance.__levents;\\r\\n\\t\\tif( !events )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif( !events.hasOwnProperty(event_type) ) \\r\\n\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\tfor(var i = 0, l = events[event_type].length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar v = events[event_type][i];\\r\\n\\t\\t\\tif(v[0] === callback && v[1] === target_instance)\\r\\n\\t\\t\\t\\treturn true;\\r\\n\\t\\t}\\r\\n\\t\\treturn false;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Tells if there is any callback binded to this event\\r\\n\\t* @method LEvent.hasBind\\r\\n\\t* @param {Object} instance where the are the events binded\\r\\n\\t* @param {String} event_name string defining the event name\\r\\n\\t* @return {boolean} true is there is at least one\\r\\n\\t**/\\r\\n\\thasBind: function( instance, event_type )\\r\\n\\t{\\r\\n\\t\\tif(!instance)\\r\\n\\t\\t\\tthrow(\\\"LEvent cannot have null as instance\\\");\\r\\n\\t\\tvar events = instance.__levents;\\r\\n\\t\\tif(!events || !events.hasOwnProperty( event_type ) || !events[event_type].length) \\r\\n\\t\\t\\treturn false;\\r\\n\\t\\treturn true;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Tells if there is any callback binded to this object pointing to a method in the target object\\r\\n\\t* @method LEvent.hasBindTo\\r\\n\\t* @param {Object} instance where there are the events binded\\r\\n\\t* @param {Object} target instance to check to\\r\\n\\t* @return {boolean} true is there is at least one\\r\\n\\t**/\\r\\n\\thasBindTo: function( instance, target )\\r\\n\\t{\\r\\n\\t\\tif(!instance)\\r\\n\\t\\t\\tthrow(\\\"LEvent cannot have null as instance\\\");\\r\\n\\t\\tvar events = instance.__levents;\\r\\n\\r\\n\\t\\t//no events binded\\r\\n\\t\\tif(!events) \\r\\n\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\tfor(var j in events)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar binds = events[j];\\r\\n\\t\\t\\tfor(var i = 0; i < binds.length; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(binds[i][1] === target) //one found\\r\\n\\t\\t\\t\\t\\treturn true;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn false;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Triggers and event in an instance\\r\\n\\t* If the callback returns true then it will stop the propagation and return true\\r\\n\\t* @method LEvent.trigger\\r\\n\\t* @param {Object} instance that triggers the event\\r\\n\\t* @param {String} event_name string defining the event name\\r\\n\\t* @param {*} parameters that will be received by the binded function\\r\\n\\t* @param {bool} reverse_order trigger in reverse order (binded last get called first)\\r\\n\\t* @param {bool} expand_parameters parameters are passed not as one single parameter, but as many\\r\\n\\t* return {bool} true if the event passed was blocked by any binded callback\\r\\n\\t**/\\r\\n\\ttrigger: function( instance, event_type, params, reverse_order, expand_parameters )\\r\\n\\t{\\r\\n\\t\\tif(!instance) \\r\\n\\t\\t\\tthrow(\\\"cannot trigger event from null\\\");\\r\\n\\t\\tif(instance.constructor === String ) \\r\\n\\t\\t\\tthrow(\\\"cannot bind event to a string\\\");\\r\\n\\r\\n\\t\\tvar events = instance.__levents;\\r\\n\\t\\tif( !events || !events.hasOwnProperty(event_type) )\\r\\n\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\tvar inst = events[event_type];\\r\\n\\t\\tif( reverse_order )\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i = inst.length - 1; i >= 0; --i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar v = inst[i];\\r\\n\\t\\t\\t\\tif(expand_parameters)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif( v && v[0].apply( v[1], params ) === true)// || event.stop)\\r\\n\\t\\t\\t\\t\\t\\treturn true; //stopPropagation\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif( v && v[0].call( v[1], event_type, params) === true)// || event.stop)\\r\\n\\t\\t\\t\\t\\t\\treturn true; //stopPropagation\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i = 0, l = inst.length; i < l; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar v = inst[i];\\r\\n\\t\\t\\t\\tif( expand_parameters )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif( v && v[0].apply( v[1], params ) === true)// || event.stop)\\r\\n\\t\\t\\t\\t\\t\\treturn true; //stopPropagation\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif( v && v[0].call(v[1], event_type, params) === true)// || event.stop)\\r\\n\\t\\t\\t\\t\\t\\treturn true; //stopPropagation\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn false;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Triggers and event to every element in an array.\\r\\n\\t* If the event returns true, it must be intercepted\\r\\n\\t* @method LEvent.triggerArray\\r\\n\\t* @param {Array} array contains all instances to triggers the event\\r\\n\\t* @param {String} event_name string defining the event name\\r\\n\\t* @param {*} parameters that will be received by the binded function\\r\\n\\t* @param {bool} reverse_order trigger in reverse order (binded last get called first)\\r\\n\\t* @param {bool} expand_parameters parameters are passed not as one single parameter, but as many\\r\\n\\t* return {bool} false \\r\\n\\t**/\\r\\n\\ttriggerArray: function( instances, event_type, params, reverse_order, expand_parameters )\\r\\n\\t{\\r\\n\\t\\tvar blocked = false;\\r\\n\\t\\tfor(var i = 0, l = instances.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar instance = instances[i];\\r\\n\\t\\t\\tif(!instance) \\r\\n\\t\\t\\t\\tthrow(\\\"cannot trigger event from null\\\");\\r\\n\\t\\t\\tif(instance.constructor === String ) \\r\\n\\t\\t\\t\\tthrow(\\\"cannot bind event to a string\\\");\\r\\n\\r\\n\\t\\t\\tvar events = instance.__levents;\\r\\n\\t\\t\\tif( !events || !events.hasOwnProperty( event_type ) )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif( reverse_order )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var j = events[event_type].length - 1; j >= 0; --j)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar v = events[event_type][j];\\r\\n\\t\\t\\t\\t\\tif(expand_parameters)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tif( v[0].apply(v[1], params ) === true)// || event.stop)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tblocked = true;\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak; //stopPropagation\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tif( v[0].call(v[1], event_type, params) === true)// || event.stop)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tblocked = true;\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak; //stopPropagation\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var j = 0, ll = events[event_type].length; j < ll; ++j)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar v = events[event_type][j];\\r\\n\\t\\t\\t\\t\\tif(expand_parameters)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tif( v[0].apply(v[1], params ) === true)// || event.stop)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tblocked = true;\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak; //stopPropagation\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tif( v[0].call(v[1], event_type, params) === true)// || event.stop)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tblocked = true;\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak; //stopPropagation\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn blocked;\\r\\n\\t},\\r\\n\\r\\n\\textendObject: function( object )\\r\\n\\t{\\r\\n\\t\\tobject.bind = function( event_type, callback, instance ){\\r\\n\\t\\t\\treturn LEvent.bind( this, event_type, callback, instance );\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tobject.trigger = function( event_type, params ){\\r\\n\\t\\t\\treturn LEvent.trigger( this, event_type, params );\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tobject.unbind = function( event_type, callback, target_instance )\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn LEvent.unbind( this, event_type, callback, instance );\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tobject.unbindAll = function( target_instance, callback )\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn LEvent.unbindAll( this, target_instance, callback );\\r\\n\\t\\t};\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Adds the methods to bind, trigger and unbind to this class prototype\\r\\n\\t* @method LEvent.extendClass\\r\\n\\t* @param {Object} constructor\\r\\n\\t**/\\r\\n\\textendClass: function( constructor )\\r\\n\\t{\\r\\n\\t\\tthis.extendObject( constructor.prototype );\\r\\n\\t}\\r\\n};\\r\\n/* geometric utilities */\\r\\nglobal.CLIP_INSIDE = GL.CLIP_INSIDE = 0;\\r\\nglobal.CLIP_OUTSIDE = GL.CLIP_OUTSIDE = 1;\\r\\nglobal.CLIP_OVERLAP = GL.CLIP_OVERLAP = 2;\\r\\n\\r\\n/**\\r\\n* @namespace\\r\\n*/\\r\\n\\r\\n\\r\\n/**\\r\\n* Computational geometry algorithms, is a static class\\r\\n* @class geo\\r\\n*/\\r\\n\\r\\nglobal.geo = {\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns a float4 containing the info about a plane with normal N and that passes through point P\\r\\n\\t* @method createPlane\\r\\n\\t* @param {vec3} P\\r\\n\\t* @param {vec3} N\\r\\n\\t* @return {vec4} plane values\\r\\n\\t*/\\r\\n\\tcreatePlane: function(P,N)\\r\\n\\t{\\r\\n\\t\\treturn new Float32Array([N[0],N[1],N[2],-vec3.dot(P,N)]);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Computes the distance between the point and the plane\\r\\n\\t* @method distancePointToPlane\\r\\n\\t* @param {vec3} point\\r\\n\\t* @param {vec4} plane\\r\\n\\t* @return {Number} distance\\r\\n\\t*/\\r\\n\\tdistancePointToPlane: function(point, plane)\\r\\n\\t{\\r\\n\\t\\treturn (vec3.dot(point,plane) + plane[3])/Math.sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Computes the square distance between the point and the plane\\r\\n\\t* @method distance2PointToPlane\\r\\n\\t* @param {vec3} point\\r\\n\\t* @param {vec4} plane\\r\\n\\t* @return {Number} distance*distance\\r\\n\\t*/\\r\\n\\tdistance2PointToPlane: function(point, plane)\\r\\n\\t{\\r\\n\\t\\treturn (vec3.dot(point,plane) + plane[3])/(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Projects a 3D point on a 3D line\\r\\n\\t* @method projectPointOnLine\\r\\n\\t* @param {vec3} P\\r\\n\\t* @param {vec3} A line start\\r\\n\\t* @param {vec3} B line end\\r\\n\\t* @param {vec3} result to store result (optional)\\r\\n\\t* @return {vec3} projectec point\\r\\n\\t*/\\r\\n\\tprojectPointOnLine: function( P, A, B, result )\\r\\n\\t{\\r\\n\\t\\tresult = result || vec3.create();\\r\\n\\t\\t//A + dot(AP,AB) / dot(AB,AB) * AB\\r\\n\\t\\tvar AP = vec3.fromValues( P[0] - A[0], P[1] - A[1], P[2] - A[2]);\\r\\n\\t\\tvar AB = vec3.fromValues( B[0] - A[0], B[1] - A[1], B[2] - A[2]);\\r\\n\\t\\tvar div = vec3.dot(AP,AB) / vec3.dot(AB,AB);\\r\\n\\t\\tresult[0] = A[0] + div[0] * AB[0];\\r\\n\\t\\tresult[1] = A[1] + div[1] * AB[1];\\r\\n\\t\\tresult[2] = A[2] + div[2] * AB[2];\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Projects a 2D point on a 2D line\\r\\n\\t* @method project2DPointOnLine\\r\\n\\t* @param {vec2} P\\r\\n\\t* @param {vec2} A line start\\r\\n\\t* @param {vec2} B line end\\r\\n\\t* @param {vec2} result to store result (optional)\\r\\n\\t* @return {vec2} projectec point\\r\\n\\t*/\\r\\n\\tproject2DPointOnLine: function( P, A, B, result )\\r\\n\\t{\\r\\n\\t\\tresult = result || vec2.create();\\r\\n\\t\\t//A + dot(AP,AB) / dot(AB,AB) * AB\\r\\n\\t\\tvar AP = vec2.fromValues(P[0] - A[0], P[1] - A[1]);\\r\\n\\t\\tvar AB = vec2.fromValues(B[0] - A[0], B[1] - A[1]);\\r\\n\\t\\tvar div = vec2.dot(AP,AB) / vec2.dot(AB,AB);\\r\\n\\t\\tresult[0] = A[0] + div[0] * AB[0];\\r\\n\\t\\tresult[1] = A[1] + div[1] * AB[1];\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Projects point on plane\\r\\n\\t* @method projectPointOnPlane\\r\\n\\t* @param {vec3} point\\r\\n\\t* @param {vec3} P plane point\\r\\n\\t* @param {vec3} N plane normal\\r\\n\\t* @param {vec3} result to store result (optional)\\r\\n\\t* @return {vec3} projectec point\\r\\n\\t*/\\r\\n\\tprojectPointOnPlane: function(point, P, N, result)\\r\\n\\t{\\r\\n\\t\\tresult = result || vec3.create();\\r\\n\\t\\tvar v = vec3.subtract( vec3.create(), point, P );\\r\\n\\t\\tvar dist = vec3.dot(v,N);\\r\\n\\t\\treturn vec3.subtract( result, point , vec3.scale( vec3.create(), N, dist ) );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Finds the reflected point over a plane (useful for reflecting camera position when rendering reflections)\\r\\n\\t* @method reflectPointInPlane\\r\\n\\t* @param {vec3} point point to reflect\\r\\n\\t* @param {vec3} P point where the plane passes\\r\\n\\t* @param {vec3} N normal of the plane\\r\\n\\t* @return {vec3} reflected point\\r\\n\\t*/\\r\\n\\treflectPointInPlane: function(point, P, N)\\r\\n\\t{\\r\\n\\t\\tvar d = -1 * (P[0] * N[0] + P[1] * N[1] + P[2] * N[2]);\\r\\n\\t\\tvar t = -(d + N[0]*point[0] + N[1]*point[1] + N[2]*point[2]) / (N[0]*N[0] + N[1]*N[1] + N[2]*N[2]);\\r\\n\\t\\t//trace(\\\"T:\\\" + t);\\r\\n\\t\\t//var closest = [ point[0]+t*N[0], point[1]+t*N[1], point[2]+t*N[2] ];\\r\\n\\t\\t//trace(\\\"Closest:\\\" + closest);\\r\\n\\t\\treturn vec3.fromValues( point[0]+t*N[0]*2, point[1]+t*N[1]*2, point[2]+t*N[2]*2 );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* test a ray plane collision and retrieves the collision point\\r\\n\\t* @method testRayPlane\\r\\n\\t* @param {vec3} start ray start\\r\\n\\t* @param {vec3} direction ray direction\\r\\n\\t* @param {vec3} P point where the plane passes\\t\\r\\n\\t* @param {vec3} N normal of the plane\\r\\n\\t* @param {vec3} result collision position\\r\\n\\t* @return {boolean} returns if the ray collides the plane or the ray is parallel to the plane\\r\\n\\t*/\\r\\n\\ttestRayPlane: function(start, direction, P, N, result)\\r\\n\\t{\\r\\n\\t\\tvar D = vec3.dot( P, N );\\r\\n\\t\\tvar numer = D - vec3.dot(N, start);\\r\\n\\t\\tvar denom = vec3.dot(N, direction);\\r\\n\\t\\tif( Math.abs(denom) < EPSILON) return false;\\r\\n\\t\\tvar t = (numer / denom);\\r\\n\\t\\tif(t < 0.0) return false; //behind the ray\\r\\n\\t\\tif(result)\\r\\n\\t\\t\\tvec3.add( result, start, vec3.scale( result, direction, t) );\\r\\n\\r\\n\\t\\treturn true;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* test collision between segment and plane and retrieves the collision point\\r\\n\\t* @method testSegmentPlane\\r\\n\\t* @param {vec3} start segment start\\r\\n\\t* @param {vec3} end segment end\\r\\n\\t* @param {vec3} P point where the plane passes\\t\\r\\n\\t* @param {vec3} N normal of the plane\\r\\n\\t* @param {vec3} result collision position\\r\\n\\t* @return {boolean} returns if the segment collides the plane or it is parallel to the plane\\r\\n\\t*/\\r\\n\\ttestSegmentPlane: (function() { \\r\\n\\t\\tvar temp = vec3.create();\\r\\n\\t\\treturn function(start, end, P, N, result)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar D = vec3.dot( P, N );\\r\\n\\t\\t\\tvar numer = D - vec3.dot(N, start);\\r\\n\\t\\t\\tvar direction = vec3.sub( temp, end, start );\\r\\n\\t\\t\\tvar denom = vec3.dot(N, direction);\\r\\n\\t\\t\\tif( Math.abs(denom) < EPSILON)\\r\\n\\t\\t\\t\\treturn false; //parallel \\r\\n\\t\\t\\tvar t = (numer / denom);\\r\\n\\t\\t\\tif(t < 0.0)\\r\\n\\t\\t\\t\\treturn false; //behind the start\\r\\n\\t\\t\\tif(t > 1.0)\\r\\n\\t\\t\\t\\treturn false; //after the end\\r\\n\\t\\t\\tif(result)\\r\\n\\t\\t\\t\\tvec3.add( result, start, vec3.scale( result, direction, t) );\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\t};\\r\\n\\t})(),\\r\\n\\r\\n\\t/**\\r\\n\\t* test a ray sphere collision and retrieves the collision point\\r\\n\\t* @method testRaySphere\\r\\n\\t* @param {vec3} start ray start\\r\\n\\t* @param {vec3} direction ray direction (normalized)\\r\\n\\t* @param {vec3} center center of the sphere\\r\\n\\t* @param {number} radius radius of the sphere\\r\\n\\t* @param {vec3} result [optional] collision position\\r\\n\\t* @param {number} max_dist not fully tested\\r\\n\\t* @return {boolean} returns if the ray collides the sphere\\r\\n\\t*/\\r\\n\\ttestRaySphere: (function() { \\r\\n\\t\\tvar temp = vec3.create();\\r\\n\\t\\treturn function(start, direction, center, radius, result, max_dist)\\r\\n\\t\\t{\\r\\n\\t\\t\\t// sphere equation (centered at origin) x2+y2+z2=r2\\r\\n\\t\\t\\t// ray equation x(t) = p0 + t*dir\\r\\n\\t\\t\\t// substitute x(t) into sphere equation\\r\\n\\t\\t\\t// solution below:\\r\\n\\r\\n\\t\\t\\t// transform ray origin into sphere local coordinates\\r\\n\\t\\t\\tvar orig = vec3.subtract( temp , start, center);\\r\\n\\r\\n\\t\\t\\tvar a = direction[0]*direction[0] + direction[1]*direction[1] + direction[2]*direction[2];\\r\\n\\t\\t\\tvar b = 2*orig[0]*direction[0] + 2*orig[1]*direction[1] + 2*orig[2]*direction[2];\\r\\n\\t\\t\\tvar c = orig[0]*orig[0] + orig[1]*orig[1] + orig[2]*orig[2] - radius*radius;\\r\\n\\t\\t\\t//return quadraticFormula(a,b,c,t0,t1) ? 2 : 0;\\r\\n\\r\\n\\t\\t\\tvar q = b*b - 4*a*c; \\r\\n\\t\\t\\tif( q < 0.0 )\\r\\n\\t\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\t\\tif(result)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar sq = Math.sqrt(q);\\r\\n\\t\\t\\t\\tvar d = 1 / (2*a);\\r\\n\\t\\t\\t\\tvar r1 = ( -b + sq ) * d;\\r\\n\\t\\t\\t\\tvar r2 = ( -b - sq ) * d;\\r\\n\\t\\t\\t\\tvar t = r1 < r2 ? r1 : r2;\\r\\n\\t\\t\\t\\tif(max_dist !== undefined && t > max_dist)\\r\\n\\t\\t\\t\\t\\treturn false;\\r\\n\\t\\t\\t\\tvec3.add(result, start, vec3.scale( result, direction, t ) );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn true;//real roots\\r\\n\\t\\t};\\r\\n\\t})(),\\r\\n\\r\\n\\t/**\\r\\n\\t* test a ray cylinder collision (only vertical cylinders) and retrieves the collision point [not fully tested]\\r\\n\\t* @method testRayCylinder\\r\\n\\t* @param {vec3} start ray start\\r\\n\\t* @param {vec3} direction ray direction\\r\\n\\t* @param {vec3} p center of the cylinder\\r\\n\\t* @param {number} q height of the cylinder\\r\\n\\t* @param {number} r radius of the cylinder\\r\\n\\t* @param {vec3} result collision position\\r\\n\\t* @return {boolean} returns if the ray collides the cylinder\\r\\n\\t*/\\r\\n\\ttestRayCylinder: function(start, direction, p, q, r, result)\\r\\n\\t{\\r\\n\\t\\tvar sa = vec3.clone(start);\\r\\n\\t\\tvar sb = vec3.add(vec3.create(), start, vec3.scale( vec3.create(), direction, 100000) );\\r\\n\\t\\tvar t = 0;\\r\\n\\t\\tvar d = vec3.subtract(vec3.create(),q,p);\\r\\n\\t\\tvar m = vec3.subtract(vec3.create(),sa,p);\\r\\n\\t\\tvar n = vec3.subtract(vec3.create(),sb,sa);\\r\\n\\t\\t//var n = vec3.create(direction);\\r\\n\\r\\n\\t\\tvar md = vec3.dot(m, d);\\r\\n\\t\\tvar nd = vec3.dot(n, d);\\r\\n\\t\\tvar dd = vec3.dot(d, d);\\r\\n\\r\\n\\t\\t// Test if segment fully outside either endcap of cylinder\\r\\n\\t\\tif (md < 0.0 && md + nd < 0.0) return false; // Segment outside �p� side of cylinder\\r\\n\\t\\tif (md > dd && md + nd > dd) return false; // Segment outside �q� side of cylinder\\r\\n\\r\\n\\t\\tvar nn = vec3.dot(n, n);\\r\\n\\t\\tvar mn = vec3.dot(m, n);\\r\\n\\t\\tvar a = dd * nn - nd * nd; \\r\\n\\t\\tvar k = vec3.dot(m,m) - r*r;\\r\\n\\t\\tvar c = dd * k - md * md;\\r\\n\\r\\n\\t\\tif (Math.abs(a) < EPSILON) \\r\\n\\t\\t{\\r\\n\\t\\t\\t// Segment runs parallel to cylinder axis\\r\\n\\t\\t\\tif (c > 0.0) return false;\\r\\n\\t\\t\\t// �a� and thus the segment lie outside cylinder\\r\\n\\t\\t\\t// Now known that segment intersects cylinder; figure out how it intersects\\r\\n\\t\\t\\tif (md < 0.0) t = -mn/nn;\\r\\n\\t\\t\\t// Intersect segment against �p� endcap\\r\\n\\t\\t\\telse if (md > dd)\\r\\n\\t\\t\\t\\tt=(nd-mn)/nn;\\r\\n\\t\\t\\t// Intersect segment against �q� endcap\\r\\n\\t\\t\\telse t = 0.0;\\r\\n\\t\\t\\t// �a� lies inside cylinder\\r\\n\\t\\t\\tif(result) \\r\\n\\t\\t\\t\\tvec3.add(result, sa, vec3.scale(result, n,t) );\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\t}\\r\\n\\t\\tvar b = dd * mn - nd * md;\\r\\n\\t\\tvar discr = b*b - a*c;\\r\\n\\t\\tif (discr < 0.0) \\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t// No real roots; no intersection\\r\\n\\t\\tt = (-b - Math.sqrt(discr)) / a;\\r\\n\\t\\tif (t < 0.0 || t > 1.0) \\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t// Intersection lies outside segment\\r\\n\\t\\tif(md+t*nd < 0.0)\\r\\n\\t\\t{\\r\\n\\t\\t\\t// Intersection outside cylinder on �p� side\\r\\n\\t\\t\\tif (nd <= 0.0) \\r\\n\\t\\t\\t\\treturn false;\\r\\n\\t\\t\\t// Segment pointing away from endcap\\r\\n\\t\\t\\tt = -md / nd;\\r\\n\\t\\t\\t// Keep intersection if Dot(S(t) - p, S(t) - p) <= r^2\\r\\n\\t\\t\\tif(result) \\r\\n\\t\\t\\t\\tvec3.add(result, sa, vec3.scale(result, n,t) );\\r\\n\\t\\t\\treturn k+2*t*(mn+t*nn) <= 0.0;\\r\\n\\t\\t} else if (md+t*nd>dd)\\r\\n\\t\\t{\\r\\n\\t\\t\\t// Intersection outside cylinder on �q� side\\r\\n\\t\\t\\tif (nd >= 0.0) return false; //Segment pointing away from endcap\\r\\n\\t\\t\\tt = (dd - md) / nd;\\r\\n\\t\\t\\t// Keep intersection if Dot(S(t) - q, S(t) - q) <= r^2\\r\\n\\t\\t\\tif(result) \\r\\n\\t\\t\\t\\tvec3.add(result, sa, vec3.scale(result, n,t) );\\r\\n\\t\\t\\treturn k+dd - 2*md+t*(2*(mn - nd)+t*nn) <= 0.0;\\r\\n\\t\\t}\\r\\n\\t\\t// Segment intersects cylinder between the endcaps; t is correct\\r\\n\\t\\tif(result)\\r\\n\\t\\t\\tvec3.add(result, sa, vec3.scale(result, n,t) );\\r\\n\\t\\treturn true;\\r\\n\\t},\\r\\n\\r\\n\\r\\n\\t/**\\r\\n\\t* test a ray bounding-box collision and retrieves the collision point, the BB must be Axis Aligned\\r\\n\\t* @method testRayBox\\r\\n\\t* @param {vec3} start ray start\\r\\n\\t* @param {vec3} direction ray direction\\r\\n\\t* @param {vec3} minB minimum position of the bounding box\\r\\n\\t* @param {vec3} maxB maximim position of the bounding box\\r\\n\\t* @param {vec3} result collision position\\r\\n\\t* @return {boolean} returns if the ray collides the box\\r\\n\\t*/\\r\\n\\ttestRayBox: (function() { \\r\\n\\t\\r\\n\\t\\tvar quadrant = new Float32Array(3);\\r\\n\\t\\tvar candidatePlane = new Float32Array(3);\\r\\n\\t\\tvar maxT = new Float32Array(3);\\r\\n\\t\\r\\n\\treturn function(start, direction, minB, maxB, result, max_dist)\\r\\n\\t{\\r\\n\\t\\t//#define NUMDIM\\t3\\r\\n\\t\\t//#define RIGHT\\t\\t0\\r\\n\\t\\t//#define LEFT\\t\\t1\\r\\n\\t\\t//#define MIDDLE\\t2\\r\\n\\r\\n\\t\\tmax_dist = max_dist || Number.MAX_VALUE;\\r\\n\\r\\n\\t\\tvar inside = true;\\r\\n\\t\\tvar i = 0|0;\\r\\n\\t\\tvar whichPlane;\\r\\n\\t\\t\\r\\n\\t\\tquadrant.fill(0);\\r\\n\\t\\tmaxT.fill(0);\\r\\n\\t\\tcandidatePlane.fill(0);\\r\\n\\r\\n\\t\\t/* Find candidate planes; this loop can be avoided if\\r\\n\\t\\trays cast all from the eye(assume perpsective view) */\\r\\n\\t\\tfor (i=0; i < 3; ++i)\\r\\n\\t\\t\\tif(start[i] < minB[i]) {\\r\\n\\t\\t\\t\\tquadrant[i] = 1;\\r\\n\\t\\t\\t\\tcandidatePlane[i] = minB[i];\\r\\n\\t\\t\\t\\tinside = false;\\r\\n\\t\\t\\t}else if (start[i] > maxB[i]) {\\r\\n\\t\\t\\t\\tquadrant[i] = 0;\\r\\n\\t\\t\\t\\tcandidatePlane[i] = maxB[i];\\r\\n\\t\\t\\t\\tinside = false;\\r\\n\\t\\t\\t}else\\t{\\r\\n\\t\\t\\t\\tquadrant[i] = 2;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t/* Ray origin inside bounding box */\\r\\n\\t\\tif(inside)\\t{\\r\\n\\t\\t\\tif(result)\\r\\n\\t\\t\\t\\tvec3.copy(result, start);\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\t}\\r\\n\\r\\n\\r\\n\\t\\t/* Calculate T distances to candidate planes */\\r\\n\\t\\tfor (i = 0; i < 3; ++i)\\r\\n\\t\\t\\tif (quadrant[i] != 2 && direction[i] != 0.)\\r\\n\\t\\t\\t\\tmaxT[i] = (candidatePlane[i] - start[i]) / direction[i];\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tmaxT[i] = -1.;\\r\\n\\r\\n\\t\\t/* Get largest of the maxT's for final choice of intersection */\\r\\n\\t\\twhichPlane = 0;\\r\\n\\t\\tfor (i = 1; i < 3; i++)\\r\\n\\t\\t\\tif (maxT[whichPlane] < maxT[i])\\r\\n\\t\\t\\t\\twhichPlane = i;\\r\\n\\r\\n\\t\\t/* Check final candidate actually inside box */\\r\\n\\t\\tif (maxT[whichPlane] < 0.) return false;\\r\\n\\t\\tif (maxT[whichPlane] > max_dist) return false; //NOT TESTED\\r\\n\\r\\n\\t\\tfor (i = 0; i < 3; ++i)\\r\\n\\t\\t\\tif (whichPlane != i) {\\r\\n\\t\\t\\t\\tvar res = start[i] + maxT[whichPlane] * direction[i];\\r\\n\\t\\t\\t\\tif (res < minB[i] || res > maxB[i])\\r\\n\\t\\t\\t\\t\\treturn false;\\r\\n\\t\\t\\t\\tif(result)\\r\\n\\t\\t\\t\\t\\tresult[i] = res;\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tif(result)\\r\\n\\t\\t\\t\\t\\tresult[i] = candidatePlane[i];\\r\\n\\t\\t\\t}\\r\\n\\t\\treturn true;\\t\\t\\t\\t/* ray hits box */\\r\\n\\t}\\r\\n\\t})(),\\t\\r\\n\\r\\n\\t/**\\r\\n\\t* test a ray bounding-box collision, it uses the BBox class and allows to use non-axis aligned bbox\\r\\n\\t* @method testRayBBox\\r\\n\\t* @param {vec3} origin ray origin\\r\\n\\t* @param {vec3} direction ray direction\\r\\n\\t* @param {BBox} box in BBox format\\r\\n\\t* @param {mat4} model transformation of the BBox [optional]\\r\\n\\t* @param {vec3} result collision position in world space unless in_local is true\\r\\n\\t* @return {boolean} returns if the ray collides the box\\r\\n\\t*/\\r\\n\\ttestRayBBox: (function(){ \\r\\n\\tvar inv = mat4.create();\\t\\r\\n\\tvar end = vec3.create();\\r\\n\\tvar origin2 = vec3.create();\\r\\n\\treturn function( origin, direction, box, model, result, max_dist, in_local )\\r\\n\\t{\\r\\n\\t\\tif(!origin || !direction || !box)\\r\\n\\t\\t\\tthrow(\\\"parameters missing\\\");\\r\\n\\t\\tif(model)\\r\\n\\t\\t{\\r\\n\\t\\t\\tmat4.invert( inv, model );\\r\\n\\t\\t\\tvec3.add( end, origin, direction );\\r\\n\\t\\t\\torigin = vec3.transformMat4( origin2, origin, inv);\\r\\n\\t\\t\\tvec3.transformMat4( end, end, inv );\\r\\n\\t\\t\\tvec3.sub( end, end, origin );\\r\\n\\t\\t\\tdirection = vec3.normalize( end, end );\\r\\n\\t\\t}\\r\\n\\t\\tvar r = this.testRayBox( origin, direction, box.subarray(6,9), box.subarray(9,12), result, max_dist );\\r\\n\\t\\tif(!in_local && model && result)\\r\\n\\t\\t\\tvec3.transformMat4(result, result, model);\\r\\n\\t\\treturn r;\\r\\n\\t}\\r\\n\\t})(),\\r\\n\\r\\n\\t/**\\r\\n\\t* test if a 3d point is inside a BBox\\r\\n\\t* @method testPointBBox\\r\\n\\t* @param {vec3} point\\r\\n\\t* @param {BBox} bbox\\r\\n\\t* @return {boolean} true if it is inside\\r\\n\\t*/\\r\\n\\ttestPointBBox: function(point, bbox) {\\r\\n\\t\\tif(point[0] < bbox[6] || point[0] > bbox[9] ||\\r\\n\\t\\t\\tpoint[1] < bbox[7] || point[0] > bbox[10] ||\\r\\n\\t\\t\\tpoint[2] < bbox[8] || point[0] > bbox[11])\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\treturn true;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* test if a BBox overlaps another BBox\\r\\n\\t* @method testBBoxBBox\\r\\n\\t* @param {BBox} a\\r\\n\\t* @param {BBox} b\\r\\n\\t* @return {boolean} true if it overlaps\\r\\n\\t*/\\r\\n\\ttestBBoxBBox: function(a, b) \\r\\n\\t{\\r\\n\\t\\tvar tx = Math.abs( b[0] - a[0]);\\r\\n\\t\\tif (tx > (a[3] + b[3]))\\r\\n\\t\\t\\treturn false; //outside\\r\\n\\t\\tvar ty = Math.abs(b[1] - a[1]);\\r\\n\\t\\tif (ty > (a[4] + b[4]))\\r\\n\\t\\t\\treturn false; //outside\\r\\n\\t\\tvar tz = Math.abs( b[2] - a[2]);\\r\\n\\t\\tif (tz > (a[5] + b[5]) )\\r\\n\\t\\t\\treturn false; //outside\\r\\n\\r\\n\\t\\tvar vmin = BBox.getMin(b);\\r\\n\\t\\tif ( geo.testPointBBox(vmin, a) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar vmax = BBox.getMax(b);\\r\\n\\t\\t\\tif (geo.testPointBBox(vmax, a))\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\treturn true;// INSIDE;// this instance contains b\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn true; //OVERLAP; // this instance overlaps with b\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* test if a sphere overlaps a BBox\\r\\n\\t* @method testSphereBBox\\r\\n\\t* @param {vec3} point\\r\\n\\t* @param {float} radius\\r\\n\\t* @param {BBox} bounding_box\\r\\n\\t* @return {boolean} true if it overlaps\\r\\n\\t*/\\r\\n\\ttestSphereBBox: function(center, radius, bbox) \\r\\n\\t{\\r\\n\\t\\t// arvo's algorithm from gamasutra\\r\\n\\t\\t// http://www.gamasutra.com/features/19991018/Gomez_4.htm\\r\\n\\r\\n\\t\\tvar s, d = 0.0;\\r\\n\\t\\t//find the square of the distance\\r\\n\\t\\t//from the sphere to the box\\r\\n\\t\\tvar vmin = BBox.getMin( bbox );\\r\\n\\t\\tvar vmax = BBox.getMax( bbox );\\r\\n\\t\\tfor(var i = 0; i < 3; ++i) \\r\\n\\t\\t{ \\r\\n\\t\\t\\tif( center[i] < vmin[i] )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ts = center[i] - vmin[i];\\r\\n\\t\\t\\t\\td += s*s; \\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if( center[i] > vmax[i] )\\r\\n\\t\\t\\t{ \\r\\n\\t\\t\\t\\ts = center[i] - vmax[i];\\r\\n\\t\\t\\t\\td += s*s; \\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\t//return d <= r*r\\r\\n\\r\\n\\t\\tvar radiusSquared = radius * radius;\\r\\n\\t\\tif (d <= radiusSquared)\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\t// this is used just to know if it overlaps or is just inside, but I dont care\\r\\n\\t\\t\\t// make an aabb aabb test with the sphere aabb to test inside state\\r\\n\\t\\t\\tvar halfsize = vec3.fromValues( radius, radius, radius );\\r\\n\\t\\t\\tvar sphere_bbox = BBox.fromCenterHalfsize( center, halfsize );\\r\\n\\t\\t\\tif ( geo.testBBoxBBox(bbox, sphere_bbox) )\\r\\n\\t\\t\\t\\treturn INSIDE;\\r\\n\\t\\t\\treturn OVERLAP;\\t\\r\\n\\t\\t\\t*/\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn false; //OUTSIDE;\\r\\n\\t},\\r\\n\\r\\n\\tclosestPointBetweenLines: function(a0,a1, b0,b1, p_a, p_b)\\r\\n\\t{\\r\\n\\t\\tvar u = vec3.subtract( vec3.create(), a1, a0 );\\r\\n\\t\\tvar v = vec3.subtract( vec3.create(), b1, b0 );\\r\\n\\t\\tvar w = vec3.subtract( vec3.create(), a0, b0 );\\r\\n\\r\\n\\t\\tvar a = vec3.dot(u,u); // always >= 0\\r\\n\\t\\tvar b = vec3.dot(u,v);\\r\\n\\t\\tvar c = vec3.dot(v,v); // always >= 0\\r\\n\\t\\tvar d = vec3.dot(u,w);\\r\\n\\t\\tvar e = vec3.dot(v,w);\\r\\n\\t\\tvar D = a*c - b*b; // always >= 0\\r\\n\\t\\tvar sc, tc;\\r\\n\\r\\n\\t\\t// compute the line parameters of the two closest points\\r\\n\\t\\tif (D < EPSILON) { // the lines are almost parallel\\r\\n\\t\\t\\tsc = 0.0;\\r\\n\\t\\t\\ttc = (b>c ? d/b : e/c); // use the largest denominator\\r\\n\\t\\t}\\r\\n\\t\\telse {\\r\\n\\t\\t\\tsc = (b*e - c*d) / D;\\r\\n\\t\\t\\ttc = (a*e - b*d) / D;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// get the difference of the two closest points\\r\\n\\t\\tif(p_a)\\tvec3.add(p_a, a0, vec3.scale(vec3.create(),u,sc));\\r\\n\\t\\tif(p_b)\\tvec3.add(p_b, b0, vec3.scale(vec3.create(),v,tc));\\r\\n\\r\\n\\t\\tvar dP = vec3.add( vec3.create(), w, vec3.subtract( vec3.create(), vec3.scale(vec3.create(),u,sc) , vec3.scale(vec3.create(),v,tc)) ); // = L1(sc) - L2(tc)\\r\\n\\t\\treturn vec3.length(dP); // return the closest distance\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* extract frustum planes given a view-projection matrix\\r\\n\\t* @method extractPlanes\\r\\n\\t* @param {mat4} viewprojection matrix\\r\\n\\t* @return {Float32Array} returns all 6 planes in a float32array[24]\\r\\n\\t*/\\r\\n\\textractPlanes: function(vp, planes)\\r\\n\\t{\\r\\n\\t\\tvar planes = planes || new Float32Array(4*6);\\r\\n\\r\\n\\t\\t//right\\r\\n\\t\\tplanes.set( [vp[3] - vp[0], vp[7] - vp[4], vp[11] - vp[8], vp[15] - vp[12] ], 0); \\r\\n\\t\\tnormalize(0);\\r\\n\\r\\n\\t\\t//left\\r\\n\\t\\tplanes.set( [vp[3] + vp[0], vp[ 7] + vp[ 4], vp[11] + vp[ 8], vp[15] + vp[12] ], 4);\\r\\n\\t\\tnormalize(4);\\r\\n\\r\\n\\t\\t//bottom\\r\\n\\t\\tplanes.set( [ vp[ 3] + vp[ 1], vp[ 7] + vp[ 5], vp[11] + vp[ 9], vp[15] + vp[13] ], 8);\\r\\n\\t\\tnormalize(8);\\r\\n\\r\\n\\t\\t//top\\r\\n\\t\\tplanes.set( [ vp[ 3] - vp[ 1], vp[ 7] - vp[ 5], vp[11] - vp[ 9], vp[15] - vp[13] ],12);\\r\\n\\t\\tnormalize(12);\\r\\n\\r\\n\\t\\t//back\\r\\n\\t\\tplanes.set( [ vp[ 3] - vp[ 2], vp[ 7] - vp[ 6], vp[11] - vp[10], vp[15] - vp[14] ],16);\\r\\n\\t\\tnormalize(16);\\r\\n\\r\\n\\t\\t//front\\r\\n\\t\\tplanes.set( [ vp[ 3] + vp[ 2], vp[ 7] + vp[ 6], vp[11] + vp[10], vp[15] + vp[14] ],20);\\r\\n\\t\\tnormalize(20);\\r\\n\\r\\n\\t\\treturn planes;\\r\\n\\r\\n\\t\\tfunction normalize(pos)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar N = planes.subarray(pos,pos+3);\\r\\n\\t\\t\\tvar l = vec3.length(N);\\r\\n\\t\\t\\tif(l === 0) return;\\r\\n\\t\\t\\tl = 1.0 / l;\\r\\n\\t\\t\\tplanes[pos] *= l;\\r\\n\\t\\t\\tplanes[pos+1] *= l;\\r\\n\\t\\t\\tplanes[pos+2] *= l;\\r\\n\\t\\t\\tplanes[pos+3] *= l;\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* test a BBox against the frustum\\r\\n\\t* @method frustumTestBox\\r\\n\\t* @param {Float32Array} planes frustum planes\\r\\n\\t* @param {BBox} boundindbox in BBox format\\r\\n\\t* @return {enum} CLIP_INSIDE, CLIP_OVERLAP, CLIP_OUTSIDE\\r\\n\\t*/\\r\\n\\tfrustumTestBox: function(planes, box)\\r\\n\\t{\\r\\n\\t\\tvar flag = 0, o = 0;\\r\\n\\r\\n\\t\\tflag = planeBoxOverlap(planes.subarray(0,4),box);\\r\\n\\t\\tif (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag;\\r\\n\\t\\tflag = planeBoxOverlap(planes.subarray(4,8),box);\\r\\n\\t\\tif (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag;\\r\\n\\t\\tflag = planeBoxOverlap(planes.subarray(8,12),box);\\r\\n\\t\\tif (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag;\\r\\n\\t\\tflag = planeBoxOverlap(planes.subarray(12,16),box);\\r\\n\\t\\tif (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag;\\r\\n\\t\\tflag = planeBoxOverlap(planes.subarray(16,20),box);\\r\\n\\t\\tif (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag;\\r\\n\\t\\tflag = planeBoxOverlap(planes.subarray(20,24),box);\\r\\n\\t\\tif (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag;\\r\\n\\r\\n\\t\\treturn o == 0 ? CLIP_INSIDE : CLIP_OVERLAP;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* test a Sphere against the frustum\\r\\n\\t* @method frustumTestSphere\\r\\n\\t* @param {vec3} center sphere center\\r\\n\\t* @param {number} radius sphere radius\\r\\n\\t* @return {enum} CLIP_INSIDE, CLIP_OVERLAP, CLIP_OUTSIDE\\r\\n\\t*/\\r\\n\\r\\n\\tfrustumTestSphere: function(planes, center, radius)\\r\\n\\t{\\r\\n\\t\\tvar dist;\\r\\n\\t\\tvar overlap = false;\\r\\n\\r\\n\\t\\tdist = distanceToPlane( planes.subarray(0,4), center );\\r\\n\\t\\tif( dist < -radius ) return CLIP_OUTSIDE;\\r\\n\\t\\telse if(dist >= -radius && dist <= radius)\\toverlap = true;\\r\\n\\t\\tdist = distanceToPlane( planes.subarray(4,8), center );\\r\\n\\t\\tif( dist < -radius ) return CLIP_OUTSIDE;\\r\\n\\t\\telse if(dist >= -radius && dist <= radius)\\toverlap = true;\\r\\n\\t\\tdist = distanceToPlane( planes.subarray(8,12), center );\\r\\n\\t\\tif( dist < -radius ) return CLIP_OUTSIDE;\\r\\n\\t\\telse if(dist >= -radius && dist <= radius)\\toverlap = true;\\r\\n\\t\\tdist = distanceToPlane( planes.subarray(12,16), center );\\r\\n\\t\\tif( dist < -radius ) return CLIP_OUTSIDE;\\r\\n\\t\\telse if(dist >= -radius && dist <= radius)\\toverlap = true;\\r\\n\\t\\tdist = distanceToPlane( planes.subarray(16,20), center );\\r\\n\\t\\tif( dist < -radius ) return CLIP_OUTSIDE;\\r\\n\\t\\telse if(dist >= -radius && dist <= radius)\\toverlap = true;\\r\\n\\t\\tdist = distanceToPlane( planes.subarray(20,24), center );\\r\\n\\t\\tif( dist < -radius ) return CLIP_OUTSIDE;\\r\\n\\t\\telse if(dist >= -radius && dist <= radius)\\toverlap = true;\\r\\n\\t\\treturn overlap ? CLIP_OVERLAP : CLIP_INSIDE;\\r\\n\\t},\\r\\n\\r\\n\\r\\n\\t/**\\r\\n\\t* test if a 2d point is inside a 2d polygon\\r\\n\\t* @method testPoint2DInPolygon\\r\\n\\t* @param {Array} poly array of 2d points\\r\\n\\t* @param {vec2} point\\r\\n\\t* @return {boolean} true if it is inside\\r\\n\\t*/\\r\\n\\ttestPoint2DInPolygon: function(poly, pt) {\\r\\n for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)\\r\\n ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1] < poly[i][1]))\\r\\n && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])\\r\\n && (c = !c);\\r\\n return c;\\r\\n\\t}\\r\\n};\\r\\n\\r\\n/**\\r\\n* BBox is a class to create BoundingBoxes but it works as glMatrix, creating Float32Array with the info inside instead of objects\\r\\n* The bounding box is stored as center,halfsize,min,max,radius (total of 13 floats)\\r\\n* @class BBox\\r\\n*/\\r\\nglobal.BBox = GL.BBox = {\\r\\n\\tcenter:0,\\r\\n\\thalfsize:3,\\r\\n\\tmin:6,\\r\\n\\tmax:9,\\r\\n\\tradius:12,\\r\\n\\tdata_length: 13,\\r\\n\\t\\r\\n\\t//corners: new Float32Array([1,1,1, 1,1,-1, 1,-1,1, 1,-1,-1, -1,1,1, -1,1,-1, -1,-1,1, -1,-1,-1 ]),\\r\\n\\tcorners: [ vec3.fromValues(1,1,1), vec3.fromValues(1,1,-1), vec3.fromValues(1,-1,1), vec3.fromValues(1,-1,-1), vec3.fromValues(-1,1,1), vec3.fromValues(-1,1,-1), vec3.fromValues(-1,-1,1), vec3.fromValues(-1,-1,-1) ] ,\\r\\n\\r\\n\\t/**\\r\\n\\t* create an empty bbox\\r\\n\\t* @method create\\r\\n\\t* @return {BBox} returns a float32array with the bbox\\r\\n\\t*/\\r\\n\\tcreate: function()\\r\\n\\t{\\r\\n\\t\\treturn new Float32Array(13);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* create an bbox copy from another one\\r\\n\\t* @method clone\\r\\n\\t* @return {BBox} returns a float32array with the bbox\\r\\n\\t*/\\r\\n\\tclone: function(bb)\\r\\n\\t{\\r\\n\\t\\treturn new Float32Array(bb);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* copy one bbox into another\\r\\n\\t* @method copy\\r\\n\\t* @param {BBox} out where to store the result\\r\\n\\t* @param {BBox} where to read the bbox\\r\\n\\t* @return {BBox} returns out\\r\\n\\t*/\\r\\n\\tcopy: function(out,bb)\\r\\n\\t{\\r\\n\\t\\tout.set(bb);\\r\\n\\t\\treturn out;\\r\\n\\t},\\t\\r\\n\\r\\n\\t/**\\r\\n\\t* create a bbox from one point\\r\\n\\t* @method fromPoint\\r\\n\\t* @param {vec3} point\\r\\n\\t* @return {BBox} returns a float32array with the bbox\\r\\n\\t*/\\r\\n\\tfromPoint: function(point)\\r\\n\\t{\\r\\n\\t\\tvar bb = this.create();\\r\\n\\t\\tbb.set(point, 0); //center\\r\\n\\t\\tbb.set(point, 6); //min\\r\\n\\t\\tbb.set(point, 9); //max\\r\\n\\t\\treturn bb;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* create a bbox from min and max points\\r\\n\\t* @method fromMinMax\\r\\n\\t* @param {vec3} min\\r\\n\\t* @param {vec3} max\\r\\n\\t* @return {BBox} returns a float32array with the bbox\\r\\n\\t*/\\r\\n\\tfromMinMax: function(min,max)\\r\\n\\t{\\r\\n\\t\\tvar bb = this.create();\\r\\n\\t\\tthis.setMinMax(bb, min, max);\\r\\n\\t\\treturn bb;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* create a bbox from center and halfsize\\r\\n\\t* @method fromCenterHalfsize\\r\\n\\t* @param {vec3} center\\r\\n\\t* @param {vec3} halfsize\\r\\n\\t* @return {BBox} returns a float32array with the bbox\\r\\n\\t*/\\r\\n\\tfromCenterHalfsize: function(center, halfsize)\\r\\n\\t{\\r\\n\\t\\tvar bb = this.create();\\r\\n\\t\\tthis.setCenterHalfsize(bb, center, halfsize);\\r\\n\\t\\treturn bb;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* create a bbox from a typed-array containing points\\r\\n\\t* @method fromPoints\\r\\n\\t* @param {Float32Array} points\\r\\n\\t* @return {BBox} returns a float32array with the bbox\\r\\n\\t*/\\r\\n\\tfromPoints: function(points)\\r\\n\\t{\\r\\n\\t\\tvar bb = this.create();\\r\\n\\t\\tthis.setFromPoints(bb, points);\\r\\n\\t\\treturn bb;\\t\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* set the values to a BB from a set of points\\r\\n\\t* @method setFromPoints\\r\\n\\t* @param {BBox} out where to store the result\\r\\n\\t* @param {Float32Array} points\\r\\n\\t* @return {BBox} returns a float32array with the bbox\\r\\n\\t*/\\r\\n\\tsetFromPoints: function(bb, points)\\r\\n\\t{\\r\\n\\t\\tvar min = bb.subarray(6,9);\\r\\n\\t\\tvar max = bb.subarray(9,12);\\r\\n\\r\\n\\t\\tmin[0] = points[0]; //min.set( points.subarray(0,3) );\\r\\n\\t\\tmin[1] = points[1];\\r\\n\\t\\tmin[2] = points[2];\\r\\n\\t\\tmax.set( min );\\r\\n\\r\\n\\t\\tvar v = 0;\\r\\n\\t\\tfor(var i = 3, l = points.length; i < l; i+=3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar x = points[i];\\r\\n\\t\\t\\tvar y = points[i+1];\\r\\n\\t\\t\\tvar z = points[i+2];\\r\\n\\t\\t\\tif( x < min[0] ) min[0] = x;\\r\\n\\t\\t\\telse if( x > max[0] ) max[0] = x;\\r\\n\\t\\t\\tif( y < min[1] ) min[1] = y;\\r\\n\\t\\t\\telse if( y > max[1] ) max[1] = y;\\r\\n\\t\\t\\tif( z < min[2] ) min[2] = z;\\r\\n\\t\\t\\telse if( z > max[2] ) max[2] = z;\\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\tv = points.subarray(i,i+3);\\r\\n\\t\\t\\tvec3.min( min, v, min);\\r\\n\\t\\t\\tvec3.max( max, v, max);\\r\\n\\t\\t\\t*/\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//center\\r\\n\\t\\tbb[0] = (min[0] + max[0]) * 0.5;\\r\\n\\t\\tbb[1] = (min[1] + max[1]) * 0.5;\\r\\n\\t\\tbb[2] = (min[2] + max[2]) * 0.5;\\r\\n\\t\\t//halfsize\\r\\n\\t\\tbb[3] = max[0] - bb[0];\\r\\n\\t\\tbb[4] = max[1] - bb[1];\\r\\n\\t\\tbb[5] = max[2] - bb[2];\\r\\n\\t\\tbb[12] = Math.sqrt( bb[3]*bb[3] + bb[4]*bb[4] + bb[5]*bb[5] );\\r\\n\\r\\n\\t\\t/*\\r\\n\\t\\tvar center = vec3.add( bb.subarray(0,3), min, max );\\r\\n\\t\\tvec3.scale( center, center, 0.5);\\r\\n\\t\\tvec3.subtract( bb.subarray(3,6), max, center );\\r\\n\\t\\tbb[12] = vec3.length(bb.subarray(3,6)); //radius\\t\\t\\r\\n\\t\\t*/\\r\\n\\t\\treturn bb;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* set the values to a BB from min and max\\r\\n\\t* @method setMinMax\\r\\n\\t* @param {BBox} out where to store the result\\r\\n\\t* @param {vec3} min\\r\\n\\t* @param {vec3} max\\r\\n\\t* @return {BBox} returns out\\r\\n\\t*/\\r\\n\\tsetMinMax: function(bb, min, max)\\r\\n\\t{\\r\\n\\t\\tbb[6] = min[0];\\r\\n\\t\\tbb[7] = min[1];\\r\\n\\t\\tbb[8] = min[2];\\r\\n\\t\\tbb[9] = max[0];\\r\\n\\t\\tbb[10] = max[1];\\r\\n\\t\\tbb[11] = max[2];\\r\\n\\r\\n\\t\\t//halfsize\\r\\n\\t\\tvar halfsize = bb.subarray(3,6); \\r\\n\\t\\tvec3.sub( halfsize, max, min ); //range\\r\\n\\t\\tvec3.scale( halfsize, halfsize, 0.5 );\\r\\n\\r\\n\\t\\t//center\\r\\n\\t\\tbb[0] = max[0] - halfsize[0];\\r\\n\\t\\tbb[1] = max[1] - halfsize[1];\\r\\n\\t\\tbb[2] = max[2] - halfsize[2];\\r\\n\\r\\n\\t\\tbb[12] = vec3.length(bb.subarray(3,6)); //radius\\r\\n\\t\\treturn bb;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* set the values to a BB from center and halfsize\\r\\n\\t* @method setCenterHalfsize\\r\\n\\t* @param {BBox} out where to store the result\\r\\n\\t* @param {vec3} min\\r\\n\\t* @param {vec3} max\\r\\n\\t* @param {number} radius [optional] (the minimum distance from the center to the further point)\\r\\n\\t* @return {BBox} returns out\\r\\n\\t*/\\r\\n\\tsetCenterHalfsize: function(bb, center, halfsize, radius)\\r\\n\\t{\\r\\n\\t\\tbb[0] = center[0];\\r\\n\\t\\tbb[1] = center[1];\\r\\n\\t\\tbb[2] = center[2];\\r\\n\\t\\tbb[3] = halfsize[0];\\r\\n\\t\\tbb[4] = halfsize[1];\\r\\n\\t\\tbb[5] = halfsize[2];\\r\\n\\r\\n\\t\\tvec3.sub(bb.subarray(6,9), bb.subarray(0,3), bb.subarray(3,6) );\\r\\n\\t\\tvec3.add(bb.subarray(9,12), bb.subarray(0,3), bb.subarray(3,6) );\\r\\n\\t\\tif(radius)\\r\\n\\t\\t\\tbb[12] = radius;\\r\\n\\t\\telse\\r\\n\\t\\t\\tbb[12] = vec3.length(halfsize);\\r\\n\\t\\treturn bb;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Apply a matrix transformation to the BBox (applies to every corner and recomputes the BB)\\r\\n\\t* @method transformMat4\\r\\n\\t* @param {BBox} out where to store the result\\r\\n\\t* @param {BBox} bb bbox you want to transform\\r\\n\\t* @param {mat4} mat transformation\\r\\n\\t* @return {BBox} returns out\\r\\n\\t*/\\r\\n\\ttransformMat4: (function(){\\r\\n\\t\\tvar hsx = 0;\\r\\n\\t\\tvar hsy = 0;\\r\\n\\t\\tvar hsz = 0;\\r\\n\\t\\tvar points_buffer = new Float32Array(8*3);\\r\\n\\t\\tvar points = [];\\r\\n\\t\\tfor(var i = 0; i < 24; i += 3 )\\r\\n\\t\\t\\tpoints.push( points_buffer.subarray( i, i+3 ) );\\r\\n\\t\\t\\r\\n\\t\\treturn function( out, bb, mat )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar centerx = bb[0];\\r\\n\\t\\t\\tvar centery = bb[1];\\r\\n\\t\\t\\tvar centerz = bb[2];\\r\\n\\t\\t\\thsx = bb[3];\\r\\n\\t\\t\\thsy = bb[4];\\r\\n\\t\\t\\thsz = bb[5];\\r\\n\\r\\n\\t\\t\\tvar corners = this.corners;\\r\\n\\r\\n\\t\\t\\tfor(var i = 0; i < 8; ++i)\\t\\t\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar corner = corners[i];\\r\\n\\t\\t\\t\\tvar result = points[i];\\r\\n\\t\\t\\t\\tresult[0] = hsx * corner[0] + centerx;\\r\\n\\t\\t\\t\\tresult[1] = hsy * corner[1] + centery;\\r\\n\\t\\t\\t\\tresult[2] = hsz * corner[2] + centerz;\\r\\n\\t\\t\\t\\tmat4.multiplyVec3( result, mat, result );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\treturn this.setFromPoints( out, points_buffer );\\r\\n\\t\\t}\\r\\n\\t})(),\\r\\n\\r\\n\\r\\n\\t/**\\r\\n\\t* Computes the eight corners of the BBox and returns it\\r\\n\\t* @method getCorners\\r\\n\\t* @param {BBox} bb the bounding box\\r\\n\\t* @param {Float32Array} result optional, should be 8 * 3\\r\\n\\t* @return {Float32Array} returns the 8 corners\\r\\n\\t*/\\r\\n\\tgetCorners: function( bb, result )\\r\\n\\t{\\r\\n\\t\\tvar center = bb; //.subarray(0,3); AVOID GC\\r\\n\\t\\tvar halfsize = bb.subarray(3,6);\\r\\n\\r\\n\\t\\tvar corners = null;\\r\\n\\t\\tif(result)\\r\\n\\t\\t{\\r\\n\\t\\t\\tresult.set(this.corners);\\r\\n\\t\\t\\tcorners = result;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tcorners = new Float32Array( this.corners );\\r\\n\\r\\n\\t\\tfor(var i = 0; i < 8; ++i)\\t\\t\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar corner = corners.subarray(i*3, i*3+3);\\r\\n\\t\\t\\tvec3.multiply( corner, halfsize, corner );\\r\\n\\t\\t\\tvec3.add( corner, corner, center );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn corners;\\r\\n\\t},\\t\\r\\n\\r\\n\\tmerge: function( out, a, b )\\r\\n\\t{\\r\\n\\t\\tvar min = out.subarray(6,9);\\r\\n\\t\\tvar max = out.subarray(9,12);\\r\\n\\t\\tvec3.min( min, a.subarray(6,9), b.subarray(6,9) );\\r\\n\\t\\tvec3.max( max, a.subarray(9,12), b.subarray(9,12) );\\r\\n\\t\\treturn BBox.setMinMax( out, min, max );\\r\\n\\t},\\r\\n\\r\\n\\textendToPoint: function( out, p )\\r\\n\\t{\\r\\n\\t\\tif( p[0] < out[6] )\\tout[6] = p[0];\\r\\n\\t\\telse if( p[0] > out[9] ) out[9] = p[0];\\r\\n\\r\\n\\t\\tif( p[1] < out[7] )\\tout[7] = p[1];\\r\\n\\t\\telse if( p[1] > out[10] ) out[10] = p[1];\\r\\n\\r\\n\\r\\n\\t\\tif( p[2] < out[8] )\\tout[8] = p[2];\\r\\n\\t\\telse if( p[2] > out[11] ) out[11] = p[2];\\r\\n\\r\\n\\t\\t//recompute \\r\\n\\t\\tvar min = out.subarray(6,9);\\r\\n\\t\\tvar max = out.subarray(9,12);\\r\\n\\t\\tvar center = vec3.add( out.subarray(0,3), min, max );\\r\\n\\t\\tvec3.scale( center, center, 0.5);\\r\\n\\t\\tvec3.subtract( out.subarray(3,6), max, center );\\r\\n\\t\\tout[12] = vec3.length( out.subarray(3,6) ); //radius\\t\\t\\r\\n\\t\\treturn out;\\r\\n\\t},\\r\\n\\r\\n\\tgetCenter: function(bb) { return bb.subarray(0,3); },\\r\\n\\tgetHalfsize: function(bb) { return bb.subarray(3,6); },\\r\\n\\tgetMin: function(bb) { return bb.subarray(6,9); },\\r\\n\\tgetMax: function(bb) { return bb.subarray(9,12); },\\r\\n\\tgetRadius: function(bb) { return bb[12]; }\\r\\n\\t//setCenter,setHalfsize not coded, too much work to update all\\r\\n}\\r\\n\\r\\nglobal.distanceToPlane = GL.distanceToPlane = function distanceToPlane(plane, point)\\r\\n{\\r\\n\\treturn vec3.dot(plane,point) + plane[3];\\r\\n}\\r\\n\\r\\nglobal.planeBoxOverlap = GL.planeBoxOverlap = function planeBoxOverlap(plane, box)\\r\\n{\\r\\n\\tvar n = plane; //.subarray(0,3); \\r\\n\\tvar d = plane[3];\\r\\n\\t//hack, to avoif GC I use indices directly\\r\\n\\tvar center = box; //.subarray(0,3);\\r\\n\\tvar halfsize = box; //.subarray(3,6);\\r\\n\\r\\n\\tvar radius = Math.abs( halfsize[3] * n[0] ) + Math.abs( halfsize[4] * n[1] ) + Math.abs( halfsize[5] * n[2] );\\r\\n\\tvar distance = vec3.dot(n,center) + d;\\r\\n\\r\\n\\tif (distance <= -radius)\\r\\n\\t\\treturn CLIP_OUTSIDE;\\r\\n\\telse if (distance <= radius)\\r\\n\\t\\treturn CLIP_OVERLAP;\\r\\n\\treturn CLIP_INSIDE;\\r\\n}\\r\\n\\r\\n/**\\r\\n* @namespace GL\\r\\n*/\\r\\n\\r\\n/**\\r\\n* Octree generator for fast ray triangle collision with meshes\\r\\n*\\tDependencies: glmatrix.js (for vector and matrix operations)\\r\\n* @class Octree\\r\\n* @constructor\\r\\n* @param {Mesh} mesh object containing vertices buffer (indices buffer optional)\\r\\n*/\\r\\n\\r\\nglobal.Octree = GL.Octree = function Octree( mesh )\\r\\n{\\r\\n\\tthis.root = null;\\r\\n\\tthis.total_depth = 0;\\r\\n\\tthis.total_nodes = 0;\\r\\n\\tif(mesh)\\r\\n\\t{\\r\\n\\t\\tthis.buildFromMesh(mesh);\\r\\n\\t\\tthis.total_nodes = this.trim();\\r\\n\\t}\\r\\n}\\r\\n\\r\\nOctree.MAX_NODE_TRIANGLES_RATIO = 0.1;\\r\\nOctree.MAX_OCTREE_DEPTH = 8;\\r\\nOctree.OCTREE_MARGIN_RATIO = 0.01;\\r\\nOctree.OCTREE_MIN_MARGIN = 0.1;\\r\\n\\r\\nvar octree_tested_boxes = 0;\\r\\nvar octree_tested_triangles = 0;\\r\\n\\r\\nOctree.prototype.buildFromMesh = function( mesh )\\r\\n{\\r\\n\\tthis.total_depth = 0;\\r\\n\\tthis.total_nodes = 0;\\r\\n\\r\\n\\tvar vertices = mesh.getBuffer(\\\"vertices\\\").data;\\r\\n\\tvar triangles = mesh.getIndexBuffer(\\\"triangles\\\");\\r\\n\\tif(triangles) \\r\\n\\t\\ttriangles = triangles.data; //get the internal data\\r\\n\\r\\n\\tvar root = this.computeAABB(vertices);\\r\\n\\tthis.root = root;\\r\\n\\tthis.total_nodes = 1;\\r\\n\\tthis.total_triangles = triangles ? triangles.length / 3 : vertices.length / 9;\\r\\n\\tthis.max_node_triangles = this.total_triangles * Octree.MAX_NODE_TRIANGLES_RATIO;\\r\\n\\r\\n\\tvar margin = vec3.create();\\r\\n\\tvec3.scale( margin, root.size, Octree.OCTREE_MARGIN_RATIO );\\r\\n\\tif(margin[0] < Octree.OCTREE_MIN_MARGIN) margin[0] = Octree.OCTREE_MIN_MARGIN;\\r\\n\\tif(margin[1] < Octree.OCTREE_MIN_MARGIN) margin[1] = Octree.OCTREE_MIN_MARGIN;\\r\\n\\tif(margin[2] < Octree.OCTREE_MIN_MARGIN) margin[2] = Octree.OCTREE_MIN_MARGIN;\\r\\n\\r\\n\\tvec3.sub(root.min, root.min, margin);\\r\\n\\tvec3.add(root.max, root.max, margin);\\r\\n\\r\\n\\troot.faces = [];\\r\\n\\troot.inside = 0;\\r\\n\\r\\n\\r\\n\\t//indexed\\r\\n\\tif(triangles)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < triangles.length; i+=3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar face = new Float32Array([vertices[triangles[i]*3], vertices[triangles[i]*3+1],vertices[triangles[i]*3+2],\\r\\n\\t\\t\\t\\t\\t\\tvertices[triangles[i+1]*3], vertices[triangles[i+1]*3+1],vertices[triangles[i+1]*3+2],\\r\\n\\t\\t\\t\\t\\t\\tvertices[triangles[i+2]*3], vertices[triangles[i+2]*3+1],vertices[triangles[i+2]*3+2],i/3]);\\r\\n\\t\\t\\tthis.addToNode( face,root,0);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < vertices.length; i+=9)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar face = new Float32Array( 10 );\\r\\n\\t\\t\\tface.set( vertices.subarray(i,i+9) );\\r\\n\\t\\t\\tface[9] = i/9;\\r\\n\\t\\t\\tthis.addToNode(face,root,0);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn root;\\r\\n}\\r\\n\\r\\nOctree.prototype.addToNode = function( face, node, depth )\\r\\n{\\r\\n\\tnode.inside += 1;\\r\\n\\r\\n\\t//has children\\r\\n\\tif(node.c)\\r\\n\\t{\\r\\n\\t\\tvar aabb = this.computeAABB(face);\\r\\n\\t\\tvar added = false;\\r\\n\\t\\tfor(var i in node.c)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar child = node.c[i];\\r\\n\\t\\t\\tif (Octree.isInsideAABB(aabb,child))\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.addToNode(face,child, depth+1);\\r\\n\\t\\t\\t\\tadded = true;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tif(!added)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(node.faces == null)\\r\\n\\t\\t\\t\\tnode.faces = [];\\r\\n\\t\\t\\tnode.faces.push(face);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse //add till full, then split\\r\\n\\t{\\r\\n\\t\\tif(node.faces == null)\\r\\n\\t\\t\\tnode.faces = [];\\r\\n\\t\\tnode.faces.push(face);\\r\\n\\r\\n\\t\\t//split\\r\\n\\t\\tif(node.faces.length > this.max_node_triangles && depth < Octree.MAX_OCTREE_DEPTH)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.splitNode(node);\\r\\n\\t\\t\\tif(this.total_depth < depth + 1)\\r\\n\\t\\t\\t\\tthis.total_depth = depth + 1;\\r\\n\\r\\n\\t\\t\\tvar faces = node.faces.concat();\\r\\n\\t\\t\\tnode.faces = null;\\r\\n\\r\\n\\t\\t\\t//redistribute all nodes\\r\\n\\t\\t\\tfor(var i in faces)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar face = faces[i];\\r\\n\\t\\t\\t\\tvar aabb = this.computeAABB(face);\\r\\n\\t\\t\\t\\tvar added = false;\\r\\n\\t\\t\\t\\tfor(var j in node.c)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar child = node.c[j];\\r\\n\\t\\t\\t\\t\\tif (Octree.isInsideAABB(aabb,child))\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tthis.addToNode(face,child, depth+1);\\r\\n\\t\\t\\t\\t\\t\\tadded = true;\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif (!added)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(node.faces == null)\\r\\n\\t\\t\\t\\t\\t\\tnode.faces = [];\\r\\n\\t\\t\\t\\t\\tnode.faces.push(face);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n};\\r\\n\\r\\nOctree.prototype.octree_pos_ref = [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]];\\r\\n\\r\\nOctree.prototype.splitNode = function(node)\\r\\n{\\r\\n\\tnode.c = [];\\r\\n\\tvar half = [(node.max[0] - node.min[0]) * 0.5, (node.max[1] - node.min[1]) * 0.5, (node.max[2] - node.min[2]) * 0.5];\\r\\n\\r\\n\\tfor(var i in this.octree_pos_ref)\\r\\n\\t{\\r\\n\\t\\tvar ref = this.octree_pos_ref[i];\\r\\n\\r\\n\\t\\tvar newnode = {};\\r\\n\\t\\tthis.total_nodes += 1;\\r\\n\\r\\n\\t\\tnewnode.min = [ node.min[0] + half[0] * ref[0], node.min[1] + half[1] * ref[1], node.min[2] + half[2] * ref[2]];\\r\\n\\t\\tnewnode.max = [newnode.min[0] + half[0], newnode.min[1] + half[1], newnode.min[2] + half[2]];\\r\\n\\t\\tnewnode.faces = null;\\r\\n\\t\\tnewnode.inside = 0;\\r\\n\\t\\tnode.c.push(newnode);\\r\\n\\t}\\r\\n}\\r\\n\\r\\nOctree.prototype.computeAABB = function(vertices)\\r\\n{\\r\\n\\tvar min = new Float32Array([ vertices[0], vertices[1], vertices[2] ]);\\r\\n\\tvar max = new Float32Array([ vertices[0], vertices[1], vertices[2] ]);\\r\\n\\r\\n\\tfor(var i = 0; i < vertices.length; i+=3)\\r\\n\\t{\\r\\n\\t\\tfor(var j = 0; j < 3; j++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(min[j] > vertices[i+j]) \\r\\n\\t\\t\\t\\tmin[j] = vertices[i+j];\\r\\n\\t\\t\\tif(max[j] < vertices[i+j]) \\r\\n\\t\\t\\t\\tmax[j] = vertices[i+j];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn {min: min, max: max, size: vec3.sub( vec3.create(), max, min) };\\r\\n}\\r\\n\\r\\n//remove empty nodes\\r\\nOctree.prototype.trim = function(node)\\r\\n{\\r\\n\\tnode = node || this.root;\\r\\n\\tif(!node.c)\\r\\n\\t\\treturn 1;\\r\\n\\r\\n\\tvar num = 1;\\r\\n\\tvar valid = [];\\r\\n\\tvar c = node.c;\\r\\n\\tfor(var i = 0; i < c.length; ++i)\\r\\n\\t{\\r\\n\\t\\tif(c[i].inside)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvalid.push(c[i]);\\r\\n\\t\\t\\tnum += this.trim(c[i]);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\tnode.c = valid;\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Test collision between ray and triangles in the octree\\r\\n* @method testRay\\r\\n* @param {vec3} origin ray origin position\\r\\n* @param {vec3} direction ray direction position\\r\\n* @param {number} dist_min\\r\\n* @param {number} dist_max\\r\\n* @return {HitTest} object containing pos and normal\\r\\n*/\\r\\nOctree.prototype.testRay = (function(){ \\r\\n\\tvar origin_temp = vec3.create();\\r\\n\\tvar direction_temp = vec3.create();\\r\\n\\tvar min_temp = vec3.create();\\r\\n\\tvar max_temp = vec3.create();\\r\\n\\r\\n\\treturn function(origin, direction, dist_min, dist_max, test_backfaces )\\r\\n\\t{\\r\\n\\t\\toctree_tested_boxes = 0;\\r\\n\\t\\toctree_tested_triangles = 0;\\r\\n\\r\\n\\t\\tif(!this.root)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthrow(\\\"Error: octree not build\\\");\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\torigin_temp.set( origin );\\r\\n\\t\\tdirection_temp.set( direction );\\r\\n\\t\\tmin_temp.set( this.root.min );\\r\\n\\t\\tmax_temp.set( this.root.max );\\r\\n\\r\\n\\t\\tvar test = Octree.hitTestBox( origin_temp, direction_temp, min_temp, max_temp );\\r\\n\\t\\tif(!test) //no collision with mesh bounding box\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar test = Octree.testRayInNode( this.root, origin_temp, direction_temp, test_backfaces );\\r\\n\\t\\tif(test != null)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pos = vec3.scale( vec3.create(), direction, test.t );\\r\\n\\t\\t\\tvec3.add( pos, pos, origin );\\r\\n\\t\\t\\ttest.pos = pos;\\r\\n\\t\\t\\treturn test;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n})();\\r\\n\\r\\n/**\\r\\n* test collision between sphere and the triangles in the octree (only test if there is any vertex inside the sphere)\\r\\n* @method testSphere\\r\\n* @param {vec3} origin sphere center\\r\\n* @param {number} radius\\r\\n* @return {Boolean} true if the sphere collided with the mesh\\r\\n*/\\r\\nOctree.prototype.testSphere = function( origin, radius )\\r\\n{\\r\\n\\torigin = vec3.clone(origin);\\r\\n\\toctree_tested_boxes = 0;\\r\\n\\toctree_tested_triangles = 0;\\r\\n\\r\\n\\tif(!this.root)\\r\\n\\t\\tthrow(\\\"Error: octree not build\\\");\\r\\n\\r\\n\\t//better to use always the radius squared, because all the calculations are going to do that\\r\\n\\tvar rr = radius * radius;\\r\\n\\r\\n\\tif( !Octree.testSphereBox( origin, rr, vec3.clone(this.root.min), vec3.clone(this.root.max) ) )\\r\\n\\t\\treturn false; //out of the box\\r\\n\\r\\n\\treturn Octree.testSphereInNode( this.root, origin, rr );\\r\\n}\\r\\n\\r\\n//WARNING: cannot use static here, it uses recursion\\r\\nOctree.testRayInNode = function( node, origin, direction, test_backfaces )\\r\\n{\\r\\n\\tvar test = null;\\r\\n\\tvar prev_test = null;\\r\\n\\toctree_tested_boxes += 1;\\r\\n\\r\\n\\t//test faces\\r\\n\\tif(node.faces)\\r\\n\\t\\tfor(var i = 0, l = node.faces.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar face = node.faces[i];\\r\\n\\t\\t\\toctree_tested_triangles += 1;\\r\\n\\t\\t\\ttest = Octree.hitTestTriangle( origin, direction, face.subarray(0,3) , face.subarray(3,6), face.subarray(6,9), test_backfaces );\\r\\n\\t\\t\\tif (test==null)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\ttest.face = face;\\r\\n\\t\\t\\tif(prev_test)\\r\\n\\t\\t\\t\\tprev_test.mergeWith( test );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tprev_test = test;\\r\\n\\t\\t}\\r\\n\\r\\n\\t//WARNING: cannot use statics here, this function uses recursion\\r\\n\\tvar child_min = vec3.create();\\r\\n\\tvar child_max = vec3.create();\\r\\n\\r\\n\\t//test children nodes faces\\r\\n\\tvar child;\\r\\n\\tif(node.c)\\r\\n\\t\\tfor(var i = 0; i < node.c.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tchild = node.c[i];\\r\\n\\t\\t\\tchild_min.set( child.min );\\r\\n\\t\\t\\tchild_max.set( child.max );\\r\\n\\r\\n\\t\\t\\t//test with node box\\r\\n\\t\\t\\ttest = Octree.hitTestBox( origin, direction, child_min, child_max );\\r\\n\\t\\t\\tif( test == null )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//nodebox behind current collision, then ignore node\\r\\n\\t\\t\\tif(prev_test && test.t > prev_test.t)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//test collision with node\\r\\n\\t\\t\\ttest = Octree.testRayInNode( child, origin, direction, test_backfaces );\\r\\n\\t\\t\\tif(test == null)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(prev_test)\\r\\n\\t\\t\\t\\tprev_test.mergeWith( test );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tprev_test = test;\\r\\n\\t\\t}\\r\\n\\r\\n\\treturn prev_test;\\r\\n}\\r\\n\\r\\n//WARNING: cannot use static here, it uses recursion\\r\\nOctree.testSphereInNode = function( node, origin, radius2 )\\r\\n{\\r\\n\\tvar test = null;\\r\\n\\tvar prev_test = null;\\r\\n\\toctree_tested_boxes += 1;\\r\\n\\r\\n\\t//test faces\\r\\n\\tif(node.faces)\\r\\n\\t\\tfor(var i = 0, l = node.faces.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar face = node.faces[i];\\r\\n\\t\\t\\toctree_tested_triangles += 1;\\r\\n\\t\\t\\tif( Octree.testSphereTriangle( origin, radius2, face.subarray(0,3) , face.subarray(3,6), face.subarray(6,9) ) )\\r\\n\\t\\t\\t\\treturn true;\\r\\n\\t\\t}\\r\\n\\r\\n\\t//WARNING: cannot use statics here, this function uses recursion\\r\\n\\tvar child_min = vec3.create();\\r\\n\\tvar child_max = vec3.create();\\r\\n\\r\\n\\t//test children nodes faces\\r\\n\\tvar child;\\r\\n\\tif(node.c)\\r\\n\\t\\tfor(var i = 0; i < node.c.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tchild = node.c[i];\\r\\n\\t\\t\\tchild_min.set( child.min );\\r\\n\\t\\t\\tchild_max.set( child.max );\\r\\n\\r\\n\\t\\t\\t//test with node box\\r\\n\\t\\t\\tif( !Octree.testSphereBox( origin, radius2, child_min, child_max ) )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//test collision with node content\\r\\n\\t\\t\\tif( Octree.testSphereInNode( child, origin, radius2 ) )\\r\\n\\t\\t\\t\\treturn true;\\r\\n\\t\\t}\\r\\n\\r\\n\\treturn false;\\r\\n}\\r\\n\\r\\n//test if one bounding is inside or overlapping another bounding\\r\\nOctree.isInsideAABB = function(a,b)\\r\\n{\\r\\n\\tif(a.min[0] < b.min[0] || a.min[1] < b.min[1] || a.min[2] < b.min[2] ||\\r\\n\\t\\ta.max[0] > b.max[0] || a.max[1] > b.max[1] || a.max[2] > b.max[2])\\r\\n\\t\\treturn false;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n\\r\\nOctree.hitTestBox = (function(){ \\r\\n\\tvar tMin = vec3.create();\\r\\n\\tvar tMax = vec3.create();\\r\\n\\tvar inv = vec3.create();\\r\\n\\tvar t1 = vec3.create();\\r\\n\\tvar t2 = vec3.create();\\r\\n\\tvar tmp = vec3.create();\\r\\n\\tvar epsilon = 1.0e-6;\\r\\n\\tvar eps = vec3.fromValues( epsilon,epsilon,epsilon );\\r\\n\\t\\r\\n\\treturn function( origin, ray, box_min, box_max ) {\\r\\n\\t\\tvec3.subtract( tMin, box_min, origin );\\r\\n\\t\\tvec3.subtract( tMax, box_max, origin );\\r\\n\\t\\t\\r\\n\\t\\tif(\\tvec3.maxValue(tMin) < 0 && vec3.minValue(tMax) > 0)\\r\\n\\t\\t\\treturn new HitTest(0,origin,ray);\\r\\n\\r\\n\\t\\tinv[0] = 1/ray[0];\\tinv[1] = 1/ray[1];\\tinv[2] = 1/ray[2];\\r\\n\\t\\tvec3.multiply(tMin, tMin, inv);\\r\\n\\t\\tvec3.multiply(tMax, tMax, inv);\\r\\n\\t\\tvec3.min(t1, tMin, tMax);\\r\\n\\t\\tvec3.max(t2, tMin, tMax);\\r\\n\\t\\tvar tNear = vec3.maxValue(t1);\\r\\n\\t\\tvar tFar = vec3.minValue(t2);\\r\\n\\r\\n\\t\\tif (tNear > 0 && tNear < tFar) {\\r\\n\\t\\t\\tvar hit = vec3.add( vec3.create(), vec3.scale(tmp, ray, tNear ), origin);\\r\\n\\t\\t\\tvec3.add( box_min, box_min, eps);\\r\\n\\t\\t\\tvec3.subtract(box_min, box_min, eps);\\r\\n\\t\\t\\treturn new HitTest(tNear, hit, vec3.fromValues(\\r\\n\\t\\t\\t (hit[0] > box_max[0]) - (hit[0] < box_min[0]),\\r\\n\\t\\t\\t (hit[1] > box_max[1]) - (hit[1] < box_min[1]),\\r\\n\\t\\t\\t (hit[2] > box_max[2]) - (hit[2] < box_min[2]) ));\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n})();\\r\\n\\r\\nOctree.hitTestTriangle = (function(){ \\r\\n\\t\\r\\n\\tvar AB = vec3.create();\\r\\n\\tvar AC = vec3.create();\\r\\n\\tvar toHit = vec3.create();\\r\\n\\tvar tmp = vec3.create();\\r\\n\\t\\r\\n\\treturn function( origin, ray, A, B, C, test_backfaces ) {\\r\\n\\t\\tvec3.subtract( AB, B, A );\\r\\n\\t\\tvec3.subtract( AC, C, A );\\r\\n\\t\\tvar normal = vec3.cross( vec3.create(), AB, AC ); //returned\\r\\n\\t\\tvec3.normalize( normal, normal );\\r\\n\\t\\tif( !test_backfaces && vec3.dot(normal,ray) > 0)\\r\\n\\t\\t\\treturn null; //ignore backface\\r\\n\\r\\n\\t\\tvar t = vec3.dot(normal, vec3.subtract( tmp, A, origin )) / vec3.dot(normal,ray);\\r\\n\\r\\n\\t if (t > 0)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar hit = vec3.scale(vec3.create(), ray, t); //returned\\r\\n\\t\\t\\tvec3.add(hit, hit, origin);\\r\\n\\t\\t\\tvec3.subtract( toHit, hit, A );\\r\\n\\t\\t\\tvar dot00 = vec3.dot(AC,AC);\\r\\n\\t\\t\\tvar dot01 = vec3.dot(AC,AB);\\r\\n\\t\\t\\tvar dot02 = vec3.dot(AC,toHit);\\r\\n\\t\\t\\tvar dot11 = vec3.dot(AB,AB);\\r\\n\\t\\t\\tvar dot12 = vec3.dot(AB,toHit);\\r\\n\\t\\t\\tvar divide = dot00 * dot11 - dot01 * dot01;\\r\\n\\t\\t\\tvar u = (dot11 * dot02 - dot01 * dot12) / divide;\\r\\n\\t\\t\\tvar v = (dot00 * dot12 - dot01 * dot02) / divide;\\r\\n\\t\\t\\tif (u >= 0 && v >= 0 && u + v <= 1)\\r\\n\\t\\t\\t\\treturn new HitTest(t, hit, normal);\\r\\n\\t\\t}\\r\\n\\t return null;\\r\\n\\t};\\r\\n})();\\r\\n\\r\\n//from http://realtimecollisiondetection.net/blog/?p=103\\r\\n//radius must be squared\\r\\nOctree.testSphereTriangle = (function(){ \\r\\n\\t\\r\\n\\tvar A = vec3.create();\\r\\n\\tvar B = vec3.create();\\r\\n\\tvar C = vec3.create();\\r\\n\\tvar AB = vec3.create();\\r\\n\\tvar AC = vec3.create();\\r\\n\\tvar BC = vec3.create();\\r\\n\\tvar CA = vec3.create();\\r\\n\\tvar V = vec3.create();\\r\\n\\t\\r\\n\\treturn function( P, rr, A_, B_, C_ ) {\\r\\n\\t\\tvec3.sub( A, A_, P );\\r\\n\\t\\tvec3.sub( B, B_, P );\\r\\n\\t\\tvec3.sub( C, C_, P );\\r\\n\\r\\n\\t\\tvec3.sub( AB, B, A );\\r\\n\\t\\tvec3.sub( AC, C, A );\\r\\n\\r\\n\\t\\tvec3.cross( V, AB, AC );\\r\\n\\t\\tvar d = vec3.dot( A, V );\\r\\n\\t\\tvar e = vec3.dot( V, V );\\r\\n\\t\\tvar sep1 = d * d > rr * e;\\r\\n\\t\\tvar aa = vec3.dot(A, A);\\r\\n\\t\\tvar ab = vec3.dot(A, B);\\r\\n\\t\\tvar ac = vec3.dot(A, C);\\r\\n\\t\\tvar bb = vec3.dot(B, B);\\r\\n\\t\\tvar bc = vec3.dot(B, C);\\r\\n\\t\\tvar cc = vec3.dot(C, C);\\r\\n\\t\\tvar sep2 = (aa > rr) & (ab > aa) & (ac > aa);\\r\\n\\t\\tvar sep3 = (bb > rr) & (ab > bb) & (bc > bb);\\r\\n\\t\\tvar sep4 = (cc > rr) & (ac > cc) & (bc > cc);\\r\\n\\r\\n\\t\\tvar d1 = ab - aa;\\r\\n\\t\\tvar d2 = bc - bb;\\r\\n\\t\\tvar d3 = ac - cc;\\r\\n\\r\\n\\t\\tvec3.sub( BC, C, B );\\r\\n\\t\\tvec3.sub( CA, A, C );\\r\\n\\r\\n\\t\\tvar e1 = vec3.dot(AB, AB);\\r\\n\\t\\tvar e2 = vec3.dot(BC, BC);\\r\\n\\t\\tvar e3 = vec3.dot(CA, CA);\\r\\n\\r\\n\\t\\tvar Q1 = vec3.scale(vec3.create(), A, e1); vec3.sub( Q1, Q1, vec3.scale(vec3.create(), AB, d1) );\\r\\n\\t\\tvar Q2 = vec3.scale(vec3.create(), B, e2); vec3.sub( Q2, Q2, vec3.scale(vec3.create(), BC, d2) );\\r\\n\\t\\tvar Q3 = vec3.scale(vec3.create(), C, e3); vec3.sub( Q3, Q3, vec3.scale(vec3.create(), CA, d3) );\\r\\n\\r\\n\\t\\tvar QC = vec3.scale( vec3.create(), C, e1 ); QC = vec3.sub( QC, QC, Q1 );\\r\\n\\t\\tvar QA = vec3.scale( vec3.create(), A, e2 ); QA = vec3.sub( QA, QA, Q2 );\\r\\n\\t\\tvar QB = vec3.scale( vec3.create(), B, e3 ); QB = vec3.sub( QB, QB, Q3 );\\r\\n\\r\\n\\t\\tvar sep5 = ( vec3.dot(Q1, Q1) > rr * e1 * e1) & (vec3.dot(Q1, QC) > 0 );\\r\\n\\t\\tvar sep6 = ( vec3.dot(Q2, Q2) > rr * e2 * e2) & (vec3.dot(Q2, QA) > 0 );\\r\\n\\t\\tvar sep7 = ( vec3.dot(Q3, Q3) > rr * e3 * e3) & (vec3.dot(Q3, QB) > 0 );\\r\\n\\r\\n\\t\\tvar separated = sep1 | sep2 | sep3 | sep4 | sep5 | sep6 | sep7\\r\\n\\t\\treturn !separated;\\r\\n\\t};\\r\\n})();\\r\\n\\r\\nOctree.testSphereBox = function( center, radius2, box_min, box_max ) {\\r\\n\\r\\n\\t// arvo's algorithm from gamasutra\\r\\n\\t// http://www.gamasutra.com/features/19991018/Gomez_4.htm\\r\\n\\tvar s, d = 0.0;\\r\\n\\t//find the square of the distance\\r\\n\\t//from the sphere to the box\\r\\n\\tfor(var i = 0; i < 3; ++i) \\r\\n\\t{ \\r\\n\\t\\tif( center[i] < box_min[i] )\\r\\n\\t\\t{\\r\\n\\t\\t\\ts = center[i] - box_min[i];\\r\\n\\t\\t\\td += s*s; \\r\\n\\t\\t}\\r\\n\\t\\telse if( center[i] > box_max[i] )\\r\\n\\t\\t{ \\r\\n\\t\\t\\ts = center[i] - box_max[i];\\r\\n\\t\\t\\td += s*s; \\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\t//return d <= r*r\\r\\n\\r\\n\\tif (d <= radius2)\\r\\n\\t{\\r\\n\\t\\treturn true;\\r\\n\\t\\t/*\\r\\n\\t\\t// this is used just to know if it overlaps or is just inside, but I dont care\\r\\n\\t\\t// make an aabb aabb test with the sphere aabb to test inside state\\r\\n\\t\\tvar halfsize = vec3.fromValues( radius, radius, radius );\\r\\n\\t\\tvar sphere_bbox = BBox.fromCenterHalfsize( center, halfsize );\\r\\n\\t\\tif ( geo.testBBoxBBox(bbox, sphere_bbox) )\\r\\n\\t\\t\\treturn INSIDE;\\r\\n\\t\\treturn OVERLAP;\\t\\r\\n\\t\\t*/\\r\\n\\t}\\r\\n\\r\\n\\treturn false; //OUTSIDE;\\r\\n};\\r\\n// Provides a convenient raytracing interface.\\r\\n\\r\\n// ### new GL.HitTest([t, hit, normal])\\r\\n// \\r\\n// This is the object used to return hit test results. If there are no\\r\\n// arguments, the constructed argument represents a hit infinitely far\\r\\n// away.\\r\\nglobal.HitTest = GL.HitTest = function HitTest(t, hit, normal) {\\r\\n this.t = arguments.length ? t : Number.MAX_VALUE;\\r\\n this.hit = hit;\\r\\n this.normal = normal;\\r\\n this.face = null;\\r\\n}\\r\\n\\r\\n// ### .mergeWith(other)\\r\\n// \\r\\n// Changes this object to be the closer of the two hit test results.\\r\\nHitTest.prototype = {\\r\\n mergeWith: function(other) {\\r\\n if (other.t > 0 && other.t < this.t) {\\r\\n this.t = other.t;\\r\\n this.hit = other.hit;\\r\\n this.normal = other.normal;\\r\\n\\t this.face = other.face;\\r\\n }\\r\\n }\\r\\n};\\r\\n\\r\\n// ### new GL.Ray( origin, direction )\\r\\nglobal.Ray = GL.Ray = function Ray( origin, direction )\\r\\n{\\r\\n\\tthis.origin = vec3.create();\\r\\n\\tthis.direction = vec3.create();\\r\\n\\tthis.collision_point = vec3.create();\\r\\n\\r\\n\\tif(origin)\\r\\n\\t\\tthis.origin.set( origin );\\r\\n\\tif(direction)\\r\\n\\t\\tthis.direction.set( direction );\\r\\n}\\r\\n\\r\\nRay.prototype.testPlane = function( P, N )\\r\\n{\\r\\n\\treturn geo.testRayPlane( this.origin, this.direction, P, N, this.collision_point );\\r\\n}\\r\\n\\r\\nRay.prototype.testSphere = function( center, radius, max_dist )\\r\\n{\\r\\n\\treturn geo.testRaySphere( this.origin, this.direction, center, radius, this.collision_point, max_dist );\\r\\n}\\r\\n\\r\\n// ### new GL.Raytracer()\\r\\n// \\r\\n// This will read the current modelview matrix, projection matrix, and viewport,\\r\\n// reconstruct the eye position, and store enough information to later generate\\r\\n// per-pixel rays using `getRayForPixel()`.\\r\\n// \\r\\n// Example usage:\\r\\n// \\r\\n// var tracer = new GL.Raytracer();\\r\\n// var ray = tracer.getRayForPixel(\\r\\n// gl.canvas.width / 2,\\r\\n// gl.canvas.height / 2);\\r\\n// var result = GL.Raytracer.hitTestSphere(\\r\\n// tracer.eye, ray, new GL.Vector(0, 0, 0), 1);\\r\\n\\r\\nglobal.Raytracer = GL.Raytracer = function Raytracer( viewprojection_matrix, viewport ) {\\r\\n\\tthis.viewport = vec4.create();\\r\\n\\tthis.ray00 = vec3.create();\\r\\n\\tthis.ray10 = vec3.create();\\r\\n\\tthis.ray01 = vec3.create();\\r\\n\\tthis.ray11 = vec3.create();\\r\\n\\tthis.eye = vec3.create();\\r\\n\\tthis.setup( viewprojection_matrix, viewport );\\r\\n}\\r\\n\\r\\nRaytracer.prototype.setup = function( viewprojection_matrix, viewport )\\r\\n{\\r\\n\\tviewport = viewport || gl.viewport_data;\\r\\n\\tthis.viewport.set( viewport );\\r\\n\\r\\n\\tvar minX = viewport[0], maxX = minX + viewport[2];\\r\\n\\tvar minY = viewport[1], maxY = minY + viewport[3];\\r\\n\\r\\n\\tvec3.set( this.ray00, minX, minY, 1 );\\r\\n\\tvec3.set( this.ray10, maxX, minY, 1 );\\r\\n\\tvec3.set( this.ray01, minX, maxY, 1 );\\r\\n\\tvec3.set( this.ray11, maxX, maxY, 1 );\\r\\n\\tvec3.unproject( this.ray00, this.ray00, viewprojection_matrix, viewport);\\r\\n\\tvec3.unproject( this.ray10, this.ray10, viewprojection_matrix, viewport);\\r\\n\\tvec3.unproject( this.ray01, this.ray01, viewprojection_matrix, viewport);\\r\\n\\tvec3.unproject( this.ray11, this.ray11, viewprojection_matrix, viewport);\\r\\n\\tvar eye = this.eye;\\r\\n\\tvec3.unproject(eye, eye, viewprojection_matrix, viewport);\\r\\n\\tvec3.subtract(this.ray00, this.ray00, eye);\\r\\n\\tvec3.subtract(this.ray10, this.ray10, eye);\\r\\n\\tvec3.subtract(this.ray01, this.ray01, eye);\\r\\n\\tvec3.subtract(this.ray11, this.ray11, eye);\\r\\n}\\r\\n\\r\\n // ### .getRayForPixel(x, y)\\r\\n // \\r\\n // Returns the ray direction originating from the camera and traveling through the pixel `x, y`.\\r\\nRaytracer.prototype.getRayForPixel = (function(){ \\r\\n\\tvar ray0 = vec3.create();\\r\\n\\tvar ray1 = vec3.create();\\r\\n\\treturn function(x, y, out) {\\r\\n\\t\\tout = out || vec3.create();\\r\\n\\t\\tx = (x - this.viewport[0]) / this.viewport[2];\\r\\n\\t\\ty = 1 - (y - this.viewport[1]) / this.viewport[3];\\r\\n\\t\\tvec3.lerp(ray0, this.ray00, this.ray10, x);\\r\\n\\t\\tvec3.lerp(ray1, this.ray01, this.ray11, x);\\r\\n\\t\\tvec3.lerp( out, ray0, ray1, y)\\r\\n\\t\\treturn vec3.normalize( out, out );\\r\\n\\t}\\r\\n})();\\r\\n\\r\\n// ### GL.Raytracer.hitTestBox(origin, ray, min, max)\\r\\n// \\r\\n// Traces the ray starting from `origin` along `ray` against the axis-aligned box\\r\\n// whose coordinates extend from `min` to `max`. Returns a `HitTest` with the\\r\\n// information or `null` for no intersection.\\r\\n// \\r\\n// This implementation uses the [slab intersection method](http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm).\\r\\nvar _hittest_inv = mat4.create();\\r\\nRaytracer.hitTestBox = function(origin, ray, min, max, model) {\\r\\n var _hittest_v3 = new Float32Array(10*3); //reuse memory to speedup\\r\\n \\r\\n if(model)\\r\\n {\\r\\n\\tvar inv = mat4.invert( _hittest_inv, model );\\r\\n\\torigin = mat4.multiplyVec3( _hittest_v3.subarray(3,6), inv, origin );\\r\\n\\tray = mat4.rotateVec3( _hittest_v3.subarray(6,9), inv, ray );\\r\\n }\\r\\n\\r\\n var tMin = vec3.subtract( _hittest_v3.subarray(9,12), min, origin );\\r\\n vec3.divide( tMin, tMin, ray );\\r\\n\\r\\n var tMax = vec3.subtract( _hittest_v3.subarray(12,15), max, origin );\\r\\n vec3.divide( tMax, tMax, ray );\\r\\n\\r\\n var t1 = vec3.min( _hittest_v3.subarray(15,18), tMin, tMax);\\r\\n var t2 = vec3.max( _hittest_v3.subarray(18,21), tMin, tMax);\\r\\n\\r\\n var tNear = vec3.maxValue(t1);\\r\\n var tFar = vec3.minValue(t2);\\r\\n\\r\\n if (tNear > 0 && tNear <= tFar) {\\r\\n var epsilon = 1.0e-6;\\r\\n\\tvar hit = vec3.scale( _hittest_v3.subarray(21,24), ray, tNear);\\r\\n\\tvec3.add( hit, origin, hit );\\r\\n\\r\\n vec3.addValue(_hittest_v3.subarray(24,27), min, epsilon);\\r\\n vec3.subValue(_hittest_v3.subarray(27,30), max, epsilon);\\r\\n\\r\\n return new HitTest(tNear, hit, vec3.fromValues(\\r\\n (hit[0] > max[0]) - (hit[0] < min[0]),\\r\\n (hit[1] > max[1]) - (hit[1] < min[1]),\\r\\n (hit[2] > max[2]) - (hit[2] < min[2])\\r\\n ));\\r\\n }\\r\\n\\r\\n return null;\\r\\n};\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n// ### GL.Raytracer.hitTestSphere(origin, ray, center, radius)\\r\\n// \\r\\n// Traces the ray starting from `origin` along `ray` against the sphere defined\\r\\n// by `center` and `radius`. Returns a `HitTest` with the information or `null`\\r\\n// for no intersection.\\r\\nRaytracer.hitTestSphere = function(origin, ray, center, radius) {\\r\\n var offset = vec3.subtract( vec3.create(), origin,center);\\r\\n var a = vec3.dot(ray,ray);\\r\\n var b = 2 * vec3.dot(ray,offset);\\r\\n var c = vec3.dot(offset,offset) - radius * radius;\\r\\n var discriminant = b * b - 4 * a * c;\\r\\n\\r\\n if (discriminant > 0) {\\r\\n var t = (-b - Math.sqrt(discriminant)) / (2 * a), hit = vec3.add(vec3.create(),origin, vec3.scale(vec3.create(), ray, t));\\r\\n return new HitTest(t, hit, vec3.scale( vec3.create(), vec3.subtract(vec3.create(), hit,center), 1.0/radius));\\r\\n }\\r\\n\\r\\n return null;\\r\\n};\\r\\n\\r\\n\\r\\n// ### GL.Raytracer.hitTestTriangle(origin, ray, a, b, c)\\r\\n// \\r\\n// Traces the ray starting from `origin` along `ray` against the triangle defined\\r\\n// by the points `a`, `b`, and `c`. Returns a `HitTest` with the information or\\r\\n// `null` for no intersection.\\r\\nRaytracer.hitTestTriangle = function(origin, ray, a, b, c) {\\r\\n var ab = vec3.subtract(vec3.create(), b,a );\\r\\n var ac = vec3.subtract(vec3.create(), c,a );\\r\\n var normal = vec3.cross( vec3.create(), ab,ac);\\r\\n vec3.normalize( normal, normal );\\r\\n var t = vec3.dot(normal, vec3.subtract( vec3.create(), a,origin)) / vec3.dot(normal,ray);\\r\\n\\r\\n if (t > 0) {\\r\\n var hit = vec3.add( vec3.create(), origin, vec3.scale(vec3.create(), ray,t));\\r\\n var toHit = vec3.subtract( vec3.create(), hit, a);\\r\\n var dot00 = vec3.dot(ac,ac);\\r\\n var dot01 = vec3.dot(ac,ab);\\r\\n var dot02 = vec3.dot(ac,toHit);\\r\\n var dot11 = vec3.dot(ab,ab);\\r\\n var dot12 = vec3.dot(ab,toHit);\\r\\n var divide = dot00 * dot11 - dot01 * dot01;\\r\\n var u = (dot11 * dot02 - dot01 * dot12) / divide;\\r\\n var v = (dot00 * dot12 - dot01 * dot02) / divide;\\r\\n if (u >= 0 && v >= 0 && u + v <= 1) return new HitTest(t, hit, normal);\\r\\n }\\r\\n\\r\\n return null;\\r\\n};\\r\\n//***** OBJ parser adapted from SpiderGL implementation *****************\\r\\n/**\\r\\n* Parses a OBJ string and returns an object with the info ready to be passed to GL.Mesh.load\\r\\n* @method Mesh.parseOBJ\\r\\n* @param {String} data all the OBJ info to be parsed\\r\\n* @param {Object} options\\r\\n* @return {Object} mesh information (vertices, coords, normals, indices)\\r\\n*/\\r\\n\\r\\nMesh.parseOBJ = function( text, options )\\r\\n{\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\t//final arrays (packed, lineal [ax,ay,az, bx,by,bz ...])\\r\\n\\tvar positionsArray = [ ];\\r\\n\\tvar texcoordsArray = [ ];\\r\\n\\tvar normalsArray = [ ];\\r\\n\\tvar indicesArray = [ ];\\r\\n\\r\\n\\t//unique arrays (not packed, lineal)\\r\\n\\tvar positions = [ ];\\r\\n\\tvar texcoords = [ ];\\r\\n\\tvar normals = [ ];\\r\\n\\tvar facemap = { };\\r\\n\\tvar index = 0;\\r\\n\\r\\n\\tvar line = null;\\r\\n\\tvar f = null;\\r\\n\\tvar pos = 0;\\r\\n\\tvar tex = 0;\\r\\n\\tvar nor = 0;\\r\\n\\tvar x = 0.0;\\r\\n\\tvar y = 0.0;\\r\\n\\tvar z = 0.0;\\r\\n\\tvar tokens = null;\\r\\n\\r\\n\\tvar hasPos = false;\\r\\n\\tvar hasTex = false;\\r\\n\\tvar hasNor = false;\\r\\n\\r\\n\\tvar parsingFaces = false;\\r\\n\\tvar indices_offset = 0;\\r\\n\\tvar negative_offset = -1; //used for weird objs with negative indices\\r\\n\\tvar max_index = 0;\\r\\n\\r\\n\\tvar skip_indices = options.noindex ? options.noindex : (text.length > 10000000 ? true : false);\\r\\n\\t//trace(\\\"SKIP INDICES: \\\" + skip_indices);\\r\\n\\tvar flip_axis = options.flipAxis;\\r\\n\\tvar flip_normals = (flip_axis || options.flipNormals);\\r\\n\\r\\n\\t//used for mesh groups (submeshes)\\r\\n\\tvar group = null;\\r\\n\\tvar groups = [];\\r\\n\\tvar materials_found = {};\\r\\n\\r\\n\\tvar V_CODE = 1;\\r\\n\\tvar VT_CODE = 2;\\r\\n\\tvar VN_CODE = 3;\\r\\n\\tvar F_CODE = 4;\\r\\n\\tvar G_CODE = 5;\\r\\n\\tvar O_CODE = 6;\\r\\n\\tvar codes = { v: V_CODE, vt: VT_CODE, vn: VN_CODE, f: F_CODE, g: G_CODE, o: O_CODE };\\r\\n\\r\\n\\r\\n\\tvar lines = text.split(\\\"\\\\n\\\");\\r\\n\\tvar length = lines.length;\\r\\n\\tfor (var lineIndex = 0; lineIndex < length; ++lineIndex) {\\r\\n\\t\\tline = lines[lineIndex].replace(/[ \\\\t]+/g, \\\" \\\").replace(/\\\\s\\\\s*$/, \\\"\\\"); //trim\\r\\n\\r\\n\\t\\tif (line[0] == \\\"#\\\") continue;\\r\\n\\t\\tif(line == \\\"\\\") continue;\\r\\n\\r\\n\\t\\ttokens = line.split(\\\" \\\");\\r\\n\\t\\tvar code = codes[ tokens[0] ];\\r\\n\\r\\n\\t\\tif(parsingFaces && code == V_CODE) //another mesh?\\r\\n\\t\\t{\\r\\n\\t\\t\\tindices_offset = index;\\r\\n\\t\\t\\tparsingFaces = false;\\r\\n\\t\\t\\t//trace(\\\"multiple meshes: \\\" + indices_offset);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//read and parse numbers\\r\\n\\t\\tif( code <= VN_CODE ) //v,vt,vn\\r\\n\\t\\t{\\r\\n\\t\\t\\tx = parseFloat(tokens[1]);\\r\\n\\t\\t\\ty = parseFloat(tokens[2]);\\r\\n\\t\\t\\tif( code != VT_CODE )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(tokens[3] == '\\\\\\\\') //super weird case, OBJ allows to break lines with slashes...\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t//HACK! only works if the var is the thirth position...\\r\\n\\t\\t\\t\\t\\t++lineIndex;\\r\\n\\t\\t\\t\\t\\tline = lines[lineIndex].replace(/[ \\\\t]+/g, \\\" \\\").replace(/\\\\s\\\\s*$/, \\\"\\\"); //better than trim\\r\\n\\t\\t\\t\\t\\tz = parseFloat(line);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tz = parseFloat(tokens[3]);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (code == V_CODE) {\\r\\n\\t\\t\\tif(flip_axis) //maya and max notation style\\r\\n\\t\\t\\t\\tpositions.push(-1*x,z,y);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tpositions.push(x,y,z);\\r\\n\\t\\t}\\r\\n\\t\\telse if (code == VT_CODE) {\\r\\n\\t\\t\\ttexcoords.push(x,y);\\r\\n\\t\\t}\\r\\n\\t\\telse if (code == VN_CODE) {\\r\\n\\r\\n\\t\\t\\tif(flip_normals) //maya and max notation style\\r\\n\\t\\t\\t\\tnormals.push(-y,-z,x);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tnormals.push(x,y,z);\\r\\n\\t\\t}\\r\\n\\t\\telse if (code == F_CODE) {\\r\\n\\t\\t\\tparsingFaces = true;\\r\\n\\r\\n\\t\\t\\tif (tokens.length < 4) continue; //faces with less that 3 vertices? nevermind\\r\\n\\r\\n\\t\\t\\t//for every corner of this polygon\\r\\n\\t\\t\\tvar polygon_indices = [];\\r\\n\\t\\t\\tfor (var i=1; i < tokens.length; ++i) \\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif (!(tokens[i] in facemap) || skip_indices) \\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tf = tokens[i].split(\\\"/\\\");\\r\\n\\r\\n\\t\\t\\t\\t\\tif (f.length == 1) { //unpacked\\r\\n\\t\\t\\t\\t\\t\\tpos = parseInt(f[0]) - 1;\\r\\n\\t\\t\\t\\t\\t\\ttex = pos;\\r\\n\\t\\t\\t\\t\\t\\tnor = pos;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse if (f.length == 2) { //no normals\\r\\n\\t\\t\\t\\t\\t\\tpos = parseInt(f[0]) - 1;\\r\\n\\t\\t\\t\\t\\t\\ttex = parseInt(f[1]) - 1;\\r\\n\\t\\t\\t\\t\\t\\tnor = -1;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse if (f.length == 3) { //all three indexed\\r\\n\\t\\t\\t\\t\\t\\tpos = parseInt(f[0]) - 1;\\r\\n\\t\\t\\t\\t\\t\\ttex = parseInt(f[1]) - 1;\\r\\n\\t\\t\\t\\t\\t\\tnor = parseInt(f[2]) - 1;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse {\\r\\n\\t\\t\\t\\t\\t\\tconsole.err(\\\"Problem parsing: unknown number of values per face\\\");\\r\\n\\t\\t\\t\\t\\t\\treturn false;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tif(i > 3 && skip_indices) //break polygon in triangles\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t//first\\r\\n\\t\\t\\t\\t\\t\\tvar pl = positionsArray.length;\\r\\n\\t\\t\\t\\t\\t\\tpositionsArray.push( positionsArray[pl - (i-3)*9], positionsArray[pl - (i-3)*9 + 1], positionsArray[pl - (i-3)*9 + 2]);\\r\\n\\t\\t\\t\\t\\t\\tpositionsArray.push( positionsArray[pl - 3], positionsArray[pl - 2], positionsArray[pl - 1]);\\r\\n\\t\\t\\t\\t\\t\\tpl = texcoordsArray.length;\\r\\n\\t\\t\\t\\t\\t\\ttexcoordsArray.push( texcoordsArray[pl - (i-3)*6], texcoordsArray[pl - (i-3)*6 + 1]);\\r\\n\\t\\t\\t\\t\\t\\ttexcoordsArray.push( texcoordsArray[pl - 2], texcoordsArray[pl - 1]);\\r\\n\\t\\t\\t\\t\\t\\tpl = normalsArray.length;\\r\\n\\t\\t\\t\\t\\t\\tnormalsArray.push( normalsArray[pl - (i-3)*9], normalsArray[pl - (i-3)*9 + 1], normalsArray[pl - (i-3)*9 + 2]);\\r\\n\\t\\t\\t\\t\\t\\tnormalsArray.push( normalsArray[pl - 3], normalsArray[pl - 2], normalsArray[pl - 1]);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t//add new vertex\\r\\n\\t\\t\\t\\t\\tx = 0.0;\\r\\n\\t\\t\\t\\t\\ty = 0.0;\\r\\n\\t\\t\\t\\t\\tz = 0.0;\\r\\n\\t\\t\\t\\t\\tif ((pos * 3 + 2) < positions.length) {\\r\\n\\t\\t\\t\\t\\t\\thasPos = true;\\r\\n\\t\\t\\t\\t\\t\\tx = positions[pos*3+0];\\r\\n\\t\\t\\t\\t\\t\\ty = positions[pos*3+1];\\r\\n\\t\\t\\t\\t\\t\\tz = positions[pos*3+2];\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tpositionsArray.push(x,y,z);\\r\\n\\r\\n\\t\\t\\t\\t\\t//add new texture coordinate\\r\\n\\t\\t\\t\\t\\tx = 0.0;\\r\\n\\t\\t\\t\\t\\ty = 0.0;\\r\\n\\t\\t\\t\\t\\tif ((tex * 2 + 1) < texcoords.length) {\\r\\n\\t\\t\\t\\t\\t\\thasTex = true;\\r\\n\\t\\t\\t\\t\\t\\tx = texcoords[tex*2+0];\\r\\n\\t\\t\\t\\t\\t\\ty = texcoords[tex*2+1];\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\ttexcoordsArray.push(x,y);\\r\\n\\r\\n\\t\\t\\t\\t\\t//add new normal\\r\\n\\t\\t\\t\\t\\tx = 0.0;\\r\\n\\t\\t\\t\\t\\ty = 0.0;\\r\\n\\t\\t\\t\\t\\tz = 1.0;\\r\\n\\t\\t\\t\\t\\tif(nor != -1)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tif ((nor * 3 + 2) < normals.length) {\\r\\n\\t\\t\\t\\t\\t\\t\\thasNor = true;\\r\\n\\t\\t\\t\\t\\t\\t\\tx = normals[nor*3+0];\\r\\n\\t\\t\\t\\t\\t\\t\\ty = normals[nor*3+1];\\r\\n\\t\\t\\t\\t\\t\\t\\tz = normals[nor*3+2];\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\r\\n\\t\\t\\t\\t\\t\\tnormalsArray.push(x,y,z);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t//Save the string \\\"10/10/10\\\" and tells which index represents it in the arrays\\r\\n\\t\\t\\t\\t\\tif(!skip_indices)\\r\\n\\t\\t\\t\\t\\t\\tfacemap[tokens[i]] = index++;\\r\\n\\t\\t\\t\\t}//end of 'if this token is new (store and index for later reuse)'\\r\\n\\r\\n\\t\\t\\t\\t//store key for this triplet\\r\\n\\t\\t\\t\\tif(!skip_indices)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar final_index = facemap[tokens[i]];\\r\\n\\t\\t\\t\\t\\tpolygon_indices.push(final_index);\\r\\n\\t\\t\\t\\t\\tif(max_index < final_index)\\r\\n\\t\\t\\t\\t\\t\\tmax_index = final_index;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} //end of for every token on a 'f' line\\r\\n\\r\\n\\t\\t\\t//polygons (not just triangles)\\r\\n\\t\\t\\tif(!skip_indices)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var iP = 2; iP < polygon_indices.length; iP++)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tindicesArray.push( polygon_indices[0], polygon_indices[iP-1], polygon_indices[iP] );\\r\\n\\t\\t\\t\\t\\t//indicesArray.push( [polygon_indices[0], polygon_indices[iP-1], polygon_indices[iP]] );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse if (code == G_CODE) { //tokens[0] == \\\"usemtl\\\"\\r\\n\\t\\t\\tnegative_offset = positions.length / 3 - 1;\\r\\n\\r\\n\\t\\t\\tif(tokens.length > 1)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(group != null)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tgroup.length = indicesArray.length - group.start;\\r\\n\\t\\t\\t\\t\\tif(group.length > 0)\\r\\n\\t\\t\\t\\t\\t\\tgroups.push(group);\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tgroup = {\\r\\n\\t\\t\\t\\t\\tname: tokens[1],\\r\\n\\t\\t\\t\\t\\tstart: indicesArray.length,\\r\\n\\t\\t\\t\\t\\tlength: -1,\\r\\n\\t\\t\\t\\t\\tmaterial: \\\"\\\"\\r\\n\\t\\t\\t\\t};\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse if (tokens[0] == \\\"usemtl\\\") {\\r\\n\\t\\t\\tif(group)\\r\\n\\t\\t\\t\\tgroup.material = tokens[1];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif(!positions.length)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"OBJ doesnt have vertices, maybe the file is not a OBJ\\\");\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tif(group && (indicesArray.length - group.start) > 1)\\r\\n\\t{\\r\\n\\t\\tgroup.length = indicesArray.length - group.start;\\r\\n\\t\\tgroups.push(group);\\r\\n\\t}\\r\\n\\r\\n\\t//deindex streams\\r\\n\\tif((max_index > 256*256 || skip_indices ) && indicesArray.length > 0)\\r\\n\\t{\\r\\n\\t\\tconsole.log(\\\"Deindexing mesh...\\\")\\r\\n\\t\\tvar finalVertices = new Float32Array(indicesArray.length * 3);\\r\\n\\t\\tvar finalNormals = normalsArray && normalsArray.length ? new Float32Array(indicesArray.length * 3) : null;\\r\\n\\t\\tvar finalTexCoords = texcoordsArray && texcoordsArray.length ? new Float32Array(indicesArray.length * 2) : null;\\r\\n\\t\\tfor(var i = 0; i < indicesArray.length; i += 1)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfinalVertices.set( positionsArray.slice( indicesArray[i]*3,indicesArray[i]*3 + 3), i*3 );\\r\\n\\t\\t\\tif(finalNormals)\\r\\n\\t\\t\\t\\tfinalNormals.set( normalsArray.slice( indicesArray[i]*3,indicesArray[i]*3 + 3 ), i*3 );\\r\\n\\t\\t\\tif(finalTexCoords)\\r\\n\\t\\t\\t\\tfinalTexCoords.set( texcoordsArray.slice(indicesArray[i]*2,indicesArray[i]*2 + 2 ), i*2 );\\r\\n\\t\\t}\\r\\n\\t\\tpositionsArray = finalVertices;\\r\\n\\t\\tif(finalNormals)\\r\\n\\t\\t\\tnormalsArray = finalNormals;\\r\\n\\t\\tif(finalTexCoords)\\r\\n\\t\\t\\ttexcoordsArray = finalTexCoords;\\r\\n\\t\\tindicesArray = null;\\r\\n\\t}\\r\\n\\r\\n\\t//Create final mesh object\\r\\n\\tvar mesh = {};\\r\\n\\r\\n\\t//create typed arrays\\r\\n\\tif (hasPos)\\r\\n\\t\\tmesh.vertices = new Float32Array(positionsArray);\\r\\n\\tif (hasNor && normalsArray.length > 0)\\r\\n\\t\\tmesh.normals = new Float32Array(normalsArray);\\r\\n\\tif (hasTex && texcoordsArray.length > 0)\\r\\n\\t\\tmesh.coords = new Float32Array(texcoordsArray);\\r\\n\\tif (indicesArray && indicesArray.length > 0)\\r\\n\\t\\tmesh.triangles = new Uint16Array(indicesArray);\\r\\n\\r\\n\\tvar info = {};\\r\\n\\tif(groups.length > 1)\\r\\n\\t\\tinfo.groups = groups;\\r\\n\\tmesh.info = info;\\r\\n\\r\\n\\tif(options.only_data)\\r\\n\\t\\treturn mesh;\\r\\n\\r\\n\\t//creates and returns a GL.Mesh\\r\\n\\tvar final_mesh = null;\\r\\n\\tfinal_mesh = Mesh.load( mesh, null, options.mesh );\\r\\n\\tfinal_mesh.updateBoundingBox();\\r\\n\\treturn final_mesh;\\r\\n}\\r\\n\\r\\nMesh.parsers[\\\"obj\\\"] = Mesh.parseOBJ;\\r\\n\\r\\nMesh.encoders[\\\"obj\\\"] = function( mesh, options )\\r\\n{\\r\\n\\t//store vertices\\r\\n\\tvar verticesBuffer = mesh.getBuffer(\\\"vertices\\\");\\r\\n\\tif(!verticesBuffer)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar lines = [];\\r\\n\\tlines.push(\\\"# Generated with liteGL.js by Javi Agenjo\\\\n\\\");\\r\\n\\r\\n\\tvar vertices = verticesBuffer.data;\\r\\n\\tfor (var i = 0; i < vertices.length; i+=3)\\r\\n\\t\\tlines.push(\\\"v \\\" + vertices[i].toFixed(4) + \\\" \\\" + vertices[i+1].toFixed(4) + \\\" \\\" + vertices[i+2].toFixed(4));\\r\\n\\r\\n\\t//store normals\\r\\n\\tvar normalsBuffer = mesh.getBuffer(\\\"normals\\\");\\r\\n\\tif(normalsBuffer)\\r\\n\\t{\\r\\n\\t\\tlines.push(\\\"\\\");\\r\\n\\t\\tvar normals = normalsBuffer.data;\\r\\n\\t\\tfor (var i = 0; i < normals.length; i+=3)\\r\\n\\t\\t\\tlines.push(\\\"vn \\\" + normals[i].toFixed(4) + \\\" \\\" + normals[i+1].toFixed(4) + \\\" \\\" + normals[i+2].toFixed(4) );\\r\\n\\t}\\r\\n\\t\\r\\n\\t//store uvs\\r\\n\\tvar coordsBuffer = mesh.getBuffer(\\\"coords\\\");\\r\\n\\tif(coordsBuffer)\\r\\n\\t{\\r\\n\\t\\tlines.push(\\\"\\\");\\r\\n\\t\\tvar coords = coordsBuffer.data;\\r\\n\\t\\tfor (var i = 0; i < coords.length; i+=2)\\r\\n\\t\\t\\tlines.push(\\\"vt \\\" + coords[i].toFixed(4) + \\\" \\\" + coords[i+1].toFixed(4) + \\\" \\\" + \\\" 0.0000\\\");\\r\\n\\t}\\r\\n\\r\\n\\tvar groups = mesh.info.groups;\\r\\n\\r\\n\\r\\n\\t//store faces\\r\\n\\tvar indicesBuffer = mesh.getIndexBuffer(\\\"triangles\\\");\\r\\n\\tif(indicesBuffer)\\r\\n\\t{\\r\\n\\t\\tvar indices = indicesBuffer.data;\\r\\n\\t\\tif(!groups || !groups.length)\\r\\n\\t\\t\\tgroups = [{start:0, length: indices.length, name:\\\"mesh\\\"}];\\r\\n\\t\\tfor(var j = 0; j < groups.length; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar group = groups[j];\\r\\n\\t\\t\\tlines.push(\\\"g \\\" + group.name );\\r\\n\\t\\t\\tlines.push(\\\"usemtl \\\" + (group.material || (\\\"mat_\\\"+j)));\\r\\n\\t\\t\\tvar start = group.start;\\r\\n\\t\\t\\tvar end = start + group.length;\\r\\n\\t\\t\\tfor (var i = start; i < end; i+=3)\\r\\n\\t\\t\\t\\tlines.push(\\\"f \\\" + (indices[i]+1) + \\\"/\\\" + (indices[i]+1) + \\\"/\\\" + (indices[i]+1) + \\\" \\\" + (indices[i+1]+1) + \\\"/\\\" + (indices[i+1]+1) + \\\"/\\\" + (indices[i+1]+1) + \\\" \\\" + (indices[i+2]+1) + \\\"/\\\" + (indices[i+2]+1) + \\\"/\\\" + (indices[i+2]+1) );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse //no indices\\r\\n\\t{\\r\\n\\t\\tif(!groups || !groups.length)\\r\\n\\t\\t\\tgroups = [{start:0, length: (vertices.length / 3), name:\\\"mesh\\\"}];\\r\\n\\t\\tfor(var j = 0; j < groups.length; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar group = groups[j];\\r\\n\\t\\t\\tlines.push(\\\"g \\\" + group.name);\\r\\n\\t\\t\\tlines.push(\\\"usemtl \\\" + (group.material || (\\\"mat_\\\"+j)));\\r\\n\\t\\t\\tvar start = group.start;\\r\\n\\t\\t\\tvar end = start + group.length;\\r\\n\\t\\t\\tfor (var i = start; i < end; i+=3)\\r\\n\\t\\t\\t\\tlines.push( \\\"f \\\" + (i+1) + \\\"/\\\" + (i+1) + \\\"/\\\" + (i+1) + \\\" \\\" + (i+2) + \\\"/\\\" + (i+2) + \\\"/\\\" + (i+2) + \\\" \\\" + (i+3) + \\\"/\\\" + (i+3) + \\\"/\\\" + (i+3) );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\t\\r\\n\\treturn lines.join(\\\"\\\\n\\\");\\r\\n}\\r\\n\\r\\n//simple format to output meshes in ASCII\\r\\nMesh.parsers[\\\"mesh\\\"] = function( text, options )\\r\\n{\\r\\n\\tvar mesh = {};\\r\\n\\r\\n\\tvar lines = text.split(\\\"\\\\n\\\");\\r\\n\\tfor(var i = 0; i < lines.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar line = lines[i];\\r\\n\\t\\tvar type = line[0];\\r\\n\\t\\tvar t = line.substr(1).split(\\\",\\\");\\r\\n\\t\\tvar name = t[0];\\r\\n\\r\\n\\t\\tif(type == \\\"-\\\") //buffer\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar data = new Float32Array( Number(t[1]) );\\r\\n\\t\\t\\tfor(var j = 0; j < data.length; ++j)\\r\\n\\t\\t\\t\\tdata[j] = Number(t[j+2]);\\r\\n\\t\\t\\tmesh[name] = data;\\r\\n\\t\\t}\\r\\n\\t\\telse if(type == \\\"*\\\") //index\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar data = Number(t[1]) > 256*256 ? new Uint32Array( Number(t[1]) ) : new Uint16Array( Number(t[1]) );\\r\\n\\t\\t\\tfor(var j = 0; j < data.length; ++j)\\r\\n\\t\\t\\t\\tdata[j] = Number(t[j+2]);\\r\\n\\t\\t\\tmesh[name] = data;\\r\\n\\t\\t}\\r\\n\\t\\telse if(type == \\\"@\\\") //info\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(name == \\\"bones\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar bones = [];\\r\\n\\t\\t\\t\\tvar num_bones = Number(t[1]);\\r\\n\\t\\t\\t\\tfor(var j = 0; j < num_bones; ++j)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar m = (t.slice(3 + j*17, 3 + (j+1)*17 - 1)).map(Number);\\r\\n\\t\\t\\t\\t\\tbones.push( [ t[2 + j*17], m ] );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tmesh.bones = bones;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(name == \\\"bind_matrix\\\")\\r\\n\\t\\t\\t\\tmesh.bind_matrix = t.slice(1,17).map(Number);\\r\\n\\t\\t\\telse if(name == \\\"groups\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tmesh.info = { groups: [] };\\r\\n\\t\\t\\t\\tvar num_groups = Number(t[1]);\\r\\n\\t\\t\\t\\tfor(var j = 0; j < num_groups; ++j)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar group = { name: t[2+j*4], material: t[2+j*4+1], start: Number(t[2+j*4+2]), length: Number(t[2+j*4+3]) };\\r\\n\\t\\t\\t\\t\\tmesh.info.groups.push(group);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tconsole.warn(\\\"type unknown: \\\" + t[0] );\\r\\n\\t}\\r\\n\\r\\n\\tif(options.only_data)\\r\\n\\t\\treturn mesh;\\r\\n\\r\\n\\t//creates and returns a GL.Mesh\\r\\n\\tvar final_mesh = null;\\r\\n\\tfinal_mesh = Mesh.load( mesh, null, options.mesh );\\r\\n\\tfinal_mesh.updateBoundingBox();\\r\\n\\treturn final_mesh;\\r\\n}\\r\\n\\r\\nMesh.encoders[\\\"mesh\\\"] = function( mesh, options )\\r\\n{\\r\\n\\tvar lines = [];\\r\\n\\tfor(var i in mesh.vertexBuffers )\\r\\n\\t{\\r\\n\\t\\tvar buffer = mesh.vertexBuffers[i];\\r\\n\\t\\tvar line = [\\\"-\\\"+i, buffer.data.length, buffer.data, typedArrayToArray( buffer.data ) ];\\r\\n\\t\\tlines.push(line.join(\\\",\\\"));\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i in mesh.indexBuffers )\\r\\n\\t{\\r\\n\\t\\tvar buffer = mesh.indexBuffers[i];\\r\\n\\t\\tvar line = [ \\\"*\\\" + i, buffer.data.length, buffer.data, typedArrayToArray( buffer.data ) ];\\r\\n\\t\\tlines.push(line.join(\\\",\\\"));\\r\\n\\t}\\r\\n\\r\\n\\tif(mesh.bounding)\\r\\n\\t\\tlines.push( [\\\"@bounding\\\", typedArrayToArray(mesh.bounding.subarray(0,6))].join(\\\",\\\") );\\r\\n\\tif(mesh.info && mesh.info.groups)\\r\\n\\t{\\r\\n\\t\\tvar groups_info = [];\\r\\n\\t\\tfor(var j = 0; j < mesh.info.groups.length; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar group = mesh.info.groups[j];\\r\\n\\t\\t\\tgroups_info.push( group.name, group.material, group.start, group.length );\\r\\n\\t\\t}\\r\\n\\t\\tlines.push( [\\\"@groups\\\", mesh.info.groups.length ].concat( groups_info ).join(\\\",\\\") );\\r\\n\\t}\\r\\n\\r\\n\\tif(mesh.bones)\\r\\n\\t\\tlines.push( [\\\"@bones\\\", mesh.bones.length, mesh.bones.flat()].join(\\\",\\\") );\\r\\n\\tif(mesh.bind_matrix)\\r\\n\\t\\tlines.push( [\\\"@bind_matrix\\\", typedArrayToArray(mesh.bind_matrix) ].join(\\\",\\\") );\\r\\n\\r\\n\\treturn lines.join(\\\"\\\\n\\\");\\r\\n}\\r\\n\\r\\n/* BINARY FORMAT ************************************/\\r\\n\\r\\nif(global.WBin)\\r\\n\\tglobal.WBin.classes[\\\"Mesh\\\"] = Mesh;\\r\\n\\r\\nMesh.binary_file_formats[\\\"wbin\\\"] = true;\\r\\n\\r\\nMesh.parsers[\\\"wbin\\\"] = Mesh.fromBinary = function( data_array, options )\\r\\n{\\r\\n\\tif(!global.WBin)\\r\\n\\t\\tthrow(\\\"To use binary meshes you need to install WBin.js from https://github.com/jagenjo/litescene.js/blob/master/src/utils/wbin.js \\\");\\r\\n\\r\\n\\toptions = options || {};\\r\\n\\r\\n\\tvar o = null;\\r\\n\\tif( data_array.constructor == ArrayBuffer )\\r\\n\\t\\to = WBin.load( data_array, true );\\r\\n\\telse\\r\\n\\t\\to = data_array;\\r\\n\\r\\n\\tif(!o.info)\\r\\n\\t\\tconsole.warn(\\\"This WBin doesn't seem to contain a mesh. Classname: \\\", o[\\\"@classname\\\"] );\\r\\n\\r\\n\\tif( o.format )\\r\\n\\t\\tGL.Mesh.decompress( o );\\r\\n\\r\\n\\tvar vertex_buffers = {};\\r\\n\\tif(o.vertex_buffers)\\r\\n\\t{\\r\\n\\t\\tfor(var i in o.vertex_buffers)\\r\\n\\t\\t\\tvertex_buffers[ o.vertex_buffers[i] ] = o[ o.vertex_buffers[i] ];\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tif(o.vertices) vertex_buffers.vertices = o.vertices;\\r\\n\\t\\tif(o.normals) vertex_buffers.normals = o.normals;\\r\\n\\t\\tif(o.coords) vertex_buffers.coords = o.coords;\\r\\n\\t\\tif(o.weights) vertex_buffers.weights = o.weights;\\r\\n\\t\\tif(o.bone_indices) vertex_buffers.bone_indices = o.bone_indices;\\r\\n\\t}\\r\\n\\r\\n\\tvar index_buffers = {};\\r\\n\\tif( o.index_buffers )\\r\\n\\t{\\r\\n\\t\\tfor(var i in o.index_buffers)\\r\\n\\t\\t\\tindex_buffers[ o.index_buffers[i] ] = o[ o.index_buffers[i] ];\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tif(o.triangles) index_buffers.triangles = o.triangles;\\r\\n\\t\\tif(o.wireframe) index_buffers.wireframe = o.wireframe;\\r\\n\\t}\\r\\n\\r\\n\\tvar mesh = { \\r\\n\\t\\tvertex_buffers: vertex_buffers,\\r\\n\\t\\tindex_buffers: index_buffers,\\r\\n\\t\\tbounding: o.bounding,\\r\\n\\t\\tinfo: o.info\\r\\n\\t};\\r\\n\\r\\n\\tif(o.bones)\\r\\n\\t{\\r\\n\\t\\tmesh.bones = o.bones;\\r\\n\\t\\t//restore Float32array\\r\\n\\t\\tfor(var i = 0; i < mesh.bones.length; ++i)\\r\\n\\t\\t\\tmesh.bones[i][1] = mat4.clone(mesh.bones[i][1]);\\r\\n\\t\\tif(o.bind_matrix)\\r\\n\\t\\t\\tmesh.bind_matrix = mat4.clone( o.bind_matrix );\\t\\t\\r\\n\\t}\\r\\n\\r\\n\\tif(o.morph_targets)\\r\\n\\t\\tmesh.morph_targets = o.morph_targets;\\r\\n\\r\\n\\tif(options.only_data)\\r\\n\\t\\treturn mesh;\\r\\n\\r\\n\\t//build mesh object\\r\\n\\tvar final_mesh = options.mesh || new GL.Mesh();\\r\\n\\tfinal_mesh.configure( mesh );\\r\\n\\treturn final_mesh;\\r\\n}\\r\\n\\r\\nMesh.encoders[\\\"wbin\\\"] = function( mesh, options )\\r\\n{\\r\\n\\treturn mesh.toBinary( options );\\r\\n}\\r\\n\\r\\nMesh.prototype.toBinary = function( options )\\r\\n{\\r\\n\\tif(!global.WBin)\\r\\n\\t\\tthrow(\\\"to use Mesh.toBinary you need to have WBin included. Check the repository for wbin.js\\\");\\r\\n\\r\\n\\tif(!this.info)\\r\\n\\t\\tthis.info = {};\\r\\n\\r\\n\\t//clean data\\r\\n\\tvar o = {\\r\\n\\t\\tobject_class: \\\"Mesh\\\",\\r\\n\\t\\tinfo: this.info,\\r\\n\\t\\tgroups: this.groups\\r\\n\\t};\\r\\n\\r\\n\\tif(this.bones)\\r\\n\\t{\\r\\n\\t\\tvar bones = [];\\r\\n\\t\\t//convert to array\\r\\n\\t\\tfor(var i = 0; i < this.bones.length; ++i)\\r\\n\\t\\t\\tbones.push([ this.bones[i][0], mat4.toArray( this.bones[i][1] ) ]);\\r\\n\\t\\to.bones = bones;\\r\\n\\t\\tif(this.bind_matrix)\\r\\n\\t\\t\\to.bind_matrix = this.bind_matrix;\\r\\n\\t}\\r\\n\\r\\n\\t//bounding box\\r\\n\\tif(!this.bounding)\\t\\r\\n\\t\\tthis.updateBoundingBox();\\r\\n\\to.bounding = this.bounding;\\r\\n\\r\\n\\tvar vertex_buffers = [];\\r\\n\\tvar index_buffers = [];\\r\\n\\r\\n\\tfor(var i in this.vertexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar stream = this.vertexBuffers[i];\\r\\n\\t\\to[ stream.name ] = stream.data;\\r\\n\\t\\tvertex_buffers.push( stream.name );\\r\\n\\r\\n\\t\\tif(stream.name == \\\"vertices\\\")\\r\\n\\t\\t\\to.info.num_vertices = stream.data.length / 3;\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i in this.indexBuffers)\\r\\n\\t{\\r\\n\\t\\tvar stream = this.indexBuffers[i];\\r\\n\\t\\to[i] = stream.data;\\r\\n\\t\\tindex_buffers.push( i );\\r\\n\\t}\\r\\n\\r\\n\\to.vertex_buffers = vertex_buffers;\\r\\n\\to.index_buffers = index_buffers;\\r\\n\\r\\n\\t//compress wbin using the bounding\\r\\n\\tif( GL.Mesh.enable_wbin_compression ) //apply compression\\r\\n\\t\\tGL.Mesh.compress( o );\\r\\n\\r\\n\\t//create pack file\\r\\n\\tvar bin = WBin.create( o, \\\"Mesh\\\" ); \\r\\n\\treturn bin;\\r\\n}\\r\\n\\r\\nMesh.compress = function( o, format )\\r\\n{\\r\\n\\tformat = format || \\\"bounding_compressed\\\";\\r\\n\\to.format = {\\r\\n\\t\\ttype: format\\r\\n\\t};\\r\\n\\r\\n\\tvar func = Mesh.compressors[ format ];\\r\\n\\tif(!func)\\r\\n\\t\\tthrow(\\\"compression format not supported:\\\" + format );\\r\\n\\treturn func( o );\\r\\n}\\r\\n\\r\\nMesh.decompress = function( o )\\r\\n{\\r\\n\\tif(!o.format)\\r\\n\\t\\treturn;\\r\\n\\tvar func = Mesh.decompressors[ o.format.type ];\\r\\n\\tif(!func)\\r\\n\\t\\tthrow(\\\"decompression format not supported:\\\" + o.format.type );\\r\\n\\treturn func( o );\\r\\n}\\r\\n\\r\\nMesh.compressors[\\\"bounding_compressed\\\"] = function(o)\\r\\n{\\r\\n\\tif(!o.vertex_buffers)\\r\\n\\t\\tthrow(\\\"buffers not found\\\");\\r\\n\\r\\n\\tvar min = BBox.getMin( o.bounding );\\r\\n\\tvar max = BBox.getMax( o.bounding );\\r\\n\\tvar range = vec3.sub( vec3.create(), max, min );\\r\\n\\r\\n\\tvar vertices = o.vertices;\\r\\n\\tvar new_vertices = new Uint16Array( vertices.length );\\r\\n\\tfor(var i = 0; i < vertices.length; i+=3)\\r\\n\\t{\\r\\n\\t\\tnew_vertices[i] = ((vertices[i] - min[0]) / range[0]) * 65535;\\r\\n\\t\\tnew_vertices[i+1] = ((vertices[i+1] - min[1]) / range[1]) * 65535;\\r\\n\\t\\tnew_vertices[i+2] = ((vertices[i+2] - min[2]) / range[2]) * 65535;\\r\\n\\t}\\r\\n\\to.vertices = new_vertices;\\t\\t\\r\\n\\r\\n\\tif( o.normals )\\r\\n\\t{\\r\\n\\t\\tvar normals = o.normals;\\r\\n\\t\\tvar new_normals = new Uint8Array( normals.length );\\r\\n\\t\\tvar normals_range = new_normals.constructor == Uint8Array ? 255 : 65535;\\r\\n\\t\\tfor(var i = 0; i < normals.length; i+=3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tnew_normals[i] = (normals[i] * 0.5 + 0.5) * normals_range;\\r\\n\\t\\t\\tnew_normals[i+1] = (normals[i+1] * 0.5 + 0.5) * normals_range;\\r\\n\\t\\t\\tnew_normals[i+2] = (normals[i+2] * 0.5 + 0.5) * normals_range;\\r\\n\\t\\t}\\r\\n\\t\\to.normals = new_normals;\\r\\n\\t}\\r\\n\\r\\n\\tif( o.coords )\\r\\n\\t{\\r\\n\\t\\t//compute uv bounding: [minu,minv,maxu,maxv]\\r\\n\\t\\tvar coords = o.coords;\\r\\n\\t\\tvar uvs_bounding = [10000,10000,-10000,-10000];\\r\\n\\t\\tfor(var i = 0; i < coords.length; i+=2)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar u = coords[i];\\r\\n\\t\\t\\tif( uvs_bounding[0] > u ) uvs_bounding[0] = u;\\r\\n\\t\\t\\telse if( uvs_bounding[2] < u ) uvs_bounding[2] = u;\\r\\n\\t\\t\\tvar v = coords[i+1];\\r\\n\\t\\t\\tif( uvs_bounding[1] > v ) uvs_bounding[1] = v;\\r\\n\\t\\t\\telse if( uvs_bounding[3] < v ) uvs_bounding[3] = v;\\r\\n\\t\\t}\\r\\n\\t\\to.format.uvs_bounding = uvs_bounding;\\r\\n\\r\\n\\t\\tvar new_coords = new Uint16Array( coords.length );\\r\\n\\t\\tvar range = [ uvs_bounding[2] - uvs_bounding[0], uvs_bounding[3] - uvs_bounding[1] ];\\r\\n\\t\\tfor(var i = 0; i < coords.length; i+=2)\\r\\n\\t\\t{\\r\\n\\t\\t\\tnew_coords[i] = ((coords[i] - uvs_bounding[0]) / range[0]) * 65535;\\r\\n\\t\\t\\tnew_coords[i+1] = ((coords[i+1] - uvs_bounding[1]) / range[1]) * 65535;\\r\\n\\t\\t}\\r\\n\\t\\to.coords = new_coords;\\r\\n\\t}\\r\\n\\r\\n\\tif( o.weights )\\r\\n\\t{\\r\\n\\t\\tvar weights = o.weights;\\r\\n\\t\\tvar new_weights = new Uint16Array( weights.length ); //using only one byte distorts the meshes a lot\\r\\n\\t\\tvar weights_range = new_weights.constructor == Uint8Array ? 255 : 65535;\\r\\n\\t\\tfor(var i = 0; i < weights.length; i+=4)\\r\\n\\t\\t{\\r\\n\\t\\t\\tnew_weights[i] = weights[i] * weights_range;\\r\\n\\t\\t\\tnew_weights[i+1] = weights[i+1] * weights_range;\\r\\n\\t\\t\\tnew_weights[i+2] = weights[i+2] * weights_range;\\r\\n\\t\\t\\tnew_weights[i+3] = weights[i+3] * weights_range;\\r\\n\\t\\t}\\r\\n\\t\\to.weights = new_weights;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\nMesh.decompressors[\\\"bounding_compressed\\\"] = function(o)\\r\\n{\\r\\n\\tvar bounding = o.bounding;\\r\\n\\tif(!bounding)\\r\\n\\t\\tthrow(\\\"error in mesh decompressing data: bounding not found, cannot use the bounding decompression.\\\");\\r\\n\\r\\n\\tvar min = BBox.getMin( bounding );\\r\\n\\tvar max = BBox.getMax( bounding );\\r\\n\\tvar range = vec3.sub( vec3.create(), max, min );\\r\\n\\r\\n\\tvar format = o.format;\\r\\n\\r\\n\\tvar inv8 = 1 / 255;\\r\\n\\tvar inv16 = 1 / 65535;\\r\\n\\tvar vertices = o.vertices;\\r\\n\\tvar new_vertices = new Float32Array( vertices.length );\\r\\n\\tfor( var i = 0, l = vertices.length; i < l; i += 3 )\\r\\n\\t{\\r\\n\\t\\tnew_vertices[i] = ((vertices[i] * inv16) * range[0]) + min[0];\\r\\n\\t\\tnew_vertices[i+1] = ((vertices[i+1] * inv16) * range[1]) + min[1];\\r\\n\\t\\tnew_vertices[i+2] = ((vertices[i+2] * inv16) * range[2]) + min[2];\\r\\n\\t}\\r\\n\\to.vertices = new_vertices;\\t\\t\\r\\n\\r\\n\\tif( o.normals && o.normals.constructor != Float32Array )\\r\\n\\t{\\r\\n\\t\\tvar normals = o.normals;\\r\\n\\t\\tvar new_normals = new Float32Array( normals.length );\\r\\n\\t\\tvar inormals_range = normals.constructor == Uint8Array ? inv8 : inv16;\\r\\n\\t\\tfor( var i = 0, l = normals.length; i < l; i += 3 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tnew_normals[i] = (normals[i] * inormals_range) * 2.0 - 1.0;\\r\\n\\t\\t\\tnew_normals[i+1] = (normals[i+1] * inormals_range) * 2.0 - 1.0;\\r\\n\\t\\t\\tnew_normals[i+2] = (normals[i+2] * inormals_range) * 2.0 - 1.0;\\r\\n\\t\\t\\tvar N = new_normals.subarray(i,i+3);\\r\\n\\t\\t\\tvec3.normalize(N,N);\\r\\n\\t\\t}\\r\\n\\t\\to.normals = new_normals;\\r\\n\\t}\\r\\n\\r\\n\\tif( o.coords && format.uvs_bounding && o.coords.constructor != Float32Array )\\r\\n\\t{\\r\\n\\t\\tvar coords = o.coords;\\r\\n\\t\\tvar uvs_bounding = format.uvs_bounding;\\r\\n\\t\\tvar range = [ uvs_bounding[2] - uvs_bounding[0], uvs_bounding[3] - uvs_bounding[1] ];\\r\\n\\t\\tvar new_coords = new Float32Array( coords.length );\\r\\n\\t\\tfor( var i = 0, l = coords.length; i < l; i += 2 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tnew_coords[i] = (coords[i] * inv16) * range[0] + uvs_bounding[0];\\r\\n\\t\\t\\tnew_coords[i+1] = (coords[i+1] * inv16) * range[1] + uvs_bounding[1];\\r\\n\\t\\t}\\r\\n\\t\\to.coords = new_coords;\\r\\n\\t}\\r\\n\\r\\n\\t//bones are already in Uint8 format so dont need to compress them further, but weights yes\\r\\n\\tif( o.weights && o.weights.constructor != Float32Array ) //do we really need to unpack them? what if we use them like this?\\r\\n\\t{\\r\\n\\t\\tvar weights = o.weights;\\r\\n\\t\\tvar new_weights = new Float32Array( weights.length );\\r\\n\\t\\tvar iweights_range = weights.constructor == Uint8Array ? inv8 : inv16;\\r\\n\\t\\tfor(var i = 0, l = weights.length; i < l; i += 4 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tnew_weights[i] = weights[i] * iweights_range;\\r\\n\\t\\t\\tnew_weights[i+1] = weights[i+1] * iweights_range;\\r\\n\\t\\t\\tnew_weights[i+2] = weights[i+2] * iweights_range;\\r\\n\\t\\t\\tnew_weights[i+3] = weights[i+3] * iweights_range;\\r\\n\\t\\t}\\r\\n\\t\\to.weights = new_weights;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//footer.js\\r\\n})( typeof(window) != \\\"undefined\\\" ? window : (typeof(self) != \\\"undefined\\\" ? self : global ) );\\r\\n\"","module.exports = \"//packer version\\r\\n\\r\\n(function(global) {\\r\\n // *************************************************************\\r\\n // LiteGraph CLASS *******\\r\\n // *************************************************************\\r\\n\\r\\n /**\\r\\n * The Global Scope. It contains all the registered node classes.\\r\\n *\\r\\n * @class LiteGraph\\r\\n * @constructor\\r\\n */\\r\\n\\r\\n var LiteGraph = (global.LiteGraph = {\\r\\n VERSION: 0.4,\\r\\n\\r\\n CANVAS_GRID_SIZE: 10,\\r\\n\\r\\n NODE_TITLE_HEIGHT: 30,\\r\\n NODE_TITLE_TEXT_Y: 20,\\r\\n NODE_SLOT_HEIGHT: 20,\\r\\n NODE_WIDGET_HEIGHT: 20,\\r\\n NODE_WIDTH: 140,\\r\\n NODE_MIN_WIDTH: 50,\\r\\n NODE_COLLAPSED_RADIUS: 10,\\r\\n NODE_COLLAPSED_WIDTH: 80,\\r\\n NODE_TITLE_COLOR: \\\"#999\\\",\\r\\n NODE_TEXT_SIZE: 14,\\r\\n NODE_TEXT_COLOR: \\\"#AAA\\\",\\r\\n NODE_SUBTEXT_SIZE: 12,\\r\\n NODE_DEFAULT_COLOR: \\\"#333\\\",\\r\\n NODE_DEFAULT_BGCOLOR: \\\"#353535\\\",\\r\\n NODE_DEFAULT_BOXCOLOR: \\\"#666\\\",\\r\\n NODE_DEFAULT_SHAPE: \\\"box\\\",\\r\\n DEFAULT_SHADOW_COLOR: \\\"rgba(0,0,0,0.5)\\\",\\r\\n DEFAULT_GROUP_FONT: 24,\\r\\n\\r\\n LINK_COLOR: \\\"#9A9\\\",\\r\\n EVENT_LINK_COLOR: \\\"#A86\\\",\\r\\n CONNECTING_LINK_COLOR: \\\"#AFA\\\",\\r\\n\\r\\n MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops\\r\\n DEFAULT_POSITION: [100, 100], //default node position\\r\\n VALID_SHAPES: [\\\"default\\\", \\\"box\\\", \\\"round\\\", \\\"card\\\"], //,\\\"circle\\\"\\r\\n\\r\\n //shapes are used for nodes but also for slots\\r\\n BOX_SHAPE: 1,\\r\\n ROUND_SHAPE: 2,\\r\\n CIRCLE_SHAPE: 3,\\r\\n CARD_SHAPE: 4,\\r\\n ARROW_SHAPE: 5,\\r\\n\\r\\n //enums\\r\\n INPUT: 1,\\r\\n OUTPUT: 2,\\r\\n\\r\\n EVENT: -1, //for outputs\\r\\n ACTION: -1, //for inputs\\r\\n\\r\\n ALWAYS: 0,\\r\\n ON_EVENT: 1,\\r\\n NEVER: 2,\\r\\n ON_TRIGGER: 3,\\r\\n\\r\\n UP: 1,\\r\\n DOWN: 2,\\r\\n LEFT: 3,\\r\\n RIGHT: 4,\\r\\n CENTER: 5,\\r\\n\\r\\n STRAIGHT_LINK: 0,\\r\\n LINEAR_LINK: 1,\\r\\n SPLINE_LINK: 2,\\r\\n\\r\\n NORMAL_TITLE: 0,\\r\\n NO_TITLE: 1,\\r\\n TRANSPARENT_TITLE: 2,\\r\\n AUTOHIDE_TITLE: 3,\\r\\n\\r\\n proxy: null, //used to redirect calls\\r\\n node_images_path: \\\"\\\",\\r\\n\\r\\n debug: false,\\r\\n catch_exceptions: true,\\r\\n throw_errors: true,\\r\\n allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits\\r\\n registered_node_types: {}, //nodetypes by string\\r\\n node_types_by_file_extension: {}, //used for dropping files in the canvas\\r\\n Nodes: {}, //node types by classname\\r\\n\\r\\n searchbox_extras: {}, //used to add extra features to the search box\\r\\n\\r\\n /**\\r\\n * Register a node class so it can be listed when the user wants to create a new one\\r\\n * @method registerNodeType\\r\\n * @param {String} type name of the node and path\\r\\n * @param {Class} base_class class containing the structure of a node\\r\\n */\\r\\n\\r\\n registerNodeType: function(type, base_class) {\\r\\n if (!base_class.prototype) {\\r\\n throw \\\"Cannot register a simple object, it must be a class with a prototype\\\";\\r\\n }\\r\\n base_class.type = type;\\r\\n\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Node registered: \\\" + type);\\r\\n }\\r\\n\\r\\n var categories = type.split(\\\"/\\\");\\r\\n var classname = base_class.name;\\r\\n\\r\\n var pos = type.lastIndexOf(\\\"/\\\");\\r\\n base_class.category = type.substr(0, pos);\\r\\n\\r\\n if (!base_class.title) {\\r\\n base_class.title = classname;\\r\\n }\\r\\n //info.name = name.substr(pos+1,name.length - pos);\\r\\n\\r\\n //extend class\\r\\n if (base_class.prototype) {\\r\\n //is a class\\r\\n for (var i in LGraphNode.prototype) {\\r\\n if (!base_class.prototype[i]) {\\r\\n base_class.prototype[i] = LGraphNode.prototype[i];\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n Object.defineProperty(base_class.prototype, \\\"shape\\\", {\\r\\n set: function(v) {\\r\\n switch (v) {\\r\\n case \\\"default\\\":\\r\\n delete this._shape;\\r\\n break;\\r\\n case \\\"box\\\":\\r\\n this._shape = LiteGraph.BOX_SHAPE;\\r\\n break;\\r\\n case \\\"round\\\":\\r\\n this._shape = LiteGraph.ROUND_SHAPE;\\r\\n break;\\r\\n case \\\"circle\\\":\\r\\n this._shape = LiteGraph.CIRCLE_SHAPE;\\r\\n break;\\r\\n case \\\"card\\\":\\r\\n this._shape = LiteGraph.CARD_SHAPE;\\r\\n break;\\r\\n default:\\r\\n this._shape = v;\\r\\n }\\r\\n },\\r\\n get: function(v) {\\r\\n return this._shape;\\r\\n },\\r\\n enumerable: true\\r\\n });\\r\\n\\r\\n var prev = this.registered_node_types[type];\\r\\n this.registered_node_types[type] = base_class;\\r\\n if (base_class.constructor.name) {\\r\\n this.Nodes[classname] = base_class;\\r\\n }\\r\\n if (LiteGraph.onNodeTypeRegistered) {\\r\\n LiteGraph.onNodeTypeRegistered(type, base_class);\\r\\n }\\r\\n if (prev && LiteGraph.onNodeTypeReplaced) {\\r\\n LiteGraph.onNodeTypeReplaced(type, base_class, prev);\\r\\n }\\r\\n\\r\\n //warnings\\r\\n if (base_class.prototype.onPropertyChange) {\\r\\n console.warn(\\r\\n \\\"LiteGraph node class \\\" +\\r\\n type +\\r\\n \\\" has onPropertyChange method, it must be called onPropertyChanged with d at the end\\\"\\r\\n );\\r\\n }\\r\\n\\r\\n if (base_class.supported_extensions) {\\r\\n for (var i in base_class.supported_extensions) {\\r\\n this.node_types_by_file_extension[\\r\\n base_class.supported_extensions[i].toLowerCase()\\r\\n ] = base_class;\\r\\n }\\r\\n }\\r\\n },\\r\\n\\r\\n /**\\r\\n * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function.\\r\\n * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output.\\r\\n * @method wrapFunctionAsNode\\r\\n * @param {String} name node name with namespace (p.e.: 'math/sum')\\r\\n * @param {Function} func\\r\\n * @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type\\r\\n * @param {String} return_type [optional] string with the return type, otherwise it will be generic\\r\\n * @param {Object} properties [optional] properties to be configurable\\r\\n */\\r\\n wrapFunctionAsNode: function(\\r\\n name,\\r\\n func,\\r\\n param_types,\\r\\n return_type,\\r\\n properties\\r\\n ) {\\r\\n var params = Array(func.length);\\r\\n var code = \\\"\\\";\\r\\n var names = LiteGraph.getParameterNames(func);\\r\\n for (var i = 0; i < names.length; ++i) {\\r\\n code +=\\r\\n \\\"this.addInput('\\\" +\\r\\n names[i] +\\r\\n \\\"',\\\" +\\r\\n (param_types && param_types[i]\\r\\n ? \\\"'\\\" + param_types[i] + \\\"'\\\"\\r\\n : \\\"0\\\") +\\r\\n \\\");\\\\n\\\";\\r\\n }\\r\\n code +=\\r\\n \\\"this.addOutput('out',\\\" +\\r\\n (return_type ? \\\"'\\\" + return_type + \\\"'\\\" : 0) +\\r\\n \\\");\\\\n\\\";\\r\\n if (properties) {\\r\\n code +=\\r\\n \\\"this.properties = \\\" + JSON.stringify(properties) + \\\";\\\\n\\\";\\r\\n }\\r\\n var classobj = Function(code);\\r\\n classobj.title = name.split(\\\"/\\\").pop();\\r\\n classobj.desc = \\\"Generated from \\\" + func.name;\\r\\n classobj.prototype.onExecute = function onExecute() {\\r\\n for (var i = 0; i < params.length; ++i) {\\r\\n params[i] = this.getInputData(i);\\r\\n }\\r\\n var r = func.apply(this, params);\\r\\n this.setOutputData(0, r);\\r\\n };\\r\\n this.registerNodeType(name, classobj);\\r\\n },\\r\\n\\r\\n /**\\r\\n * Adds this method to all nodetypes, existing and to be created\\r\\n * (You can add it to LGraphNode.prototype but then existing node types wont have it)\\r\\n * @method addNodeMethod\\r\\n * @param {Function} func\\r\\n */\\r\\n addNodeMethod: function(name, func) {\\r\\n LGraphNode.prototype[name] = func;\\r\\n for (var i in this.registered_node_types) {\\r\\n var type = this.registered_node_types[i];\\r\\n if (type.prototype[name]) {\\r\\n type.prototype[\\\"_\\\" + name] = type.prototype[name];\\r\\n } //keep old in case of replacing\\r\\n type.prototype[name] = func;\\r\\n }\\r\\n },\\r\\n\\r\\n /**\\r\\n * Create a node of a given type with a name. The node is not attached to any graph yet.\\r\\n * @method createNode\\r\\n * @param {String} type full name of the node class. p.e. \\\"math/sin\\\"\\r\\n * @param {String} name a name to distinguish from other nodes\\r\\n * @param {Object} options to set options\\r\\n */\\r\\n\\r\\n createNode: function(type, title, options) {\\r\\n var base_class = this.registered_node_types[type];\\r\\n if (!base_class) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\r\\n 'GraphNode type \\\"' + type + '\\\" not registered.'\\r\\n );\\r\\n }\\r\\n return null;\\r\\n }\\r\\n\\r\\n var prototype = base_class.prototype || base_class;\\r\\n\\r\\n title = title || base_class.title || type;\\r\\n\\r\\n var node = null;\\r\\n\\r\\n if (LiteGraph.catch_exceptions) {\\r\\n try {\\r\\n node = new base_class(title);\\r\\n } catch (err) {\\r\\n console.error(err);\\r\\n return null;\\r\\n }\\r\\n } else {\\r\\n node = new base_class(title);\\r\\n }\\r\\n\\r\\n node.type = type;\\r\\n\\r\\n if (!node.title && title) {\\r\\n node.title = title;\\r\\n }\\r\\n if (!node.properties) {\\r\\n node.properties = {};\\r\\n }\\r\\n if (!node.properties_info) {\\r\\n node.properties_info = [];\\r\\n }\\r\\n if (!node.flags) {\\r\\n node.flags = {};\\r\\n }\\r\\n if (!node.size) {\\r\\n node.size = node.computeSize();\\r\\n }\\r\\n if (!node.pos) {\\r\\n node.pos = LiteGraph.DEFAULT_POSITION.concat();\\r\\n }\\r\\n if (!node.mode) {\\r\\n node.mode = LiteGraph.ALWAYS;\\r\\n }\\r\\n\\r\\n //extra options\\r\\n if (options) {\\r\\n for (var i in options) {\\r\\n node[i] = options[i];\\r\\n }\\r\\n }\\r\\n\\r\\n return node;\\r\\n },\\r\\n\\r\\n /**\\r\\n * Returns a registered node type with a given name\\r\\n * @method getNodeType\\r\\n * @param {String} type full name of the node class. p.e. \\\"math/sin\\\"\\r\\n * @return {Class} the node class\\r\\n */\\r\\n getNodeType: function(type) {\\r\\n return this.registered_node_types[type];\\r\\n },\\r\\n\\r\\n /**\\r\\n * Returns a list of node types matching one category\\r\\n * @method getNodeType\\r\\n * @param {String} category category name\\r\\n * @return {Array} array with all the node classes\\r\\n */\\r\\n\\r\\n getNodeTypesInCategory: function(category, filter) {\\r\\n var r = [];\\r\\n for (var i in this.registered_node_types) {\\r\\n var type = this.registered_node_types[i];\\r\\n if (filter && type.filter && type.filter != filter) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n if (category == \\\"\\\") {\\r\\n if (type.category == null) {\\r\\n r.push(type);\\r\\n }\\r\\n } else if (type.category == category) {\\r\\n r.push(type);\\r\\n }\\r\\n }\\r\\n\\r\\n return r;\\r\\n },\\r\\n\\r\\n /**\\r\\n * Returns a list with all the node type categories\\r\\n * @method getNodeTypesCategories\\r\\n * @return {Array} array with all the names of the categories\\r\\n */\\r\\n\\r\\n getNodeTypesCategories: function( filter ) {\\r\\n var categories = { \\\"\\\": 1 };\\r\\n for (var i in this.registered_node_types) {\\r\\n\\t\\t\\t\\tvar type = this.registered_node_types[i];\\r\\n if ( type.category && !type.skip_list )\\r\\n {\\r\\n\\t\\t\\t\\t\\tif(filter && type.filter != filter)\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\r\\n categories[type.category] = 1;\\r\\n }\\r\\n }\\r\\n var result = [];\\r\\n for (var i in categories) {\\r\\n result.push(i);\\r\\n }\\r\\n return result;\\r\\n },\\r\\n\\r\\n //debug purposes: reloads all the js scripts that matches a wildcard\\r\\n reloadNodes: function(folder_wildcard) {\\r\\n var tmp = document.getElementsByTagName(\\\"script\\\");\\r\\n //weird, this array changes by its own, so we use a copy\\r\\n var script_files = [];\\r\\n for (var i in tmp) {\\r\\n script_files.push(tmp[i]);\\r\\n }\\r\\n\\r\\n var docHeadObj = document.getElementsByTagName(\\\"head\\\")[0];\\r\\n folder_wildcard = document.location.href + folder_wildcard;\\r\\n\\r\\n for (var i in script_files) {\\r\\n var src = script_files[i].src;\\r\\n if (\\r\\n !src ||\\r\\n src.substr(0, folder_wildcard.length) != folder_wildcard\\r\\n ) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n try {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Reloading: \\\" + src);\\r\\n }\\r\\n var dynamicScript = document.createElement(\\\"script\\\");\\r\\n dynamicScript.type = \\\"text/javascript\\\";\\r\\n dynamicScript.src = src;\\r\\n docHeadObj.appendChild(dynamicScript);\\r\\n docHeadObj.removeChild(script_files[i]);\\r\\n } catch (err) {\\r\\n if (LiteGraph.throw_errors) {\\r\\n throw err;\\r\\n }\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Error while reloading \\\" + src);\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Nodes reloaded\\\");\\r\\n }\\r\\n },\\r\\n\\r\\n //separated just to improve if it doesn't work\\r\\n cloneObject: function(obj, target) {\\r\\n if (obj == null) {\\r\\n return null;\\r\\n }\\r\\n var r = JSON.parse(JSON.stringify(obj));\\r\\n if (!target) {\\r\\n return r;\\r\\n }\\r\\n\\r\\n for (var i in r) {\\r\\n target[i] = r[i];\\r\\n }\\r\\n return target;\\r\\n },\\r\\n\\r\\n isValidConnection: function(type_a, type_b) {\\r\\n if (\\r\\n !type_a || //generic output\\r\\n !type_b || //generic input\\r\\n type_a == type_b || //same type (is valid for triggers)\\r\\n (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION)\\r\\n ) {\\r\\n return true;\\r\\n }\\r\\n\\r\\n // Enforce string type to handle toLowerCase call (-1 number not ok)\\r\\n type_a = String(type_a);\\r\\n type_b = String(type_b);\\r\\n type_a = type_a.toLowerCase();\\r\\n type_b = type_b.toLowerCase();\\r\\n\\r\\n // For nodes supporting multiple connection types\\r\\n if (type_a.indexOf(\\\",\\\") == -1 && type_b.indexOf(\\\",\\\") == -1) {\\r\\n return type_a == type_b;\\r\\n }\\r\\n\\r\\n // Check all permutations to see if one is valid\\r\\n var supported_types_a = type_a.split(\\\",\\\");\\r\\n var supported_types_b = type_b.split(\\\",\\\");\\r\\n for (var i = 0; i < supported_types_a.length; ++i) {\\r\\n for (var j = 0; j < supported_types_b.length; ++j) {\\r\\n if (supported_types_a[i] == supported_types_b[j]) {\\r\\n return true;\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n return false;\\r\\n },\\r\\n\\r\\n registerSearchboxExtra: function(node_type, description, data) {\\r\\n this.searchbox_extras[description.toLowerCase()] = {\\r\\n type: node_type,\\r\\n desc: description,\\r\\n data: data\\r\\n };\\r\\n }\\r\\n });\\r\\n\\r\\n //timer that works everywhere\\r\\n if (typeof performance != \\\"undefined\\\") {\\r\\n LiteGraph.getTime = performance.now.bind(performance);\\r\\n } else if (typeof Date != \\\"undefined\\\" && Date.now) {\\r\\n LiteGraph.getTime = Date.now.bind(Date);\\r\\n } else if (typeof process != \\\"undefined\\\") {\\r\\n LiteGraph.getTime = function() {\\r\\n var t = process.hrtime();\\r\\n return t[0] * 0.001 + t[1] * 1e-6;\\r\\n };\\r\\n } else {\\r\\n LiteGraph.getTime = function getTime() {\\r\\n return new Date().getTime();\\r\\n };\\r\\n }\\r\\n\\r\\n //*********************************************************************************\\r\\n // LGraph CLASS\\r\\n //*********************************************************************************\\r\\n\\r\\n /**\\r\\n * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.\\r\\n *\\r\\n * @class LGraph\\r\\n * @constructor\\r\\n * @param {Object} o data from previous serialization [optional]\\r\\n */\\r\\n\\r\\n function LGraph(o) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Graph created\\\");\\r\\n }\\r\\n this.list_of_graphcanvas = null;\\r\\n this.clear();\\r\\n\\r\\n if (o) {\\r\\n this.configure(o);\\r\\n }\\r\\n }\\r\\n\\r\\n global.LGraph = LiteGraph.LGraph = LGraph;\\r\\n\\r\\n //default supported types\\r\\n LGraph.supported_types = [\\\"number\\\", \\\"string\\\", \\\"boolean\\\"];\\r\\n\\r\\n //used to know which types of connections support this graph (some graphs do not allow certain types)\\r\\n LGraph.prototype.getSupportedTypes = function() {\\r\\n return this.supported_types || LGraph.supported_types;\\r\\n };\\r\\n\\r\\n LGraph.STATUS_STOPPED = 1;\\r\\n LGraph.STATUS_RUNNING = 2;\\r\\n\\r\\n /**\\r\\n * Removes all nodes from this graph\\r\\n * @method clear\\r\\n */\\r\\n\\r\\n LGraph.prototype.clear = function() {\\r\\n this.stop();\\r\\n this.status = LGraph.STATUS_STOPPED;\\r\\n\\r\\n this.last_node_id = 0;\\r\\n this.last_link_id = 0;\\r\\n\\r\\n this._version = -1; //used to detect changes\\r\\n\\r\\n //safe clear\\r\\n if (this._nodes) {\\r\\n for (var i = 0; i < this._nodes.length; ++i) {\\r\\n var node = this._nodes[i];\\r\\n if (node.onRemoved) {\\r\\n node.onRemoved();\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n //nodes\\r\\n this._nodes = [];\\r\\n this._nodes_by_id = {};\\r\\n this._nodes_in_order = []; //nodes that are executable sorted in execution order\\r\\n this._nodes_executable = null; //nodes that contain onExecute\\r\\n\\r\\n //other scene stuff\\r\\n this._groups = [];\\r\\n\\r\\n //links\\r\\n this.links = {}; //container with all the links\\r\\n\\r\\n //iterations\\r\\n this.iteration = 0;\\r\\n\\r\\n //custom data\\r\\n this.config = {};\\r\\n\\t\\tthis.vars = {};\\r\\n\\r\\n //timing\\r\\n this.globaltime = 0;\\r\\n this.runningtime = 0;\\r\\n this.fixedtime = 0;\\r\\n this.fixedtime_lapse = 0.01;\\r\\n this.elapsed_time = 0.01;\\r\\n this.last_update_time = 0;\\r\\n this.starttime = 0;\\r\\n\\r\\n this.catch_errors = true;\\r\\n\\r\\n //subgraph_data\\r\\n this.inputs = {};\\r\\n this.outputs = {};\\r\\n\\r\\n //notify canvas to redraw\\r\\n this.change();\\r\\n\\r\\n this.sendActionToCanvas(\\\"clear\\\");\\r\\n };\\r\\n\\r\\n /**\\r\\n * Attach Canvas to this graph\\r\\n * @method attachCanvas\\r\\n * @param {GraphCanvas} graph_canvas\\r\\n */\\r\\n\\r\\n LGraph.prototype.attachCanvas = function(graphcanvas) {\\r\\n if (graphcanvas.constructor != LGraphCanvas) {\\r\\n throw \\\"attachCanvas expects a LGraphCanvas instance\\\";\\r\\n }\\r\\n if (graphcanvas.graph && graphcanvas.graph != this) {\\r\\n graphcanvas.graph.detachCanvas(graphcanvas);\\r\\n }\\r\\n\\r\\n graphcanvas.graph = this;\\r\\n if (!this.list_of_graphcanvas) {\\r\\n this.list_of_graphcanvas = [];\\r\\n }\\r\\n this.list_of_graphcanvas.push(graphcanvas);\\r\\n };\\r\\n\\r\\n /**\\r\\n * Detach Canvas from this graph\\r\\n * @method detachCanvas\\r\\n * @param {GraphCanvas} graph_canvas\\r\\n */\\r\\n LGraph.prototype.detachCanvas = function(graphcanvas) {\\r\\n if (!this.list_of_graphcanvas) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var pos = this.list_of_graphcanvas.indexOf(graphcanvas);\\r\\n if (pos == -1) {\\r\\n return;\\r\\n }\\r\\n graphcanvas.graph = null;\\r\\n this.list_of_graphcanvas.splice(pos, 1);\\r\\n };\\r\\n\\r\\n /**\\r\\n * Starts running this graph every interval milliseconds.\\r\\n * @method start\\r\\n * @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate\\r\\n */\\r\\n\\r\\n LGraph.prototype.start = function(interval) {\\r\\n if (this.status == LGraph.STATUS_RUNNING) {\\r\\n return;\\r\\n }\\r\\n this.status = LGraph.STATUS_RUNNING;\\r\\n\\r\\n if (this.onPlayEvent) {\\r\\n this.onPlayEvent();\\r\\n }\\r\\n\\r\\n this.sendEventToAllNodes(\\\"onStart\\\");\\r\\n\\r\\n //launch\\r\\n this.starttime = LiteGraph.getTime();\\r\\n this.last_update_time = this.starttime;\\r\\n interval = interval || 0;\\r\\n var that = this;\\r\\n\\r\\n if (\\r\\n interval == 0 &&\\r\\n typeof window != \\\"undefined\\\" &&\\r\\n window.requestAnimationFrame\\r\\n ) {\\r\\n function on_frame() {\\r\\n if (that.execution_timer_id != -1) {\\r\\n return;\\r\\n }\\r\\n window.requestAnimationFrame(on_frame);\\r\\n that.runStep(1, !this.catch_errors);\\r\\n }\\r\\n this.execution_timer_id = -1;\\r\\n on_frame();\\r\\n } else {\\r\\n this.execution_timer_id = setInterval(function() {\\r\\n //execute\\r\\n that.runStep(1, !this.catch_errors);\\r\\n }, interval);\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Stops the execution loop of the graph\\r\\n * @method stop execution\\r\\n */\\r\\n\\r\\n LGraph.prototype.stop = function() {\\r\\n if (this.status == LGraph.STATUS_STOPPED) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.status = LGraph.STATUS_STOPPED;\\r\\n\\r\\n if (this.onStopEvent) {\\r\\n this.onStopEvent();\\r\\n }\\r\\n\\r\\n if (this.execution_timer_id != null) {\\r\\n if (this.execution_timer_id != -1) {\\r\\n clearInterval(this.execution_timer_id);\\r\\n }\\r\\n this.execution_timer_id = null;\\r\\n }\\r\\n\\r\\n this.sendEventToAllNodes(\\\"onStop\\\");\\r\\n };\\r\\n\\r\\n /**\\r\\n * Run N steps (cycles) of the graph\\r\\n * @method runStep\\r\\n * @param {number} num number of steps to run, default is 1\\r\\n * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors \\r\\n * @param {number} limit max number of nodes to execute (used to execute from start to a node)\\r\\n */\\r\\n\\r\\n LGraph.prototype.runStep = function(num, do_not_catch_errors, limit ) {\\r\\n num = num || 1;\\r\\n\\r\\n var start = LiteGraph.getTime();\\r\\n this.globaltime = 0.001 * (start - this.starttime);\\r\\n\\r\\n var nodes = this._nodes_executable\\r\\n ? this._nodes_executable\\r\\n : this._nodes;\\r\\n if (!nodes) {\\r\\n return;\\r\\n }\\r\\n\\r\\n\\t\\tlimit = limit || nodes.length;\\r\\n\\r\\n if (do_not_catch_errors) {\\r\\n //iterations\\r\\n for (var i = 0; i < num; i++) {\\r\\n for (var j = 0; j < limit; ++j) {\\r\\n var node = nodes[j];\\r\\n if (node.mode == LiteGraph.ALWAYS && node.onExecute) {\\r\\n node.onExecute(); //hard to send elapsed time\\r\\n }\\r\\n }\\r\\n\\r\\n this.fixedtime += this.fixedtime_lapse;\\r\\n if (this.onExecuteStep) {\\r\\n this.onExecuteStep();\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.onAfterExecute) {\\r\\n this.onAfterExecute();\\r\\n }\\r\\n } else {\\r\\n try {\\r\\n //iterations\\r\\n for (var i = 0; i < num; i++) {\\r\\n for (var j = 0; j < limit; ++j) {\\r\\n var node = nodes[j];\\r\\n if (node.mode == LiteGraph.ALWAYS && node.onExecute) {\\r\\n node.onExecute();\\r\\n }\\r\\n }\\r\\n\\r\\n this.fixedtime += this.fixedtime_lapse;\\r\\n if (this.onExecuteStep) {\\r\\n this.onExecuteStep();\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.onAfterExecute) {\\r\\n this.onAfterExecute();\\r\\n }\\r\\n this.errors_in_execution = false;\\r\\n } catch (err) {\\r\\n this.errors_in_execution = true;\\r\\n if (LiteGraph.throw_errors) {\\r\\n throw err;\\r\\n }\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Error during execution: \\\" + err);\\r\\n }\\r\\n this.stop();\\r\\n }\\r\\n }\\r\\n\\r\\n var now = LiteGraph.getTime();\\r\\n var elapsed = now - start;\\r\\n if (elapsed == 0) {\\r\\n elapsed = 1;\\r\\n }\\r\\n this.execution_time = 0.001 * elapsed;\\r\\n this.globaltime += 0.001 * elapsed;\\r\\n this.iteration += 1;\\r\\n this.elapsed_time = (now - this.last_update_time) * 0.001;\\r\\n this.last_update_time = now;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than\\r\\n * nodes with only inputs.\\r\\n * @method updateExecutionOrder\\r\\n */\\r\\n LGraph.prototype.updateExecutionOrder = function() {\\r\\n this._nodes_in_order = this.computeExecutionOrder(false);\\r\\n this._nodes_executable = [];\\r\\n for (var i = 0; i < this._nodes_in_order.length; ++i) {\\r\\n if (this._nodes_in_order[i].onExecute) {\\r\\n this._nodes_executable.push(this._nodes_in_order[i]);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n //This is more internal, it computes the executable nodes in order and returns it\\r\\n LGraph.prototype.computeExecutionOrder = function(\\r\\n only_onExecute,\\r\\n set_level\\r\\n ) {\\r\\n var L = [];\\r\\n var S = [];\\r\\n var M = {};\\r\\n var visited_links = {}; //to avoid repeating links\\r\\n var remaining_links = {}; //to a\\r\\n\\r\\n //search for the nodes without inputs (starting nodes)\\r\\n for (var i = 0, l = this._nodes.length; i < l; ++i) {\\r\\n var node = this._nodes[i];\\r\\n if (only_onExecute && !node.onExecute) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n M[node.id] = node; //add to pending nodes\\r\\n\\r\\n var num = 0; //num of input connections\\r\\n if (node.inputs) {\\r\\n for (var j = 0, l2 = node.inputs.length; j < l2; j++) {\\r\\n if (node.inputs[j] && node.inputs[j].link != null) {\\r\\n num += 1;\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (num == 0) {\\r\\n //is a starting node\\r\\n S.push(node);\\r\\n if (set_level) {\\r\\n node._level = 1;\\r\\n }\\r\\n } //num of input links\\r\\n else {\\r\\n if (set_level) {\\r\\n node._level = 0;\\r\\n }\\r\\n remaining_links[node.id] = num;\\r\\n }\\r\\n }\\r\\n\\r\\n while (true) {\\r\\n if (S.length == 0) {\\r\\n break;\\r\\n }\\r\\n\\r\\n //get an starting node\\r\\n var node = S.shift();\\r\\n L.push(node); //add to ordered list\\r\\n delete M[node.id]; //remove from the pending nodes\\r\\n\\r\\n if (!node.outputs) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n //for every output\\r\\n for (var i = 0; i < node.outputs.length; i++) {\\r\\n var output = node.outputs[i];\\r\\n //not connected\\r\\n if (\\r\\n output == null ||\\r\\n output.links == null ||\\r\\n output.links.length == 0\\r\\n ) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n //for every connection\\r\\n for (var j = 0; j < output.links.length; j++) {\\r\\n var link_id = output.links[j];\\r\\n var link = this.links[link_id];\\r\\n if (!link) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n //already visited link (ignore it)\\r\\n if (visited_links[link.id]) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n var target_node = this.getNodeById(link.target_id);\\r\\n if (target_node == null) {\\r\\n visited_links[link.id] = true;\\r\\n continue;\\r\\n }\\r\\n\\r\\n if (\\r\\n set_level &&\\r\\n (!target_node._level ||\\r\\n target_node._level <= node._level)\\r\\n ) {\\r\\n target_node._level = node._level + 1;\\r\\n }\\r\\n\\r\\n visited_links[link.id] = true; //mark as visited\\r\\n remaining_links[target_node.id] -= 1; //reduce the number of links remaining\\r\\n if (remaining_links[target_node.id] == 0) {\\r\\n S.push(target_node);\\r\\n } //if no more links, then add to starters array\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n //the remaining ones (loops)\\r\\n for (var i in M) {\\r\\n L.push(M[i]);\\r\\n }\\r\\n\\r\\n if (L.length != this._nodes.length && LiteGraph.debug) {\\r\\n console.warn(\\\"something went wrong, nodes missing\\\");\\r\\n }\\r\\n\\r\\n var l = L.length;\\r\\n\\r\\n //save order number in the node\\r\\n for (var i = 0; i < l; ++i) {\\r\\n L[i].order = i;\\r\\n }\\r\\n\\r\\n //sort now by priority\\r\\n L = L.sort(function(A, B) {\\r\\n var Ap = A.constructor.priority || A.priority || 0;\\r\\n var Bp = B.constructor.priority || B.priority || 0;\\r\\n if (Ap == Bp) {\\r\\n //if same priority, sort by order\\r\\n return A.order - B.order;\\r\\n }\\r\\n return Ap - Bp; //sort by priority\\r\\n });\\r\\n\\r\\n //save order number in the node, again...\\r\\n for (var i = 0; i < l; ++i) {\\r\\n L[i].order = i;\\r\\n }\\r\\n\\r\\n return L;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively.\\r\\n * It doesn't include the node itself\\r\\n * @method getAncestors\\r\\n * @return {Array} an array with all the LGraphNodes that affect this node, in order of execution\\r\\n */\\r\\n LGraph.prototype.getAncestors = function(node) {\\r\\n var ancestors = [];\\r\\n var pending = [node];\\r\\n var visited = {};\\r\\n\\r\\n while (pending.length) {\\r\\n var current = pending.shift();\\r\\n if (!current.inputs) {\\r\\n continue;\\r\\n }\\r\\n if (!visited[current.id] && current != node) {\\r\\n visited[current.id] = true;\\r\\n ancestors.push(current);\\r\\n }\\r\\n\\r\\n for (var i = 0; i < current.inputs.length; ++i) {\\r\\n var input = current.getInputNode(i);\\r\\n if (input && ancestors.indexOf(input) == -1) {\\r\\n pending.push(input);\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n ancestors.sort(function(a, b) {\\r\\n return a.order - b.order;\\r\\n });\\r\\n return ancestors;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Positions every node in a more readable manner\\r\\n * @method arrange\\r\\n */\\r\\n LGraph.prototype.arrange = function(margin) {\\r\\n margin = margin || 40;\\r\\n\\r\\n var nodes = this.computeExecutionOrder(false, true);\\r\\n var columns = [];\\r\\n for (var i = 0; i < nodes.length; ++i) {\\r\\n var node = nodes[i];\\r\\n var col = node._level || 1;\\r\\n if (!columns[col]) {\\r\\n columns[col] = [];\\r\\n }\\r\\n columns[col].push(node);\\r\\n }\\r\\n\\r\\n var x = margin;\\r\\n\\r\\n for (var i = 0; i < columns.length; ++i) {\\r\\n var column = columns[i];\\r\\n if (!column) {\\r\\n continue;\\r\\n }\\r\\n var max_size = 100;\\r\\n var y = margin;\\r\\n for (var j = 0; j < column.length; ++j) {\\r\\n var node = column[j];\\r\\n node.pos[0] = x;\\r\\n node.pos[1] = y;\\r\\n if (node.size[0] > max_size) {\\r\\n max_size = node.size[0];\\r\\n }\\r\\n y += node.size[1] + margin;\\r\\n }\\r\\n x += max_size + margin;\\r\\n }\\r\\n\\r\\n this.setDirtyCanvas(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns the amount of time the graph has been running in milliseconds\\r\\n * @method getTime\\r\\n * @return {number} number of milliseconds the graph has been running\\r\\n */\\r\\n LGraph.prototype.getTime = function() {\\r\\n return this.globaltime;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant\\r\\n * @method getFixedTime\\r\\n * @return {number} number of milliseconds the graph has been running\\r\\n */\\r\\n\\r\\n LGraph.prototype.getFixedTime = function() {\\r\\n return this.fixedtime;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct\\r\\n * if the nodes are using graphical actions\\r\\n * @method getElapsedTime\\r\\n * @return {number} number of milliseconds it took the last cycle\\r\\n */\\r\\n\\r\\n LGraph.prototype.getElapsedTime = function() {\\r\\n return this.elapsed_time;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Sends an event to all the nodes, useful to trigger stuff\\r\\n * @method sendEventToAllNodes\\r\\n * @param {String} eventname the name of the event (function to be called)\\r\\n * @param {Array} params parameters in array format\\r\\n */\\r\\n LGraph.prototype.sendEventToAllNodes = function(eventname, params, mode) {\\r\\n mode = mode || LiteGraph.ALWAYS;\\r\\n\\r\\n var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes;\\r\\n if (!nodes) {\\r\\n return;\\r\\n }\\r\\n\\r\\n for (var j = 0, l = nodes.length; j < l; ++j) {\\r\\n var node = nodes[j];\\r\\n\\r\\n if (\\r\\n node.constructor === LiteGraph.Subgraph &&\\r\\n eventname != \\\"onExecute\\\"\\r\\n ) {\\r\\n if (node.mode == mode) {\\r\\n node.sendEventToAllNodes(eventname, params, mode);\\r\\n }\\r\\n continue;\\r\\n }\\r\\n\\r\\n if (!node[eventname] || node.mode != mode) {\\r\\n continue;\\r\\n }\\r\\n if (params === undefined) {\\r\\n node[eventname]();\\r\\n } else if (params && params.constructor === Array) {\\r\\n node[eventname].apply(node, params);\\r\\n } else {\\r\\n node[eventname](params);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGraph.prototype.sendActionToCanvas = function(action, params) {\\r\\n if (!this.list_of_graphcanvas) {\\r\\n return;\\r\\n }\\r\\n\\r\\n for (var i = 0; i < this.list_of_graphcanvas.length; ++i) {\\r\\n var c = this.list_of_graphcanvas[i];\\r\\n if (c[action]) {\\r\\n c[action].apply(c, params);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Adds a new node instance to this graph\\r\\n * @method add\\r\\n * @param {LGraphNode} node the instance of the node\\r\\n */\\r\\n\\r\\n LGraph.prototype.add = function(node, skip_compute_order) {\\r\\n if (!node) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //groups\\r\\n if (node.constructor === LGraphGroup) {\\r\\n this._groups.push(node);\\r\\n this.setDirtyCanvas(true);\\r\\n this.change();\\r\\n node.graph = this;\\r\\n this._version++;\\r\\n return;\\r\\n }\\r\\n\\r\\n //nodes\\r\\n if (node.id != -1 && this._nodes_by_id[node.id] != null) {\\r\\n console.warn(\\r\\n \\\"LiteGraph: there is already a node with this ID, changing it\\\"\\r\\n );\\r\\n node.id = ++this.last_node_id;\\r\\n }\\r\\n\\r\\n if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) {\\r\\n throw \\\"LiteGraph: max number of nodes in a graph reached\\\";\\r\\n }\\r\\n\\r\\n //give him an id\\r\\n if (node.id == null || node.id == -1) {\\r\\n node.id = ++this.last_node_id;\\r\\n } else if (this.last_node_id < node.id) {\\r\\n this.last_node_id = node.id;\\r\\n }\\r\\n\\r\\n node.graph = this;\\r\\n this._version++;\\r\\n\\r\\n this._nodes.push(node);\\r\\n this._nodes_by_id[node.id] = node;\\r\\n\\r\\n if (node.onAdded) {\\r\\n node.onAdded(this);\\r\\n }\\r\\n\\r\\n if (this.config.align_to_grid) {\\r\\n node.alignToGrid();\\r\\n }\\r\\n\\r\\n if (!skip_compute_order) {\\r\\n this.updateExecutionOrder();\\r\\n }\\r\\n\\r\\n if (this.onNodeAdded) {\\r\\n this.onNodeAdded(node);\\r\\n }\\r\\n\\r\\n this.setDirtyCanvas(true);\\r\\n this.change();\\r\\n\\r\\n return node; //to chain actions\\r\\n };\\r\\n\\r\\n /**\\r\\n * Removes a node from the graph\\r\\n * @method remove\\r\\n * @param {LGraphNode} node the instance of the node\\r\\n */\\r\\n\\r\\n LGraph.prototype.remove = function(node) {\\r\\n if (node.constructor === LiteGraph.LGraphGroup) {\\r\\n var index = this._groups.indexOf(node);\\r\\n if (index != -1) {\\r\\n this._groups.splice(index, 1);\\r\\n }\\r\\n node.graph = null;\\r\\n this._version++;\\r\\n this.setDirtyCanvas(true, true);\\r\\n this.change();\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this._nodes_by_id[node.id] == null) {\\r\\n return;\\r\\n } //not found\\r\\n\\r\\n if (node.ignore_remove) {\\r\\n return;\\r\\n } //cannot be removed\\r\\n\\r\\n //disconnect inputs\\r\\n if (node.inputs) {\\r\\n for (var i = 0; i < node.inputs.length; i++) {\\r\\n var slot = node.inputs[i];\\r\\n if (slot.link != null) {\\r\\n node.disconnectInput(i);\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n //disconnect outputs\\r\\n if (node.outputs) {\\r\\n for (var i = 0; i < node.outputs.length; i++) {\\r\\n var slot = node.outputs[i];\\r\\n if (slot.links != null && slot.links.length) {\\r\\n node.disconnectOutput(i);\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n //node.id = -1; //why?\\r\\n\\r\\n //callback\\r\\n if (node.onRemoved) {\\r\\n node.onRemoved();\\r\\n }\\r\\n\\r\\n node.graph = null;\\r\\n this._version++;\\r\\n\\r\\n //remove from canvas render\\r\\n if (this.list_of_graphcanvas) {\\r\\n for (var i = 0; i < this.list_of_graphcanvas.length; ++i) {\\r\\n var canvas = this.list_of_graphcanvas[i];\\r\\n if (canvas.selected_nodes[node.id]) {\\r\\n delete canvas.selected_nodes[node.id];\\r\\n }\\r\\n if (canvas.node_dragged == node) {\\r\\n canvas.node_dragged = null;\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n //remove from containers\\r\\n var pos = this._nodes.indexOf(node);\\r\\n if (pos != -1) {\\r\\n this._nodes.splice(pos, 1);\\r\\n }\\r\\n delete this._nodes_by_id[node.id];\\r\\n\\r\\n if (this.onNodeRemoved) {\\r\\n this.onNodeRemoved(node);\\r\\n }\\r\\n\\r\\n this.setDirtyCanvas(true, true);\\r\\n this.change();\\r\\n\\r\\n this.updateExecutionOrder();\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns a node by its id.\\r\\n * @method getNodeById\\r\\n * @param {Number} id\\r\\n */\\r\\n\\r\\n LGraph.prototype.getNodeById = function(id) {\\r\\n if (id == null) {\\r\\n return null;\\r\\n }\\r\\n return this._nodes_by_id[id];\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns a list of nodes that matches a class\\r\\n * @method findNodesByClass\\r\\n * @param {Class} classObject the class itself (not an string)\\r\\n * @return {Array} a list with all the nodes of this type\\r\\n */\\r\\n LGraph.prototype.findNodesByClass = function(classObject, result) {\\r\\n result = result || [];\\r\\n result.length = 0;\\r\\n for (var i = 0, l = this._nodes.length; i < l; ++i) {\\r\\n if (this._nodes[i].constructor === classObject) {\\r\\n result.push(this._nodes[i]);\\r\\n }\\r\\n }\\r\\n return result;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns a list of nodes that matches a type\\r\\n * @method findNodesByType\\r\\n * @param {String} type the name of the node type\\r\\n * @return {Array} a list with all the nodes of this type\\r\\n */\\r\\n LGraph.prototype.findNodesByType = function(type, result) {\\r\\n var type = type.toLowerCase();\\r\\n result = result || [];\\r\\n result.length = 0;\\r\\n for (var i = 0, l = this._nodes.length; i < l; ++i) {\\r\\n if (this._nodes[i].type.toLowerCase() == type) {\\r\\n result.push(this._nodes[i]);\\r\\n }\\r\\n }\\r\\n return result;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns the first node that matches a name in its title\\r\\n * @method findNodeByTitle\\r\\n * @param {String} name the name of the node to search\\r\\n * @return {Node} the node or null\\r\\n */\\r\\n LGraph.prototype.findNodeByTitle = function(title) {\\r\\n for (var i = 0, l = this._nodes.length; i < l; ++i) {\\r\\n if (this._nodes[i].title == title) {\\r\\n return this._nodes[i];\\r\\n }\\r\\n }\\r\\n return null;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns a list of nodes that matches a name\\r\\n * @method findNodesByTitle\\r\\n * @param {String} name the name of the node to search\\r\\n * @return {Array} a list with all the nodes with this name\\r\\n */\\r\\n LGraph.prototype.findNodesByTitle = function(title) {\\r\\n var result = [];\\r\\n for (var i = 0, l = this._nodes.length; i < l; ++i) {\\r\\n if (this._nodes[i].title == title) {\\r\\n result.push(this._nodes[i]);\\r\\n }\\r\\n }\\r\\n return result;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns the top-most node in this position of the canvas\\r\\n * @method getNodeOnPos\\r\\n * @param {number} x the x coordinate in canvas space\\r\\n * @param {number} y the y coordinate in canvas space\\r\\n * @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph\\r\\n * @return {LGraphNode} the node at this position or null\\r\\n */\\r\\n LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) {\\r\\n nodes_list = nodes_list || this._nodes;\\r\\n for (var i = nodes_list.length - 1; i >= 0; i--) {\\r\\n var n = nodes_list[i];\\r\\n if (n.isPointInside(x, y, margin)) {\\r\\n return n;\\r\\n }\\r\\n }\\r\\n return null;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns the top-most group in that position\\r\\n * @method getGroupOnPos\\r\\n * @param {number} x the x coordinate in canvas space\\r\\n * @param {number} y the y coordinate in canvas space\\r\\n * @return {LGraphGroup} the group or null\\r\\n */\\r\\n LGraph.prototype.getGroupOnPos = function(x, y) {\\r\\n for (var i = this._groups.length - 1; i >= 0; i--) {\\r\\n var g = this._groups[i];\\r\\n if (g.isPointInside(x, y, 2, true)) {\\r\\n return g;\\r\\n }\\r\\n }\\r\\n return null;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Checks that the node type matches the node type registered, used when replacing a nodetype by a newer version during execution\\r\\n * this replaces the ones using the old version with the new version\\r\\n * @method checkNodeTypes\\r\\n */\\r\\n LGraph.prototype.checkNodeTypes = function() {\\r\\n var changes = false;\\r\\n for (var i = 0; i < this._nodes.length; i++) {\\r\\n var node = this._nodes[i];\\r\\n var ctor = LiteGraph.registered_node_types[node.type];\\r\\n if (node.constructor == ctor) {\\r\\n continue;\\r\\n }\\r\\n console.log(\\\"node being replaced by newer version: \\\" + node.type);\\r\\n var newnode = LiteGraph.createNode(node.type);\\r\\n changes = true;\\r\\n this._nodes[i] = newnode;\\r\\n newnode.configure(node.serialize());\\r\\n newnode.graph = this;\\r\\n this._nodes_by_id[newnode.id] = newnode;\\r\\n if (node.inputs) {\\r\\n newnode.inputs = node.inputs.concat();\\r\\n }\\r\\n if (node.outputs) {\\r\\n newnode.outputs = node.outputs.concat();\\r\\n }\\r\\n }\\r\\n this.updateExecutionOrder();\\r\\n };\\r\\n\\r\\n // ********** GLOBALS *****************\\r\\n\\r\\n LGraph.prototype.onAction = function(action, param) {\\r\\n this._input_nodes = this.findNodesByClass(\\r\\n LiteGraph.GraphInput,\\r\\n this._input_nodes\\r\\n );\\r\\n for (var i = 0; i < this._input_nodes.length; ++i) {\\r\\n var node = this._input_nodes[i];\\r\\n if (node.properties.name != action) {\\r\\n continue;\\r\\n }\\r\\n node.onAction(action, param);\\r\\n break;\\r\\n }\\r\\n };\\r\\n\\r\\n LGraph.prototype.trigger = function(action, param) {\\r\\n if (this.onTrigger) {\\r\\n this.onTrigger(action, param);\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Tell this graph it has a global graph input of this type\\r\\n * @method addGlobalInput\\r\\n * @param {String} name\\r\\n * @param {String} type\\r\\n * @param {*} value [optional]\\r\\n */\\r\\n LGraph.prototype.addInput = function(name, type, value) {\\r\\n var input = this.inputs[name];\\r\\n if (input) {\\r\\n //already exist\\r\\n return;\\r\\n }\\r\\n\\r\\n this.inputs[name] = { name: name, type: type, value: value };\\r\\n this._version++;\\r\\n\\r\\n if (this.onInputAdded) {\\r\\n this.onInputAdded(name, type);\\r\\n }\\r\\n\\r\\n if (this.onInputsOutputsChange) {\\r\\n this.onInputsOutputsChange();\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Assign a data to the global graph input\\r\\n * @method setGlobalInputData\\r\\n * @param {String} name\\r\\n * @param {*} data\\r\\n */\\r\\n LGraph.prototype.setInputData = function(name, data) {\\r\\n var input = this.inputs[name];\\r\\n if (!input) {\\r\\n return;\\r\\n }\\r\\n input.value = data;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns the current value of a global graph input\\r\\n * @method getInputData\\r\\n * @param {String} name\\r\\n * @return {*} the data\\r\\n */\\r\\n LGraph.prototype.getInputData = function(name) {\\r\\n var input = this.inputs[name];\\r\\n if (!input) {\\r\\n return null;\\r\\n }\\r\\n return input.value;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Changes the name of a global graph input\\r\\n * @method renameInput\\r\\n * @param {String} old_name\\r\\n * @param {String} new_name\\r\\n */\\r\\n LGraph.prototype.renameInput = function(old_name, name) {\\r\\n if (name == old_name) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!this.inputs[old_name]) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n if (this.inputs[name]) {\\r\\n console.error(\\\"there is already one input with that name\\\");\\r\\n return false;\\r\\n }\\r\\n\\r\\n this.inputs[name] = this.inputs[old_name];\\r\\n delete this.inputs[old_name];\\r\\n this._version++;\\r\\n\\r\\n if (this.onInputRenamed) {\\r\\n this.onInputRenamed(old_name, name);\\r\\n }\\r\\n\\r\\n if (this.onInputsOutputsChange) {\\r\\n this.onInputsOutputsChange();\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Changes the type of a global graph input\\r\\n * @method changeInputType\\r\\n * @param {String} name\\r\\n * @param {String} type\\r\\n */\\r\\n LGraph.prototype.changeInputType = function(name, type) {\\r\\n if (!this.inputs[name]) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n if (\\r\\n this.inputs[name].type &&\\r\\n String(this.inputs[name].type).toLowerCase() ==\\r\\n String(type).toLowerCase()\\r\\n ) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.inputs[name].type = type;\\r\\n this._version++;\\r\\n if (this.onInputTypeChanged) {\\r\\n this.onInputTypeChanged(name, type);\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Removes a global graph input\\r\\n * @method removeInput\\r\\n * @param {String} name\\r\\n * @param {String} type\\r\\n */\\r\\n LGraph.prototype.removeInput = function(name) {\\r\\n if (!this.inputs[name]) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n delete this.inputs[name];\\r\\n this._version++;\\r\\n\\r\\n if (this.onInputRemoved) {\\r\\n this.onInputRemoved(name);\\r\\n }\\r\\n\\r\\n if (this.onInputsOutputsChange) {\\r\\n this.onInputsOutputsChange();\\r\\n }\\r\\n return true;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Creates a global graph output\\r\\n * @method addOutput\\r\\n * @param {String} name\\r\\n * @param {String} type\\r\\n * @param {*} value\\r\\n */\\r\\n LGraph.prototype.addOutput = function(name, type, value) {\\r\\n this.outputs[name] = { name: name, type: type, value: value };\\r\\n this._version++;\\r\\n\\r\\n if (this.onOutputAdded) {\\r\\n this.onOutputAdded(name, type);\\r\\n }\\r\\n\\r\\n if (this.onInputsOutputsChange) {\\r\\n this.onInputsOutputsChange();\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Assign a data to the global output\\r\\n * @method setOutputData\\r\\n * @param {String} name\\r\\n * @param {String} value\\r\\n */\\r\\n LGraph.prototype.setOutputData = function(name, value) {\\r\\n var output = this.outputs[name];\\r\\n if (!output) {\\r\\n return;\\r\\n }\\r\\n output.value = value;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Returns the current value of a global graph output\\r\\n * @method getOutputData\\r\\n * @param {String} name\\r\\n * @return {*} the data\\r\\n */\\r\\n LGraph.prototype.getOutputData = function(name) {\\r\\n var output = this.outputs[name];\\r\\n if (!output) {\\r\\n return null;\\r\\n }\\r\\n return output.value;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Renames a global graph output\\r\\n * @method renameOutput\\r\\n * @param {String} old_name\\r\\n * @param {String} new_name\\r\\n */\\r\\n LGraph.prototype.renameOutput = function(old_name, name) {\\r\\n if (!this.outputs[old_name]) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n if (this.outputs[name]) {\\r\\n console.error(\\\"there is already one output with that name\\\");\\r\\n return false;\\r\\n }\\r\\n\\r\\n this.outputs[name] = this.outputs[old_name];\\r\\n delete this.outputs[old_name];\\r\\n this._version++;\\r\\n\\r\\n if (this.onOutputRenamed) {\\r\\n this.onOutputRenamed(old_name, name);\\r\\n }\\r\\n\\r\\n if (this.onInputsOutputsChange) {\\r\\n this.onInputsOutputsChange();\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Changes the type of a global graph output\\r\\n * @method changeOutputType\\r\\n * @param {String} name\\r\\n * @param {String} type\\r\\n */\\r\\n LGraph.prototype.changeOutputType = function(name, type) {\\r\\n if (!this.outputs[name]) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n if (\\r\\n this.outputs[name].type &&\\r\\n String(this.outputs[name].type).toLowerCase() ==\\r\\n String(type).toLowerCase()\\r\\n ) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.outputs[name].type = type;\\r\\n this._version++;\\r\\n if (this.onOutputTypeChanged) {\\r\\n this.onOutputTypeChanged(name, type);\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Removes a global graph output\\r\\n * @method removeOutput\\r\\n * @param {String} name\\r\\n */\\r\\n LGraph.prototype.removeOutput = function(name) {\\r\\n if (!this.outputs[name]) {\\r\\n return false;\\r\\n }\\r\\n delete this.outputs[name];\\r\\n this._version++;\\r\\n\\r\\n if (this.onOutputRemoved) {\\r\\n this.onOutputRemoved(name);\\r\\n }\\r\\n\\r\\n if (this.onInputsOutputsChange) {\\r\\n this.onInputsOutputsChange();\\r\\n }\\r\\n return true;\\r\\n };\\r\\n\\r\\n LGraph.prototype.triggerInput = function(name, value) {\\r\\n var nodes = this.findNodesByTitle(name);\\r\\n for (var i = 0; i < nodes.length; ++i) {\\r\\n nodes[i].onTrigger(value);\\r\\n }\\r\\n };\\r\\n\\r\\n LGraph.prototype.setCallback = function(name, func) {\\r\\n var nodes = this.findNodesByTitle(name);\\r\\n for (var i = 0; i < nodes.length; ++i) {\\r\\n nodes[i].setTrigger(func);\\r\\n }\\r\\n };\\r\\n\\r\\n LGraph.prototype.connectionChange = function(node, link_info) {\\r\\n this.updateExecutionOrder();\\r\\n if (this.onConnectionChange) {\\r\\n this.onConnectionChange(node);\\r\\n }\\r\\n this._version++;\\r\\n this.sendActionToCanvas(\\\"onConnectionChange\\\");\\r\\n };\\r\\n\\r\\n /**\\r\\n * returns if the graph is in live mode\\r\\n * @method isLive\\r\\n */\\r\\n\\r\\n LGraph.prototype.isLive = function() {\\r\\n if (!this.list_of_graphcanvas) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n for (var i = 0; i < this.list_of_graphcanvas.length; ++i) {\\r\\n var c = this.list_of_graphcanvas[i];\\r\\n if (c.live_mode) {\\r\\n return true;\\r\\n }\\r\\n }\\r\\n return false;\\r\\n };\\r\\n\\r\\n /**\\r\\n * clears the triggered slot animation in all links (stop visual animation)\\r\\n * @method clearTriggeredSlots\\r\\n */\\r\\n LGraph.prototype.clearTriggeredSlots = function() {\\r\\n for (var i in this.links) {\\r\\n var link_info = this.links[i];\\r\\n if (!link_info) {\\r\\n continue;\\r\\n }\\r\\n if (link_info._last_time) {\\r\\n link_info._last_time = 0;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n /* Called when something visually changed (not the graph!) */\\r\\n LGraph.prototype.change = function() {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Graph changed\\\");\\r\\n }\\r\\n this.sendActionToCanvas(\\\"setDirty\\\", [true, true]);\\r\\n if (this.on_change) {\\r\\n this.on_change(this);\\r\\n }\\r\\n };\\r\\n\\r\\n LGraph.prototype.setDirtyCanvas = function(fg, bg) {\\r\\n this.sendActionToCanvas(\\\"setDirty\\\", [fg, bg]);\\r\\n };\\r\\n\\r\\n /**\\r\\n * Destroys a link\\r\\n * @method removeLink\\r\\n * @param {Number} link_id\\r\\n */\\r\\n LGraph.prototype.removeLink = function(link_id) {\\r\\n var link = this.links[link_id];\\r\\n if (!link) {\\r\\n return;\\r\\n }\\r\\n var node = this.getNodeById(link.target_id);\\r\\n if (node) {\\r\\n node.disconnectInput(link.target_slot);\\r\\n }\\r\\n };\\r\\n\\r\\n //save and recover app state ***************************************\\r\\n /**\\r\\n * Creates a Object containing all the info about this graph, it can be serialized\\r\\n * @method serialize\\r\\n * @return {Object} value of the node\\r\\n */\\r\\n LGraph.prototype.serialize = function() {\\r\\n var nodes_info = [];\\r\\n for (var i = 0, l = this._nodes.length; i < l; ++i) {\\r\\n nodes_info.push(this._nodes[i].serialize());\\r\\n }\\r\\n\\r\\n //pack link info into a non-verbose format\\r\\n var links = [];\\r\\n for (var i in this.links) {\\r\\n //links is an OBJECT\\r\\n var link = this.links[i];\\r\\n if (!link.serialize) {\\r\\n //weird bug I havent solved yet\\r\\n console.warn(\\r\\n \\\"weird LLink bug, link info is not a LLink but a regular object\\\"\\r\\n );\\r\\n var link2 = new LLink();\\r\\n for (var i in link) {\\r\\n link2[i] = link[i];\\r\\n }\\r\\n this.links[i] = link2;\\r\\n link = link2;\\r\\n }\\r\\n\\r\\n links.push(link.serialize());\\r\\n }\\r\\n\\r\\n var groups_info = [];\\r\\n for (var i = 0; i < this._groups.length; ++i) {\\r\\n groups_info.push(this._groups[i].serialize());\\r\\n }\\r\\n\\r\\n var data = {\\r\\n last_node_id: this.last_node_id,\\r\\n last_link_id: this.last_link_id,\\r\\n nodes: nodes_info,\\r\\n links: links,\\r\\n groups: groups_info,\\r\\n config: this.config,\\r\\n version: LiteGraph.VERSION\\r\\n };\\r\\n\\r\\n return data;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Configure a graph from a JSON string\\r\\n * @method configure\\r\\n * @param {String} str configure a graph from a JSON string\\r\\n * @param {Boolean} returns if there was any error parsing\\r\\n */\\r\\n LGraph.prototype.configure = function(data, keep_old) {\\r\\n if (!data) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!keep_old) {\\r\\n this.clear();\\r\\n }\\r\\n\\r\\n var nodes = data.nodes;\\r\\n\\r\\n //decode links info (they are very verbose)\\r\\n if (data.links && data.links.constructor === Array) {\\r\\n var links = [];\\r\\n for (var i = 0; i < data.links.length; ++i) {\\r\\n var link_data = data.links[i];\\r\\n var link = new LLink();\\r\\n link.configure(link_data);\\r\\n links[link.id] = link;\\r\\n }\\r\\n data.links = links;\\r\\n }\\r\\n\\r\\n //copy all stored fields\\r\\n for (var i in data) {\\r\\n this[i] = data[i];\\r\\n }\\r\\n\\r\\n var error = false;\\r\\n\\r\\n //create nodes\\r\\n this._nodes = [];\\r\\n if (nodes) {\\r\\n for (var i = 0, l = nodes.length; i < l; ++i) {\\r\\n var n_info = nodes[i]; //stored info\\r\\n var node = LiteGraph.createNode(n_info.type, n_info.title);\\r\\n if (!node) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\r\\n \\\"Node not found or has errors: \\\" + n_info.type\\r\\n );\\r\\n }\\r\\n\\r\\n //in case of error we create a replacement node to avoid losing info\\r\\n node = new LGraphNode();\\r\\n node.last_serialization = n_info;\\r\\n node.has_errors = true;\\r\\n error = true;\\r\\n //continue;\\r\\n }\\r\\n\\r\\n node.id = n_info.id; //id it or it will create a new id\\r\\n this.add(node, true); //add before configure, otherwise configure cannot create links\\r\\n }\\r\\n\\r\\n //configure nodes afterwards so they can reach each other\\r\\n for (var i = 0, l = nodes.length; i < l; ++i) {\\r\\n var n_info = nodes[i];\\r\\n var node = this.getNodeById(n_info.id);\\r\\n if (node) {\\r\\n node.configure(n_info);\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n //groups\\r\\n this._groups.length = 0;\\r\\n if (data.groups) {\\r\\n for (var i = 0; i < data.groups.length; ++i) {\\r\\n var group = new LiteGraph.LGraphGroup();\\r\\n group.configure(data.groups[i]);\\r\\n this.add(group);\\r\\n }\\r\\n }\\r\\n\\r\\n this.updateExecutionOrder();\\r\\n this._version++;\\r\\n this.setDirtyCanvas(true, true);\\r\\n return error;\\r\\n };\\r\\n\\r\\n LGraph.prototype.load = function(url) {\\r\\n var that = this;\\r\\n var req = new XMLHttpRequest();\\r\\n req.open(\\\"GET\\\", url, true);\\r\\n req.send(null);\\r\\n req.onload = function(oEvent) {\\r\\n if (req.status !== 200) {\\r\\n console.error(\\\"Error loading graph:\\\", req.status, req.response);\\r\\n return;\\r\\n }\\r\\n var data = JSON.parse(req.response);\\r\\n that.configure(data);\\r\\n };\\r\\n req.onerror = function(err) {\\r\\n console.error(\\\"Error loading graph:\\\", err);\\r\\n };\\r\\n };\\r\\n\\r\\n LGraph.prototype.onNodeTrace = function(node, msg, color) {\\r\\n //TODO\\r\\n };\\r\\n\\r\\n //this is the class in charge of storing link information\\r\\n function LLink(id, type, origin_id, origin_slot, target_id, target_slot) {\\r\\n this.id = id;\\r\\n this.type = type;\\r\\n this.origin_id = origin_id;\\r\\n this.origin_slot = origin_slot;\\r\\n this.target_id = target_id;\\r\\n this.target_slot = target_slot;\\r\\n\\r\\n this._data = null;\\r\\n this._pos = new Float32Array(2); //center\\r\\n }\\r\\n\\r\\n LLink.prototype.configure = function(o) {\\r\\n if (o.constructor === Array) {\\r\\n this.id = o[0];\\r\\n this.origin_id = o[1];\\r\\n this.origin_slot = o[2];\\r\\n this.target_id = o[3];\\r\\n this.target_slot = o[4];\\r\\n this.type = o[5];\\r\\n } else {\\r\\n this.id = o.id;\\r\\n this.type = o.type;\\r\\n this.origin_id = o.origin_id;\\r\\n this.origin_slot = o.origin_slot;\\r\\n this.target_id = o.target_id;\\r\\n this.target_slot = o.target_slot;\\r\\n }\\r\\n };\\r\\n\\r\\n LLink.prototype.serialize = function() {\\r\\n return [\\r\\n this.id,\\r\\n this.origin_id,\\r\\n this.origin_slot,\\r\\n this.target_id,\\r\\n this.target_slot,\\r\\n this.type\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.LLink = LLink;\\r\\n\\r\\n // *************************************************************\\r\\n // Node CLASS *******\\r\\n // *************************************************************\\r\\n\\r\\n /*\\r\\n\\ttitle: string\\r\\n\\tpos: [x,y]\\r\\n\\tsize: [x,y]\\r\\n\\r\\n\\tinput|output: every connection\\r\\n\\t\\t+ { name:string, type:string, pos: [x,y]=Optional, direction: \\\"input\\\"|\\\"output\\\", links: Array });\\r\\n\\r\\n\\tgeneral properties:\\r\\n\\t\\t+ clip_area: if you render outside the node, it will be clipped\\r\\n\\t\\t+ unsafe_execution: not allowed for safe execution\\r\\n\\t\\t+ skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected\\r\\n\\t\\t+ resizable: if set to false it wont be resizable with the mouse\\r\\n\\t\\t+ horizontal: slots are distributed horizontally\\r\\n\\t\\t+ widgets_up: widgets start from the top of the node\\r\\n\\t\\r\\n\\tflags object:\\r\\n\\t\\t+ collapsed: if it is collapsed\\r\\n\\r\\n\\tsupported callbacks:\\r\\n\\t\\t+ onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading)\\r\\n\\t\\t+ onRemoved: when removed from graph\\r\\n\\t\\t+ onStart:\\twhen the graph starts playing\\r\\n\\t\\t+ onStop:\\twhen the graph stops playing\\r\\n\\t\\t+ onDrawForeground: render the inside widgets inside the node\\r\\n\\t\\t+ onDrawBackground: render the background area inside the node (only in edit mode)\\r\\n\\t\\t+ onMouseDown\\r\\n\\t\\t+ onMouseMove\\r\\n\\t\\t+ onMouseUp\\r\\n\\t\\t+ onMouseEnter\\r\\n\\t\\t+ onMouseLeave\\r\\n\\t\\t+ onExecute: execute the node\\r\\n\\t\\t+ onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour)\\r\\n\\t\\t+ onGetInputs: returns an array of possible inputs\\r\\n\\t\\t+ onGetOutputs: returns an array of possible outputs\\r\\n\\t\\t+ onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h])\\r\\n\\t\\t+ onDblClick: double clicked in the node\\r\\n\\t\\t+ onInputDblClick: input slot double clicked (can be used to automatically create a node connected)\\r\\n\\t\\t+ onOutputDblClick: output slot double clicked (can be used to automatically create a node connected)\\r\\n\\t\\t+ onConfigure: called after the node has been configured\\r\\n\\t\\t+ onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data)\\r\\n\\t\\t+ onSelected\\r\\n\\t\\t+ onDeselected\\r\\n\\t\\t+ onDropItem : DOM item dropped over the node\\r\\n\\t\\t+ onDropFile : file dropped over the node\\r\\n\\t\\t+ onConnectInput : if returns false the incoming connection will be canceled\\r\\n\\t\\t+ onConnectionsChange : a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info )\\r\\n\\t\\t+ onAction: action slot triggered\\r\\n\\t\\t+ getExtraMenuOptions: to add option to context menu\\r\\n*/\\r\\n\\r\\n /**\\r\\n * Base Class for all the node type classes\\r\\n * @class LGraphNode\\r\\n * @param {String} name a name for the node\\r\\n */\\r\\n\\r\\n function LGraphNode(title) {\\r\\n this._ctor(title);\\r\\n }\\r\\n\\r\\n global.LGraphNode = LiteGraph.LGraphNode = LGraphNode;\\r\\n\\r\\n LGraphNode.prototype._ctor = function(title) {\\r\\n this.title = title || \\\"Unnamed\\\";\\r\\n this.size = [LiteGraph.NODE_WIDTH, 60];\\r\\n this.graph = null;\\r\\n\\r\\n this._pos = new Float32Array(10, 10);\\r\\n\\r\\n Object.defineProperty(this, \\\"pos\\\", {\\r\\n set: function(v) {\\r\\n if (!v || v.length < 2) {\\r\\n return;\\r\\n }\\r\\n this._pos[0] = v[0];\\r\\n this._pos[1] = v[1];\\r\\n },\\r\\n get: function() {\\r\\n return this._pos;\\r\\n },\\r\\n enumerable: true\\r\\n });\\r\\n\\r\\n this.id = -1; //not know till not added\\r\\n this.type = null;\\r\\n\\r\\n //inputs available: array of inputs\\r\\n this.inputs = [];\\r\\n this.outputs = [];\\r\\n this.connections = [];\\r\\n\\r\\n //local data\\r\\n this.properties = {}; //for the values\\r\\n this.properties_info = []; //for the info\\r\\n\\r\\n this.flags = {};\\r\\n };\\r\\n\\r\\n /**\\r\\n * configure a node from an object containing the serialized info\\r\\n * @method configure\\r\\n */\\r\\n LGraphNode.prototype.configure = function(info) {\\r\\n if (this.graph) {\\r\\n this.graph._version++;\\r\\n }\\r\\n for (var j in info) {\\r\\n if (j == \\\"properties\\\") {\\r\\n //i don't want to clone properties, I want to reuse the old container\\r\\n for (var k in info.properties) {\\r\\n this.properties[k] = info.properties[k];\\r\\n if (this.onPropertyChanged) {\\r\\n this.onPropertyChanged(k, info.properties[k]);\\r\\n }\\r\\n }\\r\\n continue;\\r\\n }\\r\\n\\r\\n if (info[j] == null) {\\r\\n continue;\\r\\n } else if (typeof info[j] == \\\"object\\\") {\\r\\n //object\\r\\n if (this[j] && this[j].configure) {\\r\\n this[j].configure(info[j]);\\r\\n } else {\\r\\n this[j] = LiteGraph.cloneObject(info[j], this[j]);\\r\\n }\\r\\n } //value\\r\\n else {\\r\\n this[j] = info[j];\\r\\n }\\r\\n }\\r\\n\\r\\n if (!info.title) {\\r\\n this.title = this.constructor.title;\\r\\n }\\r\\n\\r\\n if (this.onConnectionsChange) {\\r\\n if (this.inputs) {\\r\\n for (var i = 0; i < this.inputs.length; ++i) {\\r\\n var input = this.inputs[i];\\r\\n var link_info = this.graph\\r\\n ? this.graph.links[input.link]\\r\\n : null;\\r\\n this.onConnectionsChange(\\r\\n LiteGraph.INPUT,\\r\\n i,\\r\\n true,\\r\\n link_info,\\r\\n input\\r\\n ); //link_info has been created now, so its updated\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.outputs) {\\r\\n for (var i = 0; i < this.outputs.length; ++i) {\\r\\n var output = this.outputs[i];\\r\\n if (!output.links) {\\r\\n continue;\\r\\n }\\r\\n for (var j = 0; j < output.links.length; ++j) {\\r\\n var link_info = this.graph\\r\\n ? this.graph.links[output.links[j]]\\r\\n : null;\\r\\n this.onConnectionsChange(\\r\\n LiteGraph.OUTPUT,\\r\\n i,\\r\\n true,\\r\\n link_info,\\r\\n output\\r\\n ); //link_info has been created now, so its updated\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (info.widgets_values && this.widgets) {\\r\\n for (var i = 0; i < info.widgets_values.length; ++i) {\\r\\n if (this.widgets[i]) {\\r\\n this.widgets[i].value = info.widgets_values[i];\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.onConfigure) {\\r\\n this.onConfigure(info);\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * serialize the content\\r\\n * @method serialize\\r\\n */\\r\\n\\r\\n LGraphNode.prototype.serialize = function() {\\r\\n //create serialization object\\r\\n var o = {\\r\\n id: this.id,\\r\\n type: this.type,\\r\\n pos: this.pos,\\r\\n size: this.size,\\r\\n flags: LiteGraph.cloneObject(this.flags),\\r\\n\\t\\t\\torder: this.order,\\r\\n mode: this.mode\\r\\n };\\r\\n\\r\\n //special case for when there were errors\\r\\n if (this.constructor === LGraphNode && this.last_serialization) {\\r\\n return this.last_serialization;\\r\\n }\\r\\n\\r\\n if (this.inputs) {\\r\\n o.inputs = this.inputs;\\r\\n }\\r\\n\\r\\n if (this.outputs) {\\r\\n //clear outputs last data (because data in connections is never serialized but stored inside the outputs info)\\r\\n for (var i = 0; i < this.outputs.length; i++) {\\r\\n delete this.outputs[i]._data;\\r\\n }\\r\\n o.outputs = this.outputs;\\r\\n }\\r\\n\\r\\n if (this.title && this.title != this.constructor.title) {\\r\\n o.title = this.title;\\r\\n }\\r\\n\\r\\n if (this.properties) {\\r\\n o.properties = LiteGraph.cloneObject(this.properties);\\r\\n }\\r\\n\\r\\n if (this.widgets && this.serialize_widgets) {\\r\\n o.widgets_values = [];\\r\\n for (var i = 0; i < this.widgets.length; ++i) {\\r\\n o.widgets_values[i] = this.widgets[i].value;\\r\\n }\\r\\n }\\r\\n\\r\\n if (!o.type) {\\r\\n o.type = this.constructor.type;\\r\\n }\\r\\n\\r\\n if (this.color) {\\r\\n o.color = this.color;\\r\\n }\\r\\n if (this.bgcolor) {\\r\\n o.bgcolor = this.bgcolor;\\r\\n }\\r\\n if (this.boxcolor) {\\r\\n o.boxcolor = this.boxcolor;\\r\\n }\\r\\n if (this.shape) {\\r\\n o.shape = this.shape;\\r\\n }\\r\\n\\r\\n if (this.onSerialize) {\\r\\n if (this.onSerialize(o)) {\\r\\n console.warn(\\r\\n \\\"node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter\\\"\\r\\n );\\r\\n }\\r\\n }\\r\\n\\r\\n return o;\\r\\n };\\r\\n\\r\\n /* Creates a clone of this node */\\r\\n LGraphNode.prototype.clone = function() {\\r\\n var node = LiteGraph.createNode(this.type);\\r\\n if (!node) {\\r\\n return null;\\r\\n }\\r\\n\\r\\n //we clone it because serialize returns shared containers\\r\\n var data = LiteGraph.cloneObject(this.serialize());\\r\\n\\r\\n //remove links\\r\\n if (data.inputs) {\\r\\n for (var i = 0; i < data.inputs.length; ++i) {\\r\\n data.inputs[i].link = null;\\r\\n }\\r\\n }\\r\\n\\r\\n if (data.outputs) {\\r\\n for (var i = 0; i < data.outputs.length; ++i) {\\r\\n if (data.outputs[i].links) {\\r\\n data.outputs[i].links.length = 0;\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n delete data[\\\"id\\\"];\\r\\n //remove links\\r\\n node.configure(data);\\r\\n\\r\\n return node;\\r\\n };\\r\\n\\r\\n /**\\r\\n * serialize and stringify\\r\\n * @method toString\\r\\n */\\r\\n\\r\\n LGraphNode.prototype.toString = function() {\\r\\n return JSON.stringify(this.serialize());\\r\\n };\\r\\n //LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph\\r\\n\\r\\n /**\\r\\n * get the title string\\r\\n * @method getTitle\\r\\n */\\r\\n\\r\\n LGraphNode.prototype.getTitle = function() {\\r\\n return this.title || this.constructor.title;\\r\\n };\\r\\n\\r\\n /**\\r\\n * sets the value of a property\\r\\n * @method setProperty\\r\\n * @param {String} name\\r\\n * @param {*} value\\r\\n */\\r\\n LGraphNode.prototype.setProperty = function(name, value) {\\r\\n if (!this.properties) {\\r\\n this.properties = {};\\r\\n }\\r\\n\\t\\tif( value === this.properties[name] )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar prev_value = this.properties[name];\\r\\n this.properties[name] = value;\\r\\n if (this.onPropertyChanged) {\\r\\n if( this.onPropertyChanged(name, value) === false ) //abort change\\r\\n\\t\\t\\t\\tthis.properties[name] = prev_value;\\r\\n }\\r\\n };\\r\\n\\r\\n // Execution *************************\\r\\n /**\\r\\n * sets the output data\\r\\n * @method setOutputData\\r\\n * @param {number} slot\\r\\n * @param {*} data\\r\\n */\\r\\n LGraphNode.prototype.setOutputData = function(slot, data) {\\r\\n if (!this.outputs) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //this maybe slow and a niche case\\r\\n //if(slot && slot.constructor === String)\\r\\n //\\tslot = this.findOutputSlot(slot);\\r\\n\\r\\n if (slot == -1 || slot >= this.outputs.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var output_info = this.outputs[slot];\\r\\n if (!output_info) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //store data in the output itself in case we want to debug\\r\\n output_info._data = data;\\r\\n\\r\\n //if there are connections, pass the data to the connections\\r\\n if (this.outputs[slot].links) {\\r\\n for (var i = 0; i < this.outputs[slot].links.length; i++) {\\r\\n var link_id = this.outputs[slot].links[i];\\r\\n this.graph.links[link_id].data = data;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * sets the output data type, useful when you want to be able to overwrite the data type\\r\\n * @method setOutputDataType\\r\\n * @param {number} slot\\r\\n * @param {String} datatype\\r\\n */\\r\\n LGraphNode.prototype.setOutputDataType = function(slot, type) {\\r\\n if (!this.outputs) {\\r\\n return;\\r\\n }\\r\\n if (slot == -1 || slot >= this.outputs.length) {\\r\\n return;\\r\\n }\\r\\n var output_info = this.outputs[slot];\\r\\n if (!output_info) {\\r\\n return;\\r\\n }\\r\\n //store data in the output itself in case we want to debug\\r\\n output_info.type = type;\\r\\n\\r\\n //if there are connections, pass the data to the connections\\r\\n if (this.outputs[slot].links) {\\r\\n for (var i = 0; i < this.outputs[slot].links.length; i++) {\\r\\n var link_id = this.outputs[slot].links[i];\\r\\n this.graph.links[link_id].type = type;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Retrieves the input data (data traveling through the connection) from one slot\\r\\n * @method getInputData\\r\\n * @param {number} slot\\r\\n * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link\\r\\n * @return {*} data or if it is not connected returns undefined\\r\\n */\\r\\n LGraphNode.prototype.getInputData = function(slot, force_update) {\\r\\n if (!this.inputs) {\\r\\n return;\\r\\n } //undefined;\\r\\n\\r\\n if (slot >= this.inputs.length || this.inputs[slot].link == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var link_id = this.inputs[slot].link;\\r\\n var link = this.graph.links[link_id];\\r\\n if (!link) {\\r\\n //bug: weird case but it happens sometimes\\r\\n return null;\\r\\n }\\r\\n\\r\\n if (!force_update) {\\r\\n return link.data;\\r\\n }\\r\\n\\r\\n //special case: used to extract data from the incoming connection before the graph has been executed\\r\\n var node = this.graph.getNodeById(link.origin_id);\\r\\n if (!node) {\\r\\n return link.data;\\r\\n }\\r\\n\\r\\n if (node.updateOutputData) {\\r\\n node.updateOutputData(link.origin_slot);\\r\\n } else if (node.onExecute) {\\r\\n node.onExecute();\\r\\n }\\r\\n\\r\\n return link.data;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Retrieves the input data type (in case this supports multiple input types)\\r\\n * @method getInputDataType\\r\\n * @param {number} slot\\r\\n * @return {String} datatype in string format\\r\\n */\\r\\n LGraphNode.prototype.getInputDataType = function(slot) {\\r\\n if (!this.inputs) {\\r\\n return null;\\r\\n } //undefined;\\r\\n\\r\\n if (slot >= this.inputs.length || this.inputs[slot].link == null) {\\r\\n return null;\\r\\n }\\r\\n var link_id = this.inputs[slot].link;\\r\\n var link = this.graph.links[link_id];\\r\\n if (!link) {\\r\\n //bug: weird case but it happens sometimes\\r\\n return null;\\r\\n }\\r\\n var node = this.graph.getNodeById(link.origin_id);\\r\\n if (!node) {\\r\\n return link.type;\\r\\n }\\r\\n var output_info = node.outputs[link.origin_slot];\\r\\n if (output_info) {\\r\\n return output_info.type;\\r\\n }\\r\\n return null;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Retrieves the input data from one slot using its name instead of slot number\\r\\n * @method getInputDataByName\\r\\n * @param {String} slot_name\\r\\n * @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link\\r\\n * @return {*} data or if it is not connected returns null\\r\\n */\\r\\n LGraphNode.prototype.getInputDataByName = function(\\r\\n slot_name,\\r\\n force_update\\r\\n ) {\\r\\n var slot = this.findInputSlot(slot_name);\\r\\n if (slot == -1) {\\r\\n return null;\\r\\n }\\r\\n return this.getInputData(slot, force_update);\\r\\n };\\r\\n\\r\\n /**\\r\\n * tells you if there is a connection in one input slot\\r\\n * @method isInputConnected\\r\\n * @param {number} slot\\r\\n * @return {boolean}\\r\\n */\\r\\n LGraphNode.prototype.isInputConnected = function(slot) {\\r\\n if (!this.inputs) {\\r\\n return false;\\r\\n }\\r\\n return slot < this.inputs.length && this.inputs[slot].link != null;\\r\\n };\\r\\n\\r\\n /**\\r\\n * tells you info about an input connection (which node, type, etc)\\r\\n * @method getInputInfo\\r\\n * @param {number} slot\\r\\n * @return {Object} object or null { link: id, name: string, type: string or 0 }\\r\\n */\\r\\n LGraphNode.prototype.getInputInfo = function(slot) {\\r\\n if (!this.inputs) {\\r\\n return null;\\r\\n }\\r\\n if (slot < this.inputs.length) {\\r\\n return this.inputs[slot];\\r\\n }\\r\\n return null;\\r\\n };\\r\\n\\r\\n /**\\r\\n * returns the node connected in the input slot\\r\\n * @method getInputNode\\r\\n * @param {number} slot\\r\\n * @return {LGraphNode} node or null\\r\\n */\\r\\n LGraphNode.prototype.getInputNode = function(slot) {\\r\\n if (!this.inputs) {\\r\\n return null;\\r\\n }\\r\\n if (slot >= this.inputs.length) {\\r\\n return null;\\r\\n }\\r\\n var input = this.inputs[slot];\\r\\n if (!input || input.link === null) {\\r\\n return null;\\r\\n }\\r\\n var link_info = this.graph.links[input.link];\\r\\n if (!link_info) {\\r\\n return null;\\r\\n }\\r\\n return this.graph.getNodeById(link_info.origin_id);\\r\\n };\\r\\n\\r\\n /**\\r\\n * returns the value of an input with this name, otherwise checks if there is a property with that name\\r\\n * @method getInputOrProperty\\r\\n * @param {string} name\\r\\n * @return {*} value\\r\\n */\\r\\n LGraphNode.prototype.getInputOrProperty = function(name) {\\r\\n if (!this.inputs || !this.inputs.length) {\\r\\n return this.properties ? this.properties[name] : null;\\r\\n }\\r\\n\\r\\n for (var i = 0, l = this.inputs.length; i < l; ++i) {\\r\\n var input_info = this.inputs[i];\\r\\n if (name == input_info.name && input_info.link != null) {\\r\\n var link = this.graph.links[input_info.link];\\r\\n if (link) {\\r\\n return link.data;\\r\\n }\\r\\n }\\r\\n }\\r\\n return this.properties[name];\\r\\n };\\r\\n\\r\\n /**\\r\\n * tells you the last output data that went in that slot\\r\\n * @method getOutputData\\r\\n * @param {number} slot\\r\\n * @return {Object} object or null\\r\\n */\\r\\n LGraphNode.prototype.getOutputData = function(slot) {\\r\\n if (!this.outputs) {\\r\\n return null;\\r\\n }\\r\\n if (slot >= this.outputs.length) {\\r\\n return null;\\r\\n }\\r\\n\\r\\n var info = this.outputs[slot];\\r\\n return info._data;\\r\\n };\\r\\n\\r\\n /**\\r\\n * tells you info about an output connection (which node, type, etc)\\r\\n * @method getOutputInfo\\r\\n * @param {number} slot\\r\\n * @return {Object} object or null { name: string, type: string, links: [ ids of links in number ] }\\r\\n */\\r\\n LGraphNode.prototype.getOutputInfo = function(slot) {\\r\\n if (!this.outputs) {\\r\\n return null;\\r\\n }\\r\\n if (slot < this.outputs.length) {\\r\\n return this.outputs[slot];\\r\\n }\\r\\n return null;\\r\\n };\\r\\n\\r\\n /**\\r\\n * tells you if there is a connection in one output slot\\r\\n * @method isOutputConnected\\r\\n * @param {number} slot\\r\\n * @return {boolean}\\r\\n */\\r\\n LGraphNode.prototype.isOutputConnected = function(slot) {\\r\\n if (!this.outputs) {\\r\\n return false;\\r\\n }\\r\\n return (\\r\\n slot < this.outputs.length &&\\r\\n this.outputs[slot].links &&\\r\\n this.outputs[slot].links.length\\r\\n );\\r\\n };\\r\\n\\r\\n /**\\r\\n * tells you if there is any connection in the output slots\\r\\n * @method isAnyOutputConnected\\r\\n * @return {boolean}\\r\\n */\\r\\n LGraphNode.prototype.isAnyOutputConnected = function() {\\r\\n if (!this.outputs) {\\r\\n return false;\\r\\n }\\r\\n for (var i = 0; i < this.outputs.length; ++i) {\\r\\n if (this.outputs[i].links && this.outputs[i].links.length) {\\r\\n return true;\\r\\n }\\r\\n }\\r\\n return false;\\r\\n };\\r\\n\\r\\n /**\\r\\n * retrieves all the nodes connected to this output slot\\r\\n * @method getOutputNodes\\r\\n * @param {number} slot\\r\\n * @return {array}\\r\\n */\\r\\n LGraphNode.prototype.getOutputNodes = function(slot) {\\r\\n if (!this.outputs || this.outputs.length == 0) {\\r\\n return null;\\r\\n }\\r\\n\\r\\n if (slot >= this.outputs.length) {\\r\\n return null;\\r\\n }\\r\\n\\r\\n var output = this.outputs[slot];\\r\\n if (!output.links || output.links.length == 0) {\\r\\n return null;\\r\\n }\\r\\n\\r\\n var r = [];\\r\\n for (var i = 0; i < output.links.length; i++) {\\r\\n var link_id = output.links[i];\\r\\n var link = this.graph.links[link_id];\\r\\n if (link) {\\r\\n var target_node = this.graph.getNodeById(link.target_id);\\r\\n if (target_node) {\\r\\n r.push(target_node);\\r\\n }\\r\\n }\\r\\n }\\r\\n return r;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Triggers an event in this node, this will trigger any output with the same name\\r\\n * @method trigger\\r\\n * @param {String} event name ( \\\"on_play\\\", ... ) if action is equivalent to false then the event is send to all\\r\\n * @param {*} param\\r\\n */\\r\\n LGraphNode.prototype.trigger = function(action, param) {\\r\\n if (!this.outputs || !this.outputs.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.graph) {\\r\\n this.graph._last_trigger_time = LiteGraph.getTime();\\r\\n }\\r\\n\\r\\n for (var i = 0; i < this.outputs.length; ++i) {\\r\\n var output = this.outputs[i];\\r\\n if (\\r\\n !output ||\\r\\n output.type !== LiteGraph.EVENT ||\\r\\n (action && output.name != action)\\r\\n ) {\\r\\n continue;\\r\\n }\\r\\n this.triggerSlot(i, param);\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Triggers an slot event in this node\\r\\n * @method triggerSlot\\r\\n * @param {Number} slot the index of the output slot\\r\\n * @param {*} param\\r\\n * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot\\r\\n */\\r\\n LGraphNode.prototype.triggerSlot = function(slot, param, link_id) {\\r\\n if (!this.outputs) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var output = this.outputs[slot];\\r\\n if (!output) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var links = output.links;\\r\\n if (!links || !links.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.graph) {\\r\\n this.graph._last_trigger_time = LiteGraph.getTime();\\r\\n }\\r\\n\\r\\n //for every link attached here\\r\\n for (var k = 0; k < links.length; ++k) {\\r\\n var id = links[k];\\r\\n if (link_id != null && link_id != id) {\\r\\n //to skip links\\r\\n continue;\\r\\n }\\r\\n var link_info = this.graph.links[links[k]];\\r\\n if (!link_info) {\\r\\n //not connected\\r\\n continue;\\r\\n }\\r\\n link_info._last_time = LiteGraph.getTime();\\r\\n var node = this.graph.getNodeById(link_info.target_id);\\r\\n if (!node) {\\r\\n //node not found?\\r\\n continue;\\r\\n }\\r\\n\\r\\n //used to mark events in graph\\r\\n var target_connection = node.inputs[link_info.target_slot];\\r\\n\\r\\n if (node.onAction) {\\r\\n node.onAction(target_connection.name, param);\\r\\n } else if (node.mode === LiteGraph.ON_TRIGGER) {\\r\\n if (node.onExecute) {\\r\\n node.onExecute(param);\\r\\n }\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * clears the trigger slot animation\\r\\n * @method clearTriggeredSlot\\r\\n * @param {Number} slot the index of the output slot\\r\\n * @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot\\r\\n */\\r\\n LGraphNode.prototype.clearTriggeredSlot = function(slot, link_id) {\\r\\n if (!this.outputs) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var output = this.outputs[slot];\\r\\n if (!output) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var links = output.links;\\r\\n if (!links || !links.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //for every link attached here\\r\\n for (var k = 0; k < links.length; ++k) {\\r\\n var id = links[k];\\r\\n if (link_id != null && link_id != id) {\\r\\n //to skip links\\r\\n continue;\\r\\n }\\r\\n var link_info = this.graph.links[links[k]];\\r\\n if (!link_info) {\\r\\n //not connected\\r\\n continue;\\r\\n }\\r\\n link_info._last_time = 0;\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * add a new property to this node\\r\\n * @method addProperty\\r\\n * @param {string} name\\r\\n * @param {*} default_value\\r\\n * @param {string} type string defining the output type (\\\"vec3\\\",\\\"number\\\",...)\\r\\n * @param {Object} extra_info this can be used to have special properties of the property (like values, etc)\\r\\n */\\r\\n LGraphNode.prototype.addProperty = function(\\r\\n name,\\r\\n default_value,\\r\\n type,\\r\\n extra_info\\r\\n ) {\\r\\n var o = { name: name, type: type, default_value: default_value };\\r\\n if (extra_info) {\\r\\n for (var i in extra_info) {\\r\\n o[i] = extra_info[i];\\r\\n }\\r\\n }\\r\\n if (!this.properties_info) {\\r\\n this.properties_info = [];\\r\\n }\\r\\n this.properties_info.push(o);\\r\\n if (!this.properties) {\\r\\n this.properties = {};\\r\\n }\\r\\n this.properties[name] = default_value;\\r\\n return o;\\r\\n };\\r\\n\\r\\n //connections\\r\\n\\r\\n /**\\r\\n * add a new output slot to use in this node\\r\\n * @method addOutput\\r\\n * @param {string} name\\r\\n * @param {string} type string defining the output type (\\\"vec3\\\",\\\"number\\\",...)\\r\\n * @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc)\\r\\n */\\r\\n LGraphNode.prototype.addOutput = function(name, type, extra_info) {\\r\\n var o = { name: name, type: type, links: null };\\r\\n if (extra_info) {\\r\\n for (var i in extra_info) {\\r\\n o[i] = extra_info[i];\\r\\n }\\r\\n }\\r\\n\\r\\n if (!this.outputs) {\\r\\n this.outputs = [];\\r\\n }\\r\\n this.outputs.push(o);\\r\\n if (this.onOutputAdded) {\\r\\n this.onOutputAdded(o);\\r\\n }\\r\\n this.size = this.computeSize();\\r\\n this.setDirtyCanvas(true, true);\\r\\n return o;\\r\\n };\\r\\n\\r\\n /**\\r\\n * add a new output slot to use in this node\\r\\n * @method addOutputs\\r\\n * @param {Array} array of triplets like [[name,type,extra_info],[...]]\\r\\n */\\r\\n LGraphNode.prototype.addOutputs = function(array) {\\r\\n for (var i = 0; i < array.length; ++i) {\\r\\n var info = array[i];\\r\\n var o = { name: info[0], type: info[1], link: null };\\r\\n if (array[2]) {\\r\\n for (var j in info[2]) {\\r\\n o[j] = info[2][j];\\r\\n }\\r\\n }\\r\\n\\r\\n if (!this.outputs) {\\r\\n this.outputs = [];\\r\\n }\\r\\n this.outputs.push(o);\\r\\n if (this.onOutputAdded) {\\r\\n this.onOutputAdded(o);\\r\\n }\\r\\n }\\r\\n\\r\\n this.size = this.computeSize();\\r\\n this.setDirtyCanvas(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * remove an existing output slot\\r\\n * @method removeOutput\\r\\n * @param {number} slot\\r\\n */\\r\\n LGraphNode.prototype.removeOutput = function(slot) {\\r\\n this.disconnectOutput(slot);\\r\\n this.outputs.splice(slot, 1);\\r\\n for (var i = slot; i < this.outputs.length; ++i) {\\r\\n if (!this.outputs[i] || !this.outputs[i].links) {\\r\\n continue;\\r\\n }\\r\\n var links = this.outputs[i].links;\\r\\n for (var j = 0; j < links.length; ++j) {\\r\\n var link = this.graph.links[links[j]];\\r\\n if (!link) {\\r\\n continue;\\r\\n }\\r\\n link.origin_slot -= 1;\\r\\n }\\r\\n }\\r\\n\\r\\n this.size = this.computeSize();\\r\\n if (this.onOutputRemoved) {\\r\\n this.onOutputRemoved(slot);\\r\\n }\\r\\n this.setDirtyCanvas(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * add a new input slot to use in this node\\r\\n * @method addInput\\r\\n * @param {string} name\\r\\n * @param {string} type string defining the input type (\\\"vec3\\\",\\\"number\\\",...), it its a generic one use 0\\r\\n * @param {Object} extra_info this can be used to have special properties of an input (label, color, position, etc)\\r\\n */\\r\\n LGraphNode.prototype.addInput = function(name, type, extra_info) {\\r\\n type = type || 0;\\r\\n var o = { name: name, type: type, link: null };\\r\\n if (extra_info) {\\r\\n for (var i in extra_info) {\\r\\n o[i] = extra_info[i];\\r\\n }\\r\\n }\\r\\n\\r\\n if (!this.inputs) {\\r\\n this.inputs = [];\\r\\n }\\r\\n this.inputs.push(o);\\r\\n this.size = this.computeSize();\\r\\n if (this.onInputAdded) {\\r\\n this.onInputAdded(o);\\r\\n }\\r\\n this.setDirtyCanvas(true, true);\\r\\n return o;\\r\\n };\\r\\n\\r\\n /**\\r\\n * add several new input slots in this node\\r\\n * @method addInputs\\r\\n * @param {Array} array of triplets like [[name,type,extra_info],[...]]\\r\\n */\\r\\n LGraphNode.prototype.addInputs = function(array) {\\r\\n for (var i = 0; i < array.length; ++i) {\\r\\n var info = array[i];\\r\\n var o = { name: info[0], type: info[1], link: null };\\r\\n if (array[2]) {\\r\\n for (var j in info[2]) {\\r\\n o[j] = info[2][j];\\r\\n }\\r\\n }\\r\\n\\r\\n if (!this.inputs) {\\r\\n this.inputs = [];\\r\\n }\\r\\n this.inputs.push(o);\\r\\n if (this.onInputAdded) {\\r\\n this.onInputAdded(o);\\r\\n }\\r\\n }\\r\\n\\r\\n this.size = this.computeSize();\\r\\n this.setDirtyCanvas(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * remove an existing input slot\\r\\n * @method removeInput\\r\\n * @param {number} slot\\r\\n */\\r\\n LGraphNode.prototype.removeInput = function(slot) {\\r\\n this.disconnectInput(slot);\\r\\n this.inputs.splice(slot, 1);\\r\\n for (var i = slot; i < this.inputs.length; ++i) {\\r\\n if (!this.inputs[i]) {\\r\\n continue;\\r\\n }\\r\\n var link = this.graph.links[this.inputs[i].link];\\r\\n if (!link) {\\r\\n continue;\\r\\n }\\r\\n link.target_slot -= 1;\\r\\n }\\r\\n this.size = this.computeSize();\\r\\n if (this.onInputRemoved) {\\r\\n this.onInputRemoved(slot);\\r\\n }\\r\\n this.setDirtyCanvas(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * add an special connection to this node (used for special kinds of graphs)\\r\\n * @method addConnection\\r\\n * @param {string} name\\r\\n * @param {string} type string defining the input type (\\\"vec3\\\",\\\"number\\\",...)\\r\\n * @param {[x,y]} pos position of the connection inside the node\\r\\n * @param {string} direction if is input or output\\r\\n */\\r\\n LGraphNode.prototype.addConnection = function(name, type, pos, direction) {\\r\\n var o = {\\r\\n name: name,\\r\\n type: type,\\r\\n pos: pos,\\r\\n direction: direction,\\r\\n links: null\\r\\n };\\r\\n this.connections.push(o);\\r\\n return o;\\r\\n };\\r\\n\\r\\n /**\\r\\n * computes the size of a node according to its inputs and output slots\\r\\n * @method computeSize\\r\\n * @param {number} minHeight\\r\\n * @return {number} the total size\\r\\n */\\r\\n LGraphNode.prototype.computeSize = function(minHeight, out) {\\r\\n if (this.constructor.size) {\\r\\n return this.constructor.size.concat();\\r\\n }\\r\\n\\r\\n var rows = Math.max(\\r\\n this.inputs ? this.inputs.length : 1,\\r\\n this.outputs ? this.outputs.length : 1\\r\\n );\\r\\n var size = out || new Float32Array([0, 0]);\\r\\n rows = Math.max(rows, 1);\\r\\n var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size\\r\\n size[1] =\\r\\n (this.constructor.slot_start_y || 0) +\\r\\n rows * LiteGraph.NODE_SLOT_HEIGHT;\\r\\n var widgets_height = 0;\\r\\n if (this.widgets && this.widgets.length) {\\r\\n widgets_height =\\r\\n this.widgets.length * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 8;\\r\\n }\\r\\n if (this.widgets_up) {\\r\\n size[1] = Math.max(size[1], widgets_height);\\r\\n } else {\\r\\n size[1] += widgets_height;\\r\\n }\\r\\n\\r\\n var font_size = font_size;\\r\\n var title_width = compute_text_size(this.title);\\r\\n var input_width = 0;\\r\\n var output_width = 0;\\r\\n\\r\\n if (this.inputs) {\\r\\n for (var i = 0, l = this.inputs.length; i < l; ++i) {\\r\\n var input = this.inputs[i];\\r\\n var text = input.label || input.name || \\\"\\\";\\r\\n var text_width = compute_text_size(text);\\r\\n if (input_width < text_width) {\\r\\n input_width = text_width;\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.outputs) {\\r\\n for (var i = 0, l = this.outputs.length; i < l; ++i) {\\r\\n var output = this.outputs[i];\\r\\n var text = output.label || output.name || \\\"\\\";\\r\\n var text_width = compute_text_size(text);\\r\\n if (output_width < text_width) {\\r\\n output_width = text_width;\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n size[0] = Math.max(input_width + output_width + 10, title_width);\\r\\n size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH);\\r\\n if (this.widgets && this.widgets.length) {\\r\\n size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5);\\r\\n }\\r\\n\\r\\n if (this.onResize) {\\r\\n this.onResize(size);\\r\\n }\\r\\n\\r\\n function compute_text_size(text) {\\r\\n if (!text) {\\r\\n return 0;\\r\\n }\\r\\n return font_size * text.length * 0.6;\\r\\n }\\r\\n\\r\\n if (\\r\\n this.constructor.min_height &&\\r\\n size[1] < this.constructor.min_height\\r\\n ) {\\r\\n size[1] = this.constructor.min_height;\\r\\n }\\r\\n\\r\\n size[1] += 6; //margin\\r\\n\\r\\n return size;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Allows to pass\\r\\n *\\r\\n * @method addWidget\\r\\n * @return {Object} the created widget\\r\\n */\\r\\n LGraphNode.prototype.addWidget = function(\\r\\n type,\\r\\n name,\\r\\n value,\\r\\n callback,\\r\\n options\\r\\n ) {\\r\\n if (!this.widgets) {\\r\\n this.widgets = [];\\r\\n }\\r\\n var w = {\\r\\n type: type.toLowerCase(),\\r\\n name: name,\\r\\n value: value,\\r\\n callback: callback,\\r\\n options: options || {}\\r\\n };\\r\\n\\r\\n if (w.options.y !== undefined) {\\r\\n w.y = w.options.y;\\r\\n }\\r\\n\\r\\n if (!callback) {\\r\\n console.warn(\\\"LiteGraph addWidget(...) without a callback\\\");\\r\\n }\\r\\n if (type == \\\"combo\\\" && !w.options.values) {\\r\\n throw \\\"LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }\\\";\\r\\n }\\r\\n this.widgets.push(w);\\r\\n\\t\\tthis.size = this.computeSize();\\r\\n return w;\\r\\n };\\r\\n\\r\\n LGraphNode.prototype.addCustomWidget = function(custom_widget) {\\r\\n if (!this.widgets) {\\r\\n this.widgets = [];\\r\\n }\\r\\n this.widgets.push(custom_widget);\\r\\n return custom_widget;\\r\\n };\\r\\n\\r\\n /**\\r\\n * returns the bounding of the object, used for rendering purposes\\r\\n * bounding is: [topleft_cornerx, topleft_cornery, width, height]\\r\\n * @method getBounding\\r\\n * @return {Float32Array[4]} the total size\\r\\n */\\r\\n LGraphNode.prototype.getBounding = function(out) {\\r\\n out = out || new Float32Array(4);\\r\\n out[0] = this.pos[0] - 4;\\r\\n out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT;\\r\\n out[2] = this.size[0] + 4;\\r\\n out[3] = this.size[1] + LiteGraph.NODE_TITLE_HEIGHT;\\r\\n\\r\\n if (this.onBounding) {\\r\\n this.onBounding(out);\\r\\n }\\r\\n return out;\\r\\n };\\r\\n\\r\\n /**\\r\\n * checks if a point is inside the shape of a node\\r\\n * @method isPointInside\\r\\n * @param {number} x\\r\\n * @param {number} y\\r\\n * @return {boolean}\\r\\n */\\r\\n LGraphNode.prototype.isPointInside = function(x, y, margin, skip_title) {\\r\\n margin = margin || 0;\\r\\n\\r\\n var margin_top = this.graph && this.graph.isLive() ? 0 : LiteGraph.NODE_TITLE_HEIGHT;\\r\\n if (skip_title) {\\r\\n margin_top = 0;\\r\\n }\\r\\n if (this.flags && this.flags.collapsed) {\\r\\n //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS)\\r\\n if (\\r\\n isInsideRectangle(\\r\\n x,\\r\\n y,\\r\\n this.pos[0] - margin,\\r\\n this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin,\\r\\n (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) +\\r\\n 2 * margin,\\r\\n LiteGraph.NODE_TITLE_HEIGHT + 2 * margin\\r\\n )\\r\\n ) {\\r\\n return true;\\r\\n }\\r\\n } else if (\\r\\n this.pos[0] - 4 - margin < x &&\\r\\n this.pos[0] + this.size[0] + 4 + margin > x &&\\r\\n this.pos[1] - margin_top - margin < y &&\\r\\n this.pos[1] + this.size[1] + margin > y\\r\\n ) {\\r\\n return true;\\r\\n }\\r\\n return false;\\r\\n };\\r\\n\\r\\n /**\\r\\n * checks if a point is inside a node slot, and returns info about which slot\\r\\n * @method getSlotInPosition\\r\\n * @param {number} x\\r\\n * @param {number} y\\r\\n * @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] }\\r\\n */\\r\\n LGraphNode.prototype.getSlotInPosition = function(x, y) {\\r\\n //search for inputs\\r\\n var link_pos = new Float32Array(2);\\r\\n if (this.inputs) {\\r\\n for (var i = 0, l = this.inputs.length; i < l; ++i) {\\r\\n var input = this.inputs[i];\\r\\n this.getConnectionPos(true, i, link_pos);\\r\\n if (\\r\\n isInsideRectangle(\\r\\n x,\\r\\n y,\\r\\n link_pos[0] - 10,\\r\\n link_pos[1] - 5,\\r\\n 20,\\r\\n 10\\r\\n )\\r\\n ) {\\r\\n return { input: input, slot: i, link_pos: link_pos };\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.outputs) {\\r\\n for (var i = 0, l = this.outputs.length; i < l; ++i) {\\r\\n var output = this.outputs[i];\\r\\n this.getConnectionPos(false, i, link_pos);\\r\\n if (\\r\\n isInsideRectangle(\\r\\n x,\\r\\n y,\\r\\n link_pos[0] - 10,\\r\\n link_pos[1] - 5,\\r\\n 20,\\r\\n 10\\r\\n )\\r\\n ) {\\r\\n return { output: output, slot: i, link_pos: link_pos };\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n return null;\\r\\n };\\r\\n\\r\\n /**\\r\\n * returns the input slot with a given name (used for dynamic slots), -1 if not found\\r\\n * @method findInputSlot\\r\\n * @param {string} name the name of the slot\\r\\n * @return {number} the slot (-1 if not found)\\r\\n */\\r\\n LGraphNode.prototype.findInputSlot = function(name) {\\r\\n if (!this.inputs) {\\r\\n return -1;\\r\\n }\\r\\n for (var i = 0, l = this.inputs.length; i < l; ++i) {\\r\\n if (name == this.inputs[i].name) {\\r\\n return i;\\r\\n }\\r\\n }\\r\\n return -1;\\r\\n };\\r\\n\\r\\n /**\\r\\n * returns the output slot with a given name (used for dynamic slots), -1 if not found\\r\\n * @method findOutputSlot\\r\\n * @param {string} name the name of the slot\\r\\n * @return {number} the slot (-1 if not found)\\r\\n */\\r\\n LGraphNode.prototype.findOutputSlot = function(name) {\\r\\n if (!this.outputs) {\\r\\n return -1;\\r\\n }\\r\\n for (var i = 0, l = this.outputs.length; i < l; ++i) {\\r\\n if (name == this.outputs[i].name) {\\r\\n return i;\\r\\n }\\r\\n }\\r\\n return -1;\\r\\n };\\r\\n\\r\\n /**\\r\\n * connect this node output to the input of another node\\r\\n * @method connect\\r\\n * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)\\r\\n * @param {LGraphNode} node the target node\\r\\n * @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger)\\r\\n * @return {Object} the link_info is created, otherwise null\\r\\n */\\r\\n LGraphNode.prototype.connect = function(slot, target_node, target_slot) {\\r\\n target_slot = target_slot || 0;\\r\\n\\r\\n if (!this.graph) {\\r\\n //could be connected before adding it to a graph\\r\\n console.log(\\r\\n \\\"Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them.\\\"\\r\\n ); //due to link ids being associated with graphs\\r\\n return null;\\r\\n }\\r\\n\\r\\n //seek for the output slot\\r\\n if (slot.constructor === String) {\\r\\n slot = this.findOutputSlot(slot);\\r\\n if (slot == -1) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Connect: Error, no slot of name \\\" + slot);\\r\\n }\\r\\n return null;\\r\\n }\\r\\n } else if (!this.outputs || slot >= this.outputs.length) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Connect: Error, slot number not found\\\");\\r\\n }\\r\\n return null;\\r\\n }\\r\\n\\r\\n if (target_node && target_node.constructor === Number) {\\r\\n target_node = this.graph.getNodeById(target_node);\\r\\n }\\r\\n if (!target_node) {\\r\\n throw \\\"target node is null\\\";\\r\\n }\\r\\n\\r\\n //avoid loopback\\r\\n if (target_node == this) {\\r\\n return null;\\r\\n }\\r\\n\\r\\n //you can specify the slot by name\\r\\n if (target_slot.constructor === String) {\\r\\n target_slot = target_node.findInputSlot(target_slot);\\r\\n if (target_slot == -1) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\r\\n \\\"Connect: Error, no slot of name \\\" + target_slot\\r\\n );\\r\\n }\\r\\n return null;\\r\\n }\\r\\n } else if (target_slot === LiteGraph.EVENT) {\\r\\n //search for first slot with event?\\r\\n /*\\r\\n\\t\\t//create input for trigger\\r\\n\\t\\tvar input = target_node.addInput(\\\"onTrigger\\\", LiteGraph.EVENT );\\r\\n\\t\\ttarget_slot = target_node.inputs.length - 1; //last one is the one created\\r\\n\\t\\ttarget_node.mode = LiteGraph.ON_TRIGGER;\\r\\n\\t\\t*/\\r\\n return null;\\r\\n } else if (\\r\\n !target_node.inputs ||\\r\\n target_slot >= target_node.inputs.length\\r\\n ) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Connect: Error, slot number not found\\\");\\r\\n }\\r\\n return null;\\r\\n }\\r\\n\\r\\n //if there is something already plugged there, disconnect\\r\\n if (target_node.inputs[target_slot].link != null) {\\r\\n target_node.disconnectInput(target_slot);\\r\\n }\\r\\n\\r\\n //why here??\\r\\n //this.setDirtyCanvas(false,true);\\r\\n //this.graph.connectionChange( this );\\r\\n\\r\\n var output = this.outputs[slot];\\r\\n\\r\\n //allows nodes to block connection\\r\\n if (target_node.onConnectInput) {\\r\\n if (\\r\\n target_node.onConnectInput(target_slot, output.type, output) ===\\r\\n false\\r\\n ) {\\r\\n return null;\\r\\n }\\r\\n }\\r\\n\\r\\n var input = target_node.inputs[target_slot];\\r\\n var link_info = null;\\r\\n\\r\\n if (LiteGraph.isValidConnection(output.type, input.type)) {\\r\\n link_info = new LLink(\\r\\n this.graph.last_link_id++,\\r\\n input.type,\\r\\n this.id,\\r\\n slot,\\r\\n target_node.id,\\r\\n target_slot\\r\\n );\\r\\n\\r\\n //add to graph links list\\r\\n this.graph.links[link_info.id] = link_info;\\r\\n\\r\\n //connect in output\\r\\n if (output.links == null) {\\r\\n output.links = [];\\r\\n }\\r\\n output.links.push(link_info.id);\\r\\n //connect in input\\r\\n target_node.inputs[target_slot].link = link_info.id;\\r\\n if (this.graph) {\\r\\n this.graph._version++;\\r\\n }\\r\\n if (this.onConnectionsChange) {\\r\\n this.onConnectionsChange(\\r\\n LiteGraph.OUTPUT,\\r\\n slot,\\r\\n true,\\r\\n link_info,\\r\\n output\\r\\n );\\r\\n } //link_info has been created now, so its updated\\r\\n if (target_node.onConnectionsChange) {\\r\\n target_node.onConnectionsChange(\\r\\n LiteGraph.INPUT,\\r\\n target_slot,\\r\\n true,\\r\\n link_info,\\r\\n input\\r\\n );\\r\\n }\\r\\n if (this.graph && this.graph.onNodeConnectionChange) {\\r\\n this.graph.onNodeConnectionChange(\\r\\n LiteGraph.INPUT,\\r\\n target_node,\\r\\n target_slot,\\r\\n this,\\r\\n slot\\r\\n );\\r\\n this.graph.onNodeConnectionChange(\\r\\n LiteGraph.OUTPUT,\\r\\n this,\\r\\n slot,\\r\\n target_node,\\r\\n target_slot\\r\\n );\\r\\n }\\r\\n }\\r\\n\\r\\n this.setDirtyCanvas(false, true);\\r\\n this.graph.connectionChange(this, link_info);\\r\\n\\r\\n return link_info;\\r\\n };\\r\\n\\r\\n /**\\r\\n * disconnect one output to an specific node\\r\\n * @method disconnectOutput\\r\\n * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)\\r\\n * @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected]\\r\\n * @return {boolean} if it was disconnected successfully\\r\\n */\\r\\n LGraphNode.prototype.disconnectOutput = function(slot, target_node) {\\r\\n if (slot.constructor === String) {\\r\\n slot = this.findOutputSlot(slot);\\r\\n if (slot == -1) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Connect: Error, no slot of name \\\" + slot);\\r\\n }\\r\\n return false;\\r\\n }\\r\\n } else if (!this.outputs || slot >= this.outputs.length) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Connect: Error, slot number not found\\\");\\r\\n }\\r\\n return false;\\r\\n }\\r\\n\\r\\n //get output slot\\r\\n var output = this.outputs[slot];\\r\\n if (!output || !output.links || output.links.length == 0) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n //one of the output links in this slot\\r\\n if (target_node) {\\r\\n if (target_node.constructor === Number) {\\r\\n target_node = this.graph.getNodeById(target_node);\\r\\n }\\r\\n if (!target_node) {\\r\\n throw \\\"Target Node not found\\\";\\r\\n }\\r\\n\\r\\n for (var i = 0, l = output.links.length; i < l; i++) {\\r\\n var link_id = output.links[i];\\r\\n var link_info = this.graph.links[link_id];\\r\\n\\r\\n //is the link we are searching for...\\r\\n if (link_info.target_id == target_node.id) {\\r\\n output.links.splice(i, 1); //remove here\\r\\n var input = target_node.inputs[link_info.target_slot];\\r\\n input.link = null; //remove there\\r\\n delete this.graph.links[link_id]; //remove the link from the links pool\\r\\n if (this.graph) {\\r\\n this.graph._version++;\\r\\n }\\r\\n if (target_node.onConnectionsChange) {\\r\\n target_node.onConnectionsChange(\\r\\n LiteGraph.INPUT,\\r\\n link_info.target_slot,\\r\\n false,\\r\\n link_info,\\r\\n input\\r\\n );\\r\\n } //link_info hasn't been modified so its ok\\r\\n if (this.onConnectionsChange) {\\r\\n this.onConnectionsChange(\\r\\n LiteGraph.OUTPUT,\\r\\n slot,\\r\\n false,\\r\\n link_info,\\r\\n output\\r\\n );\\r\\n }\\r\\n if (this.graph && this.graph.onNodeConnectionChange) {\\r\\n this.graph.onNodeConnectionChange(\\r\\n LiteGraph.OUTPUT,\\r\\n this,\\r\\n slot\\r\\n );\\r\\n }\\r\\n if (this.graph && this.graph.onNodeConnectionChange) {\\r\\n this.graph.onNodeConnectionChange(\\r\\n LiteGraph.OUTPUT,\\r\\n this,\\r\\n slot\\r\\n );\\r\\n this.graph.onNodeConnectionChange(\\r\\n LiteGraph.INPUT,\\r\\n target_node,\\r\\n link_info.target_slot\\r\\n );\\r\\n }\\r\\n break;\\r\\n }\\r\\n }\\r\\n } //all the links in this output slot\\r\\n else {\\r\\n for (var i = 0, l = output.links.length; i < l; i++) {\\r\\n var link_id = output.links[i];\\r\\n var link_info = this.graph.links[link_id];\\r\\n if (!link_info) {\\r\\n //bug: it happens sometimes\\r\\n continue;\\r\\n }\\r\\n\\r\\n var target_node = this.graph.getNodeById(link_info.target_id);\\r\\n var input = null;\\r\\n if (this.graph) {\\r\\n this.graph._version++;\\r\\n }\\r\\n if (target_node) {\\r\\n input = target_node.inputs[link_info.target_slot];\\r\\n input.link = null; //remove other side link\\r\\n if (target_node.onConnectionsChange) {\\r\\n target_node.onConnectionsChange(\\r\\n LiteGraph.INPUT,\\r\\n link_info.target_slot,\\r\\n false,\\r\\n link_info,\\r\\n input\\r\\n );\\r\\n } //link_info hasn't been modified so its ok\\r\\n if (this.graph && this.graph.onNodeConnectionChange) {\\r\\n this.graph.onNodeConnectionChange(\\r\\n LiteGraph.INPUT,\\r\\n target_node,\\r\\n link_info.target_slot\\r\\n );\\r\\n }\\r\\n }\\r\\n delete this.graph.links[link_id]; //remove the link from the links pool\\r\\n if (this.onConnectionsChange) {\\r\\n this.onConnectionsChange(\\r\\n LiteGraph.OUTPUT,\\r\\n slot,\\r\\n false,\\r\\n link_info,\\r\\n output\\r\\n );\\r\\n }\\r\\n if (this.graph && this.graph.onNodeConnectionChange) {\\r\\n this.graph.onNodeConnectionChange(\\r\\n LiteGraph.OUTPUT,\\r\\n this,\\r\\n slot\\r\\n );\\r\\n this.graph.onNodeConnectionChange(\\r\\n LiteGraph.INPUT,\\r\\n target_node,\\r\\n link_info.target_slot\\r\\n );\\r\\n }\\r\\n }\\r\\n output.links = null;\\r\\n }\\r\\n\\r\\n this.setDirtyCanvas(false, true);\\r\\n this.graph.connectionChange(this);\\r\\n return true;\\r\\n };\\r\\n\\r\\n /**\\r\\n * disconnect one input\\r\\n * @method disconnectInput\\r\\n * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)\\r\\n * @return {boolean} if it was disconnected successfully\\r\\n */\\r\\n LGraphNode.prototype.disconnectInput = function(slot) {\\r\\n //seek for the output slot\\r\\n if (slot.constructor === String) {\\r\\n slot = this.findInputSlot(slot);\\r\\n if (slot == -1) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Connect: Error, no slot of name \\\" + slot);\\r\\n }\\r\\n return false;\\r\\n }\\r\\n } else if (!this.inputs || slot >= this.inputs.length) {\\r\\n if (LiteGraph.debug) {\\r\\n console.log(\\\"Connect: Error, slot number not found\\\");\\r\\n }\\r\\n return false;\\r\\n }\\r\\n\\r\\n var input = this.inputs[slot];\\r\\n if (!input) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n var link_id = this.inputs[slot].link;\\r\\n this.inputs[slot].link = null;\\r\\n\\r\\n //remove other side\\r\\n var link_info = this.graph.links[link_id];\\r\\n if (link_info) {\\r\\n var target_node = this.graph.getNodeById(link_info.origin_id);\\r\\n if (!target_node) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n var output = target_node.outputs[link_info.origin_slot];\\r\\n if (!output || !output.links || output.links.length == 0) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n //search in the inputs list for this link\\r\\n for (var i = 0, l = output.links.length; i < l; i++) {\\r\\n if (output.links[i] == link_id) {\\r\\n output.links.splice(i, 1);\\r\\n break;\\r\\n }\\r\\n }\\r\\n\\r\\n delete this.graph.links[link_id]; //remove from the pool\\r\\n if (this.graph) {\\r\\n this.graph._version++;\\r\\n }\\r\\n if (this.onConnectionsChange) {\\r\\n this.onConnectionsChange(\\r\\n LiteGraph.INPUT,\\r\\n slot,\\r\\n false,\\r\\n link_info,\\r\\n input\\r\\n );\\r\\n }\\r\\n if (target_node.onConnectionsChange) {\\r\\n target_node.onConnectionsChange(\\r\\n LiteGraph.OUTPUT,\\r\\n i,\\r\\n false,\\r\\n link_info,\\r\\n output\\r\\n );\\r\\n }\\r\\n if (this.graph && this.graph.onNodeConnectionChange) {\\r\\n this.graph.onNodeConnectionChange(\\r\\n LiteGraph.OUTPUT,\\r\\n target_node,\\r\\n i\\r\\n );\\r\\n this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot);\\r\\n }\\r\\n }\\r\\n\\r\\n this.setDirtyCanvas(false, true);\\r\\n this.graph.connectionChange(this);\\r\\n return true;\\r\\n };\\r\\n\\r\\n /**\\r\\n * returns the center of a connection point in canvas coords\\r\\n * @method getConnectionPos\\r\\n * @param {boolean} is_input true if if a input slot, false if it is an output\\r\\n * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)\\r\\n * @param {vec2} out [optional] a place to store the output, to free garbage\\r\\n * @return {[x,y]} the position\\r\\n **/\\r\\n LGraphNode.prototype.getConnectionPos = function(\\r\\n is_input,\\r\\n slot_number,\\r\\n out\\r\\n ) {\\r\\n out = out || new Float32Array(2);\\r\\n var num_slots = 0;\\r\\n if (is_input && this.inputs) {\\r\\n num_slots = this.inputs.length;\\r\\n }\\r\\n if (!is_input && this.outputs) {\\r\\n num_slots = this.outputs.length;\\r\\n }\\r\\n\\r\\n var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5;\\r\\n\\r\\n if (this.flags.collapsed) {\\r\\n var w = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH;\\r\\n if (this.horizontal) {\\r\\n out[0] = this.pos[0] + w * 0.5;\\r\\n if (is_input) {\\r\\n out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT;\\r\\n } else {\\r\\n out[1] = this.pos[1];\\r\\n }\\r\\n } else {\\r\\n if (is_input) {\\r\\n out[0] = this.pos[0];\\r\\n } else {\\r\\n out[0] = this.pos[0] + w;\\r\\n }\\r\\n out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5;\\r\\n }\\r\\n return out;\\r\\n }\\r\\n\\r\\n //weird feature that never got finished\\r\\n if (is_input && slot_number == -1) {\\r\\n out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5;\\r\\n out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5;\\r\\n return out;\\r\\n }\\r\\n\\r\\n //hard-coded pos\\r\\n if (\\r\\n is_input &&\\r\\n num_slots > slot_number &&\\r\\n this.inputs[slot_number].pos\\r\\n ) {\\r\\n out[0] = this.pos[0] + this.inputs[slot_number].pos[0];\\r\\n out[1] = this.pos[1] + this.inputs[slot_number].pos[1];\\r\\n return out;\\r\\n } else if (\\r\\n !is_input &&\\r\\n num_slots > slot_number &&\\r\\n this.outputs[slot_number].pos\\r\\n ) {\\r\\n out[0] = this.pos[0] + this.outputs[slot_number].pos[0];\\r\\n out[1] = this.pos[1] + this.outputs[slot_number].pos[1];\\r\\n return out;\\r\\n }\\r\\n\\r\\n //horizontal distributed slots\\r\\n if (this.horizontal) {\\r\\n out[0] =\\r\\n this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots);\\r\\n if (is_input) {\\r\\n out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT;\\r\\n } else {\\r\\n out[1] = this.pos[1] + this.size[1];\\r\\n }\\r\\n return out;\\r\\n }\\r\\n\\r\\n //default vertical slots\\r\\n if (is_input) {\\r\\n out[0] = this.pos[0] + offset;\\r\\n } else {\\r\\n out[0] = this.pos[0] + this.size[0] + 1 - offset;\\r\\n }\\r\\n out[1] =\\r\\n this.pos[1] +\\r\\n (slot_number + 0.7) * LiteGraph.NODE_SLOT_HEIGHT +\\r\\n (this.constructor.slot_start_y || 0);\\r\\n return out;\\r\\n };\\r\\n\\r\\n /* Force align to grid */\\r\\n LGraphNode.prototype.alignToGrid = function() {\\r\\n this.pos[0] =\\r\\n LiteGraph.CANVAS_GRID_SIZE *\\r\\n Math.round(this.pos[0] / LiteGraph.CANVAS_GRID_SIZE);\\r\\n this.pos[1] =\\r\\n LiteGraph.CANVAS_GRID_SIZE *\\r\\n Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE);\\r\\n };\\r\\n\\r\\n /* Console output */\\r\\n LGraphNode.prototype.trace = function(msg) {\\r\\n if (!this.console) {\\r\\n this.console = [];\\r\\n }\\r\\n this.console.push(msg);\\r\\n if (this.console.length > LGraphNode.MAX_CONSOLE) {\\r\\n this.console.shift();\\r\\n }\\r\\n\\r\\n this.graph.onNodeTrace(this, msg);\\r\\n };\\r\\n\\r\\n /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */\\r\\n LGraphNode.prototype.setDirtyCanvas = function(\\r\\n dirty_foreground,\\r\\n dirty_background\\r\\n ) {\\r\\n if (!this.graph) {\\r\\n return;\\r\\n }\\r\\n this.graph.sendActionToCanvas(\\\"setDirty\\\", [\\r\\n dirty_foreground,\\r\\n dirty_background\\r\\n ]);\\r\\n };\\r\\n\\r\\n LGraphNode.prototype.loadImage = function(url) {\\r\\n var img = new Image();\\r\\n img.src = LiteGraph.node_images_path + url;\\r\\n img.ready = false;\\r\\n\\r\\n var that = this;\\r\\n img.onload = function() {\\r\\n this.ready = true;\\r\\n that.setDirtyCanvas(true);\\r\\n };\\r\\n return img;\\r\\n };\\r\\n\\r\\n //safe LGraphNode action execution (not sure if safe)\\r\\n /*\\r\\nLGraphNode.prototype.executeAction = function(action)\\r\\n{\\r\\n\\tif(action == \\\"\\\") return false;\\r\\n\\r\\n\\tif( action.indexOf(\\\";\\\") != -1 || action.indexOf(\\\"}\\\") != -1)\\r\\n\\t{\\r\\n\\t\\tthis.trace(\\\"Error: Action contains unsafe characters\\\");\\r\\n\\t\\treturn false;\\r\\n\\t}\\r\\n\\r\\n\\tvar tokens = action.split(\\\"(\\\");\\r\\n\\tvar func_name = tokens[0];\\r\\n\\tif( typeof(this[func_name]) != \\\"function\\\")\\r\\n\\t{\\r\\n\\t\\tthis.trace(\\\"Error: Action not found on node: \\\" + func_name);\\r\\n\\t\\treturn false;\\r\\n\\t}\\r\\n\\r\\n\\tvar code = action;\\r\\n\\r\\n\\ttry\\r\\n\\t{\\r\\n\\t\\tvar _foo = eval;\\r\\n\\t\\teval = null;\\r\\n\\t\\t(new Function(\\\"with(this) { \\\" + code + \\\"}\\\")).call(this);\\r\\n\\t\\teval = _foo;\\r\\n\\t}\\r\\n\\tcatch (err)\\r\\n\\t{\\r\\n\\t\\tthis.trace(\\\"Error executing action {\\\" + action + \\\"} :\\\" + err);\\r\\n\\t\\treturn false;\\r\\n\\t}\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n*/\\r\\n\\r\\n /* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */\\r\\n LGraphNode.prototype.captureInput = function(v) {\\r\\n if (!this.graph || !this.graph.list_of_graphcanvas) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var list = this.graph.list_of_graphcanvas;\\r\\n\\r\\n for (var i = 0; i < list.length; ++i) {\\r\\n var c = list[i];\\r\\n //releasing somebody elses capture?!\\r\\n if (!v && c.node_capturing_input != this) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n //change\\r\\n c.node_capturing_input = v ? this : null;\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Collapse the node to make it smaller on the canvas\\r\\n * @method collapse\\r\\n **/\\r\\n LGraphNode.prototype.collapse = function(force) {\\r\\n this.graph._version++;\\r\\n if (this.constructor.collapsable === false && !force) {\\r\\n return;\\r\\n }\\r\\n if (!this.flags.collapsed) {\\r\\n this.flags.collapsed = true;\\r\\n } else {\\r\\n this.flags.collapsed = false;\\r\\n }\\r\\n this.setDirtyCanvas(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * Forces the node to do not move or realign on Z\\r\\n * @method pin\\r\\n **/\\r\\n\\r\\n LGraphNode.prototype.pin = function(v) {\\r\\n this.graph._version++;\\r\\n if (v === undefined) {\\r\\n this.flags.pinned = !this.flags.pinned;\\r\\n } else {\\r\\n this.flags.pinned = v;\\r\\n }\\r\\n };\\r\\n\\r\\n LGraphNode.prototype.localToScreen = function(x, y, graphcanvas) {\\r\\n return [\\r\\n (x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0],\\r\\n (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1]\\r\\n ];\\r\\n };\\r\\n\\r\\n function LGraphGroup(title) {\\r\\n this._ctor(title);\\r\\n }\\r\\n\\r\\n global.LGraphGroup = LiteGraph.LGraphGroup = LGraphGroup;\\r\\n\\r\\n LGraphGroup.prototype._ctor = function(title) {\\r\\n this.title = title || \\\"Group\\\";\\r\\n this.font_size = 24;\\r\\n this.color = LGraphCanvas.node_colors.pale_blue\\r\\n ? LGraphCanvas.node_colors.pale_blue.groupcolor\\r\\n : \\\"#AAA\\\";\\r\\n this._bounding = new Float32Array([10, 10, 140, 80]);\\r\\n this._pos = this._bounding.subarray(0, 2);\\r\\n this._size = this._bounding.subarray(2, 4);\\r\\n this._nodes = [];\\r\\n this.graph = null;\\r\\n\\r\\n Object.defineProperty(this, \\\"pos\\\", {\\r\\n set: function(v) {\\r\\n if (!v || v.length < 2) {\\r\\n return;\\r\\n }\\r\\n this._pos[0] = v[0];\\r\\n this._pos[1] = v[1];\\r\\n },\\r\\n get: function() {\\r\\n return this._pos;\\r\\n },\\r\\n enumerable: true\\r\\n });\\r\\n\\r\\n Object.defineProperty(this, \\\"size\\\", {\\r\\n set: function(v) {\\r\\n if (!v || v.length < 2) {\\r\\n return;\\r\\n }\\r\\n this._size[0] = Math.max(140, v[0]);\\r\\n this._size[1] = Math.max(80, v[1]);\\r\\n },\\r\\n get: function() {\\r\\n return this._size;\\r\\n },\\r\\n enumerable: true\\r\\n });\\r\\n };\\r\\n\\r\\n LGraphGroup.prototype.configure = function(o) {\\r\\n this.title = o.title;\\r\\n this._bounding.set(o.bounding);\\r\\n this.color = o.color;\\r\\n this.font = o.font;\\r\\n };\\r\\n\\r\\n LGraphGroup.prototype.serialize = function() {\\r\\n var b = this._bounding;\\r\\n return {\\r\\n title: this.title,\\r\\n bounding: [\\r\\n Math.round(b[0]),\\r\\n Math.round(b[1]),\\r\\n Math.round(b[2]),\\r\\n Math.round(b[3])\\r\\n ],\\r\\n color: this.color,\\r\\n font: this.font\\r\\n };\\r\\n };\\r\\n\\r\\n LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes) {\\r\\n this._pos[0] += deltax;\\r\\n this._pos[1] += deltay;\\r\\n if (ignore_nodes) {\\r\\n return;\\r\\n }\\r\\n for (var i = 0; i < this._nodes.length; ++i) {\\r\\n var node = this._nodes[i];\\r\\n node.pos[0] += deltax;\\r\\n node.pos[1] += deltay;\\r\\n }\\r\\n };\\r\\n\\r\\n LGraphGroup.prototype.recomputeInsideNodes = function() {\\r\\n this._nodes.length = 0;\\r\\n var nodes = this.graph._nodes;\\r\\n var node_bounding = new Float32Array(4);\\r\\n\\r\\n for (var i = 0; i < nodes.length; ++i) {\\r\\n var node = nodes[i];\\r\\n node.getBounding(node_bounding);\\r\\n if (!overlapBounding(this._bounding, node_bounding)) {\\r\\n continue;\\r\\n } //out of the visible area\\r\\n this._nodes.push(node);\\r\\n }\\r\\n };\\r\\n\\r\\n LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside;\\r\\n LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas;\\r\\n\\r\\n //****************************************\\r\\n\\r\\n //Scale and Offset\\r\\n function DragAndScale(element, skip_events) {\\r\\n this.offset = new Float32Array([0, 0]);\\r\\n this.scale = 1;\\r\\n this.max_scale = 10;\\r\\n this.min_scale = 0.1;\\r\\n this.onredraw = null;\\r\\n this.enabled = true;\\r\\n this.last_mouse = [0, 0];\\r\\n this.element = null;\\r\\n this.visible_area = new Float32Array(4);\\r\\n\\r\\n if (element) {\\r\\n this.element = element;\\r\\n if (!skip_events) {\\r\\n this.bindEvents(element);\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n LiteGraph.DragAndScale = DragAndScale;\\r\\n\\r\\n DragAndScale.prototype.bindEvents = function(element) {\\r\\n this.last_mouse = new Float32Array(2);\\r\\n\\r\\n this._binded_mouse_callback = this.onMouse.bind(this);\\r\\n\\r\\n element.addEventListener(\\\"mousedown\\\", this._binded_mouse_callback);\\r\\n element.addEventListener(\\\"mousemove\\\", this._binded_mouse_callback);\\r\\n\\r\\n element.addEventListener(\\r\\n \\\"mousewheel\\\",\\r\\n this._binded_mouse_callback,\\r\\n false\\r\\n );\\r\\n element.addEventListener(\\\"wheel\\\", this._binded_mouse_callback, false);\\r\\n };\\r\\n\\r\\n DragAndScale.prototype.computeVisibleArea = function() {\\r\\n if (!this.element) {\\r\\n this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0;\\r\\n return;\\r\\n }\\r\\n var width = this.element.width;\\r\\n var height = this.element.height;\\r\\n var startx = -this.offset[0];\\r\\n var starty = -this.offset[1];\\r\\n var endx = startx + width / this.scale;\\r\\n var endy = starty + height / this.scale;\\r\\n this.visible_area[0] = startx;\\r\\n this.visible_area[1] = starty;\\r\\n this.visible_area[2] = endx - startx;\\r\\n this.visible_area[3] = endy - starty;\\r\\n };\\r\\n\\r\\n DragAndScale.prototype.onMouse = function(e) {\\r\\n if (!this.enabled) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var canvas = this.element;\\r\\n var rect = canvas.getBoundingClientRect();\\r\\n var x = e.clientX - rect.left;\\r\\n var y = e.clientY - rect.top;\\r\\n e.canvasx = x;\\r\\n e.canvasy = y;\\r\\n e.dragging = this.dragging;\\r\\n\\r\\n var ignore = false;\\r\\n if (this.onmouse) {\\r\\n ignore = this.onmouse(e);\\r\\n }\\r\\n\\r\\n if (e.type == \\\"mousedown\\\") {\\r\\n this.dragging = true;\\r\\n canvas.removeEventListener(\\r\\n \\\"mousemove\\\",\\r\\n this._binded_mouse_callback\\r\\n );\\r\\n document.body.addEventListener(\\r\\n \\\"mousemove\\\",\\r\\n this._binded_mouse_callback\\r\\n );\\r\\n document.body.addEventListener(\\r\\n \\\"mouseup\\\",\\r\\n this._binded_mouse_callback\\r\\n );\\r\\n } else if (e.type == \\\"mousemove\\\") {\\r\\n if (!ignore) {\\r\\n var deltax = x - this.last_mouse[0];\\r\\n var deltay = y - this.last_mouse[1];\\r\\n if (this.dragging) {\\r\\n this.mouseDrag(deltax, deltay);\\r\\n }\\r\\n }\\r\\n } else if (e.type == \\\"mouseup\\\") {\\r\\n this.dragging = false;\\r\\n document.body.removeEventListener(\\r\\n \\\"mousemove\\\",\\r\\n this._binded_mouse_callback\\r\\n );\\r\\n document.body.removeEventListener(\\r\\n \\\"mouseup\\\",\\r\\n this._binded_mouse_callback\\r\\n );\\r\\n canvas.addEventListener(\\\"mousemove\\\", this._binded_mouse_callback);\\r\\n } else if (\\r\\n e.type == \\\"mousewheel\\\" ||\\r\\n e.type == \\\"wheel\\\" ||\\r\\n e.type == \\\"DOMMouseScroll\\\"\\r\\n ) {\\r\\n e.eventType = \\\"mousewheel\\\";\\r\\n if (e.type == \\\"wheel\\\") {\\r\\n e.wheel = -e.deltaY;\\r\\n } else {\\r\\n e.wheel =\\r\\n e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60;\\r\\n }\\r\\n\\r\\n //from stack overflow\\r\\n e.delta = e.wheelDelta\\r\\n ? e.wheelDelta / 40\\r\\n : e.deltaY\\r\\n ? -e.deltaY / 3\\r\\n : 0;\\r\\n this.changeDeltaScale(1.0 + e.delta * 0.05);\\r\\n }\\r\\n\\r\\n this.last_mouse[0] = x;\\r\\n this.last_mouse[1] = y;\\r\\n\\r\\n e.preventDefault();\\r\\n e.stopPropagation();\\r\\n return false;\\r\\n };\\r\\n\\r\\n DragAndScale.prototype.toCanvasContext = function(ctx) {\\r\\n ctx.scale(this.scale, this.scale);\\r\\n ctx.translate(this.offset[0], this.offset[1]);\\r\\n };\\r\\n\\r\\n DragAndScale.prototype.convertOffsetToCanvas = function(pos) {\\r\\n //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]];\\r\\n return [\\r\\n (pos[0] + this.offset[0]) * this.scale,\\r\\n (pos[1] + this.offset[1]) * this.scale\\r\\n ];\\r\\n };\\r\\n\\r\\n DragAndScale.prototype.convertCanvasToOffset = function(pos, out) {\\r\\n out = out || [0, 0];\\r\\n out[0] = pos[0] / this.scale - this.offset[0];\\r\\n out[1] = pos[1] / this.scale - this.offset[1];\\r\\n return out;\\r\\n };\\r\\n\\r\\n DragAndScale.prototype.mouseDrag = function(x, y) {\\r\\n this.offset[0] += x / this.scale;\\r\\n this.offset[1] += y / this.scale;\\r\\n\\r\\n if (this.onredraw) {\\r\\n this.onredraw(this);\\r\\n }\\r\\n };\\r\\n\\r\\n DragAndScale.prototype.changeScale = function(value, zooming_center) {\\r\\n if (value < this.min_scale) {\\r\\n value = this.min_scale;\\r\\n } else if (value > this.max_scale) {\\r\\n value = this.max_scale;\\r\\n }\\r\\n\\r\\n if (value == this.scale) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!this.element) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var rect = this.element.getBoundingClientRect();\\r\\n if (!rect) {\\r\\n return;\\r\\n }\\r\\n\\r\\n zooming_center = zooming_center || [\\r\\n rect.width * 0.5,\\r\\n rect.height * 0.5\\r\\n ];\\r\\n var center = this.convertCanvasToOffset(zooming_center);\\r\\n this.scale = value;\\r\\n if (Math.abs(this.scale - 1) < 0.01) {\\r\\n this.scale = 1;\\r\\n }\\r\\n\\r\\n var new_center = this.convertCanvasToOffset(zooming_center);\\r\\n var delta_offset = [\\r\\n new_center[0] - center[0],\\r\\n new_center[1] - center[1]\\r\\n ];\\r\\n\\r\\n this.offset[0] += delta_offset[0];\\r\\n this.offset[1] += delta_offset[1];\\r\\n\\r\\n if (this.onredraw) {\\r\\n this.onredraw(this);\\r\\n }\\r\\n };\\r\\n\\r\\n DragAndScale.prototype.changeDeltaScale = function(value, zooming_center) {\\r\\n this.changeScale(this.scale * value, zooming_center);\\r\\n };\\r\\n\\r\\n DragAndScale.prototype.reset = function() {\\r\\n this.scale = 1;\\r\\n this.offset[0] = 0;\\r\\n this.offset[1] = 0;\\r\\n };\\r\\n\\r\\n //*********************************************************************************\\r\\n // LGraphCanvas: LGraph renderer CLASS\\r\\n //*********************************************************************************\\r\\n\\r\\n /**\\r\\n * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required.\\r\\n * Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked\\r\\n *\\r\\n * @class LGraphCanvas\\r\\n * @constructor\\r\\n * @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format or the canvas element itself)\\r\\n * @param {LGraph} graph [optional]\\r\\n * @param {Object} options [optional] { skip_rendering, autoresize }\\r\\n */\\r\\n function LGraphCanvas(canvas, graph, options) {\\r\\n options = options || {};\\r\\n\\r\\n //if(graph === undefined)\\r\\n //\\tthrow (\\\"No graph assigned\\\");\\r\\n this.background_image =\\r\\n \\\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII=\\\";\\r\\n\\r\\n if (canvas && canvas.constructor === String) {\\r\\n canvas = document.querySelector(canvas);\\r\\n }\\r\\n\\r\\n this.ds = new DragAndScale();\\r\\n this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much\\r\\n\\r\\n this.title_text_font = \\\"\\\" + LiteGraph.NODE_TEXT_SIZE + \\\"px Arial\\\";\\r\\n this.inner_text_font =\\r\\n \\\"normal \\\" + LiteGraph.NODE_SUBTEXT_SIZE + \\\"px Arial\\\";\\r\\n this.node_title_color = LiteGraph.NODE_TITLE_COLOR;\\r\\n this.default_link_color = LiteGraph.LINK_COLOR;\\r\\n this.default_connection_color = {\\r\\n input_off: \\\"#778\\\",\\r\\n input_on: \\\"#7F7\\\",\\r\\n output_off: \\\"#778\\\",\\r\\n output_on: \\\"#7F7\\\"\\r\\n };\\r\\n\\r\\n this.highquality_render = true;\\r\\n this.use_gradients = false; //set to true to render titlebar with gradients\\r\\n this.editor_alpha = 1; //used for transition\\r\\n this.pause_rendering = false;\\r\\n this.clear_background = true;\\r\\n\\r\\n\\t\\tthis.read_only = false; //if set to true users cannot modify the graph\\r\\n this.render_only_selected = true;\\r\\n this.live_mode = false;\\r\\n this.show_info = true;\\r\\n this.allow_dragcanvas = true;\\r\\n this.allow_dragnodes = true;\\r\\n this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc\\r\\n this.allow_searchbox = true;\\r\\n this.allow_reconnect_links = false; //allows to change a connection with having to redo it again\\r\\n\\r\\n this.drag_mode = false;\\r\\n this.dragging_rectangle = null;\\r\\n\\r\\n this.filter = null; //allows to filter to only accept some type of nodes in a graph\\r\\n\\r\\n this.always_render_background = false;\\r\\n this.render_shadows = true;\\r\\n this.render_canvas_border = true;\\r\\n this.render_connections_shadows = false; //too much cpu\\r\\n this.render_connections_border = true;\\r\\n this.render_curved_connections = false;\\r\\n this.render_connection_arrows = false;\\r\\n this.render_collapsed_slots = true;\\r\\n this.render_execution_order = false;\\r\\n this.render_title_colored = true;\\r\\n\\t\\tthis.render_link_tooltip = true;\\r\\n\\r\\n this.links_render_mode = LiteGraph.SPLINE_LINK;\\r\\n\\r\\n this.canvas_mouse = [0, 0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle\\r\\n\\r\\n //to personalize the search box\\r\\n this.onSearchBox = null;\\r\\n this.onSearchBoxSelection = null;\\r\\n\\r\\n //callbacks\\r\\n this.onMouse = null;\\r\\n this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform\\r\\n this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform\\r\\n this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs)\\r\\n\\t\\tthis.onDrawLinkTooltip = null; //called when rendering a tooltip\\r\\n\\r\\n this.connections_width = 3;\\r\\n this.round_radius = 8;\\r\\n\\r\\n this.current_node = null;\\r\\n this.node_widget = null; //used for widgets\\r\\n\\t\\tthis.over_link_center = null;\\r\\n this.last_mouse_position = [0, 0];\\r\\n this.visible_area = this.ds.visible_area;\\r\\n this.visible_links = [];\\r\\n\\r\\n //link canvas and graph\\r\\n if (graph) {\\r\\n graph.attachCanvas(this);\\r\\n }\\r\\n\\r\\n this.setCanvas(canvas);\\r\\n this.clear();\\r\\n\\r\\n if (!options.skip_render) {\\r\\n this.startRendering();\\r\\n }\\r\\n\\r\\n this.autoresize = options.autoresize;\\r\\n }\\r\\n\\r\\n global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas;\\r\\n\\r\\n LGraphCanvas.link_type_colors = {\\r\\n \\\"-1\\\": LiteGraph.EVENT_LINK_COLOR,\\r\\n number: \\\"#AAA\\\",\\r\\n node: \\\"#DCA\\\"\\r\\n };\\r\\n LGraphCanvas.gradients = {}; //cache of gradients\\r\\n\\r\\n /**\\r\\n * clears all the data inside\\r\\n *\\r\\n * @method clear\\r\\n */\\r\\n LGraphCanvas.prototype.clear = function() {\\r\\n this.frame = 0;\\r\\n this.last_draw_time = 0;\\r\\n this.render_time = 0;\\r\\n this.fps = 0;\\r\\n\\r\\n //this.scale = 1;\\r\\n //this.offset = [0,0];\\r\\n\\r\\n this.dragging_rectangle = null;\\r\\n\\r\\n this.selected_nodes = {};\\r\\n this.selected_group = null;\\r\\n\\r\\n this.visible_nodes = [];\\r\\n this.node_dragged = null;\\r\\n this.node_over = null;\\r\\n this.node_capturing_input = null;\\r\\n this.connecting_node = null;\\r\\n this.highlighted_links = {};\\r\\n\\r\\n this.dirty_canvas = true;\\r\\n this.dirty_bgcanvas = true;\\r\\n this.dirty_area = null;\\r\\n\\r\\n this.node_in_panel = null;\\r\\n this.node_widget = null;\\r\\n\\r\\n this.last_mouse = [0, 0];\\r\\n this.last_mouseclick = 0;\\r\\n this.visible_area.set([0, 0, 0, 0]);\\r\\n\\r\\n if (this.onClear) {\\r\\n this.onClear();\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * assigns a graph, you can reassign graphs to the same canvas\\r\\n *\\r\\n * @method setGraph\\r\\n * @param {LGraph} graph\\r\\n */\\r\\n LGraphCanvas.prototype.setGraph = function(graph, skip_clear) {\\r\\n if (this.graph == graph) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!skip_clear) {\\r\\n this.clear();\\r\\n }\\r\\n\\r\\n if (!graph && this.graph) {\\r\\n this.graph.detachCanvas(this);\\r\\n return;\\r\\n }\\r\\n\\r\\n /*\\r\\n\\tif(this.graph)\\r\\n\\t\\tthis.graph.canvas = null; //remove old graph link to the canvas\\r\\n\\tthis.graph = graph;\\r\\n\\tif(this.graph)\\r\\n\\t\\tthis.graph.canvas = this;\\r\\n\\t*/\\r\\n graph.attachCanvas(this);\\r\\n this.setDirty(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * opens a graph contained inside a node in the current graph\\r\\n *\\r\\n * @method openSubgraph\\r\\n * @param {LGraph} graph\\r\\n */\\r\\n LGraphCanvas.prototype.openSubgraph = function(graph) {\\r\\n if (!graph) {\\r\\n throw \\\"graph cannot be null\\\";\\r\\n }\\r\\n\\r\\n if (this.graph == graph) {\\r\\n throw \\\"graph cannot be the same\\\";\\r\\n }\\r\\n\\r\\n this.clear();\\r\\n\\r\\n if (this.graph) {\\r\\n if (!this._graph_stack) {\\r\\n this._graph_stack = [];\\r\\n }\\r\\n this._graph_stack.push(this.graph);\\r\\n }\\r\\n\\r\\n graph.attachCanvas(this);\\r\\n this.setDirty(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * closes a subgraph contained inside a node\\r\\n *\\r\\n * @method closeSubgraph\\r\\n * @param {LGraph} assigns a graph\\r\\n */\\r\\n LGraphCanvas.prototype.closeSubgraph = function() {\\r\\n if (!this._graph_stack || this._graph_stack.length == 0) {\\r\\n return;\\r\\n }\\r\\n var subgraph_node = this.graph._subgraph_node;\\r\\n var graph = this._graph_stack.pop();\\r\\n this.selected_nodes = {};\\r\\n this.highlighted_links = {};\\r\\n graph.attachCanvas(this);\\r\\n this.setDirty(true, true);\\r\\n if (subgraph_node) {\\r\\n this.centerOnNode(subgraph_node);\\r\\n this.selectNodes([subgraph_node]);\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * returns the visualy active graph (in case there are more in the stack)\\r\\n * @method getCurrentGraph\\r\\n * @return {LGraph} the active graph\\r\\n */\\r\\n LGraphCanvas.prototype.getCurrentGraph = function() {\\r\\n return this.graph;\\r\\n };\\r\\n\\r\\n /**\\r\\n * assigns a canvas\\r\\n *\\r\\n * @method setCanvas\\r\\n * @param {Canvas} assigns a canvas (also accepts the ID of the element (not a selector)\\r\\n */\\r\\n LGraphCanvas.prototype.setCanvas = function(canvas, skip_events) {\\r\\n var that = this;\\r\\n\\r\\n if (canvas) {\\r\\n if (canvas.constructor === String) {\\r\\n canvas = document.getElementById(canvas);\\r\\n if (!canvas) {\\r\\n throw \\\"Error creating LiteGraph canvas: Canvas not found\\\";\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (canvas === this.canvas) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!canvas && this.canvas) {\\r\\n //maybe detach events from old_canvas\\r\\n if (!skip_events) {\\r\\n this.unbindEvents();\\r\\n }\\r\\n }\\r\\n\\r\\n this.canvas = canvas;\\r\\n this.ds.element = canvas;\\r\\n\\r\\n if (!canvas) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //this.canvas.tabindex = \\\"1000\\\";\\r\\n canvas.className += \\\" lgraphcanvas\\\";\\r\\n canvas.data = this;\\r\\n canvas.tabindex = \\\"1\\\"; //to allow key events\\r\\n\\r\\n //bg canvas: used for non changing stuff\\r\\n this.bgcanvas = null;\\r\\n if (!this.bgcanvas) {\\r\\n this.bgcanvas = document.createElement(\\\"canvas\\\");\\r\\n this.bgcanvas.width = this.canvas.width;\\r\\n this.bgcanvas.height = this.canvas.height;\\r\\n }\\r\\n\\r\\n if (canvas.getContext == null) {\\r\\n if (canvas.localName != \\\"canvas\\\") {\\r\\n throw \\\"Element supplied for LGraphCanvas must be a element, you passed a \\\" +\\r\\n canvas.localName;\\r\\n }\\r\\n throw \\\"This browser doesn't support Canvas\\\";\\r\\n }\\r\\n\\r\\n var ctx = (this.ctx = canvas.getContext(\\\"2d\\\"));\\r\\n if (ctx == null) {\\r\\n if (!canvas.webgl_enabled) {\\r\\n console.warn(\\r\\n \\\"This canvas seems to be WebGL, enabling WebGL renderer\\\"\\r\\n );\\r\\n }\\r\\n this.enableWebGL();\\r\\n }\\r\\n\\r\\n //input: (move and up could be unbinded)\\r\\n this._mousemove_callback = this.processMouseMove.bind(this);\\r\\n this._mouseup_callback = this.processMouseUp.bind(this);\\r\\n\\r\\n if (!skip_events) {\\r\\n this.bindEvents();\\r\\n }\\r\\n };\\r\\n\\r\\n //used in some events to capture them\\r\\n LGraphCanvas.prototype._doNothing = function doNothing(e) {\\r\\n e.preventDefault();\\r\\n return false;\\r\\n };\\r\\n LGraphCanvas.prototype._doReturnTrue = function doNothing(e) {\\r\\n e.preventDefault();\\r\\n return true;\\r\\n };\\r\\n\\r\\n /**\\r\\n * binds mouse, keyboard, touch and drag events to the canvas\\r\\n * @method bindEvents\\r\\n **/\\r\\n LGraphCanvas.prototype.bindEvents = function() {\\r\\n if (this._events_binded) {\\r\\n console.warn(\\\"LGraphCanvas: events already binded\\\");\\r\\n return;\\r\\n }\\r\\n\\r\\n var canvas = this.canvas;\\r\\n var ref_window = this.getCanvasWindow();\\r\\n var document = ref_window.document; //hack used when moving canvas between windows\\r\\n\\r\\n this._mousedown_callback = this.processMouseDown.bind(this);\\r\\n this._mousewheel_callback = this.processMouseWheel.bind(this);\\r\\n\\r\\n canvas.addEventListener(\\\"mousedown\\\", this._mousedown_callback, true); //down do not need to store the binded\\r\\n canvas.addEventListener(\\\"mousemove\\\", this._mousemove_callback);\\r\\n canvas.addEventListener(\\\"mousewheel\\\", this._mousewheel_callback, false);\\r\\n\\r\\n canvas.addEventListener(\\\"contextmenu\\\", this._doNothing);\\r\\n canvas.addEventListener(\\r\\n \\\"DOMMouseScroll\\\",\\r\\n this._mousewheel_callback,\\r\\n false\\r\\n );\\r\\n\\r\\n //touch events\\r\\n //if( 'touchstart' in document.documentElement )\\r\\n {\\r\\n canvas.addEventListener(\\\"touchstart\\\", this.touchHandler, true);\\r\\n canvas.addEventListener(\\\"touchmove\\\", this.touchHandler, true);\\r\\n canvas.addEventListener(\\\"touchend\\\", this.touchHandler, true);\\r\\n canvas.addEventListener(\\\"touchcancel\\\", this.touchHandler, true);\\r\\n }\\r\\n\\r\\n //Keyboard ******************\\r\\n this._key_callback = this.processKey.bind(this);\\r\\n\\r\\n canvas.addEventListener(\\\"keydown\\\", this._key_callback, true);\\r\\n document.addEventListener(\\\"keyup\\\", this._key_callback, true); //in document, otherwise it doesn't fire keyup\\r\\n\\r\\n //Dropping Stuff over nodes ************************************\\r\\n this._ondrop_callback = this.processDrop.bind(this);\\r\\n\\r\\n canvas.addEventListener(\\\"dragover\\\", this._doNothing, false);\\r\\n canvas.addEventListener(\\\"dragend\\\", this._doNothing, false);\\r\\n canvas.addEventListener(\\\"drop\\\", this._ondrop_callback, false);\\r\\n canvas.addEventListener(\\\"dragenter\\\", this._doReturnTrue, false);\\r\\n\\r\\n this._events_binded = true;\\r\\n };\\r\\n\\r\\n /**\\r\\n * unbinds mouse events from the canvas\\r\\n * @method unbindEvents\\r\\n **/\\r\\n LGraphCanvas.prototype.unbindEvents = function() {\\r\\n if (!this._events_binded) {\\r\\n console.warn(\\\"LGraphCanvas: no events binded\\\");\\r\\n return;\\r\\n }\\r\\n\\r\\n var ref_window = this.getCanvasWindow();\\r\\n var document = ref_window.document;\\r\\n\\r\\n this.canvas.removeEventListener(\\\"mousedown\\\", this._mousedown_callback);\\r\\n this.canvas.removeEventListener(\\r\\n \\\"mousewheel\\\",\\r\\n this._mousewheel_callback\\r\\n );\\r\\n this.canvas.removeEventListener(\\r\\n \\\"DOMMouseScroll\\\",\\r\\n this._mousewheel_callback\\r\\n );\\r\\n this.canvas.removeEventListener(\\\"keydown\\\", this._key_callback);\\r\\n document.removeEventListener(\\\"keyup\\\", this._key_callback);\\r\\n this.canvas.removeEventListener(\\\"contextmenu\\\", this._doNothing);\\r\\n this.canvas.removeEventListener(\\\"drop\\\", this._ondrop_callback);\\r\\n this.canvas.removeEventListener(\\\"dragenter\\\", this._doReturnTrue);\\r\\n\\r\\n this.canvas.removeEventListener(\\\"touchstart\\\", this.touchHandler);\\r\\n this.canvas.removeEventListener(\\\"touchmove\\\", this.touchHandler);\\r\\n this.canvas.removeEventListener(\\\"touchend\\\", this.touchHandler);\\r\\n this.canvas.removeEventListener(\\\"touchcancel\\\", this.touchHandler);\\r\\n\\r\\n this._mousedown_callback = null;\\r\\n this._mousewheel_callback = null;\\r\\n this._key_callback = null;\\r\\n this._ondrop_callback = null;\\r\\n\\r\\n this._events_binded = false;\\r\\n };\\r\\n\\r\\n LGraphCanvas.getFileExtension = function(url) {\\r\\n var question = url.indexOf(\\\"?\\\");\\r\\n if (question != -1) {\\r\\n url = url.substr(0, question);\\r\\n }\\r\\n var point = url.lastIndexOf(\\\".\\\");\\r\\n if (point == -1) {\\r\\n return \\\"\\\";\\r\\n }\\r\\n return url.substr(point + 1).toLowerCase();\\r\\n };\\r\\n\\r\\n /**\\r\\n * this function allows to render the canvas using WebGL instead of Canvas2D\\r\\n * this is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL\\r\\n * @method enableWebGL\\r\\n **/\\r\\n LGraphCanvas.prototype.enableWebGL = function() {\\r\\n if (typeof GL === undefined) {\\r\\n throw \\\"litegl.js must be included to use a WebGL canvas\\\";\\r\\n }\\r\\n if (typeof enableWebGLCanvas === undefined) {\\r\\n throw \\\"webglCanvas.js must be included to use this feature\\\";\\r\\n }\\r\\n\\r\\n this.gl = this.ctx = enableWebGLCanvas(this.canvas);\\r\\n this.ctx.webgl = true;\\r\\n this.bgcanvas = this.canvas;\\r\\n this.bgctx = this.gl;\\r\\n this.canvas.webgl_enabled = true;\\r\\n\\r\\n /*\\r\\n\\tGL.create({ canvas: this.bgcanvas });\\r\\n\\tthis.bgctx = enableWebGLCanvas( this.bgcanvas );\\r\\n\\twindow.gl = this.gl;\\r\\n\\t*/\\r\\n };\\r\\n\\r\\n /**\\r\\n * marks as dirty the canvas, this way it will be rendered again\\r\\n *\\r\\n * @class LGraphCanvas\\r\\n * @method setDirty\\r\\n * @param {bool} fgcanvas if the foreground canvas is dirty (the one containing the nodes)\\r\\n * @param {bool} bgcanvas if the background canvas is dirty (the one containing the wires)\\r\\n */\\r\\n LGraphCanvas.prototype.setDirty = function(fgcanvas, bgcanvas) {\\r\\n if (fgcanvas) {\\r\\n this.dirty_canvas = true;\\r\\n }\\r\\n if (bgcanvas) {\\r\\n this.dirty_bgcanvas = true;\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * Used to attach the canvas in a popup\\r\\n *\\r\\n * @method getCanvasWindow\\r\\n * @return {window} returns the window where the canvas is attached (the DOM root node)\\r\\n */\\r\\n LGraphCanvas.prototype.getCanvasWindow = function() {\\r\\n if (!this.canvas) {\\r\\n return window;\\r\\n }\\r\\n var doc = this.canvas.ownerDocument;\\r\\n return doc.defaultView || doc.parentWindow;\\r\\n };\\r\\n\\r\\n /**\\r\\n * starts rendering the content of the canvas when needed\\r\\n *\\r\\n * @method startRendering\\r\\n */\\r\\n LGraphCanvas.prototype.startRendering = function() {\\r\\n if (this.is_rendering) {\\r\\n return;\\r\\n } //already rendering\\r\\n\\r\\n this.is_rendering = true;\\r\\n renderFrame.call(this);\\r\\n\\r\\n function renderFrame() {\\r\\n if (!this.pause_rendering) {\\r\\n this.draw();\\r\\n }\\r\\n\\r\\n var window = this.getCanvasWindow();\\r\\n if (this.is_rendering) {\\r\\n window.requestAnimationFrame(renderFrame.bind(this));\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * stops rendering the content of the canvas (to save resources)\\r\\n *\\r\\n * @method stopRendering\\r\\n */\\r\\n LGraphCanvas.prototype.stopRendering = function() {\\r\\n this.is_rendering = false;\\r\\n /*\\r\\n\\tif(this.rendering_timer_id)\\r\\n\\t{\\r\\n\\t\\tclearInterval(this.rendering_timer_id);\\r\\n\\t\\tthis.rendering_timer_id = null;\\r\\n\\t}\\r\\n\\t*/\\r\\n };\\r\\n\\r\\n /* LiteGraphCanvas input */\\r\\n\\r\\n LGraphCanvas.prototype.processMouseDown = function(e) {\\r\\n if (!this.graph) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.adjustMouseEvent(e);\\r\\n\\r\\n var ref_window = this.getCanvasWindow();\\r\\n var document = ref_window.document;\\r\\n LGraphCanvas.active_canvas = this;\\r\\n var that = this;\\r\\n\\r\\n //move mouse move event to the window in case it drags outside of the canvas\\r\\n this.canvas.removeEventListener(\\\"mousemove\\\", this._mousemove_callback);\\r\\n ref_window.document.addEventListener(\\r\\n \\\"mousemove\\\",\\r\\n this._mousemove_callback,\\r\\n true\\r\\n ); //catch for the entire window\\r\\n ref_window.document.addEventListener(\\r\\n \\\"mouseup\\\",\\r\\n this._mouseup_callback,\\r\\n true\\r\\n );\\r\\n\\r\\n var node = this.graph.getNodeOnPos(\\r\\n e.canvasX,\\r\\n e.canvasY,\\r\\n this.visible_nodes,\\r\\n 5\\r\\n );\\r\\n var skip_dragging = false;\\r\\n var skip_action = false;\\r\\n var now = LiteGraph.getTime();\\r\\n var is_double_click = now - this.last_mouseclick < 300;\\r\\n\\r\\n this.canvas_mouse[0] = e.canvasX;\\r\\n this.canvas_mouse[1] = e.canvasY;\\r\\n this.canvas.focus();\\r\\n\\r\\n LiteGraph.closeAllContextMenus(ref_window);\\r\\n\\r\\n if (this.onMouse) {\\r\\n if (this.onMouse(e) == true) {\\r\\n return;\\r\\n }\\r\\n }\\r\\n\\r\\n if (e.which == 1) {\\r\\n //left button mouse\\r\\n if (e.ctrlKey) {\\r\\n this.dragging_rectangle = new Float32Array(4);\\r\\n this.dragging_rectangle[0] = e.canvasX;\\r\\n this.dragging_rectangle[1] = e.canvasY;\\r\\n this.dragging_rectangle[2] = 1;\\r\\n this.dragging_rectangle[3] = 1;\\r\\n skip_action = true;\\r\\n }\\r\\n\\r\\n var clicking_canvas_bg = false;\\r\\n\\r\\n //when clicked on top of a node\\r\\n //and it is not interactive\\r\\n if (node && this.allow_interaction && !skip_action && !this.read_only) {\\r\\n if (!this.live_mode && !node.flags.pinned) {\\r\\n this.bringToFront(node);\\r\\n } //if it wasn't selected?\\r\\n\\r\\n //not dragging mouse to connect two slots\\r\\n if (\\r\\n !this.connecting_node &&\\r\\n !node.flags.collapsed &&\\r\\n !this.live_mode\\r\\n ) {\\r\\n //Search for corner for resize\\r\\n if (\\r\\n !skip_action &&\\r\\n node.resizable !== false &&\\r\\n isInsideRectangle(\\r\\n e.canvasX,\\r\\n e.canvasY,\\r\\n node.pos[0] + node.size[0] - 5,\\r\\n node.pos[1] + node.size[1] - 5,\\r\\n 10,\\r\\n 10\\r\\n )\\r\\n ) {\\r\\n this.resizing_node = node;\\r\\n this.canvas.style.cursor = \\\"se-resize\\\";\\r\\n skip_action = true;\\r\\n } else {\\r\\n //search for outputs\\r\\n if (node.outputs) {\\r\\n for (\\r\\n var i = 0, l = node.outputs.length;\\r\\n i < l;\\r\\n ++i\\r\\n ) {\\r\\n var output = node.outputs[i];\\r\\n var link_pos = node.getConnectionPos(false, i);\\r\\n if (\\r\\n isInsideRectangle(\\r\\n e.canvasX,\\r\\n e.canvasY,\\r\\n link_pos[0] - 15,\\r\\n link_pos[1] - 10,\\r\\n 30,\\r\\n 20\\r\\n )\\r\\n ) {\\r\\n this.connecting_node = node;\\r\\n this.connecting_output = output;\\r\\n this.connecting_pos = node.getConnectionPos( false, i );\\r\\n this.connecting_slot = i;\\r\\n\\r\\n if (e.shiftKey) {\\r\\n node.disconnectOutput(i);\\r\\n }\\r\\n\\r\\n if (is_double_click) {\\r\\n if (node.onOutputDblClick) {\\r\\n node.onOutputDblClick(i, e);\\r\\n }\\r\\n } else {\\r\\n if (node.onOutputClick) {\\r\\n node.onOutputClick(i, e);\\r\\n }\\r\\n }\\r\\n\\r\\n skip_action = true;\\r\\n break;\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n //search for inputs\\r\\n if (node.inputs) {\\r\\n for (\\r\\n var i = 0, l = node.inputs.length;\\r\\n i < l;\\r\\n ++i\\r\\n ) {\\r\\n var input = node.inputs[i];\\r\\n var link_pos = node.getConnectionPos(true, i);\\r\\n if (\\r\\n isInsideRectangle(\\r\\n e.canvasX,\\r\\n e.canvasY,\\r\\n link_pos[0] - 15,\\r\\n link_pos[1] - 10,\\r\\n 30,\\r\\n 20\\r\\n )\\r\\n ) {\\r\\n if (is_double_click) {\\r\\n if (node.onInputDblClick) {\\r\\n node.onInputDblClick(i, e);\\r\\n }\\r\\n } else {\\r\\n if (node.onInputClick) {\\r\\n node.onInputClick(i, e);\\r\\n }\\r\\n }\\r\\n\\r\\n if (input.link !== null) {\\r\\n var link_info = this.graph.links[\\r\\n input.link\\r\\n ]; //before disconnecting\\r\\n node.disconnectInput(i);\\r\\n\\r\\n if (\\r\\n this.allow_reconnect_links ||\\r\\n e.shiftKey\\r\\n ) {\\r\\n this.connecting_node = this.graph._nodes_by_id[\\r\\n link_info.origin_id\\r\\n ];\\r\\n this.connecting_slot =\\r\\n link_info.origin_slot;\\r\\n this.connecting_output = this.connecting_node.outputs[\\r\\n this.connecting_slot\\r\\n ];\\r\\n this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot );\\r\\n }\\r\\n\\r\\n this.dirty_bgcanvas = true;\\r\\n skip_action = true;\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n } //not resizing\\r\\n }\\r\\n\\r\\n //Search for corner for collapsing\\r\\n /*\\r\\n\\t\\t\\tif( !skip_action && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ))\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnode.collapse();\\r\\n\\t\\t\\t\\tskip_action = true;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t*/\\r\\n\\r\\n //it wasn't clicked on the links boxes\\r\\n if (!skip_action) {\\r\\n var block_drag_node = false;\\r\\n\\r\\n //widgets\\r\\n var widget = this.processNodeWidgets(\\r\\n node,\\r\\n this.canvas_mouse,\\r\\n e\\r\\n );\\r\\n if (widget) {\\r\\n block_drag_node = true;\\r\\n this.node_widget = [node, widget];\\r\\n }\\r\\n\\r\\n //double clicking\\r\\n if (is_double_click && this.selected_nodes[node.id]) {\\r\\n //double click node\\r\\n if (node.onDblClick) {\\r\\n node.onDblClick(\\r\\n e,\\r\\n [\\r\\n e.canvasX - node.pos[0],\\r\\n e.canvasY - node.pos[1]\\r\\n ],\\r\\n this\\r\\n );\\r\\n }\\r\\n this.processNodeDblClicked(node);\\r\\n block_drag_node = true;\\r\\n }\\r\\n\\r\\n //if do not capture mouse\\r\\n if (\\r\\n node.onMouseDown &&\\r\\n node.onMouseDown(\\r\\n e,\\r\\n [e.canvasX - node.pos[0], e.canvasY - node.pos[1]],\\r\\n this\\r\\n )\\r\\n ) {\\r\\n block_drag_node = true;\\r\\n } else if (this.live_mode) {\\r\\n clicking_canvas_bg = true;\\r\\n block_drag_node = true;\\r\\n }\\r\\n\\r\\n if (!block_drag_node) {\\r\\n if (this.allow_dragnodes) {\\r\\n this.node_dragged = node;\\r\\n }\\r\\n if (!this.selected_nodes[node.id]) {\\r\\n this.processNodeSelected(node, e);\\r\\n }\\r\\n }\\r\\n\\r\\n this.dirty_canvas = true;\\r\\n }\\r\\n } //clicked outside of nodes\\r\\n else {\\r\\n //search for link connector\\r\\n\\t\\t\\t\\tif(!this.read_only) \\r\\n\\t\\t\\t\\t\\tfor (var i = 0; i < this.visible_links.length; ++i) {\\r\\n\\t\\t\\t\\t\\t\\tvar link = this.visible_links[i];\\r\\n\\t\\t\\t\\t\\t\\tvar center = link._pos;\\r\\n\\t\\t\\t\\t\\t\\tif (\\r\\n\\t\\t\\t\\t\\t\\t\\t!center ||\\r\\n\\t\\t\\t\\t\\t\\t\\te.canvasX < center[0] - 4 ||\\r\\n\\t\\t\\t\\t\\t\\t\\te.canvasX > center[0] + 4 ||\\r\\n\\t\\t\\t\\t\\t\\t\\te.canvasY < center[1] - 4 ||\\r\\n\\t\\t\\t\\t\\t\\t\\te.canvasY > center[1] + 4\\r\\n\\t\\t\\t\\t\\t\\t) {\\r\\n\\t\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t//link clicked\\r\\n\\t\\t\\t\\t\\t\\tthis.showLinkMenu(link, e);\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY );\\r\\n this.selected_group_resizing = false;\\r\\n if (this.selected_group && !this.read_only ) {\\r\\n if (e.ctrlKey) {\\r\\n this.dragging_rectangle = null;\\r\\n }\\r\\n\\r\\n var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] );\\r\\n if (dist * this.ds.scale < 10) {\\r\\n this.selected_group_resizing = true;\\r\\n } else {\\r\\n this.selected_group.recomputeInsideNodes();\\r\\n }\\r\\n }\\r\\n\\r\\n if (is_double_click && !this.read_only ) {\\r\\n this.showSearchBox(e);\\r\\n }\\r\\n\\r\\n clicking_canvas_bg = true;\\r\\n }\\r\\n\\r\\n if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) {\\r\\n this.dragging_canvas = true;\\r\\n }\\r\\n } else if (e.which == 2) {\\r\\n //middle button\\r\\n } else if (e.which == 3) {\\r\\n //right button\\r\\n\\t\\t\\tif(!this.read_only)\\r\\n\\t this.processContextMenu(node, e);\\r\\n }\\r\\n\\r\\n //TODO\\r\\n //if(this.node_selected != prev_selected)\\r\\n //\\tthis.onNodeSelectionChange(this.node_selected);\\r\\n\\r\\n this.last_mouse[0] = e.localX;\\r\\n this.last_mouse[1] = e.localY;\\r\\n this.last_mouseclick = LiteGraph.getTime();\\r\\n this.last_mouse_dragging = true;\\r\\n\\r\\n /*\\r\\n\\tif( (this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null)\\r\\n\\t\\tthis.draw();\\r\\n\\t*/\\r\\n\\r\\n this.graph.change();\\r\\n\\r\\n //this is to ensure to defocus(blur) if a text input element is on focus\\r\\n if (\\r\\n !ref_window.document.activeElement ||\\r\\n (ref_window.document.activeElement.nodeName.toLowerCase() !=\\r\\n \\\"input\\\" &&\\r\\n ref_window.document.activeElement.nodeName.toLowerCase() !=\\r\\n \\\"textarea\\\")\\r\\n ) {\\r\\n e.preventDefault();\\r\\n }\\r\\n e.stopPropagation();\\r\\n\\r\\n if (this.onMouseDown) {\\r\\n this.onMouseDown(e);\\r\\n }\\r\\n\\r\\n return false;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Called when a mouse move event has to be processed\\r\\n * @method processMouseMove\\r\\n **/\\r\\n LGraphCanvas.prototype.processMouseMove = function(e) {\\r\\n if (this.autoresize) {\\r\\n this.resize();\\r\\n }\\r\\n\\r\\n if (!this.graph) {\\r\\n return;\\r\\n }\\r\\n\\r\\n LGraphCanvas.active_canvas = this;\\r\\n this.adjustMouseEvent(e);\\r\\n var mouse = [e.localX, e.localY];\\r\\n var delta = [\\r\\n mouse[0] - this.last_mouse[0],\\r\\n mouse[1] - this.last_mouse[1]\\r\\n ];\\r\\n this.last_mouse = mouse;\\r\\n this.canvas_mouse[0] = e.canvasX;\\r\\n this.canvas_mouse[1] = e.canvasY;\\r\\n e.dragging = this.last_mouse_dragging;\\r\\n\\r\\n if (this.node_widget) {\\r\\n this.processNodeWidgets(\\r\\n this.node_widget[0],\\r\\n this.canvas_mouse,\\r\\n e,\\r\\n this.node_widget[1]\\r\\n );\\r\\n this.dirty_canvas = true;\\r\\n }\\r\\n\\r\\n if (this.dragging_rectangle) {\\r\\n this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0];\\r\\n this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1];\\r\\n this.dirty_canvas = true;\\r\\n } else if (this.selected_group && !this.read_only) {\\r\\n //moving/resizing a group\\r\\n if (this.selected_group_resizing) {\\r\\n this.selected_group.size = [\\r\\n e.canvasX - this.selected_group.pos[0],\\r\\n e.canvasY - this.selected_group.pos[1]\\r\\n ];\\r\\n } else {\\r\\n var deltax = delta[0] / this.ds.scale;\\r\\n var deltay = delta[1] / this.ds.scale;\\r\\n this.selected_group.move(deltax, deltay, e.ctrlKey);\\r\\n if (this.selected_group._nodes.length) {\\r\\n this.dirty_canvas = true;\\r\\n }\\r\\n }\\r\\n this.dirty_bgcanvas = true;\\r\\n } else if (this.dragging_canvas) {\\r\\n this.ds.offset[0] += delta[0] / this.ds.scale;\\r\\n this.ds.offset[1] += delta[1] / this.ds.scale;\\r\\n this.dirty_canvas = true;\\r\\n this.dirty_bgcanvas = true;\\r\\n } else if (this.allow_interaction && !this.read_only) {\\r\\n if (this.connecting_node) {\\r\\n this.dirty_canvas = true;\\r\\n }\\r\\n\\r\\n //get node over\\r\\n var node = this.graph.getNodeOnPos(\\r\\n e.canvasX,\\r\\n e.canvasY,\\r\\n this.visible_nodes\\r\\n );\\r\\n\\r\\n //remove mouseover flag\\r\\n for (var i = 0, l = this.graph._nodes.length; i < l; ++i) {\\r\\n if (\\r\\n this.graph._nodes[i].mouseOver &&\\r\\n node != this.graph._nodes[i]\\r\\n ) {\\r\\n //mouse leave\\r\\n this.graph._nodes[i].mouseOver = false;\\r\\n if (this.node_over && this.node_over.onMouseLeave) {\\r\\n this.node_over.onMouseLeave(e);\\r\\n }\\r\\n this.node_over = null;\\r\\n this.dirty_canvas = true;\\r\\n }\\r\\n }\\r\\n\\r\\n //mouse over a node\\r\\n if (node) {\\r\\n //this.canvas.style.cursor = \\\"move\\\";\\r\\n if (!node.mouseOver) {\\r\\n //mouse enter\\r\\n node.mouseOver = true;\\r\\n this.node_over = node;\\r\\n this.dirty_canvas = true;\\r\\n\\r\\n if (node.onMouseEnter) {\\r\\n node.onMouseEnter(e);\\r\\n }\\r\\n }\\r\\n\\r\\n //in case the node wants to do something\\r\\n if (node.onMouseMove) {\\r\\n node.onMouseMove(\\r\\n e,\\r\\n [e.canvasX - node.pos[0], e.canvasY - node.pos[1]],\\r\\n this\\r\\n );\\r\\n }\\r\\n\\r\\n //if dragging a link\\r\\n if (this.connecting_node) {\\r\\n var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput\\r\\n\\r\\n //on top of input\\r\\n if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) {\\r\\n //mouse on top of the corner box, don't know what to do\\r\\n } else {\\r\\n //check if I have a slot below de mouse\\r\\n var slot = this.isOverNodeInput(\\r\\n node,\\r\\n e.canvasX,\\r\\n e.canvasY,\\r\\n pos\\r\\n );\\r\\n if (slot != -1 && node.inputs[slot]) {\\r\\n var slot_type = node.inputs[slot].type;\\r\\n if (\\r\\n LiteGraph.isValidConnection(\\r\\n this.connecting_output.type,\\r\\n slot_type\\r\\n )\\r\\n ) {\\r\\n this._highlight_input = pos;\\r\\n }\\r\\n } else {\\r\\n this._highlight_input = null;\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n //Search for corner\\r\\n if (this.canvas) {\\r\\n if (\\r\\n isInsideRectangle(\\r\\n e.canvasX,\\r\\n e.canvasY,\\r\\n node.pos[0] + node.size[0] - 5,\\r\\n node.pos[1] + node.size[1] - 5,\\r\\n 5,\\r\\n 5\\r\\n )\\r\\n ) {\\r\\n this.canvas.style.cursor = \\\"se-resize\\\";\\r\\n } else {\\r\\n this.canvas.style.cursor = \\\"crosshair\\\";\\r\\n }\\r\\n }\\r\\n } else { //outside\\r\\n\\r\\n //search for link connector\\r\\n\\t\\t\\t\\tvar over_link = null;\\r\\n\\t\\t\\t\\tfor (var i = 0; i < this.visible_links.length; ++i) {\\r\\n\\t\\t\\t\\t\\tvar link = this.visible_links[i];\\r\\n\\t\\t\\t\\t\\tvar center = link._pos;\\r\\n\\t\\t\\t\\t\\tif (\\r\\n\\t\\t\\t\\t\\t\\t!center ||\\r\\n\\t\\t\\t\\t\\t\\te.canvasX < center[0] - 4 ||\\r\\n\\t\\t\\t\\t\\t\\te.canvasX > center[0] + 4 ||\\r\\n\\t\\t\\t\\t\\t\\te.canvasY < center[1] - 4 ||\\r\\n\\t\\t\\t\\t\\t\\te.canvasY > center[1] + 4\\r\\n\\t\\t\\t\\t\\t) {\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tover_link = link;\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif( over_link != this.over_link_center )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tthis.over_link_center = over_link;\\r\\n\\t this.dirty_canvas = true;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif (this.canvas) {\\r\\n\\t this.canvas.style.cursor = \\\"\\\";\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n if (\\r\\n this.node_capturing_input &&\\r\\n this.node_capturing_input != node &&\\r\\n this.node_capturing_input.onMouseMove\\r\\n ) {\\r\\n this.node_capturing_input.onMouseMove(e);\\r\\n }\\r\\n\\r\\n if (this.node_dragged && !this.live_mode) {\\r\\n for (var i in this.selected_nodes) {\\r\\n var n = this.selected_nodes[i];\\r\\n n.pos[0] += delta[0] / this.ds.scale;\\r\\n n.pos[1] += delta[1] / this.ds.scale;\\r\\n }\\r\\n\\r\\n this.dirty_canvas = true;\\r\\n this.dirty_bgcanvas = true;\\r\\n }\\r\\n\\r\\n if (this.resizing_node && !this.live_mode) {\\r\\n //convert mouse to node space\\r\\n this.resizing_node.size[0] =\\r\\n e.canvasX - this.resizing_node.pos[0];\\r\\n this.resizing_node.size[1] =\\r\\n e.canvasY - this.resizing_node.pos[1];\\r\\n\\r\\n //constraint size\\r\\n var max_slots = Math.max(\\r\\n this.resizing_node.inputs\\r\\n ? this.resizing_node.inputs.length\\r\\n : 0,\\r\\n this.resizing_node.outputs\\r\\n ? this.resizing_node.outputs.length\\r\\n : 0\\r\\n );\\r\\n var min_height =\\r\\n max_slots * LiteGraph.NODE_SLOT_HEIGHT +\\r\\n (this.resizing_node.widgets\\r\\n ? this.resizing_node.widgets.length\\r\\n : 0) *\\r\\n (LiteGraph.NODE_WIDGET_HEIGHT + 4) +\\r\\n 4;\\r\\n if (this.resizing_node.size[1] < min_height) {\\r\\n this.resizing_node.size[1] = min_height;\\r\\n }\\r\\n if (this.resizing_node.size[0] < LiteGraph.NODE_MIN_WIDTH) {\\r\\n this.resizing_node.size[0] = LiteGraph.NODE_MIN_WIDTH;\\r\\n }\\r\\n\\r\\n this.canvas.style.cursor = \\\"se-resize\\\";\\r\\n this.dirty_canvas = true;\\r\\n this.dirty_bgcanvas = true;\\r\\n }\\r\\n }\\r\\n\\r\\n e.preventDefault();\\r\\n return false;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Called when a mouse up event has to be processed\\r\\n * @method processMouseUp\\r\\n **/\\r\\n LGraphCanvas.prototype.processMouseUp = function(e) {\\r\\n if (!this.graph) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var window = this.getCanvasWindow();\\r\\n var document = window.document;\\r\\n LGraphCanvas.active_canvas = this;\\r\\n\\r\\n //restore the mousemove event back to the canvas\\r\\n document.removeEventListener(\\r\\n \\\"mousemove\\\",\\r\\n this._mousemove_callback,\\r\\n true\\r\\n );\\r\\n this.canvas.addEventListener(\\r\\n \\\"mousemove\\\",\\r\\n this._mousemove_callback,\\r\\n true\\r\\n );\\r\\n document.removeEventListener(\\\"mouseup\\\", this._mouseup_callback, true);\\r\\n\\r\\n this.adjustMouseEvent(e);\\r\\n var now = LiteGraph.getTime();\\r\\n e.click_time = now - this.last_mouseclick;\\r\\n this.last_mouse_dragging = false;\\r\\n\\r\\n if (e.which == 1) {\\r\\n //left button\\r\\n this.node_widget = null;\\r\\n\\r\\n if (this.selected_group) {\\r\\n var diffx =\\r\\n this.selected_group.pos[0] -\\r\\n Math.round(this.selected_group.pos[0]);\\r\\n var diffy =\\r\\n this.selected_group.pos[1] -\\r\\n Math.round(this.selected_group.pos[1]);\\r\\n this.selected_group.move(diffx, diffy, e.ctrlKey);\\r\\n this.selected_group.pos[0] = Math.round(\\r\\n this.selected_group.pos[0]\\r\\n );\\r\\n this.selected_group.pos[1] = Math.round(\\r\\n this.selected_group.pos[1]\\r\\n );\\r\\n if (this.selected_group._nodes.length) {\\r\\n this.dirty_canvas = true;\\r\\n }\\r\\n this.selected_group = null;\\r\\n }\\r\\n this.selected_group_resizing = false;\\r\\n\\r\\n if (this.dragging_rectangle) {\\r\\n if (this.graph) {\\r\\n var nodes = this.graph._nodes;\\r\\n var node_bounding = new Float32Array(4);\\r\\n this.deselectAllNodes();\\r\\n //compute bounding and flip if left to right\\r\\n var w = Math.abs(this.dragging_rectangle[2]);\\r\\n var h = Math.abs(this.dragging_rectangle[3]);\\r\\n var startx =\\r\\n this.dragging_rectangle[2] < 0\\r\\n ? this.dragging_rectangle[0] - w\\r\\n : this.dragging_rectangle[0];\\r\\n var starty =\\r\\n this.dragging_rectangle[3] < 0\\r\\n ? this.dragging_rectangle[1] - h\\r\\n : this.dragging_rectangle[1];\\r\\n this.dragging_rectangle[0] = startx;\\r\\n this.dragging_rectangle[1] = starty;\\r\\n this.dragging_rectangle[2] = w;\\r\\n this.dragging_rectangle[3] = h;\\r\\n\\r\\n //test against all nodes (not visible because the rectangle maybe start outside\\r\\n var to_select = [];\\r\\n for (var i = 0; i < nodes.length; ++i) {\\r\\n var node = nodes[i];\\r\\n node.getBounding(node_bounding);\\r\\n if (\\r\\n !overlapBounding(\\r\\n this.dragging_rectangle,\\r\\n node_bounding\\r\\n )\\r\\n ) {\\r\\n continue;\\r\\n } //out of the visible area\\r\\n to_select.push(node);\\r\\n }\\r\\n if (to_select.length) {\\r\\n this.selectNodes(to_select);\\r\\n }\\r\\n }\\r\\n this.dragging_rectangle = null;\\r\\n } else if (this.connecting_node) {\\r\\n //dragging a connection\\r\\n this.dirty_canvas = true;\\r\\n this.dirty_bgcanvas = true;\\r\\n\\r\\n var node = this.graph.getNodeOnPos(\\r\\n e.canvasX,\\r\\n e.canvasY,\\r\\n this.visible_nodes\\r\\n );\\r\\n\\r\\n //node below mouse\\r\\n if (node) {\\r\\n if (\\r\\n this.connecting_output.type == LiteGraph.EVENT &&\\r\\n this.isOverNodeBox(node, e.canvasX, e.canvasY)\\r\\n ) {\\r\\n this.connecting_node.connect(\\r\\n this.connecting_slot,\\r\\n node,\\r\\n LiteGraph.EVENT\\r\\n );\\r\\n } else {\\r\\n //slot below mouse? connect\\r\\n var slot = this.isOverNodeInput(\\r\\n node,\\r\\n e.canvasX,\\r\\n e.canvasY\\r\\n );\\r\\n if (slot != -1) {\\r\\n this.connecting_node.connect(\\r\\n this.connecting_slot,\\r\\n node,\\r\\n slot\\r\\n );\\r\\n } else {\\r\\n //not on top of an input\\r\\n var input = node.getInputInfo(0);\\r\\n //auto connect\\r\\n if (\\r\\n this.connecting_output.type == LiteGraph.EVENT\\r\\n ) {\\r\\n this.connecting_node.connect(\\r\\n this.connecting_slot,\\r\\n node,\\r\\n LiteGraph.EVENT\\r\\n );\\r\\n } else if (\\r\\n input &&\\r\\n !input.link &&\\r\\n LiteGraph.isValidConnection(\\r\\n input.type && this.connecting_output.type\\r\\n )\\r\\n ) {\\r\\n this.connecting_node.connect(\\r\\n this.connecting_slot,\\r\\n node,\\r\\n 0\\r\\n );\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n this.connecting_output = null;\\r\\n this.connecting_pos = null;\\r\\n this.connecting_node = null;\\r\\n this.connecting_slot = -1;\\r\\n } //not dragging connection\\r\\n else if (this.resizing_node) {\\r\\n this.dirty_canvas = true;\\r\\n this.dirty_bgcanvas = true;\\r\\n this.resizing_node = null;\\r\\n } else if (this.node_dragged) {\\r\\n //node being dragged?\\r\\n var node = this.node_dragged;\\r\\n if (\\r\\n node &&\\r\\n e.click_time < 300 &&\\r\\n isInsideRectangle(\\r\\n e.canvasX,\\r\\n e.canvasY,\\r\\n node.pos[0],\\r\\n node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT,\\r\\n LiteGraph.NODE_TITLE_HEIGHT,\\r\\n LiteGraph.NODE_TITLE_HEIGHT\\r\\n )\\r\\n ) {\\r\\n node.collapse();\\r\\n }\\r\\n\\r\\n this.dirty_canvas = true;\\r\\n this.dirty_bgcanvas = true;\\r\\n this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]);\\r\\n this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]);\\r\\n if (this.graph.config.align_to_grid) {\\r\\n this.node_dragged.alignToGrid();\\r\\n }\\r\\n this.node_dragged = null;\\r\\n } //no node being dragged\\r\\n else {\\r\\n //get node over\\r\\n var node = this.graph.getNodeOnPos(\\r\\n e.canvasX,\\r\\n e.canvasY,\\r\\n this.visible_nodes\\r\\n );\\r\\n\\r\\n if (!node && e.click_time < 300) {\\r\\n this.deselectAllNodes();\\r\\n }\\r\\n\\r\\n this.dirty_canvas = true;\\r\\n this.dragging_canvas = false;\\r\\n\\r\\n if (this.node_over && this.node_over.onMouseUp) {\\r\\n this.node_over.onMouseUp(\\r\\n e,\\r\\n [\\r\\n e.canvasX - this.node_over.pos[0],\\r\\n e.canvasY - this.node_over.pos[1]\\r\\n ],\\r\\n this\\r\\n );\\r\\n }\\r\\n if (\\r\\n this.node_capturing_input &&\\r\\n this.node_capturing_input.onMouseUp\\r\\n ) {\\r\\n this.node_capturing_input.onMouseUp(e, [\\r\\n e.canvasX - this.node_capturing_input.pos[0],\\r\\n e.canvasY - this.node_capturing_input.pos[1]\\r\\n ]);\\r\\n }\\r\\n }\\r\\n } else if (e.which == 2) {\\r\\n //middle button\\r\\n //trace(\\\"middle\\\");\\r\\n this.dirty_canvas = true;\\r\\n this.dragging_canvas = false;\\r\\n } else if (e.which == 3) {\\r\\n //right button\\r\\n //trace(\\\"right\\\");\\r\\n this.dirty_canvas = true;\\r\\n this.dragging_canvas = false;\\r\\n }\\r\\n\\r\\n /*\\r\\n\\tif((this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null)\\r\\n\\t\\tthis.draw();\\r\\n\\t*/\\r\\n\\r\\n this.graph.change();\\r\\n\\r\\n e.stopPropagation();\\r\\n e.preventDefault();\\r\\n return false;\\r\\n };\\r\\n\\r\\n /**\\r\\n * Called when a mouse wheel event has to be processed\\r\\n * @method processMouseWheel\\r\\n **/\\r\\n LGraphCanvas.prototype.processMouseWheel = function(e) {\\r\\n if (!this.graph || !this.allow_dragcanvas) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var delta = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60;\\r\\n\\r\\n this.adjustMouseEvent(e);\\r\\n\\r\\n var scale = this.ds.scale;\\r\\n\\r\\n if (delta > 0) {\\r\\n scale *= 1.1;\\r\\n } else if (delta < 0) {\\r\\n scale *= 1 / 1.1;\\r\\n }\\r\\n\\r\\n //this.setZoom( scale, [ e.localX, e.localY ] );\\r\\n this.ds.changeScale(scale, [e.localX, e.localY]);\\r\\n\\r\\n this.graph.change();\\r\\n\\r\\n e.preventDefault();\\r\\n return false; // prevent default\\r\\n };\\r\\n\\r\\n /**\\r\\n * returns true if a position (in graph space) is on top of a node little corner box\\r\\n * @method isOverNodeBox\\r\\n **/\\r\\n LGraphCanvas.prototype.isOverNodeBox = function(node, canvasx, canvasy) {\\r\\n var title_height = LiteGraph.NODE_TITLE_HEIGHT;\\r\\n if (\\r\\n isInsideRectangle(\\r\\n canvasx,\\r\\n canvasy,\\r\\n node.pos[0] + 2,\\r\\n node.pos[1] + 2 - title_height,\\r\\n title_height - 4,\\r\\n title_height - 4\\r\\n )\\r\\n ) {\\r\\n return true;\\r\\n }\\r\\n return false;\\r\\n };\\r\\n\\r\\n /**\\r\\n * returns true if a position (in graph space) is on top of a node input slot\\r\\n * @method isOverNodeInput\\r\\n **/\\r\\n LGraphCanvas.prototype.isOverNodeInput = function(\\r\\n node,\\r\\n canvasx,\\r\\n canvasy,\\r\\n slot_pos\\r\\n ) {\\r\\n if (node.inputs) {\\r\\n for (var i = 0, l = node.inputs.length; i < l; ++i) {\\r\\n var input = node.inputs[i];\\r\\n var link_pos = node.getConnectionPos(true, i);\\r\\n var is_inside = false;\\r\\n if (node.horizontal) {\\r\\n is_inside = isInsideRectangle(\\r\\n canvasx,\\r\\n canvasy,\\r\\n link_pos[0] - 5,\\r\\n link_pos[1] - 10,\\r\\n 10,\\r\\n 20\\r\\n );\\r\\n } else {\\r\\n is_inside = isInsideRectangle(\\r\\n canvasx,\\r\\n canvasy,\\r\\n link_pos[0] - 10,\\r\\n link_pos[1] - 5,\\r\\n 40,\\r\\n 10\\r\\n );\\r\\n }\\r\\n if (is_inside) {\\r\\n if (slot_pos) {\\r\\n slot_pos[0] = link_pos[0];\\r\\n slot_pos[1] = link_pos[1];\\r\\n }\\r\\n return i;\\r\\n }\\r\\n }\\r\\n }\\r\\n return -1;\\r\\n };\\r\\n\\r\\n /**\\r\\n * process a key event\\r\\n * @method processKey\\r\\n **/\\r\\n LGraphCanvas.prototype.processKey = function(e) {\\r\\n if (!this.graph) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var block_default = false;\\r\\n //console.log(e); //debug\\r\\n\\r\\n if (e.target.localName == \\\"input\\\") {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (e.type == \\\"keydown\\\") {\\r\\n if (e.keyCode == 32) {\\r\\n //esc\\r\\n this.dragging_canvas = true;\\r\\n block_default = true;\\r\\n }\\r\\n\\r\\n //select all Control A\\r\\n if (e.keyCode == 65 && e.ctrlKey) {\\r\\n this.selectNodes();\\r\\n block_default = true;\\r\\n }\\r\\n\\r\\n if (e.code == \\\"KeyC\\\" && (e.metaKey || e.ctrlKey) && !e.shiftKey) {\\r\\n //copy\\r\\n if (this.selected_nodes) {\\r\\n this.copyToClipboard();\\r\\n block_default = true;\\r\\n }\\r\\n }\\r\\n\\r\\n if (e.code == \\\"KeyV\\\" && (e.metaKey || e.ctrlKey) && !e.shiftKey) {\\r\\n //paste\\r\\n this.pasteFromClipboard();\\r\\n }\\r\\n\\r\\n //delete or backspace\\r\\n if (e.keyCode == 46 || e.keyCode == 8) {\\r\\n if (\\r\\n e.target.localName != \\\"input\\\" &&\\r\\n e.target.localName != \\\"textarea\\\"\\r\\n ) {\\r\\n this.deleteSelectedNodes();\\r\\n block_default = true;\\r\\n }\\r\\n }\\r\\n\\r\\n //collapse\\r\\n //...\\r\\n\\r\\n //TODO\\r\\n if (this.selected_nodes) {\\r\\n for (var i in this.selected_nodes) {\\r\\n if (this.selected_nodes[i].onKeyDown) {\\r\\n this.selected_nodes[i].onKeyDown(e);\\r\\n }\\r\\n }\\r\\n }\\r\\n } else if (e.type == \\\"keyup\\\") {\\r\\n if (e.keyCode == 32) {\\r\\n this.dragging_canvas = false;\\r\\n }\\r\\n\\r\\n if (this.selected_nodes) {\\r\\n for (var i in this.selected_nodes) {\\r\\n if (this.selected_nodes[i].onKeyUp) {\\r\\n this.selected_nodes[i].onKeyUp(e);\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n this.graph.change();\\r\\n\\r\\n if (block_default) {\\r\\n e.preventDefault();\\r\\n e.stopImmediatePropagation();\\r\\n return false;\\r\\n }\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.copyToClipboard = function() {\\r\\n var clipboard_info = {\\r\\n nodes: [],\\r\\n links: []\\r\\n };\\r\\n var index = 0;\\r\\n var selected_nodes_array = [];\\r\\n for (var i in this.selected_nodes) {\\r\\n var node = this.selected_nodes[i];\\r\\n node._relative_id = index;\\r\\n selected_nodes_array.push(node);\\r\\n index += 1;\\r\\n }\\r\\n\\r\\n for (var i = 0; i < selected_nodes_array.length; ++i) {\\r\\n var node = selected_nodes_array[i];\\r\\n clipboard_info.nodes.push(node.clone().serialize());\\r\\n if (node.inputs && node.inputs.length) {\\r\\n for (var j = 0; j < node.inputs.length; ++j) {\\r\\n var input = node.inputs[j];\\r\\n if (!input || input.link == null) {\\r\\n continue;\\r\\n }\\r\\n var link_info = this.graph.links[input.link];\\r\\n if (!link_info) {\\r\\n continue;\\r\\n }\\r\\n var target_node = this.graph.getNodeById(\\r\\n link_info.origin_id\\r\\n );\\r\\n if (!target_node || !this.selected_nodes[target_node.id]) {\\r\\n //improve this by allowing connections to non-selected nodes\\r\\n continue;\\r\\n } //not selected\\r\\n clipboard_info.links.push([\\r\\n target_node._relative_id,\\r\\n j,\\r\\n node._relative_id,\\r\\n link_info.target_slot\\r\\n ]);\\r\\n }\\r\\n }\\r\\n }\\r\\n localStorage.setItem(\\r\\n \\\"litegrapheditor_clipboard\\\",\\r\\n JSON.stringify(clipboard_info)\\r\\n );\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.pasteFromClipboard = function() {\\r\\n var data = localStorage.getItem(\\\"litegrapheditor_clipboard\\\");\\r\\n if (!data) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //create nodes\\r\\n var clipboard_info = JSON.parse(data);\\r\\n var nodes = [];\\r\\n for (var i = 0; i < clipboard_info.nodes.length; ++i) {\\r\\n var node_data = clipboard_info.nodes[i];\\r\\n var node = LiteGraph.createNode(node_data.type);\\r\\n if (node) {\\r\\n node.configure(node_data);\\r\\n node.pos[0] += 5;\\r\\n node.pos[1] += 5;\\r\\n this.graph.add(node);\\r\\n nodes.push(node);\\r\\n }\\r\\n }\\r\\n\\r\\n //create links\\r\\n for (var i = 0; i < clipboard_info.links.length; ++i) {\\r\\n var link_info = clipboard_info.links[i];\\r\\n var origin_node = nodes[link_info[0]];\\r\\n var target_node = nodes[link_info[2]];\\r\\n origin_node.connect(link_info[1], target_node, link_info[3]);\\r\\n }\\r\\n\\r\\n this.selectNodes(nodes);\\r\\n };\\r\\n\\r\\n /**\\r\\n * process a item drop event on top the canvas\\r\\n * @method processDrop\\r\\n **/\\r\\n LGraphCanvas.prototype.processDrop = function(e) {\\r\\n e.preventDefault();\\r\\n this.adjustMouseEvent(e);\\r\\n\\r\\n var pos = [e.canvasX, e.canvasY];\\r\\n var node = this.graph.getNodeOnPos(pos[0], pos[1]);\\r\\n\\r\\n if (!node) {\\r\\n var r = null;\\r\\n if (this.onDropItem) {\\r\\n r = this.onDropItem(event);\\r\\n }\\r\\n if (!r) {\\r\\n this.checkDropItem(e);\\r\\n }\\r\\n return;\\r\\n }\\r\\n\\r\\n if (node.onDropFile || node.onDropData) {\\r\\n var files = e.dataTransfer.files;\\r\\n if (files && files.length) {\\r\\n for (var i = 0; i < files.length; i++) {\\r\\n var file = e.dataTransfer.files[0];\\r\\n var filename = file.name;\\r\\n var ext = LGraphCanvas.getFileExtension(filename);\\r\\n //console.log(file);\\r\\n\\r\\n if (node.onDropFile) {\\r\\n node.onDropFile(file);\\r\\n }\\r\\n\\r\\n if (node.onDropData) {\\r\\n //prepare reader\\r\\n var reader = new FileReader();\\r\\n reader.onload = function(event) {\\r\\n //console.log(event.target);\\r\\n var data = event.target.result;\\r\\n node.onDropData(data, filename, file);\\r\\n };\\r\\n\\r\\n //read data\\r\\n var type = file.type.split(\\\"/\\\")[0];\\r\\n if (type == \\\"text\\\" || type == \\\"\\\") {\\r\\n reader.readAsText(file);\\r\\n } else if (type == \\\"image\\\") {\\r\\n reader.readAsDataURL(file);\\r\\n } else {\\r\\n reader.readAsArrayBuffer(file);\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (node.onDropItem) {\\r\\n if (node.onDropItem(event)) {\\r\\n return true;\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.onDropItem) {\\r\\n return this.onDropItem(event);\\r\\n }\\r\\n\\r\\n return false;\\r\\n };\\r\\n\\r\\n //called if the graph doesn't have a default drop item behaviour\\r\\n LGraphCanvas.prototype.checkDropItem = function(e) {\\r\\n if (e.dataTransfer.files.length) {\\r\\n var file = e.dataTransfer.files[0];\\r\\n var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase();\\r\\n var nodetype = LiteGraph.node_types_by_file_extension[ext];\\r\\n if (nodetype) {\\r\\n var node = LiteGraph.createNode(nodetype.type);\\r\\n node.pos = [e.canvasX, e.canvasY];\\r\\n this.graph.add(node);\\r\\n if (node.onDropFile) {\\r\\n node.onDropFile(file);\\r\\n }\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.processNodeDblClicked = function(n) {\\r\\n if (this.onShowNodePanel) {\\r\\n this.onShowNodePanel(n);\\r\\n }\\r\\n\\r\\n if (this.onNodeDblClicked) {\\r\\n this.onNodeDblClicked(n);\\r\\n }\\r\\n\\r\\n this.setDirty(true);\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.processNodeSelected = function(node, e) {\\r\\n this.selectNode(node, e && e.shiftKey);\\r\\n if (this.onNodeSelected) {\\r\\n this.onNodeSelected(node);\\r\\n }\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.processNodeDeselected = function(node) {\\r\\n this.deselectNode(node);\\r\\n if (this.onNodeDeselected) {\\r\\n this.onNodeDeselected(node);\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * selects a given node (or adds it to the current selection)\\r\\n * @method selectNode\\r\\n **/\\r\\n LGraphCanvas.prototype.selectNode = function(\\r\\n node,\\r\\n add_to_current_selection\\r\\n ) {\\r\\n if (node == null) {\\r\\n this.deselectAllNodes();\\r\\n } else {\\r\\n this.selectNodes([node], add_to_current_selection);\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * selects several nodes (or adds them to the current selection)\\r\\n * @method selectNodes\\r\\n **/\\r\\n LGraphCanvas.prototype.selectNodes = function(\\r\\n nodes,\\r\\n add_to_current_selection\\r\\n ) {\\r\\n if (!add_to_current_selection) {\\r\\n this.deselectAllNodes();\\r\\n }\\r\\n\\r\\n nodes = nodes || this.graph._nodes;\\r\\n for (var i = 0; i < nodes.length; ++i) {\\r\\n var node = nodes[i];\\r\\n if (node.is_selected) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n if (!node.is_selected && node.onSelected) {\\r\\n node.onSelected();\\r\\n }\\r\\n node.is_selected = true;\\r\\n this.selected_nodes[node.id] = node;\\r\\n\\r\\n if (node.inputs) {\\r\\n for (var j = 0; j < node.inputs.length; ++j) {\\r\\n this.highlighted_links[node.inputs[j].link] = true;\\r\\n }\\r\\n }\\r\\n if (node.outputs) {\\r\\n for (var j = 0; j < node.outputs.length; ++j) {\\r\\n var out = node.outputs[j];\\r\\n if (out.links) {\\r\\n for (var k = 0; k < out.links.length; ++k) {\\r\\n this.highlighted_links[out.links[k]] = true;\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n this.setDirty(true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * removes a node from the current selection\\r\\n * @method deselectNode\\r\\n **/\\r\\n LGraphCanvas.prototype.deselectNode = function(node) {\\r\\n if (!node.is_selected) {\\r\\n return;\\r\\n }\\r\\n if (node.onDeselected) {\\r\\n node.onDeselected();\\r\\n }\\r\\n node.is_selected = false;\\r\\n\\r\\n //remove highlighted\\r\\n if (node.inputs) {\\r\\n for (var i = 0; i < node.inputs.length; ++i) {\\r\\n delete this.highlighted_links[node.inputs[i].link];\\r\\n }\\r\\n }\\r\\n if (node.outputs) {\\r\\n for (var i = 0; i < node.outputs.length; ++i) {\\r\\n var out = node.outputs[i];\\r\\n if (out.links) {\\r\\n for (var j = 0; j < out.links.length; ++j) {\\r\\n delete this.highlighted_links[out.links[j]];\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * removes all nodes from the current selection\\r\\n * @method deselectAllNodes\\r\\n **/\\r\\n LGraphCanvas.prototype.deselectAllNodes = function() {\\r\\n if (!this.graph) {\\r\\n return;\\r\\n }\\r\\n var nodes = this.graph._nodes;\\r\\n for (var i = 0, l = nodes.length; i < l; ++i) {\\r\\n var node = nodes[i];\\r\\n if (!node.is_selected) {\\r\\n continue;\\r\\n }\\r\\n if (node.onDeselected) {\\r\\n node.onDeselected();\\r\\n }\\r\\n node.is_selected = false;\\r\\n }\\r\\n this.selected_nodes = {};\\r\\n this.current_node = null;\\r\\n this.highlighted_links = {};\\r\\n this.setDirty(true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * deletes all nodes in the current selection from the graph\\r\\n * @method deleteSelectedNodes\\r\\n **/\\r\\n LGraphCanvas.prototype.deleteSelectedNodes = function() {\\r\\n for (var i in this.selected_nodes) {\\r\\n var m = this.selected_nodes[i];\\r\\n //if(m == this.node_in_panel) this.showNodePanel(null);\\r\\n this.graph.remove(m);\\r\\n }\\r\\n this.selected_nodes = {};\\r\\n this.current_node = null;\\r\\n this.highlighted_links = {};\\r\\n this.setDirty(true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * centers the camera on a given node\\r\\n * @method centerOnNode\\r\\n **/\\r\\n LGraphCanvas.prototype.centerOnNode = function(node) {\\r\\n this.ds.offset[0] =\\r\\n -node.pos[0] -\\r\\n node.size[0] * 0.5 +\\r\\n (this.canvas.width * 0.5) / this.ds.scale;\\r\\n this.ds.offset[1] =\\r\\n -node.pos[1] -\\r\\n node.size[1] * 0.5 +\\r\\n (this.canvas.height * 0.5) / this.ds.scale;\\r\\n this.setDirty(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * adds some useful properties to a mouse event, like the position in graph coordinates\\r\\n * @method adjustMouseEvent\\r\\n **/\\r\\n LGraphCanvas.prototype.adjustMouseEvent = function(e) {\\r\\n if (this.canvas) {\\r\\n var b = this.canvas.getBoundingClientRect();\\r\\n e.localX = e.clientX - b.left;\\r\\n e.localY = e.clientY - b.top;\\r\\n } else {\\r\\n e.localX = e.clientX;\\r\\n e.localY = e.clientY;\\r\\n }\\r\\n\\r\\n e.deltaX = e.localX - this.last_mouse_position[0];\\r\\n e.deltaY = e.localY - this.last_mouse_position[1];\\r\\n\\r\\n this.last_mouse_position[0] = e.localX;\\r\\n this.last_mouse_position[1] = e.localY;\\r\\n\\r\\n e.canvasX = e.localX / this.ds.scale - this.ds.offset[0];\\r\\n e.canvasY = e.localY / this.ds.scale - this.ds.offset[1];\\r\\n };\\r\\n\\r\\n /**\\r\\n * changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom\\r\\n * @method setZoom\\r\\n **/\\r\\n LGraphCanvas.prototype.setZoom = function(value, zooming_center) {\\r\\n this.ds.changeScale(value, zooming_center);\\r\\n /*\\r\\n\\tif(!zooming_center && this.canvas)\\r\\n\\t\\tzooming_center = [this.canvas.width * 0.5,this.canvas.height * 0.5];\\r\\n\\r\\n\\tvar center = this.convertOffsetToCanvas( zooming_center );\\r\\n\\r\\n\\tthis.ds.scale = value;\\r\\n\\r\\n\\tif(this.scale > this.max_zoom)\\r\\n\\t\\tthis.scale = this.max_zoom;\\r\\n\\telse if(this.scale < this.min_zoom)\\r\\n\\t\\tthis.scale = this.min_zoom;\\r\\n\\r\\n\\tvar new_center = this.convertOffsetToCanvas( zooming_center );\\r\\n\\tvar delta_offset = [new_center[0] - center[0], new_center[1] - center[1]];\\r\\n\\r\\n\\tthis.offset[0] += delta_offset[0];\\r\\n\\tthis.offset[1] += delta_offset[1];\\r\\n\\t*/\\r\\n\\r\\n this.dirty_canvas = true;\\r\\n this.dirty_bgcanvas = true;\\r\\n };\\r\\n\\r\\n /**\\r\\n * converts a coordinate from graph coordinates to canvas2D coordinates\\r\\n * @method convertOffsetToCanvas\\r\\n **/\\r\\n LGraphCanvas.prototype.convertOffsetToCanvas = function(pos, out) {\\r\\n return this.ds.convertOffsetToCanvas(pos, out);\\r\\n };\\r\\n\\r\\n /**\\r\\n * converts a coordinate from Canvas2D coordinates to graph space\\r\\n * @method convertCanvasToOffset\\r\\n **/\\r\\n LGraphCanvas.prototype.convertCanvasToOffset = function(pos, out) {\\r\\n return this.ds.convertCanvasToOffset(pos, out);\\r\\n };\\r\\n\\r\\n //converts event coordinates from canvas2D to graph coordinates\\r\\n LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) {\\r\\n var rect = this.canvas.getBoundingClientRect();\\r\\n return this.convertCanvasToOffset([\\r\\n e.clientX - rect.left,\\r\\n e.clientY - rect.top\\r\\n ]);\\r\\n };\\r\\n\\r\\n /**\\r\\n * brings a node to front (above all other nodes)\\r\\n * @method bringToFront\\r\\n **/\\r\\n LGraphCanvas.prototype.bringToFront = function(node) {\\r\\n var i = this.graph._nodes.indexOf(node);\\r\\n if (i == -1) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.graph._nodes.splice(i, 1);\\r\\n this.graph._nodes.push(node);\\r\\n };\\r\\n\\r\\n /**\\r\\n * sends a node to the back (below all other nodes)\\r\\n * @method sendToBack\\r\\n **/\\r\\n LGraphCanvas.prototype.sendToBack = function(node) {\\r\\n var i = this.graph._nodes.indexOf(node);\\r\\n if (i == -1) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.graph._nodes.splice(i, 1);\\r\\n this.graph._nodes.unshift(node);\\r\\n };\\r\\n\\r\\n /* Interaction */\\r\\n\\r\\n /* LGraphCanvas render */\\r\\n var temp = new Float32Array(4);\\r\\n\\r\\n /**\\r\\n * checks which nodes are visible (inside the camera area)\\r\\n * @method computeVisibleNodes\\r\\n **/\\r\\n LGraphCanvas.prototype.computeVisibleNodes = function(nodes, out) {\\r\\n var visible_nodes = out || [];\\r\\n visible_nodes.length = 0;\\r\\n nodes = nodes || this.graph._nodes;\\r\\n for (var i = 0, l = nodes.length; i < l; ++i) {\\r\\n var n = nodes[i];\\r\\n\\r\\n //skip rendering nodes in live mode\\r\\n if (this.live_mode && !n.onDrawBackground && !n.onDrawForeground) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n if (!overlapBounding(this.visible_area, n.getBounding(temp))) {\\r\\n continue;\\r\\n } //out of the visible area\\r\\n\\r\\n visible_nodes.push(n);\\r\\n }\\r\\n return visible_nodes;\\r\\n };\\r\\n\\r\\n /**\\r\\n * renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes)\\r\\n * @method draw\\r\\n **/\\r\\n LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas) {\\r\\n if (!this.canvas) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //fps counting\\r\\n var now = LiteGraph.getTime();\\r\\n this.render_time = (now - this.last_draw_time) * 0.001;\\r\\n this.last_draw_time = now;\\r\\n\\r\\n if (this.graph) {\\r\\n this.ds.computeVisibleArea();\\r\\n }\\r\\n\\r\\n if (\\r\\n this.dirty_bgcanvas ||\\r\\n force_bgcanvas ||\\r\\n this.always_render_background ||\\r\\n (this.graph &&\\r\\n this.graph._last_trigger_time &&\\r\\n now - this.graph._last_trigger_time < 1000)\\r\\n ) {\\r\\n this.drawBackCanvas();\\r\\n }\\r\\n\\r\\n if (this.dirty_canvas || force_canvas) {\\r\\n this.drawFrontCanvas();\\r\\n }\\r\\n\\r\\n this.fps = this.render_time ? 1.0 / this.render_time : 0;\\r\\n this.frame += 1;\\r\\n };\\r\\n\\r\\n /**\\r\\n * draws the front canvas (the one containing all the nodes)\\r\\n * @method drawFrontCanvas\\r\\n **/\\r\\n LGraphCanvas.prototype.drawFrontCanvas = function() {\\r\\n this.dirty_canvas = false;\\r\\n\\r\\n if (!this.ctx) {\\r\\n this.ctx = this.bgcanvas.getContext(\\\"2d\\\");\\r\\n }\\r\\n var ctx = this.ctx;\\r\\n if (!ctx) {\\r\\n //maybe is using webgl...\\r\\n return;\\r\\n }\\r\\n\\r\\n if (ctx.start2D) {\\r\\n ctx.start2D();\\r\\n }\\r\\n\\r\\n var canvas = this.canvas;\\r\\n\\r\\n //reset in case of error\\r\\n ctx.restore();\\r\\n ctx.setTransform(1, 0, 0, 1, 0, 0);\\r\\n\\r\\n //clip dirty area if there is one, otherwise work in full canvas\\r\\n if (this.dirty_area) {\\r\\n ctx.save();\\r\\n ctx.beginPath();\\r\\n ctx.rect(\\r\\n this.dirty_area[0],\\r\\n this.dirty_area[1],\\r\\n this.dirty_area[2],\\r\\n this.dirty_area[3]\\r\\n );\\r\\n ctx.clip();\\r\\n }\\r\\n\\r\\n //clear\\r\\n //canvas.width = canvas.width;\\r\\n if (this.clear_background) {\\r\\n ctx.clearRect(0, 0, canvas.width, canvas.height);\\r\\n }\\r\\n\\r\\n //draw bg canvas\\r\\n if (this.bgcanvas == this.canvas) {\\r\\n this.drawBackCanvas();\\r\\n } else {\\r\\n ctx.drawImage(this.bgcanvas, 0, 0);\\r\\n }\\r\\n\\r\\n //rendering\\r\\n if (this.onRender) {\\r\\n this.onRender(canvas, ctx);\\r\\n }\\r\\n\\r\\n //info widget\\r\\n if (this.show_info) {\\r\\n this.renderInfo(ctx);\\r\\n }\\r\\n\\r\\n if (this.graph) {\\r\\n //apply transformations\\r\\n ctx.save();\\r\\n this.ds.toCanvasContext(ctx);\\r\\n\\r\\n //draw nodes\\r\\n var drawn_nodes = 0;\\r\\n var visible_nodes = this.computeVisibleNodes(\\r\\n null,\\r\\n this.visible_nodes\\r\\n );\\r\\n\\r\\n for (var i = 0; i < visible_nodes.length; ++i) {\\r\\n var node = visible_nodes[i];\\r\\n\\r\\n //transform coords system\\r\\n ctx.save();\\r\\n ctx.translate(node.pos[0], node.pos[1]);\\r\\n\\r\\n //Draw\\r\\n this.drawNode(node, ctx);\\r\\n drawn_nodes += 1;\\r\\n\\r\\n //Restore\\r\\n ctx.restore();\\r\\n }\\r\\n\\r\\n //on top (debug)\\r\\n if (this.render_execution_order) {\\r\\n this.drawExecutionOrder(ctx);\\r\\n }\\r\\n\\r\\n //connections ontop?\\r\\n if (this.graph.config.links_ontop) {\\r\\n if (!this.live_mode) {\\r\\n this.drawConnections(ctx);\\r\\n }\\r\\n }\\r\\n\\r\\n //current connection (the one being dragged by the mouse)\\r\\n if (this.connecting_pos != null) {\\r\\n ctx.lineWidth = this.connections_width;\\r\\n var link_color = null;\\r\\n\\r\\n switch (this.connecting_output.type) {\\r\\n case LiteGraph.EVENT:\\r\\n link_color = LiteGraph.EVENT_LINK_COLOR;\\r\\n break;\\r\\n default:\\r\\n link_color = LiteGraph.CONNECTING_LINK_COLOR;\\r\\n }\\r\\n\\r\\n //the connection being dragged by the mouse\\r\\n this.renderLink(\\r\\n ctx,\\r\\n this.connecting_pos,\\r\\n [this.canvas_mouse[0], this.canvas_mouse[1]],\\r\\n null,\\r\\n false,\\r\\n null,\\r\\n link_color,\\r\\n this.connecting_output.dir ||\\r\\n (this.connecting_node.horizontal\\r\\n ? LiteGraph.DOWN\\r\\n : LiteGraph.RIGHT),\\r\\n LiteGraph.CENTER\\r\\n );\\r\\n\\r\\n ctx.beginPath();\\r\\n if (\\r\\n this.connecting_output.type === LiteGraph.EVENT ||\\r\\n this.connecting_output.shape === LiteGraph.BOX_SHAPE\\r\\n ) {\\r\\n ctx.rect(\\r\\n this.connecting_pos[0] - 6 + 0.5,\\r\\n this.connecting_pos[1] - 5 + 0.5,\\r\\n 14,\\r\\n 10\\r\\n );\\r\\n } else {\\r\\n ctx.arc(\\r\\n this.connecting_pos[0],\\r\\n this.connecting_pos[1],\\r\\n 4,\\r\\n 0,\\r\\n Math.PI * 2\\r\\n );\\r\\n }\\r\\n ctx.fill();\\r\\n\\r\\n ctx.fillStyle = \\\"#ffcc00\\\";\\r\\n if (this._highlight_input) {\\r\\n ctx.beginPath();\\r\\n ctx.arc(\\r\\n this._highlight_input[0],\\r\\n this._highlight_input[1],\\r\\n 6,\\r\\n 0,\\r\\n Math.PI * 2\\r\\n );\\r\\n ctx.fill();\\r\\n }\\r\\n }\\r\\n\\r\\n\\t\\t\\t//the selection rectangle\\r\\n if (this.dragging_rectangle) {\\r\\n ctx.strokeStyle = \\\"#FFF\\\";\\r\\n ctx.strokeRect(\\r\\n this.dragging_rectangle[0],\\r\\n this.dragging_rectangle[1],\\r\\n this.dragging_rectangle[2],\\r\\n this.dragging_rectangle[3]\\r\\n );\\r\\n }\\r\\n\\r\\n\\t\\t\\t//on top of link center\\r\\n\\t\\t\\tif(this.over_link_center && this.render_link_tooltip)\\r\\n\\t\\t\\t\\tthis.drawLinkTooltip( ctx, this.over_link_center );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tif(this.onDrawLinkTooltip) //to remove\\r\\n\\t\\t\\t\\t\\tthis.onDrawLinkTooltip(ctx,null);\\r\\n\\r\\n\\t\\t\\t//custom info\\r\\n if (this.onDrawForeground) {\\r\\n this.onDrawForeground(ctx, this.visible_rect);\\r\\n }\\r\\n\\r\\n ctx.restore();\\r\\n }\\r\\n\\r\\n if (this.onDrawOverlay) {\\r\\n this.onDrawOverlay(ctx);\\r\\n }\\r\\n\\r\\n if (this.dirty_area) {\\r\\n ctx.restore();\\r\\n //this.dirty_area = null;\\r\\n }\\r\\n\\r\\n if (ctx.finish2D) {\\r\\n //this is a function I use in webgl renderer\\r\\n ctx.finish2D();\\r\\n }\\r\\n };\\r\\n\\r\\n /**\\r\\n * draws some useful stats in the corner of the canvas\\r\\n * @method renderInfo\\r\\n **/\\r\\n LGraphCanvas.prototype.renderInfo = function(ctx, x, y) {\\r\\n x = x || 0;\\r\\n y = y || 0;\\r\\n\\r\\n ctx.save();\\r\\n ctx.translate(x, y);\\r\\n\\r\\n ctx.font = \\\"10px Arial\\\";\\r\\n ctx.fillStyle = \\\"#888\\\";\\r\\n if (this.graph) {\\r\\n ctx.fillText( \\\"T: \\\" + this.graph.globaltime.toFixed(2) + \\\"s\\\", 5, 13 * 1 );\\r\\n ctx.fillText(\\\"I: \\\" + this.graph.iteration, 5, 13 * 2 );\\r\\n ctx.fillText(\\\"N: \\\" + this.graph._nodes.length + \\\" [\\\" + this.visible_nodes.length + \\\"]\\\", 5, 13 * 3 );\\r\\n ctx.fillText(\\\"V: \\\" + this.graph._version, 5, 13 * 4);\\r\\n ctx.fillText(\\\"FPS:\\\" + this.fps.toFixed(2), 5, 13 * 5);\\r\\n } else {\\r\\n ctx.fillText(\\\"No graph selected\\\", 5, 13 * 1);\\r\\n }\\r\\n ctx.restore();\\r\\n };\\r\\n\\r\\n /**\\r\\n * draws the back canvas (the one containing the background and the connections)\\r\\n * @method drawBackCanvas\\r\\n **/\\r\\n LGraphCanvas.prototype.drawBackCanvas = function() {\\r\\n var canvas = this.bgcanvas;\\r\\n if (\\r\\n canvas.width != this.canvas.width ||\\r\\n canvas.height != this.canvas.height\\r\\n ) {\\r\\n canvas.width = this.canvas.width;\\r\\n canvas.height = this.canvas.height;\\r\\n }\\r\\n\\r\\n if (!this.bgctx) {\\r\\n this.bgctx = this.bgcanvas.getContext(\\\"2d\\\");\\r\\n }\\r\\n var ctx = this.bgctx;\\r\\n if (ctx.start) {\\r\\n ctx.start();\\r\\n }\\r\\n\\r\\n //clear\\r\\n if (this.clear_background) {\\r\\n ctx.clearRect(0, 0, canvas.width, canvas.height);\\r\\n }\\r\\n\\r\\n if (this._graph_stack && this._graph_stack.length) {\\r\\n ctx.save();\\r\\n var parent_graph = this._graph_stack[this._graph_stack.length - 1];\\r\\n var subgraph_node = this.graph._subgraph_node;\\r\\n ctx.strokeStyle = subgraph_node.bgcolor;\\r\\n ctx.lineWidth = 10;\\r\\n ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2);\\r\\n ctx.lineWidth = 1;\\r\\n ctx.font = \\\"40px Arial\\\";\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n ctx.fillStyle = subgraph_node.bgcolor || \\\"#AAA\\\";\\r\\n var title = \\\"\\\";\\r\\n for (var i = 1; i < this._graph_stack.length; ++i) {\\r\\n title +=\\r\\n this._graph_stack[i]._subgraph_node.getTitle() + \\\" >> \\\";\\r\\n }\\r\\n ctx.fillText(\\r\\n title + subgraph_node.getTitle(),\\r\\n canvas.width * 0.5,\\r\\n 40\\r\\n );\\r\\n ctx.restore();\\r\\n }\\r\\n\\r\\n var bg_already_painted = false;\\r\\n if (this.onRenderBackground) {\\r\\n bg_already_painted = this.onRenderBackground(canvas, ctx);\\r\\n }\\r\\n\\r\\n //reset in case of error\\r\\n ctx.restore();\\r\\n ctx.setTransform(1, 0, 0, 1, 0, 0);\\r\\n this.visible_links.length = 0;\\r\\n\\r\\n if (this.graph) {\\r\\n //apply transformations\\r\\n ctx.save();\\r\\n this.ds.toCanvasContext(ctx);\\r\\n\\r\\n //render BG\\r\\n if (\\r\\n this.background_image &&\\r\\n this.ds.scale > 0.5 &&\\r\\n !bg_already_painted\\r\\n ) {\\r\\n if (this.zoom_modify_alpha) {\\r\\n ctx.globalAlpha =\\r\\n (1.0 - 0.5 / this.ds.scale) * this.editor_alpha;\\r\\n } else {\\r\\n ctx.globalAlpha = this.editor_alpha;\\r\\n }\\r\\n ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false;\\r\\n if (\\r\\n !this._bg_img ||\\r\\n this._bg_img.name != this.background_image\\r\\n ) {\\r\\n this._bg_img = new Image();\\r\\n this._bg_img.name = this.background_image;\\r\\n this._bg_img.src = this.background_image;\\r\\n var that = this;\\r\\n this._bg_img.onload = function() {\\r\\n that.draw(true, true);\\r\\n };\\r\\n }\\r\\n\\r\\n var pattern = null;\\r\\n if (this._pattern == null && this._bg_img.width > 0) {\\r\\n pattern = ctx.createPattern(this._bg_img, \\\"repeat\\\");\\r\\n this._pattern_img = this._bg_img;\\r\\n this._pattern = pattern;\\r\\n } else {\\r\\n pattern = this._pattern;\\r\\n }\\r\\n if (pattern) {\\r\\n ctx.fillStyle = pattern;\\r\\n ctx.fillRect(\\r\\n this.visible_area[0],\\r\\n this.visible_area[1],\\r\\n this.visible_area[2],\\r\\n this.visible_area[3]\\r\\n );\\r\\n ctx.fillStyle = \\\"transparent\\\";\\r\\n }\\r\\n\\r\\n ctx.globalAlpha = 1.0;\\r\\n ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true;\\r\\n }\\r\\n\\r\\n //groups\\r\\n if (this.graph._groups.length && !this.live_mode) {\\r\\n this.drawGroups(canvas, ctx);\\r\\n }\\r\\n\\r\\n if (this.onDrawBackground) {\\r\\n this.onDrawBackground(ctx, this.visible_area);\\r\\n }\\r\\n if (this.onBackgroundRender) {\\r\\n //LEGACY\\r\\n console.error(\\r\\n \\\"WARNING! onBackgroundRender deprecated, now is named onDrawBackground \\\"\\r\\n );\\r\\n this.onBackgroundRender = null;\\r\\n }\\r\\n\\r\\n //DEBUG: show clipping area\\r\\n //ctx.fillStyle = \\\"red\\\";\\r\\n //ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20);\\r\\n\\r\\n //bg\\r\\n if (this.render_canvas_border) {\\r\\n ctx.strokeStyle = \\\"#235\\\";\\r\\n ctx.strokeRect(0, 0, canvas.width, canvas.height);\\r\\n }\\r\\n\\r\\n if (this.render_connections_shadows) {\\r\\n ctx.shadowColor = \\\"#000\\\";\\r\\n ctx.shadowOffsetX = 0;\\r\\n ctx.shadowOffsetY = 0;\\r\\n ctx.shadowBlur = 6;\\r\\n } else {\\r\\n ctx.shadowColor = \\\"rgba(0,0,0,0)\\\";\\r\\n }\\r\\n\\r\\n //draw connections\\r\\n if (!this.live_mode) {\\r\\n this.drawConnections(ctx);\\r\\n }\\r\\n\\r\\n ctx.shadowColor = \\\"rgba(0,0,0,0)\\\";\\r\\n\\r\\n //restore state\\r\\n ctx.restore();\\r\\n }\\r\\n\\r\\n if (ctx.finish) {\\r\\n ctx.finish();\\r\\n }\\r\\n\\r\\n this.dirty_bgcanvas = false;\\r\\n this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas\\r\\n };\\r\\n\\r\\n var temp_vec2 = new Float32Array(2);\\r\\n\\r\\n /**\\r\\n * draws the given node inside the canvas\\r\\n * @method drawNode\\r\\n **/\\r\\n LGraphCanvas.prototype.drawNode = function(node, ctx) {\\r\\n var glow = false;\\r\\n this.current_node = node;\\r\\n\\r\\n var color =\\r\\n node.color ||\\r\\n node.constructor.color ||\\r\\n LiteGraph.NODE_DEFAULT_COLOR;\\r\\n var bgcolor =\\r\\n node.bgcolor ||\\r\\n node.constructor.bgcolor ||\\r\\n LiteGraph.NODE_DEFAULT_BGCOLOR;\\r\\n\\r\\n //shadow and glow\\r\\n if (node.mouseOver) {\\r\\n glow = true;\\r\\n }\\r\\n\\r\\n //only render if it forces it to do it\\r\\n if (this.live_mode) {\\r\\n if (!node.flags.collapsed) {\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n if (node.onDrawForeground) {\\r\\n node.onDrawForeground(ctx, this, this.canvas);\\r\\n }\\r\\n }\\r\\n return;\\r\\n }\\r\\n\\r\\n var editor_alpha = this.editor_alpha;\\r\\n ctx.globalAlpha = editor_alpha;\\r\\n\\r\\n if (this.render_shadows) {\\r\\n ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR;\\r\\n ctx.shadowOffsetX = 2 * this.ds.scale;\\r\\n ctx.shadowOffsetY = 2 * this.ds.scale;\\r\\n ctx.shadowBlur = 3 * this.ds.scale;\\r\\n } else {\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n }\\r\\n\\r\\n //custom draw collapsed method (draw after shadows because they are affected)\\r\\n if (\\r\\n node.flags.collapsed &&\\r\\n node.onDrawCollapsed &&\\r\\n node.onDrawCollapsed(ctx, this) == true\\r\\n ) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //clip if required (mask)\\r\\n var shape = node._shape || LiteGraph.BOX_SHAPE;\\r\\n var size = temp_vec2;\\r\\n temp_vec2.set(node.size);\\r\\n var horizontal = node.horizontal; // || node.flags.horizontal;\\r\\n\\r\\n if (node.flags.collapsed) {\\r\\n ctx.font = this.inner_text_font;\\r\\n var title = node.getTitle ? node.getTitle() : node.title;\\r\\n if (title != null) {\\r\\n node._collapsed_width = Math.min(\\r\\n node.size[0],\\r\\n ctx.measureText(title).width +\\r\\n LiteGraph.NODE_TITLE_HEIGHT * 2\\r\\n ); //LiteGraph.NODE_COLLAPSED_WIDTH;\\r\\n size[0] = node._collapsed_width;\\r\\n size[1] = 0;\\r\\n }\\r\\n }\\r\\n\\r\\n if (node.clip_area) {\\r\\n //Start clipping\\r\\n ctx.save();\\r\\n ctx.beginPath();\\r\\n if (shape == LiteGraph.BOX_SHAPE) {\\r\\n ctx.rect(0, 0, size[0], size[1]);\\r\\n } else if (shape == LiteGraph.ROUND_SHAPE) {\\r\\n ctx.roundRect(0, 0, size[0], size[1], 10);\\r\\n } else if (shape == LiteGraph.CIRCLE_SHAPE) {\\r\\n ctx.arc(\\r\\n size[0] * 0.5,\\r\\n size[1] * 0.5,\\r\\n size[0] * 0.5,\\r\\n 0,\\r\\n Math.PI * 2\\r\\n );\\r\\n }\\r\\n ctx.clip();\\r\\n }\\r\\n\\r\\n //draw shape\\r\\n if (node.has_errors) {\\r\\n bgcolor = \\\"red\\\";\\r\\n }\\r\\n this.drawNodeShape(\\r\\n node,\\r\\n ctx,\\r\\n size,\\r\\n color,\\r\\n bgcolor,\\r\\n node.is_selected,\\r\\n node.mouseOver\\r\\n );\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n\\r\\n //draw foreground\\r\\n if (node.onDrawForeground) {\\r\\n node.onDrawForeground(ctx, this, this.canvas);\\r\\n }\\r\\n\\r\\n //connection slots\\r\\n ctx.textAlign = horizontal ? \\\"center\\\" : \\\"left\\\";\\r\\n ctx.font = this.inner_text_font;\\r\\n\\r\\n var render_text = this.ds.scale > 0.6;\\r\\n\\r\\n var out_slot = this.connecting_output;\\r\\n ctx.lineWidth = 1;\\r\\n\\r\\n var max_y = 0;\\r\\n var slot_pos = new Float32Array(2); //to reuse\\r\\n\\r\\n //render inputs and outputs\\r\\n if (!node.flags.collapsed) {\\r\\n //input connection slots\\r\\n if (node.inputs) {\\r\\n for (var i = 0; i < node.inputs.length; i++) {\\r\\n var slot = node.inputs[i];\\r\\n\\r\\n ctx.globalAlpha = editor_alpha;\\r\\n //change opacity of incompatible slots when dragging a connection\\r\\n if ( this.connecting_node && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) {\\r\\n ctx.globalAlpha = 0.4 * editor_alpha;\\r\\n }\\r\\n\\r\\n ctx.fillStyle =\\r\\n slot.link != null\\r\\n ? slot.color_on ||\\r\\n this.default_connection_color.input_on\\r\\n : slot.color_off ||\\r\\n this.default_connection_color.input_off;\\r\\n\\r\\n var pos = node.getConnectionPos(true, i, slot_pos);\\r\\n pos[0] -= node.pos[0];\\r\\n pos[1] -= node.pos[1];\\r\\n if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) {\\r\\n max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5;\\r\\n }\\r\\n\\r\\n ctx.beginPath();\\r\\n\\r\\n if (\\r\\n slot.type === LiteGraph.EVENT ||\\r\\n slot.shape === LiteGraph.BOX_SHAPE\\r\\n ) {\\r\\n if (horizontal) {\\r\\n ctx.rect(\\r\\n pos[0] - 5 + 0.5,\\r\\n pos[1] - 8 + 0.5,\\r\\n 10,\\r\\n 14\\r\\n );\\r\\n } else {\\r\\n ctx.rect(\\r\\n pos[0] - 6 + 0.5,\\r\\n pos[1] - 5 + 0.5,\\r\\n 14,\\r\\n 10\\r\\n );\\r\\n }\\r\\n } else if (slot.shape === LiteGraph.ARROW_SHAPE) {\\r\\n ctx.moveTo(pos[0] + 8, pos[1] + 0.5);\\r\\n ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5);\\r\\n ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5);\\r\\n ctx.closePath();\\r\\n } else {\\r\\n ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2);\\r\\n }\\r\\n\\r\\n ctx.fill();\\r\\n\\r\\n //render name\\r\\n if (render_text) {\\r\\n var text = slot.label != null ? slot.label : slot.name;\\r\\n if (text) {\\r\\n ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR;\\r\\n if (horizontal || slot.dir == LiteGraph.UP) {\\r\\n ctx.fillText(text, pos[0], pos[1] - 10);\\r\\n } else {\\r\\n ctx.fillText(text, pos[0] + 10, pos[1] + 5);\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n //output connection slots\\r\\n if (this.connecting_node) {\\r\\n ctx.globalAlpha = 0.4 * editor_alpha;\\r\\n }\\r\\n\\r\\n ctx.textAlign = horizontal ? \\\"center\\\" : \\\"right\\\";\\r\\n ctx.strokeStyle = \\\"black\\\";\\r\\n if (node.outputs) {\\r\\n for (var i = 0; i < node.outputs.length; i++) {\\r\\n var slot = node.outputs[i];\\r\\n\\r\\n var pos = node.getConnectionPos(false, i, slot_pos);\\r\\n pos[0] -= node.pos[0];\\r\\n pos[1] -= node.pos[1];\\r\\n if (max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5) {\\r\\n max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT * 0.5;\\r\\n }\\r\\n\\r\\n ctx.fillStyle =\\r\\n slot.links && slot.links.length\\r\\n ? slot.color_on ||\\r\\n this.default_connection_color.output_on\\r\\n : slot.color_off ||\\r\\n this.default_connection_color.output_off;\\r\\n ctx.beginPath();\\r\\n //ctx.rect( node.size[0] - 14,i*14,10,10);\\r\\n\\r\\n if (\\r\\n slot.type === LiteGraph.EVENT ||\\r\\n slot.shape === LiteGraph.BOX_SHAPE\\r\\n ) {\\r\\n if (horizontal) {\\r\\n ctx.rect(\\r\\n pos[0] - 5 + 0.5,\\r\\n pos[1] - 8 + 0.5,\\r\\n 10,\\r\\n 14\\r\\n );\\r\\n } else {\\r\\n ctx.rect(\\r\\n pos[0] - 6 + 0.5,\\r\\n pos[1] - 5 + 0.5,\\r\\n 14,\\r\\n 10\\r\\n );\\r\\n }\\r\\n } else if (slot.shape === LiteGraph.ARROW_SHAPE) {\\r\\n ctx.moveTo(pos[0] + 8, pos[1] + 0.5);\\r\\n ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5);\\r\\n ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5);\\r\\n ctx.closePath();\\r\\n } else {\\r\\n ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2);\\r\\n }\\r\\n\\r\\n //trigger\\r\\n //if(slot.node_id != null && slot.slot == -1)\\r\\n //\\tctx.fillStyle = \\\"#F85\\\";\\r\\n\\r\\n //if(slot.links != null && slot.links.length)\\r\\n ctx.fill();\\r\\n ctx.stroke();\\r\\n\\r\\n //render output name\\r\\n if (render_text) {\\r\\n var text = slot.label != null ? slot.label : slot.name;\\r\\n if (text) {\\r\\n ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR;\\r\\n if (horizontal || slot.dir == LiteGraph.DOWN) {\\r\\n ctx.fillText(text, pos[0], pos[1] - 8);\\r\\n } else {\\r\\n ctx.fillText(text, pos[0] - 10, pos[1] + 5);\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n ctx.globalAlpha = 1;\\r\\n\\r\\n if (node.widgets) {\\r\\n if (horizontal || node.widgets_up) {\\r\\n max_y = 2;\\r\\n }\\r\\n this.drawNodeWidgets(\\r\\n node,\\r\\n max_y,\\r\\n ctx,\\r\\n this.node_widget && this.node_widget[0] == node\\r\\n ? this.node_widget[1]\\r\\n : null\\r\\n );\\r\\n }\\r\\n } else if (this.render_collapsed_slots) {\\r\\n //if collapsed\\r\\n var input_slot = null;\\r\\n var output_slot = null;\\r\\n\\r\\n //get first connected slot to render\\r\\n if (node.inputs) {\\r\\n for (var i = 0; i < node.inputs.length; i++) {\\r\\n var slot = node.inputs[i];\\r\\n if (slot.link == null) {\\r\\n continue;\\r\\n }\\r\\n input_slot = slot;\\r\\n break;\\r\\n }\\r\\n }\\r\\n if (node.outputs) {\\r\\n for (var i = 0; i < node.outputs.length; i++) {\\r\\n var slot = node.outputs[i];\\r\\n if (!slot.links || !slot.links.length) {\\r\\n continue;\\r\\n }\\r\\n output_slot = slot;\\r\\n }\\r\\n }\\r\\n\\r\\n if (input_slot) {\\r\\n var x = 0;\\r\\n var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center\\r\\n if (horizontal) {\\r\\n x = node._collapsed_width * 0.5;\\r\\n y = -LiteGraph.NODE_TITLE_HEIGHT;\\r\\n }\\r\\n ctx.fillStyle = \\\"#686\\\";\\r\\n ctx.beginPath();\\r\\n if (\\r\\n slot.type === LiteGraph.EVENT ||\\r\\n slot.shape === LiteGraph.BOX_SHAPE\\r\\n ) {\\r\\n ctx.rect(x - 7 + 0.5, y - 4, 14, 8);\\r\\n } else if (slot.shape === LiteGraph.ARROW_SHAPE) {\\r\\n ctx.moveTo(x + 8, y);\\r\\n ctx.lineTo(x + -4, y - 4);\\r\\n ctx.lineTo(x + -4, y + 4);\\r\\n ctx.closePath();\\r\\n } else {\\r\\n ctx.arc(x, y, 4, 0, Math.PI * 2);\\r\\n }\\r\\n ctx.fill();\\r\\n }\\r\\n\\r\\n if (output_slot) {\\r\\n var x = node._collapsed_width;\\r\\n var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center\\r\\n if (horizontal) {\\r\\n x = node._collapsed_width * 0.5;\\r\\n y = 0;\\r\\n }\\r\\n ctx.fillStyle = \\\"#686\\\";\\r\\n ctx.strokeStyle = \\\"black\\\";\\r\\n ctx.beginPath();\\r\\n if (\\r\\n slot.type === LiteGraph.EVENT ||\\r\\n slot.shape === LiteGraph.BOX_SHAPE\\r\\n ) {\\r\\n ctx.rect(x - 7 + 0.5, y - 4, 14, 8);\\r\\n } else if (slot.shape === LiteGraph.ARROW_SHAPE) {\\r\\n ctx.moveTo(x + 6, y);\\r\\n ctx.lineTo(x - 6, y - 4);\\r\\n ctx.lineTo(x - 6, y + 4);\\r\\n ctx.closePath();\\r\\n } else {\\r\\n ctx.arc(x, y, 4, 0, Math.PI * 2);\\r\\n }\\r\\n ctx.fill();\\r\\n //ctx.stroke();\\r\\n }\\r\\n }\\r\\n\\r\\n if (node.clip_area) {\\r\\n ctx.restore();\\r\\n }\\r\\n\\r\\n ctx.globalAlpha = 1.0;\\r\\n };\\r\\n\\r\\n\\t//used by this.over_link_center\\r\\n\\tLGraphCanvas.prototype.drawLinkTooltip = function( ctx, link )\\r\\n\\t{\\r\\n\\t\\tvar pos = link._pos;\\r\\n\\t\\tctx.fillStyle = \\\"black\\\";\\r\\n\\t\\tctx.beginPath();\\r\\n\\t\\tctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 );\\r\\n\\t\\tctx.fill();\\r\\n\\r\\n\\t\\tif(link.data == null)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(this.onDrawLinkTooltip)\\r\\n\\t\\t\\tif( this.onDrawLinkTooltip(ctx,link,this) == true )\\r\\n\\t\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar data = link.data;\\r\\n\\t\\tvar text = null;\\r\\n\\r\\n\\t\\tif( data.constructor === Number )\\r\\n\\t\\t\\ttext = data.toFixed(2);\\r\\n\\t\\telse if( data.constructor === String )\\r\\n\\t\\t\\ttext = \\\"\\\\\\\"\\\" + data + \\\"\\\\\\\"\\\";\\r\\n\\t\\telse if( data.constructor === Boolean )\\r\\n\\t\\t\\ttext = String(data);\\r\\n\\t\\telse if (data.toToolTip)\\r\\n\\t\\t\\ttext = data.toToolTip();\\r\\n\\t\\telse\\r\\n\\t\\t\\ttext = \\\"[\\\" + data.constructor.name + \\\"]\\\";\\r\\n\\r\\n\\t\\tif(text == null)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tctx.font = \\\"14px Courier New\\\";\\r\\n\\t\\tvar info = ctx.measureText(text);\\r\\n\\t\\tvar w = info.width + 20;\\r\\n\\t\\tvar h = 24;\\r\\n\\t\\tctx.shadowColor = \\\"black\\\";\\r\\n\\t\\tctx.shadowOffsetX = 2;\\r\\n\\t\\tctx.shadowOffsetY = 2;\\r\\n\\t\\tctx.shadowBlur = 3;\\r\\n\\t\\tctx.fillStyle = \\\"#454\\\";\\r\\n\\t\\tctx.beginPath();\\r\\n\\t\\tctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h,3, 3);\\r\\n\\t\\tctx.moveTo( pos[0] - 10, pos[1] - 15 );\\r\\n\\t\\tctx.lineTo( pos[0] + 10, pos[1] - 15 );\\r\\n\\t\\tctx.lineTo( pos[0], pos[1] - 5 );\\r\\n\\t\\tctx.fill();\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n\\t\\tctx.textAlign = \\\"center\\\";\\r\\n\\t\\tctx.fillStyle = \\\"#CEC\\\";\\r\\n\\t\\tctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3);\\r\\n\\t}\\r\\n\\r\\n /**\\r\\n * draws the shape of the given node in the canvas\\r\\n * @method drawNodeShape\\r\\n **/\\r\\n var tmp_area = new Float32Array(4);\\r\\n\\r\\n LGraphCanvas.prototype.drawNodeShape = function(\\r\\n node,\\r\\n ctx,\\r\\n size,\\r\\n fgcolor,\\r\\n bgcolor,\\r\\n selected,\\r\\n mouse_over\\r\\n ) {\\r\\n //bg rect\\r\\n ctx.strokeStyle = fgcolor;\\r\\n ctx.fillStyle = bgcolor;\\r\\n\\r\\n var title_height = LiteGraph.NODE_TITLE_HEIGHT;\\r\\n var low_quality = this.ds.scale < 0.5;\\r\\n\\r\\n //render node area depending on shape\\r\\n var shape =\\r\\n node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE;\\r\\n\\r\\n var title_mode = node.constructor.title_mode;\\r\\n\\r\\n var render_title = true;\\r\\n if (title_mode == LiteGraph.TRANSPARENT_TITLE) {\\r\\n render_title = false;\\r\\n } else if (title_mode == LiteGraph.AUTOHIDE_TITLE && mouse_over) {\\r\\n render_title = true;\\r\\n }\\r\\n\\r\\n var area = tmp_area;\\r\\n area[0] = 0; //x\\r\\n area[1] = render_title ? -title_height : 0; //y\\r\\n area[2] = size[0] + 1; //w\\r\\n area[3] = render_title ? size[1] + title_height : size[1]; //h\\r\\n\\r\\n var old_alpha = ctx.globalAlpha;\\r\\n\\r\\n //full node shape\\r\\n //if(node.flags.collapsed)\\r\\n {\\r\\n ctx.beginPath();\\r\\n if (shape == LiteGraph.BOX_SHAPE || low_quality) {\\r\\n ctx.fillRect(area[0], area[1], area[2], area[3]);\\r\\n } else if (\\r\\n shape == LiteGraph.ROUND_SHAPE ||\\r\\n shape == LiteGraph.CARD_SHAPE\\r\\n ) {\\r\\n ctx.roundRect(\\r\\n area[0],\\r\\n area[1],\\r\\n area[2],\\r\\n area[3],\\r\\n this.round_radius,\\r\\n shape == LiteGraph.CARD_SHAPE ? 0 : this.round_radius\\r\\n );\\r\\n } else if (shape == LiteGraph.CIRCLE_SHAPE) {\\r\\n ctx.arc(\\r\\n size[0] * 0.5,\\r\\n size[1] * 0.5,\\r\\n size[0] * 0.5,\\r\\n 0,\\r\\n Math.PI * 2\\r\\n );\\r\\n }\\r\\n ctx.fill();\\r\\n\\r\\n\\t\\t\\t//separator\\r\\n\\t\\t\\tif(!node.flags.collapsed)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tctx.shadowColor = \\\"transparent\\\";\\r\\n\\t\\t\\t\\tctx.fillStyle = \\\"rgba(0,0,0,0.2)\\\";\\r\\n\\t\\t\\t\\tctx.fillRect(0, -1, area[2], 2);\\r\\n\\t\\t\\t}\\r\\n }\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n\\r\\n if (node.onDrawBackground) {\\r\\n node.onDrawBackground(ctx, this, this.canvas);\\r\\n }\\r\\n\\r\\n //title bg (remember, it is rendered ABOVE the node)\\r\\n if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) {\\r\\n //title bar\\r\\n if (node.onDrawTitleBar) {\\r\\n node.onDrawTitleBar(\\r\\n ctx,\\r\\n title_height,\\r\\n size,\\r\\n this.ds.scale,\\r\\n fgcolor\\r\\n );\\r\\n } else if (\\r\\n title_mode != LiteGraph.TRANSPARENT_TITLE &&\\r\\n (node.constructor.title_color || this.render_title_colored)\\r\\n ) {\\r\\n var title_color = node.constructor.title_color || fgcolor;\\r\\n\\r\\n if (node.flags.collapsed) {\\r\\n ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR;\\r\\n }\\r\\n\\r\\n //* gradient test\\r\\n if (this.use_gradients) {\\r\\n var grad = LGraphCanvas.gradients[title_color];\\r\\n if (!grad) {\\r\\n grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0);\\r\\n grad.addColorStop(0, title_color);\\r\\n grad.addColorStop(1, \\\"#000\\\");\\r\\n }\\r\\n ctx.fillStyle = grad;\\r\\n } else {\\r\\n ctx.fillStyle = title_color;\\r\\n }\\r\\n\\r\\n //ctx.globalAlpha = 0.5 * old_alpha;\\r\\n ctx.beginPath();\\r\\n if (shape == LiteGraph.BOX_SHAPE || low_quality) {\\r\\n ctx.rect(0, -title_height, size[0] + 1, title_height);\\r\\n } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) {\\r\\n ctx.roundRect(\\r\\n 0,\\r\\n -title_height,\\r\\n size[0] + 1,\\r\\n title_height,\\r\\n this.round_radius,\\r\\n node.flags.collapsed ? this.round_radius : 0\\r\\n );\\r\\n }\\r\\n ctx.fill();\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n }\\r\\n\\r\\n //title box\\r\\n var box_size = 10;\\r\\n if (node.onDrawTitleBox) {\\r\\n node.onDrawTitleBox(ctx, title_height, size, this.ds.scale);\\r\\n } else if (\\r\\n shape == LiteGraph.ROUND_SHAPE ||\\r\\n shape == LiteGraph.CIRCLE_SHAPE ||\\r\\n shape == LiteGraph.CARD_SHAPE\\r\\n ) {\\r\\n if (low_quality) {\\r\\n ctx.fillStyle = \\\"black\\\";\\r\\n ctx.beginPath();\\r\\n ctx.arc(\\r\\n title_height * 0.5,\\r\\n title_height * -0.5,\\r\\n box_size * 0.5 + 1,\\r\\n 0,\\r\\n Math.PI * 2\\r\\n );\\r\\n ctx.fill();\\r\\n }\\r\\n\\r\\n ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;\\r\\n ctx.beginPath();\\r\\n ctx.arc(\\r\\n title_height * 0.5,\\r\\n title_height * -0.5,\\r\\n box_size * 0.5,\\r\\n 0,\\r\\n Math.PI * 2\\r\\n );\\r\\n ctx.fill();\\r\\n } else {\\r\\n if (low_quality) {\\r\\n ctx.fillStyle = \\\"black\\\";\\r\\n ctx.fillRect(\\r\\n (title_height - box_size) * 0.5 - 1,\\r\\n (title_height + box_size) * -0.5 - 1,\\r\\n box_size + 2,\\r\\n box_size + 2\\r\\n );\\r\\n }\\r\\n ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;\\r\\n ctx.fillRect(\\r\\n (title_height - box_size) * 0.5,\\r\\n (title_height + box_size) * -0.5,\\r\\n box_size,\\r\\n box_size\\r\\n );\\r\\n }\\r\\n ctx.globalAlpha = old_alpha;\\r\\n\\r\\n //title text\\r\\n if (node.onDrawTitleText) {\\r\\n node.onDrawTitleText(\\r\\n ctx,\\r\\n title_height,\\r\\n size,\\r\\n this.ds.scale,\\r\\n this.title_text_font,\\r\\n selected\\r\\n );\\r\\n }\\r\\n if (!low_quality) {\\r\\n ctx.font = this.title_text_font;\\r\\n var title = node.getTitle();\\r\\n if (title) {\\r\\n if (selected) {\\r\\n ctx.fillStyle = \\\"white\\\";\\r\\n } else {\\r\\n ctx.fillStyle =\\r\\n node.constructor.title_text_color ||\\r\\n this.node_title_color;\\r\\n }\\r\\n if (node.flags.collapsed) {\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n var measure = ctx.measureText(title);\\r\\n ctx.fillText(\\r\\n title,\\r\\n title_height + measure.width * 0.5,\\r\\n LiteGraph.NODE_TITLE_TEXT_Y - title_height\\r\\n );\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n } else {\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n ctx.fillText(\\r\\n title,\\r\\n title_height,\\r\\n LiteGraph.NODE_TITLE_TEXT_Y - title_height\\r\\n );\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (node.onDrawTitle) {\\r\\n node.onDrawTitle(ctx);\\r\\n }\\r\\n }\\r\\n\\r\\n //render selection marker\\r\\n if (selected) {\\r\\n if (node.onBounding) {\\r\\n node.onBounding(area);\\r\\n }\\r\\n\\r\\n if (title_mode == LiteGraph.TRANSPARENT_TITLE) {\\r\\n area[1] -= title_height;\\r\\n area[3] += title_height;\\r\\n }\\r\\n ctx.lineWidth = 1;\\r\\n ctx.globalAlpha = 0.8;\\r\\n ctx.beginPath();\\r\\n if (shape == LiteGraph.BOX_SHAPE) {\\r\\n ctx.rect(\\r\\n -6 + area[0],\\r\\n -6 + area[1],\\r\\n 12 + area[2],\\r\\n 12 + area[3]\\r\\n );\\r\\n } else if (\\r\\n shape == LiteGraph.ROUND_SHAPE ||\\r\\n (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed)\\r\\n ) {\\r\\n ctx.roundRect(\\r\\n -6 + area[0],\\r\\n -6 + area[1],\\r\\n 12 + area[2],\\r\\n 12 + area[3],\\r\\n this.round_radius * 2\\r\\n );\\r\\n } else if (shape == LiteGraph.CARD_SHAPE) {\\r\\n ctx.roundRect(\\r\\n -6 + area[0],\\r\\n -6 + area[1],\\r\\n 12 + area[2],\\r\\n 12 + area[3],\\r\\n this.round_radius * 2,\\r\\n 2\\r\\n );\\r\\n } else if (shape == LiteGraph.CIRCLE_SHAPE) {\\r\\n ctx.arc(\\r\\n size[0] * 0.5,\\r\\n size[1] * 0.5,\\r\\n size[0] * 0.5 + 6,\\r\\n 0,\\r\\n Math.PI * 2\\r\\n );\\r\\n }\\r\\n ctx.strokeStyle = \\\"#FFF\\\";\\r\\n ctx.stroke();\\r\\n ctx.strokeStyle = fgcolor;\\r\\n ctx.globalAlpha = 1;\\r\\n }\\r\\n };\\r\\n\\r\\n var margin_area = new Float32Array(4);\\r\\n var link_bounding = new Float32Array(4);\\r\\n var tempA = new Float32Array(2);\\r\\n var tempB = new Float32Array(2);\\r\\n\\r\\n /**\\r\\n * draws every connection visible in the canvas\\r\\n * OPTIMIZE THIS: pre-catch connections position instead of recomputing them every time\\r\\n * @method drawConnections\\r\\n **/\\r\\n LGraphCanvas.prototype.drawConnections = function(ctx) {\\r\\n var now = LiteGraph.getTime();\\r\\n var visible_area = this.visible_area;\\r\\n margin_area[0] = visible_area[0] - 20;\\r\\n margin_area[1] = visible_area[1] - 20;\\r\\n margin_area[2] = visible_area[2] + 40;\\r\\n margin_area[3] = visible_area[3] + 40;\\r\\n\\r\\n //draw connections\\r\\n ctx.lineWidth = this.connections_width;\\r\\n\\r\\n ctx.fillStyle = \\\"#AAA\\\";\\r\\n ctx.strokeStyle = \\\"#AAA\\\";\\r\\n ctx.globalAlpha = this.editor_alpha;\\r\\n //for every node\\r\\n var nodes = this.graph._nodes;\\r\\n for (var n = 0, l = nodes.length; n < l; ++n) {\\r\\n var node = nodes[n];\\r\\n //for every input (we render just inputs because it is easier as every slot can only have one input)\\r\\n if (!node.inputs || !node.inputs.length) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n for (var i = 0; i < node.inputs.length; ++i) {\\r\\n var input = node.inputs[i];\\r\\n if (!input || input.link == null) {\\r\\n continue;\\r\\n }\\r\\n var link_id = input.link;\\r\\n var link = this.graph.links[link_id];\\r\\n if (!link) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n //find link info\\r\\n var start_node = this.graph.getNodeById(link.origin_id);\\r\\n if (start_node == null) {\\r\\n continue;\\r\\n }\\r\\n var start_node_slot = link.origin_slot;\\r\\n var start_node_slotpos = null;\\r\\n if (start_node_slot == -1) {\\r\\n start_node_slotpos = [\\r\\n start_node.pos[0] + 10,\\r\\n start_node.pos[1] + 10\\r\\n ];\\r\\n } else {\\r\\n start_node_slotpos = start_node.getConnectionPos(\\r\\n false,\\r\\n start_node_slot,\\r\\n tempA\\r\\n );\\r\\n }\\r\\n var end_node_slotpos = node.getConnectionPos(true, i, tempB);\\r\\n\\r\\n //compute link bounding\\r\\n link_bounding[0] = start_node_slotpos[0];\\r\\n link_bounding[1] = start_node_slotpos[1];\\r\\n link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0];\\r\\n link_bounding[3] = end_node_slotpos[1] - start_node_slotpos[1];\\r\\n if (link_bounding[2] < 0) {\\r\\n link_bounding[0] += link_bounding[2];\\r\\n link_bounding[2] = Math.abs(link_bounding[2]);\\r\\n }\\r\\n if (link_bounding[3] < 0) {\\r\\n link_bounding[1] += link_bounding[3];\\r\\n link_bounding[3] = Math.abs(link_bounding[3]);\\r\\n }\\r\\n\\r\\n //skip links outside of the visible area of the canvas\\r\\n if (!overlapBounding(link_bounding, margin_area)) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n var start_slot = start_node.outputs[start_node_slot];\\r\\n var end_slot = node.inputs[i];\\r\\n if (!start_slot || !end_slot) {\\r\\n continue;\\r\\n }\\r\\n var start_dir =\\r\\n start_slot.dir ||\\r\\n (start_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT);\\r\\n var end_dir =\\r\\n end_slot.dir ||\\r\\n (node.horizontal ? LiteGraph.UP : LiteGraph.LEFT);\\r\\n\\r\\n this.renderLink(\\r\\n ctx,\\r\\n start_node_slotpos,\\r\\n end_node_slotpos,\\r\\n link,\\r\\n false,\\r\\n 0,\\r\\n null,\\r\\n start_dir,\\r\\n end_dir\\r\\n );\\r\\n\\r\\n //event triggered rendered on top\\r\\n if (link && link._last_time && now - link._last_time < 1000) {\\r\\n var f = 2.0 - (now - link._last_time) * 0.002;\\r\\n var tmp = ctx.globalAlpha;\\r\\n ctx.globalAlpha = tmp * f;\\r\\n this.renderLink(\\r\\n ctx,\\r\\n start_node_slotpos,\\r\\n end_node_slotpos,\\r\\n link,\\r\\n true,\\r\\n f,\\r\\n \\\"white\\\",\\r\\n start_dir,\\r\\n end_dir\\r\\n );\\r\\n ctx.globalAlpha = tmp;\\r\\n }\\r\\n }\\r\\n }\\r\\n ctx.globalAlpha = 1;\\r\\n };\\r\\n\\r\\n /**\\r\\n * draws a link between two points\\r\\n * @method renderLink\\r\\n * @param {vec2} a start pos\\r\\n * @param {vec2} b end pos\\r\\n * @param {Object} link the link object with all the link info\\r\\n * @param {boolean} skip_border ignore the shadow of the link\\r\\n * @param {boolean} flow show flow animation (for events)\\r\\n * @param {string} color the color for the link\\r\\n * @param {number} start_dir the direction enum\\r\\n * @param {number} end_dir the direction enum\\r\\n * @param {number} num_sublines number of sublines (useful to represent vec3 or rgb)\\r\\n **/\\r\\n LGraphCanvas.prototype.renderLink = function(\\r\\n ctx,\\r\\n a,\\r\\n b,\\r\\n link,\\r\\n skip_border,\\r\\n flow,\\r\\n color,\\r\\n start_dir,\\r\\n end_dir,\\r\\n num_sublines\\r\\n ) {\\r\\n if (link) {\\r\\n this.visible_links.push(link);\\r\\n }\\r\\n\\r\\n //choose color\\r\\n if (!color && link) {\\r\\n color = link.color || LGraphCanvas.link_type_colors[link.type];\\r\\n }\\r\\n if (!color) {\\r\\n color = this.default_link_color;\\r\\n }\\r\\n if (link != null && this.highlighted_links[link.id]) {\\r\\n color = \\\"#FFF\\\";\\r\\n }\\r\\n\\r\\n start_dir = start_dir || LiteGraph.RIGHT;\\r\\n end_dir = end_dir || LiteGraph.LEFT;\\r\\n\\r\\n var dist = distance(a, b);\\r\\n\\r\\n if (this.render_connections_border && this.ds.scale > 0.6) {\\r\\n ctx.lineWidth = this.connections_width + 4;\\r\\n }\\r\\n ctx.lineJoin = \\\"round\\\";\\r\\n num_sublines = num_sublines || 1;\\r\\n if (num_sublines > 1) {\\r\\n ctx.lineWidth = 0.5;\\r\\n }\\r\\n\\r\\n //begin line shape\\r\\n ctx.beginPath();\\r\\n for (var i = 0; i < num_sublines; i += 1) {\\r\\n var offsety = (i - (num_sublines - 1) * 0.5) * 5;\\r\\n\\r\\n if (this.links_render_mode == LiteGraph.SPLINE_LINK) {\\r\\n ctx.moveTo(a[0], a[1] + offsety);\\r\\n var start_offset_x = 0;\\r\\n var start_offset_y = 0;\\r\\n var end_offset_x = 0;\\r\\n var end_offset_y = 0;\\r\\n switch (start_dir) {\\r\\n case LiteGraph.LEFT:\\r\\n start_offset_x = dist * -0.25;\\r\\n break;\\r\\n case LiteGraph.RIGHT:\\r\\n start_offset_x = dist * 0.25;\\r\\n break;\\r\\n case LiteGraph.UP:\\r\\n start_offset_y = dist * -0.25;\\r\\n break;\\r\\n case LiteGraph.DOWN:\\r\\n start_offset_y = dist * 0.25;\\r\\n break;\\r\\n }\\r\\n switch (end_dir) {\\r\\n case LiteGraph.LEFT:\\r\\n end_offset_x = dist * -0.25;\\r\\n break;\\r\\n case LiteGraph.RIGHT:\\r\\n end_offset_x = dist * 0.25;\\r\\n break;\\r\\n case LiteGraph.UP:\\r\\n end_offset_y = dist * -0.25;\\r\\n break;\\r\\n case LiteGraph.DOWN:\\r\\n end_offset_y = dist * 0.25;\\r\\n break;\\r\\n }\\r\\n ctx.bezierCurveTo(\\r\\n a[0] + start_offset_x,\\r\\n a[1] + start_offset_y + offsety,\\r\\n b[0] + end_offset_x,\\r\\n b[1] + end_offset_y + offsety,\\r\\n b[0],\\r\\n b[1] + offsety\\r\\n );\\r\\n } else if (this.links_render_mode == LiteGraph.LINEAR_LINK) {\\r\\n ctx.moveTo(a[0], a[1] + offsety);\\r\\n var start_offset_x = 0;\\r\\n var start_offset_y = 0;\\r\\n var end_offset_x = 0;\\r\\n var end_offset_y = 0;\\r\\n switch (start_dir) {\\r\\n case LiteGraph.LEFT:\\r\\n start_offset_x = -1;\\r\\n break;\\r\\n case LiteGraph.RIGHT:\\r\\n start_offset_x = 1;\\r\\n break;\\r\\n case LiteGraph.UP:\\r\\n start_offset_y = -1;\\r\\n break;\\r\\n case LiteGraph.DOWN:\\r\\n start_offset_y = 1;\\r\\n break;\\r\\n }\\r\\n switch (end_dir) {\\r\\n case LiteGraph.LEFT:\\r\\n end_offset_x = -1;\\r\\n break;\\r\\n case LiteGraph.RIGHT:\\r\\n end_offset_x = 1;\\r\\n break;\\r\\n case LiteGraph.UP:\\r\\n end_offset_y = -1;\\r\\n break;\\r\\n case LiteGraph.DOWN:\\r\\n end_offset_y = 1;\\r\\n break;\\r\\n }\\r\\n var l = 15;\\r\\n ctx.lineTo(\\r\\n a[0] + start_offset_x * l,\\r\\n a[1] + start_offset_y * l + offsety\\r\\n );\\r\\n ctx.lineTo(\\r\\n b[0] + end_offset_x * l,\\r\\n b[1] + end_offset_y * l + offsety\\r\\n );\\r\\n ctx.lineTo(b[0], b[1] + offsety);\\r\\n } else if (this.links_render_mode == LiteGraph.STRAIGHT_LINK) {\\r\\n ctx.moveTo(a[0], a[1]);\\r\\n var start_x = a[0];\\r\\n var start_y = a[1];\\r\\n var end_x = b[0];\\r\\n var end_y = b[1];\\r\\n if (start_dir == LiteGraph.RIGHT) {\\r\\n start_x += 10;\\r\\n } else {\\r\\n start_y += 10;\\r\\n }\\r\\n if (end_dir == LiteGraph.LEFT) {\\r\\n end_x -= 10;\\r\\n } else {\\r\\n end_y -= 10;\\r\\n }\\r\\n ctx.lineTo(start_x, start_y);\\r\\n ctx.lineTo((start_x + end_x) * 0.5, start_y);\\r\\n ctx.lineTo((start_x + end_x) * 0.5, end_y);\\r\\n ctx.lineTo(end_x, end_y);\\r\\n ctx.lineTo(b[0], b[1]);\\r\\n } else {\\r\\n return;\\r\\n } //unknown\\r\\n }\\r\\n\\r\\n //rendering the outline of the connection can be a little bit slow\\r\\n if (\\r\\n this.render_connections_border &&\\r\\n this.ds.scale > 0.6 &&\\r\\n !skip_border\\r\\n ) {\\r\\n ctx.strokeStyle = \\\"rgba(0,0,0,0.5)\\\";\\r\\n ctx.stroke();\\r\\n }\\r\\n\\r\\n ctx.lineWidth = this.connections_width;\\r\\n ctx.fillStyle = ctx.strokeStyle = color;\\r\\n ctx.stroke();\\r\\n //end line shape\\r\\n\\r\\n var pos = this.computeConnectionPoint(a, b, 0.5, start_dir, end_dir);\\r\\n if (link && link._pos) {\\r\\n link._pos[0] = pos[0];\\r\\n link._pos[1] = pos[1];\\r\\n }\\r\\n\\r\\n //render arrow in the middle\\r\\n if (\\r\\n this.ds.scale >= 0.6 &&\\r\\n this.highquality_render &&\\r\\n end_dir != LiteGraph.CENTER\\r\\n ) {\\r\\n //render arrow\\r\\n if (this.render_connection_arrows) {\\r\\n //compute two points in the connection\\r\\n var posA = this.computeConnectionPoint(\\r\\n a,\\r\\n b,\\r\\n 0.25,\\r\\n start_dir,\\r\\n end_dir\\r\\n );\\r\\n var posB = this.computeConnectionPoint(\\r\\n a,\\r\\n b,\\r\\n 0.26,\\r\\n start_dir,\\r\\n end_dir\\r\\n );\\r\\n var posC = this.computeConnectionPoint(\\r\\n a,\\r\\n b,\\r\\n 0.75,\\r\\n start_dir,\\r\\n end_dir\\r\\n );\\r\\n var posD = this.computeConnectionPoint(\\r\\n a,\\r\\n b,\\r\\n 0.76,\\r\\n start_dir,\\r\\n end_dir\\r\\n );\\r\\n\\r\\n //compute the angle between them so the arrow points in the right direction\\r\\n var angleA = 0;\\r\\n var angleB = 0;\\r\\n if (this.render_curved_connections) {\\r\\n angleA = -Math.atan2(posB[0] - posA[0], posB[1] - posA[1]);\\r\\n angleB = -Math.atan2(posD[0] - posC[0], posD[1] - posC[1]);\\r\\n } else {\\r\\n angleB = angleA = b[1] > a[1] ? 0 : Math.PI;\\r\\n }\\r\\n\\r\\n //render arrow\\r\\n ctx.save();\\r\\n ctx.translate(posA[0], posA[1]);\\r\\n ctx.rotate(angleA);\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(-5, -3);\\r\\n ctx.lineTo(0, +7);\\r\\n ctx.lineTo(+5, -3);\\r\\n ctx.fill();\\r\\n ctx.restore();\\r\\n ctx.save();\\r\\n ctx.translate(posC[0], posC[1]);\\r\\n ctx.rotate(angleB);\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(-5, -3);\\r\\n ctx.lineTo(0, +7);\\r\\n ctx.lineTo(+5, -3);\\r\\n ctx.fill();\\r\\n ctx.restore();\\r\\n }\\r\\n\\r\\n //circle\\r\\n ctx.beginPath();\\r\\n ctx.arc(pos[0], pos[1], 5, 0, Math.PI * 2);\\r\\n ctx.fill();\\r\\n }\\r\\n\\r\\n //render flowing points\\r\\n if (flow) {\\r\\n ctx.fillStyle = color;\\r\\n for (var i = 0; i < 5; ++i) {\\r\\n var f = (LiteGraph.getTime() * 0.001 + i * 0.2) % 1;\\r\\n var pos = this.computeConnectionPoint(\\r\\n a,\\r\\n b,\\r\\n f,\\r\\n start_dir,\\r\\n end_dir\\r\\n );\\r\\n ctx.beginPath();\\r\\n ctx.arc(pos[0], pos[1], 5, 0, 2 * Math.PI);\\r\\n ctx.fill();\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n //returns the link center point based on curvature\\r\\n LGraphCanvas.prototype.computeConnectionPoint = function(\\r\\n a,\\r\\n b,\\r\\n t,\\r\\n start_dir,\\r\\n end_dir\\r\\n ) {\\r\\n start_dir = start_dir || LiteGraph.RIGHT;\\r\\n end_dir = end_dir || LiteGraph.LEFT;\\r\\n\\r\\n var dist = distance(a, b);\\r\\n var p0 = a;\\r\\n var p1 = [a[0], a[1]];\\r\\n var p2 = [b[0], b[1]];\\r\\n var p3 = b;\\r\\n\\r\\n switch (start_dir) {\\r\\n case LiteGraph.LEFT:\\r\\n p1[0] += dist * -0.25;\\r\\n break;\\r\\n case LiteGraph.RIGHT:\\r\\n p1[0] += dist * 0.25;\\r\\n break;\\r\\n case LiteGraph.UP:\\r\\n p1[1] += dist * -0.25;\\r\\n break;\\r\\n case LiteGraph.DOWN:\\r\\n p1[1] += dist * 0.25;\\r\\n break;\\r\\n }\\r\\n switch (end_dir) {\\r\\n case LiteGraph.LEFT:\\r\\n p2[0] += dist * -0.25;\\r\\n break;\\r\\n case LiteGraph.RIGHT:\\r\\n p2[0] += dist * 0.25;\\r\\n break;\\r\\n case LiteGraph.UP:\\r\\n p2[1] += dist * -0.25;\\r\\n break;\\r\\n case LiteGraph.DOWN:\\r\\n p2[1] += dist * 0.25;\\r\\n break;\\r\\n }\\r\\n\\r\\n var c1 = (1 - t) * (1 - t) * (1 - t);\\r\\n var c2 = 3 * ((1 - t) * (1 - t)) * t;\\r\\n var c3 = 3 * (1 - t) * (t * t);\\r\\n var c4 = t * t * t;\\r\\n\\r\\n var x = c1 * p0[0] + c2 * p1[0] + c3 * p2[0] + c4 * p3[0];\\r\\n var y = c1 * p0[1] + c2 * p1[1] + c3 * p2[1] + c4 * p3[1];\\r\\n return [x, y];\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.drawExecutionOrder = function(ctx) {\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n ctx.globalAlpha = 0.25;\\r\\n\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n ctx.strokeStyle = \\\"white\\\";\\r\\n ctx.globalAlpha = 0.75;\\r\\n\\r\\n var visible_nodes = this.visible_nodes;\\r\\n for (var i = 0; i < visible_nodes.length; ++i) {\\r\\n var node = visible_nodes[i];\\r\\n ctx.fillStyle = \\\"black\\\";\\r\\n ctx.fillRect(\\r\\n node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT,\\r\\n node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT,\\r\\n LiteGraph.NODE_TITLE_HEIGHT,\\r\\n LiteGraph.NODE_TITLE_HEIGHT\\r\\n );\\r\\n if (node.order == 0) {\\r\\n ctx.strokeRect(\\r\\n node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5,\\r\\n node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5,\\r\\n LiteGraph.NODE_TITLE_HEIGHT,\\r\\n LiteGraph.NODE_TITLE_HEIGHT\\r\\n );\\r\\n }\\r\\n ctx.fillStyle = \\\"#FFF\\\";\\r\\n ctx.fillText(\\r\\n node.order,\\r\\n node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5,\\r\\n node.pos[1] - 6\\r\\n );\\r\\n }\\r\\n ctx.globalAlpha = 1;\\r\\n };\\r\\n\\r\\n /**\\r\\n * draws the widgets stored inside a node\\r\\n * @method drawNodeWidgets\\r\\n **/\\r\\n LGraphCanvas.prototype.drawNodeWidgets = function(\\r\\n node,\\r\\n posY,\\r\\n ctx,\\r\\n active_widget\\r\\n ) {\\r\\n if (!node.widgets || !node.widgets.length) {\\r\\n return 0;\\r\\n }\\r\\n var width = node.size[0];\\r\\n var widgets = node.widgets;\\r\\n posY += 2;\\r\\n var H = LiteGraph.NODE_WIDGET_HEIGHT;\\r\\n var show_text = this.ds.scale > 0.5;\\r\\n ctx.save();\\r\\n ctx.globalAlpha = this.editor_alpha;\\r\\n var outline_color = \\\"#666\\\";\\r\\n var background_color = \\\"#222\\\";\\r\\n var margin = 15;\\r\\n\\r\\n for (var i = 0; i < widgets.length; ++i) {\\r\\n var w = widgets[i];\\r\\n var y = posY;\\r\\n if (w.y) {\\r\\n y = w.y;\\r\\n }\\r\\n w.last_y = y;\\r\\n ctx.strokeStyle = outline_color;\\r\\n ctx.fillStyle = \\\"#222\\\";\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n\\r\\n switch (w.type) {\\r\\n case \\\"button\\\":\\r\\n if (w.clicked) {\\r\\n ctx.fillStyle = \\\"#AAA\\\";\\r\\n w.clicked = false;\\r\\n this.dirty_canvas = true;\\r\\n }\\r\\n ctx.fillRect(margin, y, width - margin * 2, H);\\r\\n ctx.strokeRect(margin, y, width - margin * 2, H);\\r\\n if (show_text) {\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n ctx.fillStyle = \\\"#AAA\\\";\\r\\n ctx.fillText(w.name, width * 0.5, y + H * 0.7);\\r\\n }\\r\\n break;\\r\\n case \\\"toggle\\\":\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n ctx.strokeStyle = outline_color;\\r\\n ctx.fillStyle = background_color;\\r\\n ctx.beginPath();\\r\\n ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5);\\r\\n ctx.fill();\\r\\n ctx.stroke();\\r\\n ctx.fillStyle = w.value ? \\\"#89A\\\" : \\\"#333\\\";\\r\\n ctx.beginPath();\\r\\n ctx.arc(\\r\\n width - margin * 2,\\r\\n y + H * 0.5,\\r\\n H * 0.36,\\r\\n 0,\\r\\n Math.PI * 2\\r\\n );\\r\\n ctx.fill();\\r\\n if (show_text) {\\r\\n ctx.fillStyle = \\\"#999\\\";\\r\\n if (w.name != null) {\\r\\n ctx.fillText(w.name, margin * 2, y + H * 0.7);\\r\\n }\\r\\n ctx.fillStyle = w.value ? \\\"#DDD\\\" : \\\"#888\\\";\\r\\n ctx.textAlign = \\\"right\\\";\\r\\n ctx.fillText(\\r\\n w.value\\r\\n ? w.options.on || \\\"true\\\"\\r\\n : w.options.off || \\\"false\\\",\\r\\n width - 40,\\r\\n y + H * 0.7\\r\\n );\\r\\n }\\r\\n break;\\r\\n case \\\"slider\\\":\\r\\n ctx.fillStyle = background_color;\\r\\n ctx.fillRect(margin, y, width - margin * 2, H);\\r\\n var range = w.options.max - w.options.min;\\r\\n var nvalue = (w.value - w.options.min) / range;\\r\\n ctx.fillStyle = active_widget == w ? \\\"#89A\\\" : \\\"#678\\\";\\r\\n ctx.fillRect(margin, y, nvalue * (width - margin * 2), H);\\r\\n ctx.strokeRect(margin, y, width - margin * 2, H);\\r\\n if (w.marker) {\\r\\n var marker_nvalue = (w.marker - w.options.min) / range;\\r\\n ctx.fillStyle = \\\"#AA9\\\";\\r\\n ctx.fillRect(\\r\\n margin + marker_nvalue * (width - margin * 2),\\r\\n y,\\r\\n 2,\\r\\n H\\r\\n );\\r\\n }\\r\\n if (show_text) {\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n ctx.fillStyle = \\\"#DDD\\\";\\r\\n ctx.fillText(\\r\\n w.name + \\\" \\\" + Number(w.value).toFixed(3),\\r\\n width * 0.5,\\r\\n y + H * 0.7\\r\\n );\\r\\n }\\r\\n break;\\r\\n case \\\"number\\\":\\r\\n case \\\"combo\\\":\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n ctx.strokeStyle = outline_color;\\r\\n ctx.fillStyle = background_color;\\r\\n ctx.beginPath();\\r\\n ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5);\\r\\n ctx.fill();\\r\\n ctx.stroke();\\r\\n if (show_text) {\\r\\n ctx.fillStyle = \\\"#AAA\\\";\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(margin + 16, posY + 5);\\r\\n ctx.lineTo(margin + 6, posY + H * 0.5);\\r\\n ctx.lineTo(margin + 16, posY + H - 5);\\r\\n ctx.moveTo(width - margin - 16, posY + 5);\\r\\n ctx.lineTo(width - margin - 6, posY + H * 0.5);\\r\\n ctx.lineTo(width - margin - 16, posY + H - 5);\\r\\n ctx.fill();\\r\\n ctx.fillStyle = \\\"#999\\\";\\r\\n ctx.fillText(w.name, margin * 2 + 5, y + H * 0.7);\\r\\n ctx.fillStyle = \\\"#DDD\\\";\\r\\n ctx.textAlign = \\\"right\\\";\\r\\n if (w.type == \\\"number\\\") {\\r\\n ctx.fillText(\\r\\n Number(w.value).toFixed(\\r\\n w.options.precision !== undefined\\r\\n ? w.options.precision\\r\\n : 3\\r\\n ),\\r\\n width - margin * 2 - 20,\\r\\n y + H * 0.7\\r\\n );\\r\\n } else {\\r\\n ctx.fillText(\\r\\n w.value,\\r\\n width - margin * 2 - 20,\\r\\n y + H * 0.7\\r\\n );\\r\\n }\\r\\n }\\r\\n break;\\r\\n case \\\"string\\\":\\r\\n case \\\"text\\\":\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n ctx.strokeStyle = outline_color;\\r\\n ctx.fillStyle = background_color;\\r\\n ctx.beginPath();\\r\\n ctx.roundRect(margin, posY, width - margin * 2, H, H * 0.5);\\r\\n ctx.fill();\\r\\n ctx.stroke();\\r\\n if (show_text) {\\r\\n ctx.fillStyle = \\\"#999\\\";\\r\\n if (w.name != null) {\\r\\n ctx.fillText(w.name, margin * 2, y + H * 0.7);\\r\\n }\\r\\n ctx.fillStyle = \\\"#DDD\\\";\\r\\n ctx.textAlign = \\\"right\\\";\\r\\n ctx.fillText(w.value, width - margin * 2, y + H * 0.7);\\r\\n }\\r\\n break;\\r\\n default:\\r\\n if (w.draw) {\\r\\n w.draw(ctx, node, w, y, H);\\r\\n }\\r\\n break;\\r\\n }\\r\\n posY += H + 4;\\r\\n }\\r\\n ctx.restore();\\r\\n };\\r\\n\\r\\n /**\\r\\n * process an event on widgets\\r\\n * @method processNodeWidgets\\r\\n **/\\r\\n LGraphCanvas.prototype.processNodeWidgets = function(\\r\\n node,\\r\\n pos,\\r\\n event,\\r\\n active_widget\\r\\n ) {\\r\\n if (!node.widgets || !node.widgets.length) {\\r\\n return null;\\r\\n }\\r\\n\\r\\n var x = pos[0] - node.pos[0];\\r\\n var y = pos[1] - node.pos[1];\\r\\n var width = node.size[0];\\r\\n var that = this;\\r\\n var ref_window = this.getCanvasWindow();\\r\\n\\r\\n for (var i = 0; i < node.widgets.length; ++i) {\\r\\n var w = node.widgets[i];\\r\\n if (\\r\\n w == active_widget ||\\r\\n (x > 6 &&\\r\\n x < width - 12 &&\\r\\n y > w.last_y &&\\r\\n y < w.last_y + LiteGraph.NODE_WIDGET_HEIGHT)\\r\\n ) {\\r\\n //inside widget\\r\\n switch (w.type) {\\r\\n case \\\"button\\\":\\r\\n if (event.type === \\\"mousemove\\\") {\\r\\n break;\\r\\n }\\r\\n if (w.callback) {\\r\\n setTimeout(function() {\\r\\n w.callback(w, that, node, pos);\\r\\n }, 20);\\r\\n }\\r\\n w.clicked = true;\\r\\n this.dirty_canvas = true;\\r\\n break;\\r\\n case \\\"slider\\\":\\r\\n var range = w.options.max - w.options.min;\\r\\n var nvalue = Math.clamp((x - 10) / (width - 20), 0, 1);\\r\\n w.value =\\r\\n w.options.min +\\r\\n (w.options.max - w.options.min) * nvalue;\\r\\n if (w.callback) {\\r\\n setTimeout(function() {\\r\\n inner_value_change(w, w.value);\\r\\n }, 20);\\r\\n }\\r\\n this.dirty_canvas = true;\\r\\n break;\\r\\n case \\\"number\\\":\\r\\n case \\\"combo\\\":\\r\\n if (event.type == \\\"mousemove\\\" && w.type == \\\"number\\\") {\\r\\n w.value +=\\r\\n event.deltaX * 0.1 * (w.options.step || 1);\\r\\n if (\\r\\n w.options.min != null &&\\r\\n w.value < w.options.min\\r\\n ) {\\r\\n w.value = w.options.min;\\r\\n }\\r\\n if (\\r\\n w.options.max != null &&\\r\\n w.value > w.options.max\\r\\n ) {\\r\\n w.value = w.options.max;\\r\\n }\\r\\n } else if (event.type == \\\"mousedown\\\") {\\r\\n var values = w.options.values;\\r\\n if (values && values.constructor === Function) {\\r\\n values = w.options.values(w, node);\\r\\n }\\r\\n\\r\\n var delta = x < 40 ? -1 : x > width - 40 ? 1 : 0;\\r\\n if (w.type == \\\"number\\\") {\\r\\n w.value += delta * 0.1 * (w.options.step || 1);\\r\\n if (\\r\\n w.options.min != null &&\\r\\n w.value < w.options.min\\r\\n ) {\\r\\n w.value = w.options.min;\\r\\n }\\r\\n if (\\r\\n w.options.max != null &&\\r\\n w.value > w.options.max\\r\\n ) {\\r\\n w.value = w.options.max;\\r\\n }\\r\\n } else if (delta) {\\r\\n var index = values.indexOf(w.value) + delta;\\r\\n if (index >= values.length) {\\r\\n index = 0;\\r\\n }\\r\\n if (index < 0) {\\r\\n index = values.length - 1;\\r\\n }\\r\\n w.value = values[index];\\r\\n } else {\\r\\n var menu = new LiteGraph.ContextMenu(\\r\\n values,\\r\\n {\\r\\n scale: Math.max(1, this.ds.scale),\\r\\n event: event,\\r\\n className: \\\"dark\\\",\\r\\n callback: inner_clicked.bind(w)\\r\\n },\\r\\n ref_window\\r\\n );\\r\\n function inner_clicked(v, option, event) {\\r\\n this.value = v;\\r\\n inner_value_change(this, v);\\r\\n that.dirty_canvas = true;\\r\\n return false;\\r\\n }\\r\\n }\\r\\n }\\r\\n setTimeout(\\r\\n function() {\\r\\n inner_value_change(this, this.value);\\r\\n }.bind(w),\\r\\n 20\\r\\n );\\r\\n this.dirty_canvas = true;\\r\\n break;\\r\\n case \\\"toggle\\\":\\r\\n if (event.type == \\\"mousedown\\\") {\\r\\n w.value = !w.value;\\r\\n if (w.callback) {\\r\\n setTimeout(function() {\\r\\n inner_value_change(w, w.value);\\r\\n }, 20);\\r\\n }\\r\\n }\\r\\n break;\\r\\n case \\\"string\\\":\\r\\n case \\\"text\\\":\\r\\n if (event.type == \\\"mousedown\\\") {\\r\\n this.prompt(\\r\\n \\\"Value\\\",\\r\\n w.value,\\r\\n function(v) {\\r\\n this.value = v;\\r\\n inner_value_change(this, v);\\r\\n }.bind(w),\\r\\n event\\r\\n );\\r\\n }\\r\\n break;\\r\\n default:\\r\\n if (w.mouse) {\\r\\n w.mouse(ctx, event, [x, y], node);\\r\\n }\\r\\n break;\\r\\n }\\r\\n\\r\\n return w;\\r\\n }\\r\\n }\\r\\n\\r\\n function inner_value_change(widget, value) {\\r\\n widget.value = value;\\r\\n if (\\r\\n widget.property &&\\r\\n node.properties[widget.property] !== undefined\\r\\n ) {\\r\\n node.properties[widget.property] = value;\\r\\n }\\r\\n if (widget.callback) {\\r\\n widget.callback(widget.value, that, node, pos, event);\\r\\n }\\r\\n }\\r\\n\\r\\n return null;\\r\\n };\\r\\n\\r\\n /**\\r\\n * draws every group area in the background\\r\\n * @method drawGroups\\r\\n **/\\r\\n LGraphCanvas.prototype.drawGroups = function(canvas, ctx) {\\r\\n if (!this.graph) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var groups = this.graph._groups;\\r\\n\\r\\n ctx.save();\\r\\n ctx.globalAlpha = 0.5 * this.editor_alpha;\\r\\n\\r\\n for (var i = 0; i < groups.length; ++i) {\\r\\n var group = groups[i];\\r\\n\\r\\n if (!overlapBounding(this.visible_area, group._bounding)) {\\r\\n continue;\\r\\n } //out of the visible area\\r\\n\\r\\n ctx.fillStyle = group.color || \\\"#335\\\";\\r\\n ctx.strokeStyle = group.color || \\\"#335\\\";\\r\\n var pos = group._pos;\\r\\n var size = group._size;\\r\\n ctx.globalAlpha = 0.25 * this.editor_alpha;\\r\\n ctx.beginPath();\\r\\n ctx.rect(pos[0] + 0.5, pos[1] + 0.5, size[0], size[1]);\\r\\n ctx.fill();\\r\\n ctx.globalAlpha = this.editor_alpha;\\r\\n ctx.stroke();\\r\\n\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(pos[0] + size[0], pos[1] + size[1]);\\r\\n ctx.lineTo(pos[0] + size[0] - 10, pos[1] + size[1]);\\r\\n ctx.lineTo(pos[0] + size[0], pos[1] + size[1] - 10);\\r\\n ctx.fill();\\r\\n\\r\\n var font_size =\\r\\n group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE;\\r\\n ctx.font = font_size + \\\"px Arial\\\";\\r\\n ctx.fillText(group.title, pos[0] + 4, pos[1] + font_size);\\r\\n }\\r\\n\\r\\n ctx.restore();\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.adjustNodesSize = function() {\\r\\n var nodes = this.graph._nodes;\\r\\n for (var i = 0; i < nodes.length; ++i) {\\r\\n nodes[i].size = nodes[i].computeSize();\\r\\n }\\r\\n this.setDirty(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode\\r\\n * @method resize\\r\\n **/\\r\\n LGraphCanvas.prototype.resize = function(width, height) {\\r\\n if (!width && !height) {\\r\\n var parent = this.canvas.parentNode;\\r\\n width = parent.offsetWidth;\\r\\n height = parent.offsetHeight;\\r\\n }\\r\\n\\r\\n if (this.canvas.width == width && this.canvas.height == height) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.canvas.width = width;\\r\\n this.canvas.height = height;\\r\\n this.bgcanvas.width = this.canvas.width;\\r\\n this.bgcanvas.height = this.canvas.height;\\r\\n this.setDirty(true, true);\\r\\n };\\r\\n\\r\\n /**\\r\\n * switches to live mode (node shapes are not rendered, only the content)\\r\\n * this feature was designed when graphs where meant to create user interfaces\\r\\n * @method switchLiveMode\\r\\n **/\\r\\n LGraphCanvas.prototype.switchLiveMode = function(transition) {\\r\\n if (!transition) {\\r\\n this.live_mode = !this.live_mode;\\r\\n this.dirty_canvas = true;\\r\\n this.dirty_bgcanvas = true;\\r\\n return;\\r\\n }\\r\\n\\r\\n var self = this;\\r\\n var delta = this.live_mode ? 1.1 : 0.9;\\r\\n if (this.live_mode) {\\r\\n this.live_mode = false;\\r\\n this.editor_alpha = 0.1;\\r\\n }\\r\\n\\r\\n var t = setInterval(function() {\\r\\n self.editor_alpha *= delta;\\r\\n self.dirty_canvas = true;\\r\\n self.dirty_bgcanvas = true;\\r\\n\\r\\n if (delta < 1 && self.editor_alpha < 0.01) {\\r\\n clearInterval(t);\\r\\n if (delta < 1) {\\r\\n self.live_mode = true;\\r\\n }\\r\\n }\\r\\n if (delta > 1 && self.editor_alpha > 0.99) {\\r\\n clearInterval(t);\\r\\n self.editor_alpha = 1;\\r\\n }\\r\\n }, 1);\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.onNodeSelectionChange = function(node) {\\r\\n return; //disabled\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.touchHandler = function(event) {\\r\\n //alert(\\\"foo\\\");\\r\\n var touches = event.changedTouches,\\r\\n first = touches[0],\\r\\n type = \\\"\\\";\\r\\n\\r\\n switch (event.type) {\\r\\n case \\\"touchstart\\\":\\r\\n type = \\\"mousedown\\\";\\r\\n break;\\r\\n case \\\"touchmove\\\":\\r\\n type = \\\"mousemove\\\";\\r\\n break;\\r\\n case \\\"touchend\\\":\\r\\n type = \\\"mouseup\\\";\\r\\n break;\\r\\n default:\\r\\n return;\\r\\n }\\r\\n\\r\\n //initMouseEvent(type, canBubble, cancelable, view, clickCount,\\r\\n // screenX, screenY, clientX, clientY, ctrlKey,\\r\\n // altKey, shiftKey, metaKey, button, relatedTarget);\\r\\n\\r\\n var window = this.getCanvasWindow();\\r\\n var document = window.document;\\r\\n\\r\\n var simulatedEvent = document.createEvent(\\\"MouseEvent\\\");\\r\\n simulatedEvent.initMouseEvent(\\r\\n type,\\r\\n true,\\r\\n true,\\r\\n window,\\r\\n 1,\\r\\n first.screenX,\\r\\n first.screenY,\\r\\n first.clientX,\\r\\n first.clientY,\\r\\n false,\\r\\n false,\\r\\n false,\\r\\n false,\\r\\n 0 /*left*/,\\r\\n null\\r\\n );\\r\\n first.target.dispatchEvent(simulatedEvent);\\r\\n event.preventDefault();\\r\\n };\\r\\n\\r\\n /* CONTEXT MENU ********************/\\r\\n\\r\\n LGraphCanvas.onGroupAdd = function(info, entry, mouse_event) {\\r\\n var canvas = LGraphCanvas.active_canvas;\\r\\n var ref_window = canvas.getCanvasWindow();\\r\\n\\r\\n var group = new LiteGraph.LGraphGroup();\\r\\n group.pos = canvas.convertEventToCanvasOffset(mouse_event);\\r\\n canvas.graph.add(group);\\r\\n };\\r\\n\\r\\n LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu) {\\r\\n var canvas = LGraphCanvas.active_canvas;\\r\\n var ref_window = canvas.getCanvasWindow();\\r\\n\\r\\n var values = LiteGraph.getNodeTypesCategories( canvas.filter );\\r\\n var entries = [];\\r\\n for (var i in values) {\\r\\n if (values[i]) {\\r\\n entries.push({ value: values[i], content: values[i], has_submenu: true });\\r\\n }\\r\\n }\\r\\n\\r\\n //show categories\\r\\n var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: inner_clicked, parentMenu: prev_menu }, ref_window );\\r\\n\\r\\n function inner_clicked(v, option, e) {\\r\\n var category = v.value;\\r\\n var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter );\\r\\n var values = [];\\r\\n for (var i in node_types) {\\r\\n if (!node_types[i].skip_list) {\\r\\n values.push({\\r\\n content: node_types[i].title,\\r\\n value: node_types[i].type\\r\\n });\\r\\n }\\r\\n }\\r\\n\\r\\n new LiteGraph.ContextMenu( values, { event: e, callback: inner_create, parentMenu: menu }, ref_window );\\r\\n return false;\\r\\n }\\r\\n\\r\\n function inner_create(v, e) {\\r\\n var first_event = prev_menu.getFirstEvent();\\r\\n var node = LiteGraph.createNode(v.value);\\r\\n if (node) {\\r\\n node.pos = canvas.convertEventToCanvasOffset(first_event);\\r\\n canvas.graph.add(node);\\r\\n }\\r\\n }\\r\\n\\r\\n return false;\\r\\n };\\r\\n\\r\\n LGraphCanvas.onMenuCollapseAll = function() {};\\r\\n\\r\\n LGraphCanvas.onMenuNodeEdit = function() {};\\r\\n\\r\\n LGraphCanvas.showMenuNodeOptionalInputs = function(\\r\\n v,\\r\\n options,\\r\\n e,\\r\\n prev_menu,\\r\\n node\\r\\n ) {\\r\\n if (!node) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var that = this;\\r\\n var canvas = LGraphCanvas.active_canvas;\\r\\n var ref_window = canvas.getCanvasWindow();\\r\\n\\r\\n var options = node.optional_inputs;\\r\\n if (node.onGetInputs) {\\r\\n options = node.onGetInputs();\\r\\n }\\r\\n\\r\\n var entries = [];\\r\\n if (options) {\\r\\n for (var i in options) {\\r\\n var entry = options[i];\\r\\n if (!entry) {\\r\\n entries.push(null);\\r\\n continue;\\r\\n }\\r\\n var label = entry[0];\\r\\n if (entry[2] && entry[2].label) {\\r\\n label = entry[2].label;\\r\\n }\\r\\n var data = { content: label, value: entry };\\r\\n if (entry[1] == LiteGraph.ACTION) {\\r\\n data.className = \\\"event\\\";\\r\\n }\\r\\n entries.push(data);\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.onMenuNodeInputs) {\\r\\n entries = this.onMenuNodeInputs(entries);\\r\\n }\\r\\n\\r\\n if (!entries.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var menu = new LiteGraph.ContextMenu(\\r\\n entries,\\r\\n {\\r\\n event: e,\\r\\n callback: inner_clicked,\\r\\n parentMenu: prev_menu,\\r\\n node: node\\r\\n },\\r\\n ref_window\\r\\n );\\r\\n\\r\\n function inner_clicked(v, e, prev) {\\r\\n if (!node) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (v.callback) {\\r\\n v.callback.call(that, node, v, e, prev);\\r\\n }\\r\\n\\r\\n if (v.value) {\\r\\n node.addInput(v.value[0], v.value[1], v.value[2]);\\r\\n node.setDirtyCanvas(true, true);\\r\\n }\\r\\n }\\r\\n\\r\\n return false;\\r\\n };\\r\\n\\r\\n LGraphCanvas.showMenuNodeOptionalOutputs = function(\\r\\n v,\\r\\n options,\\r\\n e,\\r\\n prev_menu,\\r\\n node\\r\\n ) {\\r\\n if (!node) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var that = this;\\r\\n var canvas = LGraphCanvas.active_canvas;\\r\\n var ref_window = canvas.getCanvasWindow();\\r\\n\\r\\n var options = node.optional_outputs;\\r\\n if (node.onGetOutputs) {\\r\\n options = node.onGetOutputs();\\r\\n }\\r\\n\\r\\n var entries = [];\\r\\n if (options) {\\r\\n for (var i in options) {\\r\\n var entry = options[i];\\r\\n if (!entry) {\\r\\n //separator?\\r\\n entries.push(null);\\r\\n continue;\\r\\n }\\r\\n\\r\\n if (\\r\\n node.flags &&\\r\\n node.flags.skip_repeated_outputs &&\\r\\n node.findOutputSlot(entry[0]) != -1\\r\\n ) {\\r\\n continue;\\r\\n } //skip the ones already on\\r\\n var label = entry[0];\\r\\n if (entry[2] && entry[2].label) {\\r\\n label = entry[2].label;\\r\\n }\\r\\n var data = { content: label, value: entry };\\r\\n if (entry[1] == LiteGraph.EVENT) {\\r\\n data.className = \\\"event\\\";\\r\\n }\\r\\n entries.push(data);\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.onMenuNodeOutputs) {\\r\\n entries = this.onMenuNodeOutputs(entries);\\r\\n }\\r\\n\\r\\n if (!entries.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var menu = new LiteGraph.ContextMenu(\\r\\n entries,\\r\\n {\\r\\n event: e,\\r\\n callback: inner_clicked,\\r\\n parentMenu: prev_menu,\\r\\n node: node\\r\\n },\\r\\n ref_window\\r\\n );\\r\\n\\r\\n function inner_clicked(v, e, prev) {\\r\\n if (!node) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (v.callback) {\\r\\n v.callback.call(that, node, v, e, prev);\\r\\n }\\r\\n\\r\\n if (!v.value) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var value = v.value[1];\\r\\n\\r\\n if (\\r\\n value &&\\r\\n (value.constructor === Object || value.constructor === Array)\\r\\n ) {\\r\\n //submenu why?\\r\\n var entries = [];\\r\\n for (var i in value) {\\r\\n entries.push({ content: i, value: value[i] });\\r\\n }\\r\\n new LiteGraph.ContextMenu(entries, {\\r\\n event: e,\\r\\n callback: inner_clicked,\\r\\n parentMenu: prev_menu,\\r\\n node: node\\r\\n });\\r\\n return false;\\r\\n } else {\\r\\n node.addOutput(v.value[0], v.value[1], v.value[2]);\\r\\n node.setDirtyCanvas(true, true);\\r\\n }\\r\\n }\\r\\n\\r\\n return false;\\r\\n };\\r\\n\\r\\n LGraphCanvas.onShowMenuNodeProperties = function(\\r\\n value,\\r\\n options,\\r\\n e,\\r\\n prev_menu,\\r\\n node\\r\\n ) {\\r\\n if (!node || !node.properties) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var that = this;\\r\\n var canvas = LGraphCanvas.active_canvas;\\r\\n var ref_window = canvas.getCanvasWindow();\\r\\n\\r\\n var entries = [];\\r\\n for (var i in node.properties) {\\r\\n var value =\\r\\n node.properties[i] !== undefined ? node.properties[i] : \\\" \\\";\\r\\n //value could contain invalid html characters, clean that\\r\\n value = LGraphCanvas.decodeHTML(value);\\r\\n entries.push({\\r\\n content:\\r\\n \\\"\\\" +\\r\\n i +\\r\\n \\\"\\\" +\\r\\n \\\"\\\" +\\r\\n value +\\r\\n \\\"\\\",\\r\\n value: i\\r\\n });\\r\\n }\\r\\n if (!entries.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var menu = new LiteGraph.ContextMenu(\\r\\n entries,\\r\\n {\\r\\n event: e,\\r\\n callback: inner_clicked,\\r\\n parentMenu: prev_menu,\\r\\n allow_html: true,\\r\\n node: node\\r\\n },\\r\\n ref_window\\r\\n );\\r\\n\\r\\n function inner_clicked(v, options, e, prev) {\\r\\n if (!node) {\\r\\n return;\\r\\n }\\r\\n var rect = this.getBoundingClientRect();\\r\\n canvas.showEditPropertyValue(node, v.value, {\\r\\n position: [rect.left, rect.top]\\r\\n });\\r\\n }\\r\\n\\r\\n return false;\\r\\n };\\r\\n\\r\\n LGraphCanvas.decodeHTML = function(str) {\\r\\n var e = document.createElement(\\\"div\\\");\\r\\n e.innerText = str;\\r\\n return e.innerHTML;\\r\\n };\\r\\n\\r\\n LGraphCanvas.onResizeNode = function(value, options, e, menu, node) {\\r\\n if (!node) {\\r\\n return;\\r\\n }\\r\\n node.size = node.computeSize();\\r\\n node.setDirtyCanvas(true, true);\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.showLinkMenu = function(link, e) {\\r\\n var that = this;\\r\\n\\t\\tconsole.log(link.data);\\r\\n new LiteGraph.ContextMenu([\\\"Delete\\\"], {\\r\\n event: e,\\r\\n\\t\\t\\ttitle: link.data != null ? link.data.constructor.name : null,\\r\\n callback: inner_clicked\\r\\n });\\r\\n\\r\\n function inner_clicked(v) {\\r\\n switch (v) {\\r\\n case \\\"Delete\\\":\\r\\n that.graph.removeLink(link.id);\\r\\n break;\\r\\n default:\\r\\n }\\r\\n }\\r\\n\\r\\n return false;\\r\\n };\\r\\n\\r\\n LGraphCanvas.onShowPropertyEditor = function(item, options, e, menu, node) {\\r\\n var input_html = \\\"\\\";\\r\\n var property = item.property || \\\"title\\\";\\r\\n var value = node[property];\\r\\n\\r\\n var dialog = document.createElement(\\\"div\\\");\\r\\n dialog.className = \\\"graphdialog\\\";\\r\\n dialog.innerHTML =\\r\\n \\\"\\\";\\r\\n var title = dialog.querySelector(\\\".name\\\");\\r\\n title.innerText = property;\\r\\n var input = dialog.querySelector(\\\"input\\\");\\r\\n if (input) {\\r\\n input.value = value;\\r\\n input.addEventListener(\\\"blur\\\", function(e) {\\r\\n this.focus();\\r\\n });\\r\\n input.addEventListener(\\\"keydown\\\", function(e) {\\r\\n if (e.keyCode != 13) {\\r\\n return;\\r\\n }\\r\\n inner();\\r\\n e.preventDefault();\\r\\n e.stopPropagation();\\r\\n });\\r\\n }\\r\\n\\r\\n var graphcanvas = LGraphCanvas.active_canvas;\\r\\n var canvas = graphcanvas.canvas;\\r\\n\\r\\n var rect = canvas.getBoundingClientRect();\\r\\n var offsetx = -20;\\r\\n var offsety = -20;\\r\\n if (rect) {\\r\\n offsetx -= rect.left;\\r\\n offsety -= rect.top;\\r\\n }\\r\\n\\r\\n if (event) {\\r\\n dialog.style.left = event.clientX + offsetx + \\\"px\\\";\\r\\n dialog.style.top = event.clientY + offsety + \\\"px\\\";\\r\\n } else {\\r\\n dialog.style.left = canvas.width * 0.5 + offsetx + \\\"px\\\";\\r\\n dialog.style.top = canvas.height * 0.5 + offsety + \\\"px\\\";\\r\\n }\\r\\n\\r\\n var button = dialog.querySelector(\\\"button\\\");\\r\\n button.addEventListener(\\\"click\\\", inner);\\r\\n canvas.parentNode.appendChild(dialog);\\r\\n\\r\\n function inner() {\\r\\n setValue(input.value);\\r\\n }\\r\\n\\r\\n function setValue(value) {\\r\\n if (item.type == \\\"Number\\\") {\\r\\n value = Number(value);\\r\\n } else if (item.type == \\\"Boolean\\\") {\\r\\n value = Boolean(value);\\r\\n }\\r\\n node[property] = value;\\r\\n if (dialog.parentNode) {\\r\\n dialog.parentNode.removeChild(dialog);\\r\\n }\\r\\n node.setDirtyCanvas(true, true);\\r\\n }\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.prompt = function(title, value, callback, event) {\\r\\n var that = this;\\r\\n var input_html = \\\"\\\";\\r\\n title = title || \\\"\\\";\\r\\n\\r\\n var modified = false;\\r\\n\\r\\n var dialog = document.createElement(\\\"div\\\");\\r\\n dialog.className = \\\"graphdialog rounded\\\";\\r\\n dialog.innerHTML =\\r\\n \\\" \\\";\\r\\n dialog.close = function() {\\r\\n that.prompt_box = null;\\r\\n if (dialog.parentNode) {\\r\\n dialog.parentNode.removeChild(dialog);\\r\\n }\\r\\n };\\r\\n\\r\\n if (this.ds.scale > 1) {\\r\\n dialog.style.transform = \\\"scale(\\\" + this.ds.scale + \\\")\\\";\\r\\n }\\r\\n\\r\\n dialog.addEventListener(\\\"mouseleave\\\", function(e) {\\r\\n if (!modified) {\\r\\n dialog.close();\\r\\n }\\r\\n });\\r\\n\\r\\n if (that.prompt_box) {\\r\\n that.prompt_box.close();\\r\\n }\\r\\n that.prompt_box = dialog;\\r\\n\\r\\n var first = null;\\r\\n var timeout = null;\\r\\n var selected = null;\\r\\n\\r\\n var name_element = dialog.querySelector(\\\".name\\\");\\r\\n name_element.innerText = title;\\r\\n var value_element = dialog.querySelector(\\\".value\\\");\\r\\n value_element.value = value;\\r\\n\\r\\n var input = dialog.querySelector(\\\"input\\\");\\r\\n input.addEventListener(\\\"keydown\\\", function(e) {\\r\\n modified = true;\\r\\n if (e.keyCode == 27) {\\r\\n //ESC\\r\\n dialog.close();\\r\\n } else if (e.keyCode == 13) {\\r\\n if (callback) {\\r\\n callback(this.value);\\r\\n }\\r\\n dialog.close();\\r\\n } else {\\r\\n return;\\r\\n }\\r\\n e.preventDefault();\\r\\n e.stopPropagation();\\r\\n });\\r\\n\\r\\n var button = dialog.querySelector(\\\"button\\\");\\r\\n button.addEventListener(\\\"click\\\", function(e) {\\r\\n if (callback) {\\r\\n callback(input.value);\\r\\n }\\r\\n that.setDirty(true);\\r\\n dialog.close();\\r\\n });\\r\\n\\r\\n var graphcanvas = LGraphCanvas.active_canvas;\\r\\n var canvas = graphcanvas.canvas;\\r\\n\\r\\n var rect = canvas.getBoundingClientRect();\\r\\n var offsetx = -20;\\r\\n var offsety = -20;\\r\\n if (rect) {\\r\\n offsetx -= rect.left;\\r\\n offsety -= rect.top;\\r\\n }\\r\\n\\r\\n if (event) {\\r\\n dialog.style.left = event.clientX + offsetx + \\\"px\\\";\\r\\n dialog.style.top = event.clientY + offsety + \\\"px\\\";\\r\\n } else {\\r\\n dialog.style.left = canvas.width * 0.5 + offsetx + \\\"px\\\";\\r\\n dialog.style.top = canvas.height * 0.5 + offsety + \\\"px\\\";\\r\\n }\\r\\n\\r\\n canvas.parentNode.appendChild(dialog);\\r\\n setTimeout(function() {\\r\\n input.focus();\\r\\n }, 10);\\r\\n\\r\\n return dialog;\\r\\n };\\r\\n\\r\\n LGraphCanvas.search_limit = -1;\\r\\n LGraphCanvas.prototype.showSearchBox = function(event) {\\r\\n var that = this;\\r\\n var input_html = \\\"\\\";\\r\\n\\r\\n var dialog = document.createElement(\\\"div\\\");\\r\\n dialog.className = \\\"litegraph litesearchbox graphdialog rounded\\\";\\r\\n dialog.innerHTML =\\r\\n \\\"Search
\\\";\\r\\n dialog.close = function() {\\r\\n that.search_box = null;\\r\\n document.body.focus();\\r\\n setTimeout(function() {\\r\\n that.canvas.focus();\\r\\n }, 20); //important, if canvas loses focus keys wont be captured\\r\\n if (dialog.parentNode) {\\r\\n dialog.parentNode.removeChild(dialog);\\r\\n }\\r\\n };\\r\\n\\r\\n var timeout_close = null;\\r\\n\\r\\n if (this.ds.scale > 1) {\\r\\n dialog.style.transform = \\\"scale(\\\" + this.ds.scale + \\\")\\\";\\r\\n }\\r\\n\\r\\n dialog.addEventListener(\\\"mouseenter\\\", function(e) {\\r\\n if (timeout_close) {\\r\\n clearTimeout(timeout_close);\\r\\n timeout_close = null;\\r\\n }\\r\\n });\\r\\n\\r\\n dialog.addEventListener(\\\"mouseleave\\\", function(e) {\\r\\n //dialog.close();\\r\\n timeout_close = setTimeout(function() {\\r\\n dialog.close();\\r\\n }, 500);\\r\\n });\\r\\n\\r\\n if (that.search_box) {\\r\\n that.search_box.close();\\r\\n }\\r\\n that.search_box = dialog;\\r\\n\\r\\n var helper = dialog.querySelector(\\\".helper\\\");\\r\\n\\r\\n var first = null;\\r\\n var timeout = null;\\r\\n var selected = null;\\r\\n\\r\\n var input = dialog.querySelector(\\\"input\\\");\\r\\n if (input) {\\r\\n input.addEventListener(\\\"blur\\\", function(e) {\\r\\n this.focus();\\r\\n });\\r\\n input.addEventListener(\\\"keydown\\\", function(e) {\\r\\n if (e.keyCode == 38) {\\r\\n //UP\\r\\n changeSelection(false);\\r\\n } else if (e.keyCode == 40) {\\r\\n //DOWN\\r\\n changeSelection(true);\\r\\n } else if (e.keyCode == 27) {\\r\\n //ESC\\r\\n dialog.close();\\r\\n } else if (e.keyCode == 13) {\\r\\n if (selected) {\\r\\n select(selected.innerHTML);\\r\\n } else if (first) {\\r\\n select(first);\\r\\n } else {\\r\\n dialog.close();\\r\\n }\\r\\n } else {\\r\\n if (timeout) {\\r\\n clearInterval(timeout);\\r\\n }\\r\\n timeout = setTimeout(refreshHelper, 10);\\r\\n return;\\r\\n }\\r\\n e.preventDefault();\\r\\n e.stopPropagation();\\r\\n });\\r\\n }\\r\\n\\r\\n var graphcanvas = LGraphCanvas.active_canvas;\\r\\n var canvas = graphcanvas.canvas;\\r\\n\\r\\n var rect = canvas.getBoundingClientRect();\\r\\n var offsetx = -20;\\r\\n var offsety = -20;\\r\\n if (rect) {\\r\\n offsetx -= rect.left;\\r\\n offsety -= rect.top;\\r\\n }\\r\\n\\r\\n if (event) {\\r\\n dialog.style.left = event.clientX + offsetx + \\\"px\\\";\\r\\n dialog.style.top = event.clientY + offsety + \\\"px\\\";\\r\\n } else {\\r\\n dialog.style.left = canvas.width * 0.5 + offsetx + \\\"px\\\";\\r\\n dialog.style.top = canvas.height * 0.5 + offsety + \\\"px\\\";\\r\\n }\\r\\n\\r\\n canvas.parentNode.appendChild(dialog);\\r\\n input.focus();\\r\\n\\r\\n function select(name) {\\r\\n if (name) {\\r\\n if (that.onSearchBoxSelection) {\\r\\n that.onSearchBoxSelection(name, event, graphcanvas);\\r\\n } else {\\r\\n var extra = LiteGraph.searchbox_extras[name.toLowerCase()];\\r\\n if (extra) {\\r\\n name = extra.type;\\r\\n }\\r\\n\\r\\n var node = LiteGraph.createNode(name);\\r\\n if (node) {\\r\\n node.pos = graphcanvas.convertEventToCanvasOffset(\\r\\n event\\r\\n );\\r\\n graphcanvas.graph.add(node);\\r\\n }\\r\\n\\r\\n if (extra && extra.data) {\\r\\n if (extra.data.properties) {\\r\\n for (var i in extra.data.properties) {\\r\\n node.addProperty( i, extra.data.properties[i] );\\r\\n }\\r\\n }\\r\\n if (extra.data.inputs) {\\r\\n node.inputs = [];\\r\\n for (var i in extra.data.inputs) {\\r\\n node.addOutput(\\r\\n extra.data.inputs[i][0],\\r\\n extra.data.inputs[i][1]\\r\\n );\\r\\n }\\r\\n }\\r\\n if (extra.data.outputs) {\\r\\n node.outputs = [];\\r\\n for (var i in extra.data.outputs) {\\r\\n node.addOutput(\\r\\n extra.data.outputs[i][0],\\r\\n extra.data.outputs[i][1]\\r\\n );\\r\\n }\\r\\n }\\r\\n if (extra.data.title) {\\r\\n node.title = extra.data.title;\\r\\n }\\r\\n if (extra.data.json) {\\r\\n node.configure(extra.data.json);\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n dialog.close();\\r\\n }\\r\\n\\r\\n function changeSelection(forward) {\\r\\n var prev = selected;\\r\\n if (selected) {\\r\\n selected.classList.remove(\\\"selected\\\");\\r\\n }\\r\\n if (!selected) {\\r\\n selected = forward\\r\\n ? helper.childNodes[0]\\r\\n : helper.childNodes[helper.childNodes.length];\\r\\n } else {\\r\\n selected = forward\\r\\n ? selected.nextSibling\\r\\n : selected.previousSibling;\\r\\n if (!selected) {\\r\\n selected = prev;\\r\\n }\\r\\n }\\r\\n if (!selected) {\\r\\n return;\\r\\n }\\r\\n selected.classList.add(\\\"selected\\\");\\r\\n selected.scrollIntoView();\\r\\n }\\r\\n\\r\\n function refreshHelper() {\\r\\n timeout = null;\\r\\n var str = input.value;\\r\\n first = null;\\r\\n helper.innerHTML = \\\"\\\";\\r\\n if (!str) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (that.onSearchBox) {\\r\\n var list = that.onSearchBox(helper, str, graphcanvas);\\r\\n if (list) {\\r\\n for (var i = 0; i < list.length; ++i) {\\r\\n addResult(list[i]);\\r\\n }\\r\\n }\\r\\n } else {\\r\\n var c = 0;\\r\\n str = str.toLowerCase();\\r\\n //extras\\r\\n for (var i in LiteGraph.searchbox_extras) {\\r\\n var extra = LiteGraph.searchbox_extras[i];\\r\\n if (extra.desc.toLowerCase().indexOf(str) === -1) {\\r\\n continue;\\r\\n }\\r\\n addResult(extra.desc, \\\"searchbox_extra\\\");\\r\\n if (\\r\\n LGraphCanvas.search_limit !== -1 &&\\r\\n c++ > LGraphCanvas.search_limit\\r\\n ) {\\r\\n break;\\r\\n }\\r\\n }\\r\\n\\r\\n if (Array.prototype.filter) {\\r\\n //filter supported\\r\\n //types\\r\\n var keys = Object.keys(LiteGraph.registered_node_types);\\r\\n var filtered = keys.filter(function(item) {\\r\\n return item.toLowerCase().indexOf(str) !== -1;\\r\\n });\\r\\n for (var i = 0; i < filtered.length; i++) {\\r\\n addResult(filtered[i]);\\r\\n if (\\r\\n LGraphCanvas.search_limit !== -1 &&\\r\\n c++ > LGraphCanvas.search_limit\\r\\n ) {\\r\\n break;\\r\\n }\\r\\n }\\r\\n } else {\\r\\n for (var i in LiteGraph.registered_node_types) {\\r\\n if (i.indexOf(str) != -1) {\\r\\n addResult(i);\\r\\n if (\\r\\n LGraphCanvas.search_limit !== -1 &&\\r\\n c++ > LGraphCanvas.search_limit\\r\\n ) {\\r\\n break;\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n function addResult(type, className) {\\r\\n var help = document.createElement(\\\"div\\\");\\r\\n if (!first) {\\r\\n first = type;\\r\\n }\\r\\n help.innerText = type;\\r\\n help.dataset[\\\"type\\\"] = escape(type);\\r\\n help.className = \\\"litegraph lite-search-item\\\";\\r\\n if (className) {\\r\\n help.className += \\\" \\\" + className;\\r\\n }\\r\\n help.addEventListener(\\\"click\\\", function(e) {\\r\\n select(unescape(this.dataset[\\\"type\\\"]));\\r\\n });\\r\\n helper.appendChild(help);\\r\\n }\\r\\n }\\r\\n\\r\\n return dialog;\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.showEditPropertyValue = function(\\r\\n node,\\r\\n property,\\r\\n options\\r\\n ) {\\r\\n if (!node || node.properties[property] === undefined) {\\r\\n return;\\r\\n }\\r\\n\\r\\n options = options || {};\\r\\n var that = this;\\r\\n\\r\\n var type = \\\"string\\\";\\r\\n\\r\\n if (node.properties[property] !== null) {\\r\\n type = typeof node.properties[property];\\r\\n }\\r\\n\\r\\n //for arrays\\r\\n if (type == \\\"object\\\") {\\r\\n if (node.properties[property].length) {\\r\\n type = \\\"array\\\";\\r\\n }\\r\\n }\\r\\n\\r\\n var info = null;\\r\\n if (node.getPropertyInfo) {\\r\\n info = node.getPropertyInfo(property);\\r\\n }\\r\\n if (node.properties_info) {\\r\\n for (var i = 0; i < node.properties_info.length; ++i) {\\r\\n if (node.properties_info[i].name == property) {\\r\\n info = node.properties_info[i];\\r\\n break;\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (info !== undefined && info !== null && info.type) {\\r\\n type = info.type;\\r\\n }\\r\\n\\r\\n var input_html = \\\"\\\";\\r\\n\\r\\n if (type == \\\"string\\\" || type == \\\"number\\\" || type == \\\"array\\\") {\\r\\n input_html = \\\"\\\";\\r\\n } else if (type == \\\"enum\\\" && info.values) {\\r\\n input_html = \\\"\\\";\\r\\n } else if (type == \\\"boolean\\\") {\\r\\n input_html =\\r\\n \\\"\\\";\\r\\n } else {\\r\\n console.warn(\\\"unknown type: \\\" + type);\\r\\n return;\\r\\n }\\r\\n\\r\\n var dialog = this.createDialog(\\r\\n \\\"\\\" +\\r\\n property +\\r\\n \\\"\\\" +\\r\\n input_html +\\r\\n \\\"\\\",\\r\\n options\\r\\n );\\r\\n\\r\\n if (type == \\\"enum\\\" && info.values) {\\r\\n var input = dialog.querySelector(\\\"select\\\");\\r\\n input.addEventListener(\\\"change\\\", function(e) {\\r\\n setValue(e.target.value);\\r\\n //var index = e.target.value;\\r\\n //setValue( e.options[e.selectedIndex].value );\\r\\n });\\r\\n } else if (type == \\\"boolean\\\") {\\r\\n var input = dialog.querySelector(\\\"input\\\");\\r\\n if (input) {\\r\\n input.addEventListener(\\\"click\\\", function(e) {\\r\\n setValue(!!input.checked);\\r\\n });\\r\\n }\\r\\n } else {\\r\\n var input = dialog.querySelector(\\\"input\\\");\\r\\n if (input) {\\r\\n input.addEventListener(\\\"blur\\\", function(e) {\\r\\n this.focus();\\r\\n });\\r\\n input.value =\\r\\n node.properties[property] !== undefined\\r\\n ? node.properties[property]\\r\\n : \\\"\\\";\\r\\n input.addEventListener(\\\"keydown\\\", function(e) {\\r\\n if (e.keyCode != 13) {\\r\\n return;\\r\\n }\\r\\n inner();\\r\\n e.preventDefault();\\r\\n e.stopPropagation();\\r\\n });\\r\\n }\\r\\n }\\r\\n\\r\\n var button = dialog.querySelector(\\\"button\\\");\\r\\n button.addEventListener(\\\"click\\\", inner);\\r\\n\\r\\n function inner() {\\r\\n setValue(input.value);\\r\\n }\\r\\n\\r\\n function setValue(value) {\\r\\n if (typeof node.properties[property] == \\\"number\\\") {\\r\\n value = Number(value);\\r\\n }\\r\\n if (type == \\\"array\\\") {\\r\\n value = value.split(\\\",\\\").map(Number);\\r\\n }\\r\\n node.properties[property] = value;\\r\\n if (node._graph) {\\r\\n node._graph._version++;\\r\\n }\\r\\n if (node.onPropertyChanged) {\\r\\n node.onPropertyChanged(property, value);\\r\\n }\\r\\n dialog.close();\\r\\n node.setDirtyCanvas(true, true);\\r\\n }\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.createDialog = function(html, options) {\\r\\n options = options || {};\\r\\n\\r\\n var dialog = document.createElement(\\\"div\\\");\\r\\n dialog.className = \\\"graphdialog\\\";\\r\\n dialog.innerHTML = html;\\r\\n\\r\\n var rect = this.canvas.getBoundingClientRect();\\r\\n var offsetx = -20;\\r\\n var offsety = -20;\\r\\n if (rect) {\\r\\n offsetx -= rect.left;\\r\\n offsety -= rect.top;\\r\\n }\\r\\n\\r\\n if (options.position) {\\r\\n offsetx += options.position[0];\\r\\n offsety += options.position[1];\\r\\n } else if (options.event) {\\r\\n offsetx += options.event.clientX;\\r\\n offsety += options.event.clientY;\\r\\n } //centered\\r\\n else {\\r\\n offsetx += this.canvas.width * 0.5;\\r\\n offsety += this.canvas.height * 0.5;\\r\\n }\\r\\n\\r\\n dialog.style.left = offsetx + \\\"px\\\";\\r\\n dialog.style.top = offsety + \\\"px\\\";\\r\\n\\r\\n this.canvas.parentNode.appendChild(dialog);\\r\\n\\r\\n dialog.close = function() {\\r\\n if (this.parentNode) {\\r\\n this.parentNode.removeChild(this);\\r\\n }\\r\\n };\\r\\n\\r\\n return dialog;\\r\\n };\\r\\n\\r\\n LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) {\\r\\n node.collapse();\\r\\n };\\r\\n\\r\\n LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) {\\r\\n node.pin();\\r\\n };\\r\\n\\r\\n LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) {\\r\\n new LiteGraph.ContextMenu(\\r\\n [\\\"Always\\\", \\\"On Event\\\", \\\"On Trigger\\\", \\\"Never\\\"],\\r\\n { event: e, callback: inner_clicked, parentMenu: menu, node: node }\\r\\n );\\r\\n\\r\\n function inner_clicked(v) {\\r\\n if (!node) {\\r\\n return;\\r\\n }\\r\\n switch (v) {\\r\\n case \\\"On Event\\\":\\r\\n node.mode = LiteGraph.ON_EVENT;\\r\\n break;\\r\\n case \\\"On Trigger\\\":\\r\\n node.mode = LiteGraph.ON_TRIGGER;\\r\\n break;\\r\\n case \\\"Never\\\":\\r\\n node.mode = LiteGraph.NEVER;\\r\\n break;\\r\\n case \\\"Always\\\":\\r\\n default:\\r\\n node.mode = LiteGraph.ALWAYS;\\r\\n break;\\r\\n }\\r\\n }\\r\\n\\r\\n return false;\\r\\n };\\r\\n\\r\\n LGraphCanvas.onMenuNodeColors = function(value, options, e, menu, node) {\\r\\n if (!node) {\\r\\n throw \\\"no node for color\\\";\\r\\n }\\r\\n\\r\\n var values = [];\\r\\n values.push({\\r\\n value: null,\\r\\n content:\\r\\n \\\"No color\\\"\\r\\n });\\r\\n\\r\\n for (var i in LGraphCanvas.node_colors) {\\r\\n var color = LGraphCanvas.node_colors[i];\\r\\n var value = {\\r\\n value: i,\\r\\n content:\\r\\n \\\"\\\" +\\r\\n i +\\r\\n \\\"\\\"\\r\\n };\\r\\n values.push(value);\\r\\n }\\r\\n new LiteGraph.ContextMenu(values, {\\r\\n event: e,\\r\\n callback: inner_clicked,\\r\\n parentMenu: menu,\\r\\n node: node\\r\\n });\\r\\n\\r\\n function inner_clicked(v) {\\r\\n if (!node) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var color = v.value ? LGraphCanvas.node_colors[v.value] : null;\\r\\n if (color) {\\r\\n if (node.constructor === LiteGraph.LGraphGroup) {\\r\\n node.color = color.groupcolor;\\r\\n } else {\\r\\n node.color = color.color;\\r\\n node.bgcolor = color.bgcolor;\\r\\n }\\r\\n } else {\\r\\n delete node.color;\\r\\n delete node.bgcolor;\\r\\n }\\r\\n node.setDirtyCanvas(true, true);\\r\\n }\\r\\n\\r\\n return false;\\r\\n };\\r\\n\\r\\n LGraphCanvas.onMenuNodeShapes = function(value, options, e, menu, node) {\\r\\n if (!node) {\\r\\n throw \\\"no node passed\\\";\\r\\n }\\r\\n\\r\\n new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES, {\\r\\n event: e,\\r\\n callback: inner_clicked,\\r\\n parentMenu: menu,\\r\\n node: node\\r\\n });\\r\\n\\r\\n function inner_clicked(v) {\\r\\n if (!node) {\\r\\n return;\\r\\n }\\r\\n node.shape = v;\\r\\n node.setDirtyCanvas(true);\\r\\n }\\r\\n\\r\\n return false;\\r\\n };\\r\\n\\r\\n LGraphCanvas.onMenuNodeRemove = function(value, options, e, menu, node) {\\r\\n if (!node) {\\r\\n throw \\\"no node passed\\\";\\r\\n }\\r\\n\\r\\n if (node.removable === false) {\\r\\n return;\\r\\n }\\r\\n\\r\\n node.graph.remove(node);\\r\\n node.setDirtyCanvas(true, true);\\r\\n };\\r\\n\\r\\n LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) {\\r\\n if (node.clonable == false) {\\r\\n return;\\r\\n }\\r\\n var newnode = node.clone();\\r\\n if (!newnode) {\\r\\n return;\\r\\n }\\r\\n newnode.pos = [node.pos[0] + 5, node.pos[1] + 5];\\r\\n node.graph.add(newnode);\\r\\n node.setDirtyCanvas(true, true);\\r\\n };\\r\\n\\r\\n LGraphCanvas.node_colors = {\\r\\n red: { color: \\\"#322\\\", bgcolor: \\\"#533\\\", groupcolor: \\\"#A88\\\" },\\r\\n brown: { color: \\\"#332922\\\", bgcolor: \\\"#593930\\\", groupcolor: \\\"#b06634\\\" },\\r\\n green: { color: \\\"#232\\\", bgcolor: \\\"#353\\\", groupcolor: \\\"#8A8\\\" },\\r\\n blue: { color: \\\"#223\\\", bgcolor: \\\"#335\\\", groupcolor: \\\"#88A\\\" },\\r\\n pale_blue: {\\r\\n color: \\\"#2a363b\\\",\\r\\n bgcolor: \\\"#3f5159\\\",\\r\\n groupcolor: \\\"#3f789e\\\"\\r\\n },\\r\\n cyan: { color: \\\"#233\\\", bgcolor: \\\"#355\\\", groupcolor: \\\"#8AA\\\" },\\r\\n purple: { color: \\\"#323\\\", bgcolor: \\\"#535\\\", groupcolor: \\\"#a1309b\\\" },\\r\\n yellow: { color: \\\"#432\\\", bgcolor: \\\"#653\\\", groupcolor: \\\"#b58b2a\\\" },\\r\\n black: { color: \\\"#222\\\", bgcolor: \\\"#000\\\", groupcolor: \\\"#444\\\" }\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.getCanvasMenuOptions = function() {\\r\\n var options = null;\\r\\n if (this.getMenuOptions) {\\r\\n options = this.getMenuOptions();\\r\\n } else {\\r\\n options = [\\r\\n {\\r\\n content: \\\"Add Node\\\",\\r\\n has_submenu: true,\\r\\n callback: LGraphCanvas.onMenuAdd\\r\\n },\\r\\n { content: \\\"Add Group\\\", callback: LGraphCanvas.onGroupAdd }\\r\\n //{content:\\\"Collapse All\\\", callback: LGraphCanvas.onMenuCollapseAll }\\r\\n ];\\r\\n\\r\\n if (this._graph_stack && this._graph_stack.length > 0) {\\r\\n options.push(null, {\\r\\n content: \\\"Close subgraph\\\",\\r\\n callback: this.closeSubgraph.bind(this)\\r\\n });\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.getExtraMenuOptions) {\\r\\n var extra = this.getExtraMenuOptions(this, options);\\r\\n if (extra) {\\r\\n options = options.concat(extra);\\r\\n }\\r\\n }\\r\\n\\r\\n return options;\\r\\n };\\r\\n\\r\\n //called by processContextMenu to extract the menu list\\r\\n LGraphCanvas.prototype.getNodeMenuOptions = function(node) {\\r\\n var options = null;\\r\\n\\r\\n if (node.getMenuOptions) {\\r\\n options = node.getMenuOptions(this);\\r\\n } else {\\r\\n options = [\\r\\n {\\r\\n content: \\\"Inputs\\\",\\r\\n has_submenu: true,\\r\\n disabled: true,\\r\\n callback: LGraphCanvas.showMenuNodeOptionalInputs\\r\\n },\\r\\n {\\r\\n content: \\\"Outputs\\\",\\r\\n has_submenu: true,\\r\\n disabled: true,\\r\\n callback: LGraphCanvas.showMenuNodeOptionalOutputs\\r\\n },\\r\\n null,\\r\\n {\\r\\n content: \\\"Properties\\\",\\r\\n has_submenu: true,\\r\\n callback: LGraphCanvas.onShowMenuNodeProperties\\r\\n },\\r\\n null,\\r\\n {\\r\\n content: \\\"Title\\\",\\r\\n callback: LGraphCanvas.onShowPropertyEditor\\r\\n },\\r\\n {\\r\\n content: \\\"Mode\\\",\\r\\n has_submenu: true,\\r\\n callback: LGraphCanvas.onMenuNodeMode\\r\\n },\\r\\n { content: \\\"Resize\\\", callback: LGraphCanvas.onResizeNode },\\r\\n {\\r\\n content: \\\"Collapse\\\",\\r\\n callback: LGraphCanvas.onMenuNodeCollapse\\r\\n },\\r\\n { content: \\\"Pin\\\", callback: LGraphCanvas.onMenuNodePin },\\r\\n {\\r\\n content: \\\"Colors\\\",\\r\\n has_submenu: true,\\r\\n callback: LGraphCanvas.onMenuNodeColors\\r\\n },\\r\\n {\\r\\n content: \\\"Shapes\\\",\\r\\n has_submenu: true,\\r\\n callback: LGraphCanvas.onMenuNodeShapes\\r\\n },\\r\\n null\\r\\n ];\\r\\n }\\r\\n\\r\\n if (node.onGetInputs) {\\r\\n var inputs = node.onGetInputs();\\r\\n if (inputs && inputs.length) {\\r\\n options[0].disabled = false;\\r\\n }\\r\\n }\\r\\n\\r\\n if (node.onGetOutputs) {\\r\\n var outputs = node.onGetOutputs();\\r\\n if (outputs && outputs.length) {\\r\\n options[1].disabled = false;\\r\\n }\\r\\n }\\r\\n\\r\\n if (node.getExtraMenuOptions) {\\r\\n var extra = node.getExtraMenuOptions(this);\\r\\n if (extra) {\\r\\n extra.push(null);\\r\\n options = extra.concat(options);\\r\\n }\\r\\n }\\r\\n\\r\\n if (node.clonable !== false) {\\r\\n options.push({\\r\\n content: \\\"Clone\\\",\\r\\n callback: LGraphCanvas.onMenuNodeClone\\r\\n });\\r\\n }\\r\\n if (node.removable !== false) {\\r\\n options.push(null, {\\r\\n content: \\\"Remove\\\",\\r\\n callback: LGraphCanvas.onMenuNodeRemove\\r\\n });\\r\\n }\\r\\n\\r\\n if (node.graph && node.graph.onGetNodeMenuOptions) {\\r\\n node.graph.onGetNodeMenuOptions(options, node);\\r\\n }\\r\\n\\r\\n return options;\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.getGroupMenuOptions = function(node) {\\r\\n var o = [\\r\\n { content: \\\"Title\\\", callback: LGraphCanvas.onShowPropertyEditor },\\r\\n {\\r\\n content: \\\"Color\\\",\\r\\n has_submenu: true,\\r\\n callback: LGraphCanvas.onMenuNodeColors\\r\\n },\\r\\n {\\r\\n content: \\\"Font size\\\",\\r\\n property: \\\"font_size\\\",\\r\\n type: \\\"Number\\\",\\r\\n callback: LGraphCanvas.onShowPropertyEditor\\r\\n },\\r\\n null,\\r\\n { content: \\\"Remove\\\", callback: LGraphCanvas.onMenuNodeRemove }\\r\\n ];\\r\\n\\r\\n return o;\\r\\n };\\r\\n\\r\\n LGraphCanvas.prototype.processContextMenu = function(node, event) {\\r\\n var that = this;\\r\\n var canvas = LGraphCanvas.active_canvas;\\r\\n var ref_window = canvas.getCanvasWindow();\\r\\n\\r\\n var menu_info = null;\\r\\n var options = {\\r\\n event: event,\\r\\n callback: inner_option_clicked,\\r\\n extra: node\\r\\n };\\r\\n\\r\\n\\t\\tif(node)\\r\\n\\t\\t\\toptions.title = node.type;\\r\\n\\r\\n //check if mouse is in input\\r\\n var slot = null;\\r\\n if (node) {\\r\\n slot = node.getSlotInPosition(event.canvasX, event.canvasY);\\r\\n LGraphCanvas.active_node = node;\\r\\n }\\r\\n\\r\\n if (slot) {\\r\\n //on slot\\r\\n menu_info = [];\\r\\n if (\\r\\n slot &&\\r\\n slot.output &&\\r\\n slot.output.links &&\\r\\n slot.output.links.length\\r\\n ) {\\r\\n menu_info.push({ content: \\\"Disconnect Links\\\", slot: slot });\\r\\n }\\r\\n var _slot = slot.input || slot.output;\\r\\n menu_info.push(\\r\\n _slot.locked\\r\\n ? \\\"Cannot remove\\\"\\r\\n : { content: \\\"Remove Slot\\\", slot: slot }\\r\\n );\\r\\n menu_info.push(\\r\\n _slot.nameLocked\\r\\n ? \\\"Cannot rename\\\"\\r\\n : { content: \\\"Rename Slot\\\", slot: slot }\\r\\n );\\r\\n options.title =\\r\\n (slot.input ? slot.input.type : slot.output.type) || \\\"*\\\";\\r\\n if (slot.input && slot.input.type == LiteGraph.ACTION) {\\r\\n options.title = \\\"Action\\\";\\r\\n }\\r\\n if (slot.output && slot.output.type == LiteGraph.EVENT) {\\r\\n options.title = \\\"Event\\\";\\r\\n }\\r\\n } else {\\r\\n if (node) {\\r\\n //on node\\r\\n menu_info = this.getNodeMenuOptions(node);\\r\\n } else {\\r\\n menu_info = this.getCanvasMenuOptions();\\r\\n var group = this.graph.getGroupOnPos(\\r\\n event.canvasX,\\r\\n event.canvasY\\r\\n );\\r\\n if (group) {\\r\\n //on group\\r\\n menu_info.push(null, {\\r\\n content: \\\"Edit Group\\\",\\r\\n has_submenu: true,\\r\\n submenu: {\\r\\n title: \\\"Group\\\",\\r\\n extra: group,\\r\\n options: this.getGroupMenuOptions(group)\\r\\n }\\r\\n });\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n //show menu\\r\\n if (!menu_info) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var menu = new LiteGraph.ContextMenu(menu_info, options, ref_window);\\r\\n\\r\\n function inner_option_clicked(v, options, e) {\\r\\n if (!v) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (v.content == \\\"Remove Slot\\\") {\\r\\n var info = v.slot;\\r\\n if (info.input) {\\r\\n node.removeInput(info.slot);\\r\\n } else if (info.output) {\\r\\n node.removeOutput(info.slot);\\r\\n }\\r\\n return;\\r\\n } else if (v.content == \\\"Disconnect Links\\\") {\\r\\n var info = v.slot;\\r\\n if (info.output) {\\r\\n node.disconnectOutput(info.slot);\\r\\n } else if (info.input) {\\r\\n node.disconnectInput(info.slot);\\r\\n }\\r\\n return;\\r\\n } else if (v.content == \\\"Rename Slot\\\") {\\r\\n var info = v.slot;\\r\\n var slot_info = info.input\\r\\n ? node.getInputInfo(info.slot)\\r\\n : node.getOutputInfo(info.slot);\\r\\n var dialog = that.createDialog(\\r\\n \\\"Name\\\",\\r\\n options\\r\\n );\\r\\n var input = dialog.querySelector(\\\"input\\\");\\r\\n if (input && slot_info) {\\r\\n input.value = slot_info.label || \\\"\\\";\\r\\n }\\r\\n dialog\\r\\n .querySelector(\\\"button\\\")\\r\\n .addEventListener(\\\"click\\\", function(e) {\\r\\n if (input.value) {\\r\\n if (slot_info) {\\r\\n slot_info.label = input.value;\\r\\n }\\r\\n that.setDirty(true);\\r\\n }\\r\\n dialog.close();\\r\\n });\\r\\n }\\r\\n\\r\\n //if(v.callback)\\r\\n //\\treturn v.callback.call(that, node, options, e, menu, that, event );\\r\\n }\\r\\n };\\r\\n\\r\\n //API *************************************************\\r\\n //like rect but rounded corners\\r\\n if (this.CanvasRenderingContext2D) {\\r\\n CanvasRenderingContext2D.prototype.roundRect = function(\\r\\n x,\\r\\n y,\\r\\n width,\\r\\n height,\\r\\n radius,\\r\\n radius_low\\r\\n ) {\\r\\n if (radius === undefined) {\\r\\n radius = 5;\\r\\n }\\r\\n\\r\\n if (radius_low === undefined) {\\r\\n radius_low = radius;\\r\\n }\\r\\n\\r\\n this.moveTo(x + radius, y);\\r\\n this.lineTo(x + width - radius, y);\\r\\n this.quadraticCurveTo(x + width, y, x + width, y + radius);\\r\\n\\r\\n this.lineTo(x + width, y + height - radius_low);\\r\\n this.quadraticCurveTo(\\r\\n x + width,\\r\\n y + height,\\r\\n x + width - radius_low,\\r\\n y + height\\r\\n );\\r\\n this.lineTo(x + radius_low, y + height);\\r\\n this.quadraticCurveTo(x, y + height, x, y + height - radius_low);\\r\\n this.lineTo(x, y + radius);\\r\\n this.quadraticCurveTo(x, y, x + radius, y);\\r\\n };\\r\\n }\\r\\n\\r\\n function compareObjects(a, b) {\\r\\n for (var i in a) {\\r\\n if (a[i] != b[i]) {\\r\\n return false;\\r\\n }\\r\\n }\\r\\n return true;\\r\\n }\\r\\n LiteGraph.compareObjects = compareObjects;\\r\\n\\r\\n function distance(a, b) {\\r\\n return Math.sqrt(\\r\\n (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])\\r\\n );\\r\\n }\\r\\n LiteGraph.distance = distance;\\r\\n\\r\\n function colorToString(c) {\\r\\n return (\\r\\n \\\"rgba(\\\" +\\r\\n Math.round(c[0] * 255).toFixed() +\\r\\n \\\",\\\" +\\r\\n Math.round(c[1] * 255).toFixed() +\\r\\n \\\",\\\" +\\r\\n Math.round(c[2] * 255).toFixed() +\\r\\n \\\",\\\" +\\r\\n (c.length == 4 ? c[3].toFixed(2) : \\\"1.0\\\") +\\r\\n \\\")\\\"\\r\\n );\\r\\n }\\r\\n LiteGraph.colorToString = colorToString;\\r\\n\\r\\n function isInsideRectangle(x, y, left, top, width, height) {\\r\\n if (left < x && left + width > x && top < y && top + height > y) {\\r\\n return true;\\r\\n }\\r\\n return false;\\r\\n }\\r\\n LiteGraph.isInsideRectangle = isInsideRectangle;\\r\\n\\r\\n //[minx,miny,maxx,maxy]\\r\\n function growBounding(bounding, x, y) {\\r\\n if (x < bounding[0]) {\\r\\n bounding[0] = x;\\r\\n } else if (x > bounding[2]) {\\r\\n bounding[2] = x;\\r\\n }\\r\\n\\r\\n if (y < bounding[1]) {\\r\\n bounding[1] = y;\\r\\n } else if (y > bounding[3]) {\\r\\n bounding[3] = y;\\r\\n }\\r\\n }\\r\\n LiteGraph.growBounding = growBounding;\\r\\n\\r\\n //point inside bounding box\\r\\n function isInsideBounding(p, bb) {\\r\\n if (\\r\\n p[0] < bb[0][0] ||\\r\\n p[1] < bb[0][1] ||\\r\\n p[0] > bb[1][0] ||\\r\\n p[1] > bb[1][1]\\r\\n ) {\\r\\n return false;\\r\\n }\\r\\n return true;\\r\\n }\\r\\n LiteGraph.isInsideBounding = isInsideBounding;\\r\\n\\r\\n //bounding overlap, format: [ startx, starty, width, height ]\\r\\n function overlapBounding(a, b) {\\r\\n var A_end_x = a[0] + a[2];\\r\\n var A_end_y = a[1] + a[3];\\r\\n var B_end_x = b[0] + b[2];\\r\\n var B_end_y = b[1] + b[3];\\r\\n\\r\\n if (\\r\\n a[0] > B_end_x ||\\r\\n a[1] > B_end_y ||\\r\\n A_end_x < b[0] ||\\r\\n A_end_y < b[1]\\r\\n ) {\\r\\n return false;\\r\\n }\\r\\n return true;\\r\\n }\\r\\n LiteGraph.overlapBounding = overlapBounding;\\r\\n\\r\\n //Convert a hex value to its decimal value - the inputted hex must be in the\\r\\n //\\tformat of a hex triplet - the kind we use for HTML colours. The function\\r\\n //\\twill return an array with three values.\\r\\n function hex2num(hex) {\\r\\n if (hex.charAt(0) == \\\"#\\\") {\\r\\n hex = hex.slice(1);\\r\\n } //Remove the '#' char - if there is one.\\r\\n hex = hex.toUpperCase();\\r\\n var hex_alphabets = \\\"0123456789ABCDEF\\\";\\r\\n var value = new Array(3);\\r\\n var k = 0;\\r\\n var int1, int2;\\r\\n for (var i = 0; i < 6; i += 2) {\\r\\n int1 = hex_alphabets.indexOf(hex.charAt(i));\\r\\n int2 = hex_alphabets.indexOf(hex.charAt(i + 1));\\r\\n value[k] = int1 * 16 + int2;\\r\\n k++;\\r\\n }\\r\\n return value;\\r\\n }\\r\\n\\r\\n LiteGraph.hex2num = hex2num;\\r\\n\\r\\n //Give a array with three values as the argument and the function will return\\r\\n //\\tthe corresponding hex triplet.\\r\\n function num2hex(triplet) {\\r\\n var hex_alphabets = \\\"0123456789ABCDEF\\\";\\r\\n var hex = \\\"#\\\";\\r\\n var int1, int2;\\r\\n for (var i = 0; i < 3; i++) {\\r\\n int1 = triplet[i] / 16;\\r\\n int2 = triplet[i] % 16;\\r\\n\\r\\n hex += hex_alphabets.charAt(int1) + hex_alphabets.charAt(int2);\\r\\n }\\r\\n return hex;\\r\\n }\\r\\n\\r\\n LiteGraph.num2hex = num2hex;\\r\\n\\r\\n /* LiteGraph GUI elements used for canvas editing *************************************/\\r\\n\\r\\n /**\\r\\n * ContextMenu from LiteGUI\\r\\n *\\r\\n * @class ContextMenu\\r\\n * @constructor\\r\\n * @param {Array} values (allows object { title: \\\"Nice text\\\", callback: function ... })\\r\\n * @param {Object} options [optional] Some options:\\\\\\r\\n * - title: title to show on top of the menu\\r\\n * - callback: function to call when an option is clicked, it receives the item information\\r\\n * - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback\\r\\n * - event: you can pass a MouseEvent, this way the ContextMenu appears in that position\\r\\n */\\r\\n function ContextMenu(values, options) {\\r\\n options = options || {};\\r\\n this.options = options;\\r\\n var that = this;\\r\\n\\r\\n //to link a menu with its parent\\r\\n if (options.parentMenu) {\\r\\n if (options.parentMenu.constructor !== this.constructor) {\\r\\n console.error(\\r\\n \\\"parentMenu must be of class ContextMenu, ignoring it\\\"\\r\\n );\\r\\n options.parentMenu = null;\\r\\n } else {\\r\\n this.parentMenu = options.parentMenu;\\r\\n this.parentMenu.lock = true;\\r\\n this.parentMenu.current_submenu = this;\\r\\n }\\r\\n }\\r\\n\\r\\n if (\\r\\n options.event &&\\r\\n options.event.constructor !== MouseEvent &&\\r\\n options.event.constructor !== CustomEvent\\r\\n ) {\\r\\n console.error(\\r\\n \\\"Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it.\\\"\\r\\n );\\r\\n options.event = null;\\r\\n }\\r\\n\\r\\n var root = document.createElement(\\\"div\\\");\\r\\n root.className = \\\"litegraph litecontextmenu litemenubar-panel\\\";\\r\\n if (options.className) {\\r\\n root.className += \\\" \\\" + options.className;\\r\\n }\\r\\n root.style.minWidth = 100;\\r\\n root.style.minHeight = 100;\\r\\n root.style.pointerEvents = \\\"none\\\";\\r\\n setTimeout(function() {\\r\\n root.style.pointerEvents = \\\"auto\\\";\\r\\n }, 100); //delay so the mouse up event is not caught by this element\\r\\n\\r\\n //this prevents the default context browser menu to open in case this menu was created when pressing right button\\r\\n root.addEventListener(\\r\\n \\\"mouseup\\\",\\r\\n function(e) {\\r\\n e.preventDefault();\\r\\n return true;\\r\\n },\\r\\n true\\r\\n );\\r\\n root.addEventListener(\\r\\n \\\"contextmenu\\\",\\r\\n function(e) {\\r\\n if (e.button != 2) {\\r\\n //right button\\r\\n return false;\\r\\n }\\r\\n e.preventDefault();\\r\\n return false;\\r\\n },\\r\\n true\\r\\n );\\r\\n\\r\\n root.addEventListener(\\r\\n \\\"mousedown\\\",\\r\\n function(e) {\\r\\n if (e.button == 2) {\\r\\n that.close();\\r\\n e.preventDefault();\\r\\n return true;\\r\\n }\\r\\n },\\r\\n true\\r\\n );\\r\\n\\r\\n function on_mouse_wheel(e) {\\r\\n var pos = parseInt(root.style.top);\\r\\n root.style.top =\\r\\n (pos + e.deltaY * options.scroll_speed).toFixed() + \\\"px\\\";\\r\\n e.preventDefault();\\r\\n return true;\\r\\n }\\r\\n\\r\\n if (!options.scroll_speed) {\\r\\n options.scroll_speed = 0.1;\\r\\n }\\r\\n\\r\\n root.addEventListener(\\\"wheel\\\", on_mouse_wheel, true);\\r\\n root.addEventListener(\\\"mousewheel\\\", on_mouse_wheel, true);\\r\\n\\r\\n this.root = root;\\r\\n\\r\\n //title\\r\\n if (options.title) {\\r\\n var element = document.createElement(\\\"div\\\");\\r\\n element.className = \\\"litemenu-title\\\";\\r\\n element.innerHTML = options.title;\\r\\n root.appendChild(element);\\r\\n }\\r\\n\\r\\n //entries\\r\\n var num = 0;\\r\\n for (var i in values) {\\r\\n var name = values.constructor == Array ? values[i] : i;\\r\\n if (name != null && name.constructor !== String) {\\r\\n name = name.content === undefined ? String(name) : name.content;\\r\\n }\\r\\n var value = values[i];\\r\\n this.addItem(name, value, options);\\r\\n num++;\\r\\n }\\r\\n\\r\\n //close on leave\\r\\n root.addEventListener(\\\"mouseleave\\\", function(e) {\\r\\n if (that.lock) {\\r\\n return;\\r\\n }\\r\\n if (root.closing_timer) {\\r\\n clearTimeout(root.closing_timer);\\r\\n }\\r\\n root.closing_timer = setTimeout(that.close.bind(that, e), 500);\\r\\n //that.close(e);\\r\\n });\\r\\n\\r\\n root.addEventListener(\\\"mouseenter\\\", function(e) {\\r\\n if (root.closing_timer) {\\r\\n clearTimeout(root.closing_timer);\\r\\n }\\r\\n });\\r\\n\\r\\n //insert before checking position\\r\\n var root_document = document;\\r\\n if (options.event) {\\r\\n root_document = options.event.target.ownerDocument;\\r\\n }\\r\\n\\r\\n if (!root_document) {\\r\\n root_document = document;\\r\\n }\\r\\n root_document.body.appendChild(root);\\r\\n\\r\\n //compute best position\\r\\n var left = options.left || 0;\\r\\n var top = options.top || 0;\\r\\n if (options.event) {\\r\\n left = options.event.clientX - 10;\\r\\n top = options.event.clientY - 10;\\r\\n if (options.title) {\\r\\n top -= 20;\\r\\n }\\r\\n\\r\\n if (options.parentMenu) {\\r\\n var rect = options.parentMenu.root.getBoundingClientRect();\\r\\n left = rect.left + rect.width;\\r\\n }\\r\\n\\r\\n var body_rect = document.body.getBoundingClientRect();\\r\\n var root_rect = root.getBoundingClientRect();\\r\\n\\r\\n if (left > body_rect.width - root_rect.width - 10) {\\r\\n left = body_rect.width - root_rect.width - 10;\\r\\n }\\r\\n if (top > body_rect.height - root_rect.height - 10) {\\r\\n top = body_rect.height - root_rect.height - 10;\\r\\n }\\r\\n }\\r\\n\\r\\n root.style.left = left + \\\"px\\\";\\r\\n root.style.top = top + \\\"px\\\";\\r\\n\\r\\n if (options.scale) {\\r\\n root.style.transform = \\\"scale(\\\" + options.scale + \\\")\\\";\\r\\n }\\r\\n }\\r\\n\\r\\n ContextMenu.prototype.addItem = function(name, value, options) {\\r\\n var that = this;\\r\\n options = options || {};\\r\\n\\r\\n var element = document.createElement(\\\"div\\\");\\r\\n element.className = \\\"litemenu-entry submenu\\\";\\r\\n\\r\\n var disabled = false;\\r\\n\\r\\n if (value === null) {\\r\\n element.classList.add(\\\"separator\\\");\\r\\n //element.innerHTML = \\\"
\\\"\\r\\n //continue;\\r\\n } else {\\r\\n element.innerHTML = value && value.title ? value.title : name;\\r\\n element.value = value;\\r\\n\\r\\n if (value) {\\r\\n if (value.disabled) {\\r\\n disabled = true;\\r\\n element.classList.add(\\\"disabled\\\");\\r\\n }\\r\\n if (value.submenu || value.has_submenu) {\\r\\n element.classList.add(\\\"has_submenu\\\");\\r\\n }\\r\\n }\\r\\n\\r\\n if (typeof value == \\\"function\\\") {\\r\\n element.dataset[\\\"value\\\"] = name;\\r\\n element.onclick_callback = value;\\r\\n } else {\\r\\n element.dataset[\\\"value\\\"] = value;\\r\\n }\\r\\n\\r\\n if (value.className) {\\r\\n element.className += \\\" \\\" + value.className;\\r\\n }\\r\\n }\\r\\n\\r\\n this.root.appendChild(element);\\r\\n if (!disabled) {\\r\\n element.addEventListener(\\\"click\\\", inner_onclick);\\r\\n }\\r\\n if (options.autoopen) {\\r\\n element.addEventListener(\\\"mouseenter\\\", inner_over);\\r\\n }\\r\\n\\r\\n function inner_over(e) {\\r\\n var value = this.value;\\r\\n if (!value || !value.has_submenu) {\\r\\n return;\\r\\n }\\r\\n //if it is a submenu, autoopen like the item was clicked\\r\\n inner_onclick.call(this, e);\\r\\n }\\r\\n\\r\\n //menu option clicked\\r\\n function inner_onclick(e) {\\r\\n var value = this.value;\\r\\n var close_parent = true;\\r\\n\\r\\n if (that.current_submenu) {\\r\\n that.current_submenu.close(e);\\r\\n }\\r\\n\\r\\n //global callback\\r\\n if (options.callback) {\\r\\n var r = options.callback.call(\\r\\n this,\\r\\n value,\\r\\n options,\\r\\n e,\\r\\n that,\\r\\n options.node\\r\\n );\\r\\n if (r === true) {\\r\\n close_parent = false;\\r\\n }\\r\\n }\\r\\n\\r\\n //special cases\\r\\n if (value) {\\r\\n if (\\r\\n value.callback &&\\r\\n !options.ignore_item_callbacks &&\\r\\n value.disabled !== true\\r\\n ) {\\r\\n //item callback\\r\\n var r = value.callback.call(\\r\\n this,\\r\\n value,\\r\\n options,\\r\\n e,\\r\\n that,\\r\\n options.extra\\r\\n );\\r\\n if (r === true) {\\r\\n close_parent = false;\\r\\n }\\r\\n }\\r\\n if (value.submenu) {\\r\\n if (!value.submenu.options) {\\r\\n throw \\\"ContextMenu submenu needs options\\\";\\r\\n }\\r\\n var submenu = new that.constructor(value.submenu.options, {\\r\\n callback: value.submenu.callback,\\r\\n event: e,\\r\\n parentMenu: that,\\r\\n ignore_item_callbacks:\\r\\n value.submenu.ignore_item_callbacks,\\r\\n title: value.submenu.title,\\r\\n extra: value.submenu.extra,\\r\\n autoopen: options.autoopen\\r\\n });\\r\\n close_parent = false;\\r\\n }\\r\\n }\\r\\n\\r\\n if (close_parent && !that.lock) {\\r\\n that.close();\\r\\n }\\r\\n }\\r\\n\\r\\n return element;\\r\\n };\\r\\n\\r\\n ContextMenu.prototype.close = function(e, ignore_parent_menu) {\\r\\n if (this.root.parentNode) {\\r\\n this.root.parentNode.removeChild(this.root);\\r\\n }\\r\\n if (this.parentMenu && !ignore_parent_menu) {\\r\\n this.parentMenu.lock = false;\\r\\n this.parentMenu.current_submenu = null;\\r\\n if (e === undefined) {\\r\\n this.parentMenu.close();\\r\\n } else if (\\r\\n e &&\\r\\n !ContextMenu.isCursorOverElement(e, this.parentMenu.root)\\r\\n ) {\\r\\n ContextMenu.trigger(this.parentMenu.root, \\\"mouseleave\\\", e);\\r\\n }\\r\\n }\\r\\n if (this.current_submenu) {\\r\\n this.current_submenu.close(e, true);\\r\\n }\\r\\n\\r\\n if (this.root.closing_timer) {\\r\\n clearTimeout(this.root.closing_timer);\\r\\n }\\r\\n };\\r\\n\\r\\n //this code is used to trigger events easily (used in the context menu mouseleave\\r\\n ContextMenu.trigger = function(element, event_name, params, origin) {\\r\\n var evt = document.createEvent(\\\"CustomEvent\\\");\\r\\n evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail\\r\\n evt.srcElement = origin;\\r\\n if (element.dispatchEvent) {\\r\\n element.dispatchEvent(evt);\\r\\n } else if (element.__events) {\\r\\n element.__events.dispatchEvent(evt);\\r\\n }\\r\\n //else nothing seems binded here so nothing to do\\r\\n return evt;\\r\\n };\\r\\n\\r\\n //returns the top most menu\\r\\n ContextMenu.prototype.getTopMenu = function() {\\r\\n if (this.options.parentMenu) {\\r\\n return this.options.parentMenu.getTopMenu();\\r\\n }\\r\\n return this;\\r\\n };\\r\\n\\r\\n ContextMenu.prototype.getFirstEvent = function() {\\r\\n if (this.options.parentMenu) {\\r\\n return this.options.parentMenu.getFirstEvent();\\r\\n }\\r\\n return this.options.event;\\r\\n };\\r\\n\\r\\n ContextMenu.isCursorOverElement = function(event, element) {\\r\\n var left = event.clientX;\\r\\n var top = event.clientY;\\r\\n var rect = element.getBoundingClientRect();\\r\\n if (!rect) {\\r\\n return false;\\r\\n }\\r\\n if (\\r\\n top > rect.top &&\\r\\n top < rect.top + rect.height &&\\r\\n left > rect.left &&\\r\\n left < rect.left + rect.width\\r\\n ) {\\r\\n return true;\\r\\n }\\r\\n return false;\\r\\n };\\r\\n\\r\\n LiteGraph.ContextMenu = ContextMenu;\\r\\n\\r\\n LiteGraph.closeAllContextMenus = function(ref_window) {\\r\\n ref_window = ref_window || window;\\r\\n\\r\\n var elements = ref_window.document.querySelectorAll(\\\".litecontextmenu\\\");\\r\\n if (!elements.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var result = [];\\r\\n for (var i = 0; i < elements.length; i++) {\\r\\n result.push(elements[i]);\\r\\n }\\r\\n\\r\\n for (var i in result) {\\r\\n if (result[i].close) {\\r\\n result[i].close();\\r\\n } else if (result[i].parentNode) {\\r\\n result[i].parentNode.removeChild(result[i]);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.extendClass = function(target, origin) {\\r\\n for (var i in origin) {\\r\\n //copy class properties\\r\\n if (target.hasOwnProperty(i)) {\\r\\n continue;\\r\\n }\\r\\n target[i] = origin[i];\\r\\n }\\r\\n\\r\\n if (origin.prototype) {\\r\\n //copy prototype properties\\r\\n for (var i in origin.prototype) {\\r\\n //only enumerable\\r\\n if (!origin.prototype.hasOwnProperty(i)) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n if (target.prototype.hasOwnProperty(i)) {\\r\\n //avoid overwriting existing ones\\r\\n continue;\\r\\n }\\r\\n\\r\\n //copy getters\\r\\n if (origin.prototype.__lookupGetter__(i)) {\\r\\n target.prototype.__defineGetter__(\\r\\n i,\\r\\n origin.prototype.__lookupGetter__(i)\\r\\n );\\r\\n } else {\\r\\n target.prototype[i] = origin.prototype[i];\\r\\n }\\r\\n\\r\\n //and setters\\r\\n if (origin.prototype.__lookupSetter__(i)) {\\r\\n target.prototype.__defineSetter__(\\r\\n i,\\r\\n origin.prototype.__lookupSetter__(i)\\r\\n );\\r\\n }\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n //used to create nodes from wrapping functions\\r\\n LiteGraph.getParameterNames = function(func) {\\r\\n return (func + \\\"\\\")\\r\\n .replace(/[/][/].*$/gm, \\\"\\\") // strip single-line comments\\r\\n .replace(/\\\\s+/g, \\\"\\\") // strip white space\\r\\n .replace(/[/][*][^/*]*[*][/]/g, \\\"\\\") // strip multi-line comments /**/\\r\\n .split(\\\"){\\\", 1)[0]\\r\\n .replace(/^[^(]*[(]/, \\\"\\\") // extract the parameters\\r\\n .replace(/=[^,]+/g, \\\"\\\") // strip any ES6 defaults\\r\\n .split(\\\",\\\")\\r\\n .filter(Boolean); // split & filter [\\\"\\\"]\\r\\n };\\r\\n\\r\\n Math.clamp = function(v, a, b) {\\r\\n return a > v ? a : b < v ? b : v;\\r\\n };\\r\\n\\r\\n if (typeof window != \\\"undefined\\\" && !window[\\\"requestAnimationFrame\\\"]) {\\r\\n window.requestAnimationFrame =\\r\\n window.webkitRequestAnimationFrame ||\\r\\n window.mozRequestAnimationFrame ||\\r\\n function(callback) {\\r\\n window.setTimeout(callback, 1000 / 60);\\r\\n };\\r\\n }\\r\\n})(this);\\r\\n\\r\\nif (typeof exports != \\\"undefined\\\") {\\r\\n exports.LiteGraph = this.LiteGraph;\\r\\n}\\r\\n\\r\\n//basic nodes\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n //Constant\\r\\n function Time() {\\r\\n this.addOutput(\\\"in ms\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"in sec\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n Time.title = \\\"Time\\\";\\r\\n Time.desc = \\\"Time\\\";\\r\\n\\r\\n Time.prototype.onExecute = function() {\\r\\n this.setOutputData(0, this.graph.globaltime * 1000);\\r\\n this.setOutputData(1, this.graph.globaltime);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/time\\\", Time);\\r\\n\\r\\n //Subgraph: a node that contains a graph\\r\\n function Subgraph() {\\r\\n var that = this;\\r\\n this.size = [140, 80];\\r\\n this.properties = { enabled: true };\\r\\n this.enabled = true;\\r\\n\\r\\n //create inner graph\\r\\n this.subgraph = new LGraph();\\r\\n this.subgraph._subgraph_node = this;\\r\\n this.subgraph._is_subgraph = true;\\r\\n\\r\\n this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this);\\r\\n\\r\\n this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this);\\r\\n this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this);\\r\\n this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(\\r\\n this\\r\\n );\\r\\n this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this);\\r\\n\\r\\n this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this);\\r\\n this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this);\\r\\n this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(\\r\\n this\\r\\n );\\r\\n this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this);\\r\\n }\\r\\n\\r\\n Subgraph.title = \\\"Subgraph\\\";\\r\\n Subgraph.desc = \\\"Graph inside a node\\\";\\r\\n Subgraph.title_color = \\\"#334\\\";\\r\\n\\r\\n Subgraph.prototype.onGetInputs = function() {\\r\\n return [[\\\"enabled\\\", \\\"boolean\\\"]];\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onDrawTitle = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n ctx.fillStyle = \\\"#555\\\";\\r\\n var w = LiteGraph.NODE_TITLE_HEIGHT;\\r\\n var x = this.size[0] - w;\\r\\n ctx.fillRect(x, -w, w, w);\\r\\n ctx.fillStyle = \\\"#333\\\";\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(x + w * 0.2, -w * 0.6);\\r\\n ctx.lineTo(x + w * 0.8, -w * 0.6);\\r\\n ctx.lineTo(x + w * 0.5, -w * 0.3);\\r\\n ctx.fill();\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) {\\r\\n var that = this;\\r\\n setTimeout(function() {\\r\\n graphcanvas.openSubgraph(that.subgraph);\\r\\n }, 10);\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) {\\r\\n if (\\r\\n !this.flags.collapsed &&\\r\\n pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT &&\\r\\n pos[1] < 0\\r\\n ) {\\r\\n var that = this;\\r\\n setTimeout(function() {\\r\\n graphcanvas.openSubgraph(that.subgraph);\\r\\n }, 10);\\r\\n }\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onAction = function(action, param) {\\r\\n this.subgraph.onAction(action, param);\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onExecute = function() {\\r\\n this.enabled = this.getInputOrProperty(\\\"enabled\\\");\\r\\n if (!this.enabled) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //send inputs to subgraph global inputs\\r\\n if (this.inputs) {\\r\\n for (var i = 0; i < this.inputs.length; i++) {\\r\\n var input = this.inputs[i];\\r\\n var value = this.getInputData(i);\\r\\n this.subgraph.setInputData(input.name, value);\\r\\n }\\r\\n }\\r\\n\\r\\n //execute\\r\\n this.subgraph.runStep();\\r\\n\\r\\n //send subgraph global outputs to outputs\\r\\n if (this.outputs) {\\r\\n for (var i = 0; i < this.outputs.length; i++) {\\r\\n var output = this.outputs[i];\\r\\n var value = this.subgraph.getOutputData(output.name);\\r\\n this.setOutputData(i, value);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) {\\r\\n if (this.enabled) {\\r\\n this.subgraph.sendEventToAllNodes(eventname, param, mode);\\r\\n }\\r\\n };\\r\\n\\r\\n //**** INPUTS ***********************************\\r\\n Subgraph.prototype.onSubgraphTrigger = function(event, param) {\\r\\n var slot = this.findOutputSlot(event);\\r\\n if (slot != -1) {\\r\\n this.triggerSlot(slot);\\r\\n }\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onSubgraphNewInput = function(name, type) {\\r\\n var slot = this.findInputSlot(name);\\r\\n if (slot == -1) {\\r\\n //add input to the node\\r\\n this.addInput(name, type);\\r\\n }\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) {\\r\\n var slot = this.findInputSlot(oldname);\\r\\n if (slot == -1) {\\r\\n return;\\r\\n }\\r\\n var info = this.getInputInfo(slot);\\r\\n info.name = name;\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) {\\r\\n var slot = this.findInputSlot(name);\\r\\n if (slot == -1) {\\r\\n return;\\r\\n }\\r\\n var info = this.getInputInfo(slot);\\r\\n info.type = type;\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onSubgraphRemovedInput = function(name) {\\r\\n var slot = this.findInputSlot(name);\\r\\n if (slot == -1) {\\r\\n return;\\r\\n }\\r\\n this.removeInput(slot);\\r\\n };\\r\\n\\r\\n //**** OUTPUTS ***********************************\\r\\n Subgraph.prototype.onSubgraphNewOutput = function(name, type) {\\r\\n var slot = this.findOutputSlot(name);\\r\\n if (slot == -1) {\\r\\n this.addOutput(name, type);\\r\\n }\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) {\\r\\n var slot = this.findOutputSlot(oldname);\\r\\n if (slot == -1) {\\r\\n return;\\r\\n }\\r\\n var info = this.getOutputInfo(slot);\\r\\n info.name = name;\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) {\\r\\n var slot = this.findOutputSlot(name);\\r\\n if (slot == -1) {\\r\\n return;\\r\\n }\\r\\n var info = this.getOutputInfo(slot);\\r\\n info.type = type;\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onSubgraphRemovedOutput = function(name) {\\r\\n var slot = this.findInputSlot(name);\\r\\n if (slot == -1) {\\r\\n return;\\r\\n }\\r\\n this.removeOutput(slot);\\r\\n };\\r\\n // *****************************************************\\r\\n\\r\\n Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) {\\r\\n var that = this;\\r\\n return [\\r\\n {\\r\\n content: \\\"Open\\\",\\r\\n callback: function() {\\r\\n graphcanvas.openSubgraph(that.subgraph);\\r\\n }\\r\\n }\\r\\n ];\\r\\n };\\r\\n\\r\\n Subgraph.prototype.onResize = function(size) {\\r\\n size[1] += 20;\\r\\n };\\r\\n\\r\\n Subgraph.prototype.serialize = function() {\\r\\n var data = LGraphNode.prototype.serialize.call(this);\\r\\n data.subgraph = this.subgraph.serialize();\\r\\n return data;\\r\\n };\\r\\n //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure()\\r\\n\\r\\n Subgraph.prototype.clone = function() {\\r\\n var node = LiteGraph.createNode(this.type);\\r\\n var data = this.serialize();\\r\\n delete data[\\\"id\\\"];\\r\\n delete data[\\\"inputs\\\"];\\r\\n delete data[\\\"outputs\\\"];\\r\\n node.configure(data);\\r\\n return node;\\r\\n };\\r\\n\\r\\n LiteGraph.Subgraph = Subgraph;\\r\\n LiteGraph.registerNodeType(\\\"graph/subgraph\\\", Subgraph);\\r\\n\\r\\n //Input for a subgraph\\r\\n function GraphInput() {\\r\\n this.addOutput(\\\"\\\", \\\"number\\\");\\r\\n\\r\\n this.name_in_graph = \\\"\\\";\\r\\n this.properties = {\\r\\n\\t\\t\\tname: \\\"\\\",\\r\\n\\t\\t\\ttype: \\\"number\\\",\\r\\n\\t\\t\\tvalue: 0\\r\\n\\t\\t}; \\r\\n\\r\\n var that = this;\\r\\n\\r\\n this.name_widget = this.addWidget(\\r\\n \\\"text\\\",\\r\\n \\\"Name\\\",\\r\\n this.properties.name,\\r\\n function(v) {\\r\\n if (!v) {\\r\\n return;\\r\\n }\\r\\n that.setProperty(\\\"name\\\",v);\\r\\n }\\r\\n );\\r\\n this.type_widget = this.addWidget(\\r\\n \\\"text\\\",\\r\\n \\\"Type\\\",\\r\\n this.properties.type,\\r\\n function(v) {\\r\\n\\t\\t\\t\\tthat.setProperty(\\\"type\\\",v);\\r\\n }\\r\\n );\\r\\n\\r\\n this.value_widget = this.addWidget(\\r\\n \\\"number\\\",\\r\\n \\\"Value\\\",\\r\\n this.properties.value,\\r\\n function(v) {\\r\\n that.setProperty(\\\"value\\\",v);\\r\\n }\\r\\n );\\r\\n\\r\\n this.widgets_up = true;\\r\\n this.size = [180, 90];\\r\\n }\\r\\n\\r\\n GraphInput.title = \\\"Input\\\";\\r\\n GraphInput.desc = \\\"Input of the graph\\\";\\r\\n\\r\\n\\tGraphInput.prototype.onConfigure = function()\\r\\n\\t{\\r\\n\\t\\tthis.updateType();\\r\\n\\t}\\r\\n\\r\\n\\tGraphInput.prototype.updateType = function()\\r\\n\\t{\\r\\n\\t\\tvar type = this.properties.type;\\r\\n\\t\\tthis.type_widget.value = type;\\r\\n\\t\\tif(type == \\\"number\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.value_widget.type = \\\"number\\\";\\r\\n\\t\\t\\tthis.value_widget.value = 0;\\r\\n\\t\\t}\\r\\n\\t\\telse if(type == \\\"bool\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.value_widget.type = \\\"toggle\\\";\\r\\n\\t\\t\\tthis.value_widget.value = true;\\r\\n\\t\\t}\\r\\n\\t\\telse if(type == \\\"string\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.value_widget.type = \\\"text\\\";\\r\\n\\t\\t\\tthis.value_widget.value = \\\"\\\";\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.value_widget.type = null;\\r\\n\\t\\t\\tthis.value_widget.value = null;\\r\\n\\t\\t}\\r\\n\\t\\tthis.properties.value = this.value_widget.value;\\r\\n\\t}\\r\\n\\r\\n\\tGraphInput.prototype.onPropertyChanged = function(name,v)\\r\\n\\t{\\r\\n\\t\\tif( name == \\\"name\\\" )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif (v == \\\"\\\" || v == this.name_in_graph || v == \\\"enabled\\\") {\\r\\n\\t\\t\\t\\treturn false;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif(this.graph)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif (this.name_in_graph) {\\r\\n\\t\\t\\t\\t\\t//already added\\r\\n\\t\\t\\t\\t\\tthis.graph.renameInput( this.name_in_graph, v );\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tthis.graph.addInput( v, this.properties.type );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} //what if not?!\\r\\n\\t\\t\\tthis.name_widget.value = v;\\r\\n\\t\\t\\tthis.name_in_graph = v;\\r\\n\\t\\t}\\r\\n\\t\\telse if( name == \\\"type\\\" )\\r\\n\\t\\t{\\r\\n\\t\\t\\tv = v || \\\"\\\";\\r\\n\\t\\t\\tthis.updateType(v);\\r\\n\\t\\t}\\r\\n\\t\\telse if( name == \\\"value\\\" )\\r\\n\\t\\t{\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n GraphInput.prototype.getTitle = function() {\\r\\n if (this.flags.collapsed) {\\r\\n return this.properties.name;\\r\\n }\\r\\n return this.title;\\r\\n };\\r\\n\\r\\n GraphInput.prototype.onAction = function(action, param) {\\r\\n if (this.properties.type == LiteGraph.EVENT) {\\r\\n this.triggerSlot(0, param);\\r\\n }\\r\\n };\\r\\n\\r\\n GraphInput.prototype.onExecute = function() {\\r\\n var name = this.properties.name;\\r\\n //read from global input\\r\\n var data = this.graph.inputs[name];\\r\\n if (!data) {\\r\\n this.setOutputData(0, this.properties.value );\\r\\n }\\r\\n this.setOutputData(0, data.value === undefined ? this.properties.value : data.value);\\r\\n };\\r\\n\\r\\n GraphInput.prototype.onRemoved = function() {\\r\\n if (this.name_in_graph) {\\r\\n this.graph.removeInput(this.name_in_graph);\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.GraphInput = GraphInput;\\r\\n LiteGraph.registerNodeType(\\\"graph/input\\\", GraphInput);\\r\\n\\r\\n //Output for a subgraph\\r\\n function GraphOutput() {\\r\\n this.addInput(\\\"\\\", \\\"\\\");\\r\\n\\r\\n this.name_in_graph = \\\"\\\";\\r\\n this.properties = {};\\r\\n var that = this;\\r\\n\\r\\n Object.defineProperty(this.properties, \\\"name\\\", {\\r\\n get: function() {\\r\\n return that.name_in_graph;\\r\\n },\\r\\n set: function(v) {\\r\\n if (v == \\\"\\\" || v == that.name_in_graph) {\\r\\n return;\\r\\n }\\r\\n if (that.name_in_graph) {\\r\\n //already added\\r\\n that.graph.renameOutput(that.name_in_graph, v);\\r\\n } else {\\r\\n that.graph.addOutput(v, that.properties.type);\\r\\n }\\r\\n that.name_widget.value = v;\\r\\n that.name_in_graph = v;\\r\\n },\\r\\n enumerable: true\\r\\n });\\r\\n\\r\\n Object.defineProperty(this.properties, \\\"type\\\", {\\r\\n get: function() {\\r\\n return that.inputs[0].type;\\r\\n },\\r\\n set: function(v) {\\r\\n if (v == \\\"action\\\" || v == \\\"event\\\") {\\r\\n v = LiteGraph.ACTION;\\r\\n }\\r\\n that.inputs[0].type = v;\\r\\n if (that.name_in_graph) {\\r\\n //already added\\r\\n that.graph.changeOutputType(\\r\\n that.name_in_graph,\\r\\n that.inputs[0].type\\r\\n );\\r\\n }\\r\\n that.type_widget.value = v || \\\"\\\";\\r\\n },\\r\\n enumerable: true\\r\\n });\\r\\n\\r\\n this.name_widget = this.addWidget(\\r\\n \\\"text\\\",\\r\\n \\\"Name\\\",\\r\\n this.properties.name,\\r\\n function(v) {\\r\\n if (!v) {\\r\\n return;\\r\\n }\\r\\n that.properties.name = v;\\r\\n }\\r\\n );\\r\\n this.type_widget = this.addWidget(\\r\\n \\\"text\\\",\\r\\n \\\"Type\\\",\\r\\n this.properties.type,\\r\\n function(v) {\\r\\n v = v || \\\"\\\";\\r\\n that.properties.type = v;\\r\\n }\\r\\n );\\r\\n\\r\\n this.widgets_up = true;\\r\\n this.size = [180, 60];\\r\\n }\\r\\n\\r\\n GraphOutput.title = \\\"Output\\\";\\r\\n GraphOutput.desc = \\\"Output of the graph\\\";\\r\\n\\r\\n GraphOutput.prototype.onExecute = function() {\\r\\n this._value = this.getInputData(0);\\r\\n this.graph.setOutputData(this.properties.name, this._value);\\r\\n };\\r\\n\\r\\n GraphOutput.prototype.onAction = function(action, param) {\\r\\n if (this.properties.type == LiteGraph.ACTION) {\\r\\n this.graph.trigger(this.properties.name, param);\\r\\n }\\r\\n };\\r\\n\\r\\n GraphOutput.prototype.onRemoved = function() {\\r\\n if (this.name_in_graph) {\\r\\n this.graph.removeOutput(this.name_in_graph);\\r\\n }\\r\\n };\\r\\n\\r\\n GraphOutput.prototype.getTitle = function() {\\r\\n if (this.flags.collapsed) {\\r\\n return this.properties.name;\\r\\n }\\r\\n return this.title;\\r\\n };\\r\\n\\r\\n LiteGraph.GraphOutput = GraphOutput;\\r\\n LiteGraph.registerNodeType(\\\"graph/output\\\", GraphOutput);\\r\\n\\r\\n //Constant\\r\\n function ConstantNumber() {\\r\\n this.addOutput(\\\"value\\\", \\\"number\\\");\\r\\n this.addProperty(\\\"value\\\", 1.0);\\r\\n }\\r\\n\\r\\n ConstantNumber.title = \\\"Const Number\\\";\\r\\n ConstantNumber.desc = \\\"Constant number\\\";\\r\\n\\r\\n ConstantNumber.prototype.onExecute = function() {\\r\\n this.setOutputData(0, parseFloat(this.properties[\\\"value\\\"]));\\r\\n };\\r\\n\\r\\n ConstantNumber.prototype.getTitle = function() {\\r\\n if (this.flags.collapsed) {\\r\\n return this.properties.value;\\r\\n }\\r\\n return this.title;\\r\\n };\\r\\n\\r\\n ConstantNumber.prototype.setValue = function(v) {\\r\\n this.properties.value = v;\\r\\n };\\r\\n\\r\\n ConstantNumber.prototype.onDrawBackground = function(ctx) {\\r\\n //show the current value\\r\\n this.outputs[0].label = this.properties[\\\"value\\\"].toFixed(3);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/const\\\", ConstantNumber);\\r\\n\\r\\n function ConstantString() {\\r\\n this.addOutput(\\\"\\\", \\\"string\\\");\\r\\n this.addProperty(\\\"value\\\", \\\"\\\");\\r\\n this.widget = this.addWidget(\\r\\n \\\"text\\\",\\r\\n \\\"value\\\",\\r\\n \\\"\\\",\\r\\n this.setValue.bind(this)\\r\\n );\\r\\n this.widgets_up = true;\\r\\n this.size = [100, 30];\\r\\n }\\r\\n\\r\\n ConstantString.title = \\\"Const String\\\";\\r\\n ConstantString.desc = \\\"Constant string\\\";\\r\\n\\r\\n ConstantString.prototype.setValue = function(v) {\\r\\n this.properties.value = v;\\r\\n };\\r\\n\\r\\n ConstantString.prototype.onPropertyChanged = function(name, value) {\\r\\n this.widget.value = value;\\r\\n };\\r\\n\\r\\n ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle;\\r\\n\\r\\n ConstantString.prototype.onExecute = function() {\\r\\n this.setOutputData(0, this.properties[\\\"value\\\"]);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/string\\\", ConstantString);\\r\\n\\r\\n function ConstantData() {\\r\\n this.addOutput(\\\"\\\", \\\"\\\");\\r\\n this.addProperty(\\\"value\\\", \\\"\\\");\\r\\n this.widget = this.addWidget(\\r\\n \\\"text\\\",\\r\\n \\\"json\\\",\\r\\n \\\"\\\",\\r\\n this.setValue.bind(this)\\r\\n );\\r\\n this.widgets_up = true;\\r\\n this.size = [140, 30];\\r\\n this._value = null;\\r\\n }\\r\\n\\r\\n ConstantData.title = \\\"Const Data\\\";\\r\\n ConstantData.desc = \\\"Constant Data\\\";\\r\\n\\r\\n ConstantData.prototype.setValue = function(v) {\\r\\n this.properties.value = v;\\r\\n this.onPropertyChanged(\\\"value\\\", v);\\r\\n };\\r\\n\\r\\n ConstantData.prototype.onPropertyChanged = function(name, value) {\\r\\n this.widget.value = value;\\r\\n if (value == null || value == \\\"\\\") {\\r\\n return;\\r\\n }\\r\\n\\r\\n try {\\r\\n this._value = JSON.parse(value);\\r\\n this.boxcolor = \\\"#AEA\\\";\\r\\n } catch (err) {\\r\\n this.boxcolor = \\\"red\\\";\\r\\n }\\r\\n };\\r\\n\\r\\n ConstantData.prototype.onExecute = function() {\\r\\n this.setOutputData(0, this._value);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/data\\\", ConstantData);\\r\\n\\r\\n function ObjectProperty() {\\r\\n this.addInput(\\\"obj\\\", \\\"\\\");\\r\\n this.addOutput(\\\"\\\", \\\"\\\");\\r\\n this.addProperty(\\\"value\\\", \\\"\\\");\\r\\n this.widget = this.addWidget(\\r\\n \\\"text\\\",\\r\\n \\\"prop.\\\",\\r\\n \\\"\\\",\\r\\n this.setValue.bind(this)\\r\\n );\\r\\n this.widgets_up = true;\\r\\n this.size = [140, 30];\\r\\n this._value = null;\\r\\n }\\r\\n\\r\\n ObjectProperty.title = \\\"Object property\\\";\\r\\n ObjectProperty.desc = \\\"Outputs the property of an object\\\";\\r\\n\\r\\n ObjectProperty.prototype.setValue = function(v) {\\r\\n this.properties.value = v;\\r\\n this.widget.value = v;\\r\\n };\\r\\n\\r\\n ObjectProperty.prototype.getTitle = function() {\\r\\n if (this.flags.collapsed) {\\r\\n return \\\"in.\\\" + this.properties.value;\\r\\n }\\r\\n return this.title;\\r\\n };\\r\\n\\r\\n ObjectProperty.prototype.onPropertyChanged = function(name, value) {\\r\\n this.widget.value = value;\\r\\n };\\r\\n\\r\\n ObjectProperty.prototype.onExecute = function() {\\r\\n var data = this.getInputData(0);\\r\\n if (data != null) {\\r\\n this.setOutputData(0, data[this.properties.value]);\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/object_property\\\", ObjectProperty);\\r\\n\\r\\n function ObjectKeys() {\\r\\n this.addInput(\\\"obj\\\", \\\"\\\");\\r\\n this.addOutput(\\\"keys\\\", \\\"array\\\");\\r\\n this.size = [140, 30];\\r\\n }\\r\\n\\r\\n ObjectKeys.title = \\\"Object keys\\\";\\r\\n ObjectKeys.desc = \\\"Outputs an array with the keys of an object\\\";\\r\\n\\r\\n ObjectKeys.prototype.onExecute = function() {\\r\\n var data = this.getInputData(0);\\r\\n if (data != null) {\\r\\n this.setOutputData(0, Object.keys(data) );\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/object_keys\\\", ObjectKeys);\\r\\n\\r\\n function MergeObjects() {\\r\\n this.addInput(\\\"A\\\", \\\"object\\\");\\r\\n this.addInput(\\\"B\\\", \\\"object\\\");\\r\\n this.addOutput(\\\"\\\", \\\"object\\\");\\r\\n\\t\\tthis._result = {};\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tthis.addWidget(\\\"button\\\",\\\"clear\\\",\\\"\\\",function(){\\r\\n\\t\\t\\tthat._result = {};\\r\\n\\t\\t});\\r\\n\\t\\tthis.size = this.computeSize();\\r\\n }\\r\\n\\r\\n MergeObjects.title = \\\"Merge Objects\\\";\\r\\n MergeObjects.desc = \\\"Creates an object copying properties from others\\\";\\r\\n\\r\\n MergeObjects.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n var B = this.getInputData(1);\\r\\n\\t\\tvar C = this._result;\\r\\n\\t\\tif(A)\\r\\n\\t\\t\\tfor(var i in A)\\r\\n\\t\\t\\t\\tC[i] = A[i];\\r\\n\\t\\tif(B)\\r\\n\\t\\t\\tfor(var i in B)\\r\\n\\t\\t\\t\\tC[i] = B[i];\\r\\n\\t\\tthis.setOutputData(0,C);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/merge_objects\\\", MergeObjects );\\r\\n\\r\\n //Store as variable\\r\\n function Variable() {\\r\\n this.size = [60, 30];\\r\\n this.addInput(\\\"in\\\");\\r\\n this.addOutput(\\\"out\\\");\\r\\n\\t\\tthis.properties = { varname: \\\"myname\\\", global: false };\\r\\n this.value = null;\\r\\n }\\r\\n\\r\\n Variable.title = \\\"Variable\\\";\\r\\n Variable.desc = \\\"store/read variable value\\\";\\r\\n\\r\\n Variable.prototype.onExecute = function() {\\r\\n\\t\\tthis.value = this.getInputData(0);\\r\\n\\t\\tif(this.graph)\\r\\n\\t\\t\\tthis.graph.vars[ this.properties.varname ] = this.value;\\r\\n\\t\\tif(this.properties.global)\\r\\n\\t\\t\\tglobal[this.properties.varname] = this.value;\\r\\n\\t\\tthis.setOutputData(0, this.value );\\r\\n };\\r\\n\\r\\n Variable.prototype.getTitle = function() {\\r\\n return this.properties.varname;\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/variable\\\", Variable);\\r\\n\\r\\n //Watch a value in the editor\\r\\n function Watch() {\\r\\n this.size = [60, 30];\\r\\n this.addInput(\\\"value\\\", 0, { label: \\\"\\\" });\\r\\n this.value = 0;\\r\\n }\\r\\n\\r\\n Watch.title = \\\"Watch\\\";\\r\\n Watch.desc = \\\"Show value of input\\\";\\r\\n\\r\\n Watch.prototype.onExecute = function() {\\r\\n if (this.inputs[0]) {\\r\\n this.value = this.getInputData(0);\\r\\n }\\r\\n };\\r\\n\\r\\n Watch.prototype.getTitle = function() {\\r\\n if (this.flags.collapsed) {\\r\\n return this.inputs[0].label;\\r\\n }\\r\\n return this.title;\\r\\n };\\r\\n\\r\\n Watch.toString = function(o) {\\r\\n if (o == null) {\\r\\n return \\\"null\\\";\\r\\n } else if (o.constructor === Number) {\\r\\n return o.toFixed(3);\\r\\n } else if (o.constructor === Array) {\\r\\n var str = \\\"[\\\";\\r\\n for (var i = 0; i < o.length; ++i) {\\r\\n str += Watch.toString(o[i]) + (i + 1 != o.length ? \\\",\\\" : \\\"\\\");\\r\\n }\\r\\n str += \\\"]\\\";\\r\\n return str;\\r\\n } else {\\r\\n return String(o);\\r\\n }\\r\\n };\\r\\n\\r\\n Watch.prototype.onDrawBackground = function(ctx) {\\r\\n //show the current value\\r\\n this.inputs[0].label = Watch.toString(this.value);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/watch\\\", Watch);\\r\\n\\r\\n //in case one type doesnt match other type but you want to connect them anyway\\r\\n function Cast() {\\r\\n this.addInput(\\\"in\\\", 0);\\r\\n this.addOutput(\\\"out\\\", 0);\\r\\n this.size = [40, 30];\\r\\n }\\r\\n\\r\\n Cast.title = \\\"Cast\\\";\\r\\n Cast.desc = \\\"Allows to connect different types\\\";\\r\\n\\r\\n Cast.prototype.onExecute = function() {\\r\\n this.setOutputData(0, this.getInputData(0));\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/cast\\\", Cast);\\r\\n\\r\\n //Show value inside the debug console\\r\\n function Console() {\\r\\n this.mode = LiteGraph.ON_EVENT;\\r\\n this.size = [80, 30];\\r\\n this.addProperty(\\\"msg\\\", \\\"\\\");\\r\\n this.addInput(\\\"log\\\", LiteGraph.EVENT);\\r\\n this.addInput(\\\"msg\\\", 0);\\r\\n }\\r\\n\\r\\n Console.title = \\\"Console\\\";\\r\\n Console.desc = \\\"Show value inside the console\\\";\\r\\n\\r\\n Console.prototype.onAction = function(action, param) {\\r\\n if (action == \\\"log\\\") {\\r\\n console.log(param);\\r\\n } else if (action == \\\"warn\\\") {\\r\\n console.warn(param);\\r\\n } else if (action == \\\"error\\\") {\\r\\n console.error(param);\\r\\n }\\r\\n };\\r\\n\\r\\n Console.prototype.onExecute = function() {\\r\\n var msg = this.getInputData(1);\\r\\n if (msg !== null) {\\r\\n this.properties.msg = msg;\\r\\n }\\r\\n console.log(msg);\\r\\n };\\r\\n\\r\\n Console.prototype.onGetInputs = function() {\\r\\n return [\\r\\n [\\\"log\\\", LiteGraph.ACTION],\\r\\n [\\\"warn\\\", LiteGraph.ACTION],\\r\\n [\\\"error\\\", LiteGraph.ACTION]\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/console\\\", Console);\\r\\n\\r\\n //Show value inside the debug console\\r\\n function Alert() {\\r\\n this.mode = LiteGraph.ON_EVENT;\\r\\n this.addProperty(\\\"msg\\\", \\\"\\\");\\r\\n this.addInput(\\\"\\\", LiteGraph.EVENT);\\r\\n var that = this;\\r\\n this.widget = this.addWidget(\\\"text\\\", \\\"Text\\\", \\\"\\\", function(v) {\\r\\n that.properties.msg = v;\\r\\n });\\r\\n this.widgets_up = true;\\r\\n this.size = [200, 30];\\r\\n }\\r\\n\\r\\n Alert.title = \\\"Alert\\\";\\r\\n Alert.desc = \\\"Show an alert window\\\";\\r\\n Alert.color = \\\"#510\\\";\\r\\n\\r\\n Alert.prototype.onConfigure = function(o) {\\r\\n this.widget.value = o.properties.msg;\\r\\n };\\r\\n\\r\\n Alert.prototype.onAction = function(action, param) {\\r\\n var msg = this.properties.msg;\\r\\n setTimeout(function() {\\r\\n alert(msg);\\r\\n }, 10);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/alert\\\", Alert);\\r\\n\\r\\n //Execites simple code\\r\\n function NodeScript() {\\r\\n this.size = [60, 30];\\r\\n this.addProperty(\\\"onExecute\\\", \\\"return A;\\\");\\r\\n this.addInput(\\\"A\\\", \\\"\\\");\\r\\n this.addInput(\\\"B\\\", \\\"\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"\\\");\\r\\n\\r\\n this._func = null;\\r\\n this.data = {};\\r\\n }\\r\\n\\r\\n NodeScript.prototype.onConfigure = function(o) {\\r\\n if (o.properties.onExecute && LiteGraph.allow_scripts)\\r\\n this.compileCode(o.properties.onExecute);\\r\\n\\t\\telse\\r\\n\\t\\t\\tconsole.warn(\\\"Script not compiled, LiteGraph.allow_scripts is false\\\");\\r\\n };\\r\\n\\r\\n NodeScript.title = \\\"Script\\\";\\r\\n NodeScript.desc = \\\"executes a code (max 100 characters)\\\";\\r\\n\\r\\n NodeScript.widgets_info = {\\r\\n onExecute: { type: \\\"code\\\" }\\r\\n };\\r\\n\\r\\n NodeScript.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"onExecute\\\" && LiteGraph.allow_scripts)\\r\\n this.compileCode(value);\\r\\n\\t\\telse\\r\\n\\t\\t\\tconsole.warn(\\\"Script not compiled, LiteGraph.allow_scripts is false\\\");\\r\\n };\\r\\n\\r\\n NodeScript.prototype.compileCode = function(code) {\\r\\n this._func = null;\\r\\n if (code.length > 256) {\\r\\n console.warn(\\\"Script too long, max 256 chars\\\");\\r\\n } else {\\r\\n var code_low = code.toLowerCase();\\r\\n var forbidden_words = [\\r\\n \\\"script\\\",\\r\\n \\\"body\\\",\\r\\n \\\"document\\\",\\r\\n \\\"eval\\\",\\r\\n \\\"nodescript\\\",\\r\\n \\\"function\\\"\\r\\n ]; //bad security solution\\r\\n for (var i = 0; i < forbidden_words.length; ++i) {\\r\\n if (code_low.indexOf(forbidden_words[i]) != -1) {\\r\\n console.warn(\\\"invalid script\\\");\\r\\n return;\\r\\n }\\r\\n }\\r\\n try {\\r\\n this._func = new Function(\\\"A\\\", \\\"B\\\", \\\"C\\\", \\\"DATA\\\", \\\"node\\\", code);\\r\\n } catch (err) {\\r\\n console.error(\\\"Error parsing script\\\");\\r\\n console.error(err);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n NodeScript.prototype.onExecute = function() {\\r\\n if (!this._func) {\\r\\n return;\\r\\n }\\r\\n\\r\\n try {\\r\\n var A = this.getInputData(0);\\r\\n var B = this.getInputData(1);\\r\\n var C = this.getInputData(2);\\r\\n this.setOutputData(0, this._func(A, B, C, this.data, this));\\r\\n } catch (err) {\\r\\n console.error(\\\"Error in script\\\");\\r\\n console.error(err);\\r\\n }\\r\\n };\\r\\n\\r\\n NodeScript.prototype.onGetOutputs = function() {\\r\\n return [[\\\"C\\\", \\\"\\\"]];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/script\\\", NodeScript);\\r\\n})(this);\\r\\n\\r\\n//event related nodes\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n //Show value inside the debug console\\r\\n function LogEvent() {\\r\\n this.size = [60, 30];\\r\\n this.addInput(\\\"event\\\", LiteGraph.ACTION);\\r\\n }\\r\\n\\r\\n LogEvent.title = \\\"Log Event\\\";\\r\\n LogEvent.desc = \\\"Log event in console\\\";\\r\\n\\r\\n LogEvent.prototype.onAction = function(action, param) {\\r\\n console.log(action, param);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"events/log\\\", LogEvent);\\r\\n\\r\\n //convert to Event if the value is true\\r\\n function TriggerEvent() {\\r\\n this.size = [60, 30];\\r\\n this.addInput(\\\"in\\\", \\\"\\\");\\r\\n this.addOutput(\\\"true\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"change\\\", LiteGraph.EVENT);\\r\\n\\t\\tthis.was_true = false;\\r\\n }\\r\\n\\r\\n TriggerEvent.title = \\\"TriggerEvent\\\";\\r\\n TriggerEvent.desc = \\\"Triggers event if value is true\\\";\\r\\n\\r\\n TriggerEvent.prototype.onExecute = function(action, param) {\\r\\n\\t\\tvar v = this.getInputData(0);\\r\\n\\t\\tif(v)\\r\\n\\t this.triggerSlot(0, param);\\r\\n\\t\\tif(v && !this.was_true)\\r\\n\\t this.triggerSlot(1, param);\\r\\n\\t\\tthis.was_true = v;\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"events/trigger\\\", TriggerEvent);\\r\\n\\r\\n //Sequencer for events\\r\\n function Sequencer() {\\r\\n this.addInput(\\\"\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"\\\", LiteGraph.ACTION);\\r\\n this.addOutput(\\\"\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"\\\", LiteGraph.EVENT);\\r\\n this.size = [120, 30];\\r\\n this.flags = { horizontal: true, render_box: false };\\r\\n }\\r\\n\\r\\n Sequencer.title = \\\"Sequencer\\\";\\r\\n Sequencer.desc = \\\"Trigger events when an event arrives\\\";\\r\\n\\r\\n Sequencer.prototype.getTitle = function() {\\r\\n return \\\"\\\";\\r\\n };\\r\\n\\r\\n Sequencer.prototype.onAction = function(action, param) {\\r\\n if (this.outputs) {\\r\\n for (var i = 0; i < this.outputs.length; ++i) {\\r\\n this.triggerSlot(i, param);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"events/sequencer\\\", Sequencer);\\r\\n\\r\\n //Filter events\\r\\n function FilterEvent() {\\r\\n this.size = [60, 30];\\r\\n this.addInput(\\\"event\\\", LiteGraph.ACTION);\\r\\n this.addOutput(\\\"event\\\", LiteGraph.EVENT);\\r\\n this.properties = {\\r\\n equal_to: \\\"\\\",\\r\\n has_property: \\\"\\\",\\r\\n property_equal_to: \\\"\\\"\\r\\n };\\r\\n }\\r\\n\\r\\n FilterEvent.title = \\\"Filter Event\\\";\\r\\n FilterEvent.desc = \\\"Blocks events that do not match the filter\\\";\\r\\n\\r\\n FilterEvent.prototype.onAction = function(action, param) {\\r\\n if (param == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.properties.equal_to && this.properties.equal_to != param) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.properties.has_property) {\\r\\n var prop = param[this.properties.has_property];\\r\\n if (prop == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (\\r\\n this.properties.property_equal_to &&\\r\\n this.properties.property_equal_to != prop\\r\\n ) {\\r\\n return;\\r\\n }\\r\\n }\\r\\n\\r\\n this.triggerSlot(0, param);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"events/filter\\\", FilterEvent);\\r\\n\\r\\n //Show value inside the debug console\\r\\n function EventCounter() {\\r\\n this.addInput(\\\"inc\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"dec\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"reset\\\", LiteGraph.ACTION);\\r\\n this.addOutput(\\\"change\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"num\\\", \\\"number\\\");\\r\\n this.num = 0;\\r\\n }\\r\\n\\r\\n EventCounter.title = \\\"Counter\\\";\\r\\n EventCounter.desc = \\\"Counts events\\\";\\r\\n\\r\\n EventCounter.prototype.getTitle = function() {\\r\\n if (this.flags.collapsed) {\\r\\n return String(this.num);\\r\\n }\\r\\n return this.title;\\r\\n };\\r\\n\\r\\n EventCounter.prototype.onAction = function(action, param) {\\r\\n var v = this.num;\\r\\n if (action == \\\"inc\\\") {\\r\\n this.num += 1;\\r\\n } else if (action == \\\"dec\\\") {\\r\\n this.num -= 1;\\r\\n } else if (action == \\\"reset\\\") {\\r\\n this.num = 0;\\r\\n }\\r\\n if (this.num != v) {\\r\\n this.trigger(\\\"change\\\", this.num);\\r\\n }\\r\\n };\\r\\n\\r\\n EventCounter.prototype.onDrawBackground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n ctx.fillStyle = \\\"#AAA\\\";\\r\\n ctx.font = \\\"20px Arial\\\";\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n ctx.fillText(this.num, this.size[0] * 0.5, this.size[1] * 0.5);\\r\\n };\\r\\n\\r\\n EventCounter.prototype.onExecute = function() {\\r\\n this.setOutputData(1, this.num);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"events/counter\\\", EventCounter);\\r\\n\\r\\n //Show value inside the debug console\\r\\n function DelayEvent() {\\r\\n this.size = [60, 30];\\r\\n this.addProperty(\\\"time_in_ms\\\", 1000);\\r\\n this.addInput(\\\"event\\\", LiteGraph.ACTION);\\r\\n this.addOutput(\\\"on_time\\\", LiteGraph.EVENT);\\r\\n\\r\\n this._pending = [];\\r\\n }\\r\\n\\r\\n DelayEvent.title = \\\"Delay\\\";\\r\\n DelayEvent.desc = \\\"Delays one event\\\";\\r\\n\\r\\n DelayEvent.prototype.onAction = function(action, param) {\\r\\n var time = this.properties.time_in_ms;\\r\\n if (time <= 0) {\\r\\n this.trigger(null, param);\\r\\n } else {\\r\\n this._pending.push([time, param]);\\r\\n }\\r\\n };\\r\\n\\r\\n DelayEvent.prototype.onExecute = function() {\\r\\n var dt = this.graph.elapsed_time * 1000; //in ms\\r\\n\\r\\n if (this.isInputConnected(1)) {\\r\\n this.properties.time_in_ms = this.getInputData(1);\\r\\n }\\r\\n\\r\\n for (var i = 0; i < this._pending.length; ++i) {\\r\\n var action = this._pending[i];\\r\\n action[0] -= dt;\\r\\n if (action[0] > 0) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n //remove\\r\\n this._pending.splice(i, 1);\\r\\n --i;\\r\\n\\r\\n //trigger\\r\\n this.trigger(null, action[1]);\\r\\n }\\r\\n };\\r\\n\\r\\n DelayEvent.prototype.onGetInputs = function() {\\r\\n return [[\\\"event\\\", LiteGraph.ACTION], [\\\"time_in_ms\\\", \\\"number\\\"]];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"events/delay\\\", DelayEvent);\\r\\n\\r\\n //Show value inside the debug console\\r\\n function TimerEvent() {\\r\\n this.addProperty(\\\"interval\\\", 1000);\\r\\n this.addProperty(\\\"event\\\", \\\"tick\\\");\\r\\n this.addOutput(\\\"on_tick\\\", LiteGraph.EVENT);\\r\\n this.time = 0;\\r\\n this.last_interval = 1000;\\r\\n this.triggered = false;\\r\\n }\\r\\n\\r\\n TimerEvent.title = \\\"Timer\\\";\\r\\n TimerEvent.desc = \\\"Sends an event every N milliseconds\\\";\\r\\n\\r\\n TimerEvent.prototype.onStart = function() {\\r\\n this.time = 0;\\r\\n };\\r\\n\\r\\n TimerEvent.prototype.getTitle = function() {\\r\\n return \\\"Timer: \\\" + this.last_interval.toString() + \\\"ms\\\";\\r\\n };\\r\\n\\r\\n TimerEvent.on_color = \\\"#AAA\\\";\\r\\n TimerEvent.off_color = \\\"#222\\\";\\r\\n\\r\\n TimerEvent.prototype.onDrawBackground = function() {\\r\\n this.boxcolor = this.triggered\\r\\n ? TimerEvent.on_color\\r\\n : TimerEvent.off_color;\\r\\n this.triggered = false;\\r\\n };\\r\\n\\r\\n TimerEvent.prototype.onExecute = function() {\\r\\n var dt = this.graph.elapsed_time * 1000; //in ms\\r\\n\\r\\n var trigger = this.time == 0;\\r\\n\\r\\n this.time += dt;\\r\\n this.last_interval = Math.max(\\r\\n 1,\\r\\n this.getInputOrProperty(\\\"interval\\\") | 0\\r\\n );\\r\\n\\r\\n if (\\r\\n !trigger &&\\r\\n (this.time < this.last_interval || isNaN(this.last_interval))\\r\\n ) {\\r\\n if (this.inputs && this.inputs.length > 1 && this.inputs[1]) {\\r\\n this.setOutputData(1, false);\\r\\n }\\r\\n return;\\r\\n }\\r\\n\\r\\n this.triggered = true;\\r\\n this.time = this.time % this.last_interval;\\r\\n this.trigger(\\\"on_tick\\\", this.properties.event);\\r\\n if (this.inputs && this.inputs.length > 1 && this.inputs[1]) {\\r\\n this.setOutputData(1, true);\\r\\n }\\r\\n };\\r\\n\\r\\n TimerEvent.prototype.onGetInputs = function() {\\r\\n return [[\\\"interval\\\", \\\"number\\\"]];\\r\\n };\\r\\n\\r\\n TimerEvent.prototype.onGetOutputs = function() {\\r\\n return [[\\\"tick\\\", \\\"boolean\\\"]];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"events/timer\\\", TimerEvent);\\r\\n\\r\\n function DataStore() {\\r\\n this.addInput(\\\"data\\\", \\\"\\\");\\r\\n this.addInput(\\\"assign\\\", LiteGraph.ACTION);\\r\\n this.addOutput(\\\"data\\\", \\\"\\\");\\r\\n\\t\\tthis._last_value = null;\\r\\n\\t\\tthis.properties = { data: null, serialize: true };\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tthis.addWidget(\\\"button\\\",\\\"store\\\",\\\"\\\",function(){\\r\\n\\t\\t\\tthat.properties.data = that._last_value;\\r\\n\\t\\t});\\r\\n }\\r\\n\\r\\n DataStore.title = \\\"Data Store\\\";\\r\\n DataStore.desc = \\\"Stores data and only changes when event is received\\\";\\r\\n\\r\\n\\tDataStore.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tthis._last_value = this.getInputData(0);\\r\\n\\t\\tthis.setOutputData(0, this.properties.data );\\r\\n\\t}\\r\\n\\r\\n DataStore.prototype.onAction = function(action, param) {\\r\\n\\t\\tthis.properties.data = this._last_value;\\r\\n };\\r\\n\\r\\n\\tDataStore.prototype.onSerialize = function(o)\\r\\n\\t{\\r\\n\\t\\tif(o.data == null)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(this.properties.serialize == false || (o.data.constructor !== String && o.data.constructor !== Number && o.data.constructor !== Boolean && o.data.constructor !== Array && o.data.constructor !== Object ))\\r\\n\\t\\t\\to.data = null;\\r\\n\\t}\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"basic/data_store\\\", DataStore);\\r\\n})(this);\\r\\n\\r\\n//widgets\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n /* Button ****************/\\r\\n\\r\\n function WidgetButton() {\\r\\n this.addOutput(\\\"\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"\\\", \\\"boolean\\\");\\r\\n this.addProperty(\\\"text\\\", \\\"click me\\\");\\r\\n this.addProperty(\\\"font_size\\\", 30);\\r\\n this.addProperty(\\\"message\\\", \\\"\\\");\\r\\n this.size = [164, 84];\\r\\n this.clicked = false;\\r\\n }\\r\\n\\r\\n WidgetButton.title = \\\"Button\\\";\\r\\n WidgetButton.desc = \\\"Triggers an event\\\";\\r\\n\\r\\n WidgetButton.font = \\\"Arial\\\";\\r\\n WidgetButton.prototype.onDrawForeground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n var margin = 10;\\r\\n ctx.fillStyle = \\\"black\\\";\\r\\n ctx.fillRect(\\r\\n margin + 1,\\r\\n margin + 1,\\r\\n this.size[0] - margin * 2,\\r\\n this.size[1] - margin * 2\\r\\n );\\r\\n ctx.fillStyle = \\\"#AAF\\\";\\r\\n ctx.fillRect(\\r\\n margin - 1,\\r\\n margin - 1,\\r\\n this.size[0] - margin * 2,\\r\\n this.size[1] - margin * 2\\r\\n );\\r\\n ctx.fillStyle = this.clicked\\r\\n ? \\\"white\\\"\\r\\n : this.mouseOver\\r\\n ? \\\"#668\\\"\\r\\n : \\\"#334\\\";\\r\\n ctx.fillRect(\\r\\n margin,\\r\\n margin,\\r\\n this.size[0] - margin * 2,\\r\\n this.size[1] - margin * 2\\r\\n );\\r\\n\\r\\n if (this.properties.text || this.properties.text === 0) {\\r\\n var font_size = this.properties.font_size || 30;\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n ctx.fillStyle = this.clicked ? \\\"black\\\" : \\\"white\\\";\\r\\n ctx.font = font_size + \\\"px \\\" + WidgetButton.font;\\r\\n ctx.fillText(\\r\\n this.properties.text,\\r\\n this.size[0] * 0.5,\\r\\n this.size[1] * 0.5 + font_size * 0.3\\r\\n );\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n }\\r\\n };\\r\\n\\r\\n WidgetButton.prototype.onMouseDown = function(e, local_pos) {\\r\\n if (\\r\\n local_pos[0] > 1 &&\\r\\n local_pos[1] > 1 &&\\r\\n local_pos[0] < this.size[0] - 2 &&\\r\\n local_pos[1] < this.size[1] - 2\\r\\n ) {\\r\\n this.clicked = true;\\r\\n this.triggerSlot(0, this.properties.message);\\r\\n return true;\\r\\n }\\r\\n };\\r\\n\\r\\n WidgetButton.prototype.onExecute = function() {\\r\\n this.setOutputData(1, this.clicked);\\r\\n };\\r\\n\\r\\n WidgetButton.prototype.onMouseUp = function(e) {\\r\\n this.clicked = false;\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"widget/button\\\", WidgetButton);\\r\\n\\r\\n function WidgetToggle() {\\r\\n this.addInput(\\\"\\\", \\\"boolean\\\");\\r\\n this.addInput(\\\"e\\\", LiteGraph.ACTION);\\r\\n this.addOutput(\\\"v\\\", \\\"boolean\\\");\\r\\n this.addOutput(\\\"e\\\", LiteGraph.EVENT);\\r\\n this.properties = { font: \\\"\\\", value: false };\\r\\n this.size = [160, 44];\\r\\n }\\r\\n\\r\\n WidgetToggle.title = \\\"Toggle\\\";\\r\\n WidgetToggle.desc = \\\"Toggles between true or false\\\";\\r\\n\\r\\n WidgetToggle.prototype.onDrawForeground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var size = this.size[1] * 0.5;\\r\\n var margin = 0.25;\\r\\n var h = this.size[1] * 0.8;\\r\\n ctx.font = this.properties.font || (size * 0.8).toFixed(0) + \\\"px Arial\\\";\\r\\n var w = ctx.measureText(this.title).width;\\r\\n var x = (this.size[0] - (w + size)) * 0.5;\\r\\n\\r\\n ctx.fillStyle = \\\"#AAA\\\";\\r\\n ctx.fillRect(x, h - size, size, size);\\r\\n\\r\\n ctx.fillStyle = this.properties.value ? \\\"#AEF\\\" : \\\"#000\\\";\\r\\n ctx.fillRect(\\r\\n x + size * margin,\\r\\n h - size + size * margin,\\r\\n size * (1 - margin * 2),\\r\\n size * (1 - margin * 2)\\r\\n );\\r\\n\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n ctx.fillStyle = \\\"#AAA\\\";\\r\\n ctx.fillText(this.title, size * 1.2 + x, h * 0.85);\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n };\\r\\n\\r\\n WidgetToggle.prototype.onAction = function(action) {\\r\\n this.properties.value = !this.properties.value;\\r\\n this.trigger(\\\"e\\\", this.properties.value);\\r\\n };\\r\\n\\r\\n WidgetToggle.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v != null) {\\r\\n this.properties.value = v;\\r\\n }\\r\\n this.setOutputData(0, this.properties.value);\\r\\n };\\r\\n\\r\\n WidgetToggle.prototype.onMouseDown = function(e, local_pos) {\\r\\n if (\\r\\n local_pos[0] > 1 &&\\r\\n local_pos[1] > 1 &&\\r\\n local_pos[0] < this.size[0] - 2 &&\\r\\n local_pos[1] < this.size[1] - 2\\r\\n ) {\\r\\n this.properties.value = !this.properties.value;\\r\\n this.graph._version++;\\r\\n this.trigger(\\\"e\\\", this.properties.value);\\r\\n return true;\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"widget/toggle\\\", WidgetToggle);\\r\\n\\r\\n /* Number ****************/\\r\\n\\r\\n function WidgetNumber() {\\r\\n this.addOutput(\\\"\\\", \\\"number\\\");\\r\\n this.size = [80, 60];\\r\\n this.properties = { min: -1000, max: 1000, value: 1, step: 1 };\\r\\n this.old_y = -1;\\r\\n this._remainder = 0;\\r\\n this._precision = 0;\\r\\n this.mouse_captured = false;\\r\\n }\\r\\n\\r\\n WidgetNumber.title = \\\"Number\\\";\\r\\n WidgetNumber.desc = \\\"Widget to select number value\\\";\\r\\n\\r\\n WidgetNumber.pixels_threshold = 10;\\r\\n WidgetNumber.markers_color = \\\"#666\\\";\\r\\n\\r\\n WidgetNumber.prototype.onDrawForeground = function(ctx) {\\r\\n var x = this.size[0] * 0.5;\\r\\n var h = this.size[1];\\r\\n if (h > 30) {\\r\\n ctx.fillStyle = WidgetNumber.markers_color;\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(x, h * 0.1);\\r\\n ctx.lineTo(x + h * 0.1, h * 0.2);\\r\\n ctx.lineTo(x + h * -0.1, h * 0.2);\\r\\n ctx.fill();\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(x, h * 0.9);\\r\\n ctx.lineTo(x + h * 0.1, h * 0.8);\\r\\n ctx.lineTo(x + h * -0.1, h * 0.8);\\r\\n ctx.fill();\\r\\n ctx.font = (h * 0.7).toFixed(1) + \\\"px Arial\\\";\\r\\n } else {\\r\\n ctx.font = (h * 0.8).toFixed(1) + \\\"px Arial\\\";\\r\\n }\\r\\n\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n ctx.font = (h * 0.7).toFixed(1) + \\\"px Arial\\\";\\r\\n ctx.fillStyle = \\\"#EEE\\\";\\r\\n ctx.fillText(\\r\\n this.properties.value.toFixed(this._precision),\\r\\n x,\\r\\n h * 0.75\\r\\n );\\r\\n };\\r\\n\\r\\n WidgetNumber.prototype.onExecute = function() {\\r\\n this.setOutputData(0, this.properties.value);\\r\\n };\\r\\n\\r\\n WidgetNumber.prototype.onPropertyChanged = function(name, value) {\\r\\n var t = (this.properties.step + \\\"\\\").split(\\\".\\\");\\r\\n this._precision = t.length > 1 ? t[1].length : 0;\\r\\n };\\r\\n\\r\\n WidgetNumber.prototype.onMouseDown = function(e, pos) {\\r\\n if (pos[1] < 0) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.old_y = e.canvasY;\\r\\n this.captureInput(true);\\r\\n this.mouse_captured = true;\\r\\n\\r\\n return true;\\r\\n };\\r\\n\\r\\n WidgetNumber.prototype.onMouseMove = function(e) {\\r\\n if (!this.mouse_captured) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var delta = this.old_y - e.canvasY;\\r\\n if (e.shiftKey) {\\r\\n delta *= 10;\\r\\n }\\r\\n if (e.metaKey || e.altKey) {\\r\\n delta *= 0.1;\\r\\n }\\r\\n this.old_y = e.canvasY;\\r\\n\\r\\n var steps = this._remainder + delta / WidgetNumber.pixels_threshold;\\r\\n this._remainder = steps % 1;\\r\\n steps = steps | 0;\\r\\n\\r\\n var v = Math.clamp(\\r\\n this.properties.value + steps * this.properties.step,\\r\\n this.properties.min,\\r\\n this.properties.max\\r\\n );\\r\\n this.properties.value = v;\\r\\n this.graph._version++;\\r\\n this.setDirtyCanvas(true);\\r\\n };\\r\\n\\r\\n WidgetNumber.prototype.onMouseUp = function(e, pos) {\\r\\n if (e.click_time < 200) {\\r\\n var steps = pos[1] > this.size[1] * 0.5 ? -1 : 1;\\r\\n this.properties.value = Math.clamp(\\r\\n this.properties.value + steps * this.properties.step,\\r\\n this.properties.min,\\r\\n this.properties.max\\r\\n );\\r\\n this.graph._version++;\\r\\n this.setDirtyCanvas(true);\\r\\n }\\r\\n\\r\\n if (this.mouse_captured) {\\r\\n this.mouse_captured = false;\\r\\n this.captureInput(false);\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"widget/number\\\", WidgetNumber);\\r\\n\\r\\n /* Knob ****************/\\r\\n\\r\\n function WidgetKnob() {\\r\\n this.addOutput(\\\"\\\", \\\"number\\\");\\r\\n this.size = [64, 84];\\r\\n this.properties = {\\r\\n min: 0,\\r\\n max: 1,\\r\\n value: 0.5,\\r\\n color: \\\"#7AF\\\",\\r\\n precision: 2\\r\\n };\\r\\n this.value = -1;\\r\\n }\\r\\n\\r\\n WidgetKnob.title = \\\"Knob\\\";\\r\\n WidgetKnob.desc = \\\"Circular controller\\\";\\r\\n WidgetKnob.size = [80, 100];\\r\\n\\r\\n WidgetKnob.prototype.onDrawForeground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.value == -1) {\\r\\n this.value =\\r\\n (this.properties.value - this.properties.min) /\\r\\n (this.properties.max - this.properties.min);\\r\\n }\\r\\n\\r\\n var center_x = this.size[0] * 0.5;\\r\\n var center_y = this.size[1] * 0.5;\\r\\n var radius = Math.min(this.size[0], this.size[1]) * 0.5 - 5;\\r\\n var w = Math.floor(radius * 0.05);\\r\\n\\r\\n ctx.globalAlpha = 1;\\r\\n ctx.save();\\r\\n ctx.translate(center_x, center_y);\\r\\n ctx.rotate(Math.PI * 0.75);\\r\\n\\r\\n //bg\\r\\n ctx.fillStyle = \\\"rgba(0,0,0,0.5)\\\";\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(0, 0);\\r\\n ctx.arc(0, 0, radius, 0, Math.PI * 1.5);\\r\\n ctx.fill();\\r\\n\\r\\n //value\\r\\n ctx.strokeStyle = \\\"black\\\";\\r\\n ctx.fillStyle = this.properties.color;\\r\\n ctx.lineWidth = 2;\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(0, 0);\\r\\n ctx.arc(\\r\\n 0,\\r\\n 0,\\r\\n radius - 4,\\r\\n 0,\\r\\n Math.PI * 1.5 * Math.max(0.01, this.value)\\r\\n );\\r\\n ctx.closePath();\\r\\n ctx.fill();\\r\\n //ctx.stroke();\\r\\n ctx.lineWidth = 1;\\r\\n ctx.globalAlpha = 1;\\r\\n ctx.restore();\\r\\n\\r\\n //inner\\r\\n ctx.fillStyle = \\\"black\\\";\\r\\n ctx.beginPath();\\r\\n ctx.arc(center_x, center_y, radius * 0.75, 0, Math.PI * 2, true);\\r\\n ctx.fill();\\r\\n\\r\\n //miniball\\r\\n ctx.fillStyle = this.mouseOver ? \\\"white\\\" : this.properties.color;\\r\\n ctx.beginPath();\\r\\n var angle = this.value * Math.PI * 1.5 + Math.PI * 0.75;\\r\\n ctx.arc(\\r\\n center_x + Math.cos(angle) * radius * 0.65,\\r\\n center_y + Math.sin(angle) * radius * 0.65,\\r\\n radius * 0.05,\\r\\n 0,\\r\\n Math.PI * 2,\\r\\n true\\r\\n );\\r\\n ctx.fill();\\r\\n\\r\\n //text\\r\\n ctx.fillStyle = this.mouseOver ? \\\"white\\\" : \\\"#AAA\\\";\\r\\n ctx.font = Math.floor(radius * 0.5) + \\\"px Arial\\\";\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n ctx.fillText(\\r\\n this.properties.value.toFixed(this.properties.precision),\\r\\n center_x,\\r\\n center_y + radius * 0.15\\r\\n );\\r\\n };\\r\\n\\r\\n WidgetKnob.prototype.onExecute = function() {\\r\\n this.setOutputData(0, this.properties.value);\\r\\n this.boxcolor = LiteGraph.colorToString([\\r\\n this.value,\\r\\n this.value,\\r\\n this.value\\r\\n ]);\\r\\n };\\r\\n\\r\\n WidgetKnob.prototype.onMouseDown = function(e) {\\r\\n this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20];\\r\\n this.radius = this.size[0] * 0.5;\\r\\n if (\\r\\n e.canvasY - this.pos[1] < 20 ||\\r\\n LiteGraph.distance(\\r\\n [e.canvasX, e.canvasY],\\r\\n [this.pos[0] + this.center[0], this.pos[1] + this.center[1]]\\r\\n ) > this.radius\\r\\n ) {\\r\\n return false;\\r\\n }\\r\\n this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]];\\r\\n this.captureInput(true);\\r\\n return true;\\r\\n };\\r\\n\\r\\n WidgetKnob.prototype.onMouseMove = function(e) {\\r\\n if (!this.oldmouse) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]];\\r\\n\\r\\n var v = this.value;\\r\\n v -= (m[1] - this.oldmouse[1]) * 0.01;\\r\\n if (v > 1.0) {\\r\\n v = 1.0;\\r\\n } else if (v < 0.0) {\\r\\n v = 0.0;\\r\\n }\\r\\n this.value = v;\\r\\n this.properties.value =\\r\\n this.properties.min +\\r\\n (this.properties.max - this.properties.min) * this.value;\\r\\n this.oldmouse = m;\\r\\n this.setDirtyCanvas(true);\\r\\n };\\r\\n\\r\\n WidgetKnob.prototype.onMouseUp = function(e) {\\r\\n if (this.oldmouse) {\\r\\n this.oldmouse = null;\\r\\n this.captureInput(false);\\r\\n }\\r\\n };\\r\\n\\r\\n WidgetKnob.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"min\\\" || name == \\\"max\\\" || name == \\\"value\\\") {\\r\\n this.properties[name] = parseFloat(value);\\r\\n return true; //block\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"widget/knob\\\", WidgetKnob);\\r\\n\\r\\n //Show value inside the debug console\\r\\n function WidgetSliderGUI() {\\r\\n this.addOutput(\\\"\\\", \\\"number\\\");\\r\\n this.properties = {\\r\\n value: 0.5,\\r\\n min: 0,\\r\\n max: 1,\\r\\n text: \\\"V\\\"\\r\\n };\\r\\n var that = this;\\r\\n this.size = [140, 40];\\r\\n this.slider = this.addWidget(\\r\\n \\\"slider\\\",\\r\\n \\\"V\\\",\\r\\n this.properties.value,\\r\\n function(v) {\\r\\n that.properties.value = v;\\r\\n },\\r\\n this.properties\\r\\n );\\r\\n this.widgets_up = true;\\r\\n }\\r\\n\\r\\n WidgetSliderGUI.title = \\\"Inner Slider\\\";\\r\\n\\r\\n WidgetSliderGUI.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"value\\\") {\\r\\n this.slider.value = value;\\r\\n }\\r\\n };\\r\\n\\r\\n WidgetSliderGUI.prototype.onExecute = function() {\\r\\n this.setOutputData(0, this.properties.value);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"widget/internal_slider\\\", WidgetSliderGUI);\\r\\n\\r\\n //Widget H SLIDER\\r\\n function WidgetHSlider() {\\r\\n this.size = [160, 26];\\r\\n this.addOutput(\\\"\\\", \\\"number\\\");\\r\\n this.properties = { color: \\\"#7AF\\\", min: 0, max: 1, value: 0.5 };\\r\\n this.value = -1;\\r\\n }\\r\\n\\r\\n WidgetHSlider.title = \\\"H.Slider\\\";\\r\\n WidgetHSlider.desc = \\\"Linear slider controller\\\";\\r\\n\\r\\n WidgetHSlider.prototype.onDrawForeground = function(ctx) {\\r\\n if (this.value == -1) {\\r\\n this.value =\\r\\n (this.properties.value - this.properties.min) /\\r\\n (this.properties.max - this.properties.min);\\r\\n }\\r\\n\\r\\n //border\\r\\n ctx.globalAlpha = 1;\\r\\n ctx.lineWidth = 1;\\r\\n ctx.fillStyle = \\\"#000\\\";\\r\\n ctx.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4);\\r\\n\\r\\n ctx.fillStyle = this.properties.color;\\r\\n ctx.beginPath();\\r\\n ctx.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8);\\r\\n ctx.fill();\\r\\n };\\r\\n\\r\\n WidgetHSlider.prototype.onExecute = function() {\\r\\n this.properties.value =\\r\\n this.properties.min +\\r\\n (this.properties.max - this.properties.min) * this.value;\\r\\n this.setOutputData(0, this.properties.value);\\r\\n this.boxcolor = LiteGraph.colorToString([\\r\\n this.value,\\r\\n this.value,\\r\\n this.value\\r\\n ]);\\r\\n };\\r\\n\\r\\n WidgetHSlider.prototype.onMouseDown = function(e) {\\r\\n if (e.canvasY - this.pos[1] < 0) {\\r\\n return false;\\r\\n }\\r\\n\\r\\n this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]];\\r\\n this.captureInput(true);\\r\\n return true;\\r\\n };\\r\\n\\r\\n WidgetHSlider.prototype.onMouseMove = function(e) {\\r\\n if (!this.oldmouse) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]];\\r\\n\\r\\n var v = this.value;\\r\\n var delta = m[0] - this.oldmouse[0];\\r\\n v += delta / this.size[0];\\r\\n if (v > 1.0) {\\r\\n v = 1.0;\\r\\n } else if (v < 0.0) {\\r\\n v = 0.0;\\r\\n }\\r\\n\\r\\n this.value = v;\\r\\n\\r\\n this.oldmouse = m;\\r\\n this.setDirtyCanvas(true);\\r\\n };\\r\\n\\r\\n WidgetHSlider.prototype.onMouseUp = function(e) {\\r\\n this.oldmouse = null;\\r\\n this.captureInput(false);\\r\\n };\\r\\n\\r\\n WidgetHSlider.prototype.onMouseLeave = function(e) {\\r\\n //this.oldmouse = null;\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"widget/hslider\\\", WidgetHSlider);\\r\\n\\r\\n function WidgetProgress() {\\r\\n this.size = [160, 26];\\r\\n this.addInput(\\\"\\\", \\\"number\\\");\\r\\n this.properties = { min: 0, max: 1, value: 0, color: \\\"#AAF\\\" };\\r\\n }\\r\\n\\r\\n WidgetProgress.title = \\\"Progress\\\";\\r\\n WidgetProgress.desc = \\\"Shows data in linear progress\\\";\\r\\n\\r\\n WidgetProgress.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v != undefined) {\\r\\n this.properties[\\\"value\\\"] = v;\\r\\n }\\r\\n };\\r\\n\\r\\n WidgetProgress.prototype.onDrawForeground = function(ctx) {\\r\\n //border\\r\\n ctx.lineWidth = 1;\\r\\n ctx.fillStyle = this.properties.color;\\r\\n var v =\\r\\n (this.properties.value - this.properties.min) /\\r\\n (this.properties.max - this.properties.min);\\r\\n v = Math.min(1, v);\\r\\n v = Math.max(0, v);\\r\\n ctx.fillRect(2, 2, (this.size[0] - 4) * v, this.size[1] - 4);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"widget/progress\\\", WidgetProgress);\\r\\n\\r\\n function WidgetText() {\\r\\n this.addInputs(\\\"\\\", 0);\\r\\n this.properties = {\\r\\n value: \\\"...\\\",\\r\\n font: \\\"Arial\\\",\\r\\n fontsize: 18,\\r\\n color: \\\"#AAA\\\",\\r\\n align: \\\"left\\\",\\r\\n glowSize: 0,\\r\\n decimals: 1\\r\\n };\\r\\n }\\r\\n\\r\\n WidgetText.title = \\\"Text\\\";\\r\\n WidgetText.desc = \\\"Shows the input value\\\";\\r\\n WidgetText.widgets = [\\r\\n { name: \\\"resize\\\", text: \\\"Resize box\\\", type: \\\"button\\\" },\\r\\n { name: \\\"led_text\\\", text: \\\"LED\\\", type: \\\"minibutton\\\" },\\r\\n { name: \\\"normal_text\\\", text: \\\"Normal\\\", type: \\\"minibutton\\\" }\\r\\n ];\\r\\n\\r\\n WidgetText.prototype.onDrawForeground = function(ctx) {\\r\\n //ctx.fillStyle=\\\"#000\\\";\\r\\n //ctx.fillRect(0,0,100,60);\\r\\n ctx.fillStyle = this.properties[\\\"color\\\"];\\r\\n var v = this.properties[\\\"value\\\"];\\r\\n\\r\\n if (this.properties[\\\"glowSize\\\"]) {\\r\\n ctx.shadowColor = this.properties.color;\\r\\n ctx.shadowOffsetX = 0;\\r\\n ctx.shadowOffsetY = 0;\\r\\n ctx.shadowBlur = this.properties[\\\"glowSize\\\"];\\r\\n } else {\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n }\\r\\n\\r\\n var fontsize = this.properties[\\\"fontsize\\\"];\\r\\n\\r\\n ctx.textAlign = this.properties[\\\"align\\\"];\\r\\n ctx.font = fontsize.toString() + \\\"px \\\" + this.properties[\\\"font\\\"];\\r\\n this.str =\\r\\n typeof v == \\\"number\\\" ? v.toFixed(this.properties[\\\"decimals\\\"]) : v;\\r\\n\\r\\n if (typeof this.str == \\\"string\\\") {\\r\\n var lines = this.str.split(\\\"\\\\\\\\n\\\");\\r\\n for (var i in lines) {\\r\\n ctx.fillText(\\r\\n lines[i],\\r\\n this.properties[\\\"align\\\"] == \\\"left\\\" ? 15 : this.size[0] - 15,\\r\\n fontsize * -0.15 + fontsize * (parseInt(i) + 1)\\r\\n );\\r\\n }\\r\\n }\\r\\n\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n this.last_ctx = ctx;\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n };\\r\\n\\r\\n WidgetText.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v != null) {\\r\\n this.properties[\\\"value\\\"] = v;\\r\\n }\\r\\n //this.setDirtyCanvas(true);\\r\\n };\\r\\n\\r\\n WidgetText.prototype.resize = function() {\\r\\n if (!this.last_ctx) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var lines = this.str.split(\\\"\\\\\\\\n\\\");\\r\\n this.last_ctx.font =\\r\\n this.properties[\\\"fontsize\\\"] + \\\"px \\\" + this.properties[\\\"font\\\"];\\r\\n var max = 0;\\r\\n for (var i in lines) {\\r\\n var w = this.last_ctx.measureText(lines[i]).width;\\r\\n if (max < w) {\\r\\n max = w;\\r\\n }\\r\\n }\\r\\n this.size[0] = max + 20;\\r\\n this.size[1] = 4 + lines.length * this.properties[\\\"fontsize\\\"];\\r\\n\\r\\n this.setDirtyCanvas(true);\\r\\n };\\r\\n\\r\\n WidgetText.prototype.onPropertyChanged = function(name, value) {\\r\\n this.properties[name] = value;\\r\\n this.str = typeof value == \\\"number\\\" ? value.toFixed(3) : value;\\r\\n //this.resize();\\r\\n return true;\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"widget/text\\\", WidgetText);\\r\\n\\r\\n function WidgetPanel() {\\r\\n this.size = [200, 100];\\r\\n this.properties = {\\r\\n borderColor: \\\"#ffffff\\\",\\r\\n bgcolorTop: \\\"#f0f0f0\\\",\\r\\n bgcolorBottom: \\\"#e0e0e0\\\",\\r\\n shadowSize: 2,\\r\\n borderRadius: 3\\r\\n };\\r\\n }\\r\\n\\r\\n WidgetPanel.title = \\\"Panel\\\";\\r\\n WidgetPanel.desc = \\\"Non interactive panel\\\";\\r\\n WidgetPanel.widgets = [{ name: \\\"update\\\", text: \\\"Update\\\", type: \\\"button\\\" }];\\r\\n\\r\\n WidgetPanel.prototype.createGradient = function(ctx) {\\r\\n if (\\r\\n this.properties[\\\"bgcolorTop\\\"] == \\\"\\\" ||\\r\\n this.properties[\\\"bgcolorBottom\\\"] == \\\"\\\"\\r\\n ) {\\r\\n this.lineargradient = 0;\\r\\n return;\\r\\n }\\r\\n\\r\\n this.lineargradient = ctx.createLinearGradient(0, 0, 0, this.size[1]);\\r\\n this.lineargradient.addColorStop(0, this.properties[\\\"bgcolorTop\\\"]);\\r\\n this.lineargradient.addColorStop(1, this.properties[\\\"bgcolorBottom\\\"]);\\r\\n };\\r\\n\\r\\n WidgetPanel.prototype.onDrawForeground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.lineargradient == null) {\\r\\n this.createGradient(ctx);\\r\\n }\\r\\n\\r\\n if (!this.lineargradient) {\\r\\n return;\\r\\n }\\r\\n\\r\\n ctx.lineWidth = 1;\\r\\n ctx.strokeStyle = this.properties[\\\"borderColor\\\"];\\r\\n //ctx.fillStyle = \\\"#ebebeb\\\";\\r\\n ctx.fillStyle = this.lineargradient;\\r\\n\\r\\n if (this.properties[\\\"shadowSize\\\"]) {\\r\\n ctx.shadowColor = \\\"#000\\\";\\r\\n ctx.shadowOffsetX = 0;\\r\\n ctx.shadowOffsetY = 0;\\r\\n ctx.shadowBlur = this.properties[\\\"shadowSize\\\"];\\r\\n } else {\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n }\\r\\n\\r\\n ctx.roundRect(\\r\\n 0,\\r\\n 0,\\r\\n this.size[0] - 1,\\r\\n this.size[1] - 1,\\r\\n this.properties[\\\"shadowSize\\\"]\\r\\n );\\r\\n ctx.fill();\\r\\n ctx.shadowColor = \\\"transparent\\\";\\r\\n ctx.stroke();\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"widget/panel\\\", WidgetPanel);\\r\\n})(this);\\r\\n\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n function GamepadInput() {\\r\\n this.addOutput(\\\"left_x_axis\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"left_y_axis\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"button_pressed\\\", LiteGraph.EVENT);\\r\\n this.properties = { gamepad_index: 0, threshold: 0.1 };\\r\\n\\r\\n this._left_axis = new Float32Array(2);\\r\\n this._right_axis = new Float32Array(2);\\r\\n this._triggers = new Float32Array(2);\\r\\n this._previous_buttons = new Uint8Array(17);\\r\\n this._current_buttons = new Uint8Array(17);\\r\\n }\\r\\n\\r\\n GamepadInput.title = \\\"Gamepad\\\";\\r\\n GamepadInput.desc = \\\"gets the input of the gamepad\\\";\\r\\n\\r\\n GamepadInput.CENTER = 0;\\r\\n GamepadInput.LEFT = 1;\\r\\n GamepadInput.RIGHT = 2;\\r\\n GamepadInput.UP = 4;\\r\\n GamepadInput.DOWN = 8;\\r\\n\\r\\n GamepadInput.zero = new Float32Array(2);\\r\\n GamepadInput.buttons = [\\r\\n \\\"a\\\",\\r\\n \\\"b\\\",\\r\\n \\\"x\\\",\\r\\n \\\"y\\\",\\r\\n \\\"lb\\\",\\r\\n \\\"rb\\\",\\r\\n \\\"lt\\\",\\r\\n \\\"rt\\\",\\r\\n \\\"back\\\",\\r\\n \\\"start\\\",\\r\\n \\\"ls\\\",\\r\\n \\\"rs\\\",\\r\\n \\\"home\\\"\\r\\n ];\\r\\n\\r\\n GamepadInput.prototype.onExecute = function() {\\r\\n //get gamepad\\r\\n var gamepad = this.getGamepad();\\r\\n var threshold = this.properties.threshold || 0.0;\\r\\n\\r\\n if (gamepad) {\\r\\n this._left_axis[0] =\\r\\n Math.abs(gamepad.xbox.axes[\\\"lx\\\"]) > threshold\\r\\n ? gamepad.xbox.axes[\\\"lx\\\"]\\r\\n : 0;\\r\\n this._left_axis[1] =\\r\\n Math.abs(gamepad.xbox.axes[\\\"ly\\\"]) > threshold\\r\\n ? gamepad.xbox.axes[\\\"ly\\\"]\\r\\n : 0;\\r\\n this._right_axis[0] =\\r\\n Math.abs(gamepad.xbox.axes[\\\"rx\\\"]) > threshold\\r\\n ? gamepad.xbox.axes[\\\"rx\\\"]\\r\\n : 0;\\r\\n this._right_axis[1] =\\r\\n Math.abs(gamepad.xbox.axes[\\\"ry\\\"]) > threshold\\r\\n ? gamepad.xbox.axes[\\\"ry\\\"]\\r\\n : 0;\\r\\n this._triggers[0] =\\r\\n Math.abs(gamepad.xbox.axes[\\\"ltrigger\\\"]) > threshold\\r\\n ? gamepad.xbox.axes[\\\"ltrigger\\\"]\\r\\n : 0;\\r\\n this._triggers[1] =\\r\\n Math.abs(gamepad.xbox.axes[\\\"rtrigger\\\"]) > threshold\\r\\n ? gamepad.xbox.axes[\\\"rtrigger\\\"]\\r\\n : 0;\\r\\n }\\r\\n\\r\\n if (this.outputs) {\\r\\n for (var i = 0; i < this.outputs.length; i++) {\\r\\n var output = this.outputs[i];\\r\\n if (!output.links || !output.links.length) {\\r\\n continue;\\r\\n }\\r\\n var v = null;\\r\\n\\r\\n if (gamepad) {\\r\\n switch (output.name) {\\r\\n case \\\"left_axis\\\":\\r\\n v = this._left_axis;\\r\\n break;\\r\\n case \\\"right_axis\\\":\\r\\n v = this._right_axis;\\r\\n break;\\r\\n case \\\"left_x_axis\\\":\\r\\n v = this._left_axis[0];\\r\\n break;\\r\\n case \\\"left_y_axis\\\":\\r\\n v = this._left_axis[1];\\r\\n break;\\r\\n case \\\"right_x_axis\\\":\\r\\n v = this._right_axis[0];\\r\\n break;\\r\\n case \\\"right_y_axis\\\":\\r\\n v = this._right_axis[1];\\r\\n break;\\r\\n case \\\"trigger_left\\\":\\r\\n v = this._triggers[0];\\r\\n break;\\r\\n case \\\"trigger_right\\\":\\r\\n v = this._triggers[1];\\r\\n break;\\r\\n case \\\"a_button\\\":\\r\\n v = gamepad.xbox.buttons[\\\"a\\\"] ? 1 : 0;\\r\\n break;\\r\\n case \\\"b_button\\\":\\r\\n v = gamepad.xbox.buttons[\\\"b\\\"] ? 1 : 0;\\r\\n break;\\r\\n case \\\"x_button\\\":\\r\\n v = gamepad.xbox.buttons[\\\"x\\\"] ? 1 : 0;\\r\\n break;\\r\\n case \\\"y_button\\\":\\r\\n v = gamepad.xbox.buttons[\\\"y\\\"] ? 1 : 0;\\r\\n break;\\r\\n case \\\"lb_button\\\":\\r\\n v = gamepad.xbox.buttons[\\\"lb\\\"] ? 1 : 0;\\r\\n break;\\r\\n case \\\"rb_button\\\":\\r\\n v = gamepad.xbox.buttons[\\\"rb\\\"] ? 1 : 0;\\r\\n break;\\r\\n case \\\"ls_button\\\":\\r\\n v = gamepad.xbox.buttons[\\\"ls\\\"] ? 1 : 0;\\r\\n break;\\r\\n case \\\"rs_button\\\":\\r\\n v = gamepad.xbox.buttons[\\\"rs\\\"] ? 1 : 0;\\r\\n break;\\r\\n case \\\"hat_left\\\":\\r\\n v = gamepad.xbox.hatmap & GamepadInput.LEFT;\\r\\n break;\\r\\n case \\\"hat_right\\\":\\r\\n v = gamepad.xbox.hatmap & GamepadInput.RIGHT;\\r\\n break;\\r\\n case \\\"hat_up\\\":\\r\\n v = gamepad.xbox.hatmap & GamepadInput.UP;\\r\\n break;\\r\\n case \\\"hat_down\\\":\\r\\n v = gamepad.xbox.hatmap & GamepadInput.DOWN;\\r\\n break;\\r\\n case \\\"hat\\\":\\r\\n v = gamepad.xbox.hatmap;\\r\\n break;\\r\\n case \\\"start_button\\\":\\r\\n v = gamepad.xbox.buttons[\\\"start\\\"] ? 1 : 0;\\r\\n break;\\r\\n case \\\"back_button\\\":\\r\\n v = gamepad.xbox.buttons[\\\"back\\\"] ? 1 : 0;\\r\\n break;\\r\\n case \\\"button_pressed\\\":\\r\\n for (\\r\\n var j = 0;\\r\\n j < this._current_buttons.length;\\r\\n ++j\\r\\n ) {\\r\\n if (\\r\\n this._current_buttons[j] &&\\r\\n !this._previous_buttons[j]\\r\\n ) {\\r\\n this.triggerSlot(\\r\\n i,\\r\\n GamepadInput.buttons[j]\\r\\n );\\r\\n }\\r\\n }\\r\\n break;\\r\\n default:\\r\\n break;\\r\\n }\\r\\n } else {\\r\\n //if no gamepad is connected, output 0\\r\\n switch (output.name) {\\r\\n case \\\"button_pressed\\\":\\r\\n break;\\r\\n case \\\"left_axis\\\":\\r\\n case \\\"right_axis\\\":\\r\\n v = GamepadInput.zero;\\r\\n break;\\r\\n default:\\r\\n v = 0;\\r\\n }\\r\\n }\\r\\n this.setOutputData(i, v);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n\\tGamepadInput.mapping = {a:0,b:1,x:2,y:3,lb:4,rb:5,lt:6,rt:7,back:8,start:9,ls:10,rs:11 };\\r\\n\\tGamepadInput.mapping_array = [\\\"a\\\",\\\"b\\\",\\\"x\\\",\\\"y\\\",\\\"lb\\\",\\\"rb\\\",\\\"lt\\\",\\\"rt\\\",\\\"back\\\",\\\"start\\\",\\\"ls\\\",\\\"rs\\\"];\\r\\n\\r\\n GamepadInput.prototype.getGamepad = function() {\\r\\n var getGamepads =\\r\\n navigator.getGamepads ||\\r\\n navigator.webkitGetGamepads ||\\r\\n navigator.mozGetGamepads;\\r\\n if (!getGamepads) {\\r\\n return null;\\r\\n }\\r\\n var gamepads = getGamepads.call(navigator);\\r\\n var gamepad = null;\\r\\n\\r\\n this._previous_buttons.set(this._current_buttons);\\r\\n\\r\\n //pick the first connected\\r\\n for (var i = this.properties.gamepad_index; i < 4; i++) {\\r\\n if (!gamepads[i]) {\\r\\n continue;\\r\\n }\\r\\n gamepad = gamepads[i];\\r\\n\\r\\n //xbox controller mapping\\r\\n var xbox = this.xbox_mapping;\\r\\n if (!xbox) {\\r\\n xbox = this.xbox_mapping = {\\r\\n axes: [],\\r\\n buttons: {},\\r\\n hat: \\\"\\\",\\r\\n hatmap: GamepadInput.CENTER\\r\\n };\\r\\n }\\r\\n\\r\\n xbox.axes[\\\"lx\\\"] = gamepad.axes[0];\\r\\n xbox.axes[\\\"ly\\\"] = gamepad.axes[1];\\r\\n xbox.axes[\\\"rx\\\"] = gamepad.axes[2];\\r\\n xbox.axes[\\\"ry\\\"] = gamepad.axes[3];\\r\\n xbox.axes[\\\"ltrigger\\\"] = gamepad.buttons[6].value;\\r\\n xbox.axes[\\\"rtrigger\\\"] = gamepad.buttons[7].value;\\r\\n xbox.hat = \\\"\\\";\\r\\n xbox.hatmap = GamepadInput.CENTER;\\r\\n\\r\\n for (var j = 0; j < gamepad.buttons.length; j++) {\\r\\n this._current_buttons[j] = gamepad.buttons[j].pressed;\\r\\n\\r\\n\\t\\t\\t\\tif(j < 12)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\txbox.buttons[ GamepadInput.mapping_array[j] ] = gamepad.buttons[j].pressed;\\r\\n\\t\\t\\t\\t\\tif(gamepad.buttons[j].was_pressed)\\r\\n\\t\\t\\t\\t\\t\\tthis.trigger( GamepadInput.mapping_array[j] + \\\"_button_event\\\" );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse //mapping of XBOX\\r\\n\\t\\t\\t\\t\\tswitch ( j ) //I use a switch to ensure that a player with another gamepad could play\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tcase 12:\\r\\n\\t\\t\\t\\t\\t\\t\\tif (gamepad.buttons[j].pressed) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\txbox.hat += \\\"up\\\";\\r\\n\\t\\t\\t\\t\\t\\t\\t\\txbox.hatmap |= GamepadInput.UP;\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\tcase 13:\\r\\n\\t\\t\\t\\t\\t\\t\\tif (gamepad.buttons[j].pressed) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\txbox.hat += \\\"down\\\";\\r\\n\\t\\t\\t\\t\\t\\t\\t\\txbox.hatmap |= GamepadInput.DOWN;\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\tcase 14:\\r\\n\\t\\t\\t\\t\\t\\t\\tif (gamepad.buttons[j].pressed) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\txbox.hat += \\\"left\\\";\\r\\n\\t\\t\\t\\t\\t\\t\\t\\txbox.hatmap |= GamepadInput.LEFT;\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\tcase 15:\\r\\n\\t\\t\\t\\t\\t\\t\\tif (gamepad.buttons[j].pressed) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\txbox.hat += \\\"right\\\";\\r\\n\\t\\t\\t\\t\\t\\t\\t\\txbox.hatmap |= GamepadInput.RIGHT;\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\tcase 16:\\r\\n\\t\\t\\t\\t\\t\\t\\txbox.buttons[\\\"home\\\"] = gamepad.buttons[j].pressed;\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\t}\\r\\n }\\r\\n gamepad.xbox = xbox;\\r\\n return gamepad;\\r\\n }\\r\\n };\\r\\n\\r\\n GamepadInput.prototype.onDrawBackground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //render gamepad state?\\r\\n var la = this._left_axis;\\r\\n var ra = this._right_axis;\\r\\n ctx.strokeStyle = \\\"#88A\\\";\\r\\n ctx.strokeRect(\\r\\n (la[0] + 1) * 0.5 * this.size[0] - 4,\\r\\n (la[1] + 1) * 0.5 * this.size[1] - 4,\\r\\n 8,\\r\\n 8\\r\\n );\\r\\n ctx.strokeStyle = \\\"#8A8\\\";\\r\\n ctx.strokeRect(\\r\\n (ra[0] + 1) * 0.5 * this.size[0] - 4,\\r\\n (ra[1] + 1) * 0.5 * this.size[1] - 4,\\r\\n 8,\\r\\n 8\\r\\n );\\r\\n var h = this.size[1] / this._current_buttons.length;\\r\\n ctx.fillStyle = \\\"#AEB\\\";\\r\\n for (var i = 0; i < this._current_buttons.length; ++i) {\\r\\n if (this._current_buttons[i]) {\\r\\n ctx.fillRect(0, h * i, 6, h);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n GamepadInput.prototype.onGetOutputs = function() {\\r\\n return [\\r\\n [\\\"left_axis\\\", \\\"vec2\\\"],\\r\\n [\\\"right_axis\\\", \\\"vec2\\\"],\\r\\n [\\\"left_x_axis\\\", \\\"number\\\"],\\r\\n [\\\"left_y_axis\\\", \\\"number\\\"],\\r\\n [\\\"right_x_axis\\\", \\\"number\\\"],\\r\\n [\\\"right_y_axis\\\", \\\"number\\\"],\\r\\n [\\\"trigger_left\\\", \\\"number\\\"],\\r\\n [\\\"trigger_right\\\", \\\"number\\\"],\\r\\n [\\\"a_button\\\", \\\"number\\\"],\\r\\n [\\\"b_button\\\", \\\"number\\\"],\\r\\n [\\\"x_button\\\", \\\"number\\\"],\\r\\n [\\\"y_button\\\", \\\"number\\\"],\\r\\n [\\\"lb_button\\\", \\\"number\\\"],\\r\\n [\\\"rb_button\\\", \\\"number\\\"],\\r\\n [\\\"ls_button\\\", \\\"number\\\"],\\r\\n [\\\"rs_button\\\", \\\"number\\\"],\\r\\n [\\\"start_button\\\", \\\"number\\\"],\\r\\n [\\\"back_button\\\", \\\"number\\\"],\\r\\n [\\\"a_button_event\\\", LiteGraph.EVENT ],\\r\\n [\\\"b_button_event\\\", LiteGraph.EVENT ],\\r\\n [\\\"x_button_event\\\", LiteGraph.EVENT ],\\r\\n [\\\"y_button_event\\\", LiteGraph.EVENT ],\\r\\n [\\\"lb_button_event\\\", LiteGraph.EVENT ],\\r\\n [\\\"rb_button_event\\\", LiteGraph.EVENT ],\\r\\n [\\\"ls_button_event\\\", LiteGraph.EVENT ],\\r\\n [\\\"rs_button_event\\\", LiteGraph.EVENT ],\\r\\n [\\\"start_button_event\\\", LiteGraph.EVENT ],\\r\\n [\\\"back_button_event\\\", LiteGraph.EVENT ],\\r\\n [\\\"hat_left\\\", \\\"number\\\"],\\r\\n [\\\"hat_right\\\", \\\"number\\\"],\\r\\n [\\\"hat_up\\\", \\\"number\\\"],\\r\\n [\\\"hat_down\\\", \\\"number\\\"],\\r\\n [\\\"hat\\\", \\\"number\\\"],\\r\\n [\\\"button_pressed\\\", LiteGraph.EVENT]\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"input/gamepad\\\", GamepadInput);\\r\\n})(this);\\r\\n\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n //Converter\\r\\n function Converter() {\\r\\n this.addInput(\\\"in\\\", \\\"*\\\");\\r\\n this.size = [80, 30];\\r\\n }\\r\\n\\r\\n Converter.title = \\\"Converter\\\";\\r\\n Converter.desc = \\\"type A to type B\\\";\\r\\n\\r\\n Converter.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.outputs) {\\r\\n for (var i = 0; i < this.outputs.length; i++) {\\r\\n var output = this.outputs[i];\\r\\n if (!output.links || !output.links.length) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n var result = null;\\r\\n switch (output.name) {\\r\\n case \\\"number\\\":\\r\\n result = v.length ? v[0] : parseFloat(v);\\r\\n break;\\r\\n case \\\"vec2\\\":\\r\\n case \\\"vec3\\\":\\r\\n case \\\"vec4\\\":\\r\\n var result = null;\\r\\n var count = 1;\\r\\n switch (output.name) {\\r\\n case \\\"vec2\\\":\\r\\n count = 2;\\r\\n break;\\r\\n case \\\"vec3\\\":\\r\\n count = 3;\\r\\n break;\\r\\n case \\\"vec4\\\":\\r\\n count = 4;\\r\\n break;\\r\\n }\\r\\n\\r\\n var result = new Float32Array(count);\\r\\n if (v.length) {\\r\\n for (\\r\\n var j = 0;\\r\\n j < v.length && j < result.length;\\r\\n j++\\r\\n ) {\\r\\n result[j] = v[j];\\r\\n }\\r\\n } else {\\r\\n result[0] = parseFloat(v);\\r\\n }\\r\\n break;\\r\\n }\\r\\n this.setOutputData(i, result);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n Converter.prototype.onGetOutputs = function() {\\r\\n return [\\r\\n [\\\"number\\\", \\\"number\\\"],\\r\\n [\\\"vec2\\\", \\\"vec2\\\"],\\r\\n [\\\"vec3\\\", \\\"vec3\\\"],\\r\\n [\\\"vec4\\\", \\\"vec4\\\"]\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/converter\\\", Converter);\\r\\n\\r\\n //Bypass\\r\\n function Bypass() {\\r\\n this.addInput(\\\"in\\\");\\r\\n this.addOutput(\\\"out\\\");\\r\\n this.size = [80, 30];\\r\\n }\\r\\n\\r\\n Bypass.title = \\\"Bypass\\\";\\r\\n Bypass.desc = \\\"removes the type\\\";\\r\\n\\r\\n Bypass.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n this.setOutputData(0, v);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/bypass\\\", Bypass);\\r\\n\\r\\n function ToNumber() {\\r\\n this.addInput(\\\"in\\\");\\r\\n this.addOutput(\\\"out\\\");\\r\\n }\\r\\n\\r\\n ToNumber.title = \\\"to Number\\\";\\r\\n ToNumber.desc = \\\"Cast to number\\\";\\r\\n\\r\\n ToNumber.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n this.setOutputData(0, Number(v));\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/to_number\\\", ToNumber);\\r\\n\\r\\n function MathRange() {\\r\\n this.addInput(\\\"in\\\", \\\"number\\\", { locked: true });\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\", { locked: true });\\r\\n\\r\\n this.addProperty(\\\"in\\\", 0);\\r\\n this.addProperty(\\\"in_min\\\", 0);\\r\\n this.addProperty(\\\"in_max\\\", 1);\\r\\n this.addProperty(\\\"out_min\\\", 0);\\r\\n this.addProperty(\\\"out_max\\\", 1);\\r\\n\\r\\n this.size = [80, 30];\\r\\n }\\r\\n\\r\\n MathRange.title = \\\"Range\\\";\\r\\n MathRange.desc = \\\"Convert a number from one range to another\\\";\\r\\n\\r\\n MathRange.prototype.getTitle = function() {\\r\\n if (this.flags.collapsed) {\\r\\n return (this._last_v || 0).toFixed(2);\\r\\n }\\r\\n return this.title;\\r\\n };\\r\\n\\r\\n MathRange.prototype.onExecute = function() {\\r\\n if (this.inputs) {\\r\\n for (var i = 0; i < this.inputs.length; i++) {\\r\\n var input = this.inputs[i];\\r\\n var v = this.getInputData(i);\\r\\n if (v === undefined) {\\r\\n continue;\\r\\n }\\r\\n this.properties[input.name] = v;\\r\\n }\\r\\n }\\r\\n\\r\\n var v = this.properties[\\\"in\\\"];\\r\\n if (v === undefined || v === null || v.constructor !== Number) {\\r\\n v = 0;\\r\\n }\\r\\n\\r\\n var in_min = this.properties.in_min;\\r\\n var in_max = this.properties.in_max;\\r\\n var out_min = this.properties.out_min;\\r\\n var out_max = this.properties.out_max;\\r\\n\\r\\n this._last_v =\\r\\n ((v - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min;\\r\\n this.setOutputData(0, this._last_v);\\r\\n };\\r\\n\\r\\n MathRange.prototype.onDrawBackground = function(ctx) {\\r\\n //show the current value\\r\\n if (this._last_v) {\\r\\n this.outputs[0].label = this._last_v.toFixed(3);\\r\\n } else {\\r\\n this.outputs[0].label = \\\"?\\\";\\r\\n }\\r\\n };\\r\\n\\r\\n MathRange.prototype.onGetInputs = function() {\\r\\n return [\\r\\n [\\\"in_min\\\", \\\"number\\\"],\\r\\n [\\\"in_max\\\", \\\"number\\\"],\\r\\n [\\\"out_min\\\", \\\"number\\\"],\\r\\n [\\\"out_max\\\", \\\"number\\\"]\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/range\\\", MathRange);\\r\\n\\r\\n function MathRand() {\\r\\n this.addOutput(\\\"value\\\", \\\"number\\\");\\r\\n this.addProperty(\\\"min\\\", 0);\\r\\n this.addProperty(\\\"max\\\", 1);\\r\\n this.size = [80, 30];\\r\\n }\\r\\n\\r\\n MathRand.title = \\\"Rand\\\";\\r\\n MathRand.desc = \\\"Random number\\\";\\r\\n\\r\\n MathRand.prototype.onExecute = function() {\\r\\n if (this.inputs) {\\r\\n for (var i = 0; i < this.inputs.length; i++) {\\r\\n var input = this.inputs[i];\\r\\n var v = this.getInputData(i);\\r\\n if (v === undefined) {\\r\\n continue;\\r\\n }\\r\\n this.properties[input.name] = v;\\r\\n }\\r\\n }\\r\\n\\r\\n var min = this.properties.min;\\r\\n var max = this.properties.max;\\r\\n this._last_v = Math.random() * (max - min) + min;\\r\\n this.setOutputData(0, this._last_v);\\r\\n };\\r\\n\\r\\n MathRand.prototype.onDrawBackground = function(ctx) {\\r\\n //show the current value\\r\\n this.outputs[0].label = (this._last_v || 0).toFixed(3);\\r\\n };\\r\\n\\r\\n MathRand.prototype.onGetInputs = function() {\\r\\n return [[\\\"min\\\", \\\"number\\\"], [\\\"max\\\", \\\"number\\\"]];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/rand\\\", MathRand);\\r\\n\\r\\n //basic continuous noise\\r\\n function MathNoise() {\\r\\n this.addInput(\\\"in\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n this.addProperty(\\\"min\\\", 0);\\r\\n this.addProperty(\\\"max\\\", 1);\\r\\n this.addProperty(\\\"smooth\\\", true);\\r\\n this.size = [90, 30];\\r\\n }\\r\\n\\r\\n MathNoise.title = \\\"Noise\\\";\\r\\n MathNoise.desc = \\\"Random number with temporal continuity\\\";\\r\\n MathNoise.data = null;\\r\\n\\r\\n MathNoise.getValue = function(f, smooth) {\\r\\n if (!MathNoise.data) {\\r\\n MathNoise.data = new Float32Array(1024);\\r\\n for (var i = 0; i < MathNoise.data.length; ++i) {\\r\\n MathNoise.data[i] = Math.random();\\r\\n }\\r\\n }\\r\\n f = f % 1024;\\r\\n if (f < 0) {\\r\\n f += 1024;\\r\\n }\\r\\n var f_min = Math.floor(f);\\r\\n var f = f - f_min;\\r\\n var r1 = MathNoise.data[f_min];\\r\\n var r2 = MathNoise.data[f_min == 1023 ? 0 : f_min + 1];\\r\\n if (smooth) {\\r\\n f = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);\\r\\n }\\r\\n return r1 * (1 - f) + r2 * f;\\r\\n };\\r\\n\\r\\n MathNoise.prototype.onExecute = function() {\\r\\n var f = this.getInputData(0) || 0;\\r\\n var r = MathNoise.getValue(f, this.properties.smooth);\\r\\n var min = this.properties.min;\\r\\n var max = this.properties.max;\\r\\n this._last_v = r * (max - min) + min;\\r\\n this.setOutputData(0, this._last_v);\\r\\n };\\r\\n\\r\\n MathNoise.prototype.onDrawBackground = function(ctx) {\\r\\n //show the current value\\r\\n this.outputs[0].label = (this._last_v || 0).toFixed(3);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/noise\\\", MathNoise);\\r\\n\\r\\n //generates spikes every random time\\r\\n function MathSpikes() {\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n this.addProperty(\\\"min_time\\\", 1);\\r\\n this.addProperty(\\\"max_time\\\", 2);\\r\\n this.addProperty(\\\"duration\\\", 0.2);\\r\\n this.size = [90, 30];\\r\\n this._remaining_time = 0;\\r\\n this._blink_time = 0;\\r\\n }\\r\\n\\r\\n MathSpikes.title = \\\"Spikes\\\";\\r\\n MathSpikes.desc = \\\"spike every random time\\\";\\r\\n\\r\\n MathSpikes.prototype.onExecute = function() {\\r\\n var dt = this.graph.elapsed_time; //in secs\\r\\n\\r\\n this._remaining_time -= dt;\\r\\n this._blink_time -= dt;\\r\\n\\r\\n var v = 0;\\r\\n if (this._blink_time > 0) {\\r\\n var f = this._blink_time / this.properties.duration;\\r\\n v = 1 / (Math.pow(f * 8 - 4, 4) + 1);\\r\\n }\\r\\n\\r\\n if (this._remaining_time < 0) {\\r\\n this._remaining_time =\\r\\n Math.random() *\\r\\n (this.properties.max_time - this.properties.min_time) +\\r\\n this.properties.min_time;\\r\\n this._blink_time = this.properties.duration;\\r\\n this.boxcolor = \\\"#FFF\\\";\\r\\n } else {\\r\\n this.boxcolor = \\\"#000\\\";\\r\\n }\\r\\n this.setOutputData(0, v);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/spikes\\\", MathSpikes);\\r\\n\\r\\n //Math clamp\\r\\n function MathClamp() {\\r\\n this.addInput(\\\"in\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n this.size = [80, 30];\\r\\n this.addProperty(\\\"min\\\", 0);\\r\\n this.addProperty(\\\"max\\\", 1);\\r\\n }\\r\\n\\r\\n MathClamp.title = \\\"Clamp\\\";\\r\\n MathClamp.desc = \\\"Clamp number between min and max\\\";\\r\\n MathClamp.filter = \\\"shader\\\";\\r\\n\\r\\n MathClamp.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n v = Math.max(this.properties.min, v);\\r\\n v = Math.min(this.properties.max, v);\\r\\n this.setOutputData(0, v);\\r\\n };\\r\\n\\r\\n MathClamp.prototype.getCode = function(lang) {\\r\\n var code = \\\"\\\";\\r\\n if (this.isInputConnected(0)) {\\r\\n code +=\\r\\n \\\"clamp({{0}},\\\" +\\r\\n this.properties.min +\\r\\n \\\",\\\" +\\r\\n this.properties.max +\\r\\n \\\")\\\";\\r\\n }\\r\\n return code;\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/clamp\\\", MathClamp);\\r\\n\\r\\n //Math ABS\\r\\n function MathLerp() {\\r\\n this.properties = { f: 0.5 };\\r\\n this.addInput(\\\"A\\\", \\\"number\\\");\\r\\n this.addInput(\\\"B\\\", \\\"number\\\");\\r\\n\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n MathLerp.title = \\\"Lerp\\\";\\r\\n MathLerp.desc = \\\"Linear Interpolation\\\";\\r\\n\\r\\n MathLerp.prototype.onExecute = function() {\\r\\n var v1 = this.getInputData(0);\\r\\n if (v1 == null) {\\r\\n v1 = 0;\\r\\n }\\r\\n var v2 = this.getInputData(1);\\r\\n if (v2 == null) {\\r\\n v2 = 0;\\r\\n }\\r\\n\\r\\n var f = this.properties.f;\\r\\n\\r\\n var _f = this.getInputData(2);\\r\\n if (_f !== undefined) {\\r\\n f = _f;\\r\\n }\\r\\n\\r\\n this.setOutputData(0, v1 * (1 - f) + v2 * f);\\r\\n };\\r\\n\\r\\n MathLerp.prototype.onGetInputs = function() {\\r\\n return [[\\\"f\\\", \\\"number\\\"]];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/lerp\\\", MathLerp);\\r\\n\\r\\n //Math ABS\\r\\n function MathAbs() {\\r\\n this.addInput(\\\"in\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n this.size = [80, 30];\\r\\n }\\r\\n\\r\\n MathAbs.title = \\\"Abs\\\";\\r\\n MathAbs.desc = \\\"Absolute\\\";\\r\\n\\r\\n MathAbs.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n this.setOutputData(0, Math.abs(v));\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/abs\\\", MathAbs);\\r\\n\\r\\n //Math Floor\\r\\n function MathFloor() {\\r\\n this.addInput(\\\"in\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n this.size = [80, 30];\\r\\n }\\r\\n\\r\\n MathFloor.title = \\\"Floor\\\";\\r\\n MathFloor.desc = \\\"Floor number to remove fractional part\\\";\\r\\n\\r\\n MathFloor.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n this.setOutputData(0, Math.floor(v));\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/floor\\\", MathFloor);\\r\\n\\r\\n //Math frac\\r\\n function MathFrac() {\\r\\n this.addInput(\\\"in\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n this.size = [80, 30];\\r\\n }\\r\\n\\r\\n MathFrac.title = \\\"Frac\\\";\\r\\n MathFrac.desc = \\\"Returns fractional part\\\";\\r\\n\\r\\n MathFrac.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n this.setOutputData(0, v % 1);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/frac\\\", MathFrac);\\r\\n\\r\\n //Math Floor\\r\\n function MathSmoothStep() {\\r\\n this.addInput(\\\"in\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n this.size = [80, 30];\\r\\n this.properties = { A: 0, B: 1 };\\r\\n }\\r\\n\\r\\n MathSmoothStep.title = \\\"Smoothstep\\\";\\r\\n MathSmoothStep.desc = \\\"Smoothstep\\\";\\r\\n\\r\\n MathSmoothStep.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v === undefined) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var edge0 = this.properties.A;\\r\\n var edge1 = this.properties.B;\\r\\n\\r\\n // Scale, bias and saturate x to 0..1 range\\r\\n v = Math.clamp((v - edge0) / (edge1 - edge0), 0.0, 1.0);\\r\\n // Evaluate polynomial\\r\\n v = v * v * (3 - 2 * v);\\r\\n\\r\\n this.setOutputData(0, v);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/smoothstep\\\", MathSmoothStep);\\r\\n\\r\\n //Math scale\\r\\n function MathScale() {\\r\\n this.addInput(\\\"in\\\", \\\"number\\\", { label: \\\"\\\" });\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\", { label: \\\"\\\" });\\r\\n this.size = [80, 30];\\r\\n this.addProperty(\\\"factor\\\", 1);\\r\\n }\\r\\n\\r\\n MathScale.title = \\\"Scale\\\";\\r\\n MathScale.desc = \\\"v * factor\\\";\\r\\n\\r\\n MathScale.prototype.onExecute = function() {\\r\\n var value = this.getInputData(0);\\r\\n if (value != null) {\\r\\n this.setOutputData(0, value * this.properties.factor);\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/scale\\\", MathScale);\\r\\n\\r\\n\\t//Gate\\r\\n\\tfunction Gate() {\\r\\n\\t\\tthis.addInput(\\\"v\\\",\\\"boolean\\\");\\r\\n\\t\\tthis.addInput(\\\"A\\\");\\r\\n\\t\\tthis.addInput(\\\"B\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\");\\r\\n\\t}\\r\\n\\r\\n\\tGate.title = \\\"Gate\\\";\\r\\n\\tGate.desc = \\\"if v is true, then outputs A, otherwise B\\\";\\r\\n\\r\\n\\tGate.prototype.onExecute = function() {\\r\\n\\t\\tvar v = this.getInputData(0);\\r\\n\\t\\tthis.setOutputData(0, this.getInputData( v ? 1 : 2 ));\\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"math/gate\\\", Gate);\\r\\n\\r\\n\\r\\n //Math Average\\r\\n function MathAverageFilter() {\\r\\n this.addInput(\\\"in\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n this.size = [80, 30];\\r\\n this.addProperty(\\\"samples\\\", 10);\\r\\n this._values = new Float32Array(10);\\r\\n this._current = 0;\\r\\n }\\r\\n\\r\\n MathAverageFilter.title = \\\"Average\\\";\\r\\n MathAverageFilter.desc = \\\"Average Filter\\\";\\r\\n\\r\\n MathAverageFilter.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n v = 0;\\r\\n }\\r\\n\\r\\n var num_samples = this._values.length;\\r\\n\\r\\n this._values[this._current % num_samples] = v;\\r\\n this._current += 1;\\r\\n if (this._current > num_samples) {\\r\\n this._current = 0;\\r\\n }\\r\\n\\r\\n var avr = 0;\\r\\n for (var i = 0; i < num_samples; ++i) {\\r\\n avr += this._values[i];\\r\\n }\\r\\n\\r\\n this.setOutputData(0, avr / num_samples);\\r\\n };\\r\\n\\r\\n MathAverageFilter.prototype.onPropertyChanged = function(name, value) {\\r\\n if (value < 1) {\\r\\n value = 1;\\r\\n }\\r\\n this.properties.samples = Math.round(value);\\r\\n var old = this._values;\\r\\n\\r\\n this._values = new Float32Array(this.properties.samples);\\r\\n if (old.length <= this._values.length) {\\r\\n this._values.set(old);\\r\\n } else {\\r\\n this._values.set(old.subarray(0, this._values.length));\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/average\\\", MathAverageFilter);\\r\\n\\r\\n //Math\\r\\n function MathTendTo() {\\r\\n this.addInput(\\\"in\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n this.addProperty(\\\"factor\\\", 0.1);\\r\\n this.size = [80, 30];\\r\\n this._value = null;\\r\\n }\\r\\n\\r\\n MathTendTo.title = \\\"TendTo\\\";\\r\\n MathTendTo.desc = \\\"moves the output value always closer to the input\\\";\\r\\n\\r\\n MathTendTo.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n v = 0;\\r\\n }\\r\\n var f = this.properties.factor;\\r\\n if (this._value == null) {\\r\\n this._value = v;\\r\\n } else {\\r\\n this._value = this._value * (1 - f) + v * f;\\r\\n }\\r\\n this.setOutputData(0, this._value);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/tendTo\\\", MathTendTo);\\r\\n\\r\\n //Math operation\\r\\n function MathOperation() {\\r\\n this.addInput(\\\"A\\\", \\\"number\\\");\\r\\n this.addInput(\\\"B\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"=\\\", \\\"number\\\");\\r\\n this.addProperty(\\\"A\\\", 1);\\r\\n this.addProperty(\\\"B\\\", 1);\\r\\n this.addProperty(\\\"OP\\\", \\\"+\\\", \\\"enum\\\", { values: MathOperation.values });\\r\\n }\\r\\n\\r\\n MathOperation.values = [\\\"+\\\", \\\"-\\\", \\\"*\\\", \\\"/\\\", \\\"%\\\", \\\"^\\\", \\\"max\\\", \\\"min\\\"];\\r\\n\\r\\n\\tMathOperation.title = \\\"Operation\\\";\\r\\n MathOperation.desc = \\\"Easy math operators\\\";\\r\\n MathOperation[\\\"@OP\\\"] = {\\r\\n type: \\\"enum\\\",\\r\\n title: \\\"operation\\\",\\r\\n values: MathOperation.values\\r\\n };\\r\\n MathOperation.size = [100, 60];\\r\\n\\r\\n MathOperation.prototype.getTitle = function() {\\r\\n\\t\\tif(this.properties.OP == \\\"max\\\" || this.properties.OP == \\\"min\\\")\\r\\n\\t\\t\\treturn this.properties.OP + \\\"(A,B)\\\";\\r\\n return \\\"A \\\" + this.properties.OP + \\\" B\\\";\\r\\n };\\r\\n\\r\\n MathOperation.prototype.setValue = function(v) {\\r\\n if (typeof v == \\\"string\\\") {\\r\\n v = parseFloat(v);\\r\\n }\\r\\n this.properties[\\\"value\\\"] = v;\\r\\n };\\r\\n\\r\\n MathOperation.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n var B = this.getInputData(1);\\r\\n if (A != null) {\\r\\n this.properties[\\\"A\\\"] = A;\\r\\n } else {\\r\\n A = this.properties[\\\"A\\\"];\\r\\n }\\r\\n\\r\\n if (B != null) {\\r\\n this.properties[\\\"B\\\"] = B;\\r\\n } else {\\r\\n B = this.properties[\\\"B\\\"];\\r\\n }\\r\\n\\r\\n var result = 0;\\r\\n switch (this.properties.OP) {\\r\\n case \\\"+\\\":\\r\\n result = A + B;\\r\\n break;\\r\\n case \\\"-\\\":\\r\\n result = A - B;\\r\\n break;\\r\\n case \\\"x\\\":\\r\\n case \\\"X\\\":\\r\\n case \\\"*\\\":\\r\\n result = A * B;\\r\\n break;\\r\\n case \\\"/\\\":\\r\\n result = A / B;\\r\\n break;\\r\\n case \\\"%\\\":\\r\\n result = A % B;\\r\\n break;\\r\\n case \\\"^\\\":\\r\\n result = Math.pow(A, B);\\r\\n break;\\r\\n case \\\"max\\\":\\r\\n result = Math.max(A, B);\\r\\n break;\\r\\n case \\\"min\\\":\\r\\n result = Math.min(A, B);\\r\\n break;\\r\\n default:\\r\\n console.warn(\\\"Unknown operation: \\\" + this.properties.OP);\\r\\n }\\r\\n this.setOutputData(0, result);\\r\\n };\\r\\n\\r\\n MathOperation.prototype.onDrawBackground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n ctx.font = \\\"40px Arial\\\";\\r\\n ctx.fillStyle = \\\"#666\\\";\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n ctx.fillText(\\r\\n this.properties.OP,\\r\\n this.size[0] * 0.5,\\r\\n (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5\\r\\n );\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/operation\\\", MathOperation);\\r\\n\\r\\n LiteGraph.registerSearchboxExtra(\\\"math/operation\\\", \\\"MAX\\\", {\\r\\n properties: {OP:\\\"max\\\"},\\r\\n title: \\\"MAX()\\\"\\r\\n });\\r\\n\\r\\n LiteGraph.registerSearchboxExtra(\\\"math/operation\\\", \\\"MIN\\\", {\\r\\n properties: {OP:\\\"min\\\"},\\r\\n title: \\\"MIN()\\\"\\r\\n });\\r\\n\\r\\n //Math compare\\r\\n function MathCompare() {\\r\\n this.addInput(\\\"A\\\", \\\"number\\\");\\r\\n this.addInput(\\\"B\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"A==B\\\", \\\"boolean\\\");\\r\\n this.addOutput(\\\"A!=B\\\", \\\"boolean\\\");\\r\\n this.addProperty(\\\"A\\\", 0);\\r\\n this.addProperty(\\\"B\\\", 0);\\r\\n }\\r\\n\\r\\n MathCompare.title = \\\"Compare\\\";\\r\\n MathCompare.desc = \\\"compares between two values\\\";\\r\\n\\r\\n MathCompare.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n var B = this.getInputData(1);\\r\\n if (A !== undefined) {\\r\\n this.properties[\\\"A\\\"] = A;\\r\\n } else {\\r\\n A = this.properties[\\\"A\\\"];\\r\\n }\\r\\n\\r\\n if (B !== undefined) {\\r\\n this.properties[\\\"B\\\"] = B;\\r\\n } else {\\r\\n B = this.properties[\\\"B\\\"];\\r\\n }\\r\\n\\r\\n for (var i = 0, l = this.outputs.length; i < l; ++i) {\\r\\n var output = this.outputs[i];\\r\\n if (!output.links || !output.links.length) {\\r\\n continue;\\r\\n }\\r\\n var value;\\r\\n switch (output.name) {\\r\\n case \\\"A==B\\\":\\r\\n value = A == B;\\r\\n break;\\r\\n case \\\"A!=B\\\":\\r\\n value = A != B;\\r\\n break;\\r\\n case \\\"A>B\\\":\\r\\n value = A > B;\\r\\n break;\\r\\n case \\\"A=B\\\":\\r\\n value = A >= B;\\r\\n break;\\r\\n }\\r\\n this.setOutputData(i, value);\\r\\n }\\r\\n };\\r\\n\\r\\n MathCompare.prototype.onGetOutputs = function() {\\r\\n return [\\r\\n [\\\"A==B\\\", \\\"boolean\\\"],\\r\\n [\\\"A!=B\\\", \\\"boolean\\\"],\\r\\n [\\\"A>B\\\", \\\"boolean\\\"],\\r\\n [\\\"A=B\\\", \\\"boolean\\\"],\\r\\n [\\\"A<=B\\\", \\\"boolean\\\"]\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/compare\\\", MathCompare);\\r\\n\\r\\n LiteGraph.registerSearchboxExtra(\\\"math/compare\\\", \\\"==\\\", {\\r\\n outputs: [[\\\"A==B\\\", \\\"boolean\\\"]],\\r\\n title: \\\"A==B\\\"\\r\\n });\\r\\n LiteGraph.registerSearchboxExtra(\\\"math/compare\\\", \\\"!=\\\", {\\r\\n outputs: [[\\\"A!=B\\\", \\\"boolean\\\"]],\\r\\n title: \\\"A!=B\\\"\\r\\n });\\r\\n LiteGraph.registerSearchboxExtra(\\\"math/compare\\\", \\\">\\\", {\\r\\n outputs: [[\\\"A>B\\\", \\\"boolean\\\"]],\\r\\n title: \\\"A>B\\\"\\r\\n });\\r\\n LiteGraph.registerSearchboxExtra(\\\"math/compare\\\", \\\"<\\\", {\\r\\n outputs: [[\\\"A=\\\", {\\r\\n outputs: [[\\\"A>=B\\\", \\\"boolean\\\"]],\\r\\n title: \\\"A>=B\\\"\\r\\n });\\r\\n LiteGraph.registerSearchboxExtra(\\\"math/compare\\\", \\\"<=\\\", {\\r\\n outputs: [[\\\"A<=B\\\", \\\"boolean\\\"]],\\r\\n title: \\\"A<=B\\\"\\r\\n });\\r\\n\\r\\n function MathCondition() {\\r\\n this.addInput(\\\"A\\\", \\\"number\\\");\\r\\n this.addInput(\\\"B\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"true\\\", \\\"boolean\\\");\\r\\n this.addOutput(\\\"false\\\", \\\"boolean\\\");\\r\\n this.addProperty(\\\"A\\\", 1);\\r\\n this.addProperty(\\\"B\\\", 1);\\r\\n this.addProperty(\\\"OP\\\", \\\">\\\", \\\"enum\\\", { values: MathCondition.values });\\r\\n\\r\\n this.size = [80, 60];\\r\\n }\\r\\n\\r\\n MathCondition.values = [\\\">\\\", \\\"<\\\", \\\"==\\\", \\\"!=\\\", \\\"<=\\\", \\\">=\\\", \\\"||\\\", \\\"&&\\\" ];\\r\\n MathCondition[\\\"@OP\\\"] = {\\r\\n type: \\\"enum\\\",\\r\\n title: \\\"operation\\\",\\r\\n values: MathCondition.values\\r\\n };\\r\\n\\r\\n MathCondition.title = \\\"Condition\\\";\\r\\n MathCondition.desc = \\\"evaluates condition between A and B\\\";\\r\\n\\r\\n MathCondition.prototype.getTitle = function() {\\r\\n return \\\"A \\\" + this.properties.OP + \\\" B\\\";\\r\\n };\\r\\n\\r\\n MathCondition.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n if (A === undefined) {\\r\\n A = this.properties.A;\\r\\n } else {\\r\\n this.properties.A = A;\\r\\n }\\r\\n\\r\\n var B = this.getInputData(1);\\r\\n if (B === undefined) {\\r\\n B = this.properties.B;\\r\\n } else {\\r\\n this.properties.B = B;\\r\\n }\\r\\n\\r\\n var result = true;\\r\\n switch (this.properties.OP) {\\r\\n case \\\">\\\":\\r\\n result = A > B;\\r\\n break;\\r\\n case \\\"<\\\":\\r\\n result = A < B;\\r\\n break;\\r\\n case \\\"==\\\":\\r\\n result = A == B;\\r\\n break;\\r\\n case \\\"!=\\\":\\r\\n result = A != B;\\r\\n break;\\r\\n case \\\"<=\\\":\\r\\n result = A <= B;\\r\\n break;\\r\\n case \\\">=\\\":\\r\\n result = A >= B;\\r\\n break;\\r\\n case \\\"||\\\":\\r\\n result = A || B;\\r\\n break;\\r\\n case \\\"&&\\\":\\r\\n result = A && B;\\r\\n break;\\r\\n }\\r\\n\\r\\n this.setOutputData(0, result);\\r\\n this.setOutputData(1, !result);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/condition\\\", MathCondition);\\r\\n\\r\\n function MathAccumulate() {\\r\\n this.addInput(\\\"inc\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"total\\\", \\\"number\\\");\\r\\n this.addProperty(\\\"increment\\\", 1);\\r\\n this.addProperty(\\\"value\\\", 0);\\r\\n }\\r\\n\\r\\n MathAccumulate.title = \\\"Accumulate\\\";\\r\\n MathAccumulate.desc = \\\"Increments a value every time\\\";\\r\\n\\r\\n MathAccumulate.prototype.onExecute = function() {\\r\\n if (this.properties.value === null) {\\r\\n this.properties.value = 0;\\r\\n }\\r\\n\\r\\n var inc = this.getInputData(0);\\r\\n if (inc !== null) {\\r\\n this.properties.value += inc;\\r\\n } else {\\r\\n this.properties.value += this.properties.increment;\\r\\n }\\r\\n this.setOutputData(0, this.properties.value);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/accumulate\\\", MathAccumulate);\\r\\n\\r\\n //Math Trigonometry\\r\\n function MathTrigonometry() {\\r\\n this.addInput(\\\"v\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"sin\\\", \\\"number\\\");\\r\\n\\r\\n this.addProperty(\\\"amplitude\\\", 1);\\r\\n this.addProperty(\\\"offset\\\", 0);\\r\\n this.bgImageUrl = \\\"nodes/imgs/icon-sin.png\\\";\\r\\n }\\r\\n\\r\\n MathTrigonometry.title = \\\"Trigonometry\\\";\\r\\n MathTrigonometry.desc = \\\"Sin Cos Tan\\\";\\r\\n //MathTrigonometry.filter = \\\"shader\\\";\\r\\n\\r\\n MathTrigonometry.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n v = 0;\\r\\n }\\r\\n var amplitude = this.properties[\\\"amplitude\\\"];\\r\\n var slot = this.findInputSlot(\\\"amplitude\\\");\\r\\n if (slot != -1) {\\r\\n amplitude = this.getInputData(slot);\\r\\n }\\r\\n var offset = this.properties[\\\"offset\\\"];\\r\\n slot = this.findInputSlot(\\\"offset\\\");\\r\\n if (slot != -1) {\\r\\n offset = this.getInputData(slot);\\r\\n }\\r\\n\\r\\n for (var i = 0, l = this.outputs.length; i < l; ++i) {\\r\\n var output = this.outputs[i];\\r\\n var value;\\r\\n switch (output.name) {\\r\\n case \\\"sin\\\":\\r\\n value = Math.sin(v);\\r\\n break;\\r\\n case \\\"cos\\\":\\r\\n value = Math.cos(v);\\r\\n break;\\r\\n case \\\"tan\\\":\\r\\n value = Math.tan(v);\\r\\n break;\\r\\n case \\\"asin\\\":\\r\\n value = Math.asin(v);\\r\\n break;\\r\\n case \\\"acos\\\":\\r\\n value = Math.acos(v);\\r\\n break;\\r\\n case \\\"atan\\\":\\r\\n value = Math.atan(v);\\r\\n break;\\r\\n }\\r\\n this.setOutputData(i, amplitude * value + offset);\\r\\n }\\r\\n };\\r\\n\\r\\n MathTrigonometry.prototype.onGetInputs = function() {\\r\\n return [[\\\"v\\\", \\\"number\\\"], [\\\"amplitude\\\", \\\"number\\\"], [\\\"offset\\\", \\\"number\\\"]];\\r\\n };\\r\\n\\r\\n MathTrigonometry.prototype.onGetOutputs = function() {\\r\\n return [\\r\\n [\\\"sin\\\", \\\"number\\\"],\\r\\n [\\\"cos\\\", \\\"number\\\"],\\r\\n [\\\"tan\\\", \\\"number\\\"],\\r\\n [\\\"asin\\\", \\\"number\\\"],\\r\\n [\\\"acos\\\", \\\"number\\\"],\\r\\n [\\\"atan\\\", \\\"number\\\"]\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/trigonometry\\\", MathTrigonometry);\\r\\n\\r\\n LiteGraph.registerSearchboxExtra(\\\"math/trigonometry\\\", \\\"SIN()\\\", {\\r\\n outputs: [[\\\"sin\\\", \\\"number\\\"]],\\r\\n title: \\\"SIN()\\\"\\r\\n });\\r\\n LiteGraph.registerSearchboxExtra(\\\"math/trigonometry\\\", \\\"COS()\\\", {\\r\\n outputs: [[\\\"cos\\\", \\\"number\\\"]],\\r\\n title: \\\"COS()\\\"\\r\\n });\\r\\n LiteGraph.registerSearchboxExtra(\\\"math/trigonometry\\\", \\\"TAN()\\\", {\\r\\n outputs: [[\\\"tan\\\", \\\"number\\\"]],\\r\\n title: \\\"TAN()\\\"\\r\\n });\\r\\n\\r\\n //math library for safe math operations without eval\\r\\n function MathFormula() {\\r\\n this.addInput(\\\"x\\\", \\\"number\\\");\\r\\n this.addInput(\\\"y\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"\\\", \\\"number\\\");\\r\\n this.properties = { x: 1.0, y: 1.0, formula: \\\"x+y\\\" };\\r\\n this.code_widget = this.addWidget(\\r\\n \\\"text\\\",\\r\\n \\\"F(x,y)\\\",\\r\\n this.properties.formula,\\r\\n function(v, canvas, node) {\\r\\n node.properties.formula = v;\\r\\n }\\r\\n );\\r\\n this.addWidget(\\\"toggle\\\", \\\"allow\\\", LiteGraph.allow_scripts, function(v) {\\r\\n LiteGraph.allow_scripts = v;\\r\\n });\\r\\n this._func = null;\\r\\n }\\r\\n\\r\\n MathFormula.title = \\\"Formula\\\";\\r\\n MathFormula.desc = \\\"Compute formula\\\";\\r\\n MathFormula.size = [160, 100];\\r\\n\\r\\n MathAverageFilter.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"formula\\\") {\\r\\n this.code_widget.value = value;\\r\\n }\\r\\n };\\r\\n\\r\\n MathFormula.prototype.onExecute = function() {\\r\\n if (!LiteGraph.allow_scripts) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var x = this.getInputData(0);\\r\\n var y = this.getInputData(1);\\r\\n if (x != null) {\\r\\n this.properties[\\\"x\\\"] = x;\\r\\n } else {\\r\\n x = this.properties[\\\"x\\\"];\\r\\n }\\r\\n\\r\\n if (y != null) {\\r\\n this.properties[\\\"y\\\"] = y;\\r\\n } else {\\r\\n y = this.properties[\\\"y\\\"];\\r\\n }\\r\\n\\r\\n var f = this.properties[\\\"formula\\\"];\\r\\n\\r\\n var value;\\r\\n try {\\r\\n if (!this._func || this._func_code != this.properties.formula) {\\r\\n this._func = new Function(\\r\\n \\\"x\\\",\\r\\n \\\"y\\\",\\r\\n \\\"TIME\\\",\\r\\n \\\"return \\\" + this.properties.formula\\r\\n );\\r\\n this._func_code = this.properties.formula;\\r\\n }\\r\\n value = this._func(x, y, this.graph.globaltime);\\r\\n this.boxcolor = null;\\r\\n } catch (err) {\\r\\n this.boxcolor = \\\"red\\\";\\r\\n }\\r\\n this.setOutputData(0, value);\\r\\n };\\r\\n\\r\\n MathFormula.prototype.getTitle = function() {\\r\\n return this._func_code || \\\"Formula\\\";\\r\\n };\\r\\n\\r\\n MathFormula.prototype.onDrawBackground = function() {\\r\\n var f = this.properties[\\\"formula\\\"];\\r\\n if (this.outputs && this.outputs.length) {\\r\\n this.outputs[0].label = f;\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math/formula\\\", MathFormula);\\r\\n\\r\\n function Math3DVec2ToXYZ() {\\r\\n this.addInput(\\\"vec2\\\", \\\"vec2\\\");\\r\\n this.addOutput(\\\"x\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"y\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n Math3DVec2ToXYZ.title = \\\"Vec2->XY\\\";\\r\\n Math3DVec2ToXYZ.desc = \\\"vector 2 to components\\\";\\r\\n\\r\\n Math3DVec2ToXYZ.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.setOutputData(0, v[0]);\\r\\n this.setOutputData(1, v[1]);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec2-to-xyz\\\", Math3DVec2ToXYZ);\\r\\n\\r\\n function Math3DXYToVec2() {\\r\\n this.addInputs([[\\\"x\\\", \\\"number\\\"], [\\\"y\\\", \\\"number\\\"]]);\\r\\n this.addOutput(\\\"vec2\\\", \\\"vec2\\\");\\r\\n this.properties = { x: 0, y: 0 };\\r\\n this._data = new Float32Array(2);\\r\\n }\\r\\n\\r\\n Math3DXYToVec2.title = \\\"XY->Vec2\\\";\\r\\n Math3DXYToVec2.desc = \\\"components to vector2\\\";\\r\\n\\r\\n Math3DXYToVec2.prototype.onExecute = function() {\\r\\n var x = this.getInputData(0);\\r\\n if (x == null) {\\r\\n x = this.properties.x;\\r\\n }\\r\\n var y = this.getInputData(1);\\r\\n if (y == null) {\\r\\n y = this.properties.y;\\r\\n }\\r\\n\\r\\n var data = this._data;\\r\\n data[0] = x;\\r\\n data[1] = y;\\r\\n\\r\\n this.setOutputData(0, data);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/xy-to-vec2\\\", Math3DXYToVec2);\\r\\n\\r\\n function Math3DVec3ToXYZ() {\\r\\n this.addInput(\\\"vec3\\\", \\\"vec3\\\");\\r\\n this.addOutput(\\\"x\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"y\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"z\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n Math3DVec3ToXYZ.title = \\\"Vec3->XYZ\\\";\\r\\n Math3DVec3ToXYZ.desc = \\\"vector 3 to components\\\";\\r\\n\\r\\n Math3DVec3ToXYZ.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.setOutputData(0, v[0]);\\r\\n this.setOutputData(1, v[1]);\\r\\n this.setOutputData(2, v[2]);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec3-to-xyz\\\", Math3DVec3ToXYZ);\\r\\n\\r\\n function Math3DXYZToVec3() {\\r\\n this.addInputs([[\\\"x\\\", \\\"number\\\"], [\\\"y\\\", \\\"number\\\"], [\\\"z\\\", \\\"number\\\"]]);\\r\\n this.addOutput(\\\"vec3\\\", \\\"vec3\\\");\\r\\n this.properties = { x: 0, y: 0, z: 0 };\\r\\n this._data = new Float32Array(3);\\r\\n }\\r\\n\\r\\n Math3DXYZToVec3.title = \\\"XYZ->Vec3\\\";\\r\\n Math3DXYZToVec3.desc = \\\"components to vector3\\\";\\r\\n\\r\\n Math3DXYZToVec3.prototype.onExecute = function() {\\r\\n var x = this.getInputData(0);\\r\\n if (x == null) {\\r\\n x = this.properties.x;\\r\\n }\\r\\n var y = this.getInputData(1);\\r\\n if (y == null) {\\r\\n y = this.properties.y;\\r\\n }\\r\\n var z = this.getInputData(2);\\r\\n if (z == null) {\\r\\n z = this.properties.z;\\r\\n }\\r\\n\\r\\n var data = this._data;\\r\\n data[0] = x;\\r\\n data[1] = y;\\r\\n data[2] = z;\\r\\n\\r\\n this.setOutputData(0, data);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/xyz-to-vec3\\\", Math3DXYZToVec3);\\r\\n\\r\\n function Math3DVec4ToXYZW() {\\r\\n this.addInput(\\\"vec4\\\", \\\"vec4\\\");\\r\\n this.addOutput(\\\"x\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"y\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"z\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"w\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n Math3DVec4ToXYZW.title = \\\"Vec4->XYZW\\\";\\r\\n Math3DVec4ToXYZW.desc = \\\"vector 4 to components\\\";\\r\\n\\r\\n Math3DVec4ToXYZW.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.setOutputData(0, v[0]);\\r\\n this.setOutputData(1, v[1]);\\r\\n this.setOutputData(2, v[2]);\\r\\n this.setOutputData(3, v[3]);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec4-to-xyzw\\\", Math3DVec4ToXYZW);\\r\\n\\r\\n function Math3DXYZWToVec4() {\\r\\n this.addInputs([\\r\\n [\\\"x\\\", \\\"number\\\"],\\r\\n [\\\"y\\\", \\\"number\\\"],\\r\\n [\\\"z\\\", \\\"number\\\"],\\r\\n [\\\"w\\\", \\\"number\\\"]\\r\\n ]);\\r\\n this.addOutput(\\\"vec4\\\", \\\"vec4\\\");\\r\\n this.properties = { x: 0, y: 0, z: 0, w: 0 };\\r\\n this._data = new Float32Array(4);\\r\\n }\\r\\n\\r\\n Math3DXYZWToVec4.title = \\\"XYZW->Vec4\\\";\\r\\n Math3DXYZWToVec4.desc = \\\"components to vector4\\\";\\r\\n\\r\\n Math3DXYZWToVec4.prototype.onExecute = function() {\\r\\n var x = this.getInputData(0);\\r\\n if (x == null) {\\r\\n x = this.properties.x;\\r\\n }\\r\\n var y = this.getInputData(1);\\r\\n if (y == null) {\\r\\n y = this.properties.y;\\r\\n }\\r\\n var z = this.getInputData(2);\\r\\n if (z == null) {\\r\\n z = this.properties.z;\\r\\n }\\r\\n var w = this.getInputData(3);\\r\\n if (w == null) {\\r\\n w = this.properties.w;\\r\\n }\\r\\n\\r\\n var data = this._data;\\r\\n data[0] = x;\\r\\n data[1] = y;\\r\\n data[2] = z;\\r\\n data[3] = w;\\r\\n\\r\\n this.setOutputData(0, data);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/xyzw-to-vec4\\\", Math3DXYZWToVec4);\\r\\n\\r\\n //if glMatrix is installed...\\r\\n if (global.glMatrix) {\\r\\n function Math3DQuaternion() {\\r\\n this.addOutput(\\\"quat\\\", \\\"quat\\\");\\r\\n this.properties = { x: 0, y: 0, z: 0, w: 1 };\\r\\n this._value = quat.create();\\r\\n }\\r\\n\\r\\n Math3DQuaternion.title = \\\"Quaternion\\\";\\r\\n Math3DQuaternion.desc = \\\"quaternion\\\";\\r\\n\\r\\n Math3DQuaternion.prototype.onExecute = function() {\\r\\n this._value[0] = this.properties.x;\\r\\n this._value[1] = this.properties.y;\\r\\n this._value[2] = this.properties.z;\\r\\n this._value[3] = this.properties.w;\\r\\n this.setOutputData(0, this._value);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/quaternion\\\", Math3DQuaternion);\\r\\n\\r\\n function Math3DRotation() {\\r\\n this.addInputs([[\\\"degrees\\\", \\\"number\\\"], [\\\"axis\\\", \\\"vec3\\\"]]);\\r\\n this.addOutput(\\\"quat\\\", \\\"quat\\\");\\r\\n this.properties = { angle: 90.0, axis: vec3.fromValues(0, 1, 0) };\\r\\n\\r\\n this._value = quat.create();\\r\\n }\\r\\n\\r\\n Math3DRotation.title = \\\"Rotation\\\";\\r\\n Math3DRotation.desc = \\\"quaternion rotation\\\";\\r\\n\\r\\n Math3DRotation.prototype.onExecute = function() {\\r\\n var angle = this.getInputData(0);\\r\\n if (angle == null) {\\r\\n angle = this.properties.angle;\\r\\n }\\r\\n var axis = this.getInputData(1);\\r\\n if (axis == null) {\\r\\n axis = this.properties.axis;\\r\\n }\\r\\n\\r\\n var R = quat.setAxisAngle(this._value, axis, angle * 0.0174532925);\\r\\n this.setOutputData(0, R);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/rotation\\\", Math3DRotation);\\r\\n\\r\\n //Math3D rotate vec3\\r\\n function Math3DRotateVec3() {\\r\\n this.addInputs([[\\\"vec3\\\", \\\"vec3\\\"], [\\\"quat\\\", \\\"quat\\\"]]);\\r\\n this.addOutput(\\\"result\\\", \\\"vec3\\\");\\r\\n this.properties = { vec: [0, 0, 1] };\\r\\n }\\r\\n\\r\\n Math3DRotateVec3.title = \\\"Rot. Vec3\\\";\\r\\n Math3DRotateVec3.desc = \\\"rotate a point\\\";\\r\\n\\r\\n Math3DRotateVec3.prototype.onExecute = function() {\\r\\n var vec = this.getInputData(0);\\r\\n if (vec == null) {\\r\\n vec = this.properties.vec;\\r\\n }\\r\\n var quat = this.getInputData(1);\\r\\n if (quat == null) {\\r\\n this.setOutputData(vec);\\r\\n } else {\\r\\n this.setOutputData(\\r\\n 0,\\r\\n vec3.transformQuat(vec3.create(), vec, quat)\\r\\n );\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/rotate_vec3\\\", Math3DRotateVec3);\\r\\n\\r\\n function Math3DMultQuat() {\\r\\n this.addInputs([[\\\"A\\\", \\\"quat\\\"], [\\\"B\\\", \\\"quat\\\"]]);\\r\\n this.addOutput(\\\"A*B\\\", \\\"quat\\\");\\r\\n\\r\\n this._value = quat.create();\\r\\n }\\r\\n\\r\\n Math3DMultQuat.title = \\\"Mult. Quat\\\";\\r\\n Math3DMultQuat.desc = \\\"rotate quaternion\\\";\\r\\n\\r\\n Math3DMultQuat.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n if (A == null) {\\r\\n return;\\r\\n }\\r\\n var B = this.getInputData(1);\\r\\n if (B == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var R = quat.multiply(this._value, A, B);\\r\\n this.setOutputData(0, R);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/mult-quat\\\", Math3DMultQuat);\\r\\n\\r\\n function Math3DQuatSlerp() {\\r\\n this.addInputs([\\r\\n [\\\"A\\\", \\\"quat\\\"],\\r\\n [\\\"B\\\", \\\"quat\\\"],\\r\\n [\\\"factor\\\", \\\"number\\\"]\\r\\n ]);\\r\\n this.addOutput(\\\"slerp\\\", \\\"quat\\\");\\r\\n this.addProperty(\\\"factor\\\", 0.5);\\r\\n\\r\\n this._value = quat.create();\\r\\n }\\r\\n\\r\\n Math3DQuatSlerp.title = \\\"Quat Slerp\\\";\\r\\n Math3DQuatSlerp.desc = \\\"quaternion spherical interpolation\\\";\\r\\n\\r\\n Math3DQuatSlerp.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n if (A == null) {\\r\\n return;\\r\\n }\\r\\n var B = this.getInputData(1);\\r\\n if (B == null) {\\r\\n return;\\r\\n }\\r\\n var factor = this.properties.factor;\\r\\n if (this.getInputData(2) != null) {\\r\\n factor = this.getInputData(2);\\r\\n }\\r\\n\\r\\n var R = quat.slerp(this._value, A, B, factor);\\r\\n this.setOutputData(0, R);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/quat-slerp\\\", Math3DQuatSlerp);\\r\\n } //glMatrix\\r\\n})(this);\\r\\n\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n //Math 3D operation\\r\\n function Math3DOperation() {\\r\\n this.addInput(\\\"A\\\", \\\"number,vec3\\\");\\r\\n this.addInput(\\\"B\\\", \\\"number,vec3\\\");\\r\\n this.addOutput(\\\"=\\\", \\\"vec3\\\");\\r\\n this.addProperty(\\\"OP\\\", \\\"+\\\", \\\"enum\\\", { values: Math3DOperation.values });\\r\\n\\t\\tthis._result = vec3.create();\\r\\n }\\r\\n\\r\\n Math3DOperation.values = [\\\"+\\\", \\\"-\\\", \\\"*\\\", \\\"/\\\", \\\"%\\\", \\\"^\\\", \\\"max\\\", \\\"min\\\"];\\r\\n\\r\\n\\tMath3DOperation.title = \\\"Operation\\\";\\r\\n Math3DOperation.desc = \\\"Easy math 3D operators\\\";\\r\\n Math3DOperation[\\\"@OP\\\"] = {\\r\\n type: \\\"enum\\\",\\r\\n title: \\\"operation\\\",\\r\\n values: Math3DOperation.values\\r\\n };\\r\\n Math3DOperation.size = [100, 60];\\r\\n\\r\\n Math3DOperation.prototype.getTitle = function() {\\r\\n\\t\\tif(this.properties.OP == \\\"max\\\" || this.properties.OP == \\\"min\\\" )\\r\\n\\t\\t\\treturn this.properties.OP + \\\"(A,B)\\\";\\r\\n return \\\"A \\\" + this.properties.OP + \\\" B\\\";\\r\\n };\\r\\n\\r\\n Math3DOperation.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n var B = this.getInputData(1);\\r\\n\\t\\tif(A == null || B == null)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(A.constructor === Number)\\r\\n\\t\\t\\tA = [A,A,A];\\r\\n\\t\\tif(B.constructor === Number)\\r\\n\\t\\t\\tB = [B,B,B];\\r\\n\\r\\n var result = this._result;\\r\\n switch (this.properties.OP) {\\r\\n case \\\"+\\\":\\r\\n result = vec3.add(result,A,B);\\r\\n break;\\r\\n case \\\"-\\\":\\r\\n result = vec3.sub(result,A,B);\\r\\n break;\\r\\n case \\\"x\\\":\\r\\n case \\\"X\\\":\\r\\n case \\\"*\\\":\\r\\n result = vec3.mul(result,A,B);\\r\\n break;\\r\\n case \\\"/\\\":\\r\\n result = vec3.div(result,A,B);\\r\\n break;\\r\\n case \\\"%\\\":\\r\\n result[0] = A[0]%B[0];\\r\\n result[1] = A[1]%B[1];\\r\\n result[2] = A[2]%B[2];\\r\\n break;\\r\\n case \\\"^\\\":\\r\\n result[0] = Math.pow(A[0],B[0]);\\r\\n result[1] = Math.pow(A[1],B[1]);\\r\\n result[2] = Math.pow(A[2],B[2]);\\r\\n break;\\r\\n case \\\"max\\\":\\r\\n result[0] = Math.max(A[0],B[0]);\\r\\n result[1] = Math.max(A[1],B[1]);\\r\\n result[2] = Math.max(A[2],B[2]);\\r\\n break;\\r\\n case \\\"min\\\":\\r\\n result[0] = Math.min(A[0],B[0]);\\r\\n result[1] = Math.min(A[1],B[1]);\\r\\n result[2] = Math.min(A[2],B[2]);\\r\\n break;\\r\\n default:\\r\\n console.warn(\\\"Unknown operation: \\\" + this.properties.OP);\\r\\n }\\r\\n this.setOutputData(0, result);\\r\\n };\\r\\n\\r\\n Math3DOperation.prototype.onDrawBackground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n ctx.font = \\\"40px Arial\\\";\\r\\n ctx.fillStyle = \\\"#666\\\";\\r\\n ctx.textAlign = \\\"center\\\";\\r\\n ctx.fillText(\\r\\n this.properties.OP,\\r\\n this.size[0] * 0.5,\\r\\n (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5\\r\\n );\\r\\n ctx.textAlign = \\\"left\\\";\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/operation\\\", Math3DOperation);\\r\\n\\r\\n function Math3DVec2ToXYZ() {\\r\\n this.addInput(\\\"vec2\\\", \\\"vec2\\\");\\r\\n this.addOutput(\\\"x\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"y\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n Math3DVec2ToXYZ.title = \\\"Vec2->XY\\\";\\r\\n Math3DVec2ToXYZ.desc = \\\"vector 2 to components\\\";\\r\\n\\r\\n Math3DVec2ToXYZ.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.setOutputData(0, v[0]);\\r\\n this.setOutputData(1, v[1]);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec2-to-xyz\\\", Math3DVec2ToXYZ);\\r\\n\\r\\n function Math3DXYToVec2() {\\r\\n this.addInputs([[\\\"x\\\", \\\"number\\\"], [\\\"y\\\", \\\"number\\\"]]);\\r\\n this.addOutput(\\\"vec2\\\", \\\"vec2\\\");\\r\\n this.properties = { x: 0, y: 0 };\\r\\n this._data = new Float32Array(2);\\r\\n }\\r\\n\\r\\n Math3DXYToVec2.title = \\\"XY->Vec2\\\";\\r\\n Math3DXYToVec2.desc = \\\"components to vector2\\\";\\r\\n\\r\\n Math3DXYToVec2.prototype.onExecute = function() {\\r\\n var x = this.getInputData(0);\\r\\n if (x == null) {\\r\\n x = this.properties.x;\\r\\n }\\r\\n var y = this.getInputData(1);\\r\\n if (y == null) {\\r\\n y = this.properties.y;\\r\\n }\\r\\n\\r\\n var data = this._data;\\r\\n data[0] = x;\\r\\n data[1] = y;\\r\\n\\r\\n this.setOutputData(0, data);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/xy-to-vec2\\\", Math3DXYToVec2);\\r\\n\\r\\n function Math3DVec3ToXYZ() {\\r\\n this.addInput(\\\"vec3\\\", \\\"vec3\\\");\\r\\n this.addOutput(\\\"x\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"y\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"z\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n Math3DVec3ToXYZ.title = \\\"Vec3->XYZ\\\";\\r\\n Math3DVec3ToXYZ.desc = \\\"vector 3 to components\\\";\\r\\n\\r\\n Math3DVec3ToXYZ.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.setOutputData(0, v[0]);\\r\\n this.setOutputData(1, v[1]);\\r\\n this.setOutputData(2, v[2]);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec3-to-xyz\\\", Math3DVec3ToXYZ);\\r\\n\\r\\n function Math3DXYZToVec3() {\\r\\n this.addInputs([[\\\"x\\\", \\\"number\\\"], [\\\"y\\\", \\\"number\\\"], [\\\"z\\\", \\\"number\\\"]]);\\r\\n this.addOutput(\\\"vec3\\\", \\\"vec3\\\");\\r\\n this.properties = { x: 0, y: 0, z: 0 };\\r\\n this._data = new Float32Array(3);\\r\\n }\\r\\n\\r\\n Math3DXYZToVec3.title = \\\"XYZ->Vec3\\\";\\r\\n Math3DXYZToVec3.desc = \\\"components to vector3\\\";\\r\\n\\r\\n Math3DXYZToVec3.prototype.onExecute = function() {\\r\\n var x = this.getInputData(0);\\r\\n if (x == null) {\\r\\n x = this.properties.x;\\r\\n }\\r\\n var y = this.getInputData(1);\\r\\n if (y == null) {\\r\\n y = this.properties.y;\\r\\n }\\r\\n var z = this.getInputData(2);\\r\\n if (z == null) {\\r\\n z = this.properties.z;\\r\\n }\\r\\n\\r\\n var data = this._data;\\r\\n data[0] = x;\\r\\n data[1] = y;\\r\\n data[2] = z;\\r\\n\\r\\n this.setOutputData(0, data);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/xyz-to-vec3\\\", Math3DXYZToVec3);\\r\\n\\r\\n function Math3DVec4ToXYZW() {\\r\\n this.addInput(\\\"vec4\\\", \\\"vec4\\\");\\r\\n this.addOutput(\\\"x\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"y\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"z\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"w\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n Math3DVec4ToXYZW.title = \\\"Vec4->XYZW\\\";\\r\\n Math3DVec4ToXYZW.desc = \\\"vector 4 to components\\\";\\r\\n\\r\\n Math3DVec4ToXYZW.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this.setOutputData(0, v[0]);\\r\\n this.setOutputData(1, v[1]);\\r\\n this.setOutputData(2, v[2]);\\r\\n this.setOutputData(3, v[3]);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec4-to-xyzw\\\", Math3DVec4ToXYZW);\\r\\n\\r\\n function Math3DXYZWToVec4() {\\r\\n this.addInputs([\\r\\n [\\\"x\\\", \\\"number\\\"],\\r\\n [\\\"y\\\", \\\"number\\\"],\\r\\n [\\\"z\\\", \\\"number\\\"],\\r\\n [\\\"w\\\", \\\"number\\\"]\\r\\n ]);\\r\\n this.addOutput(\\\"vec4\\\", \\\"vec4\\\");\\r\\n this.properties = { x: 0, y: 0, z: 0, w: 0 };\\r\\n this._data = new Float32Array(4);\\r\\n }\\r\\n\\r\\n Math3DXYZWToVec4.title = \\\"XYZW->Vec4\\\";\\r\\n Math3DXYZWToVec4.desc = \\\"components to vector4\\\";\\r\\n\\r\\n Math3DXYZWToVec4.prototype.onExecute = function() {\\r\\n var x = this.getInputData(0);\\r\\n if (x == null) {\\r\\n x = this.properties.x;\\r\\n }\\r\\n var y = this.getInputData(1);\\r\\n if (y == null) {\\r\\n y = this.properties.y;\\r\\n }\\r\\n var z = this.getInputData(2);\\r\\n if (z == null) {\\r\\n z = this.properties.z;\\r\\n }\\r\\n var w = this.getInputData(3);\\r\\n if (w == null) {\\r\\n w = this.properties.w;\\r\\n }\\r\\n\\r\\n var data = this._data;\\r\\n data[0] = x;\\r\\n data[1] = y;\\r\\n data[2] = z;\\r\\n data[3] = w;\\r\\n\\r\\n this.setOutputData(0, data);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/xyzw-to-vec4\\\", Math3DXYZWToVec4);\\r\\n\\r\\n function Math3DVec3Scale() {\\r\\n this.addInput(\\\"in\\\", \\\"vec3\\\");\\r\\n this.addInput(\\\"f\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"vec3\\\");\\r\\n this.properties = { f: 1 };\\r\\n this._data = new Float32Array(3);\\r\\n }\\r\\n\\r\\n Math3DVec3Scale.title = \\\"vec3_scale\\\";\\r\\n Math3DVec3Scale.desc = \\\"scales the components of a vec3\\\";\\r\\n\\r\\n Math3DVec3Scale.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n var f = this.getInputData(1);\\r\\n if (f == null) {\\r\\n f = this.properties.f;\\r\\n }\\r\\n\\r\\n var data = this._data;\\r\\n data[0] = v[0] * f;\\r\\n data[1] = v[1] * f;\\r\\n data[2] = v[2] * f;\\r\\n this.setOutputData(0, data);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec3-scale\\\", Math3DVec3Scale);\\r\\n\\r\\n function Math3DVec3Length() {\\r\\n this.addInput(\\\"in\\\", \\\"vec3\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n Math3DVec3Length.title = \\\"vec3_length\\\";\\r\\n Math3DVec3Length.desc = \\\"returns the module of a vector\\\";\\r\\n\\r\\n Math3DVec3Length.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);\\r\\n this.setOutputData(0, dist);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec3-length\\\", Math3DVec3Length);\\r\\n\\r\\n function Math3DVec3Normalize() {\\r\\n this.addInput(\\\"in\\\", \\\"vec3\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"vec3\\\");\\r\\n this._data = new Float32Array(3);\\r\\n }\\r\\n\\r\\n Math3DVec3Normalize.title = \\\"vec3_normalize\\\";\\r\\n Math3DVec3Normalize.desc = \\\"returns the vector normalized\\\";\\r\\n\\r\\n Math3DVec3Normalize.prototype.onExecute = function() {\\r\\n var v = this.getInputData(0);\\r\\n if (v == null) {\\r\\n return;\\r\\n }\\r\\n var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);\\r\\n var data = this._data;\\r\\n data[0] = v[0] / dist;\\r\\n data[1] = v[1] / dist;\\r\\n data[2] = v[2] / dist;\\r\\n\\r\\n this.setOutputData(0, data);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec3-normalize\\\", Math3DVec3Normalize);\\r\\n\\r\\n function Math3DVec3Lerp() {\\r\\n this.addInput(\\\"A\\\", \\\"vec3\\\");\\r\\n this.addInput(\\\"B\\\", \\\"vec3\\\");\\r\\n this.addInput(\\\"f\\\", \\\"vec3\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"vec3\\\");\\r\\n this.properties = { f: 0.5 };\\r\\n this._data = new Float32Array(3);\\r\\n }\\r\\n\\r\\n Math3DVec3Lerp.title = \\\"vec3_lerp\\\";\\r\\n Math3DVec3Lerp.desc = \\\"returns the interpolated vector\\\";\\r\\n\\r\\n Math3DVec3Lerp.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n if (A == null) {\\r\\n return;\\r\\n }\\r\\n var B = this.getInputData(1);\\r\\n if (B == null) {\\r\\n return;\\r\\n }\\r\\n var f = this.getInputOrProperty(\\\"f\\\");\\r\\n\\r\\n var data = this._data;\\r\\n data[0] = A[0] * (1 - f) + B[0] * f;\\r\\n data[1] = A[1] * (1 - f) + B[1] * f;\\r\\n data[2] = A[2] * (1 - f) + B[2] * f;\\r\\n\\r\\n this.setOutputData(0, data);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec3-lerp\\\", Math3DVec3Lerp);\\r\\n\\r\\n function Math3DVec3Dot() {\\r\\n this.addInput(\\\"A\\\", \\\"vec3\\\");\\r\\n this.addInput(\\\"B\\\", \\\"vec3\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n Math3DVec3Dot.title = \\\"vec3_dot\\\";\\r\\n Math3DVec3Dot.desc = \\\"returns the dot product\\\";\\r\\n\\r\\n Math3DVec3Dot.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n if (A == null) {\\r\\n return;\\r\\n }\\r\\n var B = this.getInputData(1);\\r\\n if (B == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var dot = A[0] * B[0] + A[1] * B[1] + A[2] * B[2];\\r\\n this.setOutputData(0, dot);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/vec3-dot\\\", Math3DVec3Dot);\\r\\n\\r\\n //if glMatrix is installed...\\r\\n if (global.glMatrix) {\\r\\n function Math3DQuaternion() {\\r\\n this.addOutput(\\\"quat\\\", \\\"quat\\\");\\r\\n this.properties = { x: 0, y: 0, z: 0, w: 1, normalize: false };\\r\\n this._value = quat.create();\\r\\n }\\r\\n\\r\\n Math3DQuaternion.title = \\\"Quaternion\\\";\\r\\n Math3DQuaternion.desc = \\\"quaternion\\\";\\r\\n\\r\\n Math3DQuaternion.prototype.onExecute = function() {\\r\\n this._value[0] = this.getInputOrProperty(\\\"x\\\");\\r\\n this._value[1] = this.getInputOrProperty(\\\"y\\\");\\r\\n this._value[2] = this.getInputOrProperty(\\\"z\\\");\\r\\n this._value[3] = this.getInputOrProperty(\\\"w\\\");\\r\\n if (this.properties.normalize) {\\r\\n quat.normalize(this._value, this._value);\\r\\n }\\r\\n this.setOutputData(0, this._value);\\r\\n };\\r\\n\\r\\n Math3DQuaternion.prototype.onGetInputs = function() {\\r\\n return [\\r\\n [\\\"x\\\", \\\"number\\\"],\\r\\n [\\\"y\\\", \\\"number\\\"],\\r\\n [\\\"z\\\", \\\"number\\\"],\\r\\n [\\\"w\\\", \\\"number\\\"]\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/quaternion\\\", Math3DQuaternion);\\r\\n\\r\\n function Math3DRotation() {\\r\\n this.addInputs([[\\\"degrees\\\", \\\"number\\\"], [\\\"axis\\\", \\\"vec3\\\"]]);\\r\\n this.addOutput(\\\"quat\\\", \\\"quat\\\");\\r\\n this.properties = { angle: 90.0, axis: vec3.fromValues(0, 1, 0) };\\r\\n\\r\\n this._value = quat.create();\\r\\n }\\r\\n\\r\\n Math3DRotation.title = \\\"Rotation\\\";\\r\\n Math3DRotation.desc = \\\"quaternion rotation\\\";\\r\\n\\r\\n Math3DRotation.prototype.onExecute = function() {\\r\\n var angle = this.getInputData(0);\\r\\n if (angle == null) {\\r\\n angle = this.properties.angle;\\r\\n }\\r\\n var axis = this.getInputData(1);\\r\\n if (axis == null) {\\r\\n axis = this.properties.axis;\\r\\n }\\r\\n\\r\\n var R = quat.setAxisAngle(this._value, axis, angle * 0.0174532925);\\r\\n this.setOutputData(0, R);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/rotation\\\", Math3DRotation);\\r\\n\\r\\n //Math3D rotate vec3\\r\\n function Math3DRotateVec3() {\\r\\n this.addInputs([[\\\"vec3\\\", \\\"vec3\\\"], [\\\"quat\\\", \\\"quat\\\"]]);\\r\\n this.addOutput(\\\"result\\\", \\\"vec3\\\");\\r\\n this.properties = { vec: [0, 0, 1] };\\r\\n }\\r\\n\\r\\n Math3DRotateVec3.title = \\\"Rot. Vec3\\\";\\r\\n Math3DRotateVec3.desc = \\\"rotate a point\\\";\\r\\n\\r\\n Math3DRotateVec3.prototype.onExecute = function() {\\r\\n var vec = this.getInputData(0);\\r\\n if (vec == null) {\\r\\n vec = this.properties.vec;\\r\\n }\\r\\n var quat = this.getInputData(1);\\r\\n if (quat == null) {\\r\\n this.setOutputData(vec);\\r\\n } else {\\r\\n this.setOutputData(\\r\\n 0,\\r\\n vec3.transformQuat(vec3.create(), vec, quat)\\r\\n );\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/rotate_vec3\\\", Math3DRotateVec3);\\r\\n\\r\\n function Math3DMultQuat() {\\r\\n this.addInputs([[\\\"A\\\", \\\"quat\\\"], [\\\"B\\\", \\\"quat\\\"]]);\\r\\n this.addOutput(\\\"A*B\\\", \\\"quat\\\");\\r\\n\\r\\n this._value = quat.create();\\r\\n }\\r\\n\\r\\n Math3DMultQuat.title = \\\"Mult. Quat\\\";\\r\\n Math3DMultQuat.desc = \\\"rotate quaternion\\\";\\r\\n\\r\\n Math3DMultQuat.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n if (A == null) {\\r\\n return;\\r\\n }\\r\\n var B = this.getInputData(1);\\r\\n if (B == null) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var R = quat.multiply(this._value, A, B);\\r\\n this.setOutputData(0, R);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/mult-quat\\\", Math3DMultQuat);\\r\\n\\r\\n function Math3DQuatSlerp() {\\r\\n this.addInputs([\\r\\n [\\\"A\\\", \\\"quat\\\"],\\r\\n [\\\"B\\\", \\\"quat\\\"],\\r\\n [\\\"factor\\\", \\\"number\\\"]\\r\\n ]);\\r\\n this.addOutput(\\\"slerp\\\", \\\"quat\\\");\\r\\n this.addProperty(\\\"factor\\\", 0.5);\\r\\n\\r\\n this._value = quat.create();\\r\\n }\\r\\n\\r\\n Math3DQuatSlerp.title = \\\"Quat Slerp\\\";\\r\\n Math3DQuatSlerp.desc = \\\"quaternion spherical interpolation\\\";\\r\\n\\r\\n Math3DQuatSlerp.prototype.onExecute = function() {\\r\\n var A = this.getInputData(0);\\r\\n if (A == null) {\\r\\n return;\\r\\n }\\r\\n var B = this.getInputData(1);\\r\\n if (B == null) {\\r\\n return;\\r\\n }\\r\\n var factor = this.properties.factor;\\r\\n if (this.getInputData(2) != null) {\\r\\n factor = this.getInputData(2);\\r\\n }\\r\\n\\r\\n var R = quat.slerp(this._value, A, B, factor);\\r\\n this.setOutputData(0, R);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/quat-slerp\\\", Math3DQuatSlerp);\\r\\n\\r\\n\\r\\n //Math3D rotate vec3\\r\\n function Math3DRemapRange() {\\r\\n this.addInput(\\\"vec3\\\", \\\"vec3\\\");\\r\\n this.addOutput(\\\"remap\\\", \\\"vec3\\\");\\r\\n\\t\\t\\tthis.addOutput(\\\"clamped\\\", \\\"vec3\\\");\\r\\n this.properties = { clamp: true, range_min: [-1, -1, 0], range_max: [1, 1, 0], target_min: [-1,-1,0], target_max:[1,1,0] };\\r\\n\\t\\t\\tthis._value = vec3.create();\\r\\n\\t\\t\\tthis._clamped = vec3.create();\\r\\n }\\r\\n\\r\\n Math3DRemapRange.title = \\\"Remap Range\\\";\\r\\n Math3DRemapRange.desc = \\\"remap a 3D range\\\";\\r\\n\\r\\n Math3DRemapRange.prototype.onExecute = function() {\\r\\n var vec = this.getInputData(0);\\r\\n\\t\\t\\tif(vec)\\r\\n\\t\\t\\t\\tthis._value.set(vec);\\r\\n\\t\\t\\tvar range_min = this.properties.range_min;\\r\\n\\t\\t\\tvar range_max = this.properties.range_max;\\r\\n\\t\\t\\tvar target_min = this.properties.target_min;\\r\\n\\t\\t\\tvar target_max = this.properties.target_max;\\r\\n\\r\\n\\t\\t\\tfor(var i = 0; i < 3; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar r = range_max[i] - range_min[i];\\r\\n\\t\\t\\t\\tthis._clamped[i] = Math.clamp( this._value[i], range_min[i], range_max[i] );\\r\\n\\t\\t\\t\\tif(r == 0)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tthis._value[i] = (target_min[i] + target_max[i]) * 0.5;\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tvar n = (this._value[i] - range_min[i]) / r;\\r\\n\\t\\t\\t\\tif(this.properties.clamp)\\r\\n\\t\\t\\t\\t\\tn = Math.clamp(n,0,1);\\r\\n\\t\\t\\t\\tvar t = target_max[i] - target_min[i];\\r\\n\\t\\t\\t\\tthis._value[i] = target_min[i] + n * t;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tthis.setOutputData(0,this._value);\\r\\n\\t\\t\\tthis.setOutputData(1,this._clamped);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"math3d/remap_range\\\", Math3DRemapRange);\\r\\n\\r\\n\\r\\n\\r\\n } //glMatrix\\r\\n\\telse\\r\\n\\t\\tconsole.warn(\\\"No glmatrix found, some Math3D nodes may not work\\\");\\r\\n\\r\\n})(this);\\r\\n\\r\\n//basic nodes\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n function toString(a) {\\r\\n return String(a);\\r\\n }\\r\\n\\r\\n LiteGraph.wrapFunctionAsNode(\\\"string/toString\\\", compare, [\\\"*\\\"], \\\"String\\\");\\r\\n\\r\\n function compare(a, b) {\\r\\n return a == b;\\r\\n }\\r\\n\\r\\n LiteGraph.wrapFunctionAsNode(\\r\\n \\\"string/compare\\\",\\r\\n compare,\\r\\n [\\\"String\\\", \\\"String\\\"],\\r\\n \\\"Boolean\\\"\\r\\n );\\r\\n\\r\\n function concatenate(a, b) {\\r\\n if (a === undefined) {\\r\\n return b;\\r\\n }\\r\\n if (b === undefined) {\\r\\n return a;\\r\\n }\\r\\n return a + b;\\r\\n }\\r\\n\\r\\n LiteGraph.wrapFunctionAsNode(\\r\\n \\\"string/concatenate\\\",\\r\\n concatenate,\\r\\n [\\\"String\\\", \\\"String\\\"],\\r\\n \\\"String\\\"\\r\\n );\\r\\n\\r\\n function contains(a, b) {\\r\\n if (a === undefined || b === undefined) {\\r\\n return false;\\r\\n }\\r\\n return a.indexOf(b) != -1;\\r\\n }\\r\\n\\r\\n LiteGraph.wrapFunctionAsNode(\\r\\n \\\"string/contains\\\",\\r\\n contains,\\r\\n [\\\"String\\\", \\\"String\\\"],\\r\\n \\\"Boolean\\\"\\r\\n );\\r\\n\\r\\n function toUpperCase(a) {\\r\\n if (a != null && a.constructor === String) {\\r\\n return a.toUpperCase();\\r\\n }\\r\\n return a;\\r\\n }\\r\\n\\r\\n LiteGraph.wrapFunctionAsNode(\\r\\n \\\"string/toUpperCase\\\",\\r\\n toUpperCase,\\r\\n [\\\"String\\\"],\\r\\n \\\"String\\\"\\r\\n );\\r\\n\\r\\n function split(a, b) {\\r\\n if (a != null && a.constructor === String) {\\r\\n return a.split(b || \\\" \\\");\\r\\n }\\r\\n return [a];\\r\\n }\\r\\n\\r\\n LiteGraph.wrapFunctionAsNode(\\r\\n \\\"string/split\\\",\\r\\n toUpperCase,\\r\\n [\\\"String\\\", \\\"String\\\"],\\r\\n \\\"Array\\\"\\r\\n );\\r\\n\\r\\n function toFixed(a) {\\r\\n if (a != null && a.constructor === Number) {\\r\\n return a.toFixed(this.properties.precision);\\r\\n }\\r\\n return a;\\r\\n }\\r\\n\\r\\n LiteGraph.wrapFunctionAsNode(\\r\\n \\\"string/toFixed\\\",\\r\\n toFixed,\\r\\n [\\\"Number\\\"],\\r\\n \\\"String\\\",\\r\\n { precision: 0 }\\r\\n );\\r\\n})(this);\\r\\n\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n function Selector() {\\r\\n this.addInput(\\\"sel\\\", \\\"number\\\");\\r\\n this.addInput(\\\"A\\\");\\r\\n this.addInput(\\\"B\\\");\\r\\n this.addInput(\\\"C\\\");\\r\\n this.addInput(\\\"D\\\");\\r\\n this.addOutput(\\\"out\\\");\\r\\n\\r\\n this.selected = 0;\\r\\n }\\r\\n\\r\\n Selector.title = \\\"Selector\\\";\\r\\n Selector.desc = \\\"selects an output\\\";\\r\\n\\r\\n Selector.prototype.onDrawBackground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n ctx.fillStyle = \\\"#AFB\\\";\\r\\n var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6;\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(50, y);\\r\\n ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT);\\r\\n ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5);\\r\\n ctx.fill();\\r\\n };\\r\\n\\r\\n Selector.prototype.onExecute = function() {\\r\\n var sel = this.getInputData(0);\\r\\n if (sel == null || sel.constructor !== Number)\\r\\n sel = 0;\\r\\n this.selected = sel = Math.round(sel) % (this.inputs.length - 1);\\r\\n var v = this.getInputData(sel + 1);\\r\\n if (v !== undefined) {\\r\\n this.setOutputData(0, v);\\r\\n }\\r\\n };\\r\\n\\r\\n Selector.prototype.onGetInputs = function() {\\r\\n return [[\\\"E\\\", 0], [\\\"F\\\", 0], [\\\"G\\\", 0], [\\\"H\\\", 0]];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"logic/selector\\\", Selector);\\r\\n\\r\\n function Sequence() {\\r\\n this.properties = {\\r\\n sequence: \\\"A,B,C\\\"\\r\\n };\\r\\n this.addInput(\\\"index\\\", \\\"number\\\");\\r\\n this.addInput(\\\"seq\\\");\\r\\n this.addOutput(\\\"out\\\");\\r\\n\\r\\n this.index = 0;\\r\\n this.values = this.properties.sequence.split(\\\",\\\");\\r\\n }\\r\\n\\r\\n Sequence.title = \\\"Sequence\\\";\\r\\n Sequence.desc = \\\"select one element from a sequence from a string\\\";\\r\\n\\r\\n Sequence.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"sequence\\\") {\\r\\n this.values = value.split(\\\",\\\");\\r\\n }\\r\\n };\\r\\n\\r\\n Sequence.prototype.onExecute = function() {\\r\\n var seq = this.getInputData(1);\\r\\n if (seq && seq != this.current_sequence) {\\r\\n this.values = seq.split(\\\",\\\");\\r\\n this.current_sequence = seq;\\r\\n }\\r\\n var index = this.getInputData(0);\\r\\n if (index == null) {\\r\\n index = 0;\\r\\n }\\r\\n this.index = index = Math.round(index) % this.values.length;\\r\\n\\r\\n this.setOutputData(0, this.values[index]);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"logic/sequence\\\", Sequence);\\r\\n})(this);\\r\\n\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n function GraphicsPlot() {\\r\\n this.addInput(\\\"A\\\", \\\"Number\\\");\\r\\n this.addInput(\\\"B\\\", \\\"Number\\\");\\r\\n this.addInput(\\\"C\\\", \\\"Number\\\");\\r\\n this.addInput(\\\"D\\\", \\\"Number\\\");\\r\\n\\r\\n this.values = [[], [], [], []];\\r\\n this.properties = { scale: 2 };\\r\\n }\\r\\n\\r\\n GraphicsPlot.title = \\\"Plot\\\";\\r\\n GraphicsPlot.desc = \\\"Plots data over time\\\";\\r\\n GraphicsPlot.colors = [\\\"#FFF\\\", \\\"#F99\\\", \\\"#9F9\\\", \\\"#99F\\\"];\\r\\n\\r\\n GraphicsPlot.prototype.onExecute = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var size = this.size;\\r\\n\\r\\n for (var i = 0; i < 4; ++i) {\\r\\n var v = this.getInputData(i);\\r\\n if (v == null) {\\r\\n continue;\\r\\n }\\r\\n var values = this.values[i];\\r\\n values.push(v);\\r\\n if (values.length > size[0]) {\\r\\n values.shift();\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n GraphicsPlot.prototype.onDrawBackground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var size = this.size;\\r\\n\\r\\n var scale = (0.5 * size[1]) / this.properties.scale;\\r\\n var colors = GraphicsPlot.colors;\\r\\n var offset = size[1] * 0.5;\\r\\n\\r\\n ctx.fillStyle = \\\"#000\\\";\\r\\n ctx.fillRect(0, 0, size[0], size[1]);\\r\\n ctx.strokeStyle = \\\"#555\\\";\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(0, offset);\\r\\n ctx.lineTo(size[0], offset);\\r\\n ctx.stroke();\\r\\n\\r\\n if (this.inputs) {\\r\\n for (var i = 0; i < 4; ++i) {\\r\\n var values = this.values[i];\\r\\n if (!this.inputs[i] || !this.inputs[i].link) {\\r\\n continue;\\r\\n }\\r\\n ctx.strokeStyle = colors[i];\\r\\n ctx.beginPath();\\r\\n var v = values[0] * scale * -1 + offset;\\r\\n ctx.moveTo(0, Math.clamp(v, 0, size[1]));\\r\\n for (var j = 1; j < values.length && j < size[0]; ++j) {\\r\\n var v = values[j] * scale * -1 + offset;\\r\\n ctx.lineTo(j, Math.clamp(v, 0, size[1]));\\r\\n }\\r\\n ctx.stroke();\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"graphics/plot\\\", GraphicsPlot);\\r\\n\\r\\n function GraphicsImage() {\\r\\n this.addOutput(\\\"frame\\\", \\\"image\\\");\\r\\n this.properties = { url: \\\"\\\" };\\r\\n }\\r\\n\\r\\n GraphicsImage.title = \\\"Image\\\";\\r\\n GraphicsImage.desc = \\\"Image loader\\\";\\r\\n GraphicsImage.widgets = [{ name: \\\"load\\\", text: \\\"Load\\\", type: \\\"button\\\" }];\\r\\n\\r\\n GraphicsImage.supported_extensions = [\\\"jpg\\\", \\\"jpeg\\\", \\\"png\\\", \\\"gif\\\"];\\r\\n\\r\\n GraphicsImage.prototype.onAdded = function() {\\r\\n if (this.properties[\\\"url\\\"] != \\\"\\\" && this.img == null) {\\r\\n this.loadImage(this.properties[\\\"url\\\"]);\\r\\n }\\r\\n };\\r\\n\\r\\n GraphicsImage.prototype.onDrawBackground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n if (this.img && this.size[0] > 5 && this.size[1] > 5) {\\r\\n ctx.drawImage(this.img, 0, 0, this.size[0], this.size[1]);\\r\\n }\\r\\n };\\r\\n\\r\\n GraphicsImage.prototype.onExecute = function() {\\r\\n if (!this.img) {\\r\\n this.boxcolor = \\\"#000\\\";\\r\\n }\\r\\n if (this.img && this.img.width) {\\r\\n this.setOutputData(0, this.img);\\r\\n } else {\\r\\n this.setOutputData(0, null);\\r\\n }\\r\\n if (this.img && this.img.dirty) {\\r\\n this.img.dirty = false;\\r\\n }\\r\\n };\\r\\n\\r\\n GraphicsImage.prototype.onPropertyChanged = function(name, value) {\\r\\n this.properties[name] = value;\\r\\n if (name == \\\"url\\\" && value != \\\"\\\") {\\r\\n this.loadImage(value);\\r\\n }\\r\\n\\r\\n return true;\\r\\n };\\r\\n\\r\\n GraphicsImage.prototype.loadImage = function(url, callback) {\\r\\n if (url == \\\"\\\") {\\r\\n this.img = null;\\r\\n return;\\r\\n }\\r\\n\\r\\n this.img = document.createElement(\\\"img\\\");\\r\\n\\r\\n if (url.substr(0, 4) == \\\"http\\\" && LiteGraph.proxy) {\\r\\n url = LiteGraph.proxy + url.substr(url.indexOf(\\\":\\\") + 3);\\r\\n }\\r\\n\\r\\n this.img.src = url;\\r\\n this.boxcolor = \\\"#F95\\\";\\r\\n var that = this;\\r\\n this.img.onload = function() {\\r\\n if (callback) {\\r\\n callback(this);\\r\\n }\\r\\n that.trace(\\r\\n \\\"Image loaded, size: \\\" + that.img.width + \\\"x\\\" + that.img.height\\r\\n );\\r\\n this.dirty = true;\\r\\n that.boxcolor = \\\"#9F9\\\";\\r\\n that.setDirtyCanvas(true);\\r\\n };\\r\\n };\\r\\n\\r\\n GraphicsImage.prototype.onWidget = function(e, widget) {\\r\\n if (widget.name == \\\"load\\\") {\\r\\n this.loadImage(this.properties[\\\"url\\\"]);\\r\\n }\\r\\n };\\r\\n\\r\\n GraphicsImage.prototype.onDropFile = function(file) {\\r\\n var that = this;\\r\\n if (this._url) {\\r\\n URL.revokeObjectURL(this._url);\\r\\n }\\r\\n this._url = URL.createObjectURL(file);\\r\\n this.properties.url = this._url;\\r\\n this.loadImage(this._url, function(img) {\\r\\n that.size[1] = (img.height / img.width) * that.size[0];\\r\\n });\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"graphics/image\\\", GraphicsImage);\\r\\n\\r\\n function ColorPalette() {\\r\\n this.addInput(\\\"f\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"Color\\\", \\\"color\\\");\\r\\n this.properties = {\\r\\n colorA: \\\"#444444\\\",\\r\\n colorB: \\\"#44AAFF\\\",\\r\\n colorC: \\\"#44FFAA\\\",\\r\\n colorD: \\\"#FFFFFF\\\"\\r\\n };\\r\\n }\\r\\n\\r\\n ColorPalette.title = \\\"Palette\\\";\\r\\n ColorPalette.desc = \\\"Generates a color\\\";\\r\\n\\r\\n ColorPalette.prototype.onExecute = function() {\\r\\n var c = [];\\r\\n\\r\\n if (this.properties.colorA != null) {\\r\\n c.push(hex2num(this.properties.colorA));\\r\\n }\\r\\n if (this.properties.colorB != null) {\\r\\n c.push(hex2num(this.properties.colorB));\\r\\n }\\r\\n if (this.properties.colorC != null) {\\r\\n c.push(hex2num(this.properties.colorC));\\r\\n }\\r\\n if (this.properties.colorD != null) {\\r\\n c.push(hex2num(this.properties.colorD));\\r\\n }\\r\\n\\r\\n var f = this.getInputData(0);\\r\\n if (f == null) {\\r\\n f = 0.5;\\r\\n }\\r\\n if (f > 1.0) {\\r\\n f = 1.0;\\r\\n } else if (f < 0.0) {\\r\\n f = 0.0;\\r\\n }\\r\\n\\r\\n if (c.length == 0) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var result = [0, 0, 0];\\r\\n if (f == 0) {\\r\\n result = c[0];\\r\\n } else if (f == 1) {\\r\\n result = c[c.length - 1];\\r\\n } else {\\r\\n var pos = (c.length - 1) * f;\\r\\n var c1 = c[Math.floor(pos)];\\r\\n var c2 = c[Math.floor(pos) + 1];\\r\\n var t = pos - Math.floor(pos);\\r\\n result[0] = c1[0] * (1 - t) + c2[0] * t;\\r\\n result[1] = c1[1] * (1 - t) + c2[1] * t;\\r\\n result[2] = c1[2] * (1 - t) + c2[2] * t;\\r\\n }\\r\\n\\r\\n /*\\r\\n\\tc[0] = 1.0 - Math.abs( Math.sin( 0.1 * reModular.getTime() * Math.PI) );\\r\\n\\tc[1] = Math.abs( Math.sin( 0.07 * reModular.getTime() * Math.PI) );\\r\\n\\tc[2] = Math.abs( Math.sin( 0.01 * reModular.getTime() * Math.PI) );\\r\\n\\t*/\\r\\n\\r\\n for (var i in result) {\\r\\n result[i] /= 255;\\r\\n }\\r\\n\\r\\n this.boxcolor = colorToString(result);\\r\\n this.setOutputData(0, result);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"color/palette\\\", ColorPalette);\\r\\n\\r\\n function ImageFrame() {\\r\\n this.addInput(\\\"\\\", \\\"image,canvas\\\");\\r\\n this.size = [200, 200];\\r\\n }\\r\\n\\r\\n ImageFrame.title = \\\"Frame\\\";\\r\\n ImageFrame.desc = \\\"Frame viewerew\\\";\\r\\n ImageFrame.widgets = [\\r\\n { name: \\\"resize\\\", text: \\\"Resize box\\\", type: \\\"button\\\" },\\r\\n { name: \\\"view\\\", text: \\\"View Image\\\", type: \\\"button\\\" }\\r\\n ];\\r\\n\\r\\n ImageFrame.prototype.onDrawBackground = function(ctx) {\\r\\n if (this.frame && !this.flags.collapsed) {\\r\\n ctx.drawImage(this.frame, 0, 0, this.size[0], this.size[1]);\\r\\n }\\r\\n };\\r\\n\\r\\n ImageFrame.prototype.onExecute = function() {\\r\\n this.frame = this.getInputData(0);\\r\\n this.setDirtyCanvas(true);\\r\\n };\\r\\n\\r\\n ImageFrame.prototype.onWidget = function(e, widget) {\\r\\n if (widget.name == \\\"resize\\\" && this.frame) {\\r\\n var width = this.frame.width;\\r\\n var height = this.frame.height;\\r\\n\\r\\n if (!width && this.frame.videoWidth != null) {\\r\\n width = this.frame.videoWidth;\\r\\n height = this.frame.videoHeight;\\r\\n }\\r\\n\\r\\n if (width && height) {\\r\\n this.size = [width, height];\\r\\n }\\r\\n this.setDirtyCanvas(true, true);\\r\\n } else if (widget.name == \\\"view\\\") {\\r\\n this.show();\\r\\n }\\r\\n };\\r\\n\\r\\n ImageFrame.prototype.show = function() {\\r\\n //var str = this.canvas.toDataURL(\\\"image/png\\\");\\r\\n if (showElement && this.frame) {\\r\\n showElement(this.frame);\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"graphics/frame\\\", ImageFrame);\\r\\n\\r\\n function ImageFade() {\\r\\n this.addInputs([\\r\\n [\\\"img1\\\", \\\"image\\\"],\\r\\n [\\\"img2\\\", \\\"image\\\"],\\r\\n [\\\"fade\\\", \\\"number\\\"]\\r\\n ]);\\r\\n this.addOutput(\\\"\\\", \\\"image\\\");\\r\\n this.properties = { fade: 0.5, width: 512, height: 512 };\\r\\n }\\r\\n\\r\\n ImageFade.title = \\\"Image fade\\\";\\r\\n ImageFade.desc = \\\"Fades between images\\\";\\r\\n ImageFade.widgets = [\\r\\n { name: \\\"resizeA\\\", text: \\\"Resize to A\\\", type: \\\"button\\\" },\\r\\n { name: \\\"resizeB\\\", text: \\\"Resize to B\\\", type: \\\"button\\\" }\\r\\n ];\\r\\n\\r\\n ImageFade.prototype.onAdded = function() {\\r\\n this.createCanvas();\\r\\n var ctx = this.canvas.getContext(\\\"2d\\\");\\r\\n ctx.fillStyle = \\\"#000\\\";\\r\\n ctx.fillRect(0, 0, this.properties[\\\"width\\\"], this.properties[\\\"height\\\"]);\\r\\n };\\r\\n\\r\\n ImageFade.prototype.createCanvas = function() {\\r\\n this.canvas = document.createElement(\\\"canvas\\\");\\r\\n this.canvas.width = this.properties[\\\"width\\\"];\\r\\n this.canvas.height = this.properties[\\\"height\\\"];\\r\\n };\\r\\n\\r\\n ImageFade.prototype.onExecute = function() {\\r\\n var ctx = this.canvas.getContext(\\\"2d\\\");\\r\\n this.canvas.width = this.canvas.width;\\r\\n\\r\\n var A = this.getInputData(0);\\r\\n if (A != null) {\\r\\n ctx.drawImage(A, 0, 0, this.canvas.width, this.canvas.height);\\r\\n }\\r\\n\\r\\n var fade = this.getInputData(2);\\r\\n if (fade == null) {\\r\\n fade = this.properties[\\\"fade\\\"];\\r\\n } else {\\r\\n this.properties[\\\"fade\\\"] = fade;\\r\\n }\\r\\n\\r\\n ctx.globalAlpha = fade;\\r\\n var B = this.getInputData(1);\\r\\n if (B != null) {\\r\\n ctx.drawImage(B, 0, 0, this.canvas.width, this.canvas.height);\\r\\n }\\r\\n ctx.globalAlpha = 1.0;\\r\\n\\r\\n this.setOutputData(0, this.canvas);\\r\\n this.setDirtyCanvas(true);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"graphics/imagefade\\\", ImageFade);\\r\\n\\r\\n function ImageCrop() {\\r\\n this.addInput(\\\"\\\", \\\"image\\\");\\r\\n this.addOutput(\\\"\\\", \\\"image\\\");\\r\\n this.properties = { width: 256, height: 256, x: 0, y: 0, scale: 1.0 };\\r\\n this.size = [50, 20];\\r\\n }\\r\\n\\r\\n ImageCrop.title = \\\"Crop\\\";\\r\\n ImageCrop.desc = \\\"Crop Image\\\";\\r\\n\\r\\n ImageCrop.prototype.onAdded = function() {\\r\\n this.createCanvas();\\r\\n };\\r\\n\\r\\n ImageCrop.prototype.createCanvas = function() {\\r\\n this.canvas = document.createElement(\\\"canvas\\\");\\r\\n this.canvas.width = this.properties[\\\"width\\\"];\\r\\n this.canvas.height = this.properties[\\\"height\\\"];\\r\\n };\\r\\n\\r\\n ImageCrop.prototype.onExecute = function() {\\r\\n var input = this.getInputData(0);\\r\\n if (!input) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (input.width) {\\r\\n var ctx = this.canvas.getContext(\\\"2d\\\");\\r\\n\\r\\n ctx.drawImage(\\r\\n input,\\r\\n -this.properties[\\\"x\\\"],\\r\\n -this.properties[\\\"y\\\"],\\r\\n input.width * this.properties[\\\"scale\\\"],\\r\\n input.height * this.properties[\\\"scale\\\"]\\r\\n );\\r\\n this.setOutputData(0, this.canvas);\\r\\n } else {\\r\\n this.setOutputData(0, null);\\r\\n }\\r\\n };\\r\\n\\r\\n ImageCrop.prototype.onDrawBackground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n if (this.canvas) {\\r\\n ctx.drawImage(\\r\\n this.canvas,\\r\\n 0,\\r\\n 0,\\r\\n this.canvas.width,\\r\\n this.canvas.height,\\r\\n 0,\\r\\n 0,\\r\\n this.size[0],\\r\\n this.size[1]\\r\\n );\\r\\n }\\r\\n };\\r\\n\\r\\n ImageCrop.prototype.onPropertyChanged = function(name, value) {\\r\\n this.properties[name] = value;\\r\\n\\r\\n if (name == \\\"scale\\\") {\\r\\n this.properties[name] = parseFloat(value);\\r\\n if (this.properties[name] == 0) {\\r\\n this.trace(\\\"Error in scale\\\");\\r\\n this.properties[name] = 1.0;\\r\\n }\\r\\n } else {\\r\\n this.properties[name] = parseInt(value);\\r\\n }\\r\\n\\r\\n this.createCanvas();\\r\\n\\r\\n return true;\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"graphics/cropImage\\\", ImageCrop);\\r\\n\\r\\n //CANVAS stuff\\r\\n\\r\\n function CanvasNode() {\\r\\n this.addInput(\\\"clear\\\", LiteGraph.ACTION);\\r\\n this.addOutput(\\\"\\\", \\\"canvas\\\");\\r\\n this.properties = { width: 512, height: 512, autoclear: true };\\r\\n\\r\\n this.canvas = document.createElement(\\\"canvas\\\");\\r\\n this.ctx = this.canvas.getContext(\\\"2d\\\");\\r\\n }\\r\\n\\r\\n CanvasNode.title = \\\"Canvas\\\";\\r\\n CanvasNode.desc = \\\"Canvas to render stuff\\\";\\r\\n\\r\\n CanvasNode.prototype.onExecute = function() {\\r\\n var canvas = this.canvas;\\r\\n var w = this.properties.width | 0;\\r\\n var h = this.properties.height | 0;\\r\\n if (canvas.width != w) {\\r\\n canvas.width = w;\\r\\n }\\r\\n if (canvas.height != h) {\\r\\n canvas.height = h;\\r\\n }\\r\\n\\r\\n if (this.properties.autoclear) {\\r\\n this.ctx.clearRect(0, 0, canvas.width, canvas.height);\\r\\n }\\r\\n this.setOutputData(0, canvas);\\r\\n };\\r\\n\\r\\n CanvasNode.prototype.onAction = function(action, param) {\\r\\n if (action == \\\"clear\\\") {\\r\\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"graphics/canvas\\\", CanvasNode);\\r\\n\\r\\n function DrawImageNode() {\\r\\n this.addInput(\\\"canvas\\\", \\\"canvas\\\");\\r\\n this.addInput(\\\"img\\\", \\\"image,canvas\\\");\\r\\n this.addInput(\\\"x\\\", \\\"number\\\");\\r\\n this.addInput(\\\"y\\\", \\\"number\\\");\\r\\n this.properties = { x: 0, y: 0, opacity: 1 };\\r\\n }\\r\\n\\r\\n DrawImageNode.title = \\\"DrawImage\\\";\\r\\n DrawImageNode.desc = \\\"Draws image into a canvas\\\";\\r\\n\\r\\n DrawImageNode.prototype.onExecute = function() {\\r\\n var canvas = this.getInputData(0);\\r\\n if (!canvas) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var img = this.getInputOrProperty(\\\"img\\\");\\r\\n if (!img) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var x = this.getInputOrProperty(\\\"x\\\");\\r\\n var y = this.getInputOrProperty(\\\"y\\\");\\r\\n var ctx = canvas.getContext(\\\"2d\\\");\\r\\n ctx.drawImage(img, x, y);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"graphics/drawImage\\\", DrawImageNode);\\r\\n\\r\\n function DrawRectangleNode() {\\r\\n this.addInput(\\\"canvas\\\", \\\"canvas\\\");\\r\\n this.addInput(\\\"x\\\", \\\"number\\\");\\r\\n this.addInput(\\\"y\\\", \\\"number\\\");\\r\\n this.addInput(\\\"w\\\", \\\"number\\\");\\r\\n this.addInput(\\\"h\\\", \\\"number\\\");\\r\\n this.properties = {\\r\\n x: 0,\\r\\n y: 0,\\r\\n w: 10,\\r\\n h: 10,\\r\\n color: \\\"white\\\",\\r\\n opacity: 1\\r\\n };\\r\\n }\\r\\n\\r\\n DrawRectangleNode.title = \\\"DrawRectangle\\\";\\r\\n DrawRectangleNode.desc = \\\"Draws rectangle in canvas\\\";\\r\\n\\r\\n DrawRectangleNode.prototype.onExecute = function() {\\r\\n var canvas = this.getInputData(0);\\r\\n if (!canvas) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var x = this.getInputOrProperty(\\\"x\\\");\\r\\n var y = this.getInputOrProperty(\\\"y\\\");\\r\\n var w = this.getInputOrProperty(\\\"w\\\");\\r\\n var h = this.getInputOrProperty(\\\"h\\\");\\r\\n var ctx = canvas.getContext(\\\"2d\\\");\\r\\n ctx.fillRect(x, y, w, h);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"graphics/drawRectangle\\\", DrawRectangleNode);\\r\\n\\r\\n function ImageVideo() {\\r\\n this.addInput(\\\"t\\\", \\\"number\\\");\\r\\n this.addOutputs([[\\\"frame\\\", \\\"image\\\"], [\\\"t\\\", \\\"number\\\"], [\\\"d\\\", \\\"number\\\"]]);\\r\\n this.properties = { url: \\\"\\\", use_proxy: true };\\r\\n }\\r\\n\\r\\n ImageVideo.title = \\\"Video\\\";\\r\\n ImageVideo.desc = \\\"Video playback\\\";\\r\\n ImageVideo.widgets = [\\r\\n { name: \\\"play\\\", text: \\\"PLAY\\\", type: \\\"minibutton\\\" },\\r\\n { name: \\\"stop\\\", text: \\\"STOP\\\", type: \\\"minibutton\\\" },\\r\\n { name: \\\"demo\\\", text: \\\"Demo video\\\", type: \\\"button\\\" },\\r\\n { name: \\\"mute\\\", text: \\\"Mute video\\\", type: \\\"button\\\" }\\r\\n ];\\r\\n\\r\\n ImageVideo.prototype.onExecute = function() {\\r\\n if (!this.properties.url) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.properties.url != this._video_url) {\\r\\n this.loadVideo(this.properties.url);\\r\\n }\\r\\n\\r\\n if (!this._video || this._video.width == 0) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var t = this.getInputData(0);\\r\\n if (t && t >= 0 && t <= 1.0) {\\r\\n this._video.currentTime = t * this._video.duration;\\r\\n this._video.pause();\\r\\n }\\r\\n\\r\\n this._video.dirty = true;\\r\\n this.setOutputData(0, this._video);\\r\\n this.setOutputData(1, this._video.currentTime);\\r\\n this.setOutputData(2, this._video.duration);\\r\\n this.setDirtyCanvas(true);\\r\\n };\\r\\n\\r\\n ImageVideo.prototype.onStart = function() {\\r\\n this.play();\\r\\n };\\r\\n\\r\\n ImageVideo.prototype.onStop = function() {\\r\\n this.stop();\\r\\n };\\r\\n\\r\\n ImageVideo.prototype.loadVideo = function(url) {\\r\\n this._video_url = url;\\r\\n\\r\\n if (\\r\\n this.properties.use_proxy &&\\r\\n url.substr(0, 4) == \\\"http\\\" &&\\r\\n LiteGraph.proxy\\r\\n ) {\\r\\n url = LiteGraph.proxy + url.substr(url.indexOf(\\\":\\\") + 3);\\r\\n }\\r\\n\\r\\n this._video = document.createElement(\\\"video\\\");\\r\\n this._video.src = url;\\r\\n this._video.type = \\\"type=video/mp4\\\";\\r\\n\\r\\n this._video.muted = true;\\r\\n this._video.autoplay = true;\\r\\n\\r\\n var that = this;\\r\\n this._video.addEventListener(\\\"loadedmetadata\\\", function(e) {\\r\\n //onload\\r\\n that.trace(\\\"Duration: \\\" + this.duration + \\\" seconds\\\");\\r\\n that.trace(\\\"Size: \\\" + this.videoWidth + \\\",\\\" + this.videoHeight);\\r\\n that.setDirtyCanvas(true);\\r\\n this.width = this.videoWidth;\\r\\n this.height = this.videoHeight;\\r\\n });\\r\\n this._video.addEventListener(\\\"progress\\\", function(e) {\\r\\n //onload\\r\\n //that.trace(\\\"loading...\\\");\\r\\n });\\r\\n this._video.addEventListener(\\\"error\\\", function(e) {\\r\\n console.log(\\\"Error loading video: \\\" + this.src);\\r\\n that.trace(\\\"Error loading video: \\\" + this.src);\\r\\n if (this.error) {\\r\\n switch (this.error.code) {\\r\\n case this.error.MEDIA_ERR_ABORTED:\\r\\n that.trace(\\\"You stopped the video.\\\");\\r\\n break;\\r\\n case this.error.MEDIA_ERR_NETWORK:\\r\\n that.trace(\\\"Network error - please try again later.\\\");\\r\\n break;\\r\\n case this.error.MEDIA_ERR_DECODE:\\r\\n that.trace(\\\"Video is broken..\\\");\\r\\n break;\\r\\n case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:\\r\\n that.trace(\\r\\n \\\"Sorry, your browser can't play this video.\\\"\\r\\n );\\r\\n break;\\r\\n }\\r\\n }\\r\\n });\\r\\n\\r\\n this._video.addEventListener(\\\"ended\\\", function(e) {\\r\\n that.trace(\\\"Ended.\\\");\\r\\n this.play(); //loop\\r\\n });\\r\\n\\r\\n //document.body.appendChild(this.video);\\r\\n };\\r\\n\\r\\n ImageVideo.prototype.onPropertyChanged = function(name, value) {\\r\\n this.properties[name] = value;\\r\\n if (name == \\\"url\\\" && value != \\\"\\\") {\\r\\n this.loadVideo(value);\\r\\n }\\r\\n\\r\\n return true;\\r\\n };\\r\\n\\r\\n ImageVideo.prototype.play = function() {\\r\\n if (this._video) {\\r\\n this._video.play();\\r\\n }\\r\\n };\\r\\n\\r\\n ImageVideo.prototype.playPause = function() {\\r\\n if (!this._video) {\\r\\n return;\\r\\n }\\r\\n if (this._video.paused) {\\r\\n this.play();\\r\\n } else {\\r\\n this.pause();\\r\\n }\\r\\n };\\r\\n\\r\\n ImageVideo.prototype.stop = function() {\\r\\n if (!this._video) {\\r\\n return;\\r\\n }\\r\\n this._video.pause();\\r\\n this._video.currentTime = 0;\\r\\n };\\r\\n\\r\\n ImageVideo.prototype.pause = function() {\\r\\n if (!this._video) {\\r\\n return;\\r\\n }\\r\\n this.trace(\\\"Video paused\\\");\\r\\n this._video.pause();\\r\\n };\\r\\n\\r\\n ImageVideo.prototype.onWidget = function(e, widget) {\\r\\n /*\\r\\n\\tif(widget.name == \\\"demo\\\")\\r\\n\\t{\\r\\n\\t\\tthis.loadVideo();\\r\\n\\t}\\r\\n\\telse if(widget.name == \\\"play\\\")\\r\\n\\t{\\r\\n\\t\\tif(this._video)\\r\\n\\t\\t\\tthis.playPause();\\r\\n\\t}\\r\\n\\tif(widget.name == \\\"stop\\\")\\r\\n\\t{\\r\\n\\t\\tthis.stop();\\r\\n\\t}\\r\\n\\telse if(widget.name == \\\"mute\\\")\\r\\n\\t{\\r\\n\\t\\tif(this._video)\\r\\n\\t\\t\\tthis._video.muted = !this._video.muted;\\r\\n\\t}\\r\\n\\t*/\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"graphics/video\\\", ImageVideo);\\r\\n\\r\\n // Texture Webcam *****************************************\\r\\n function ImageWebcam() {\\r\\n this.addOutput(\\\"Webcam\\\", \\\"image\\\");\\r\\n this.properties = { facingMode: \\\"user\\\" };\\r\\n this.boxcolor = \\\"black\\\";\\r\\n this.frame = 0;\\r\\n }\\r\\n\\r\\n ImageWebcam.title = \\\"Webcam\\\";\\r\\n ImageWebcam.desc = \\\"Webcam image\\\";\\r\\n ImageWebcam.is_webcam_open = false;\\r\\n\\r\\n ImageWebcam.prototype.openStream = function() {\\r\\n if (!navigator.getUserMedia) {\\r\\n //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags');\\r\\n return;\\r\\n }\\r\\n\\r\\n this._waiting_confirmation = true;\\r\\n\\r\\n // Not showing vendor prefixes.\\r\\n var constraints = {\\r\\n audio: false,\\r\\n video: { facingMode: this.properties.facingMode }\\r\\n };\\r\\n navigator.mediaDevices\\r\\n .getUserMedia(constraints)\\r\\n .then(this.streamReady.bind(this))\\r\\n .catch(onFailSoHard);\\r\\n\\r\\n var that = this;\\r\\n function onFailSoHard(e) {\\r\\n console.log(\\\"Webcam rejected\\\", e);\\r\\n that._webcam_stream = false;\\r\\n ImageWebcam.is_webcam_open = false;\\r\\n that.boxcolor = \\\"red\\\";\\r\\n that.trigger(\\\"stream_error\\\");\\r\\n }\\r\\n };\\r\\n\\r\\n ImageWebcam.prototype.closeStream = function() {\\r\\n if (this._webcam_stream) {\\r\\n var tracks = this._webcam_stream.getTracks();\\r\\n if (tracks.length) {\\r\\n for (var i = 0; i < tracks.length; ++i) {\\r\\n tracks[i].stop();\\r\\n }\\r\\n }\\r\\n ImageWebcam.is_webcam_open = false;\\r\\n this._webcam_stream = null;\\r\\n this._video = null;\\r\\n this.boxcolor = \\\"black\\\";\\r\\n this.trigger(\\\"stream_closed\\\");\\r\\n }\\r\\n };\\r\\n\\r\\n ImageWebcam.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"facingMode\\\") {\\r\\n this.properties.facingMode = value;\\r\\n this.closeStream();\\r\\n this.openStream();\\r\\n }\\r\\n };\\r\\n\\r\\n ImageWebcam.prototype.onRemoved = function() {\\r\\n this.closeStream();\\r\\n };\\r\\n\\r\\n ImageWebcam.prototype.streamReady = function(localMediaStream) {\\r\\n this._webcam_stream = localMediaStream;\\r\\n //this._waiting_confirmation = false;\\r\\n this.boxcolor = \\\"green\\\";\\r\\n\\r\\n var video = this._video;\\r\\n if (!video) {\\r\\n video = document.createElement(\\\"video\\\");\\r\\n video.autoplay = true;\\r\\n video.srcObject = localMediaStream;\\r\\n this._video = video;\\r\\n //document.body.appendChild( video ); //debug\\r\\n //when video info is loaded (size and so)\\r\\n video.onloadedmetadata = function(e) {\\r\\n // Ready to go. Do some stuff.\\r\\n console.log(e);\\r\\n ImageWebcam.is_webcam_open = true;\\r\\n };\\r\\n }\\r\\n\\r\\n this.trigger(\\\"stream_ready\\\", video);\\r\\n };\\r\\n\\r\\n ImageWebcam.prototype.onExecute = function() {\\r\\n if (this._webcam_stream == null && !this._waiting_confirmation) {\\r\\n this.openStream();\\r\\n }\\r\\n\\r\\n if (!this._video || !this._video.videoWidth) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this._video.frame = ++this.frame;\\r\\n this._video.width = this._video.videoWidth;\\r\\n this._video.height = this._video.videoHeight;\\r\\n this.setOutputData(0, this._video);\\r\\n for (var i = 1; i < this.outputs.length; ++i) {\\r\\n if (!this.outputs[i]) {\\r\\n continue;\\r\\n }\\r\\n switch (this.outputs[i].name) {\\r\\n case \\\"width\\\":\\r\\n this.setOutputData(i, this._video.videoWidth);\\r\\n break;\\r\\n case \\\"height\\\":\\r\\n this.setOutputData(i, this._video.videoHeight);\\r\\n break;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n ImageWebcam.prototype.getExtraMenuOptions = function(graphcanvas) {\\r\\n var that = this;\\r\\n var txt = !that.properties.show ? \\\"Show Frame\\\" : \\\"Hide Frame\\\";\\r\\n return [\\r\\n {\\r\\n content: txt,\\r\\n callback: function() {\\r\\n that.properties.show = !that.properties.show;\\r\\n }\\r\\n }\\r\\n ];\\r\\n };\\r\\n\\r\\n ImageWebcam.prototype.onDrawBackground = function(ctx) {\\r\\n if (\\r\\n this.flags.collapsed ||\\r\\n this.size[1] <= 20 ||\\r\\n !this.properties.show\\r\\n ) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!this._video) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //render to graph canvas\\r\\n ctx.save();\\r\\n ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]);\\r\\n ctx.restore();\\r\\n };\\r\\n\\r\\n ImageWebcam.prototype.onGetOutputs = function() {\\r\\n return [\\r\\n [\\\"width\\\", \\\"number\\\"],\\r\\n [\\\"height\\\", \\\"number\\\"],\\r\\n [\\\"stream_ready\\\", LiteGraph.EVENT],\\r\\n [\\\"stream_closed\\\", LiteGraph.EVENT],\\r\\n [\\\"stream_error\\\", LiteGraph.EVENT]\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"graphics/webcam\\\", ImageWebcam);\\r\\n})(this);\\r\\n\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n //Works with Litegl.js to create WebGL nodes\\r\\n global.LGraphTexture = null;\\r\\n\\r\\n if (typeof GL == \\\"undefined\\\")\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tLGraphCanvas.link_type_colors[\\\"Texture\\\"] = \\\"#987\\\";\\r\\n\\r\\n\\tfunction LGraphTexture() {\\r\\n\\t\\tthis.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = { name: \\\"\\\", filter: true };\\r\\n\\t\\tthis.size = [\\r\\n\\t\\t\\tLGraphTexture.image_preview_size,\\r\\n\\t\\t\\tLGraphTexture.image_preview_size\\r\\n\\t\\t];\\r\\n\\t}\\r\\n\\r\\n\\tglobal.LGraphTexture = LGraphTexture;\\r\\n\\r\\n\\tLGraphTexture.title = \\\"Texture\\\";\\r\\n\\tLGraphTexture.desc = \\\"Texture\\\";\\r\\n\\tLGraphTexture.widgets_info = {\\r\\n\\t\\tname: { widget: \\\"texture\\\" },\\r\\n\\t\\tfilter: { widget: \\\"checkbox\\\" }\\r\\n\\t};\\r\\n\\r\\n\\t//REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK\\r\\n\\tLGraphTexture.loadTextureCallback = null; //function in charge of loading textures when not present in the container\\r\\n\\tLGraphTexture.image_preview_size = 256;\\r\\n\\r\\n\\t//flags to choose output texture type\\r\\n\\tLGraphTexture.PASS_THROUGH = 1; //do not apply FX\\r\\n\\tLGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture\\r\\n\\tLGraphTexture.LOW = 3; //create new texture with low precision (byte)\\r\\n\\tLGraphTexture.HIGH = 4; //create new texture with high precision (half-float)\\r\\n\\tLGraphTexture.REUSE = 5; //reuse input texture\\r\\n\\tLGraphTexture.DEFAULT = 2;\\r\\n\\r\\n\\tLGraphTexture.MODE_VALUES = {\\r\\n\\t\\t\\\"pass through\\\": LGraphTexture.PASS_THROUGH,\\r\\n\\t\\tcopy: LGraphTexture.COPY,\\r\\n\\t\\tlow: LGraphTexture.LOW,\\r\\n\\t\\thigh: LGraphTexture.HIGH,\\r\\n\\t\\treuse: LGraphTexture.REUSE,\\r\\n\\t\\tdefault: LGraphTexture.DEFAULT\\r\\n\\t};\\r\\n\\r\\n\\t//returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager)\\r\\n\\tLGraphTexture.getTexturesContainer = function() {\\r\\n\\t\\treturn gl.textures;\\r\\n\\t};\\r\\n\\r\\n\\t//process the loading of a texture (overwrite it if you have a Resources Manager)\\r\\n\\tLGraphTexture.loadTexture = function(name, options) {\\r\\n\\t\\toptions = options || {};\\r\\n\\t\\tvar url = name;\\r\\n\\t\\tif (url.substr(0, 7) == \\\"http://\\\") {\\r\\n\\t\\t\\tif (LiteGraph.proxy) {\\r\\n\\t\\t\\t\\t//proxy external files\\r\\n\\t\\t\\t\\turl = LiteGraph.proxy + url.substr(7);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar container = LGraphTexture.getTexturesContainer();\\r\\n\\t\\tvar tex = (container[name] = GL.Texture.fromURL(url, options));\\r\\n\\t\\treturn tex;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.getTexture = function(name) {\\r\\n\\t\\tvar container = this.getTexturesContainer();\\r\\n\\r\\n\\t\\tif (!container) {\\r\\n\\t\\t\\tthrow \\\"Cannot load texture, container of textures not found\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar tex = container[name];\\r\\n\\t\\tif (!tex && name && name[0] != \\\":\\\") {\\r\\n\\t\\t\\treturn this.loadTexture(name);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn tex;\\r\\n\\t};\\r\\n\\r\\n\\t//used to compute the appropiate output texture\\r\\n\\tLGraphTexture.getTargetTexture = function(origin, target, mode) {\\r\\n\\t\\tif (!origin) {\\r\\n\\t\\t\\tthrow \\\"LGraphTexture.getTargetTexture expects a reference texture\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar tex_type = null;\\r\\n\\r\\n\\t\\tswitch (mode) {\\r\\n\\t\\t\\tcase LGraphTexture.LOW:\\r\\n\\t\\t\\t\\ttex_type = gl.UNSIGNED_BYTE;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase LGraphTexture.HIGH:\\r\\n\\t\\t\\t\\ttex_type = gl.HIGH_PRECISION_FORMAT;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase LGraphTexture.REUSE:\\r\\n\\t\\t\\t\\treturn origin;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase LGraphTexture.COPY:\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\ttex_type = origin ? origin.type : gl.UNSIGNED_BYTE;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (\\r\\n\\t\\t\\t!target ||\\r\\n\\t\\t\\ttarget.width != origin.width ||\\r\\n\\t\\t\\ttarget.height != origin.height ||\\r\\n\\t\\t\\ttarget.type != tex_type\\r\\n\\t\\t) {\\r\\n\\t\\t\\ttarget = new GL.Texture(origin.width, origin.height, {\\r\\n\\t\\t\\t\\ttype: tex_type,\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn target;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.getTextureType = function(precision, ref_texture) {\\r\\n\\t\\tvar type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE;\\r\\n\\t\\tswitch (precision) {\\r\\n\\t\\t\\tcase LGraphTexture.HIGH:\\r\\n\\t\\t\\t\\ttype = gl.HIGH_PRECISION_FORMAT;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase LGraphTexture.LOW:\\r\\n\\t\\t\\t\\ttype = gl.UNSIGNED_BYTE;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t//no default\\r\\n\\t\\t}\\r\\n\\t\\treturn type;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.getWhiteTexture = function() {\\r\\n\\t\\tif (this._white_texture) {\\r\\n\\t\\t\\treturn this._white_texture;\\r\\n\\t\\t}\\r\\n\\t\\tvar texture = (this._white_texture = GL.Texture.fromMemory(\\r\\n\\t\\t\\t1,\\r\\n\\t\\t\\t1,\\r\\n\\t\\t\\t[255, 255, 255, 255],\\r\\n\\t\\t\\t{ format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST }\\r\\n\\t\\t));\\r\\n\\t\\treturn texture;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.getNoiseTexture = function() {\\r\\n\\t\\tif (this._noise_texture) {\\r\\n\\t\\t\\treturn this._noise_texture;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar noise = new Uint8Array(512 * 512 * 4);\\r\\n\\t\\tfor (var i = 0; i < 512 * 512 * 4; ++i) {\\r\\n\\t\\t\\tnoise[i] = Math.random() * 255;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar texture = GL.Texture.fromMemory(512, 512, noise, {\\r\\n\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\twrap: gl.REPEAT,\\r\\n\\t\\t\\tfilter: gl.NEAREST\\r\\n\\t\\t});\\r\\n\\t\\tthis._noise_texture = texture;\\r\\n\\t\\treturn texture;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.prototype.onDropFile = function(data, filename, file) {\\r\\n\\t\\tif (!data) {\\r\\n\\t\\t\\tthis._drop_texture = null;\\r\\n\\t\\t\\tthis.properties.name = \\\"\\\";\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tvar texture = null;\\r\\n\\t\\t\\tif (typeof data == \\\"string\\\") {\\r\\n\\t\\t\\t\\ttexture = GL.Texture.fromURL(data);\\r\\n\\t\\t\\t} else if (filename.toLowerCase().indexOf(\\\".dds\\\") != -1) {\\r\\n\\t\\t\\t\\ttexture = GL.Texture.fromDDSInMemory(data);\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tvar blob = new Blob([file]);\\r\\n\\t\\t\\t\\tvar url = URL.createObjectURL(blob);\\r\\n\\t\\t\\t\\ttexture = GL.Texture.fromURL(url);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tthis._drop_texture = texture;\\r\\n\\t\\t\\tthis.properties.name = filename;\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) {\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tif (!this._drop_texture) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\treturn [\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcontent: \\\"Clear\\\",\\r\\n\\t\\t\\t\\tcallback: function() {\\r\\n\\t\\t\\t\\t\\tthat._drop_texture = null;\\r\\n\\t\\t\\t\\t\\tthat.properties.name = \\\"\\\";\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t];\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = null;\\r\\n\\t\\tif (this.isOutputConnected(1)) {\\r\\n\\t\\t\\ttex = this.getInputData(0);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!tex && this._drop_texture) {\\r\\n\\t\\t\\ttex = this._drop_texture;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!tex && this.properties.name) {\\r\\n\\t\\t\\ttex = LGraphTexture.getTexture(this.properties.name);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._last_tex = tex;\\r\\n\\r\\n\\t\\tif (this.properties.filter === false) {\\r\\n\\t\\t\\ttex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST);\\r\\n\\t\\t} else {\\r\\n\\t\\t\\ttex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setOutputData(0, tex);\\r\\n\\r\\n\\t\\tfor (var i = 1; i < this.outputs.length; i++) {\\r\\n\\t\\t\\tvar output = this.outputs[i];\\r\\n\\t\\t\\tif (!output) {\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tvar v = null;\\r\\n\\t\\t\\tif (output.name == \\\"width\\\") {\\r\\n\\t\\t\\t\\tv = tex.width;\\r\\n\\t\\t\\t} else if (output.name == \\\"height\\\") {\\r\\n\\t\\t\\t\\tv = tex.height;\\r\\n\\t\\t\\t} else if (output.name == \\\"aspect\\\") {\\r\\n\\t\\t\\t\\tv = tex.width / tex.height;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.setOutputData(i, v);\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.prototype.onResourceRenamed = function(\\r\\n\\t\\told_name,\\r\\n\\t\\tnew_name\\r\\n\\t) {\\r\\n\\t\\tif (this.properties.name == old_name) {\\r\\n\\t\\t\\tthis.properties.name = new_name;\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.prototype.onDrawBackground = function(ctx) {\\r\\n\\t\\tif (this.flags.collapsed || this.size[1] <= 20) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (this._drop_texture && ctx.webgl) {\\r\\n\\t\\t\\tctx.drawImage(\\r\\n\\t\\t\\t\\tthis._drop_texture,\\r\\n\\t\\t\\t\\t0,\\r\\n\\t\\t\\t\\t0,\\r\\n\\t\\t\\t\\tthis.size[0],\\r\\n\\t\\t\\t\\tthis.size[1]\\r\\n\\t\\t\\t);\\r\\n\\t\\t\\t//this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//Different texture? then get it from the GPU\\r\\n\\t\\tif (this._last_preview_tex != this._last_tex) {\\r\\n\\t\\t\\tif (ctx.webgl) {\\r\\n\\t\\t\\t\\tthis._canvas = this._last_tex;\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tvar tex_canvas = LGraphTexture.generateLowResTexturePreview(\\r\\n\\t\\t\\t\\t\\tthis._last_tex\\r\\n\\t\\t\\t\\t);\\r\\n\\t\\t\\t\\tif (!tex_canvas) {\\r\\n\\t\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tthis._last_preview_tex = this._last_tex;\\r\\n\\t\\t\\t\\tthis._canvas = cloneCanvas(tex_canvas);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this._canvas) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//render to graph canvas\\r\\n\\t\\tctx.save();\\r\\n\\t\\tif (!ctx.webgl) {\\r\\n\\t\\t\\t//reverse image\\r\\n\\t\\t\\tctx.translate(0, this.size[1]);\\r\\n\\t\\t\\tctx.scale(1, -1);\\r\\n\\t\\t}\\r\\n\\t\\tctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]);\\r\\n\\t\\tctx.restore();\\r\\n\\t};\\r\\n\\r\\n\\t//very slow, used at your own risk\\r\\n\\tLGraphTexture.generateLowResTexturePreview = function(tex) {\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar size = LGraphTexture.image_preview_size;\\r\\n\\t\\tvar temp_tex = tex;\\r\\n\\r\\n\\t\\tif (tex.format == gl.DEPTH_COMPONENT) {\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t} //cannot generate from depth\\r\\n\\r\\n\\t\\t//Generate low-level version in the GPU to speed up\\r\\n\\t\\tif (tex.width > size || tex.height > size) {\\r\\n\\t\\t\\ttemp_tex = this._preview_temp_tex;\\r\\n\\t\\t\\tif (!this._preview_temp_tex) {\\r\\n\\t\\t\\t\\ttemp_tex = new GL.Texture(size, size, {\\r\\n\\t\\t\\t\\t\\tminFilter: gl.NEAREST\\r\\n\\t\\t\\t\\t});\\r\\n\\t\\t\\t\\tthis._preview_temp_tex = temp_tex;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//copy\\r\\n\\t\\t\\ttex.copyTo(temp_tex);\\r\\n\\t\\t\\ttex = temp_tex;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//create intermediate canvas with lowquality version\\r\\n\\t\\tvar tex_canvas = this._preview_canvas;\\r\\n\\t\\tif (!tex_canvas) {\\r\\n\\t\\t\\ttex_canvas = createCanvas(size, size);\\r\\n\\t\\t\\tthis._preview_canvas = tex_canvas;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (temp_tex) {\\r\\n\\t\\t\\ttemp_tex.toCanvas(tex_canvas);\\r\\n\\t\\t}\\r\\n\\t\\treturn tex_canvas;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.prototype.getResources = function(res) {\\r\\n\\t\\tif(this.properties.name)\\r\\n\\t\\t\\tres[this.properties.name] = GL.Texture;\\r\\n\\t\\treturn res;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.prototype.onGetInputs = function() {\\r\\n\\t\\treturn [[\\\"in\\\", \\\"Texture\\\"]];\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTexture.prototype.onGetOutputs = function() {\\r\\n\\t\\treturn [\\r\\n\\t\\t\\t[\\\"width\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"height\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"aspect\\\", \\\"number\\\"]\\r\\n\\t\\t];\\r\\n\\t};\\r\\n\\r\\n\\t//used to replace shader code\\r\\n\\tLGraphTexture.replaceCode = function( code, context )\\r\\n\\t{\\r\\n\\t\\treturn code.replace(/\\\\{\\\\{[a-zA-Z0-9_]*\\\\}\\\\}/g, function(v){\\r\\n\\t\\t\\tv = v.replace( /[\\\\{\\\\}]/g, \\\"\\\" );\\r\\n\\t\\t\\treturn context[v] || \\\"\\\";\\r\\n\\t\\t});\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/texture\\\", LGraphTexture);\\r\\n\\r\\n\\t//**************************\\r\\n\\tfunction LGraphTexturePreview() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = { flipY: false };\\r\\n\\t\\tthis.size = [\\r\\n\\t\\t\\tLGraphTexture.image_preview_size,\\r\\n\\t\\t\\tLGraphTexture.image_preview_size\\r\\n\\t\\t];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTexturePreview.title = \\\"Preview\\\";\\r\\n\\tLGraphTexturePreview.desc = \\\"Show a texture in the graph canvas\\\";\\r\\n\\tLGraphTexturePreview.allow_preview = false;\\r\\n\\r\\n\\tLGraphTexturePreview.prototype.onDrawBackground = function(ctx) {\\r\\n\\t\\tif (this.flags.collapsed) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!ctx.webgl && !LGraphTexturePreview.allow_preview) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //not working well\\r\\n\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar tex_canvas = null;\\r\\n\\r\\n\\t\\tif (!tex.handle && ctx.webgl) {\\r\\n\\t\\t\\ttex_canvas = tex;\\r\\n\\t\\t} else {\\r\\n\\t\\t\\ttex_canvas = LGraphTexture.generateLowResTexturePreview(tex);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//render to graph canvas\\r\\n\\t\\tctx.save();\\r\\n\\t\\tif (this.properties.flipY) {\\r\\n\\t\\t\\tctx.translate(0, this.size[1]);\\r\\n\\t\\t\\tctx.scale(1, -1);\\r\\n\\t\\t}\\r\\n\\t\\tctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]);\\r\\n\\t\\tctx.restore();\\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/preview\\\", LGraphTexturePreview);\\r\\n\\r\\n\\t//**************************************\\r\\n\\r\\n\\tfunction LGraphTextureSave() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = { name: \\\"\\\" };\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureSave.title = \\\"Save\\\";\\r\\n\\tLGraphTextureSave.desc = \\\"Save a texture in the repository\\\";\\r\\n\\r\\n\\tLGraphTextureSave.prototype.getPreviewTexture = function()\\r\\n\\t{\\r\\n\\t\\treturn this._texture;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureSave.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (this.properties.name) {\\r\\n\\t\\t\\t//for cases where we want to perform something when storing it\\r\\n\\t\\t\\tif (LGraphTexture.storeTexture) {\\r\\n\\t\\t\\t\\tLGraphTexture.storeTexture(this.properties.name, tex);\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tvar container = LGraphTexture.getTexturesContainer();\\r\\n\\t\\t\\t\\tcontainer[this.properties.name] = tex;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._texture = tex;\\r\\n\\t\\tthis.setOutputData(0, tex);\\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/save\\\", LGraphTextureSave);\\r\\n\\r\\n\\t//****************************************************\\r\\n\\r\\n\\tfunction LGraphTextureOperation() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"TextureB\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"value\\\", \\\"number\\\");\\r\\n\\t\\tthis.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.help = \\\"

pixelcode must be vec3, uvcode must be vec2, is optional

\\\\\\r\\n\\t\\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

\\\";\\r\\n\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tvalue: 1,\\r\\n\\t\\t\\tpixelcode: \\\"color + colorB * value\\\",\\r\\n\\t\\t\\tuvcode: \\\"\\\",\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.has_error = false;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureOperation.widgets_info = {\\r\\n\\t\\tuvcode: { widget: \\\"code\\\" },\\r\\n\\t\\tpixelcode: { widget: \\\"code\\\" },\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureOperation.title = \\\"Operation\\\";\\r\\n\\tLGraphTextureOperation.desc = \\\"Texture shader operation\\\";\\r\\n\\r\\n\\tLGraphTextureOperation.presets = {};\\r\\n\\r\\n\\tLGraphTextureOperation.prototype.getExtraMenuOptions = function(\\r\\n\\t\\tgraphcanvas\\r\\n\\t) {\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tvar txt = !that.properties.show ? \\\"Show Texture\\\" : \\\"Hide Texture\\\";\\r\\n\\t\\treturn [\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcontent: txt,\\r\\n\\t\\t\\t\\tcallback: function() {\\r\\n\\t\\t\\t\\t\\tthat.properties.show = !that.properties.show;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t];\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureOperation.prototype.onPropertyChanged = function()\\r\\n\\t{\\r\\n\\t\\tthis.has_error = false;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureOperation.prototype.onDrawBackground = function(ctx) {\\r\\n\\t\\tif (\\r\\n\\t\\t\\tthis.flags.collapsed ||\\r\\n\\t\\t\\tthis.size[1] <= 20 ||\\r\\n\\t\\t\\t!this.properties.show\\r\\n\\t\\t) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this._tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//only works if using a webgl renderer\\r\\n\\t\\tif (this._tex.gl != ctx) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//render to graph canvas\\r\\n\\t\\tctx.save();\\r\\n\\t\\tctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]);\\r\\n\\t\\tctx.restore();\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureOperation.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tif (this.properties.precision === LGraphTexture.PASS_THROUGH) {\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar texB = this.getInputData(1);\\r\\n\\r\\n\\t\\tif (!this.properties.uvcode && !this.properties.pixelcode) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar width = 512;\\r\\n\\t\\tvar height = 512;\\r\\n\\t\\tif (tex) {\\r\\n\\t\\t\\twidth = tex.width;\\r\\n\\t\\t\\theight = tex.height;\\r\\n\\t\\t} else if (texB) {\\r\\n\\t\\t\\twidth = texB.width;\\r\\n\\t\\t\\theight = texB.height;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!texB)\\r\\n\\t\\t\\ttexB = GL.Texture.getWhiteTexture();\\r\\n\\r\\n\\t\\tvar type = LGraphTexture.getTextureType( this.properties.precision, tex );\\r\\n\\r\\n\\t\\tif (!tex && !this._tex) {\\r\\n\\t\\t\\tthis._tex = new GL.Texture(width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR });\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tthis._tex = LGraphTexture.getTargetTexture( tex || this._tex, this._tex, this.properties.precision );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar uvcode = \\\"\\\";\\r\\n\\t\\tif (this.properties.uvcode) {\\r\\n\\t\\t\\tuvcode = \\\"uv = \\\" + this.properties.uvcode;\\r\\n\\t\\t\\tif (this.properties.uvcode.indexOf(\\\";\\\") != -1) {\\r\\n\\t\\t\\t\\t//there are line breaks, means multiline code\\r\\n\\t\\t\\t\\tuvcode = this.properties.uvcode;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar pixelcode = \\\"\\\";\\r\\n\\t\\tif (this.properties.pixelcode) {\\r\\n\\t\\t\\tpixelcode = \\\"result = \\\" + this.properties.pixelcode;\\r\\n\\t\\t\\tif (this.properties.pixelcode.indexOf(\\\";\\\") != -1) {\\r\\n\\t\\t\\t\\t//there are line breaks, means multiline code\\r\\n\\t\\t\\t\\tpixelcode = this.properties.pixelcode;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar shader = this._shader;\\r\\n\\r\\n\\t\\tif ( !this.has_error && (!shader || this._shader_code != uvcode + \\\"|\\\" + pixelcode) ) {\\r\\n\\r\\n\\t\\t\\tvar final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE:uvcode, PIXEL_CODE:pixelcode });\\r\\n\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tshader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code );\\r\\n\\t\\t\\t\\tthis.boxcolor = \\\"#00FF00\\\";\\r\\n\\t\\t\\t} catch (err) {\\r\\n\\t\\t\\t\\t//console.log(\\\"Error compiling shader: \\\", err, final_pixel_code );\\r\\n\\t\\t\\t\\tGL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER, final_pixel_code);\\r\\n\\t\\t\\t\\tthis.boxcolor = \\\"#FF0000\\\";\\r\\n\\t\\t\\t\\tthis.has_error = true;\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis._shader = shader;\\r\\n\\t\\t\\tthis._shader_code = uvcode + \\\"|\\\" + pixelcode;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!this._shader)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar value = this.getInputData(2);\\r\\n\\t\\tif (value != null) {\\r\\n\\t\\t\\tthis.properties.value = value;\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tvalue = parseFloat(this.properties.value);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar time = this.graph.getTime();\\r\\n\\r\\n\\t\\tthis._tex.drawTo(function() {\\r\\n\\t\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\t\\tgl.disable(gl.CULL_FACE);\\r\\n\\t\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\t\\tif (tex) {\\r\\n\\t\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif (texB) {\\r\\n\\t\\t\\t\\ttexB.bind(1);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\t\\tshader\\r\\n\\t\\t\\t\\t.uniforms({\\r\\n\\t\\t\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\t\\t\\tu_textureB: 1,\\r\\n\\t\\t\\t\\t\\tvalue: value,\\r\\n\\t\\t\\t\\t\\ttexSize: [width, height],\\r\\n\\t\\t\\t\\t\\ttime: time\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t.draw(mesh);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._tex);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureOperation.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureB;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform vec2 texSize;\\\\n\\\\\\r\\n\\t\\tuniform float time;\\\\n\\\\\\r\\n\\t\\tuniform float value;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv = v_coord;\\\\n\\\\\\r\\n\\t\\t\\t{{UV_CODE}};\\\\n\\\\\\r\\n\\t\\t\\tvec4 color4 = texture2D(u_texture, uv);\\\\n\\\\\\r\\n\\t\\t\\tvec3 color = color4.rgb;\\\\n\\\\\\r\\n\\t\\t\\tvec4 color4B = texture2D(u_textureB, uv);\\\\n\\\\\\r\\n\\t\\t\\tvec3 colorB = color4B.rgb;\\\\n\\\\\\r\\n\\t\\t\\tvec3 result = color;\\\\n\\\\\\r\\n\\t\\t\\tfloat alpha = 1.0;\\\\n\\\\\\r\\n\\t\\t\\t{{PIXEL_CODE}};\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = vec4(result, alpha);\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLGraphTextureOperation.registerPreset = function ( name, code )\\r\\n\\t{\\r\\n\\t\\tLGraphTextureOperation.presets[name] = code;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"\\\",\\\"\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"bypass\\\",\\\"color\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"add\\\",\\\"color + colorB * value\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"substract\\\",\\\"(color - colorB) * value\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"mate\\\",\\\"mix( color, colorB, color4B.a * value)\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"invert\\\",\\\"vec3(1.0) - color\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"multiply\\\",\\\"color * colorB * value\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"divide\\\",\\\"(color / colorB) / value\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"difference\\\",\\\"abs(color - colorB) * value\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"max\\\",\\\"max(color, colorB) * value\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"min\\\",\\\"min(color, colorB) * value\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"displace\\\",\\\"texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"grayscale\\\",\\\"vec3(color.x + color.y + color.z) * value / 3.0\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"saturation\\\",\\\"mix( vec3(color.x + color.y + color.z) / 3.0, color, value )\\\");\\r\\n\\tLGraphTextureOperation.registerPreset(\\\"threshold\\\",\\\"vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)\\\");\\r\\n\\r\\n\\t//webglstudio stuff...\\r\\n\\tLGraphTextureOperation.prototype.onInspect = function(widgets)\\r\\n\\t{\\r\\n\\t\\tvar that = this;\\r\\n\\t\\twidgets.addCombo(\\\"Presets\\\",\\\"\\\",{ values: Object.keys(LGraphTextureOperation.presets), callback: function(v){\\r\\n\\t\\t\\tvar code = LGraphTextureOperation.presets[v];\\r\\n\\t\\t\\tif(!code)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tthat.setProperty(\\\"pixelcode\\\",code);\\r\\n\\t\\t\\tthat.title = v;\\r\\n\\t\\t\\twidgets.refresh();\\r\\n\\t\\t}});\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/operation\\\", LGraphTextureOperation);\\r\\n\\r\\n\\t//****************************************************\\r\\n\\r\\n\\tfunction LGraphTextureShader() {\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tcode: \\\"\\\",\\r\\n\\t\\t\\tu_value: 1,\\r\\n\\t\\t\\tu_color: [1,1,1,1],\\r\\n\\t\\t\\twidth: 512,\\r\\n\\t\\t\\theight: 512,\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.properties.code =\\r\\n\\t\\t\\t\\\"//time: time in seconds\\\\n//texSize: vec2 with res\\\\nuniform float u_value;\\\\nuniform vec4 u_color;\\\\n\\\\nvoid main() {\\\\n vec2 uv = v_coord;\\\\n vec3 color = vec3(0.0);\\\\n\\t//your code here\\\\n\\tcolor.xy=uv;\\\\n\\\\ngl_FragColor = vec4(color, 1.0);\\\\n}\\\\n\\\";\\r\\n\\t\\tthis._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 };\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureShader.title = \\\"Shader\\\";\\r\\n\\tLGraphTextureShader.desc = \\\"Texture shader\\\";\\r\\n\\tLGraphTextureShader.widgets_info = {\\r\\n\\t\\tcode: { type: \\\"code\\\" },\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureShader.prototype.onPropertyChanged = function(\\r\\n\\t\\tname,\\r\\n\\t\\tvalue\\r\\n\\t) {\\r\\n\\t\\tif (name != \\\"code\\\") {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar shader = this.getShader();\\r\\n\\t\\tif (!shader) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//update connections\\r\\n\\t\\tvar uniforms = shader.uniformInfo;\\r\\n\\r\\n\\t\\t//remove deprecated slots\\r\\n\\t\\tif (this.inputs) {\\r\\n\\t\\t\\tvar already = {};\\r\\n\\t\\t\\tfor (var i = 0; i < this.inputs.length; ++i) {\\r\\n\\t\\t\\t\\tvar info = this.getInputInfo(i);\\r\\n\\t\\t\\t\\tif (!info) {\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif (uniforms[info.name] && !already[info.name]) {\\r\\n\\t\\t\\t\\t\\talready[info.name] = true;\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tthis.removeInput(i);\\r\\n\\t\\t\\t\\ti--;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//update existing ones\\r\\n\\t\\tfor (var i in uniforms) {\\r\\n\\t\\t\\tvar info = shader.uniformInfo[i];\\r\\n\\t\\t\\tif (info.loc === null) {\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t} //is an attribute, not a uniform\\r\\n\\t\\t\\tif (i == \\\"time\\\") {\\r\\n\\t\\t\\t\\t//default one\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar type = \\\"number\\\";\\r\\n\\t\\t\\tif (this._shader.samplers[i]) {\\r\\n\\t\\t\\t\\ttype = \\\"texture\\\";\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tswitch (info.size) {\\r\\n\\t\\t\\t\\t\\tcase 1:\\r\\n\\t\\t\\t\\t\\t\\ttype = \\\"number\\\";\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase 2:\\r\\n\\t\\t\\t\\t\\t\\ttype = \\\"vec2\\\";\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase 3:\\r\\n\\t\\t\\t\\t\\t\\ttype = \\\"vec3\\\";\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase 4:\\r\\n\\t\\t\\t\\t\\t\\ttype = \\\"vec4\\\";\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase 9:\\r\\n\\t\\t\\t\\t\\t\\ttype = \\\"mat3\\\";\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase 16:\\r\\n\\t\\t\\t\\t\\t\\ttype = \\\"mat4\\\";\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar slot = this.findInputSlot(i);\\r\\n\\t\\t\\tif (slot == -1) {\\r\\n\\t\\t\\t\\tthis.addInput(i, type);\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar input_info = this.getInputInfo(slot);\\r\\n\\t\\t\\tif (!input_info) {\\r\\n\\t\\t\\t\\tthis.addInput(i, type);\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tif (input_info.type == type) {\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tthis.removeInput(slot, type);\\r\\n\\t\\t\\t\\tthis.addInput(i, type);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureShader.prototype.getShader = function() {\\r\\n\\t\\t//replug\\r\\n\\t\\tif (this._shader && this._shader_code == this.properties.code) {\\r\\n\\t\\t\\treturn this._shader;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._shader_code = this.properties.code;\\r\\n\\t\\tthis._shader = new GL.Shader(\\r\\n\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\tLGraphTextureShader.pixel_shader + this.properties.code\\r\\n\\t\\t);\\r\\n\\t\\tif (!this._shader) {\\r\\n\\t\\t\\tthis.boxcolor = \\\"red\\\";\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tthis.boxcolor = \\\"green\\\";\\r\\n\\t\\t}\\r\\n\\t\\treturn this._shader;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureShader.prototype.onExecute = function() {\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tvar shader = this.getShader();\\r\\n\\t\\tif (!shader) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar tex_slot = 0;\\r\\n\\t\\tvar in_tex = null;\\r\\n\\r\\n\\t\\t//set uniforms\\r\\n\\t\\tif(this.inputs)\\r\\n\\t\\tfor (var i = 0; i < this.inputs.length; ++i) {\\r\\n\\t\\t\\tvar info = this.getInputInfo(i);\\r\\n\\t\\t\\tvar data = this.getInputData(i);\\r\\n\\t\\t\\tif (data == null) {\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif (data.constructor === GL.Texture) {\\r\\n\\t\\t\\t\\tdata.bind(tex_slot);\\r\\n\\t\\t\\t\\tif (!in_tex) {\\r\\n\\t\\t\\t\\t\\tin_tex = data;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tdata = tex_slot;\\r\\n\\t\\t\\t\\ttex_slot++;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tshader.setUniform(info.name, data); //data is tex_slot\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\t\\tvar type = LGraphTexture.getTextureType( this.properties.precision, in_tex );\\r\\n\\r\\n\\t\\t//render to texture\\r\\n\\t\\tvar w = this.properties.width | 0;\\r\\n\\t\\tvar h = this.properties.height | 0;\\r\\n\\t\\tif (w == 0) {\\r\\n\\t\\t\\tw = in_tex ? in_tex.width : gl.canvas.width;\\r\\n\\t\\t}\\r\\n\\t\\tif (h == 0) {\\r\\n\\t\\t\\th = in_tex ? in_tex.height : gl.canvas.height;\\r\\n\\t\\t}\\r\\n\\t\\tuniforms.texSize[0] = w;\\r\\n\\t\\tuniforms.texSize[1] = h;\\r\\n\\t\\tuniforms.time = this.graph.getTime();\\r\\n\\t\\tuniforms.u_value = this.properties.u_value;\\r\\n\\t\\tuniforms.u_color.set( this.properties.u_color );\\r\\n\\r\\n\\t\\tif ( !this._tex || this._tex.type != type || this._tex.width != w || this._tex.height != h ) {\\r\\n\\t\\t\\tthis._tex = new GL.Texture(w, h, { type: type, format: gl.RGBA, filter: gl.LINEAR });\\r\\n\\t\\t}\\r\\n\\t\\tvar tex = this._tex;\\r\\n\\t\\ttex.drawTo(function() {\\r\\n\\t\\t\\tshader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad());\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._tex);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureShader.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform float time;\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/shader\\\", LGraphTextureShader);\\r\\n\\r\\n\\t// Texture Scale Offset\\r\\n\\r\\n\\tfunction LGraphTextureScaleOffset() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"scale\\\", \\\"vec2\\\");\\r\\n\\t\\tthis.addInput(\\\"offset\\\", \\\"vec2\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\toffset: vec2.fromValues(0, 0),\\r\\n\\t\\t\\tscale: vec2.fromValues(1, 1),\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureScaleOffset.widgets_info = {\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureScaleOffset.title = \\\"Scale/Offset\\\";\\r\\n\\tLGraphTextureScaleOffset.desc = \\\"Applies an scaling and offseting\\\";\\r\\n\\r\\n\\tLGraphTextureScaleOffset.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0) || !tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tif (this.properties.precision === LGraphTexture.PASS_THROUGH) {\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar width = tex.width;\\r\\n\\t\\tvar height = tex.height;\\r\\n\\t\\tvar type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT;\\r\\n\\t\\tif (this.precision === LGraphTexture.DEFAULT) {\\r\\n\\t\\t\\ttype = tex.type;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (\\r\\n\\t\\t\\t!this._tex ||\\r\\n\\t\\t\\tthis._tex.width != width ||\\r\\n\\t\\t\\tthis._tex.height != height ||\\r\\n\\t\\t\\tthis._tex.type != type\\r\\n\\t\\t) {\\r\\n\\t\\t\\tthis._tex = new GL.Texture(width, height, {\\r\\n\\t\\t\\t\\ttype: type,\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar shader = this._shader;\\r\\n\\r\\n\\t\\tif (!shader) {\\r\\n\\t\\t\\tshader = new GL.Shader(\\r\\n\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureScaleOffset.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar scale = this.getInputData(1);\\r\\n\\t\\tif (scale) {\\r\\n\\t\\t\\tthis.properties.scale[0] = scale[0];\\r\\n\\t\\t\\tthis.properties.scale[1] = scale[1];\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tscale = this.properties.scale;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar offset = this.getInputData(2);\\r\\n\\t\\tif (offset) {\\r\\n\\t\\t\\tthis.properties.offset[0] = offset[0];\\r\\n\\t\\t\\tthis.properties.offset[1] = offset[1];\\r\\n\\t\\t} else {\\r\\n\\t\\t\\toffset = this.properties.offset;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._tex.drawTo(function() {\\r\\n\\t\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\t\\tgl.disable(gl.CULL_FACE);\\r\\n\\t\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\t\\tshader\\r\\n\\t\\t\\t\\t.uniforms({\\r\\n\\t\\t\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\t\\t\\tu_scale: scale,\\r\\n\\t\\t\\t\\t\\tu_offset: offset\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t.draw(mesh);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._tex);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureScaleOffset.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureB;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_scale;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_offset;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv = v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuv = uv / u_scale - u_offset;\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = texture2D(u_texture, uv);\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\r\\n\\t\\t\\\"texture/scaleOffset\\\",\\r\\n\\t\\tLGraphTextureScaleOffset\\r\\n\\t);\\r\\n\\r\\n\\t// Warp (distort a texture) *************************\\r\\n\\r\\n\\tfunction LGraphTextureWarp() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"warp\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"factor\\\", \\\"number\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tfactor: 0.01,\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureWarp.widgets_info = {\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureWarp.title = \\\"Warp\\\";\\r\\n\\tLGraphTextureWarp.desc = \\\"Texture warp operation\\\";\\r\\n\\r\\n\\tLGraphTextureWarp.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tif (this.properties.precision === LGraphTexture.PASS_THROUGH) {\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar texB = this.getInputData(1);\\r\\n\\r\\n\\t\\tvar width = 512;\\r\\n\\t\\tvar height = 512;\\r\\n\\t\\tvar type = gl.UNSIGNED_BYTE;\\r\\n\\t\\tif (tex) {\\r\\n\\t\\t\\twidth = tex.width;\\r\\n\\t\\t\\theight = tex.height;\\r\\n\\t\\t\\ttype = tex.type;\\r\\n\\t\\t} else if (texB) {\\r\\n\\t\\t\\twidth = texB.width;\\r\\n\\t\\t\\theight = texB.height;\\r\\n\\t\\t\\ttype = texB.type;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!tex && !this._tex) {\\r\\n\\t\\t\\tthis._tex = new GL.Texture(width, height, {\\r\\n\\t\\t\\t\\ttype:\\r\\n\\t\\t\\t\\t\\tthis.precision === LGraphTexture.LOW\\r\\n\\t\\t\\t\\t\\t\\t? gl.UNSIGNED_BYTE\\r\\n\\t\\t\\t\\t\\t\\t: gl.HIGH_PRECISION_FORMAT,\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t});\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tthis._tex = LGraphTexture.getTargetTexture(\\r\\n\\t\\t\\t\\ttex || this._tex,\\r\\n\\t\\t\\t\\tthis._tex,\\r\\n\\t\\t\\t\\tthis.properties.precision\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar shader = this._shader;\\r\\n\\r\\n\\t\\tif (!shader) {\\r\\n\\t\\t\\tshader = new GL.Shader(\\r\\n\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureWarp.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar factor = this.getInputData(2);\\r\\n\\t\\tif (factor != null) {\\r\\n\\t\\t\\tthis.properties.factor = factor;\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tfactor = parseFloat(this.properties.factor);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._tex.drawTo(function() {\\r\\n\\t\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\t\\tgl.disable(gl.CULL_FACE);\\r\\n\\t\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\t\\tif (tex) {\\r\\n\\t\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif (texB) {\\r\\n\\t\\t\\t\\ttexB.bind(1);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\t\\tshader\\r\\n\\t\\t\\t\\t.uniforms({ u_texture: 0, u_textureB: 1, u_factor: factor })\\r\\n\\t\\t\\t\\t.draw(mesh);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._tex);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureWarp.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureB;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform float u_factor;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv = v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor;\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = texture2D(u_texture, uv);\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/warp\\\", LGraphTextureWarp);\\r\\n\\r\\n\\t//****************************************************\\r\\n\\r\\n\\t// Texture to Viewport *****************************************\\r\\n\\tfunction LGraphTextureToViewport() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tadditive: false,\\r\\n\\t\\t\\tantialiasing: false,\\r\\n\\t\\t\\tfilter: true,\\r\\n\\t\\t\\tdisable_alpha: false,\\r\\n\\t\\t\\tgamma: 1.0\\r\\n\\t\\t};\\r\\n\\t\\tthis.size[0] = 130;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureToViewport.title = \\\"to Viewport\\\";\\r\\n\\tLGraphTextureToViewport.desc = \\\"Texture to viewport\\\";\\r\\n\\r\\n\\tLGraphTextureToViewport.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (this.properties.disable_alpha) {\\r\\n\\t\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tgl.enable(gl.BLEND);\\r\\n\\t\\t\\tif (this.properties.additive) {\\r\\n\\t\\t\\t\\tgl.blendFunc(gl.SRC_ALPHA, gl.ONE);\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tgl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\tvar gamma = this.properties.gamma || 1.0;\\r\\n\\t\\tif (this.isInputConnected(1)) {\\r\\n\\t\\t\\tgamma = this.getInputData(1);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\ttex.setParameter(\\r\\n\\t\\t\\tgl.TEXTURE_MAG_FILTER,\\r\\n\\t\\t\\tthis.properties.filter ? gl.LINEAR : gl.NEAREST\\r\\n\\t\\t);\\r\\n\\r\\n\\t\\tif (this.properties.antialiasing) {\\r\\n\\t\\t\\tif (!LGraphTextureToViewport._shader) {\\r\\n\\t\\t\\t\\tLGraphTextureToViewport._shader = new GL.Shader(\\r\\n\\t\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\t\\tLGraphTextureToViewport.aa_pixel_shader\\r\\n\\t\\t\\t\\t);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar viewport = gl.getViewport(); //gl.getParameter(gl.VIEWPORT);\\r\\n\\t\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tLGraphTextureToViewport._shader\\r\\n\\t\\t\\t\\t.uniforms({\\r\\n\\t\\t\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\t\\t\\tuViewportSize: [tex.width, tex.height],\\r\\n\\t\\t\\t\\t\\tu_igamma: 1 / gamma,\\r\\n\\t\\t\\t\\t\\tinverseVP: [1 / tex.width, 1 / tex.height]\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t.draw(mesh);\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tif (gamma != 1.0) {\\r\\n\\t\\t\\t\\tif (!LGraphTextureToViewport._gamma_shader) {\\r\\n\\t\\t\\t\\t\\tLGraphTextureToViewport._gamma_shader = new GL.Shader(\\r\\n\\t\\t\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\t\\t\\tLGraphTextureToViewport.gamma_pixel_shader\\r\\n\\t\\t\\t\\t\\t);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\ttex.toViewport(LGraphTextureToViewport._gamma_shader, {\\r\\n\\t\\t\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\t\\t\\tu_igamma: 1 / gamma\\r\\n\\t\\t\\t\\t});\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\ttex.toViewport();\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureToViewport.prototype.onGetInputs = function() {\\r\\n\\t\\treturn [[\\\"gamma\\\", \\\"number\\\"]];\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureToViewport.aa_pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform vec2 uViewportSize;\\\\n\\\\\\r\\n\\t\\tuniform vec2 inverseVP;\\\\n\\\\\\r\\n\\t\\tuniform float u_igamma;\\\\n\\\\\\r\\n\\t\\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\\\\n\\\\\\r\\n\\t\\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\\\\n\\\\\\r\\n\\t\\t#define FXAA_SPAN_MAX 8.0\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\\\\n\\\\\\r\\n\\t\\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t\\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\\\\n\\\\\\r\\n\\t\\t\\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\\\\n\\\\\\r\\n\\t\\t\\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\\\\n\\\\\\r\\n\\t\\t\\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\\\\n\\\\\\r\\n\\t\\t\\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\\\\n\\\\\\r\\n\\t\\t\\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\\\\n\\\\\\r\\n\\t\\t\\tvec3 luma = vec3(0.299, 0.587, 0.114);\\\\n\\\\\\r\\n\\t\\t\\tfloat lumaNW = dot(rgbNW, luma);\\\\n\\\\\\r\\n\\t\\t\\tfloat lumaNE = dot(rgbNE, luma);\\\\n\\\\\\r\\n\\t\\t\\tfloat lumaSW = dot(rgbSW, luma);\\\\n\\\\\\r\\n\\t\\t\\tfloat lumaSE = dot(rgbSE, luma);\\\\n\\\\\\r\\n\\t\\t\\tfloat lumaM = dot(rgbM, luma);\\\\n\\\\\\r\\n\\t\\t\\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\\\\n\\\\\\r\\n\\t\\t\\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvec2 dir;\\\\n\\\\\\r\\n\\t\\t\\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\\\\n\\\\\\r\\n\\t\\t\\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\\\\n\\\\\\r\\n\\t\\t\\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \\\\n\\\\\\r\\n\\t\\t\\t\\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\\\\n\\\\\\r\\n\\t\\t\\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \\\\n\\\\\\r\\n\\t\\t\\t\\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t//return vec4(rgbA,1.0);\\\\n\\\\\\r\\n\\t\\t\\tfloat lumaB = dot(rgbB, luma);\\\\n\\\\\\r\\n\\t\\t\\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor = vec4(rgbA, 1.0);\\\\n\\\\\\r\\n\\t\\t\\telse\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor = vec4(rgbB, 1.0);\\\\n\\\\\\r\\n\\t\\t\\tif(u_igamma != 1.0)\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\\\\n\\\\\\r\\n\\t\\t\\treturn color;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLGraphTextureToViewport.gamma_pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform float u_igamma;\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = texture2D( u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\\\\n\\\\\\r\\n\\t\\t gl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\r\\n\\t\\t\\\"texture/toviewport\\\",\\r\\n\\t\\tLGraphTextureToViewport\\r\\n\\t);\\r\\n\\r\\n\\t// Texture Copy *****************************************\\r\\n\\tfunction LGraphTextureCopy() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tsize: 0,\\r\\n\\t\\t\\tgenerate_mipmaps: false,\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureCopy.title = \\\"Copy\\\";\\r\\n\\tLGraphTextureCopy.desc = \\\"Copy Texture\\\";\\r\\n\\tLGraphTextureCopy.widgets_info = {\\r\\n\\t\\tsize: {\\r\\n\\t\\t\\twidget: \\\"combo\\\",\\r\\n\\t\\t\\tvalues: [0, 32, 64, 128, 256, 512, 1024, 2048]\\r\\n\\t\\t},\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureCopy.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex && !this._temp_texture) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\t//copy the texture\\r\\n\\t\\tif (tex) {\\r\\n\\t\\t\\tvar width = tex.width;\\r\\n\\t\\t\\tvar height = tex.height;\\r\\n\\r\\n\\t\\t\\tif (this.properties.size != 0) {\\r\\n\\t\\t\\t\\twidth = this.properties.size;\\r\\n\\t\\t\\t\\theight = this.properties.size;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar temp = this._temp_texture;\\r\\n\\r\\n\\t\\t\\tvar type = tex.type;\\r\\n\\t\\t\\tif (this.properties.precision === LGraphTexture.LOW) {\\r\\n\\t\\t\\t\\ttype = gl.UNSIGNED_BYTE;\\r\\n\\t\\t\\t} else if (this.properties.precision === LGraphTexture.HIGH) {\\r\\n\\t\\t\\t\\ttype = gl.HIGH_PRECISION_FORMAT;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif (\\r\\n\\t\\t\\t\\t!temp ||\\r\\n\\t\\t\\t\\ttemp.width != width ||\\r\\n\\t\\t\\t\\ttemp.height != height ||\\r\\n\\t\\t\\t\\ttemp.type != type\\r\\n\\t\\t\\t) {\\r\\n\\t\\t\\t\\tvar minFilter = gl.LINEAR;\\r\\n\\t\\t\\t\\tif (\\r\\n\\t\\t\\t\\t\\tthis.properties.generate_mipmaps &&\\r\\n\\t\\t\\t\\t\\tisPowerOfTwo(width) &&\\r\\n\\t\\t\\t\\t\\tisPowerOfTwo(height)\\r\\n\\t\\t\\t\\t) {\\r\\n\\t\\t\\t\\t\\tminFilter = gl.LINEAR_MIPMAP_LINEAR;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tthis._temp_texture = new GL.Texture(width, height, {\\r\\n\\t\\t\\t\\t\\ttype: type,\\r\\n\\t\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\t\\tminFilter: minFilter,\\r\\n\\t\\t\\t\\t\\tmagFilter: gl.LINEAR\\r\\n\\t\\t\\t\\t});\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\ttex.copyTo(this._temp_texture);\\r\\n\\r\\n\\t\\t\\tif (this.properties.generate_mipmaps) {\\r\\n\\t\\t\\t\\tthis._temp_texture.bind(0);\\r\\n\\t\\t\\t\\tgl.generateMipmap(this._temp_texture.texture_type);\\r\\n\\t\\t\\t\\tthis._temp_texture.unbind(0);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._temp_texture);\\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/copy\\\", LGraphTextureCopy);\\r\\n\\r\\n\\t// Texture Downsample *****************************************\\r\\n\\tfunction LGraphTextureDownsample() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\titerations: 1,\\r\\n\\t\\t\\tgenerate_mipmaps: false,\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureDownsample.title = \\\"Downsample\\\";\\r\\n\\tLGraphTextureDownsample.desc = \\\"Downsample Texture\\\";\\r\\n\\tLGraphTextureDownsample.widgets_info = {\\r\\n\\t\\titerations: { type: \\\"number\\\", step: 1, precision: 0, min: 0 },\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureDownsample.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex && !this._temp_texture) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\t//we do not allow any texture different than texture 2D\\r\\n\\t\\tif (!tex || tex.texture_type !== GL.TEXTURE_2D) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (this.properties.iterations < 1) {\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar shader = LGraphTextureDownsample._shader;\\r\\n\\t\\tif (!shader) {\\r\\n\\t\\t\\tLGraphTextureDownsample._shader = shader = new GL.Shader(\\r\\n\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureDownsample.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar width = tex.width | 0;\\r\\n\\t\\tvar height = tex.height | 0;\\r\\n\\t\\tvar type = tex.type;\\r\\n\\t\\tif (this.properties.precision === LGraphTexture.LOW) {\\r\\n\\t\\t\\ttype = gl.UNSIGNED_BYTE;\\r\\n\\t\\t} else if (this.properties.precision === LGraphTexture.HIGH) {\\r\\n\\t\\t\\ttype = gl.HIGH_PRECISION_FORMAT;\\r\\n\\t\\t}\\r\\n\\t\\tvar iterations = this.properties.iterations || 1;\\r\\n\\r\\n\\t\\tvar origin = tex;\\r\\n\\t\\tvar target = null;\\r\\n\\r\\n\\t\\tvar temp = [];\\r\\n\\t\\tvar options = {\\r\\n\\t\\t\\ttype: type,\\r\\n\\t\\t\\tformat: tex.format\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tvar offset = vec2.create();\\r\\n\\t\\tvar uniforms = {\\r\\n\\t\\t\\tu_offset: offset\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tif (this._texture) {\\r\\n\\t\\t\\tGL.Texture.releaseTemporary(this._texture);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor (var i = 0; i < iterations; ++i) {\\r\\n\\t\\t\\toffset[0] = 1 / width;\\r\\n\\t\\t\\toffset[1] = 1 / height;\\r\\n\\t\\t\\twidth = width >> 1 || 0;\\r\\n\\t\\t\\theight = height >> 1 || 0;\\r\\n\\t\\t\\ttarget = GL.Texture.getTemporary(width, height, options);\\r\\n\\t\\t\\ttemp.push(target);\\r\\n\\t\\t\\torigin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST);\\r\\n\\t\\t\\torigin.copyTo(target, shader, uniforms);\\r\\n\\t\\t\\tif (width == 1 && height == 1) {\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t} //nothing else to do\\r\\n\\t\\t\\torigin = target;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//keep the last texture used\\r\\n\\t\\tthis._texture = temp.pop();\\r\\n\\r\\n\\t\\t//free the rest\\r\\n\\t\\tfor (var i = 0; i < temp.length; ++i) {\\r\\n\\t\\t\\tGL.Texture.releaseTemporary(temp[i]);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (this.properties.generate_mipmaps) {\\r\\n\\t\\t\\tthis._texture.bind(0);\\r\\n\\t\\t\\tgl.generateMipmap(this._texture.texture_type);\\r\\n\\t\\t\\tthis._texture.unbind(0);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._texture);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureDownsample.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_offset;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = texture2D(u_texture, v_coord );\\\\n\\\\\\r\\n\\t\\t\\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\\\\n\\\\\\r\\n\\t\\t\\tcolor += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\\\\n\\\\\\r\\n\\t\\t\\tcolor += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\\\\n\\\\\\r\\n\\t\\t gl_FragColor = color * 0.25;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\r\\n\\t\\t\\\"texture/downsample\\\",\\r\\n\\t\\tLGraphTextureDownsample\\r\\n\\t);\\r\\n\\r\\n\\t// Texture Average *****************************************\\r\\n\\tfunction LGraphTextureAverage() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"tex\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"avg\\\", \\\"vec4\\\");\\r\\n\\t\\tthis.addOutput(\\\"lum\\\", \\\"number\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tuse_previous_frame: true, //to avoid stalls \\r\\n\\t\\t\\thigh_quality: false //to use as much pixels as possible\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis._uniforms = {\\r\\n\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\tu_mipmap_offset: 0\\r\\n\\t\\t};\\r\\n\\t\\tthis._luminance = new Float32Array(4);\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureAverage.title = \\\"Average\\\";\\r\\n\\tLGraphTextureAverage.desc =\\r\\n\\t\\t\\\"Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\\\\n If high_quality is true, then it generates the mipmaps first and reads from the lower one.\\\";\\r\\n\\r\\n\\tLGraphTextureAverage.prototype.onExecute = function() {\\r\\n\\t\\tif (!this.properties.use_previous_frame) {\\r\\n\\t\\t\\tthis.updateAverage();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar v = this._luminance;\\r\\n\\t\\tthis.setOutputData(0, this._temp_texture);\\r\\n\\t\\tthis.setOutputData(1, v);\\r\\n\\t\\tthis.setOutputData(2, (v[0] + v[1] + v[2]) / 3);\\r\\n\\t};\\r\\n\\r\\n\\t//executed before rendering the frame\\r\\n\\tLGraphTextureAverage.prototype.onPreRenderExecute = function() {\\r\\n\\t\\tthis.updateAverage();\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureAverage.prototype.updateAverage = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (\\r\\n\\t\\t\\t!this.isOutputConnected(0) &&\\r\\n\\t\\t\\t!this.isOutputConnected(1) &&\\r\\n\\t\\t\\t!this.isOutputConnected(2)\\r\\n\\t\\t) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tif (!LGraphTextureAverage._shader) {\\r\\n\\t\\t\\tLGraphTextureAverage._shader = new GL.Shader(\\r\\n\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureAverage.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t\\t//creates 256 random numbers and stores them in two mat4\\r\\n\\t\\t\\tvar samples = new Float32Array(16);\\r\\n\\t\\t\\tfor (var i = 0; i < samples.length; ++i) {\\r\\n\\t\\t\\t\\tsamples[i] = Math.random(); //poorly distributed samples\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t//upload only once\\r\\n\\t\\t\\tLGraphTextureAverage._shader.uniforms({\\r\\n\\t\\t\\t\\tu_samples_a: samples.subarray(0, 16),\\r\\n\\t\\t\\t\\tu_samples_b: samples.subarray(16, 32)\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar temp = this._temp_texture;\\r\\n\\t\\tvar type = gl.UNSIGNED_BYTE;\\r\\n\\t\\tif (tex.type != type) {\\r\\n\\t\\t\\t//force floats, half floats cannot be read with gl.readPixels\\r\\n\\t\\t\\ttype = gl.FLOAT;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!temp || temp.type != type) {\\r\\n\\t\\t\\tthis._temp_texture = new GL.Texture(1, 1, {\\r\\n\\t\\t\\t\\ttype: type,\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.NEAREST\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._uniforms.u_mipmap_offset = 0;\\r\\n\\r\\n\\t\\tif(this.properties.high_quality)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( !this._temp_pot2_texture || this._temp_pot2_texture.type != type )\\r\\n\\t\\t\\t\\tthis._temp_pot2_texture = new GL.Texture(512, 512, {\\r\\n\\t\\t\\t\\t\\ttype: type,\\r\\n\\t\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\t\\tminFilter: gl.LINEAR_MIPMAP_LINEAR,\\r\\n\\t\\t\\t\\t\\tmagFilter: gl.LINEAR\\r\\n\\t\\t\\t\\t});\\r\\n\\r\\n\\t\\t\\ttex.copyTo( this._temp_pot2_texture );\\r\\n\\t\\t\\ttex = this._temp_pot2_texture;\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tgl.generateMipmap(GL.TEXTURE_2D);\\r\\n\\t\\t\\tthis._uniforms.u_mipmap_offset = 9;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar shader = LGraphTextureAverage._shader;\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\t\\tuniforms.u_mipmap_offset = this.properties.mipmap_offset;\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\tthis._temp_texture.drawTo(function() {\\r\\n\\t\\t\\ttex.toViewport(shader, uniforms);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tif (this.isOutputConnected(1) || this.isOutputConnected(2)) {\\r\\n\\t\\t\\tvar pixel = this._temp_texture.getPixels();\\r\\n\\t\\t\\tif (pixel) {\\r\\n\\t\\t\\t\\tvar v = this._luminance;\\r\\n\\t\\t\\t\\tvar type = this._temp_texture.type;\\r\\n\\t\\t\\t\\tv.set(pixel);\\r\\n\\t\\t\\t\\tif (type == gl.UNSIGNED_BYTE) {\\r\\n\\t\\t\\t\\t\\tvec4.scale(v, v, 1 / 255);\\r\\n\\t\\t\\t\\t} else if (\\r\\n\\t\\t\\t\\t\\ttype == GL.HALF_FLOAT ||\\r\\n\\t\\t\\t\\t\\ttype == GL.HALF_FLOAT_OES\\r\\n\\t\\t\\t\\t) {\\r\\n\\t\\t\\t\\t\\t//no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureAverage.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_samples_a;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_samples_b;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform float u_mipmap_offset;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t\\t//random average\\\\n\\\\\\r\\n\\t\\t\\tfor(int i = 0; i < 4; ++i)\\\\n\\\\\\r\\n\\t\\t\\t\\tfor(int j = 0; j < 4; ++j)\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t gl_FragColor = color * 0.03125;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/average\\\", LGraphTextureAverage);\\r\\n\\r\\n\\r\\n\\r\\n\\t// Computes operation between pixels (max, min) *****************************************\\r\\n\\tfunction LGraphTextureMinMax() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"min_t\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"max_t\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"min\\\", \\\"vec4\\\");\\r\\n\\t\\tthis.addOutput(\\\"max\\\", \\\"vec4\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tmode: \\\"max\\\",\\r\\n\\t\\t\\tuse_previous_frame: true //to avoid stalls \\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis._uniforms = {\\r\\n\\t\\t\\tu_texture: 0\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis._max = new Float32Array(4);\\r\\n\\t\\tthis._min = new Float32Array(4);\\r\\n\\r\\n\\t\\tthis._textures_chain = [];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureMinMax.widgets_info = {\\r\\n\\t\\tmode: { widget: \\\"combo\\\", values: [\\\"min\\\",\\\"max\\\",\\\"avg\\\"] }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureMinMax.title = \\\"MinMax\\\";\\r\\n\\tLGraphTextureMinMax.desc = \\\"Compute the scene min max\\\";\\r\\n\\r\\n\\tLGraphTextureMinMax.prototype.onExecute = function() {\\r\\n\\t\\tif (!this.properties.use_previous_frame) {\\r\\n\\t\\t\\tthis.update();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._temp_texture);\\r\\n\\t\\tthis.setOutputData(1, this._luminance);\\r\\n\\t};\\r\\n\\r\\n\\t//executed before rendering the frame\\r\\n\\tLGraphTextureMinMax.prototype.onPreRenderExecute = function() {\\r\\n\\t\\tthis.update();\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureMinMax.prototype.update = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif ( !this.isOutputConnected(0) && !this.isOutputConnected(1) ) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tif (!LGraphTextureMinMax._shader) {\\r\\n\\t\\t\\tLGraphTextureMinMax._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureMinMax.pixel_shader );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar temp = this._temp_texture;\\r\\n\\t\\tvar type = gl.UNSIGNED_BYTE;\\r\\n\\t\\tif (tex.type != type) {\\r\\n\\t\\t\\t//force floats, half floats cannot be read with gl.readPixels\\r\\n\\t\\t\\ttype = gl.FLOAT;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar size = 512;\\r\\n\\r\\n\\t\\tif( !this._textures_chain.length || this._textures_chain[0].type != type )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar index = 0;\\r\\n\\t\\t\\twhile(i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._textures_chain[i] = new GL.Texture( size, size, {\\r\\n\\t\\t\\t\\t\\ttype: type,\\r\\n\\t\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\t\\tfilter: gl.NEAREST\\r\\n\\t\\t\\t\\t});\\r\\n\\t\\t\\t\\tsize = size >> 2;\\r\\n\\t\\t\\t\\ti++;\\r\\n\\t\\t\\t\\tif(size == 1)\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\ttex.copyTo( this._textures_chain[0] );\\r\\n\\t\\tvar prev = this._textures_chain[0];\\r\\n\\t\\tfor(var i = 1; i <= this._textures_chain.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar tex = this._textures_chain[i];\\r\\n\\r\\n\\t\\t\\tprev = tex;\\t\\t\\t\\t\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar shader = LGraphTextureMinMax._shader;\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\t\\tuniforms.u_mipmap_offset = this.properties.mipmap_offset;\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\tthis._temp_texture.drawTo(function() {\\r\\n\\t\\t\\ttex.toViewport(shader, uniforms);\\r\\n\\t\\t});\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureMinMax.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_samples_a;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_samples_b;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform float u_mipmap_offset;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t\\t//random average\\\\n\\\\\\r\\n\\t\\t\\tfor(int i = 0; i < 4; ++i)\\\\n\\\\\\r\\n\\t\\t\\t\\tfor(int j = 0; j < 4; ++j)\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tcolor += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tcolor += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t gl_FragColor = color * 0.03125;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\t//LiteGraph.registerNodeType(\\\"texture/clustered_operation\\\", LGraphTextureClusteredOperation);\\r\\n\\r\\n\\r\\n\\tfunction LGraphTextureTemporalSmooth() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"factor\\\", \\\"Number\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = { factor: 0.5 };\\r\\n\\t\\tthis._uniforms = {\\r\\n\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\tu_textureB: 1,\\r\\n\\t\\t\\tu_factor: this.properties.factor\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureTemporalSmooth.title = \\\"Smooth\\\";\\r\\n\\tLGraphTextureTemporalSmooth.desc = \\\"Smooth texture over time\\\";\\r\\n\\r\\n\\tLGraphTextureTemporalSmooth.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex || !this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!LGraphTextureTemporalSmooth._shader) {\\r\\n\\t\\t\\tLGraphTextureTemporalSmooth._shader = new GL.Shader(\\r\\n\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureTemporalSmooth.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar temp = this._temp_texture;\\r\\n\\t\\tif (\\r\\n\\t\\t\\t!temp ||\\r\\n\\t\\t\\ttemp.type != tex.type ||\\r\\n\\t\\t\\ttemp.width != tex.width ||\\r\\n\\t\\t\\ttemp.height != tex.height\\r\\n\\t\\t) {\\r\\n\\t\\t\\tvar options = {\\r\\n\\t\\t\\t\\ttype: tex.type,\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.NEAREST\\r\\n\\t\\t\\t};\\r\\n\\t\\t\\tthis._temp_texture = new GL.Texture(tex.width, tex.height, options );\\r\\n\\t\\t\\tthis._temp_texture2 = new GL.Texture(tex.width, tex.height, options );\\r\\n\\t\\t\\ttex.copyTo(this._temp_texture2);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar tempA = this._temp_texture;\\r\\n\\t\\tvar tempB = this._temp_texture2;\\r\\n\\r\\n\\t\\tvar shader = LGraphTextureTemporalSmooth._shader;\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\t\\tuniforms.u_factor = 1.0 - this.getInputOrProperty(\\\"factor\\\");\\r\\n\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\ttempA.drawTo(function() {\\r\\n\\t\\t\\ttempB.bind(1);\\r\\n\\t\\t\\ttex.toViewport(shader, uniforms);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, tempA);\\r\\n\\r\\n\\t\\t//swap\\r\\n\\t\\tthis._temp_texture = tempB;\\r\\n\\t\\tthis._temp_texture2 = tempA;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureTemporalSmooth.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureB;\\\\n\\\\\\r\\n\\t\\tuniform float u_factor;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType( \\\"texture/temporal_smooth\\\", LGraphTextureTemporalSmooth );\\r\\n\\r\\n\\r\\n\\tfunction LGraphTextureLinearAvgSmooth() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"avg\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"array\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = { samples: 64, frames_interval: 1 };\\r\\n\\t\\tthis._uniforms = {\\r\\n\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\tu_textureB: 1,\\r\\n\\t\\t\\tu_samples: this.properties.samples,\\r\\n\\t\\t\\tu_isamples: 1/this.properties.samples\\r\\n\\t\\t};\\r\\n\\t\\tthis.frame = 0;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureLinearAvgSmooth.title = \\\"Lineal Avg Smooth\\\";\\r\\n\\tLGraphTextureLinearAvgSmooth.desc = \\\"Smooth texture linearly over time\\\";\\r\\n\\r\\n\\tLGraphTextureLinearAvgSmooth[\\\"@samples\\\"] = { type: \\\"number\\\", min: 1, max: 64, step: 1, precision: 1 };\\r\\n\\r\\n\\tLGraphTextureLinearAvgSmooth.prototype.getPreviewTexture = function()\\r\\n\\t{\\r\\n\\t\\treturn this._temp_texture2;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureLinearAvgSmooth.prototype.onExecute = function() {\\r\\n\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex || !this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!LGraphTextureLinearAvgSmooth._shader) {\\r\\n\\t\\t\\tLGraphTextureLinearAvgSmooth._shader_copy = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_copy );\\r\\n\\t\\t\\tLGraphTextureLinearAvgSmooth._shader_avg = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_avg );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar samples = Math.clamp(this.properties.samples,0,64);\\r\\n\\t\\tvar frame = this.frame;\\r\\n\\t\\tvar interval = this.properties.frames_interval;\\r\\n\\r\\n\\t\\tif( interval == 0 || frame % interval == 0 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar temp = this._temp_texture;\\r\\n\\t\\t\\tif ( !temp || temp.type != tex.type || temp.width != samples ) {\\r\\n\\t\\t\\t\\tvar options = {\\r\\n\\t\\t\\t\\t\\ttype: tex.type,\\r\\n\\t\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\t\\tfilter: gl.NEAREST\\r\\n\\t\\t\\t\\t};\\r\\n\\t\\t\\t\\tthis._temp_texture = new GL.Texture( samples, 1, options );\\r\\n\\t\\t\\t\\tthis._temp_texture2 = new GL.Texture( samples, 1, options );\\r\\n\\t\\t\\t\\tthis._temp_texture_out = new GL.Texture( 1, 1, options );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar tempA = this._temp_texture;\\r\\n\\t\\t\\tvar tempB = this._temp_texture2;\\r\\n\\r\\n\\t\\t\\tvar shader_copy = LGraphTextureLinearAvgSmooth._shader_copy;\\r\\n\\t\\t\\tvar shader_avg = LGraphTextureLinearAvgSmooth._shader_avg;\\r\\n\\t\\t\\tvar uniforms = this._uniforms;\\r\\n\\t\\t\\tuniforms.u_samples = samples;\\r\\n\\t\\t\\tuniforms.u_isamples = 1.0 / samples;\\r\\n\\r\\n\\t\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\t\\ttempA.drawTo(function() {\\r\\n\\t\\t\\t\\ttempB.bind(1);\\r\\n\\t\\t\\t\\ttex.toViewport( shader_copy, uniforms );\\r\\n\\t\\t\\t});\\r\\n\\r\\n\\t\\t\\tthis._temp_texture_out.drawTo(function() {\\r\\n\\t\\t\\t\\ttempA.toViewport( shader_avg, uniforms );\\r\\n\\t\\t\\t});\\r\\n\\r\\n\\t\\t\\tthis.setOutputData( 0, this._temp_texture_out );\\r\\n\\r\\n\\t\\t\\t//swap\\r\\n\\t\\t\\tthis._temp_texture = tempB;\\r\\n\\t\\t\\tthis._temp_texture2 = tempA;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.setOutputData(0, this._temp_texture_out);\\r\\n\\t\\tthis.setOutputData(1, this._temp_texture2);\\r\\n\\t\\tthis.frame++;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureLinearAvgSmooth.pixel_shader_copy =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureB;\\\\n\\\\\\r\\n\\t\\tuniform float u_isamples;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tif( v_coord.x <= u_isamples )\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = texture2D( u_texture, vec2(0.5) );\\\\n\\\\\\r\\n\\t\\t\\telse\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLGraphTextureLinearAvgSmooth.pixel_shader_avg =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform int u_samples;\\\\n\\\\\\r\\n\\t\\tuniform float u_isamples;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t\\tfor(int i = 0; i < 64; ++i)\\\\n\\\\\\r\\n\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\\\\n\\\\\\r\\n\\t\\t\\t\\tif(i == (u_samples - 1))\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tbreak;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = color * u_isamples;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\r\\n\\tLiteGraph.registerNodeType( \\\"texture/linear_avg_smooth\\\", LGraphTextureLinearAvgSmooth );\\r\\n\\r\\n\\t// Image To Texture *****************************************\\r\\n\\tfunction LGraphImageToTexture() {\\r\\n\\t\\tthis.addInput(\\\"Image\\\", \\\"image\\\");\\r\\n\\t\\tthis.addOutput(\\\"\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphImageToTexture.title = \\\"Image to Texture\\\";\\r\\n\\tLGraphImageToTexture.desc = \\\"Uploads an image to the GPU\\\";\\r\\n\\t//LGraphImageToTexture.widgets_info = { size: { widget:\\\"combo\\\", values:[0,32,64,128,256,512,1024,2048]} };\\r\\n\\r\\n\\tLGraphImageToTexture.prototype.onExecute = function() {\\r\\n\\t\\tvar img = this.getInputData(0);\\r\\n\\t\\tif (!img) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar width = img.videoWidth || img.width;\\r\\n\\t\\tvar height = img.videoHeight || img.height;\\r\\n\\r\\n\\t\\t//this is in case we are using a webgl canvas already, no need to reupload it\\r\\n\\t\\tif (img.gltexture) {\\r\\n\\t\\t\\tthis.setOutputData(0, img.gltexture);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar temp = this._temp_texture;\\r\\n\\t\\tif (!temp || temp.width != width || temp.height != height) {\\r\\n\\t\\t\\tthis._temp_texture = new GL.Texture(width, height, {\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tthis._temp_texture.uploadImage(img);\\r\\n\\t\\t} catch (err) {\\r\\n\\t\\t\\tconsole.error(\\r\\n\\t\\t\\t\\t\\\"image comes from an unsafe location, cannot be uploaded to webgl: \\\" +\\r\\n\\t\\t\\t\\t\\terr\\r\\n\\t\\t\\t);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._temp_texture);\\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\r\\n\\t\\t\\\"texture/imageToTexture\\\",\\r\\n\\t\\tLGraphImageToTexture\\r\\n\\t);\\r\\n\\r\\n\\t// Texture LUT *****************************************\\r\\n\\tfunction LGraphTextureLUT() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"LUT\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"Intensity\\\", \\\"number\\\");\\r\\n\\t\\tthis.addOutput(\\\"\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = { enabled: true, intensity: 1, precision: LGraphTexture.DEFAULT, texture: null };\\r\\n\\r\\n\\t\\tif (!LGraphTextureLUT._shader) {\\r\\n\\t\\t\\tLGraphTextureLUT._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureLUT.pixel_shader );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureLUT.widgets_info = {\\r\\n\\t\\ttexture: { widget: \\\"texture\\\" },\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureLUT.title = \\\"LUT\\\";\\r\\n\\tLGraphTextureLUT.desc = \\\"Apply LUT to Texture\\\";\\r\\n\\r\\n\\tLGraphTextureLUT.prototype.onExecute = function() {\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\r\\n\\t\\tif (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) {\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar lut_tex = this.getInputData(1);\\r\\n\\r\\n\\t\\tif (!lut_tex) {\\r\\n\\t\\t\\tlut_tex = LGraphTexture.getTexture(this.properties.texture);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!lut_tex) {\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tlut_tex.bind(0);\\r\\n\\t\\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\\r\\n\\t\\tgl.texParameteri(\\r\\n\\t\\t\\tgl.TEXTURE_2D,\\r\\n\\t\\t\\tgl.TEXTURE_WRAP_S,\\r\\n\\t\\t\\tgl.CLAMP_TO_EDGE\\r\\n\\t\\t);\\r\\n\\t\\tgl.texParameteri(\\r\\n\\t\\t\\tgl.TEXTURE_2D,\\r\\n\\t\\t\\tgl.TEXTURE_WRAP_T,\\r\\n\\t\\t\\tgl.CLAMP_TO_EDGE\\r\\n\\t\\t);\\r\\n\\t\\tgl.bindTexture(gl.TEXTURE_2D, null);\\r\\n\\r\\n\\t\\tvar intensity = this.properties.intensity;\\r\\n\\t\\tif (this.isInputConnected(2)) {\\r\\n\\t\\t\\tthis.properties.intensity = intensity = this.getInputData(2);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._tex = LGraphTexture.getTargetTexture(\\r\\n\\t\\t\\ttex,\\r\\n\\t\\t\\tthis._tex,\\r\\n\\t\\t\\tthis.properties.precision\\r\\n\\t\\t);\\r\\n\\r\\n\\t\\t//var mesh = Mesh.getScreenQuad();\\r\\n\\r\\n\\t\\tthis._tex.drawTo(function() {\\r\\n\\t\\t\\tlut_tex.bind(1);\\r\\n\\t\\t\\ttex.toViewport(LGraphTextureLUT._shader, {\\r\\n\\t\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\t\\tu_textureB: 1,\\r\\n\\t\\t\\t\\tu_amount: intensity\\r\\n\\t\\t\\t});\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._tex);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureLUT.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureB;\\\\n\\\\\\r\\n\\t\\tuniform float u_amount;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\\\\n\\\\\\r\\n\\t\\t\\t mediump float blueColor = textureColor.b * 63.0;\\\\n\\\\\\r\\n\\t\\t\\t mediump vec2 quad1;\\\\n\\\\\\r\\n\\t\\t\\t quad1.y = floor(floor(blueColor) / 8.0);\\\\n\\\\\\r\\n\\t\\t\\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\\\\n\\\\\\r\\n\\t\\t\\t mediump vec2 quad2;\\\\n\\\\\\r\\n\\t\\t\\t quad2.y = floor(ceil(blueColor) / 8.0);\\\\n\\\\\\r\\n\\t\\t\\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\\\\n\\\\\\r\\n\\t\\t\\t highp vec2 texPos1;\\\\n\\\\\\r\\n\\t\\t\\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\\\\n\\\\\\r\\n\\t\\t\\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\\\\n\\\\\\r\\n\\t\\t\\t highp vec2 texPos2;\\\\n\\\\\\r\\n\\t\\t\\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\\\\n\\\\\\r\\n\\t\\t\\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\\\\n\\\\\\r\\n\\t\\t\\t lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\\\\n\\\\\\r\\n\\t\\t\\t lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\\\\n\\\\\\r\\n\\t\\t\\t lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/LUT\\\", LGraphTextureLUT);\\r\\n\\r\\n\\t// Texture Channels *****************************************\\r\\n\\tfunction LGraphTextureChannels() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\r\\n\\t\\tthis.addOutput(\\\"R\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"G\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"B\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"A\\\", \\\"Texture\\\");\\r\\n\\r\\n\\t\\t//this.properties = { use_single_channel: true };\\r\\n\\t\\tif (!LGraphTextureChannels._shader) {\\r\\n\\t\\t\\tLGraphTextureChannels._shader = new GL.Shader(\\r\\n\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureChannels.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureChannels.title = \\\"Texture to Channels\\\";\\r\\n\\tLGraphTextureChannels.desc = \\\"Split texture channels\\\";\\r\\n\\r\\n\\tLGraphTextureChannels.prototype.onExecute = function() {\\r\\n\\t\\tvar texA = this.getInputData(0);\\r\\n\\t\\tif (!texA) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this._channels) {\\r\\n\\t\\t\\tthis._channels = Array(4);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//var format = this.properties.use_single_channel ? gl.LUMINANCE : gl.RGBA; //not supported by WebGL1\\r\\n\\t\\tvar format = gl.RGB;\\r\\n\\t\\tvar connections = 0;\\r\\n\\t\\tfor (var i = 0; i < 4; i++) {\\r\\n\\t\\t\\tif (this.isOutputConnected(i)) {\\r\\n\\t\\t\\t\\tif (\\r\\n\\t\\t\\t\\t\\t!this._channels[i] ||\\r\\n\\t\\t\\t\\t\\tthis._channels[i].width != texA.width ||\\r\\n\\t\\t\\t\\t\\tthis._channels[i].height != texA.height ||\\r\\n\\t\\t\\t\\t\\tthis._channels[i].type != texA.type ||\\r\\n\\t\\t\\t\\t\\tthis._channels[i].format != format\\r\\n\\t\\t\\t\\t) {\\r\\n\\t\\t\\t\\t\\tthis._channels[i] = new GL.Texture(\\r\\n\\t\\t\\t\\t\\t\\ttexA.width,\\r\\n\\t\\t\\t\\t\\t\\ttexA.height,\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: texA.type,\\r\\n\\t\\t\\t\\t\\t\\t\\tformat: format,\\r\\n\\t\\t\\t\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tconnections++;\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tthis._channels[i] = null;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!connections) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\r\\n\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\tvar shader = LGraphTextureChannels._shader;\\r\\n\\t\\tvar masks = [\\r\\n\\t\\t\\t[1, 0, 0, 0],\\r\\n\\t\\t\\t[0, 1, 0, 0],\\r\\n\\t\\t\\t[0, 0, 1, 0],\\r\\n\\t\\t\\t[0, 0, 0, 1]\\r\\n\\t\\t];\\r\\n\\r\\n\\t\\tfor (var i = 0; i < 4; i++) {\\r\\n\\t\\t\\tif (!this._channels[i]) {\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tthis._channels[i].drawTo(function() {\\r\\n\\t\\t\\t\\ttexA.bind(0);\\r\\n\\t\\t\\t\\tshader\\r\\n\\t\\t\\t\\t\\t.uniforms({ u_texture: 0, u_mask: masks[i] })\\r\\n\\t\\t\\t\\t\\t.draw(mesh);\\r\\n\\t\\t\\t});\\r\\n\\t\\t\\tthis.setOutputData(i, this._channels[i]);\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureChannels.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform vec4 u_mask;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\r\\n\\t\\t\\\"texture/textureChannels\\\",\\r\\n\\t\\tLGraphTextureChannels\\r\\n\\t);\\r\\n\\r\\n\\t// Texture Channels to Texture *****************************************\\r\\n\\tfunction LGraphChannelsTexture() {\\r\\n\\t\\tthis.addInput(\\\"R\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"G\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"B\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"A\\\", \\\"Texture\\\");\\r\\n\\r\\n\\t\\tthis.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT,\\r\\n\\t\\t\\tR: 1,\\r\\n\\t\\t\\tG: 1,\\r\\n\\t\\t\\tB: 1,\\r\\n\\t\\t\\tA: 1\\r\\n\\t\\t};\\r\\n\\t\\tthis._color = vec4.create();\\r\\n\\t\\tthis._uniforms = {\\r\\n\\t\\t\\tu_textureR: 0,\\r\\n\\t\\t\\tu_textureG: 1,\\r\\n\\t\\t\\tu_textureB: 2,\\r\\n\\t\\t\\tu_textureA: 3,\\r\\n\\t\\t\\tu_color: this._color\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphChannelsTexture.title = \\\"Channels to Texture\\\";\\r\\n\\tLGraphChannelsTexture.desc = \\\"Split texture channels\\\";\\r\\n\\tLGraphChannelsTexture.widgets_info = {\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphChannelsTexture.prototype.onExecute = function() {\\r\\n\\t\\tvar white = LGraphTexture.getWhiteTexture();\\r\\n\\t\\tvar texR = this.getInputData(0) || white;\\r\\n\\t\\tvar texG = this.getInputData(1) || white;\\r\\n\\t\\tvar texB = this.getInputData(2) || white;\\r\\n\\t\\tvar texA = this.getInputData(3) || white;\\r\\n\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\r\\n\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\tif (!LGraphChannelsTexture._shader) {\\r\\n\\t\\t\\tLGraphChannelsTexture._shader = new GL.Shader(\\r\\n\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphChannelsTexture.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\t\\tvar shader = LGraphChannelsTexture._shader;\\r\\n\\r\\n\\t\\tvar w = Math.max(texR.width, texG.width, texB.width, texA.width);\\r\\n\\t\\tvar h = Math.max(\\r\\n\\t\\t\\ttexR.height,\\r\\n\\t\\t\\ttexG.height,\\r\\n\\t\\t\\ttexB.height,\\r\\n\\t\\t\\ttexA.height\\r\\n\\t\\t);\\r\\n\\t\\tvar type =\\r\\n\\t\\t\\tthis.properties.precision == LGraphTexture.HIGH\\r\\n\\t\\t\\t\\t? LGraphTexture.HIGH_PRECISION_FORMAT\\r\\n\\t\\t\\t\\t: gl.UNSIGNED_BYTE;\\r\\n\\r\\n\\t\\tif (\\r\\n\\t\\t\\t!this._texture ||\\r\\n\\t\\t\\tthis._texture.width != w ||\\r\\n\\t\\t\\tthis._texture.height != h ||\\r\\n\\t\\t\\tthis._texture.type != type\\r\\n\\t\\t) {\\r\\n\\t\\t\\tthis._texture = new GL.Texture(w, h, {\\r\\n\\t\\t\\t\\ttype: type,\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar color = this._color;\\r\\n\\t\\tcolor[0] = this.properties.R;\\r\\n\\t\\tcolor[1] = this.properties.G;\\r\\n\\t\\tcolor[2] = this.properties.B;\\r\\n\\t\\tcolor[3] = this.properties.A;\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\r\\n\\t\\tthis._texture.drawTo(function() {\\r\\n\\t\\t\\ttexR.bind(0);\\r\\n\\t\\t\\ttexG.bind(1);\\r\\n\\t\\t\\ttexB.bind(2);\\r\\n\\t\\t\\ttexA.bind(3);\\r\\n\\t\\t\\tshader.uniforms(uniforms).draw(mesh);\\r\\n\\t\\t});\\r\\n\\t\\tthis.setOutputData(0, this._texture);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphChannelsTexture.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureR;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureG;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureB;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureA;\\\\n\\\\\\r\\n\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t gl_FragColor = u_color * vec4( \\\\\\r\\n\\t\\t\\t\\t\\ttexture2D(u_textureR, v_coord).r,\\\\\\r\\n\\t\\t\\t\\t\\ttexture2D(u_textureG, v_coord).r,\\\\\\r\\n\\t\\t\\t\\t\\ttexture2D(u_textureB, v_coord).r,\\\\\\r\\n\\t\\t\\t\\t\\ttexture2D(u_textureA, v_coord).r);\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\r\\n\\t\\t\\\"texture/channelsTexture\\\",\\r\\n\\t\\tLGraphChannelsTexture\\r\\n\\t);\\r\\n\\r\\n\\t// Texture Color *****************************************\\r\\n\\tfunction LGraphTextureColor() {\\r\\n\\t\\tthis.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\r\\n\\t\\tthis._tex_color = vec4.create();\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tcolor: vec4.create(),\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureColor.title = \\\"Color\\\";\\r\\n\\tLGraphTextureColor.desc =\\r\\n\\t\\t\\\"Generates a 1x1 texture with a constant color\\\";\\r\\n\\r\\n\\tLGraphTextureColor.widgets_info = {\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureColor.prototype.onDrawBackground = function(ctx) {\\r\\n\\t\\tvar c = this.properties.color;\\r\\n\\t\\tctx.fillStyle =\\r\\n\\t\\t\\t\\\"rgb(\\\" +\\r\\n\\t\\t\\tMath.floor(Math.clamp(c[0], 0, 1) * 255) +\\r\\n\\t\\t\\t\\\",\\\" +\\r\\n\\t\\t\\tMath.floor(Math.clamp(c[1], 0, 1) * 255) +\\r\\n\\t\\t\\t\\\",\\\" +\\r\\n\\t\\t\\tMath.floor(Math.clamp(c[2], 0, 1) * 255) +\\r\\n\\t\\t\\t\\\")\\\";\\r\\n\\t\\tif (this.flags.collapsed) {\\r\\n\\t\\t\\tthis.boxcolor = ctx.fillStyle;\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tctx.fillRect(0, 0, this.size[0], this.size[1]);\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureColor.prototype.onExecute = function() {\\r\\n\\t\\tvar type =\\r\\n\\t\\t\\tthis.properties.precision == LGraphTexture.HIGH\\r\\n\\t\\t\\t\\t? LGraphTexture.HIGH_PRECISION_FORMAT\\r\\n\\t\\t\\t\\t: gl.UNSIGNED_BYTE;\\r\\n\\r\\n\\t\\tif (!this._tex || this._tex.type != type) {\\r\\n\\t\\t\\tthis._tex = new GL.Texture(1, 1, {\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\ttype: type,\\r\\n\\t\\t\\t\\tminFilter: gl.NEAREST\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\t\\tvar color = this.properties.color;\\r\\n\\r\\n\\t\\tif (this.inputs) {\\r\\n\\t\\t\\tfor (var i = 0; i < this.inputs.length; i++) {\\r\\n\\t\\t\\t\\tvar input = this.inputs[i];\\r\\n\\t\\t\\t\\tvar v = this.getInputData(i);\\r\\n\\t\\t\\t\\tif (v === undefined) {\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tswitch (input.name) {\\r\\n\\t\\t\\t\\t\\tcase \\\"RGB\\\":\\r\\n\\t\\t\\t\\t\\tcase \\\"RGBA\\\":\\r\\n\\t\\t\\t\\t\\t\\tcolor.set(v);\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"R\\\":\\r\\n\\t\\t\\t\\t\\t\\tcolor[0] = v;\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"G\\\":\\r\\n\\t\\t\\t\\t\\t\\tcolor[1] = v;\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"B\\\":\\r\\n\\t\\t\\t\\t\\t\\tcolor[2] = v;\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"A\\\":\\r\\n\\t\\t\\t\\t\\t\\tcolor[3] = v;\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (vec4.sqrDist(this._tex_color, color) > 0.001) {\\r\\n\\t\\t\\tthis._tex_color.set(color);\\r\\n\\t\\t\\tthis._tex.fill(color);\\r\\n\\t\\t}\\r\\n\\t\\tthis.setOutputData(0, this._tex);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureColor.prototype.onGetInputs = function() {\\r\\n\\t\\treturn [\\r\\n\\t\\t\\t[\\\"RGB\\\", \\\"vec3\\\"],\\r\\n\\t\\t\\t[\\\"RGBA\\\", \\\"vec4\\\"],\\r\\n\\t\\t\\t[\\\"R\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"G\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"B\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"A\\\", \\\"number\\\"]\\r\\n\\t\\t];\\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/color\\\", LGraphTextureColor);\\r\\n\\r\\n\\t// Texture Channels to Texture *****************************************\\r\\n\\tfunction LGraphTextureGradient() {\\r\\n\\t\\tthis.addInput(\\\"A\\\", \\\"color\\\");\\r\\n\\t\\tthis.addInput(\\\"B\\\", \\\"color\\\");\\r\\n\\t\\tthis.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tangle: 0,\\r\\n\\t\\t\\tscale: 1,\\r\\n\\t\\t\\tA: [0, 0, 0],\\r\\n\\t\\t\\tB: [1, 1, 1],\\r\\n\\t\\t\\ttexture_size: 32\\r\\n\\t\\t};\\r\\n\\t\\tif (!LGraphTextureGradient._shader) {\\r\\n\\t\\t\\tLGraphTextureGradient._shader = new GL.Shader(\\r\\n\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureGradient.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._uniforms = {\\r\\n\\t\\t\\tu_angle: 0,\\r\\n\\t\\t\\tu_colorA: vec3.create(),\\r\\n\\t\\t\\tu_colorB: vec3.create()\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureGradient.title = \\\"Gradient\\\";\\r\\n\\tLGraphTextureGradient.desc = \\\"Generates a gradient\\\";\\r\\n\\tLGraphTextureGradient[\\\"@A\\\"] = { type: \\\"color\\\" };\\r\\n\\tLGraphTextureGradient[\\\"@B\\\"] = { type: \\\"color\\\" };\\r\\n\\tLGraphTextureGradient[\\\"@texture_size\\\"] = {\\r\\n\\t\\ttype: \\\"enum\\\",\\r\\n\\t\\tvalues: [32, 64, 128, 256, 512]\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureGradient.prototype.onExecute = function() {\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\r\\n\\t\\tvar mesh = GL.Mesh.getScreenQuad();\\r\\n\\t\\tvar shader = LGraphTextureGradient._shader;\\r\\n\\r\\n\\t\\tvar A = this.getInputData(0);\\r\\n\\t\\tif (!A) {\\r\\n\\t\\t\\tA = this.properties.A;\\r\\n\\t\\t}\\r\\n\\t\\tvar B = this.getInputData(1);\\r\\n\\t\\tif (!B) {\\r\\n\\t\\t\\tB = this.properties.B;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//angle and scale\\r\\n\\t\\tfor (var i = 2; i < this.inputs.length; i++) {\\r\\n\\t\\t\\tvar input = this.inputs[i];\\r\\n\\t\\t\\tvar v = this.getInputData(i);\\r\\n\\t\\t\\tif (v === undefined) {\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.properties[input.name] = v;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\t\\tthis._uniforms.u_angle = this.properties.angle * DEG2RAD;\\r\\n\\t\\tthis._uniforms.u_scale = this.properties.scale;\\r\\n\\t\\tvec3.copy(uniforms.u_colorA, A);\\r\\n\\t\\tvec3.copy(uniforms.u_colorB, B);\\r\\n\\r\\n\\t\\tvar size = parseInt(this.properties.texture_size);\\r\\n\\t\\tif (!this._tex || this._tex.width != size) {\\r\\n\\t\\t\\tthis._tex = new GL.Texture(size, size, {\\r\\n\\t\\t\\t\\tformat: gl.RGB,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._tex.drawTo(function() {\\r\\n\\t\\t\\tshader.uniforms(uniforms).draw(mesh);\\r\\n\\t\\t});\\r\\n\\t\\tthis.setOutputData(0, this._tex);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureGradient.prototype.onGetInputs = function() {\\r\\n\\t\\treturn [[\\\"angle\\\", \\\"number\\\"], [\\\"scale\\\", \\\"number\\\"]];\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureGradient.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform float u_angle;\\\\n\\\\\\r\\n\\t\\tuniform float u_scale;\\\\n\\\\\\r\\n\\t\\tuniform vec3 u_colorA;\\\\n\\\\\\r\\n\\t\\tuniform vec3 u_colorB;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvec2 rotate(vec2 v, float angle)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tvec2 result;\\\\n\\\\\\r\\n\\t\\t\\tfloat _cos = cos(angle);\\\\n\\\\\\r\\n\\t\\t\\tfloat _sin = sin(angle);\\\\n\\\\\\r\\n\\t\\t\\tresult.x = v.x * _cos - v.y * _sin;\\\\n\\\\\\r\\n\\t\\t\\tresult.y = v.x * _sin + v.y * _cos;\\\\n\\\\\\r\\n\\t\\t\\treturn result;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tfloat f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\\\\n\\\\\\r\\n\\t\\t\\tvec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\\\\n\\\\\\r\\n\\t\\t gl_FragColor = vec4(color,1.0);\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/gradient\\\", LGraphTextureGradient);\\r\\n\\r\\n\\t// Texture Mix *****************************************\\r\\n\\tfunction LGraphTextureMix() {\\r\\n\\t\\tthis.addInput(\\\"A\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"B\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"Mixer\\\", \\\"Texture\\\");\\r\\n\\r\\n\\t\\tthis.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = { factor: 0.5, precision: LGraphTexture.DEFAULT };\\r\\n\\t\\tthis._uniforms = {\\r\\n\\t\\t\\tu_textureA: 0,\\r\\n\\t\\t\\tu_textureB: 1,\\r\\n\\t\\t\\tu_textureMix: 2,\\r\\n\\t\\t\\tu_mix: vec4.create()\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureMix.title = \\\"Mix\\\";\\r\\n\\tLGraphTextureMix.desc = \\\"Generates a texture mixing two textures\\\";\\r\\n\\r\\n\\tLGraphTextureMix.widgets_info = {\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureMix.prototype.onExecute = function() {\\r\\n\\t\\tvar texA = this.getInputData(0);\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tif (this.properties.precision === LGraphTexture.PASS_THROUGH) {\\r\\n\\t\\t\\tthis.setOutputData(0, texA);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar texB = this.getInputData(1);\\r\\n\\t\\tif (!texA || !texB) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar texMix = this.getInputData(2);\\r\\n\\r\\n\\t\\tvar factor = this.getInputData(3);\\r\\n\\r\\n\\t\\tthis._tex = LGraphTexture.getTargetTexture(\\r\\n\\t\\t\\ttexA,\\r\\n\\t\\t\\tthis._tex,\\r\\n\\t\\t\\tthis.properties.precision\\r\\n\\t\\t);\\r\\n\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\r\\n\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\tvar shader = null;\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\t\\tif (texMix) {\\r\\n\\t\\t\\tshader = LGraphTextureMix._shader_tex;\\r\\n\\t\\t\\tif (!shader) {\\r\\n\\t\\t\\t\\tshader = LGraphTextureMix._shader_tex = new GL.Shader(\\r\\n\\t\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\t\\tLGraphTextureMix.pixel_shader,\\r\\n\\t\\t\\t\\t\\t{ MIX_TEX: \\\"\\\" }\\r\\n\\t\\t\\t\\t);\\r\\n\\t\\t\\t}\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tshader = LGraphTextureMix._shader_factor;\\r\\n\\t\\t\\tif (!shader) {\\r\\n\\t\\t\\t\\tshader = LGraphTextureMix._shader_factor = new GL.Shader(\\r\\n\\t\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\t\\tLGraphTextureMix.pixel_shader\\r\\n\\t\\t\\t\\t);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tvar f = factor == null ? this.properties.factor : factor;\\r\\n\\t\\t\\tuniforms.u_mix.set([f, f, f, f]);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._tex.drawTo(function() {\\r\\n\\t\\t\\ttexA.bind(0);\\r\\n\\t\\t\\ttexB.bind(1);\\r\\n\\t\\t\\tif (texMix) {\\r\\n\\t\\t\\t\\ttexMix.bind(2);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tshader.uniforms(uniforms).draw(mesh);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._tex);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureMix.prototype.onGetInputs = function() {\\r\\n\\t\\treturn [[\\\"factor\\\", \\\"number\\\"]];\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureMix.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureA;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_textureB;\\\\n\\\\\\r\\n\\t\\t#ifdef MIX_TEX\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_textureMix;\\\\n\\\\\\r\\n\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_mix;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t#ifdef MIX_TEX\\\\n\\\\\\r\\n\\t\\t\\t vec4 f = texture2D(u_textureMix, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\t vec4 f = u_mix;\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/mix\\\", LGraphTextureMix);\\r\\n\\r\\n\\t// Texture Edges detection *****************************************\\r\\n\\tfunction LGraphTextureEdges() {\\r\\n\\t\\tthis.addInput(\\\"Tex.\\\", \\\"Texture\\\");\\r\\n\\r\\n\\t\\tthis.addOutput(\\\"Edges\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tinvert: true,\\r\\n\\t\\t\\tthreshold: false,\\r\\n\\t\\t\\tfactor: 1,\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tif (!LGraphTextureEdges._shader) {\\r\\n\\t\\t\\tLGraphTextureEdges._shader = new GL.Shader(\\r\\n\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureEdges.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureEdges.title = \\\"Edges\\\";\\r\\n\\tLGraphTextureEdges.desc = \\\"Detects edges\\\";\\r\\n\\r\\n\\tLGraphTextureEdges.widgets_info = {\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureEdges.prototype.onExecute = function() {\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\r\\n\\t\\tif (this.properties.precision === LGraphTexture.PASS_THROUGH) {\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._tex = LGraphTexture.getTargetTexture(\\r\\n\\t\\t\\ttex,\\r\\n\\t\\t\\tthis._tex,\\r\\n\\t\\t\\tthis.properties.precision\\r\\n\\t\\t);\\r\\n\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\r\\n\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\tvar shader = LGraphTextureEdges._shader;\\r\\n\\t\\tvar invert = this.properties.invert;\\r\\n\\t\\tvar factor = this.properties.factor;\\r\\n\\t\\tvar threshold = this.properties.threshold ? 1 : 0;\\r\\n\\r\\n\\t\\tthis._tex.drawTo(function() {\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tshader\\r\\n\\t\\t\\t\\t.uniforms({\\r\\n\\t\\t\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\t\\t\\tu_isize: [1 / tex.width, 1 / tex.height],\\r\\n\\t\\t\\t\\t\\tu_factor: factor,\\r\\n\\t\\t\\t\\t\\tu_threshold: threshold,\\r\\n\\t\\t\\t\\t\\tu_invert: invert ? 1 : 0\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t.draw(mesh);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._tex);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureEdges.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_isize;\\\\n\\\\\\r\\n\\t\\tuniform int u_invert;\\\\n\\\\\\r\\n\\t\\tuniform float u_factor;\\\\n\\\\\\r\\n\\t\\tuniform float u_threshold;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec4 center = texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\\\\n\\\\\\r\\n\\t\\t\\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\\\\n\\\\\\r\\n\\t\\t\\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\\\\n\\\\\\r\\n\\t\\t\\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\\\\n\\\\\\r\\n\\t\\t\\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\\\\n\\\\\\r\\n\\t\\t\\tdiff *= u_factor;\\\\n\\\\\\r\\n\\t\\t\\tif(u_invert == 1)\\\\n\\\\\\r\\n\\t\\t\\t\\tdiff.xyz = vec3(1.0) - diff.xyz;\\\\n\\\\\\r\\n\\t\\t\\tif( u_threshold == 0.0 )\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = vec4( diff.xyz, center.a );\\\\n\\\\\\r\\n\\t\\t\\telse\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/edges\\\", LGraphTextureEdges);\\r\\n\\r\\n\\t// Texture Depth *****************************************\\r\\n\\tfunction LGraphTextureDepthRange() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"Distance\\\", \\\"number\\\");\\r\\n\\t\\tthis.addInput(\\\"Range\\\", \\\"number\\\");\\r\\n\\t\\tthis.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tdistance: 100,\\r\\n\\t\\t\\trange: 50,\\r\\n\\t\\t\\tonly_depth: false,\\r\\n\\t\\t\\thigh_precision: false\\r\\n\\t\\t};\\r\\n\\t\\tthis._uniforms = {\\r\\n\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\tu_distance: 100,\\r\\n\\t\\t\\tu_range: 50,\\r\\n\\t\\t\\tu_camera_planes: null\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureDepthRange.title = \\\"Depth Range\\\";\\r\\n\\tLGraphTextureDepthRange.desc = \\\"Generates a texture with a depth range\\\";\\r\\n\\r\\n\\tLGraphTextureDepthRange.prototype.onExecute = function() {\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar precision = gl.UNSIGNED_BYTE;\\r\\n\\t\\tif (this.properties.high_precision) {\\r\\n\\t\\t\\tprecision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (\\r\\n\\t\\t\\t!this._temp_texture ||\\r\\n\\t\\t\\tthis._temp_texture.type != precision ||\\r\\n\\t\\t\\tthis._temp_texture.width != tex.width ||\\r\\n\\t\\t\\tthis._temp_texture.height != tex.height\\r\\n\\t\\t) {\\r\\n\\t\\t\\tthis._temp_texture = new GL.Texture(tex.width, tex.height, {\\r\\n\\t\\t\\t\\ttype: precision,\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\r\\n\\t\\t//iterations\\r\\n\\t\\tvar distance = this.properties.distance;\\r\\n\\t\\tif (this.isInputConnected(1)) {\\r\\n\\t\\t\\tdistance = this.getInputData(1);\\r\\n\\t\\t\\tthis.properties.distance = distance;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar range = this.properties.range;\\r\\n\\t\\tif (this.isInputConnected(2)) {\\r\\n\\t\\t\\trange = this.getInputData(2);\\r\\n\\t\\t\\tthis.properties.range = range;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tuniforms.u_distance = distance;\\r\\n\\t\\tuniforms.u_range = range;\\r\\n\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\tif (!LGraphTextureDepthRange._shader) {\\r\\n\\t\\t\\tLGraphTextureDepthRange._shader = new GL.Shader(\\r\\n\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureDepthRange.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t\\tLGraphTextureDepthRange._shader_onlydepth = new GL.Shader(\\r\\n\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureDepthRange.pixel_shader,\\r\\n\\t\\t\\t\\t{ ONLY_DEPTH: \\\"\\\" }\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\t\\tvar shader = this.properties.only_depth\\r\\n\\t\\t\\t? LGraphTextureDepthRange._shader_onlydepth\\r\\n\\t\\t\\t: LGraphTextureDepthRange._shader;\\r\\n\\r\\n\\t\\t//NEAR AND FAR PLANES\\r\\n\\t\\tvar planes = null;\\r\\n\\t\\tif (tex.near_far_planes) {\\r\\n\\t\\t\\tplanes = tex.near_far_planes;\\r\\n\\t\\t} else if (window.LS && LS.Renderer._main_camera) {\\r\\n\\t\\t\\tplanes = LS.Renderer._main_camera._uniforms.u_camera_planes;\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tplanes = [0.1, 1000];\\r\\n\\t\\t} //hardcoded\\r\\n\\t\\tuniforms.u_camera_planes = planes;\\r\\n\\r\\n\\t\\tthis._temp_texture.drawTo(function() {\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tshader.uniforms(uniforms).draw(mesh);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis._temp_texture.near_far_planes = planes;\\r\\n\\t\\tthis.setOutputData(0, this._temp_texture);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureDepthRange.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_camera_planes;\\\\n\\\\\\r\\n\\t\\tuniform float u_distance;\\\\n\\\\\\r\\n\\t\\tuniform float u_range;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tfloat LinearDepth()\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tfloat zNear = u_camera_planes.x;\\\\n\\\\\\r\\n\\t\\t\\tfloat zFar = u_camera_planes.y;\\\\n\\\\\\r\\n\\t\\t\\tfloat depth = texture2D(u_texture, v_coord).x;\\\\n\\\\\\r\\n\\t\\t\\tdepth = depth * 2.0 - 1.0;\\\\n\\\\\\r\\n\\t\\t\\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tfloat depth = LinearDepth();\\\\n\\\\\\r\\n\\t\\t\\t#ifdef ONLY_DEPTH\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = vec4(depth);\\\\n\\\\\\r\\n\\t\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat dof = 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tif(diff <= u_range)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tdof = diff / u_range;\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = vec4(dof);\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\r\\n\\t\\t\\\"texture/depth_range\\\",\\r\\n\\t\\tLGraphTextureDepthRange\\r\\n\\t);\\r\\n\\r\\n\\t// Texture Blur *****************************************\\r\\n\\tfunction LGraphTextureBlur() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"Iterations\\\", \\\"number\\\");\\r\\n\\t\\tthis.addInput(\\\"Intensity\\\", \\\"number\\\");\\r\\n\\t\\tthis.addOutput(\\\"Blurred\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tintensity: 1,\\r\\n\\t\\t\\titerations: 1,\\r\\n\\t\\t\\tpreserve_aspect: false,\\r\\n\\t\\t\\tscale: [1, 1],\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureBlur.title = \\\"Blur\\\";\\r\\n\\tLGraphTextureBlur.desc = \\\"Blur a texture\\\";\\r\\n\\r\\n\\tLGraphTextureBlur.widgets_info = {\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureBlur.max_iterations = 20;\\r\\n\\r\\n\\tLGraphTextureBlur.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tvar temp = this._final_texture;\\r\\n\\r\\n\\t\\tif (\\r\\n\\t\\t\\t!temp ||\\r\\n\\t\\t\\ttemp.width != tex.width ||\\r\\n\\t\\t\\ttemp.height != tex.height ||\\r\\n\\t\\t\\ttemp.type != tex.type\\r\\n\\t\\t) {\\r\\n\\t\\t\\t//we need two textures to do the blurring\\r\\n\\t\\t\\t//this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR });\\r\\n\\t\\t\\ttemp = this._final_texture = new GL.Texture(\\r\\n\\t\\t\\t\\ttex.width,\\r\\n\\t\\t\\t\\ttex.height,\\r\\n\\t\\t\\t\\t{ type: tex.type, format: gl.RGBA, filter: gl.LINEAR }\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//iterations\\r\\n\\t\\tvar iterations = this.properties.iterations;\\r\\n\\t\\tif (this.isInputConnected(1)) {\\r\\n\\t\\t\\titerations = this.getInputData(1);\\r\\n\\t\\t\\tthis.properties.iterations = iterations;\\r\\n\\t\\t}\\r\\n\\t\\titerations = Math.min(\\r\\n\\t\\t\\tMath.floor(iterations),\\r\\n\\t\\t\\tLGraphTextureBlur.max_iterations\\r\\n\\t\\t);\\r\\n\\t\\tif (iterations == 0) {\\r\\n\\t\\t\\t//skip blurring\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar intensity = this.properties.intensity;\\r\\n\\t\\tif (this.isInputConnected(2)) {\\r\\n\\t\\t\\tintensity = this.getInputData(2);\\r\\n\\t\\t\\tthis.properties.intensity = intensity;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//blur sometimes needs an aspect correction\\r\\n\\t\\tvar aspect = LiteGraph.camera_aspect;\\r\\n\\t\\tif (!aspect && window.gl !== undefined) {\\r\\n\\t\\t\\taspect = gl.canvas.height / gl.canvas.width;\\r\\n\\t\\t}\\r\\n\\t\\tif (!aspect) {\\r\\n\\t\\t\\taspect = 1;\\r\\n\\t\\t}\\r\\n\\t\\taspect = this.properties.preserve_aspect ? aspect : 1;\\r\\n\\r\\n\\t\\tvar scale = this.properties.scale || [1, 1];\\r\\n\\t\\ttex.applyBlur(aspect * scale[0], scale[1], intensity, temp);\\r\\n\\t\\tfor (var i = 1; i < iterations; ++i) {\\r\\n\\t\\t\\ttemp.applyBlur(\\r\\n\\t\\t\\t\\taspect * scale[0] * (i + 1),\\r\\n\\t\\t\\t\\tscale[1] * (i + 1),\\r\\n\\t\\t\\t\\tintensity\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setOutputData(0, temp);\\r\\n\\t};\\r\\n\\r\\n\\t/*\\r\\nLGraphTextureBlur.pixel_shader = \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_offset;\\\\n\\\\\\r\\n\\t\\tuniform float u_intensity;\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t vec4 sum = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t vec4 center = texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\\\\n\\\\\\r\\n\\t\\t sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\\\\n\\\\\\r\\n\\t\\t sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\\\\n\\\\\\r\\n\\t\\t sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\\\\n\\\\\\r\\n\\t\\t sum += center * 0.16/0.98;\\\\n\\\\\\r\\n\\t\\t sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\\\\n\\\\\\r\\n\\t\\t sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\\\\n\\\\\\r\\n\\t\\t sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\\\\n\\\\\\r\\n\\t\\t sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\\\\n\\\\\\r\\n\\t\\t gl_FragColor = u_intensity * sum;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n*/\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/blur\\\", LGraphTextureBlur);\\r\\n\\r\\n\\t// Texture Glow *****************************************\\r\\n\\t//based in https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/\\r\\n\\tfunction LGraphTextureGlow() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"dirt\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"glow\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tenabled: true,\\r\\n\\t\\t\\tintensity: 1,\\r\\n\\t\\t\\tpersistence: 0.99,\\r\\n\\t\\t\\titerations: 16,\\r\\n\\t\\t\\tthreshold: 0,\\r\\n\\t\\t\\tscale: 1,\\r\\n\\t\\t\\tdirt_factor: 0.5,\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\t\\tthis._textures = [];\\r\\n\\t\\tthis._uniforms = {\\r\\n\\t\\t\\tu_intensity: 1,\\r\\n\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\tu_glow_texture: 1,\\r\\n\\t\\t\\tu_threshold: 0,\\r\\n\\t\\t\\tu_texel_size: vec2.create()\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureGlow.title = \\\"Glow\\\";\\r\\n\\tLGraphTextureGlow.desc = \\\"Filters a texture giving it a glow effect\\\";\\r\\n\\tLGraphTextureGlow.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]);\\r\\n\\r\\n\\tLGraphTextureGlow.widgets_info = {\\r\\n\\t\\titerations: {\\r\\n\\t\\t\\ttype: \\\"number\\\",\\r\\n\\t\\t\\tmin: 0,\\r\\n\\t\\t\\tmax: 16,\\r\\n\\t\\t\\tstep: 1,\\r\\n\\t\\t\\tprecision: 0\\r\\n\\t\\t},\\r\\n\\t\\tthreshold: {\\r\\n\\t\\t\\ttype: \\\"number\\\",\\r\\n\\t\\t\\tmin: 0,\\r\\n\\t\\t\\tmax: 10,\\r\\n\\t\\t\\tstep: 0.01,\\r\\n\\t\\t\\tprecision: 2\\r\\n\\t\\t},\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureGlow.prototype.onGetInputs = function() {\\r\\n\\t\\treturn [\\r\\n\\t\\t\\t[\\\"enabled\\\", \\\"boolean\\\"],\\r\\n\\t\\t\\t[\\\"threshold\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"intensity\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"persistence\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"iterations\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"dirt_factor\\\", \\\"number\\\"]\\r\\n\\t\\t];\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureGlow.prototype.onGetOutputs = function() {\\r\\n\\t\\treturn [[\\\"average\\\", \\\"Texture\\\"]];\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureGlow.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this.isAnyOutputConnected()) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tif (\\r\\n\\t\\t\\tthis.properties.precision === LGraphTexture.PASS_THROUGH ||\\r\\n\\t\\t\\tthis.getInputOrProperty(\\\"enabled\\\") === false\\r\\n\\t\\t) {\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar width = tex.width;\\r\\n\\t\\tvar height = tex.height;\\r\\n\\r\\n\\t\\tvar texture_info = {\\r\\n\\t\\t\\tformat: tex.format,\\r\\n\\t\\t\\ttype: tex.type,\\r\\n\\t\\t\\tminFilter: GL.LINEAR,\\r\\n\\t\\t\\tmagFilter: GL.LINEAR,\\r\\n\\t\\t\\twrap: gl.CLAMP_TO_EDGE\\r\\n\\t\\t};\\r\\n\\t\\tvar type = LGraphTexture.getTextureType(\\r\\n\\t\\t\\tthis.properties.precision,\\r\\n\\t\\t\\ttex\\r\\n\\t\\t);\\r\\n\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\t\\tvar textures = this._textures;\\r\\n\\r\\n\\t\\t//cut\\r\\n\\t\\tvar shader = LGraphTextureGlow._cut_shader;\\r\\n\\t\\tif (!shader) {\\r\\n\\t\\t\\tshader = LGraphTextureGlow._cut_shader = new GL.Shader(\\r\\n\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureGlow.cut_pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\r\\n\\t\\tuniforms.u_threshold = this.getInputOrProperty(\\\"threshold\\\");\\r\\n\\t\\tvar currentDestination = (textures[0] = GL.Texture.getTemporary(\\r\\n\\t\\t\\twidth,\\r\\n\\t\\t\\theight,\\r\\n\\t\\t\\ttexture_info\\r\\n\\t\\t));\\r\\n\\t\\ttex.blit(currentDestination, shader.uniforms(uniforms));\\r\\n\\t\\tvar currentSource = currentDestination;\\r\\n\\r\\n\\t\\tvar iterations = this.getInputOrProperty(\\\"iterations\\\");\\r\\n\\t\\titerations = Math.clamp(iterations, 1, 16) | 0;\\r\\n\\t\\tvar texel_size = uniforms.u_texel_size;\\r\\n\\t\\tvar intensity = this.getInputOrProperty(\\\"intensity\\\");\\r\\n\\r\\n\\t\\tuniforms.u_intensity = 1;\\r\\n\\t\\tuniforms.u_delta = this.properties.scale; //1\\r\\n\\r\\n\\t\\t//downscale/upscale shader\\r\\n\\t\\tvar shader = LGraphTextureGlow._shader;\\r\\n\\t\\tif (!shader) {\\r\\n\\t\\t\\tshader = LGraphTextureGlow._shader = new GL.Shader(\\r\\n\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureGlow.scale_pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar i = 1;\\r\\n\\t\\t//downscale\\r\\n\\t\\tfor (; i < iterations; i++) {\\r\\n\\t\\t\\twidth = width >> 1;\\r\\n\\t\\t\\tif ((height | 0) > 1) {\\r\\n\\t\\t\\t\\theight = height >> 1;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif (width < 2) {\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcurrentDestination = textures[i] = GL.Texture.getTemporary(\\r\\n\\t\\t\\t\\twidth,\\r\\n\\t\\t\\t\\theight,\\r\\n\\t\\t\\t\\ttexture_info\\r\\n\\t\\t\\t);\\r\\n\\t\\t\\ttexel_size[0] = 1 / currentSource.width;\\r\\n\\t\\t\\ttexel_size[1] = 1 / currentSource.height;\\r\\n\\t\\t\\tcurrentSource.blit(\\r\\n\\t\\t\\t\\tcurrentDestination,\\r\\n\\t\\t\\t\\tshader.uniforms(uniforms)\\r\\n\\t\\t\\t);\\r\\n\\t\\t\\tcurrentSource = currentDestination;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//average\\r\\n\\t\\tif (this.isOutputConnected(2)) {\\r\\n\\t\\t\\tvar average_texture = this._average_texture;\\r\\n\\t\\t\\tif (\\r\\n\\t\\t\\t\\t!average_texture ||\\r\\n\\t\\t\\t\\taverage_texture.type != tex.type ||\\r\\n\\t\\t\\t\\taverage_texture.format != tex.format\\r\\n\\t\\t\\t) {\\r\\n\\t\\t\\t\\taverage_texture = this._average_texture = new GL.Texture(\\r\\n\\t\\t\\t\\t\\t1,\\r\\n\\t\\t\\t\\t\\t1,\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\ttype: tex.type,\\r\\n\\t\\t\\t\\t\\t\\tformat: tex.format,\\r\\n\\t\\t\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\ttexel_size[0] = 1 / currentSource.width;\\r\\n\\t\\t\\ttexel_size[1] = 1 / currentSource.height;\\r\\n\\t\\t\\tuniforms.u_intensity = intensity;\\r\\n\\t\\t\\tuniforms.u_delta = 1;\\r\\n\\t\\t\\tcurrentSource.blit(average_texture, shader.uniforms(uniforms));\\r\\n\\t\\t\\tthis.setOutputData(2, average_texture);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//upscale and blend\\r\\n\\t\\tgl.enable(gl.BLEND);\\r\\n\\t\\tgl.blendFunc(gl.ONE, gl.ONE);\\r\\n\\t\\tuniforms.u_intensity = this.getInputOrProperty(\\\"persistence\\\");\\r\\n\\t\\tuniforms.u_delta = 0.5;\\r\\n\\r\\n\\t\\tfor (\\r\\n\\t\\t\\ti -= 2;\\r\\n\\t\\t\\ti >= 0;\\r\\n\\t\\t\\ti-- // i-=2 => -1 to point to last element in array, -1 to go to texture above\\r\\n\\t\\t) {\\r\\n\\t\\t\\tcurrentDestination = textures[i];\\r\\n\\t\\t\\ttextures[i] = null;\\r\\n\\t\\t\\ttexel_size[0] = 1 / currentSource.width;\\r\\n\\t\\t\\ttexel_size[1] = 1 / currentSource.height;\\r\\n\\t\\t\\tcurrentSource.blit(\\r\\n\\t\\t\\t\\tcurrentDestination,\\r\\n\\t\\t\\t\\tshader.uniforms(uniforms)\\r\\n\\t\\t\\t);\\r\\n\\t\\t\\tGL.Texture.releaseTemporary(currentSource);\\r\\n\\t\\t\\tcurrentSource = currentDestination;\\r\\n\\t\\t}\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\r\\n\\t\\t//glow\\r\\n\\t\\tif (this.isOutputConnected(1)) {\\r\\n\\t\\t\\tvar glow_texture = this._glow_texture;\\r\\n\\t\\t\\tif (\\r\\n\\t\\t\\t\\t!glow_texture ||\\r\\n\\t\\t\\t\\tglow_texture.width != tex.width ||\\r\\n\\t\\t\\t\\tglow_texture.height != tex.height ||\\r\\n\\t\\t\\t\\tglow_texture.type != type ||\\r\\n\\t\\t\\t\\tglow_texture.format != tex.format\\r\\n\\t\\t\\t) {\\r\\n\\t\\t\\t\\tglow_texture = this._glow_texture = new GL.Texture(\\r\\n\\t\\t\\t\\t\\ttex.width,\\r\\n\\t\\t\\t\\t\\ttex.height,\\r\\n\\t\\t\\t\\t\\t{ type: type, format: tex.format, filter: gl.LINEAR }\\r\\n\\t\\t\\t\\t);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcurrentSource.blit(glow_texture);\\r\\n\\t\\t\\tthis.setOutputData(1, glow_texture);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//final composition\\r\\n\\t\\tif (this.isOutputConnected(0)) {\\r\\n\\t\\t\\tvar final_texture = this._final_texture;\\r\\n\\t\\t\\tif (\\r\\n\\t\\t\\t\\t!final_texture ||\\r\\n\\t\\t\\t\\tfinal_texture.width != tex.width ||\\r\\n\\t\\t\\t\\tfinal_texture.height != tex.height ||\\r\\n\\t\\t\\t\\tfinal_texture.type != type ||\\r\\n\\t\\t\\t\\tfinal_texture.format != tex.format\\r\\n\\t\\t\\t) {\\r\\n\\t\\t\\t\\tfinal_texture = this._final_texture = new GL.Texture(\\r\\n\\t\\t\\t\\t\\ttex.width,\\r\\n\\t\\t\\t\\t\\ttex.height,\\r\\n\\t\\t\\t\\t\\t{ type: type, format: tex.format, filter: gl.LINEAR }\\r\\n\\t\\t\\t\\t);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar dirt_texture = this.getInputData(1);\\r\\n\\t\\t\\tvar dirt_factor = this.getInputOrProperty(\\\"dirt_factor\\\");\\r\\n\\r\\n\\t\\t\\tuniforms.u_intensity = intensity;\\r\\n\\r\\n\\t\\t\\tshader = dirt_texture\\r\\n\\t\\t\\t\\t? LGraphTextureGlow._dirt_final_shader\\r\\n\\t\\t\\t\\t: LGraphTextureGlow._final_shader;\\r\\n\\t\\t\\tif (!shader) {\\r\\n\\t\\t\\t\\tif (dirt_texture) {\\r\\n\\t\\t\\t\\t\\tshader = LGraphTextureGlow._dirt_final_shader = new GL.Shader(\\r\\n\\t\\t\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\t\\t\\tLGraphTextureGlow.final_pixel_shader,\\r\\n\\t\\t\\t\\t\\t\\t{ USE_DIRT: \\\"\\\" }\\r\\n\\t\\t\\t\\t\\t);\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tshader = LGraphTextureGlow._final_shader = new GL.Shader(\\r\\n\\t\\t\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\t\\t\\tLGraphTextureGlow.final_pixel_shader\\r\\n\\t\\t\\t\\t\\t);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tfinal_texture.drawTo(function() {\\r\\n\\t\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\t\\tcurrentSource.bind(1);\\r\\n\\t\\t\\t\\tif (dirt_texture) {\\r\\n\\t\\t\\t\\t\\tshader.setUniform(\\\"u_dirt_factor\\\", dirt_factor);\\r\\n\\t\\t\\t\\t\\tshader.setUniform(\\r\\n\\t\\t\\t\\t\\t\\t\\\"u_dirt_texture\\\",\\r\\n\\t\\t\\t\\t\\t\\tdirt_texture.bind(2)\\r\\n\\t\\t\\t\\t\\t);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tshader.toViewport(uniforms);\\r\\n\\t\\t\\t});\\r\\n\\t\\t\\tthis.setOutputData(0, final_texture);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tGL.Texture.releaseTemporary(currentSource);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureGlow.cut_pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\tuniform float u_threshold;\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\\\\n\\\\\\r\\n\\t}\\\";\\r\\n\\r\\n\\tLGraphTextureGlow.scale_pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\tuniform vec2 u_texel_size;\\\\n\\\\\\r\\n\\tuniform float u_delta;\\\\n\\\\\\r\\n\\tuniform float u_intensity;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvec4 sampleBox(vec2 uv) {\\\\n\\\\\\r\\n\\t\\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\\\\n\\\\\\r\\n\\t\\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\\\\n\\\\\\r\\n\\t\\treturn s * 0.25;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tgl_FragColor = u_intensity * sampleBox( v_coord );\\\\n\\\\\\r\\n\\t}\\\";\\r\\n\\r\\n\\tLGraphTextureGlow.final_pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\tuniform sampler2D u_glow_texture;\\\\n\\\\\\r\\n\\t#ifdef USE_DIRT\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_dirt_texture;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\tuniform vec2 u_texel_size;\\\\n\\\\\\r\\n\\tuniform float u_delta;\\\\n\\\\\\r\\n\\tuniform float u_intensity;\\\\n\\\\\\r\\n\\tuniform float u_dirt_factor;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvec4 sampleBox(vec2 uv) {\\\\n\\\\\\r\\n\\t\\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\\\\n\\\\\\r\\n\\t\\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\\\\n\\\\\\r\\n\\t\\treturn s * 0.25;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tvec4 glow = sampleBox( v_coord );\\\\n\\\\\\r\\n\\t\\t#ifdef USE_DIRT\\\\n\\\\\\r\\n\\t\\t\\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\\\\n\\\\\\r\\n\\t}\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/glow\\\", LGraphTextureGlow);\\r\\n\\r\\n\\t// Texture Filter *****************************************\\r\\n\\tfunction LGraphTextureKuwaharaFilter() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"Filtered\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = { intensity: 1, radius: 5 };\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureKuwaharaFilter.title = \\\"Kuwahara Filter\\\";\\r\\n\\tLGraphTextureKuwaharaFilter.desc =\\r\\n\\t\\t\\\"Filters a texture giving an artistic oil canvas painting\\\";\\r\\n\\r\\n\\tLGraphTextureKuwaharaFilter.max_radius = 10;\\r\\n\\tLGraphTextureKuwaharaFilter._shaders = [];\\r\\n\\r\\n\\tLGraphTextureKuwaharaFilter.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tvar temp = this._temp_texture;\\r\\n\\r\\n\\t\\tif (\\r\\n\\t\\t\\t!temp ||\\r\\n\\t\\t\\ttemp.width != tex.width ||\\r\\n\\t\\t\\ttemp.height != tex.height ||\\r\\n\\t\\t\\ttemp.type != tex.type\\r\\n\\t\\t) {\\r\\n\\t\\t\\tthis._temp_texture = new GL.Texture(tex.width, tex.height, {\\r\\n\\t\\t\\t\\ttype: tex.type,\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//iterations\\r\\n\\t\\tvar radius = this.properties.radius;\\r\\n\\t\\tradius = Math.min(\\r\\n\\t\\t\\tMath.floor(radius),\\r\\n\\t\\t\\tLGraphTextureKuwaharaFilter.max_radius\\r\\n\\t\\t);\\r\\n\\t\\tif (radius == 0) {\\r\\n\\t\\t\\t//skip blurring\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar intensity = this.properties.intensity;\\r\\n\\r\\n\\t\\t//blur sometimes needs an aspect correction\\r\\n\\t\\tvar aspect = LiteGraph.camera_aspect;\\r\\n\\t\\tif (!aspect && window.gl !== undefined) {\\r\\n\\t\\t\\taspect = gl.canvas.height / gl.canvas.width;\\r\\n\\t\\t}\\r\\n\\t\\tif (!aspect) {\\r\\n\\t\\t\\taspect = 1;\\r\\n\\t\\t}\\r\\n\\t\\taspect = this.properties.preserve_aspect ? aspect : 1;\\r\\n\\r\\n\\t\\tif (!LGraphTextureKuwaharaFilter._shaders[radius]) {\\r\\n\\t\\t\\tLGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader(\\r\\n\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureKuwaharaFilter.pixel_shader,\\r\\n\\t\\t\\t\\t{ RADIUS: radius.toFixed(0) }\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar shader = LGraphTextureKuwaharaFilter._shaders[radius];\\r\\n\\t\\tvar mesh = GL.Mesh.getScreenQuad();\\r\\n\\t\\ttex.bind(0);\\r\\n\\r\\n\\t\\tthis._temp_texture.drawTo(function() {\\r\\n\\t\\t\\tshader\\r\\n\\t\\t\\t\\t.uniforms({\\r\\n\\t\\t\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\t\\t\\tu_intensity: intensity,\\r\\n\\t\\t\\t\\t\\tu_resolution: [tex.width, tex.height],\\r\\n\\t\\t\\t\\t\\tu_iResolution: [1 / tex.width, 1 / tex.height]\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t.draw(mesh);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._temp_texture);\\r\\n\\t};\\r\\n\\r\\n\\t//from https://www.shadertoy.com/view/MsXSz4\\r\\n\\tLGraphTextureKuwaharaFilter.pixel_shader =\\r\\n\\t\\t\\\"\\\\n\\\\\\r\\nprecision highp float;\\\\n\\\\\\r\\nvarying vec2 v_coord;\\\\n\\\\\\r\\nuniform sampler2D u_texture;\\\\n\\\\\\r\\nuniform float u_intensity;\\\\n\\\\\\r\\nuniform vec2 u_resolution;\\\\n\\\\\\r\\nuniform vec2 u_iResolution;\\\\n\\\\\\r\\n#ifndef RADIUS\\\\n\\\\\\r\\n\\t#define RADIUS 7\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\nvoid main() {\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\tconst int radius = RADIUS;\\\\n\\\\\\r\\n\\tvec2 fragCoord = v_coord;\\\\n\\\\\\r\\n\\tvec2 src_size = u_iResolution;\\\\n\\\\\\r\\n\\tvec2 uv = v_coord;\\\\n\\\\\\r\\n\\tfloat n = float((radius + 1) * (radius + 1));\\\\n\\\\\\r\\n\\tint i;\\\\n\\\\\\r\\n\\tint j;\\\\n\\\\\\r\\n\\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\\\\n\\\\\\r\\n\\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\\\\n\\\\\\r\\n\\tvec3 c;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfor (int j = -radius; j <= 0; ++j) {\\\\n\\\\\\r\\n\\t\\tfor (int i = -radius; i <= 0; ++i) {\\\\n\\\\\\r\\n\\t\\t\\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\\\\n\\\\\\r\\n\\t\\t\\tm0 += c;\\\\n\\\\\\r\\n\\t\\t\\ts0 += c * c;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfor (int j = -radius; j <= 0; ++j) {\\\\n\\\\\\r\\n\\t\\tfor (int i = 0; i <= radius; ++i) {\\\\n\\\\\\r\\n\\t\\t\\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\\\\n\\\\\\r\\n\\t\\t\\tm1 += c;\\\\n\\\\\\r\\n\\t\\t\\ts1 += c * c;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfor (int j = 0; j <= radius; ++j) {\\\\n\\\\\\r\\n\\t\\tfor (int i = 0; i <= radius; ++i) {\\\\n\\\\\\r\\n\\t\\t\\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\\\\n\\\\\\r\\n\\t\\t\\tm2 += c;\\\\n\\\\\\r\\n\\t\\t\\ts2 += c * c;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfor (int j = 0; j <= radius; ++j) {\\\\n\\\\\\r\\n\\t\\tfor (int i = -radius; i <= 0; ++i) {\\\\n\\\\\\r\\n\\t\\t\\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\\\\n\\\\\\r\\n\\t\\t\\tm3 += c;\\\\n\\\\\\r\\n\\t\\t\\ts3 += c * c;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat min_sigma2 = 1e+2;\\\\n\\\\\\r\\n\\tm0 /= n;\\\\n\\\\\\r\\n\\ts0 = abs(s0 / n - m0 * m0);\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat sigma2 = s0.r + s0.g + s0.b;\\\\n\\\\\\r\\n\\tif (sigma2 < min_sigma2) {\\\\n\\\\\\r\\n\\t\\tmin_sigma2 = sigma2;\\\\n\\\\\\r\\n\\t\\tgl_FragColor = vec4(m0, 1.0);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tm1 /= n;\\\\n\\\\\\r\\n\\ts1 = abs(s1 / n - m1 * m1);\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tsigma2 = s1.r + s1.g + s1.b;\\\\n\\\\\\r\\n\\tif (sigma2 < min_sigma2) {\\\\n\\\\\\r\\n\\t\\tmin_sigma2 = sigma2;\\\\n\\\\\\r\\n\\t\\tgl_FragColor = vec4(m1, 1.0);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tm2 /= n;\\\\n\\\\\\r\\n\\ts2 = abs(s2 / n - m2 * m2);\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tsigma2 = s2.r + s2.g + s2.b;\\\\n\\\\\\r\\n\\tif (sigma2 < min_sigma2) {\\\\n\\\\\\r\\n\\t\\tmin_sigma2 = sigma2;\\\\n\\\\\\r\\n\\t\\tgl_FragColor = vec4(m2, 1.0);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tm3 /= n;\\\\n\\\\\\r\\n\\ts3 = abs(s3 / n - m3 * m3);\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tsigma2 = s3.r + s3.g + s3.b;\\\\n\\\\\\r\\n\\tif (sigma2 < min_sigma2) {\\\\n\\\\\\r\\n\\t\\tmin_sigma2 = sigma2;\\\\n\\\\\\r\\n\\t\\tgl_FragColor = vec4(m3, 1.0);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\r\\n\\t\\t\\\"texture/kuwahara\\\",\\r\\n\\t\\tLGraphTextureKuwaharaFilter\\r\\n\\t);\\r\\n\\r\\n\\t// Texture *****************************************\\r\\n\\tfunction LGraphTextureXDoGFilter() {\\r\\n\\t\\tthis.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"Filtered\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tsigma: 1.4,\\r\\n\\t\\t\\tk: 1.6,\\r\\n\\t\\t\\tp: 21.7,\\r\\n\\t\\t\\tepsilon: 79,\\r\\n\\t\\t\\tphi: 0.017\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureXDoGFilter.title = \\\"XDoG Filter\\\";\\r\\n\\tLGraphTextureXDoGFilter.desc =\\r\\n\\t\\t\\\"Filters a texture giving an artistic ink style\\\";\\r\\n\\r\\n\\tLGraphTextureXDoGFilter.max_radius = 10;\\r\\n\\tLGraphTextureXDoGFilter._shaders = [];\\r\\n\\r\\n\\tLGraphTextureXDoGFilter.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tvar temp = this._temp_texture;\\r\\n\\t\\tif (\\r\\n\\t\\t\\t!temp ||\\r\\n\\t\\t\\ttemp.width != tex.width ||\\r\\n\\t\\t\\ttemp.height != tex.height ||\\r\\n\\t\\t\\ttemp.type != tex.type\\r\\n\\t\\t) {\\r\\n\\t\\t\\tthis._temp_texture = new GL.Texture(tex.width, tex.height, {\\r\\n\\t\\t\\t\\ttype: tex.type,\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!LGraphTextureXDoGFilter._xdog_shader) {\\r\\n\\t\\t\\tLGraphTextureXDoGFilter._xdog_shader = new GL.Shader(\\r\\n\\t\\t\\t\\tShader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureXDoGFilter.xdog_pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\t\\tvar shader = LGraphTextureXDoGFilter._xdog_shader;\\r\\n\\t\\tvar mesh = GL.Mesh.getScreenQuad();\\r\\n\\r\\n\\t\\tvar sigma = this.properties.sigma;\\r\\n\\t\\tvar k = this.properties.k;\\r\\n\\t\\tvar p = this.properties.p;\\r\\n\\t\\tvar epsilon = this.properties.epsilon;\\r\\n\\t\\tvar phi = this.properties.phi;\\r\\n\\t\\ttex.bind(0);\\r\\n\\t\\tthis._temp_texture.drawTo(function() {\\r\\n\\t\\t\\tshader\\r\\n\\t\\t\\t\\t.uniforms({\\r\\n\\t\\t\\t\\t\\tsrc: 0,\\r\\n\\t\\t\\t\\t\\tsigma: sigma,\\r\\n\\t\\t\\t\\t\\tk: k,\\r\\n\\t\\t\\t\\t\\tp: p,\\r\\n\\t\\t\\t\\t\\tepsilon: epsilon,\\r\\n\\t\\t\\t\\t\\tphi: phi,\\r\\n\\t\\t\\t\\t\\tcvsWidth: tex.width,\\r\\n\\t\\t\\t\\t\\tcvsHeight: tex.height\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t.draw(mesh);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._temp_texture);\\r\\n\\t};\\r\\n\\r\\n\\t//from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js\\r\\n\\tLGraphTextureXDoGFilter.xdog_pixel_shader =\\r\\n\\t\\t\\\"\\\\n\\\\\\r\\nprecision highp float;\\\\n\\\\\\r\\nuniform sampler2D src;\\\\n\\\\n\\\\\\r\\nuniform float cvsHeight;\\\\n\\\\\\r\\nuniform float cvsWidth;\\\\n\\\\n\\\\\\r\\nuniform float sigma;\\\\n\\\\\\r\\nuniform float k;\\\\n\\\\\\r\\nuniform float p;\\\\n\\\\\\r\\nuniform float epsilon;\\\\n\\\\\\r\\nuniform float phi;\\\\n\\\\\\r\\nvarying vec2 v_coord;\\\\n\\\\n\\\\\\r\\nfloat cosh(float val)\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n\\tfloat tmp = exp(val);\\\\n\\\\\\r\\n\\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\\\\n\\\\\\r\\n\\treturn cosH;\\\\n\\\\\\r\\n}\\\\n\\\\n\\\\\\r\\nfloat tanh(float val)\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n\\tfloat tmp = exp(val);\\\\n\\\\\\r\\n\\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\\\\n\\\\\\r\\n\\treturn tanH;\\\\n\\\\\\r\\n}\\\\n\\\\n\\\\\\r\\nfloat sinh(float val)\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n\\tfloat tmp = exp(val);\\\\n\\\\\\r\\n\\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\\\\n\\\\\\r\\n\\treturn sinH;\\\\n\\\\\\r\\n}\\\\n\\\\n\\\\\\r\\nvoid main(void){\\\\n\\\\\\r\\n\\tvec3 destColor = vec3(0.0);\\\\n\\\\\\r\\n\\tfloat tFrag = 1.0 / cvsHeight;\\\\n\\\\\\r\\n\\tfloat sFrag = 1.0 / cvsWidth;\\\\n\\\\\\r\\n\\tvec2 Frag = vec2(sFrag,tFrag);\\\\n\\\\\\r\\n\\tvec2 uv = gl_FragCoord.st;\\\\n\\\\\\r\\n\\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\\\\n\\\\\\r\\n\\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\\\\n\\\\\\r\\n\\tint halfWidth = int(ceil( 1.0 * sigma * k ));\\\\n\\\\n\\\\\\r\\n\\tconst int MAX_NUM_ITERATION = 99999;\\\\n\\\\\\r\\n\\tvec2 sum = vec2(0.0);\\\\n\\\\\\r\\n\\tvec2 norm = vec2(0.0);\\\\n\\\\n\\\\\\r\\n\\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\\\\n\\\\\\r\\n\\t\\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\\\\n\\\\\\r\\n\\t\\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\\\\n\\\\n\\\\\\r\\n\\t\\tfloat d = length(vec2(i,j));\\\\n\\\\\\r\\n\\t\\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\t\\texp( -d * d / twoSigmaRSquared ));\\\\n\\\\n\\\\\\r\\n\\t\\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\\\\n\\\\n\\\\\\r\\n\\t\\tnorm += kernel;\\\\n\\\\\\r\\n\\t\\tsum += kernel * L;\\\\n\\\\\\r\\n\\t}\\\\n\\\\n\\\\\\r\\n\\tsum /= norm;\\\\n\\\\n\\\\\\r\\n\\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\\\\n\\\\\\r\\n\\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\\\\n\\\\\\r\\n\\tdestColor = vec3(edge);\\\\n\\\\\\r\\n\\tgl_FragColor = vec4(destColor, 1.0);\\\\n\\\\\\r\\n}\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/xDoG\\\", LGraphTextureXDoGFilter);\\r\\n\\r\\n\\t// Texture Webcam *****************************************\\r\\n\\tfunction LGraphTextureWebcam() {\\r\\n\\t\\tthis.addOutput(\\\"Webcam\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = { texture_name: \\\"\\\", facingMode: \\\"user\\\" };\\r\\n\\t\\tthis.boxcolor = \\\"black\\\";\\r\\n\\t\\tthis.version = 0;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureWebcam.title = \\\"Webcam\\\";\\r\\n\\tLGraphTextureWebcam.desc = \\\"Webcam texture\\\";\\r\\n\\r\\n\\tLGraphTextureWebcam.is_webcam_open = false;\\r\\n\\r\\n\\tLGraphTextureWebcam.prototype.openStream = function() {\\r\\n\\t\\tif (!navigator.getUserMedia) {\\r\\n\\t\\t\\t//console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags');\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._waiting_confirmation = true;\\r\\n\\r\\n\\t\\t// Not showing vendor prefixes.\\r\\n\\t\\tvar constraints = {\\r\\n\\t\\t\\taudio: false,\\r\\n\\t\\t\\tvideo: { facingMode: this.properties.facingMode }\\r\\n\\t\\t};\\r\\n\\t\\tnavigator.mediaDevices\\r\\n\\t\\t\\t.getUserMedia(constraints)\\r\\n\\t\\t\\t.then(this.streamReady.bind(this))\\r\\n\\t\\t\\t.catch(onFailSoHard);\\r\\n\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tfunction onFailSoHard(e) {\\r\\n\\t\\t\\tLGraphTextureWebcam.is_webcam_open = false;\\r\\n\\t\\t\\tconsole.log(\\\"Webcam rejected\\\", e);\\r\\n\\t\\t\\tthat._webcam_stream = false;\\r\\n\\t\\t\\tthat.boxcolor = \\\"red\\\";\\r\\n\\t\\t\\tthat.trigger(\\\"stream_error\\\");\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureWebcam.prototype.closeStream = function() {\\r\\n\\t\\tif (this._webcam_stream) {\\r\\n\\t\\t\\tvar tracks = this._webcam_stream.getTracks();\\r\\n\\t\\t\\tif (tracks.length) {\\r\\n\\t\\t\\t\\tfor (var i = 0; i < tracks.length; ++i) {\\r\\n\\t\\t\\t\\t\\ttracks[i].stop();\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tLGraphTextureWebcam.is_webcam_open = false;\\r\\n\\t\\t\\tthis._webcam_stream = null;\\r\\n\\t\\t\\tthis._video = null;\\r\\n\\t\\t\\tthis.boxcolor = \\\"black\\\";\\r\\n\\t\\t\\tthis.trigger(\\\"stream_closed\\\");\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureWebcam.prototype.streamReady = function(localMediaStream) {\\r\\n\\t\\tthis._webcam_stream = localMediaStream;\\r\\n\\t\\t//this._waiting_confirmation = false;\\r\\n\\t\\tthis.boxcolor = \\\"green\\\";\\r\\n\\t\\tvar video = this._video;\\r\\n\\t\\tif (!video) {\\r\\n\\t\\t\\tvideo = document.createElement(\\\"video\\\");\\r\\n\\t\\t\\tvideo.autoplay = true;\\r\\n\\t\\t\\tvideo.srcObject = localMediaStream;\\r\\n\\t\\t\\tthis._video = video;\\r\\n\\t\\t\\t//document.body.appendChild( video ); //debug\\r\\n\\t\\t\\t//when video info is loaded (size and so)\\r\\n\\t\\t\\tvideo.onloadedmetadata = function(e) {\\r\\n\\t\\t\\t\\t// Ready to go. Do some stuff.\\r\\n\\t\\t\\t\\tLGraphTextureWebcam.is_webcam_open = true;\\r\\n\\t\\t\\t\\tconsole.log(e);\\r\\n\\t\\t\\t};\\r\\n\\t\\t}\\r\\n\\t\\tthis.trigger(\\\"stream_ready\\\", video);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureWebcam.prototype.onPropertyChanged = function(\\r\\n\\t\\tname,\\r\\n\\t\\tvalue\\r\\n\\t) {\\r\\n\\t\\tif (name == \\\"facingMode\\\") {\\r\\n\\t\\t\\tthis.properties.facingMode = value;\\r\\n\\t\\t\\tthis.closeStream();\\r\\n\\t\\t\\tthis.openStream();\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureWebcam.prototype.onRemoved = function() {\\r\\n\\t\\tif (!this._webcam_stream) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar tracks = this._webcam_stream.getTracks();\\r\\n\\t\\tif (tracks.length) {\\r\\n\\t\\t\\tfor (var i = 0; i < tracks.length; ++i) {\\r\\n\\t\\t\\t\\ttracks[i].stop();\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._webcam_stream = null;\\r\\n\\t\\tthis._video = null;\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureWebcam.prototype.onDrawBackground = function(ctx) {\\r\\n\\t\\tif (this.flags.collapsed || this.size[1] <= 20) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this._video) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//render to graph canvas\\r\\n\\t\\tctx.save();\\r\\n\\t\\tif (!ctx.webgl) {\\r\\n\\t\\t\\t//reverse image\\r\\n\\t\\t\\tctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]);\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tif (this._video_texture) {\\r\\n\\t\\t\\t\\tctx.drawImage(\\r\\n\\t\\t\\t\\t\\tthis._video_texture,\\r\\n\\t\\t\\t\\t\\t0,\\r\\n\\t\\t\\t\\t\\t0,\\r\\n\\t\\t\\t\\t\\tthis.size[0],\\r\\n\\t\\t\\t\\t\\tthis.size[1]\\r\\n\\t\\t\\t\\t);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tctx.restore();\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureWebcam.prototype.onExecute = function() {\\r\\n\\t\\tif (this._webcam_stream == null && !this._waiting_confirmation) {\\r\\n\\t\\t\\tthis.openStream();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this._video || !this._video.videoWidth) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar width = this._video.videoWidth;\\r\\n\\t\\tvar height = this._video.videoHeight;\\r\\n\\r\\n\\t\\tvar temp = this._video_texture;\\r\\n\\t\\tif (!temp || temp.width != width || temp.height != height) {\\r\\n\\t\\t\\tthis._video_texture = new GL.Texture(width, height, {\\r\\n\\t\\t\\t\\tformat: gl.RGB,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._video_texture.uploadImage(this._video);\\r\\n\\t\\tthis._video_texture.version = ++this.version;\\r\\n\\r\\n\\t\\tif (this.properties.texture_name) {\\r\\n\\t\\t\\tvar container = LGraphTexture.getTexturesContainer();\\r\\n\\t\\t\\tcontainer[this.properties.texture_name] = this._video_texture;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._video_texture);\\r\\n\\t\\tfor (var i = 1; i < this.outputs.length; ++i) {\\r\\n\\t\\t\\tif (!this.outputs[i]) {\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tswitch (this.outputs[i].name) {\\r\\n\\t\\t\\t\\tcase \\\"width\\\":\\r\\n\\t\\t\\t\\t\\tthis.setOutputData(i, this._video.videoWidth);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"height\\\":\\r\\n\\t\\t\\t\\t\\tthis.setOutputData(i, this._video.videoHeight);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureWebcam.prototype.onGetOutputs = function() {\\r\\n\\t\\treturn [\\r\\n\\t\\t\\t[\\\"width\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"height\\\", \\\"number\\\"],\\r\\n\\t\\t\\t[\\\"stream_ready\\\", LiteGraph.EVENT],\\r\\n\\t\\t\\t[\\\"stream_closed\\\", LiteGraph.EVENT],\\r\\n\\t\\t\\t[\\\"stream_error\\\", LiteGraph.EVENT]\\r\\n\\t\\t];\\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/webcam\\\", LGraphTextureWebcam);\\r\\n\\r\\n\\t//from https://github.com/spite/Wagner\\r\\n\\tfunction LGraphLensFX() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"f\\\", \\\"number\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tenabled: true,\\r\\n\\t\\t\\tfactor: 1,\\r\\n\\t\\t\\tprecision: LGraphTexture.LOW\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis._uniforms = { u_texture: 0, u_factor: 1 };\\r\\n\\t}\\r\\n\\r\\n\\tLGraphLensFX.title = \\\"Lens FX\\\";\\r\\n\\tLGraphLensFX.desc = \\\"distortion and chromatic aberration\\\";\\r\\n\\r\\n\\tLGraphLensFX.widgets_info = {\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphLensFX.prototype.onGetInputs = function() {\\r\\n\\t\\treturn [[\\\"enabled\\\", \\\"boolean\\\"]];\\r\\n\\t};\\r\\n\\r\\n\\tLGraphLensFX.prototype.onExecute = function() {\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tif (\\r\\n\\t\\t\\tthis.properties.precision === LGraphTexture.PASS_THROUGH ||\\r\\n\\t\\t\\tthis.getInputOrProperty(\\\"enabled\\\") === false\\r\\n\\t\\t) {\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar temp = this._temp_texture;\\r\\n\\t\\tif (\\r\\n\\t\\t\\t!temp ||\\r\\n\\t\\t\\ttemp.width != tex.width ||\\r\\n\\t\\t\\ttemp.height != tex.height ||\\r\\n\\t\\t\\ttemp.type != tex.type\\r\\n\\t\\t) {\\r\\n\\t\\t\\ttemp = this._temp_texture = new GL.Texture(\\r\\n\\t\\t\\t\\ttex.width,\\r\\n\\t\\t\\t\\ttex.height,\\r\\n\\t\\t\\t\\t{ type: tex.type, format: gl.RGBA, filter: gl.LINEAR }\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar shader = LGraphLensFX._shader;\\r\\n\\t\\tif (!shader) {\\r\\n\\t\\t\\tshader = LGraphLensFX._shader = new GL.Shader(\\r\\n\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphLensFX.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar factor = this.getInputData(1);\\r\\n\\t\\tif (factor == null) {\\r\\n\\t\\t\\tfactor = this.properties.factor;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\t\\tuniforms.u_factor = factor;\\r\\n\\r\\n\\t\\t//apply shader\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\t\\ttemp.drawTo(function() {\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tshader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad());\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, temp);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphLensFX.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform float u_factor;\\\\n\\\\\\r\\n\\t\\tvec2 barrelDistortion(vec2 coord, float amt) {\\\\n\\\\\\r\\n\\t\\t\\tvec2 cc = coord - 0.5;\\\\n\\\\\\r\\n\\t\\t\\tfloat dist = dot(cc, cc);\\\\n\\\\\\r\\n\\t\\t\\treturn coord + cc * dist * amt;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tfloat sat( float t )\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\treturn clamp( t, 0.0, 1.0 );\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tfloat linterp( float t ) {\\\\n\\\\\\r\\n\\t\\t\\treturn sat( 1.0 - abs( 2.0*t - 1.0 ) );\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tfloat remap( float t, float a, float b ) {\\\\n\\\\\\r\\n\\t\\t\\treturn sat( (t - a) / (b - a) );\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvec4 spectrum_offset( float t ) {\\\\n\\\\\\r\\n\\t\\t\\tvec4 ret;\\\\n\\\\\\r\\n\\t\\t\\tfloat lo = step(t,0.5);\\\\n\\\\\\r\\n\\t\\t\\tfloat hi = 1.0-lo;\\\\n\\\\\\r\\n\\t\\t\\tfloat w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\\\\n\\\\\\r\\n\\t\\t\\tret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\t\\treturn pow( ret, vec4(1.0/2.2) );\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tconst float max_distort = 2.2;\\\\n\\\\\\r\\n\\t\\tconst int num_iter = 12;\\\\n\\\\\\r\\n\\t\\tconst float reci_num_iter_f = 1.0 / float(num_iter);\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main()\\\\n\\\\\\r\\n\\t\\t{\\t\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv=v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvec4 sumcol = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t\\tvec4 sumw = vec4(0.0);\\t\\\\n\\\\\\r\\n\\t\\t\\tfor ( int i=0; i= res)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tbreak;\\\\n\\\\\\r\\n\\t\\t\\t\\tiCount++;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\tfloat nf = n/normK;\\\\n\\\\\\r\\n\\t\\t\\treturn nf*nf*nf*nf;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t}\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/perlin\\\", LGraphTexturePerlin);\\r\\n\\r\\n\\tfunction LGraphTextureCanvas2D() {\\r\\n\\t\\tthis.addInput(\\\"v\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tcode: \\\"\\\",\\r\\n\\t\\t\\twidth: 512,\\r\\n\\t\\t\\theight: 512,\\r\\n\\t\\t\\tclear: true,\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT,\\r\\n\\t\\t\\tuse_html_canvas: false\\r\\n\\t\\t};\\r\\n\\t\\tthis._func = null;\\r\\n\\t\\tthis._temp_texture = null;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureCanvas2D.title = \\\"Canvas2D\\\";\\r\\n\\tLGraphTextureCanvas2D.desc = \\\"Executes Canvas2D code inside a texture or the viewport.\\\";\\r\\n\\tLGraphTextureCanvas2D.help = \\\"Set width and height to 0 to match viewport size.\\\";\\r\\n\\r\\n\\tLGraphTextureCanvas2D.widgets_info = {\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES },\\r\\n\\t\\tcode: { type: \\\"code\\\" },\\r\\n\\t\\twidth: { type: \\\"Number\\\", precision: 0, step: 1 },\\r\\n\\t\\theight: { type: \\\"Number\\\", precision: 0, step: 1 }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) {\\r\\n\\t\\tif (name == \\\"code\\\" )\\r\\n\\t\\t\\tthis.compileCode( value );\\r\\n\\t}\\r\\n\\t\\r\\n\\tLGraphTextureCanvas2D.prototype.compileCode = function( code ) {\\r\\n\\t\\tthis._func = null;\\r\\n\\t\\tif( !LiteGraph.allow_scripts )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tthis._func = new Function( \\\"canvas\\\", \\\"ctx\\\", \\\"time\\\", \\\"script\\\",\\\"v\\\", code );\\r\\n\\t\\t\\tthis.boxcolor = \\\"#00FF00\\\";\\r\\n\\t\\t} catch (err) {\\r\\n\\t\\t\\tthis.boxcolor = \\\"#FF0000\\\";\\r\\n\\t\\t\\tconsole.error(\\\"Error parsing script\\\");\\r\\n\\t\\t\\tconsole.error(err);\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureCanvas2D.prototype.onExecute = function() {\\r\\n\\t\\tvar func = this._func;\\r\\n\\t\\tif (!func || !this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tthis.executeDraw( func );\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureCanvas2D.prototype.executeDraw = function( func_context ) {\\r\\n\\r\\n\\t\\tvar width = this.properties.width || gl.canvas.width;\\r\\n\\t\\tvar height = this.properties.height || gl.canvas.height;\\r\\n\\t\\tvar temp = this._temp_texture;\\r\\n\\t\\tvar type = LGraphTexture.getTextureType( this.properties.precision );\\r\\n\\t\\tif (!temp || temp.width != width || temp.height != height || temp.type != type ) {\\r\\n\\t\\t\\ttemp = this._temp_texture = new GL.Texture(width, height, {\\r\\n\\t\\t\\t\\tformat: gl.RGBA,\\r\\n\\t\\t\\t\\tfilter: gl.LINEAR,\\r\\n\\t\\t\\t\\ttype: type\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar v = this.getInputData(0);\\r\\n\\r\\n\\t\\tvar properties = this.properties;\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tvar time = this.graph.getTime();\\r\\n\\t\\tvar ctx = gl;\\r\\n\\t\\tvar canvas = gl.canvas;\\r\\n\\t\\tif( this.properties.use_html_canvas || !global.enableWebGLCanvas )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!this._canvas)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcanvas = this._canvas = createCanvas(width.height);\\r\\n\\t\\t\\t\\tctx = this._ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcanvas = this._canvas;\\r\\n\\t\\t\\t\\tctx = this._ctx;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcanvas.width = width;\\r\\n\\t\\t\\tcanvas.height = height;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(ctx == gl) //using Canvas2DtoWebGL\\r\\n\\t\\t\\ttemp.drawTo(function() {\\r\\n\\t\\t\\t\\tgl.start2D();\\r\\n\\t\\t\\t\\tif(properties.clear)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tgl.clearColor(0,0,0,0);\\r\\n\\t\\t\\t\\t\\tgl.clear( gl.COLOR_BUFFER_BIT );\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\tif (func_context.draw) {\\r\\n\\t\\t\\t\\t\\t\\tfunc_context.draw.call(that, canvas, ctx, time, func_context, v);\\r\\n\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\tfunc_context.call(that, canvas, ctx, time, func_context,v);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tthat.boxcolor = \\\"#00FF00\\\";\\r\\n\\t\\t\\t\\t} catch (err) {\\r\\n\\t\\t\\t\\t\\tthat.boxcolor = \\\"#FF0000\\\";\\r\\n\\t\\t\\t\\t\\tconsole.error(\\\"Error executing script\\\");\\r\\n\\t\\t\\t\\t\\tconsole.error(err);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tgl.finish2D();\\r\\n\\t\\t\\t});\\r\\n\\t\\telse //rendering to offscren canvas and uploading to texture\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(properties.clear)\\r\\n\\t\\t\\t\\tctx.clearRect(0,0,canvas.width,canvas.height);\\r\\n\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tif (func_context.draw) {\\r\\n\\t\\t\\t\\t\\tfunc_context.draw.call(this, canvas, ctx, time, func_context, v);\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tfunc_context.call(this, canvas, ctx, time, func_context,v);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tthis.boxcolor = \\\"#00FF00\\\";\\r\\n\\t\\t\\t} catch (err) {\\r\\n\\t\\t\\t\\tthis.boxcolor = \\\"#FF0000\\\";\\r\\n\\t\\t\\t\\tconsole.error(\\\"Error executing script\\\");\\r\\n\\t\\t\\t\\tconsole.error(err);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\ttemp.uploadImage( canvas );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setOutputData(0, temp);\\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/canvas2D\\\", LGraphTextureCanvas2D);\\r\\n\\r\\n\\t// To do chroma keying *****************\\r\\n\\r\\n\\tfunction LGraphTextureMatte() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"Texture\\\");\\r\\n\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tkey_color: vec3.fromValues(0, 1, 0),\\r\\n\\t\\t\\tthreshold: 0.8,\\r\\n\\t\\t\\tslope: 0.2,\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureMatte.title = \\\"Matte\\\";\\r\\n\\tLGraphTextureMatte.desc = \\\"Extracts background\\\";\\r\\n\\r\\n\\tLGraphTextureMatte.widgets_info = {\\r\\n\\t\\tkey_color: { widget: \\\"color\\\" },\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureMatte.prototype.onExecute = function() {\\r\\n\\t\\tif (!this.isOutputConnected(0)) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t} //saves work\\r\\n\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\r\\n\\t\\tif (this.properties.precision === LGraphTexture.PASS_THROUGH) {\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!tex) {\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._tex = LGraphTexture.getTargetTexture(\\r\\n\\t\\t\\ttex,\\r\\n\\t\\t\\tthis._tex,\\r\\n\\t\\t\\tthis.properties.precision\\r\\n\\t\\t);\\r\\n\\r\\n\\t\\tgl.disable(gl.BLEND);\\r\\n\\t\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\r\\n\\t\\tif (!this._uniforms) {\\r\\n\\t\\t\\tthis._uniforms = {\\r\\n\\t\\t\\t\\tu_texture: 0,\\r\\n\\t\\t\\t\\tu_key_color: this.properties.key_color,\\r\\n\\t\\t\\t\\tu_threshold: 1,\\r\\n\\t\\t\\t\\tu_slope: 1\\r\\n\\t\\t\\t};\\r\\n\\t\\t}\\r\\n\\t\\tvar uniforms = this._uniforms;\\r\\n\\r\\n\\t\\tvar mesh = Mesh.getScreenQuad();\\r\\n\\t\\tvar shader = LGraphTextureMatte._shader;\\r\\n\\t\\tif (!shader) {\\r\\n\\t\\t\\tshader = LGraphTextureMatte._shader = new GL.Shader(\\r\\n\\t\\t\\t\\tGL.Shader.SCREEN_VERTEX_SHADER,\\r\\n\\t\\t\\t\\tLGraphTextureMatte.pixel_shader\\r\\n\\t\\t\\t);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tuniforms.u_key_color = this.properties.key_color;\\r\\n\\t\\tuniforms.u_threshold = this.properties.threshold;\\r\\n\\t\\tuniforms.u_slope = this.properties.slope;\\r\\n\\r\\n\\t\\tthis._tex.drawTo(function() {\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tshader.uniforms(uniforms).draw(mesh);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._tex);\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureMatte.pixel_shader =\\r\\n\\t\\t\\\"precision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform vec3 u_key_color;\\\\n\\\\\\r\\n\\t\\tuniform float u_threshold;\\\\n\\\\\\r\\n\\t\\tuniform float u_slope;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec3 color = texture2D( u_texture, v_coord ).xyz;\\\\n\\\\\\r\\n\\t\\t\\tfloat diff = length( normalize(color) - normalize(u_key_color) );\\\\n\\\\\\r\\n\\t\\t\\tfloat edge = u_threshold * (1.0 - u_slope);\\\\n\\\\\\r\\n\\t\\t\\tfloat alpha = smoothstep( edge, u_threshold, diff);\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = vec4( color, alpha );\\\\n\\\\\\r\\n\\t\\t}\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/matte\\\", LGraphTextureMatte);\\r\\n\\r\\n\\t//***********************************\\r\\n\\tfunction LGraphCubemapToTexture2D() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"texture\\\");\\r\\n\\t\\tthis.addInput(\\\"yaw\\\", \\\"number\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"texture\\\");\\r\\n\\t\\tthis.properties = { yaw: 0 };\\r\\n\\t}\\r\\n\\r\\n\\tLGraphCubemapToTexture2D.title = \\\"CubemapToTexture2D\\\";\\r\\n\\tLGraphCubemapToTexture2D.desc = \\\"Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation\\\";\\r\\n\\r\\n\\tLGraphCubemapToTexture2D.prototype.onExecute = function() {\\r\\n\\t\\tif (!this.isOutputConnected(0))\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tif ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type ))\\r\\n\\t\\t\\tthis._last_tex = null;\\r\\n\\t\\tvar yaw = this.getInputOrProperty(\\\"yaw\\\");\\r\\n\\t\\tthis._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw );\\r\\n\\t\\tthis.setOutputData( 0, this._last_tex );\\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.registerNodeType( \\\"texture/cubemapToTexture2D\\\", LGraphCubemapToTexture2D );\\r\\n})(this);\\r\\n\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n\\tvar view_matrix = new Float32Array(16);\\r\\n\\tvar projection_matrix = new Float32Array(16);\\r\\n\\tvar viewprojection_matrix = new Float32Array(16);\\r\\n\\tvar model_matrix = new Float32Array(16);\\r\\n\\tvar global_uniforms = {\\r\\n\\t\\tu_view: view_matrix,\\r\\n\\t\\tu_projection: projection_matrix,\\r\\n\\t\\tu_viewprojection: viewprojection_matrix,\\r\\n\\t\\tu_model: model_matrix \\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.LGraphRender = {\\r\\n\\t\\tonRequestCameraMatrices: null //overwrite with your 3D engine specifics, it will receive (view_matrix, projection_matrix,viewprojection_matrix) and must be filled\\r\\n\\t};\\r\\n\\r\\n\\tfunction generateGeometryId() {\\r\\n\\t\\treturn (Math.random() * 100000)|0;\\r\\n\\t}\\r\\n\\r\\n\\tfunction LGraphPoints3D() {\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"geometry\\\");\\r\\n\\t\\tthis.addOutput(\\\"points\\\", \\\"array\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tradius: 1,\\r\\n\\t\\t\\tnum_points: 1024,\\r\\n\\t\\t\\tmode: LGraphPoints3D.SPHERE\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.points = new Float32Array( this.properties.num_points * 3 );\\r\\n\\t\\tthis.must_update = true;\\r\\n\\t\\tthis.version = 0;\\r\\n\\r\\n\\t\\tthis.geometry = {\\r\\n\\t\\t\\tvertices: null,\\r\\n\\t\\t\\t_id: generateGeometryId()\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tglobal.LGraphPoints3D = LGraphPoints3D;\\r\\n\\r\\n\\tLGraphPoints3D.RECTANGLE = 1;\\r\\n\\tLGraphPoints3D.CIRCLE = 2;\\r\\n\\r\\n\\tLGraphPoints3D.CUBE = 10;\\r\\n\\tLGraphPoints3D.SPHERE = 11;\\r\\n\\tLGraphPoints3D.HEMISPHERE = 12;\\r\\n\\tLGraphPoints3D.INSIDE_SPHERE = 13;\\r\\n\\r\\n\\tLGraphPoints3D.MODE_VALUES = { \\\"rectangle\\\":LGraphPoints3D.RECTANGLE, \\\"circle\\\":LGraphPoints3D.CIRCLE, \\\"cube\\\":LGraphPoints3D.CUBE, \\\"sphere\\\":LGraphPoints3D.SPHERE, \\\"hemisphere\\\":LGraphPoints3D.HEMISPHERE, \\\"inside_sphere\\\":LGraphPoints3D.INSIDE_SPHERE };\\r\\n\\r\\n\\tLGraphPoints3D.widgets_info = {\\r\\n\\t\\tmode: { widget: \\\"combo\\\", values: LGraphPoints3D.MODE_VALUES }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphPoints3D.title = \\\"random points\\\";\\r\\n\\tLGraphPoints3D.desc = \\\"returns an array of random points\\\";\\r\\n\\r\\n\\tLGraphPoints3D.prototype.onPropertyChanged = function(name,value)\\r\\n\\t{\\r\\n\\t\\tthis.must_update = true;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphPoints3D.prototype.onExecute = function() {\\r\\n\\t\\tif(this.must_update)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.must_update = false;\\r\\n\\t\\t\\tthis.updatePoints();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.geometry.vertices = this.points;\\r\\n\\t\\tthis.geometry._version = this.version;\\r\\n\\r\\n\\t\\tthis.setOutputData( 0, this.geometry );\\r\\n\\t}\\r\\n\\r\\n\\tLGraphPoints3D.generatePoints = function( radius, num_points, mode, points )\\r\\n\\t{\\r\\n\\t\\tvar size = num_points * 3;\\r\\n\\t\\tif(!points || points.length != size)\\r\\n\\t\\t\\tpoints = new Float32Array( size );\\r\\n\\r\\n\\t\\tif( mode == LGraphPoints3D.RECTANGLE)\\r\\n\\t\\t\\tfor(var i = 0; i < size; i+=3)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tpoints[i] = (Math.random() - 0.5) * radius * 2;\\r\\n\\t\\t\\t\\tpoints[i+1] = 0;\\r\\n\\t\\t\\t\\tpoints[i+2] = (Math.random() - 0.5) * radius * 2;\\r\\n\\t\\t\\t}\\r\\n\\t\\telse if( mode == LGraphPoints3D.CUBE)\\r\\n\\t\\t\\tfor(var i = 0; i < size; i+=3)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tpoints[i] = (Math.random() - 0.5) * radius * 2;\\r\\n\\t\\t\\t\\tpoints[i+1] = (Math.random() - 0.5) * radius * 2;\\r\\n\\t\\t\\t\\tpoints[i+2] = (Math.random() - 0.5) * radius * 2;\\r\\n\\t\\t\\t}\\r\\n\\t\\telse if( mode == LGraphPoints3D.SPHERE)\\r\\n\\t\\t\\tfor(var i = 0; i < size; i+=3)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar r1 = Math.random();\\r\\n\\t\\t\\t\\tvar r2 = Math.random();\\r\\n\\t\\t\\t\\tvar x = 2 * Math.cos( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) );\\r\\n\\t\\t\\t\\tvar y = 1 - 2 * r2;\\r\\n\\t\\t\\t\\tvar z = 2 * Math.sin( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) );\\r\\n\\t\\t\\t\\tpoints[i] = x * radius;\\r\\n\\t\\t\\t\\tpoints[i+1] = y * radius;\\r\\n\\t\\t\\t\\tpoints[i+2] = z * radius;\\r\\n\\t\\t\\t}\\t\\t\\t\\r\\n\\t\\telse if( mode == LGraphPoints3D.HEMISPHERE)\\r\\n\\t\\t\\tfor(var i = 0; i < size; i+=3)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar r1 = Math.random();\\r\\n\\t\\t\\t\\tvar r2 = Math.random();\\r\\n\\t\\t\\t\\tvar x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 );\\r\\n\\t\\t\\t\\tvar y = r2;\\r\\n\\t\\t\\t\\tvar z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 );\\r\\n\\t\\t\\t\\tpoints[i] = x * radius;\\r\\n\\t\\t\\t\\tpoints[i+1] = y * radius;\\r\\n\\t\\t\\t\\tpoints[i+2] = z * radius;\\r\\n\\t\\t\\t}\\r\\n\\t\\telse if( mode == LGraphPoints3D.CIRCLE)\\r\\n\\t\\t\\tfor(var i = 0; i < size; i+=3)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar r1 = Math.random();\\r\\n\\t\\t\\t\\tvar r2 = Math.random();\\r\\n\\t\\t\\t\\tvar x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 );\\r\\n\\t\\t\\t\\tvar y = r2;\\r\\n\\t\\t\\t\\tvar z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 );\\r\\n\\t\\t\\t\\tpoints[i] = x * radius;\\r\\n\\t\\t\\t\\tpoints[i+1] = 0;\\r\\n\\t\\t\\t\\tpoints[i+2] = z * radius;\\r\\n\\t\\t\\t}\\r\\n\\t\\telse if( mode == LGraphPoints3D.INSIDE_SPHERE)\\r\\n\\t\\t\\tfor(var i = 0; i < size; i+=3)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar u = Math.random();\\r\\n\\t\\t\\t\\tvar v = Math.random();\\r\\n\\t\\t\\t\\tvar theta = u * 2.0 * Math.PI;\\r\\n\\t\\t\\t\\tvar phi = Math.acos(2.0 * v - 1.0);\\r\\n\\t\\t\\t\\tvar r = Math.cbrt(Math.random()) * radius;\\r\\n\\t\\t\\t\\tvar sinTheta = Math.sin(theta);\\r\\n\\t\\t\\t\\tvar cosTheta = Math.cos(theta);\\r\\n\\t\\t\\t\\tvar sinPhi = Math.sin(phi);\\r\\n\\t\\t\\t\\tvar cosPhi = Math.cos(phi);\\r\\n\\t\\t\\t\\tpoints[i] = r * sinPhi * cosTheta;\\r\\n\\t\\t\\t\\tpoints[i+1] = r * sinPhi * sinTheta;\\r\\n\\t\\t\\t\\tpoints[i+2] = r * cosPhi;\\r\\n\\t\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tconsole.warn(\\\"wrong mode in LGraphPoints3D\\\");\\r\\n\\r\\n\\t\\treturn points;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphPoints3D.prototype.updatePoints = function() {\\r\\n\\t\\tvar num_points = this.properties.num_points|0;\\r\\n\\t\\tif(num_points < 1)\\r\\n\\t\\t\\tnum_points = 1;\\r\\n\\t\\tif(this.points.length != num_points * 3)\\r\\n\\t\\t\\tthis.points = new Float32Array( num_points * 3 );\\r\\n\\r\\n\\t\\tvar points = this.points;\\r\\n\\t\\tvar radius = this.properties.radius;\\r\\n\\t\\tvar mode = this.properties.mode;\\r\\n\\r\\n\\t\\tLGraphPoints3D.generatePoints( radius, num_points, mode, this.points );\\r\\n\\r\\n\\t\\tthis.version++;\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType( \\\"geometry/points3D\\\", LGraphPoints3D );\\r\\n\\r\\n\\tfunction LGraphToGeometry() {\\r\\n\\t\\tthis.addInput(\\\"mesh\\\", \\\"mesh\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"geometry\\\");\\r\\n\\r\\n\\t\\tthis.geometry = {};\\r\\n\\t\\tthis.last_mesh = null;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphToGeometry.title = \\\"to geometry\\\";\\r\\n\\tLGraphToGeometry.desc = \\\"converts a mesh to geometry\\\";\\r\\n\\r\\n\\tLGraphToGeometry.prototype.onExecute = function() {\\r\\n\\t\\tvar mesh = this.getInputData(0);\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(mesh != this.last_mesh)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.last_mesh = mesh;\\r\\n\\t\\t\\tfor(i in mesh.vertexBuffers)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar buffer = mesh.vertexBuffers[i];\\r\\n\\t\\t\\t\\tthis.geometry[i] = buffer.data\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif(mesh.indexBuffers[\\\"triangles\\\"])\\r\\n\\t\\t\\t\\tthis.geometry.indices = mesh.indexBuffers[\\\"triangles\\\"].data;\\r\\n\\r\\n\\t\\t\\tthis.geometry._id = generateGeometryId();\\r\\n\\t\\t\\tthis.geometry._version = 0;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setOutputData(0,this.geometry);\\r\\n\\t\\tif(this.geometry)\\r\\n\\t\\t\\tthis.setOutputData(1,this.geometry.vertices);\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType( \\\"geometry/toGeometry\\\", LGraphToGeometry );\\r\\n\\r\\n\\tfunction LGraphGeometryQuantize() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"geometry\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"geometry\\\");\\r\\n\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tgrid_size: 1\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.geometry = null;\\r\\n\\t\\tthis.geometry_id = -1;\\r\\n\\t\\tthis.version = -1;\\r\\n\\t\\tthis.must_update = true;\\r\\n\\r\\n\\t\\tthis.vertices = null;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGeometryQuantize.title = \\\"quantize\\\";\\r\\n\\tLGraphGeometryQuantize.desc = \\\"quantize vertices\\\";\\r\\n\\r\\n\\tLGraphGeometryQuantize.prototype.onExecute = function() {\\r\\n\\t\\tvar geometry = this.getInputData(0);\\r\\n\\t\\tif(!geometry)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.must_update = false;\\r\\n\\t\\t\\tthis.geometry_id = geometry._id;\\r\\n\\t\\t\\tthis.version = geometry._version;\\r\\n\\r\\n\\t\\t\\t//copy\\r\\n\\t\\t\\tthis.geometry = {};\\r\\n\\t\\t\\tfor(var i in geometry)\\r\\n\\t\\t\\t\\tthis.geometry[i] = geometry[i];\\r\\n\\t\\t\\tthis.geometry._id = geometry._id;\\r\\n\\t\\t\\tthis.geometry._version = geometry._version + 1;\\r\\n\\r\\n\\t\\t\\tvar grid_size = this.properties.grid_size;\\r\\n\\t\\t\\tif(grid_size != 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar vertices = this.vertices;\\r\\n\\t\\t\\t\\tif(!vertices || this.vertices.length != this.geometry.vertices.length)\\r\\n\\t\\t\\t\\t\\tvertices = this.vertices = new Float32Array( this.geometry.vertices );\\r\\n\\t\\t\\t\\tfor(var i = 0; i < vertices.length; i+=3)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvertices[i] = Math.round(vertices[i]/grid_size) * grid_size;\\r\\n\\t\\t\\t\\t\\tvertices[i+1] = Math.round(vertices[i+1]/grid_size) * grid_size;\\r\\n\\t\\t\\t\\t\\tvertices[i+2] = Math.round(vertices[i+2]/grid_size) * grid_size;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tthis.geometry.vertices = vertices;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setOutputData(0,this.geometry);\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType( \\\"geometry/quantize\\\", LGraphGeometryQuantize );\\r\\n\\r\\n\\tfunction LGraphConnectPoints() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"geometry\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"geometry\\\");\\r\\n\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tmin_dist: 0.4,\\r\\n\\t\\t\\tmax_dist: 0.5,\\r\\n\\t\\t\\tmax_connections: 0,\\r\\n\\t\\t\\tprobability: 1\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.geometry_id = -1;\\r\\n\\t\\tthis.version = -1;\\r\\n\\t\\tthis.my_version = 1;\\r\\n\\t\\tthis.must_update = true;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphConnectPoints.title = \\\"connect points\\\";\\r\\n\\tLGraphConnectPoints.desc = \\\"adds indices between near points\\\";\\r\\n\\r\\n\\tLGraphConnectPoints.prototype.onPropertyChanged = function(name,value)\\r\\n\\t{\\r\\n\\t\\tthis.must_update = true;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphConnectPoints.prototype.onExecute = function() {\\r\\n\\t\\tvar geometry = this.getInputData(0);\\r\\n\\t\\tif(!geometry)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.must_update = false;\\r\\n\\t\\t\\tthis.geometry_id = geometry._id;\\r\\n\\t\\t\\tthis.version = geometry._version;\\r\\n\\r\\n\\t\\t\\t//copy\\r\\n\\t\\t\\tthis.geometry = {};\\r\\n\\t\\t\\tfor(var i in geometry)\\r\\n\\t\\t\\t\\tthis.geometry[i] = geometry[i];\\r\\n\\t\\t\\tthis.geometry._id = generateGeometryId();\\r\\n\\t\\t\\tthis.geometry._version = this.my_version++;\\r\\n\\r\\n\\t\\t\\tvar vertices = geometry.vertices;\\r\\n\\t\\t\\tvar l = vertices.length;\\r\\n\\t\\t\\tvar min_dist = this.properties.min_dist;\\r\\n\\t\\t\\tvar max_dist = this.properties.max_dist;\\r\\n\\t\\t\\tvar probability = this.properties.probability;\\r\\n\\t\\t\\tvar max_connections = this.properties.max_connections;\\r\\n\\t\\t\\tvar indices = [];\\r\\n\\t\\t\\t\\r\\n\\t\\t\\tfor(var i = 0; i < l; i+=3)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar x = vertices[i];\\r\\n\\t\\t\\t\\tvar y = vertices[i+1];\\r\\n\\t\\t\\t\\tvar z = vertices[i+2];\\r\\n\\t\\t\\t\\tvar connections = 0;\\r\\n\\t\\t\\t\\tfor(var j = i+3; j < l; j+=3)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar x2 = vertices[j];\\r\\n\\t\\t\\t\\t\\tvar y2 = vertices[j+1];\\r\\n\\t\\t\\t\\t\\tvar z2 = vertices[j+2];\\r\\n\\t\\t\\t\\t\\tvar dist = Math.sqrt( (x-x2)*(x-x2) + (y-y2)*(y-y2) + (z-z2)*(z-z2));\\r\\n\\t\\t\\t\\t\\tif(dist > max_dist || dist < min_dist || (probability < 1 && probability < Math.random()) )\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\tindices.push(i/3,j/3);\\r\\n\\t\\t\\t\\t\\tconnections += 1;\\r\\n\\t\\t\\t\\t\\tif(max_connections && connections > max_connections)\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.geometry.indices = this.indices = new Uint32Array(indices);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(this.indices && this.indices.length)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.geometry.indices = this.indices;\\r\\n\\t\\t\\tthis.setOutputData( 0, this.geometry );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.setOutputData( 0, null );\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType( \\\"geometry/connectPoints\\\", LGraphConnectPoints );\\r\\n\\r\\n //Works with Litegl.js to create WebGL nodes\\r\\n if (typeof GL == \\\"undefined\\\") //LiteGL RELATED **********************************************\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfunction LGraphRenderGeometry() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"geometry\\\");\\r\\n\\t\\tthis.addInput(\\\"mat4\\\", \\\"mat4\\\");\\r\\n\\t\\tthis.addInput(\\\"tex\\\", \\\"texture\\\");\\r\\n\\t\\tthis.addOutput(\\\"mesh\\\", \\\"mesh\\\");\\r\\n\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tenabled: true,\\r\\n\\t\\t\\tprimitive: GL.TRIANGLES,\\r\\n\\t\\t\\tadditive: false,\\r\\n\\t\\t\\tcolor: [1,1,1],\\r\\n\\t\\t\\topacity: 1\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.color = vec4.create([1,1,1,1]);\\r\\n\\t\\tthis.uniforms = {\\r\\n\\t\\t\\tu_color: this.color\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.version = -1;\\r\\n\\t\\tthis.mesh = null;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRenderGeometry.title = \\\"render\\\";\\r\\n\\tLGraphRenderGeometry.desc = \\\"renders a geometry\\\";\\r\\n\\r\\n\\tLGraphRenderGeometry.PRIMITIVE_VALUES = { \\\"points\\\":GL.POINTS, \\\"lines\\\":GL.LINES, \\\"line_loop\\\":GL.LINE_LOOP,\\\"line_strip\\\":GL.LINE_STRIP, \\\"triangles\\\":GL.TRIANGLES, \\\"triangle_fan\\\":GL.TRIANGLE_FAN, \\\"triangle_strip\\\":GL.TRIANGLE_STRIP };\\r\\n\\r\\n\\tLGraphRenderGeometry.widgets_info = {\\r\\n\\t\\tprimitive: { widget: \\\"combo\\\", values: LGraphRenderGeometry.PRIMITIVE_VALUES },\\r\\n\\t\\tcolor: { widget: \\\"color\\\" }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphRenderGeometry.prototype.updateMesh = function(geometry)\\r\\n\\t{\\r\\n\\t\\tif(!this.mesh)\\r\\n\\t\\t\\tthis.mesh = new GL.Mesh();\\r\\n\\r\\n\\t\\tfor(var i in geometry)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(i[0] == \\\"_\\\")\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar buffer_data = geometry[i];\\r\\n\\r\\n\\t\\t\\tvar info = GL.Mesh.common_buffers[i];\\r\\n\\t\\t\\tif(!info && i != \\\"indices\\\") //unknown buffer\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar spacing = info ? info.spacing : 3;\\r\\n\\t\\t\\tvar mesh_buffer = this.mesh.vertexBuffers[i];\\r\\n\\r\\n\\t\\t\\tif(!mesh_buffer || mesh_buffer.data.length != buffer_data.length)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tmesh_buffer = new GL.Buffer( i == \\\"indices\\\" ? GL.ELEMENT_ARRAY_BUFFER : GL.ARRAY_BUFFER, buffer_data, spacing, GL.DYNAMIC_DRAW );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tmesh_buffer.data.set( buffer_data );\\r\\n\\t\\t\\t\\tmesh_buffer.upload(GL.DYNAMIC_DRAW);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tthis.mesh.addBuffer( i, mesh_buffer );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.geometry_id = this.mesh.id = geometry._id;\\r\\n\\t\\tthis.version = this.mesh.version = geometry._version;\\r\\n\\t\\treturn this.mesh;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRenderGeometry.prototype.onExecute = function() {\\r\\n\\r\\n\\t\\tif(!this.properties.enabled)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar geometry = this.getInputData(0);\\r\\n\\t\\tif(!geometry)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif( this.version != geometry._version || this.geometry_id != geometry._id )\\r\\n\\t\\t\\tthis.updateMesh( geometry );\\r\\n\\r\\n\\t\\tif(!LiteGraph.LGraphRender.onRequestCameraMatrices)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix );\\r\\n\\t\\tvar shader = null;\\r\\n\\r\\n\\t\\tvar texture = this.getInputData(2);\\r\\n\\t\\t\\r\\n\\t\\tif(texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\tshader = gl.shaders[\\\"textured\\\"];\\r\\n\\t\\t\\tif(!shader)\\r\\n\\t\\t\\t\\tshader = gl.shaders[\\\"textured\\\"] = new GL.Shader( vertex_shader_code, fragment_shader_code, { USE_TEXTURE:\\\"\\\" });\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tshader = gl.shaders[\\\"flat\\\"];\\r\\n\\t\\t\\tif(!shader)\\r\\n\\t\\t\\t\\tshader = gl.shaders[\\\"flat\\\"] = new GL.Shader( vertex_shader_code, fragment_shader_code );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.color.set( this.properties.color );\\r\\n\\t\\tthis.color[3] = this.properties.opacity;\\r\\n\\r\\n\\t\\tvar m = this.getInputData(1);\\r\\n\\t\\tif(m)\\r\\n\\t\\t\\tmodel_matrix.set(m);\\r\\n\\t\\telse\\r\\n\\t\\t\\tmat4.identity( model_matrix );\\r\\n\\r\\n\\t\\tthis.uniforms.u_point_size = 1;\\r\\n\\t\\tvar primitive = this.properties.primitive;\\r\\n\\r\\n\\t\\tshader.uniforms( global_uniforms );\\r\\n\\t\\tshader.uniforms( this.uniforms );\\r\\n\\r\\n\\t\\tif(this.properties.opacity >= 1)\\r\\n\\t\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.enable( gl.BLEND );\\r\\n\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\tif( this.properties.additive )\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE );\\r\\n\\t\\t\\tgl.depthMask( false );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );\\r\\n\\t\\tshader.draw( this.mesh, primitive, \\\"indices\\\" );\\r\\n\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\tgl.depthMask( true );\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType( \\\"geometry/render\\\", LGraphRenderGeometry );\\r\\n\\r\\n\\tfunction LGraphRenderPoints() {\\r\\n\\t\\tthis.addInput(\\\"in\\\", \\\"geometry\\\");\\r\\n\\t\\tthis.addInput(\\\"mat4\\\", \\\"mat4\\\");\\r\\n\\t\\tthis.addInput(\\\"tex\\\", \\\"texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tenabled: true,\\r\\n\\t\\t\\tpoint_size: 0.1,\\r\\n\\t\\t\\tfixed_size: false,\\r\\n\\t\\t\\tadditive: true,\\r\\n\\t\\t\\tcolor: [1,1,1],\\r\\n\\t\\t\\topacity: 1\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.color = vec4.create([1,1,1,1]);\\r\\n\\r\\n\\t\\tthis.uniforms = {\\r\\n\\t\\t\\tu_point_size: 1,\\r\\n\\t\\t\\tu_perspective: 1,\\r\\n\\t\\t\\tu_point_perspective: 1,\\r\\n\\t\\t\\tu_color: this.color\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.geometry_id = -1;\\r\\n\\t\\tthis.version = -1;\\r\\n\\t\\tthis.mesh = null;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRenderPoints.widgets_info = {\\r\\n\\t\\tcolor: { widget: \\\"color\\\" }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphRenderPoints.prototype.updateMesh = function(geometry)\\r\\n\\t{\\r\\n\\t\\tvar buffer = this.buffer;\\r\\n\\t\\tif(!this.buffer || this.buffer.data.length != geometry.vertices.length)\\r\\n\\t\\t\\tthis.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW);\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.buffer.data.set( geometry.vertices );\\r\\n\\t\\t\\tthis.buffer.upload(GL.DYNAMIC_DRAW);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!this.mesh)\\r\\n\\t\\t\\tthis.mesh = new GL.Mesh();\\r\\n\\r\\n\\t\\tthis.mesh.addBuffer(\\\"vertices\\\",this.buffer);\\r\\n\\t\\tthis.geometry_id = this.mesh.id = geometry._id;\\r\\n\\t\\tthis.version = this.mesh.version = geometry._version;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRenderPoints.prototype.onExecute = function() {\\r\\n\\r\\n\\t\\tif(!this.properties.enabled)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar geometry = this.getInputData(0);\\r\\n\\t\\tif(!geometry)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(this.version != geometry._version || this.geometry_id != geometry._id )\\r\\n\\t\\t\\tthis.updateMesh( geometry );\\r\\n\\r\\n\\t\\tif(!LiteGraph.LGraphRender.onRequestCameraMatrices)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix );\\r\\n\\t\\tvar shader = null;\\r\\n\\r\\n\\t\\tvar texture = this.getInputData(2);\\r\\n\\t\\t\\r\\n\\t\\tif(texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\tshader = gl.shaders[\\\"textured_points\\\"];\\r\\n\\t\\t\\tif(!shader)\\r\\n\\t\\t\\t\\tshader = gl.shaders[\\\"textured_points\\\"] = new GL.Shader( vertex_shader_code, fragment_shader_code, { USE_TEXTURED_POINTS:\\\"\\\" });\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tshader = gl.shaders[\\\"points\\\"];\\r\\n\\t\\t\\tif(!shader)\\r\\n\\t\\t\\t\\tshader = gl.shaders[\\\"points\\\"] = new GL.Shader( vertex_shader_code, fragment_shader_code, { USE_POINTS: \\\"\\\" });\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.color.set( this.properties.color );\\r\\n\\t\\tthis.color[3] = this.properties.opacity;\\r\\n\\r\\n\\t\\tvar m = this.getInputData(1);\\r\\n\\t\\tif(m)\\r\\n\\t\\t\\tmodel_matrix.set(m);\\r\\n\\t\\telse\\r\\n\\t\\t\\tmat4.identity( model_matrix );\\r\\n\\r\\n\\t\\tthis.uniforms.u_point_size = this.properties.point_size;\\r\\n\\t\\tthis.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1;\\r\\n\\t\\tthis.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5];\\r\\n\\r\\n\\t\\tshader.uniforms( global_uniforms );\\r\\n\\t\\tshader.uniforms( this.uniforms );\\r\\n\\r\\n\\t\\tif(this.properties.opacity >= 1)\\r\\n\\t\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.enable( gl.BLEND );\\r\\n\\r\\n\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\tif( this.properties.additive )\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE );\\r\\n\\t\\t\\tgl.depthMask( false );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );\\r\\n\\r\\n\\t\\tshader.draw( this.mesh, GL.POINTS );\\r\\n\\r\\n\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\tgl.depthMask( true );\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType( \\\"geometry/render_points\\\", LGraphRenderPoints );\\r\\n\\r\\n\\tvar vertex_shader_code = '\\\\\\r\\n\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\tvarying vec3 v_vertex;\\\\n\\\\\\r\\n\\t\\tattribute vec3 a_normal;\\\\n\\\\\\r\\n\\t\\tvarying vec3 v_normal;\\\\n\\\\\\r\\n\\t\\t#ifdef USE_COLOR\\\\n\\\\\\r\\n\\t\\t\\tattribute vec4 a_color;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec4 v_color;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\tattribute vec2 a_coord;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t#ifdef USE_SIZE\\\\n\\\\\\r\\n\\t\\t\\tattribute float a_extra;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t#ifdef USE_INSTANCING\\\\n\\\\\\r\\n\\t\\t\\tattribute mat4 u_model;\\\\n\\\\\\r\\n\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_model;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n\\t\\tuniform float u_point_size;\\\\n\\\\\\r\\n\\t\\tuniform float u_perspective;\\\\n\\\\\\r\\n\\t\\tuniform float u_point_perspective;\\\\n\\\\\\r\\n\\t\\tfloat computePointSize(float radius, float w)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tif(radius < 0.0)\\\\n\\\\\\r\\n\\t\\t\\t\\treturn -radius;\\\\n\\\\\\r\\n\\t\\t\\treturn u_perspective * radius / w;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tv_coord = a_coord;\\\\n\\\\\\r\\n\\t\\t\\t#ifdef USE_COLOR\\\\n\\\\\\r\\n\\t\\t\\t\\tv_color = a_color;\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\tv_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\\\\n\\\\\\r\\n\\t\\t\\tv_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\\\\n\\\\\\r\\n\\t\\t\\tgl_Position = u_viewprojection * vec4(v_vertex,1.0);\\\\n\\\\\\r\\n\\t\\t\\tgl_PointSize = u_point_size;\\\\n\\\\\\r\\n\\t\\t\\t#ifdef USE_SIZE\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_PointSize = a_extra;\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\tif(u_point_perspective != 0.0)\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\\\\n\\\\\\r\\n\\t\\t}\\\\\\r\\n\\t';\\r\\n\\r\\n\\tvar fragment_shader_code = '\\\\\\r\\n\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t#ifdef USE_COLOR\\\\n\\\\\\r\\n\\t\\t\\tvarying vec4 v_color;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = u_color;\\\\n\\\\\\r\\n\\t\\t\\t#ifdef USE_TEXTURED_POINTS\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor *= texture2D(u_texture, gl_PointCoord.xy);\\\\n\\\\\\r\\n\\t\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\t\\t#ifdef USE_TEXTURE\\\\n\\\\\\r\\n\\t\\t\\t\\t color *= texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t\\t if(color.a < 0.1)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tdiscard;\\\\n\\\\\\r\\n\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\t#ifdef USE_POINTS\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tfloat dist = length( gl_PointCoord.xy - vec2(0.5) );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif( dist > 0.45 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tdiscard;\\\\n\\\\\\r\\n\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t#ifdef USE_COLOR\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor *= v_color;\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t}\\\\\\r\\n\\t';\\r\\n\\r\\n\\r\\n})(this);\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n //Works with Litegl.js to create WebGL nodes\\r\\n if (typeof GL != \\\"undefined\\\") {\\r\\n // Texture Lens *****************************************\\r\\n function LGraphFXLens() {\\r\\n this.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n this.addInput(\\\"Aberration\\\", \\\"number\\\");\\r\\n this.addInput(\\\"Distortion\\\", \\\"number\\\");\\r\\n this.addInput(\\\"Blur\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n this.properties = {\\r\\n aberration: 1.0,\\r\\n distortion: 1.0,\\r\\n blur: 1.0,\\r\\n precision: LGraphTexture.DEFAULT\\r\\n };\\r\\n\\r\\n if (!LGraphFXLens._shader) {\\r\\n LGraphFXLens._shader = new GL.Shader(\\r\\n GL.Shader.SCREEN_VERTEX_SHADER,\\r\\n LGraphFXLens.pixel_shader\\r\\n );\\r\\n LGraphFXLens._texture = new GL.Texture(3, 1, {\\r\\n format: gl.RGB,\\r\\n wrap: gl.CLAMP_TO_EDGE,\\r\\n magFilter: gl.LINEAR,\\r\\n minFilter: gl.LINEAR,\\r\\n pixel_data: [255, 0, 0, 0, 255, 0, 0, 0, 255]\\r\\n });\\r\\n }\\r\\n }\\r\\n\\r\\n LGraphFXLens.title = \\\"Lens\\\";\\r\\n LGraphFXLens.desc = \\\"Camera Lens distortion\\\";\\r\\n LGraphFXLens.widgets_info = {\\r\\n precision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n };\\r\\n\\r\\n LGraphFXLens.prototype.onExecute = function() {\\r\\n var tex = this.getInputData(0);\\r\\n if (this.properties.precision === LGraphTexture.PASS_THROUGH) {\\r\\n this.setOutputData(0, tex);\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!tex) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this._tex = LGraphTexture.getTargetTexture(\\r\\n tex,\\r\\n this._tex,\\r\\n this.properties.precision\\r\\n );\\r\\n\\r\\n var aberration = this.properties.aberration;\\r\\n if (this.isInputConnected(1)) {\\r\\n aberration = this.getInputData(1);\\r\\n this.properties.aberration = aberration;\\r\\n }\\r\\n\\r\\n var distortion = this.properties.distortion;\\r\\n if (this.isInputConnected(2)) {\\r\\n distortion = this.getInputData(2);\\r\\n this.properties.distortion = distortion;\\r\\n }\\r\\n\\r\\n var blur = this.properties.blur;\\r\\n if (this.isInputConnected(3)) {\\r\\n blur = this.getInputData(3);\\r\\n this.properties.blur = blur;\\r\\n }\\r\\n\\r\\n gl.disable(gl.BLEND);\\r\\n gl.disable(gl.DEPTH_TEST);\\r\\n var mesh = Mesh.getScreenQuad();\\r\\n var shader = LGraphFXLens._shader;\\r\\n //var camera = LS.Renderer._current_camera;\\r\\n\\r\\n this._tex.drawTo(function() {\\r\\n tex.bind(0);\\r\\n shader\\r\\n .uniforms({\\r\\n u_texture: 0,\\r\\n u_aberration: aberration,\\r\\n u_distortion: distortion,\\r\\n u_blur: blur\\r\\n })\\r\\n .draw(mesh);\\r\\n });\\r\\n\\r\\n this.setOutputData(0, this._tex);\\r\\n };\\r\\n\\r\\n LGraphFXLens.pixel_shader =\\r\\n \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_camera_planes;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_aberration;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_distortion;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_blur;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 coord = v_coord;\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat dist = distance(vec2(0.5), coord);\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 dist_coord = coord - vec2(0.5);\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\\\\n\\\\\\r\\n\\t\\t\\t\\tdist_coord *= percent;\\\\n\\\\\\r\\n\\t\\t\\t\\tcoord = dist_coord + vec2(0.5);\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D(u_texture,coord, u_blur * dist);\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n /*\\r\\n\\t\\t\\tfloat normalized_tunable_sigmoid(float xs, float k)\\\\n\\\\\\r\\n\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\txs = xs * 2.0 - 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat signx = sign(xs);\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat absx = abs(xs);\\\\n\\\\\\r\\n\\t\\t\\t\\treturn signx * ((-k - 1.0)*absx)/(2.0*(-2.0*k*absx+k-1.0)) + 0.5;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t*/\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"fx/lens\\\", LGraphFXLens);\\r\\n global.LGraphFXLens = LGraphFXLens;\\r\\n\\r\\n /* not working yet\\r\\n\\tfunction LGraphDepthOfField()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"Color\\\",\\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"Linear Depth\\\",\\\"Texture\\\");\\r\\n\\t\\tthis.addInput(\\\"Camera\\\",\\\"camera\\\");\\r\\n\\t\\tthis.addOutput(\\\"Texture\\\",\\\"Texture\\\");\\r\\n\\t\\tthis.properties = { high_precision: false };\\r\\n\\t}\\r\\n\\r\\n\\tLGraphDepthOfField.title = \\\"Depth Of Field\\\";\\r\\n\\tLGraphDepthOfField.desc = \\\"Applies a depth of field effect\\\";\\r\\n\\r\\n\\tLGraphDepthOfField.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tvar tex = this.getInputData(0);\\r\\n\\t\\tvar depth = this.getInputData(1);\\r\\n\\t\\tvar camera = this.getInputData(2);\\r\\n\\r\\n\\t\\tif(!tex || !depth || !camera) \\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar precision = gl.UNSIGNED_BYTE;\\r\\n\\t\\tif(this.properties.high_precision)\\r\\n\\t\\t\\tprecision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT;\\t\\t\\t\\r\\n\\t\\tif(!this._temp_texture || this._temp_texture.type != precision ||\\r\\n\\t\\t\\tthis._temp_texture.width != tex.width || this._temp_texture.height != tex.height)\\r\\n\\t\\t\\tthis._temp_texture = new GL.Texture( tex.width, tex.height, { type: precision, format: gl.RGBA, filter: gl.LINEAR });\\r\\n\\r\\n\\t\\tvar shader = LGraphDepthOfField._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphDepthOfField._pixel_shader );\\r\\n\\r\\n\\t\\tvar screen_mesh = Mesh.getScreenQuad();\\r\\n\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.disable( gl.BLEND );\\r\\n\\r\\n\\t\\tvar camera_position = camera.getEye();\\r\\n\\t\\tvar focus_point = camera.getCenter();\\r\\n\\t\\tvar distance = vec3.distance( camera_position, focus_point );\\r\\n\\t\\tvar far = camera.far;\\r\\n\\t\\tvar focus_range = distance * 0.5;\\r\\n\\r\\n\\t\\tthis._temp_texture.drawTo( function() {\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tdepth.bind(1);\\r\\n\\t\\t\\tshader.uniforms({u_texture:0, u_depth_texture:1, u_resolution: [1/tex.width, 1/tex.height], u_far: far, u_focus_point: distance, u_focus_scale: focus_range }).draw(screen_mesh);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.setOutputData(0, this._temp_texture);\\r\\n\\t}\\r\\n\\r\\n\\t//from http://tuxedolabs.blogspot.com.es/2018/05/bokeh-depth-of-field-in-single-pass.html\\r\\n\\tLGraphDepthOfField._pixel_shader = \\\"\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture; //Image to be processed\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_depth_texture; //Linear depth, where 1.0 == far plane\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_iresolution; //The size of a pixel: vec2(1.0/width, 1.0/height)\\\\n\\\\\\r\\n\\t\\tuniform float u_far; // Far plane\\\\n\\\\\\r\\n\\t\\tuniform float u_focus_point;\\\\n\\\\\\r\\n\\t\\tuniform float u_focus_scale;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tconst float GOLDEN_ANGLE = 2.39996323;\\\\n\\\\\\r\\n\\t\\tconst float MAX_BLUR_SIZE = 20.0;\\\\n\\\\\\r\\n\\t\\tconst float RAD_SCALE = 0.5; // Smaller = nicer blur, larger = faster\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tfloat getBlurSize(float depth, float focusPoint, float focusScale)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t float coc = clamp((1.0 / focusPoint - 1.0 / depth)*focusScale, -1.0, 1.0);\\\\n\\\\\\r\\n\\t\\t return abs(coc) * MAX_BLUR_SIZE;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvec3 depthOfField(vec2 texCoord, float focusPoint, float focusScale)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t float centerDepth = texture2D(u_depth_texture, texCoord).r * u_far;\\\\n\\\\\\r\\n\\t\\t float centerSize = getBlurSize(centerDepth, focusPoint, focusScale);\\\\n\\\\\\r\\n\\t\\t vec3 color = texture2D(u_texture, v_coord).rgb;\\\\n\\\\\\r\\n\\t\\t float tot = 1.0;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\t float radius = RAD_SCALE;\\\\n\\\\\\r\\n\\t\\t for (float ang = 0.0; ang < 100.0; ang += GOLDEN_ANGLE)\\\\n\\\\\\r\\n\\t\\t {\\\\n\\\\\\r\\n\\t\\t vec2 tc = texCoord + vec2(cos(ang), sin(ang)) * u_iresolution * radius;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t vec3 sampleColor = texture2D(u_texture, tc).rgb;\\\\n\\\\\\r\\n\\t\\t float sampleDepth = texture2D(u_depth_texture, tc).r * u_far;\\\\n\\\\\\r\\n\\t\\t float sampleSize = getBlurSize( sampleDepth, focusPoint, focusScale );\\\\n\\\\\\r\\n\\t\\t if (sampleDepth > centerDepth)\\\\n\\\\\\r\\n\\t\\t sampleSize = clamp(sampleSize, 0.0, centerSize*2.0);\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t float m = smoothstep(radius-0.5, radius+0.5, sampleSize);\\\\n\\\\\\r\\n\\t\\t color += mix(color/tot, sampleColor, m);\\\\n\\\\\\r\\n\\t\\t tot += 1.0;\\\\n\\\\\\r\\n\\t\\t radius += RAD_SCALE/radius;\\\\n\\\\\\r\\n\\t\\t if(radius>=MAX_BLUR_SIZE)\\\\n\\\\\\r\\n\\t\\t\\t return color / tot;\\\\n\\\\\\r\\n\\t\\t }\\\\n\\\\\\r\\n\\t\\t return color / tot;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\tvoid main()\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = vec4( depthOfField( v_coord, u_focus_point, u_focus_scale ), 1.0 );\\\\n\\\\\\r\\n\\t\\t\\t//gl_FragColor = vec4( texture2D(u_depth_texture, v_coord).r );\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"fx/DOF\\\", LGraphDepthOfField );\\r\\n\\tglobal.LGraphDepthOfField = LGraphDepthOfField;\\r\\n\\t*/\\r\\n\\r\\n //*******************************************************\\r\\n\\r\\n function LGraphFXBokeh() {\\r\\n this.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n this.addInput(\\\"Blurred\\\", \\\"Texture\\\");\\r\\n this.addInput(\\\"Mask\\\", \\\"Texture\\\");\\r\\n this.addInput(\\\"Threshold\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n this.properties = {\\r\\n shape: \\\"\\\",\\r\\n size: 10,\\r\\n alpha: 1.0,\\r\\n threshold: 1.0,\\r\\n high_precision: false\\r\\n };\\r\\n }\\r\\n\\r\\n LGraphFXBokeh.title = \\\"Bokeh\\\";\\r\\n LGraphFXBokeh.desc = \\\"applies an Bokeh effect\\\";\\r\\n\\r\\n LGraphFXBokeh.widgets_info = { shape: { widget: \\\"texture\\\" } };\\r\\n\\r\\n LGraphFXBokeh.prototype.onExecute = function() {\\r\\n var tex = this.getInputData(0);\\r\\n var blurred_tex = this.getInputData(1);\\r\\n var mask_tex = this.getInputData(2);\\r\\n if (!tex || !mask_tex || !this.properties.shape) {\\r\\n this.setOutputData(0, tex);\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!blurred_tex) {\\r\\n blurred_tex = tex;\\r\\n }\\r\\n\\r\\n var shape_tex = LGraphTexture.getTexture(this.properties.shape);\\r\\n if (!shape_tex) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var threshold = this.properties.threshold;\\r\\n if (this.isInputConnected(3)) {\\r\\n threshold = this.getInputData(3);\\r\\n this.properties.threshold = threshold;\\r\\n }\\r\\n\\r\\n var precision = gl.UNSIGNED_BYTE;\\r\\n if (this.properties.high_precision) {\\r\\n precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT;\\r\\n }\\r\\n if (\\r\\n !this._temp_texture ||\\r\\n this._temp_texture.type != precision ||\\r\\n this._temp_texture.width != tex.width ||\\r\\n this._temp_texture.height != tex.height\\r\\n ) {\\r\\n this._temp_texture = new GL.Texture(tex.width, tex.height, {\\r\\n type: precision,\\r\\n format: gl.RGBA,\\r\\n filter: gl.LINEAR\\r\\n });\\r\\n }\\r\\n\\r\\n //iterations\\r\\n var size = this.properties.size;\\r\\n\\r\\n var first_shader = LGraphFXBokeh._first_shader;\\r\\n if (!first_shader) {\\r\\n first_shader = LGraphFXBokeh._first_shader = new GL.Shader(\\r\\n Shader.SCREEN_VERTEX_SHADER,\\r\\n LGraphFXBokeh._first_pixel_shader\\r\\n );\\r\\n }\\r\\n\\r\\n var second_shader = LGraphFXBokeh._second_shader;\\r\\n if (!second_shader) {\\r\\n second_shader = LGraphFXBokeh._second_shader = new GL.Shader(\\r\\n LGraphFXBokeh._second_vertex_shader,\\r\\n LGraphFXBokeh._second_pixel_shader\\r\\n );\\r\\n }\\r\\n\\r\\n var points_mesh = this._points_mesh;\\r\\n if (\\r\\n !points_mesh ||\\r\\n points_mesh._width != tex.width ||\\r\\n points_mesh._height != tex.height ||\\r\\n points_mesh._spacing != 2\\r\\n ) {\\r\\n points_mesh = this.createPointsMesh(tex.width, tex.height, 2);\\r\\n }\\r\\n\\r\\n var screen_mesh = Mesh.getScreenQuad();\\r\\n\\r\\n var point_size = this.properties.size;\\r\\n var min_light = this.properties.min_light;\\r\\n var alpha = this.properties.alpha;\\r\\n\\r\\n gl.disable(gl.DEPTH_TEST);\\r\\n gl.disable(gl.BLEND);\\r\\n\\r\\n this._temp_texture.drawTo(function() {\\r\\n tex.bind(0);\\r\\n blurred_tex.bind(1);\\r\\n mask_tex.bind(2);\\r\\n first_shader\\r\\n .uniforms({\\r\\n u_texture: 0,\\r\\n u_texture_blur: 1,\\r\\n u_mask: 2,\\r\\n u_texsize: [tex.width, tex.height]\\r\\n })\\r\\n .draw(screen_mesh);\\r\\n });\\r\\n\\r\\n this._temp_texture.drawTo(function() {\\r\\n //clear because we use blending\\r\\n //gl.clearColor(0.0,0.0,0.0,1.0);\\r\\n //gl.clear( gl.COLOR_BUFFER_BIT );\\r\\n gl.enable(gl.BLEND);\\r\\n gl.blendFunc(gl.ONE, gl.ONE);\\r\\n\\r\\n tex.bind(0);\\r\\n shape_tex.bind(3);\\r\\n second_shader\\r\\n .uniforms({\\r\\n u_texture: 0,\\r\\n u_mask: 2,\\r\\n u_shape: 3,\\r\\n u_alpha: alpha,\\r\\n u_threshold: threshold,\\r\\n u_pointSize: point_size,\\r\\n u_itexsize: [1.0 / tex.width, 1.0 / tex.height]\\r\\n })\\r\\n .draw(points_mesh, gl.POINTS);\\r\\n });\\r\\n\\r\\n this.setOutputData(0, this._temp_texture);\\r\\n };\\r\\n\\r\\n LGraphFXBokeh.prototype.createPointsMesh = function(\\r\\n width,\\r\\n height,\\r\\n spacing\\r\\n ) {\\r\\n var nwidth = Math.round(width / spacing);\\r\\n var nheight = Math.round(height / spacing);\\r\\n\\r\\n var vertices = new Float32Array(nwidth * nheight * 2);\\r\\n\\r\\n var ny = -1;\\r\\n var dx = (2 / width) * spacing;\\r\\n var dy = (2 / height) * spacing;\\r\\n for (var y = 0; y < nheight; ++y) {\\r\\n var nx = -1;\\r\\n for (var x = 0; x < nwidth; ++x) {\\r\\n var pos = y * nwidth * 2 + x * 2;\\r\\n vertices[pos] = nx;\\r\\n vertices[pos + 1] = ny;\\r\\n nx += dx;\\r\\n }\\r\\n ny += dy;\\r\\n }\\r\\n\\r\\n this._points_mesh = GL.Mesh.load({ vertices2D: vertices });\\r\\n this._points_mesh._width = width;\\r\\n this._points_mesh._height = height;\\r\\n this._points_mesh._spacing = spacing;\\r\\n\\r\\n return this._points_mesh;\\r\\n };\\r\\n\\r\\n /*\\r\\n\\tLGraphTextureBokeh._pixel_shader = \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 a_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_shape;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D( u_texture, gl_PointCoord );\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor *= v_color * u_alpha;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\";\\r\\n\\t*/\\r\\n\\r\\n LGraphFXBokeh._first_pixel_shader =\\r\\n \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture_blur;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_mask;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 blurred_color = texture2D(u_texture_blur, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat mask = texture2D(u_mask, v_coord).x;\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = mix(color, blurred_color, mask);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n LGraphFXBokeh._second_vertex_shader =\\r\\n \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec2 a_vertex2D;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec4 v_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_mask;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_itexsize;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_pointSize;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_threshold;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 coord = a_vertex2D * 0.5 + 0.5;\\\\n\\\\\\r\\n\\t\\t\\t\\tv_color = texture2D( u_texture, coord );\\\\n\\\\\\r\\n\\t\\t\\t\\tv_color += texture2D( u_texture, coord + vec2(u_itexsize.x, 0.0) );\\\\n\\\\\\r\\n\\t\\t\\t\\tv_color += texture2D( u_texture, coord + vec2(0.0, u_itexsize.y));\\\\n\\\\\\r\\n\\t\\t\\t\\tv_color += texture2D( u_texture, coord + u_itexsize);\\\\n\\\\\\r\\n\\t\\t\\t\\tv_color *= 0.25;\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat mask = texture2D(u_mask, coord).x;\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat luminance = length(v_color) * mask;\\\\n\\\\\\r\\n\\t\\t\\t\\t/*luminance /= (u_pointSize*u_pointSize)*0.01 */;\\\\n\\\\\\r\\n\\t\\t\\t\\tluminance -= u_threshold;\\\\n\\\\\\r\\n\\t\\t\\t\\tif(luminance < 0.0)\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tgl_Position.x = -100.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn;\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_PointSize = u_pointSize;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = vec4(a_vertex2D,0.0,1.0);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n LGraphFXBokeh._second_pixel_shader =\\r\\n \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec4 v_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_shape;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_alpha;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D( u_shape, gl_PointCoord );\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor *= v_color * u_alpha;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\";\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"fx/bokeh\\\", LGraphFXBokeh);\\r\\n global.LGraphFXBokeh = LGraphFXBokeh;\\r\\n\\r\\n //************************************************\\r\\n\\r\\n function LGraphFXGeneric() {\\r\\n this.addInput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n this.addInput(\\\"value1\\\", \\\"number\\\");\\r\\n this.addInput(\\\"value2\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n this.properties = {\\r\\n fx: \\\"halftone\\\",\\r\\n value1: 1,\\r\\n value2: 1,\\r\\n precision: LGraphTexture.DEFAULT\\r\\n };\\r\\n }\\r\\n\\r\\n LGraphFXGeneric.title = \\\"FX\\\";\\r\\n LGraphFXGeneric.desc = \\\"applies an FX from a list\\\";\\r\\n\\r\\n LGraphFXGeneric.widgets_info = {\\r\\n fx: {\\r\\n widget: \\\"combo\\\",\\r\\n values: [\\\"halftone\\\", \\\"pixelate\\\", \\\"lowpalette\\\", \\\"noise\\\", \\\"gamma\\\"]\\r\\n },\\r\\n precision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n };\\r\\n LGraphFXGeneric.shaders = {};\\r\\n\\r\\n LGraphFXGeneric.prototype.onExecute = function() {\\r\\n if (!this.isOutputConnected(0)) {\\r\\n return;\\r\\n } //saves work\\r\\n\\r\\n var tex = this.getInputData(0);\\r\\n if (this.properties.precision === LGraphTexture.PASS_THROUGH) {\\r\\n this.setOutputData(0, tex);\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!tex) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this._tex = LGraphTexture.getTargetTexture(\\r\\n tex,\\r\\n this._tex,\\r\\n this.properties.precision\\r\\n );\\r\\n\\r\\n //iterations\\r\\n var value1 = this.properties.value1;\\r\\n if (this.isInputConnected(1)) {\\r\\n value1 = this.getInputData(1);\\r\\n this.properties.value1 = value1;\\r\\n }\\r\\n\\r\\n var value2 = this.properties.value2;\\r\\n if (this.isInputConnected(2)) {\\r\\n value2 = this.getInputData(2);\\r\\n this.properties.value2 = value2;\\r\\n }\\r\\n\\r\\n var fx = this.properties.fx;\\r\\n var shader = LGraphFXGeneric.shaders[fx];\\r\\n if (!shader) {\\r\\n var pixel_shader_code = LGraphFXGeneric[\\\"pixel_shader_\\\" + fx];\\r\\n if (!pixel_shader_code) {\\r\\n return;\\r\\n }\\r\\n\\r\\n shader = LGraphFXGeneric.shaders[fx] = new GL.Shader(\\r\\n Shader.SCREEN_VERTEX_SHADER,\\r\\n pixel_shader_code\\r\\n );\\r\\n }\\r\\n\\r\\n gl.disable(gl.BLEND);\\r\\n gl.disable(gl.DEPTH_TEST);\\r\\n var mesh = Mesh.getScreenQuad();\\r\\n var camera = global.LS ? LS.Renderer._current_camera : null;\\r\\n var camera_planes;\\r\\n if (camera) {\\r\\n camera_planes = [\\r\\n LS.Renderer._current_camera.near,\\r\\n LS.Renderer._current_camera.far\\r\\n ];\\r\\n } else {\\r\\n camera_planes = [1, 100];\\r\\n }\\r\\n\\r\\n var noise = null;\\r\\n if (fx == \\\"noise\\\") {\\r\\n noise = LGraphTexture.getNoiseTexture();\\r\\n }\\r\\n\\r\\n this._tex.drawTo(function() {\\r\\n tex.bind(0);\\r\\n if (fx == \\\"noise\\\") {\\r\\n noise.bind(1);\\r\\n }\\r\\n\\r\\n shader\\r\\n .uniforms({\\r\\n u_texture: 0,\\r\\n u_noise: 1,\\r\\n u_size: [tex.width, tex.height],\\r\\n u_rand: [Math.random(), Math.random()],\\r\\n u_value1: value1,\\r\\n u_value2: value2,\\r\\n u_camera_planes: camera_planes\\r\\n })\\r\\n .draw(mesh);\\r\\n });\\r\\n\\r\\n this.setOutputData(0, this._tex);\\r\\n };\\r\\n\\r\\n LGraphFXGeneric.pixel_shader_halftone =\\r\\n \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_camera_planes;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_size;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_value1;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_value2;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tfloat pattern() {\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat s = sin(u_value1 * 3.1415), c = cos(u_value1 * 3.1415);\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 tex = v_coord * u_size.xy;\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 point = vec2(\\\\n\\\\\\r\\n\\t\\t\\t\\t c * tex.x - s * tex.y ,\\\\n\\\\\\r\\n\\t\\t\\t\\t s * tex.x + c * tex.y \\\\n\\\\\\r\\n\\t\\t\\t\\t) * u_value2;\\\\n\\\\\\r\\n\\t\\t\\t\\treturn (sin(point.x) * sin(point.y)) * 4.0;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat average = (color.r + color.g + color.b) / 3.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\";\\r\\n\\r\\n LGraphFXGeneric.pixel_shader_pixelate =\\r\\n \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_camera_planes;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_size;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_value1;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_value2;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 coord = vec2( floor(v_coord.x * u_value1) / u_value1, floor(v_coord.y * u_value2) / u_value2 );\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D(u_texture, coord);\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\";\\r\\n\\r\\n LGraphFXGeneric.pixel_shader_lowpalette =\\r\\n \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_camera_planes;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_size;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_value1;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_value2;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = floor(color * u_value1) / u_value1;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\";\\r\\n\\r\\n LGraphFXGeneric.pixel_shader_noise =\\r\\n \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_noise;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_size;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_value1;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_value2;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_rand;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 noise = texture2D(u_noise, v_coord * vec2(u_size.x / 512.0, u_size.y / 512.0) + u_rand).xyz - vec3(0.5);\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = vec4( color.xyz + noise * u_value1, color.a );\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\";\\r\\n\\r\\n LGraphFXGeneric.pixel_shader_gamma =\\r\\n \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_value1;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat gamma = 1.0 / u_value1;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = vec4( pow( color.xyz, vec3(gamma) ), color.a );\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\";\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"fx/generic\\\", LGraphFXGeneric);\\r\\n global.LGraphFXGeneric = LGraphFXGeneric;\\r\\n\\r\\n // Vigneting ************************************\\r\\n\\r\\n function LGraphFXVigneting() {\\r\\n this.addInput(\\\"Tex.\\\", \\\"Texture\\\");\\r\\n this.addInput(\\\"intensity\\\", \\\"number\\\");\\r\\n\\r\\n this.addOutput(\\\"Texture\\\", \\\"Texture\\\");\\r\\n this.properties = {\\r\\n intensity: 1,\\r\\n invert: false,\\r\\n precision: LGraphTexture.DEFAULT\\r\\n };\\r\\n\\r\\n if (!LGraphFXVigneting._shader) {\\r\\n LGraphFXVigneting._shader = new GL.Shader(\\r\\n Shader.SCREEN_VERTEX_SHADER,\\r\\n LGraphFXVigneting.pixel_shader\\r\\n );\\r\\n }\\r\\n }\\r\\n\\r\\n LGraphFXVigneting.title = \\\"Vigneting\\\";\\r\\n LGraphFXVigneting.desc = \\\"Vigneting\\\";\\r\\n\\r\\n LGraphFXVigneting.widgets_info = {\\r\\n precision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n };\\r\\n\\r\\n LGraphFXVigneting.prototype.onExecute = function() {\\r\\n var tex = this.getInputData(0);\\r\\n\\r\\n if (this.properties.precision === LGraphTexture.PASS_THROUGH) {\\r\\n this.setOutputData(0, tex);\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!tex) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this._tex = LGraphTexture.getTargetTexture(\\r\\n tex,\\r\\n this._tex,\\r\\n this.properties.precision\\r\\n );\\r\\n\\r\\n var intensity = this.properties.intensity;\\r\\n if (this.isInputConnected(1)) {\\r\\n intensity = this.getInputData(1);\\r\\n this.properties.intensity = intensity;\\r\\n }\\r\\n\\r\\n gl.disable(gl.BLEND);\\r\\n gl.disable(gl.DEPTH_TEST);\\r\\n\\r\\n var mesh = Mesh.getScreenQuad();\\r\\n var shader = LGraphFXVigneting._shader;\\r\\n var invert = this.properties.invert;\\r\\n\\r\\n this._tex.drawTo(function() {\\r\\n tex.bind(0);\\r\\n shader\\r\\n .uniforms({\\r\\n u_texture: 0,\\r\\n u_intensity: intensity,\\r\\n u_isize: [1 / tex.width, 1 / tex.height],\\r\\n u_invert: invert ? 1 : 0\\r\\n })\\r\\n .draw(mesh);\\r\\n });\\r\\n\\r\\n this.setOutputData(0, this._tex);\\r\\n };\\r\\n\\r\\n LGraphFXVigneting.pixel_shader =\\r\\n \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_intensity;\\\\n\\\\\\r\\n\\t\\t\\tuniform int u_invert;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat luminance = 1.0 - length( v_coord - vec2(0.5) ) * 1.414;\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t\\t\\tif(u_invert == 1)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tluminance = 1.0 - luminance;\\\\n\\\\\\r\\n\\t\\t\\t\\tluminance = mix(1.0, luminance, u_intensity);\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = vec4( luminance * color.xyz, color.a);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"fx/vigneting\\\", LGraphFXVigneting);\\r\\n global.LGraphFXVigneting = LGraphFXVigneting;\\r\\n }\\r\\n})(this);\\r\\n\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n var MIDI_COLOR = \\\"#243\\\";\\r\\n\\r\\n function MIDIEvent(data) {\\r\\n this.channel = 0;\\r\\n this.cmd = 0;\\r\\n this.data = new Uint32Array(3);\\r\\n\\r\\n if (data) {\\r\\n this.setup(data);\\r\\n }\\r\\n }\\r\\n\\r\\n LiteGraph.MIDIEvent = MIDIEvent;\\r\\n\\r\\n MIDIEvent.prototype.fromJSON = function(o) {\\r\\n this.setup(o.data);\\r\\n };\\r\\n\\r\\n MIDIEvent.prototype.setup = function(data) {\\r\\n var raw_data = data;\\r\\n if (data.constructor === Object) {\\r\\n raw_data = data.data;\\r\\n }\\r\\n\\r\\n this.data.set(raw_data);\\r\\n\\r\\n var midiStatus = raw_data[0];\\r\\n this.status = midiStatus;\\r\\n\\r\\n var midiCommand = midiStatus & 0xf0;\\r\\n\\r\\n if (midiStatus >= 0xf0) {\\r\\n this.cmd = midiStatus;\\r\\n } else {\\r\\n this.cmd = midiCommand;\\r\\n }\\r\\n\\r\\n if (this.cmd == MIDIEvent.NOTEON && this.velocity == 0) {\\r\\n this.cmd = MIDIEvent.NOTEOFF;\\r\\n }\\r\\n\\r\\n this.cmd_str = MIDIEvent.commands[this.cmd] || \\\"\\\";\\r\\n\\r\\n if (\\r\\n midiCommand >= MIDIEvent.NOTEON ||\\r\\n midiCommand <= MIDIEvent.NOTEOFF\\r\\n ) {\\r\\n this.channel = midiStatus & 0x0f;\\r\\n }\\r\\n };\\r\\n\\r\\n Object.defineProperty(MIDIEvent.prototype, \\\"velocity\\\", {\\r\\n get: function() {\\r\\n if (this.cmd == MIDIEvent.NOTEON) {\\r\\n return this.data[2];\\r\\n }\\r\\n return -1;\\r\\n },\\r\\n set: function(v) {\\r\\n this.data[2] = v; // v / 127;\\r\\n },\\r\\n enumerable: true\\r\\n });\\r\\n\\r\\n MIDIEvent.notes = [\\r\\n \\\"A\\\",\\r\\n \\\"A#\\\",\\r\\n \\\"B\\\",\\r\\n \\\"C\\\",\\r\\n \\\"C#\\\",\\r\\n \\\"D\\\",\\r\\n \\\"D#\\\",\\r\\n \\\"E\\\",\\r\\n \\\"F\\\",\\r\\n \\\"F#\\\",\\r\\n \\\"G\\\",\\r\\n \\\"G#\\\"\\r\\n ];\\r\\n MIDIEvent.note_to_index = {\\r\\n A: 0,\\r\\n \\\"A#\\\": 1,\\r\\n B: 2,\\r\\n C: 3,\\r\\n \\\"C#\\\": 4,\\r\\n D: 5,\\r\\n \\\"D#\\\": 6,\\r\\n E: 7,\\r\\n F: 8,\\r\\n \\\"F#\\\": 9,\\r\\n G: 10,\\r\\n \\\"G#\\\": 11\\r\\n };\\r\\n\\r\\n Object.defineProperty(MIDIEvent.prototype, \\\"note\\\", {\\r\\n get: function() {\\r\\n if (this.cmd != MIDIEvent.NOTEON) {\\r\\n return -1;\\r\\n }\\r\\n return MIDIEvent.toNoteString(this.data[1], true);\\r\\n },\\r\\n set: function(v) {\\r\\n throw \\\"notes cannot be assigned this way, must modify the data[1]\\\";\\r\\n },\\r\\n enumerable: true\\r\\n });\\r\\n\\r\\n Object.defineProperty(MIDIEvent.prototype, \\\"octave\\\", {\\r\\n get: function() {\\r\\n if (this.cmd != MIDIEvent.NOTEON) {\\r\\n return -1;\\r\\n }\\r\\n var octave = this.data[1] - 24;\\r\\n return Math.floor(octave / 12 + 1);\\r\\n },\\r\\n set: function(v) {\\r\\n throw \\\"octave cannot be assigned this way, must modify the data[1]\\\";\\r\\n },\\r\\n enumerable: true\\r\\n });\\r\\n\\r\\n //returns HZs\\r\\n MIDIEvent.prototype.getPitch = function() {\\r\\n return Math.pow(2, (this.data[1] - 69) / 12) * 440;\\r\\n };\\r\\n\\r\\n MIDIEvent.computePitch = function(note) {\\r\\n return Math.pow(2, (note - 69) / 12) * 440;\\r\\n };\\r\\n\\r\\n MIDIEvent.prototype.getCC = function() {\\r\\n return this.data[1];\\r\\n };\\r\\n\\r\\n MIDIEvent.prototype.getCCValue = function() {\\r\\n return this.data[2];\\r\\n };\\r\\n\\r\\n //not tested, there is a formula missing here\\r\\n MIDIEvent.prototype.getPitchBend = function() {\\r\\n return this.data[1] + (this.data[2] << 7) - 8192;\\r\\n };\\r\\n\\r\\n MIDIEvent.computePitchBend = function(v1, v2) {\\r\\n return v1 + (v2 << 7) - 8192;\\r\\n };\\r\\n\\r\\n MIDIEvent.prototype.setCommandFromString = function(str) {\\r\\n this.cmd = MIDIEvent.computeCommandFromString(str);\\r\\n };\\r\\n\\r\\n MIDIEvent.computeCommandFromString = function(str) {\\r\\n if (!str) {\\r\\n return 0;\\r\\n }\\r\\n\\r\\n if (str && str.constructor === Number) {\\r\\n return str;\\r\\n }\\r\\n\\r\\n str = str.toUpperCase();\\r\\n switch (str) {\\r\\n case \\\"NOTE ON\\\":\\r\\n case \\\"NOTEON\\\":\\r\\n return MIDIEvent.NOTEON;\\r\\n break;\\r\\n case \\\"NOTE OFF\\\":\\r\\n case \\\"NOTEOFF\\\":\\r\\n return MIDIEvent.NOTEON;\\r\\n break;\\r\\n case \\\"KEY PRESSURE\\\":\\r\\n case \\\"KEYPRESSURE\\\":\\r\\n return MIDIEvent.KEYPRESSURE;\\r\\n break;\\r\\n case \\\"CONTROLLER CHANGE\\\":\\r\\n case \\\"CONTROLLERCHANGE\\\":\\r\\n case \\\"CC\\\":\\r\\n return MIDIEvent.CONTROLLERCHANGE;\\r\\n break;\\r\\n case \\\"PROGRAM CHANGE\\\":\\r\\n case \\\"PROGRAMCHANGE\\\":\\r\\n case \\\"PC\\\":\\r\\n return MIDIEvent.PROGRAMCHANGE;\\r\\n break;\\r\\n case \\\"CHANNEL PRESSURE\\\":\\r\\n case \\\"CHANNELPRESSURE\\\":\\r\\n return MIDIEvent.CHANNELPRESSURE;\\r\\n break;\\r\\n case \\\"PITCH BEND\\\":\\r\\n case \\\"PITCHBEND\\\":\\r\\n return MIDIEvent.PITCHBEND;\\r\\n break;\\r\\n case \\\"TIME TICK\\\":\\r\\n case \\\"TIMETICK\\\":\\r\\n return MIDIEvent.TIMETICK;\\r\\n break;\\r\\n default:\\r\\n return Number(str); //asume its a hex code\\r\\n }\\r\\n };\\r\\n\\r\\n //transform from a pitch number to string like \\\"C4\\\"\\r\\n MIDIEvent.toNoteString = function(d, skip_octave) {\\r\\n d = Math.round(d); //in case it has decimals\\r\\n var note = d - 21;\\r\\n var octave = Math.floor((d - 24) / 12 + 1);\\r\\n note = note % 12;\\r\\n if (note < 0) {\\r\\n note = 12 + note;\\r\\n }\\r\\n return MIDIEvent.notes[note] + (skip_octave ? \\\"\\\" : octave);\\r\\n };\\r\\n\\r\\n MIDIEvent.NoteStringToPitch = function(str) {\\r\\n str = str.toUpperCase();\\r\\n var note = str[0];\\r\\n var octave = 4;\\r\\n\\r\\n if (str[1] == \\\"#\\\") {\\r\\n note += \\\"#\\\";\\r\\n if (str.length > 2) {\\r\\n octave = Number(str[2]);\\r\\n }\\r\\n } else {\\r\\n if (str.length > 1) {\\r\\n octave = Number(str[1]);\\r\\n }\\r\\n }\\r\\n var pitch = MIDIEvent.note_to_index[note];\\r\\n if (pitch == null) {\\r\\n return null;\\r\\n }\\r\\n return (octave - 1) * 12 + pitch + 21;\\r\\n };\\r\\n\\r\\n MIDIEvent.prototype.toString = function() {\\r\\n var str = \\\"\\\" + this.channel + \\\". \\\";\\r\\n switch (this.cmd) {\\r\\n case MIDIEvent.NOTEON:\\r\\n str += \\\"NOTEON \\\" + MIDIEvent.toNoteString(this.data[1]);\\r\\n break;\\r\\n case MIDIEvent.NOTEOFF:\\r\\n str += \\\"NOTEOFF \\\" + MIDIEvent.toNoteString(this.data[1]);\\r\\n break;\\r\\n case MIDIEvent.CONTROLLERCHANGE:\\r\\n str += \\\"CC \\\" + this.data[1] + \\\" \\\" + this.data[2];\\r\\n break;\\r\\n case MIDIEvent.PROGRAMCHANGE:\\r\\n str += \\\"PC \\\" + this.data[1];\\r\\n break;\\r\\n case MIDIEvent.PITCHBEND:\\r\\n str += \\\"PITCHBEND \\\" + this.getPitchBend();\\r\\n break;\\r\\n case MIDIEvent.KEYPRESSURE:\\r\\n str += \\\"KEYPRESS \\\" + this.data[1];\\r\\n break;\\r\\n }\\r\\n\\r\\n return str;\\r\\n };\\r\\n\\r\\n MIDIEvent.prototype.toHexString = function() {\\r\\n var str = \\\"\\\";\\r\\n for (var i = 0; i < this.data.length; i++) {\\r\\n str += this.data[i].toString(16) + \\\" \\\";\\r\\n }\\r\\n };\\r\\n\\r\\n MIDIEvent.prototype.toJSON = function() {\\r\\n return {\\r\\n data: [this.data[0], this.data[1], this.data[2]],\\r\\n object_class: \\\"MIDIEvent\\\"\\r\\n };\\r\\n };\\r\\n\\r\\n MIDIEvent.NOTEOFF = 0x80;\\r\\n MIDIEvent.NOTEON = 0x90;\\r\\n MIDIEvent.KEYPRESSURE = 0xa0;\\r\\n MIDIEvent.CONTROLLERCHANGE = 0xb0;\\r\\n MIDIEvent.PROGRAMCHANGE = 0xc0;\\r\\n MIDIEvent.CHANNELPRESSURE = 0xd0;\\r\\n MIDIEvent.PITCHBEND = 0xe0;\\r\\n MIDIEvent.TIMETICK = 0xf8;\\r\\n\\r\\n MIDIEvent.commands = {\\r\\n 0x80: \\\"note off\\\",\\r\\n 0x90: \\\"note on\\\",\\r\\n 0xa0: \\\"key pressure\\\",\\r\\n 0xb0: \\\"controller change\\\",\\r\\n 0xc0: \\\"program change\\\",\\r\\n 0xd0: \\\"channel pressure\\\",\\r\\n 0xe0: \\\"pitch bend\\\",\\r\\n 0xf0: \\\"system\\\",\\r\\n 0xf2: \\\"Song pos\\\",\\r\\n 0xf3: \\\"Song select\\\",\\r\\n 0xf6: \\\"Tune request\\\",\\r\\n 0xf8: \\\"time tick\\\",\\r\\n 0xfa: \\\"Start Song\\\",\\r\\n 0xfb: \\\"Continue Song\\\",\\r\\n 0xfc: \\\"Stop Song\\\",\\r\\n 0xfe: \\\"Sensing\\\",\\r\\n 0xff: \\\"Reset\\\"\\r\\n };\\r\\n\\r\\n MIDIEvent.commands_short = {\\r\\n 0x80: \\\"NOTEOFF\\\",\\r\\n 0x90: \\\"NOTEOFF\\\",\\r\\n 0xa0: \\\"KEYP\\\",\\r\\n 0xb0: \\\"CC\\\",\\r\\n 0xc0: \\\"PC\\\",\\r\\n 0xd0: \\\"CP\\\",\\r\\n 0xe0: \\\"PB\\\",\\r\\n 0xf0: \\\"SYS\\\",\\r\\n 0xf2: \\\"POS\\\",\\r\\n 0xf3: \\\"SELECT\\\",\\r\\n 0xf6: \\\"TUNEREQ\\\",\\r\\n 0xf8: \\\"TT\\\",\\r\\n 0xfa: \\\"START\\\",\\r\\n 0xfb: \\\"CONTINUE\\\",\\r\\n 0xfc: \\\"STOP\\\",\\r\\n 0xfe: \\\"SENS\\\",\\r\\n 0xff: \\\"RESET\\\"\\r\\n };\\r\\n\\r\\n MIDIEvent.commands_reversed = {};\\r\\n for (var i in MIDIEvent.commands) {\\r\\n MIDIEvent.commands_reversed[MIDIEvent.commands[i]] = i;\\r\\n }\\r\\n\\r\\n //MIDI wrapper\\r\\n function MIDIInterface(on_ready, on_error) {\\r\\n if (!navigator.requestMIDIAccess) {\\r\\n this.error = \\\"not suppoorted\\\";\\r\\n if (on_error) {\\r\\n on_error(\\\"Not supported\\\");\\r\\n } else {\\r\\n console.error(\\\"MIDI NOT SUPPORTED, enable by chrome://flags\\\");\\r\\n }\\r\\n return;\\r\\n }\\r\\n\\r\\n this.on_ready = on_ready;\\r\\n\\r\\n this.state = {\\r\\n note: [],\\r\\n cc: []\\r\\n };\\r\\n\\r\\n navigator\\r\\n .requestMIDIAccess()\\r\\n .then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this));\\r\\n }\\r\\n\\r\\n MIDIInterface.input = null;\\r\\n\\r\\n MIDIInterface.MIDIEvent = MIDIEvent;\\r\\n\\r\\n MIDIInterface.prototype.onMIDISuccess = function(midiAccess) {\\r\\n console.log(\\\"MIDI ready!\\\");\\r\\n console.log(midiAccess);\\r\\n this.midi = midiAccess; // store in the global (in real usage, would probably keep in an object instance)\\r\\n this.updatePorts();\\r\\n\\r\\n if (this.on_ready) {\\r\\n this.on_ready(this);\\r\\n }\\r\\n };\\r\\n\\r\\n MIDIInterface.prototype.updatePorts = function() {\\r\\n var midi = this.midi;\\r\\n this.input_ports = midi.inputs;\\r\\n var num = 0;\\r\\n\\r\\n var it = this.input_ports.values();\\r\\n var it_value = it.next();\\r\\n while (it_value && it_value.done === false) {\\r\\n var port_info = it_value.value;\\r\\n console.log(\\r\\n \\\"Input port [type:'\\\" +\\r\\n port_info.type +\\r\\n \\\"'] id:'\\\" +\\r\\n port_info.id +\\r\\n \\\"' manufacturer:'\\\" +\\r\\n port_info.manufacturer +\\r\\n \\\"' name:'\\\" +\\r\\n port_info.name +\\r\\n \\\"' version:'\\\" +\\r\\n port_info.version +\\r\\n \\\"'\\\"\\r\\n );\\r\\n num++;\\r\\n it_value = it.next();\\r\\n }\\r\\n this.num_input_ports = num;\\r\\n\\r\\n num = 0;\\r\\n this.output_ports = midi.outputs;\\r\\n var it = this.output_ports.values();\\r\\n var it_value = it.next();\\r\\n while (it_value && it_value.done === false) {\\r\\n var port_info = it_value.value;\\r\\n console.log(\\r\\n \\\"Output port [type:'\\\" +\\r\\n port_info.type +\\r\\n \\\"'] id:'\\\" +\\r\\n port_info.id +\\r\\n \\\"' manufacturer:'\\\" +\\r\\n port_info.manufacturer +\\r\\n \\\"' name:'\\\" +\\r\\n port_info.name +\\r\\n \\\"' version:'\\\" +\\r\\n port_info.version +\\r\\n \\\"'\\\"\\r\\n );\\r\\n num++;\\r\\n it_value = it.next();\\r\\n }\\r\\n this.num_output_ports = num;\\r\\n\\r\\n /* OLD WAY\\r\\n\\tfor (var i = 0; i < this.input_ports.size; ++i) {\\r\\n\\t\\t var input = this.input_ports.get(i);\\r\\n\\t\\t if(!input)\\r\\n\\t\\t\\t continue; //sometimes it is null?!\\r\\n\\t\\t\\tconsole.log( \\\"Input port [type:'\\\" + input.type + \\\"'] id:'\\\" + input.id +\\r\\n\\t\\t \\\"' manufacturer:'\\\" + input.manufacturer + \\\"' name:'\\\" + input.name +\\r\\n\\t\\t \\\"' version:'\\\" + input.version + \\\"'\\\" );\\r\\n\\t\\t\\tnum++;\\r\\n\\t }\\r\\n\\tthis.num_input_ports = num;\\r\\n\\r\\n\\r\\n\\tnum = 0;\\r\\n\\tthis.output_ports = midi.outputs;\\r\\n\\tfor (var i = 0; i < this.output_ports.size; ++i) {\\r\\n\\t\\t var output = this.output_ports.get(i);\\r\\n\\t\\t if(!output)\\r\\n\\t\\t\\t continue; \\r\\n\\t\\tconsole.log( \\\"Output port [type:'\\\" + output.type + \\\"'] id:'\\\" + output.id +\\r\\n\\t\\t \\\"' manufacturer:'\\\" + output.manufacturer + \\\"' name:'\\\" + output.name +\\r\\n\\t\\t \\\"' version:'\\\" + output.version + \\\"'\\\" );\\r\\n\\t\\t\\tnum++;\\r\\n\\t }\\r\\n\\tthis.num_output_ports = num;\\r\\n\\t*/\\r\\n };\\r\\n\\r\\n MIDIInterface.prototype.onMIDIFailure = function(msg) {\\r\\n console.error(\\\"Failed to get MIDI access - \\\" + msg);\\r\\n };\\r\\n\\r\\n MIDIInterface.prototype.openInputPort = function(port, callback) {\\r\\n var input_port = this.input_ports.get(\\\"input-\\\" + port);\\r\\n if (!input_port) {\\r\\n return false;\\r\\n }\\r\\n MIDIInterface.input = this;\\r\\n var that = this;\\r\\n\\r\\n input_port.onmidimessage = function(a) {\\r\\n var midi_event = new MIDIEvent(a.data);\\r\\n that.updateState(midi_event);\\r\\n if (callback) {\\r\\n callback(a.data, midi_event);\\r\\n }\\r\\n if (MIDIInterface.on_message) {\\r\\n MIDIInterface.on_message(a.data, midi_event);\\r\\n }\\r\\n };\\r\\n console.log(\\\"port open: \\\", input_port);\\r\\n return true;\\r\\n };\\r\\n\\r\\n MIDIInterface.parseMsg = function(data) {};\\r\\n\\r\\n MIDIInterface.prototype.updateState = function(midi_event) {\\r\\n switch (midi_event.cmd) {\\r\\n case MIDIEvent.NOTEON:\\r\\n this.state.note[midi_event.value1 | 0] = midi_event.value2;\\r\\n break;\\r\\n case MIDIEvent.NOTEOFF:\\r\\n this.state.note[midi_event.value1 | 0] = 0;\\r\\n break;\\r\\n case MIDIEvent.CONTROLLERCHANGE:\\r\\n this.state.cc[midi_event.getCC()] = midi_event.getCCValue();\\r\\n break;\\r\\n }\\r\\n };\\r\\n\\r\\n MIDIInterface.prototype.sendMIDI = function(port, midi_data) {\\r\\n if (!midi_data) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var output_port = this.output_ports.get(\\\"output-\\\" + port);\\r\\n if (!output_port) {\\r\\n return;\\r\\n }\\r\\n\\r\\n MIDIInterface.output = this;\\r\\n\\r\\n if (midi_data.constructor === MIDIEvent) {\\r\\n output_port.send(midi_data.data);\\r\\n } else {\\r\\n output_port.send(midi_data);\\r\\n }\\r\\n };\\r\\n\\r\\n function LGMIDIIn() {\\r\\n this.addOutput(\\\"on_midi\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"out\\\", \\\"midi\\\");\\r\\n this.properties = { port: 0 };\\r\\n this._last_midi_event = null;\\r\\n this._current_midi_event = null;\\r\\n this.boxcolor = \\\"#AAA\\\";\\r\\n this._last_time = 0;\\r\\n\\r\\n var that = this;\\r\\n new MIDIInterface(function(midi) {\\r\\n //open\\r\\n that._midi = midi;\\r\\n if (that._waiting) {\\r\\n that.onStart();\\r\\n }\\r\\n that._waiting = false;\\r\\n });\\r\\n }\\r\\n\\r\\n LGMIDIIn.MIDIInterface = MIDIInterface;\\r\\n\\r\\n LGMIDIIn.title = \\\"MIDI Input\\\";\\r\\n LGMIDIIn.desc = \\\"Reads MIDI from a input port\\\";\\r\\n LGMIDIIn.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDIIn.prototype.getPropertyInfo = function(name) {\\r\\n if (!this._midi) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (name == \\\"port\\\") {\\r\\n var values = {};\\r\\n for (var i = 0; i < this._midi.input_ports.size; ++i) {\\r\\n var input = this._midi.input_ports.get(\\\"input-\\\" + i);\\r\\n values[i] =\\r\\n i + \\\".- \\\" + input.name + \\\" version:\\\" + input.version;\\r\\n }\\r\\n return { type: \\\"enum\\\", values: values };\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIIn.prototype.onStart = function() {\\r\\n if (this._midi) {\\r\\n this._midi.openInputPort(\\r\\n this.properties.port,\\r\\n this.onMIDIEvent.bind(this)\\r\\n );\\r\\n } else {\\r\\n this._waiting = true;\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIIn.prototype.onMIDIEvent = function(data, midi_event) {\\r\\n this._last_midi_event = midi_event;\\r\\n this.boxcolor = \\\"#AFA\\\";\\r\\n this._last_time = LiteGraph.getTime();\\r\\n this.trigger(\\\"on_midi\\\", midi_event);\\r\\n if (midi_event.cmd == MIDIEvent.NOTEON) {\\r\\n this.trigger(\\\"on_noteon\\\", midi_event);\\r\\n } else if (midi_event.cmd == MIDIEvent.NOTEOFF) {\\r\\n this.trigger(\\\"on_noteoff\\\", midi_event);\\r\\n } else if (midi_event.cmd == MIDIEvent.CONTROLLERCHANGE) {\\r\\n this.trigger(\\\"on_cc\\\", midi_event);\\r\\n } else if (midi_event.cmd == MIDIEvent.PROGRAMCHANGE) {\\r\\n this.trigger(\\\"on_pc\\\", midi_event);\\r\\n } else if (midi_event.cmd == MIDIEvent.PITCHBEND) {\\r\\n this.trigger(\\\"on_pitchbend\\\", midi_event);\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIIn.prototype.onDrawBackground = function(ctx) {\\r\\n this.boxcolor = \\\"#AAA\\\";\\r\\n if (!this.flags.collapsed && this._last_midi_event) {\\r\\n ctx.fillStyle = \\\"white\\\";\\r\\n var now = LiteGraph.getTime();\\r\\n var f = 1.0 - Math.max(0, (now - this._last_time) * 0.001);\\r\\n if (f > 0) {\\r\\n var t = ctx.globalAlpha;\\r\\n ctx.globalAlpha *= f;\\r\\n ctx.font = \\\"12px Tahoma\\\";\\r\\n ctx.fillText(\\r\\n this._last_midi_event.toString(),\\r\\n 2,\\r\\n this.size[1] * 0.5 + 3\\r\\n );\\r\\n //ctx.fillRect(0,0,this.size[0],this.size[1]);\\r\\n ctx.globalAlpha = t;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIIn.prototype.onExecute = function() {\\r\\n if (this.outputs) {\\r\\n var last = this._last_midi_event;\\r\\n for (var i = 0; i < this.outputs.length; ++i) {\\r\\n var output = this.outputs[i];\\r\\n var v = null;\\r\\n switch (output.name) {\\r\\n case \\\"midi\\\":\\r\\n v = this._midi;\\r\\n break;\\r\\n case \\\"last_midi\\\":\\r\\n v = last;\\r\\n break;\\r\\n default:\\r\\n continue;\\r\\n }\\r\\n this.setOutputData(i, v);\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIIn.prototype.onGetOutputs = function() {\\r\\n return [\\r\\n [\\\"last_midi\\\", \\\"midi\\\"],\\r\\n [\\\"on_midi\\\", LiteGraph.EVENT],\\r\\n [\\\"on_noteon\\\", LiteGraph.EVENT],\\r\\n [\\\"on_noteoff\\\", LiteGraph.EVENT],\\r\\n [\\\"on_cc\\\", LiteGraph.EVENT],\\r\\n [\\\"on_pc\\\", LiteGraph.EVENT],\\r\\n [\\\"on_pitchbend\\\", LiteGraph.EVENT]\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/input\\\", LGMIDIIn);\\r\\n\\r\\n function LGMIDIOut() {\\r\\n this.addInput(\\\"send\\\", LiteGraph.EVENT);\\r\\n this.properties = { port: 0 };\\r\\n\\r\\n var that = this;\\r\\n new MIDIInterface(function(midi) {\\r\\n that._midi = midi;\\r\\n });\\r\\n }\\r\\n\\r\\n LGMIDIOut.MIDIInterface = MIDIInterface;\\r\\n\\r\\n LGMIDIOut.title = \\\"MIDI Output\\\";\\r\\n LGMIDIOut.desc = \\\"Sends MIDI to output channel\\\";\\r\\n LGMIDIOut.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDIOut.prototype.getPropertyInfo = function(name) {\\r\\n if (!this._midi) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (name == \\\"port\\\") {\\r\\n var values = {};\\r\\n for (var i = 0; i < this._midi.output_ports.size; ++i) {\\r\\n var output = this._midi.output_ports.get(i);\\r\\n values[i] =\\r\\n i + \\\".- \\\" + output.name + \\\" version:\\\" + output.version;\\r\\n }\\r\\n return { type: \\\"enum\\\", values: values };\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIOut.prototype.onAction = function(event, midi_event) {\\r\\n //console.log(midi_event);\\r\\n if (!this._midi) {\\r\\n return;\\r\\n }\\r\\n if (event == \\\"send\\\") {\\r\\n this._midi.sendMIDI(this.port, midi_event);\\r\\n }\\r\\n this.trigger(\\\"midi\\\", midi_event);\\r\\n };\\r\\n\\r\\n LGMIDIOut.prototype.onGetInputs = function() {\\r\\n return [[\\\"send\\\", LiteGraph.ACTION]];\\r\\n };\\r\\n\\r\\n LGMIDIOut.prototype.onGetOutputs = function() {\\r\\n return [[\\\"on_midi\\\", LiteGraph.EVENT]];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/output\\\", LGMIDIOut);\\r\\n\\r\\n function LGMIDIShow() {\\r\\n this.addInput(\\\"on_midi\\\", LiteGraph.EVENT);\\r\\n this._str = \\\"\\\";\\r\\n this.size = [200, 40];\\r\\n }\\r\\n\\r\\n LGMIDIShow.title = \\\"MIDI Show\\\";\\r\\n LGMIDIShow.desc = \\\"Shows MIDI in the graph\\\";\\r\\n LGMIDIShow.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDIShow.prototype.getTitle = function() {\\r\\n if (this.flags.collapsed) {\\r\\n return this._str;\\r\\n }\\r\\n return this.title;\\r\\n };\\r\\n\\r\\n LGMIDIShow.prototype.onAction = function(event, midi_event) {\\r\\n if (!midi_event) {\\r\\n return;\\r\\n }\\r\\n if (midi_event.constructor === MIDIEvent) {\\r\\n this._str = midi_event.toString();\\r\\n } else {\\r\\n this._str = \\\"???\\\";\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIShow.prototype.onDrawForeground = function(ctx) {\\r\\n if (!this._str || this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n ctx.font = \\\"30px Arial\\\";\\r\\n ctx.fillText(this._str, 10, this.size[1] * 0.8);\\r\\n };\\r\\n\\r\\n LGMIDIShow.prototype.onGetInputs = function() {\\r\\n return [[\\\"in\\\", LiteGraph.ACTION]];\\r\\n };\\r\\n\\r\\n LGMIDIShow.prototype.onGetOutputs = function() {\\r\\n return [[\\\"on_midi\\\", LiteGraph.EVENT]];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/show\\\", LGMIDIShow);\\r\\n\\r\\n function LGMIDIFilter() {\\r\\n this.properties = {\\r\\n channel: -1,\\r\\n cmd: -1,\\r\\n min_value: -1,\\r\\n max_value: -1\\r\\n };\\r\\n\\r\\n var that = this;\\r\\n this._learning = false;\\r\\n this.addWidget(\\\"button\\\", \\\"Learn\\\", \\\"\\\", function() {\\r\\n that._learning = true;\\r\\n that.boxcolor = \\\"#FA3\\\";\\r\\n });\\r\\n\\r\\n this.addInput(\\\"in\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"on_midi\\\", LiteGraph.EVENT);\\r\\n this.boxcolor = \\\"#AAA\\\";\\r\\n }\\r\\n\\r\\n LGMIDIFilter.title = \\\"MIDI Filter\\\";\\r\\n LGMIDIFilter.desc = \\\"Filters MIDI messages\\\";\\r\\n LGMIDIFilter.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDIFilter[\\\"@cmd\\\"] = {\\r\\n type: \\\"enum\\\",\\r\\n title: \\\"Command\\\",\\r\\n values: MIDIEvent.commands_reversed\\r\\n };\\r\\n\\r\\n LGMIDIFilter.prototype.getTitle = function() {\\r\\n var str = null;\\r\\n if (this.properties.cmd == -1) {\\r\\n str = \\\"Nothing\\\";\\r\\n } else {\\r\\n str = MIDIEvent.commands_short[this.properties.cmd] || \\\"Unknown\\\";\\r\\n }\\r\\n\\r\\n if (\\r\\n this.properties.min_value != -1 &&\\r\\n this.properties.max_value != -1\\r\\n ) {\\r\\n str +=\\r\\n \\\" \\\" +\\r\\n (this.properties.min_value == this.properties.max_value\\r\\n ? this.properties.max_value\\r\\n : this.properties.min_value +\\r\\n \\\"..\\\" +\\r\\n this.properties.max_value);\\r\\n }\\r\\n\\r\\n return \\\"Filter: \\\" + str;\\r\\n };\\r\\n\\r\\n LGMIDIFilter.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"cmd\\\") {\\r\\n var num = Number(value);\\r\\n if (isNaN(num)) {\\r\\n num = MIDIEvent.commands[value] || 0;\\r\\n }\\r\\n this.properties.cmd = num;\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIFilter.prototype.onAction = function(event, midi_event) {\\r\\n if (!midi_event || midi_event.constructor !== MIDIEvent) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this._learning) {\\r\\n this._learning = false;\\r\\n this.boxcolor = \\\"#AAA\\\";\\r\\n this.properties.channel = midi_event.channel;\\r\\n this.properties.cmd = midi_event.cmd;\\r\\n this.properties.min_value = this.properties.max_value =\\r\\n midi_event.data[1];\\r\\n } else {\\r\\n if (\\r\\n this.properties.channel != -1 &&\\r\\n midi_event.channel != this.properties.channel\\r\\n ) {\\r\\n return;\\r\\n }\\r\\n if (\\r\\n this.properties.cmd != -1 &&\\r\\n midi_event.cmd != this.properties.cmd\\r\\n ) {\\r\\n return;\\r\\n }\\r\\n if (\\r\\n this.properties.min_value != -1 &&\\r\\n midi_event.data[1] < this.properties.min_value\\r\\n ) {\\r\\n return;\\r\\n }\\r\\n if (\\r\\n this.properties.max_value != -1 &&\\r\\n midi_event.data[1] > this.properties.max_value\\r\\n ) {\\r\\n return;\\r\\n }\\r\\n }\\r\\n\\r\\n this.trigger(\\\"on_midi\\\", midi_event);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/filter\\\", LGMIDIFilter);\\r\\n\\r\\n function LGMIDIEvent() {\\r\\n this.properties = {\\r\\n channel: 0,\\r\\n cmd: 144, //0x90\\r\\n value1: 1,\\r\\n value2: 1\\r\\n };\\r\\n\\r\\n this.addInput(\\\"send\\\", LiteGraph.EVENT);\\r\\n this.addInput(\\\"assign\\\", LiteGraph.EVENT);\\r\\n this.addOutput(\\\"on_midi\\\", LiteGraph.EVENT);\\r\\n\\r\\n this.midi_event = new MIDIEvent();\\r\\n this.gate = false;\\r\\n }\\r\\n\\r\\n LGMIDIEvent.title = \\\"MIDIEvent\\\";\\r\\n LGMIDIEvent.desc = \\\"Create a MIDI Event\\\";\\r\\n LGMIDIEvent.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDIEvent.prototype.onAction = function(event, midi_event) {\\r\\n if (event == \\\"assign\\\") {\\r\\n this.properties.channel = midi_event.channel;\\r\\n this.properties.cmd = midi_event.cmd;\\r\\n this.properties.value1 = midi_event.data[1];\\r\\n this.properties.value2 = midi_event.data[2];\\r\\n if (midi_event.cmd == MIDIEvent.NOTEON) {\\r\\n this.gate = true;\\r\\n } else if (midi_event.cmd == MIDIEvent.NOTEOFF) {\\r\\n this.gate = false;\\r\\n }\\r\\n return;\\r\\n }\\r\\n\\r\\n //send\\r\\n var midi_event = this.midi_event;\\r\\n midi_event.channel = this.properties.channel;\\r\\n if (this.properties.cmd && this.properties.cmd.constructor === String) {\\r\\n midi_event.setCommandFromString(this.properties.cmd);\\r\\n } else {\\r\\n midi_event.cmd = this.properties.cmd;\\r\\n }\\r\\n midi_event.data[0] = midi_event.cmd | midi_event.channel;\\r\\n midi_event.data[1] = Number(this.properties.value1);\\r\\n midi_event.data[2] = Number(this.properties.value2);\\r\\n\\r\\n this.trigger(\\\"on_midi\\\", midi_event);\\r\\n };\\r\\n\\r\\n LGMIDIEvent.prototype.onExecute = function() {\\r\\n var props = this.properties;\\r\\n\\r\\n if (this.inputs) {\\r\\n for (var i = 0; i < this.inputs.length; ++i) {\\r\\n var input = this.inputs[i];\\r\\n if (input.link == -1) {\\r\\n continue;\\r\\n }\\r\\n switch (input.name) {\\r\\n case \\\"note\\\":\\r\\n var v = this.getInputData(i);\\r\\n if (v != null) {\\r\\n if (v.constructor === String) {\\r\\n v = MIDIEvent.NoteStringToPitch(v);\\r\\n }\\r\\n this.properties.value1 = (v | 0) % 255;\\r\\n }\\r\\n break;\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.outputs) {\\r\\n for (var i = 0; i < this.outputs.length; ++i) {\\r\\n var output = this.outputs[i];\\r\\n var v = null;\\r\\n switch (output.name) {\\r\\n case \\\"midi\\\":\\r\\n v = new MIDIEvent();\\r\\n v.setup([props.cmd, props.value1, props.value2]);\\r\\n v.channel = props.channel;\\r\\n break;\\r\\n case \\\"command\\\":\\r\\n v = props.cmd;\\r\\n break;\\r\\n case \\\"cc\\\":\\r\\n v = props.value1;\\r\\n break;\\r\\n case \\\"cc_value\\\":\\r\\n v = props.value2;\\r\\n break;\\r\\n case \\\"note\\\":\\r\\n v =\\r\\n props.cmd == MIDIEvent.NOTEON ||\\r\\n props.cmd == MIDIEvent.NOTEOFF\\r\\n ? props.value1\\r\\n : null;\\r\\n break;\\r\\n case \\\"velocity\\\":\\r\\n v = props.cmd == MIDIEvent.NOTEON ? props.value2 : null;\\r\\n break;\\r\\n case \\\"pitch\\\":\\r\\n v =\\r\\n props.cmd == MIDIEvent.NOTEON\\r\\n ? MIDIEvent.computePitch(props.value1)\\r\\n : null;\\r\\n break;\\r\\n case \\\"pitchbend\\\":\\r\\n v =\\r\\n props.cmd == MIDIEvent.PITCHBEND\\r\\n ? MIDIEvent.computePitchBend(\\r\\n props.value1,\\r\\n props.value2\\r\\n )\\r\\n : null;\\r\\n break;\\r\\n case \\\"gate\\\":\\r\\n v = this.gate;\\r\\n break;\\r\\n default:\\r\\n continue;\\r\\n }\\r\\n if (v !== null) {\\r\\n this.setOutputData(i, v);\\r\\n }\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIEvent.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"cmd\\\") {\\r\\n this.properties.cmd = MIDIEvent.computeCommandFromString(value);\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIEvent.prototype.onGetInputs = function() {\\r\\n return [[\\\"note\\\", \\\"number\\\"]];\\r\\n };\\r\\n\\r\\n LGMIDIEvent.prototype.onGetOutputs = function() {\\r\\n return [\\r\\n [\\\"midi\\\", \\\"midi\\\"],\\r\\n [\\\"on_midi\\\", LiteGraph.EVENT],\\r\\n [\\\"command\\\", \\\"number\\\"],\\r\\n [\\\"note\\\", \\\"number\\\"],\\r\\n [\\\"velocity\\\", \\\"number\\\"],\\r\\n [\\\"cc\\\", \\\"number\\\"],\\r\\n [\\\"cc_value\\\", \\\"number\\\"],\\r\\n [\\\"pitch\\\", \\\"number\\\"],\\r\\n [\\\"gate\\\", \\\"bool\\\"],\\r\\n [\\\"pitchbend\\\", \\\"number\\\"]\\r\\n ];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/event\\\", LGMIDIEvent);\\r\\n\\r\\n function LGMIDICC() {\\r\\n this.properties = {\\r\\n //\\t\\tchannel: 0,\\r\\n cc: 1,\\r\\n value: 0\\r\\n };\\r\\n\\r\\n this.addOutput(\\\"value\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n LGMIDICC.title = \\\"MIDICC\\\";\\r\\n LGMIDICC.desc = \\\"gets a Controller Change\\\";\\r\\n LGMIDICC.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDICC.prototype.onExecute = function() {\\r\\n var props = this.properties;\\r\\n if (MIDIInterface.input) {\\r\\n this.properties.value =\\r\\n MIDIInterface.input.state.cc[this.properties.cc];\\r\\n }\\r\\n this.setOutputData(0, this.properties.value);\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/cc\\\", LGMIDICC);\\r\\n\\r\\n function LGMIDIGenerator() {\\r\\n this.addInput(\\\"generate\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"scale\\\", \\\"string\\\");\\r\\n this.addInput(\\\"octave\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"note\\\", LiteGraph.EVENT);\\r\\n this.properties = {\\r\\n notes: \\\"A,A#,B,C,C#,D,D#,E,F,F#,G,G#\\\",\\r\\n octave: 2,\\r\\n duration: 0.5,\\r\\n mode: \\\"sequence\\\"\\r\\n };\\r\\n\\r\\n this.notes_pitches = LGMIDIGenerator.processScale(\\r\\n this.properties.notes\\r\\n );\\r\\n this.sequence_index = 0;\\r\\n }\\r\\n\\r\\n LGMIDIGenerator.title = \\\"MIDI Generator\\\";\\r\\n LGMIDIGenerator.desc = \\\"Generates a random MIDI note\\\";\\r\\n LGMIDIGenerator.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDIGenerator.processScale = function(scale) {\\r\\n var notes = scale.split(\\\",\\\");\\r\\n for (var i = 0; i < notes.length; ++i) {\\r\\n var n = notes[i];\\r\\n if ((n.length == 2 && n[1] != \\\"#\\\") || n.length > 2) {\\r\\n notes[i] = -LiteGraph.MIDIEvent.NoteStringToPitch(n);\\r\\n } else {\\r\\n notes[i] = MIDIEvent.note_to_index[n] || 0;\\r\\n }\\r\\n }\\r\\n return notes;\\r\\n };\\r\\n\\r\\n LGMIDIGenerator.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"notes\\\") {\\r\\n this.notes_pitches = LGMIDIGenerator.processScale(value);\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIGenerator.prototype.onExecute = function() {\\r\\n var octave = this.getInputData(2);\\r\\n if (octave != null) {\\r\\n this.properties.octave = octave;\\r\\n }\\r\\n\\r\\n var scale = this.getInputData(1);\\r\\n if (scale) {\\r\\n this.notes_pitches = LGMIDIGenerator.processScale(scale);\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIGenerator.prototype.onAction = function(event, midi_event) {\\r\\n //var range = this.properties.max - this.properties.min;\\r\\n //var pitch = this.properties.min + ((Math.random() * range)|0);\\r\\n var pitch = 0;\\r\\n var range = this.notes_pitches.length;\\r\\n var index = 0;\\r\\n\\r\\n if (this.properties.mode == \\\"sequence\\\") {\\r\\n index = this.sequence_index = (this.sequence_index + 1) % range;\\r\\n } else if (this.properties.mode == \\\"random\\\") {\\r\\n index = Math.floor(Math.random() * range);\\r\\n }\\r\\n\\r\\n var note = this.notes_pitches[index];\\r\\n if (note >= 0) {\\r\\n pitch = note + (this.properties.octave - 1) * 12 + 33;\\r\\n } else {\\r\\n pitch = -note;\\r\\n }\\r\\n\\r\\n var midi_event = new MIDIEvent();\\r\\n midi_event.setup([MIDIEvent.NOTEON, pitch, 10]);\\r\\n var duration = this.properties.duration || 1;\\r\\n this.trigger(\\\"note\\\", midi_event);\\r\\n\\r\\n //noteoff\\r\\n setTimeout(\\r\\n function() {\\r\\n var midi_event = new MIDIEvent();\\r\\n midi_event.setup([MIDIEvent.NOTEOFF, pitch, 0]);\\r\\n this.trigger(\\\"note\\\", midi_event);\\r\\n }.bind(this),\\r\\n duration * 1000\\r\\n );\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/generator\\\", LGMIDIGenerator);\\r\\n\\r\\n function LGMIDITranspose() {\\r\\n this.properties = {\\r\\n amount: 0\\r\\n };\\r\\n this.addInput(\\\"in\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"amount\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", LiteGraph.EVENT);\\r\\n\\r\\n this.midi_event = new MIDIEvent();\\r\\n }\\r\\n\\r\\n LGMIDITranspose.title = \\\"MIDI Transpose\\\";\\r\\n LGMIDITranspose.desc = \\\"Transpose a MIDI note\\\";\\r\\n LGMIDITranspose.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDITranspose.prototype.onAction = function(event, midi_event) {\\r\\n if (!midi_event || midi_event.constructor !== MIDIEvent) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (\\r\\n midi_event.data[0] == MIDIEvent.NOTEON ||\\r\\n midi_event.data[0] == MIDIEvent.NOTEOFF\\r\\n ) {\\r\\n this.midi_event = new MIDIEvent();\\r\\n this.midi_event.setup(midi_event.data);\\r\\n this.midi_event.data[1] = Math.round(\\r\\n this.midi_event.data[1] + this.properties.amount\\r\\n );\\r\\n this.trigger(\\\"out\\\", this.midi_event);\\r\\n } else {\\r\\n this.trigger(\\\"out\\\", midi_event);\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDITranspose.prototype.onExecute = function() {\\r\\n var amount = this.getInputData(1);\\r\\n if (amount != null) {\\r\\n this.properties.amount = amount;\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/transpose\\\", LGMIDITranspose);\\r\\n\\r\\n function LGMIDIQuantize() {\\r\\n this.properties = {\\r\\n scale: \\\"A,A#,B,C,C#,D,D#,E,F,F#,G,G#\\\"\\r\\n };\\r\\n this.addInput(\\\"note\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"scale\\\", \\\"string\\\");\\r\\n this.addOutput(\\\"out\\\", LiteGraph.EVENT);\\r\\n\\r\\n this.valid_notes = new Array(12);\\r\\n this.offset_notes = new Array(12);\\r\\n this.processScale(this.properties.scale);\\r\\n }\\r\\n\\r\\n LGMIDIQuantize.title = \\\"MIDI Quantize Pitch\\\";\\r\\n LGMIDIQuantize.desc = \\\"Transpose a MIDI note tp fit an scale\\\";\\r\\n LGMIDIQuantize.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDIQuantize.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"scale\\\") {\\r\\n this.processScale(value);\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIQuantize.prototype.processScale = function(scale) {\\r\\n this._current_scale = scale;\\r\\n this.notes_pitches = LGMIDIGenerator.processScale(scale);\\r\\n for (var i = 0; i < 12; ++i) {\\r\\n this.valid_notes[i] = this.notes_pitches.indexOf(i) != -1;\\r\\n }\\r\\n for (var i = 0; i < 12; ++i) {\\r\\n if (this.valid_notes[i]) {\\r\\n this.offset_notes[i] = 0;\\r\\n continue;\\r\\n }\\r\\n for (var j = 1; j < 12; ++j) {\\r\\n if (this.valid_notes[(i - j) % 12]) {\\r\\n this.offset_notes[i] = -j;\\r\\n break;\\r\\n }\\r\\n if (this.valid_notes[(i + j) % 12]) {\\r\\n this.offset_notes[i] = j;\\r\\n break;\\r\\n }\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIQuantize.prototype.onAction = function(event, midi_event) {\\r\\n if (!midi_event || midi_event.constructor !== MIDIEvent) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (\\r\\n midi_event.data[0] == MIDIEvent.NOTEON ||\\r\\n midi_event.data[0] == MIDIEvent.NOTEOFF\\r\\n ) {\\r\\n this.midi_event = new MIDIEvent();\\r\\n this.midi_event.setup(midi_event.data);\\r\\n var note = midi_event.note;\\r\\n var index = MIDIEvent.note_to_index[note];\\r\\n var offset = this.offset_notes[index];\\r\\n this.midi_event.data[1] += offset;\\r\\n this.trigger(\\\"out\\\", this.midi_event);\\r\\n } else {\\r\\n this.trigger(\\\"out\\\", midi_event);\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIQuantize.prototype.onExecute = function() {\\r\\n var scale = this.getInputData(1);\\r\\n if (scale != null && scale != this._current_scale) {\\r\\n this.processScale(scale);\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/quantize\\\", LGMIDIQuantize);\\r\\n\\r\\n function LGMIDIPlay() {\\r\\n this.properties = {\\r\\n volume: 0.5,\\r\\n duration: 1\\r\\n };\\r\\n this.addInput(\\\"note\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"volume\\\", \\\"number\\\");\\r\\n this.addInput(\\\"duration\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"note\\\", LiteGraph.EVENT);\\r\\n\\r\\n if (typeof AudioSynth == \\\"undefined\\\") {\\r\\n console.error(\\r\\n \\\"Audiosynth.js not included, LGMidiPlay requires that library\\\"\\r\\n );\\r\\n this.boxcolor = \\\"red\\\";\\r\\n } else {\\r\\n var Synth = (this.synth = new AudioSynth());\\r\\n this.instrument = Synth.createInstrument(\\\"piano\\\");\\r\\n }\\r\\n }\\r\\n\\r\\n LGMIDIPlay.title = \\\"MIDI Play\\\";\\r\\n LGMIDIPlay.desc = \\\"Plays a MIDI note\\\";\\r\\n LGMIDIPlay.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDIPlay.prototype.onAction = function(event, midi_event) {\\r\\n if (!midi_event || midi_event.constructor !== MIDIEvent) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.instrument && midi_event.data[0] == MIDIEvent.NOTEON) {\\r\\n var note = midi_event.note; //C#\\r\\n if (!note || note == \\\"undefined\\\" || note.constructor !== String) {\\r\\n return;\\r\\n }\\r\\n this.instrument.play(\\r\\n note,\\r\\n midi_event.octave,\\r\\n this.properties.duration,\\r\\n this.properties.volume\\r\\n );\\r\\n }\\r\\n this.trigger(\\\"note\\\", midi_event);\\r\\n };\\r\\n\\r\\n LGMIDIPlay.prototype.onExecute = function() {\\r\\n var volume = this.getInputData(1);\\r\\n if (volume != null) {\\r\\n this.properties.volume = volume;\\r\\n }\\r\\n\\r\\n var duration = this.getInputData(2);\\r\\n if (duration != null) {\\r\\n this.properties.duration = duration;\\r\\n }\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/play\\\", LGMIDIPlay);\\r\\n\\r\\n function LGMIDIKeys() {\\r\\n this.properties = {\\r\\n num_octaves: 2,\\r\\n start_octave: 2\\r\\n };\\r\\n this.addInput(\\\"note\\\", LiteGraph.ACTION);\\r\\n this.addInput(\\\"reset\\\", LiteGraph.ACTION);\\r\\n this.addOutput(\\\"note\\\", LiteGraph.EVENT);\\r\\n this.size = [400, 100];\\r\\n this.keys = [];\\r\\n this._last_key = -1;\\r\\n }\\r\\n\\r\\n LGMIDIKeys.title = \\\"MIDI Keys\\\";\\r\\n LGMIDIKeys.desc = \\\"Keyboard to play notes\\\";\\r\\n LGMIDIKeys.color = MIDI_COLOR;\\r\\n\\r\\n LGMIDIKeys.keys = [\\r\\n { x: 0, w: 1, h: 1, t: 0 },\\r\\n { x: 0.75, w: 0.5, h: 0.6, t: 1 },\\r\\n { x: 1, w: 1, h: 1, t: 0 },\\r\\n { x: 1.75, w: 0.5, h: 0.6, t: 1 },\\r\\n { x: 2, w: 1, h: 1, t: 0 },\\r\\n { x: 2.75, w: 0.5, h: 0.6, t: 1 },\\r\\n { x: 3, w: 1, h: 1, t: 0 },\\r\\n { x: 4, w: 1, h: 1, t: 0 },\\r\\n { x: 4.75, w: 0.5, h: 0.6, t: 1 },\\r\\n { x: 5, w: 1, h: 1, t: 0 },\\r\\n { x: 5.75, w: 0.5, h: 0.6, t: 1 },\\r\\n { x: 6, w: 1, h: 1, t: 0 }\\r\\n ];\\r\\n\\r\\n LGMIDIKeys.prototype.onDrawForeground = function(ctx) {\\r\\n if (this.flags.collapsed) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var num_keys = this.properties.num_octaves * 12;\\r\\n this.keys.length = num_keys;\\r\\n var key_width = this.size[0] / (this.properties.num_octaves * 7);\\r\\n var key_height = this.size[1];\\r\\n\\r\\n ctx.globalAlpha = 1;\\r\\n\\r\\n for (\\r\\n var k = 0;\\r\\n k < 2;\\r\\n k++ //draw first whites (0) then blacks (1)\\r\\n ) {\\r\\n for (var i = 0; i < num_keys; ++i) {\\r\\n var key_info = LGMIDIKeys.keys[i % 12];\\r\\n if (key_info.t != k) {\\r\\n continue;\\r\\n }\\r\\n var octave = Math.floor(i / 12);\\r\\n var x = octave * 7 * key_width + key_info.x * key_width;\\r\\n if (k == 0) {\\r\\n ctx.fillStyle = this.keys[i] ? \\\"#CCC\\\" : \\\"white\\\";\\r\\n } else {\\r\\n ctx.fillStyle = this.keys[i] ? \\\"#333\\\" : \\\"black\\\";\\r\\n }\\r\\n ctx.fillRect(\\r\\n x + 1,\\r\\n 0,\\r\\n key_width * key_info.w - 2,\\r\\n key_height * key_info.h\\r\\n );\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGMIDIKeys.prototype.getKeyIndex = function(pos) {\\r\\n var num_keys = this.properties.num_octaves * 12;\\r\\n var key_width = this.size[0] / (this.properties.num_octaves * 7);\\r\\n var key_height = this.size[1];\\r\\n\\r\\n for (\\r\\n var k = 1;\\r\\n k >= 0;\\r\\n k-- //test blacks first (1) then whites (0)\\r\\n ) {\\r\\n for (var i = 0; i < this.keys.length; ++i) {\\r\\n var key_info = LGMIDIKeys.keys[i % 12];\\r\\n if (key_info.t != k) {\\r\\n continue;\\r\\n }\\r\\n var octave = Math.floor(i / 12);\\r\\n var x = octave * 7 * key_width + key_info.x * key_width;\\r\\n var w = key_width * key_info.w;\\r\\n var h = key_height * key_info.h;\\r\\n if (pos[0] < x || pos[0] > x + w || pos[1] > h) {\\r\\n continue;\\r\\n }\\r\\n return i;\\r\\n }\\r\\n }\\r\\n return -1;\\r\\n };\\r\\n\\r\\n LGMIDIKeys.prototype.onAction = function(event, params) {\\r\\n if (event == \\\"reset\\\") {\\r\\n for (var i = 0; i < this.keys.length; ++i) {\\r\\n this.keys[i] = false;\\r\\n }\\r\\n return;\\r\\n }\\r\\n\\r\\n if (!params || params.constructor !== MIDIEvent) {\\r\\n return;\\r\\n }\\r\\n var midi_event = params;\\r\\n var start_note = (this.properties.start_octave - 1) * 12 + 29;\\r\\n var index = midi_event.data[1] - start_note;\\r\\n if (index >= 0 && index < this.keys.length) {\\r\\n if (midi_event.data[0] == MIDIEvent.NOTEON) {\\r\\n this.keys[index] = true;\\r\\n } else if (midi_event.data[0] == MIDIEvent.NOTEOFF) {\\r\\n this.keys[index] = false;\\r\\n }\\r\\n }\\r\\n\\r\\n this.trigger(\\\"note\\\", midi_event);\\r\\n };\\r\\n\\r\\n LGMIDIKeys.prototype.onMouseDown = function(e, pos) {\\r\\n if (pos[1] < 0) {\\r\\n return;\\r\\n }\\r\\n var index = this.getKeyIndex(pos);\\r\\n this.keys[index] = true;\\r\\n this._last_key = index;\\r\\n var pitch = (this.properties.start_octave - 1) * 12 + 29 + index;\\r\\n var midi_event = new MIDIEvent();\\r\\n midi_event.setup([MIDIEvent.NOTEON, pitch, 100]);\\r\\n this.trigger(\\\"note\\\", midi_event);\\r\\n return true;\\r\\n };\\r\\n\\r\\n LGMIDIKeys.prototype.onMouseMove = function(e, pos) {\\r\\n if (pos[1] < 0 || this._last_key == -1) {\\r\\n return;\\r\\n }\\r\\n this.setDirtyCanvas(true);\\r\\n var index = this.getKeyIndex(pos);\\r\\n if (this._last_key == index) {\\r\\n return true;\\r\\n }\\r\\n this.keys[this._last_key] = false;\\r\\n var pitch =\\r\\n (this.properties.start_octave - 1) * 12 + 29 + this._last_key;\\r\\n var midi_event = new MIDIEvent();\\r\\n midi_event.setup([MIDIEvent.NOTEOFF, pitch, 100]);\\r\\n this.trigger(\\\"note\\\", midi_event);\\r\\n\\r\\n this.keys[index] = true;\\r\\n var pitch = (this.properties.start_octave - 1) * 12 + 29 + index;\\r\\n var midi_event = new MIDIEvent();\\r\\n midi_event.setup([MIDIEvent.NOTEON, pitch, 100]);\\r\\n this.trigger(\\\"note\\\", midi_event);\\r\\n\\r\\n this._last_key = index;\\r\\n return true;\\r\\n };\\r\\n\\r\\n LGMIDIKeys.prototype.onMouseUp = function(e, pos) {\\r\\n if (pos[1] < 0) {\\r\\n return;\\r\\n }\\r\\n var index = this.getKeyIndex(pos);\\r\\n this.keys[index] = false;\\r\\n this._last_key = -1;\\r\\n var pitch = (this.properties.start_octave - 1) * 12 + 29 + index;\\r\\n var midi_event = new MIDIEvent();\\r\\n midi_event.setup([MIDIEvent.NOTEOFF, pitch, 100]);\\r\\n this.trigger(\\\"note\\\", midi_event);\\r\\n return true;\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"midi/keys\\\", LGMIDIKeys);\\r\\n\\r\\n function now() {\\r\\n return window.performance.now();\\r\\n }\\r\\n})(this);\\r\\n\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n var LGAudio = {};\\r\\n global.LGAudio = LGAudio;\\r\\n\\r\\n LGAudio.getAudioContext = function() {\\r\\n if (!this._audio_context) {\\r\\n window.AudioContext =\\r\\n window.AudioContext || window.webkitAudioContext;\\r\\n if (!window.AudioContext) {\\r\\n console.error(\\\"AudioContext not supported by browser\\\");\\r\\n return null;\\r\\n }\\r\\n this._audio_context = new AudioContext();\\r\\n this._audio_context.onmessage = function(msg) {\\r\\n console.log(\\\"msg\\\", msg);\\r\\n };\\r\\n this._audio_context.onended = function(msg) {\\r\\n console.log(\\\"ended\\\", msg);\\r\\n };\\r\\n this._audio_context.oncomplete = function(msg) {\\r\\n console.log(\\\"complete\\\", msg);\\r\\n };\\r\\n }\\r\\n\\r\\n //in case it crashes\\r\\n //if(this._audio_context.state == \\\"suspended\\\")\\r\\n //\\tthis._audio_context.resume();\\r\\n return this._audio_context;\\r\\n };\\r\\n\\r\\n LGAudio.connect = function(audionodeA, audionodeB) {\\r\\n try {\\r\\n audionodeA.connect(audionodeB);\\r\\n } catch (err) {\\r\\n console.warn(\\\"LGraphAudio:\\\", err);\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudio.disconnect = function(audionodeA, audionodeB) {\\r\\n try {\\r\\n audionodeA.disconnect(audionodeB);\\r\\n } catch (err) {\\r\\n console.warn(\\\"LGraphAudio:\\\", err);\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudio.changeAllAudiosConnections = function(node, connect) {\\r\\n if (node.inputs) {\\r\\n for (var i = 0; i < node.inputs.length; ++i) {\\r\\n var input = node.inputs[i];\\r\\n var link_info = node.graph.links[input.link];\\r\\n if (!link_info) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n var origin_node = node.graph.getNodeById(link_info.origin_id);\\r\\n var origin_audionode = null;\\r\\n if (origin_node.getAudioNodeInOutputSlot) {\\r\\n origin_audionode = origin_node.getAudioNodeInOutputSlot(\\r\\n link_info.origin_slot\\r\\n );\\r\\n } else {\\r\\n origin_audionode = origin_node.audionode;\\r\\n }\\r\\n\\r\\n var target_audionode = null;\\r\\n if (node.getAudioNodeInInputSlot) {\\r\\n target_audionode = node.getAudioNodeInInputSlot(i);\\r\\n } else {\\r\\n target_audionode = node.audionode;\\r\\n }\\r\\n\\r\\n if (connect) {\\r\\n LGAudio.connect(origin_audionode, target_audionode);\\r\\n } else {\\r\\n LGAudio.disconnect(origin_audionode, target_audionode);\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (node.outputs) {\\r\\n for (var i = 0; i < node.outputs.length; ++i) {\\r\\n var output = node.outputs[i];\\r\\n for (var j = 0; j < output.links.length; ++j) {\\r\\n var link_info = node.graph.links[output.links[j]];\\r\\n if (!link_info) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n var origin_audionode = null;\\r\\n if (node.getAudioNodeInOutputSlot) {\\r\\n origin_audionode = node.getAudioNodeInOutputSlot(i);\\r\\n } else {\\r\\n origin_audionode = node.audionode;\\r\\n }\\r\\n\\r\\n var target_node = node.graph.getNodeById(\\r\\n link_info.target_id\\r\\n );\\r\\n var target_audionode = null;\\r\\n if (target_node.getAudioNodeInInputSlot) {\\r\\n target_audionode = target_node.getAudioNodeInInputSlot(\\r\\n link_info.target_slot\\r\\n );\\r\\n } else {\\r\\n target_audionode = target_node.audionode;\\r\\n }\\r\\n\\r\\n if (connect) {\\r\\n LGAudio.connect(origin_audionode, target_audionode);\\r\\n } else {\\r\\n LGAudio.disconnect(origin_audionode, target_audionode);\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n //used by many nodes\\r\\n LGAudio.onConnectionsChange = function(\\r\\n connection,\\r\\n slot,\\r\\n connected,\\r\\n link_info\\r\\n ) {\\r\\n //only process the outputs events\\r\\n if (connection != LiteGraph.OUTPUT) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var target_node = null;\\r\\n if (link_info) {\\r\\n target_node = this.graph.getNodeById(link_info.target_id);\\r\\n }\\r\\n\\r\\n if (!target_node) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //get origin audionode\\r\\n var local_audionode = null;\\r\\n if (this.getAudioNodeInOutputSlot) {\\r\\n local_audionode = this.getAudioNodeInOutputSlot(slot);\\r\\n } else {\\r\\n local_audionode = this.audionode;\\r\\n }\\r\\n\\r\\n //get target audionode\\r\\n var target_audionode = null;\\r\\n if (target_node.getAudioNodeInInputSlot) {\\r\\n target_audionode = target_node.getAudioNodeInInputSlot(\\r\\n link_info.target_slot\\r\\n );\\r\\n } else {\\r\\n target_audionode = target_node.audionode;\\r\\n }\\r\\n\\r\\n //do the connection/disconnection\\r\\n if (connected) {\\r\\n LGAudio.connect(local_audionode, target_audionode);\\r\\n } else {\\r\\n LGAudio.disconnect(local_audionode, target_audionode);\\r\\n }\\r\\n };\\r\\n\\r\\n //this function helps creating wrappers to existing classes\\r\\n LGAudio.createAudioNodeWrapper = function(class_object) {\\r\\n var old_func = class_object.prototype.onPropertyChanged;\\r\\n\\r\\n class_object.prototype.onPropertyChanged = function(name, value) {\\r\\n if (old_func) {\\r\\n old_func.call(this, name, value);\\r\\n }\\r\\n\\r\\n if (!this.audionode) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.audionode[name] === undefined) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.audionode[name].value !== undefined) {\\r\\n this.audionode[name].value = value;\\r\\n } else {\\r\\n this.audionode[name] = value;\\r\\n }\\r\\n };\\r\\n\\r\\n class_object.prototype.onConnectionsChange =\\r\\n LGAudio.onConnectionsChange;\\r\\n };\\r\\n\\r\\n //contains the samples decoded of the loaded audios in AudioBuffer format\\r\\n LGAudio.cached_audios = {};\\r\\n\\r\\n LGAudio.loadSound = function(url, on_complete, on_error) {\\r\\n if (LGAudio.cached_audios[url] && url.indexOf(\\\"blob:\\\") == -1) {\\r\\n if (on_complete) {\\r\\n on_complete(LGAudio.cached_audios[url]);\\r\\n }\\r\\n return;\\r\\n }\\r\\n\\r\\n if (LGAudio.onProcessAudioURL) {\\r\\n url = LGAudio.onProcessAudioURL(url);\\r\\n }\\r\\n\\r\\n //load new sample\\r\\n var request = new XMLHttpRequest();\\r\\n request.open(\\\"GET\\\", url, true);\\r\\n request.responseType = \\\"arraybuffer\\\";\\r\\n\\r\\n var context = LGAudio.getAudioContext();\\r\\n\\r\\n // Decode asynchronously\\r\\n request.onload = function() {\\r\\n console.log(\\\"AudioSource loaded\\\");\\r\\n context.decodeAudioData(\\r\\n request.response,\\r\\n function(buffer) {\\r\\n console.log(\\\"AudioSource decoded\\\");\\r\\n LGAudio.cached_audios[url] = buffer;\\r\\n if (on_complete) {\\r\\n on_complete(buffer);\\r\\n }\\r\\n },\\r\\n onError\\r\\n );\\r\\n };\\r\\n request.send();\\r\\n\\r\\n function onError(err) {\\r\\n console.log(\\\"Audio loading sample error:\\\", err);\\r\\n if (on_error) {\\r\\n on_error(err);\\r\\n }\\r\\n }\\r\\n\\r\\n return request;\\r\\n };\\r\\n\\r\\n //****************************************************\\r\\n\\r\\n function LGAudioSource() {\\r\\n this.properties = {\\r\\n src: \\\"\\\",\\r\\n gain: 0.5,\\r\\n loop: true,\\r\\n autoplay: true,\\r\\n playbackRate: 1\\r\\n };\\r\\n\\r\\n this._loading_audio = false;\\r\\n this._audiobuffer = null; //points to AudioBuffer with the audio samples decoded\\r\\n this._audionodes = [];\\r\\n this._last_sourcenode = null; //the last AudioBufferSourceNode (there could be more if there are several sounds playing)\\r\\n\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n this.addInput(\\\"gain\\\", \\\"number\\\");\\r\\n\\r\\n //init context\\r\\n var context = LGAudio.getAudioContext();\\r\\n\\r\\n //create gain node to control volume\\r\\n this.audionode = context.createGain();\\r\\n this.audionode.graphnode = this;\\r\\n this.audionode.gain.value = this.properties.gain;\\r\\n\\r\\n //debug\\r\\n if (this.properties.src) {\\r\\n this.loadSound(this.properties.src);\\r\\n }\\r\\n }\\r\\n\\r\\n LGAudioSource[\\\"@src\\\"] = { widget: \\\"resource\\\" };\\r\\n LGAudioSource.supported_extensions = [\\\"wav\\\", \\\"ogg\\\", \\\"mp3\\\"];\\r\\n\\r\\n LGAudioSource.prototype.onAdded = function(graph) {\\r\\n if (graph.status === LGraph.STATUS_RUNNING) {\\r\\n this.onStart();\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.onStart = function() {\\r\\n if (!this._audiobuffer) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (this.properties.autoplay) {\\r\\n this.playBuffer(this._audiobuffer);\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.onStop = function() {\\r\\n this.stopAllSounds();\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.onPause = function() {\\r\\n this.pauseAllSounds();\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.onUnpause = function() {\\r\\n this.unpauseAllSounds();\\r\\n //this.onStart();\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.onRemoved = function() {\\r\\n this.stopAllSounds();\\r\\n if (this._dropped_url) {\\r\\n URL.revokeObjectURL(this._url);\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.stopAllSounds = function() {\\r\\n //iterate and stop\\r\\n for (var i = 0; i < this._audionodes.length; ++i) {\\r\\n if (this._audionodes[i].started) {\\r\\n this._audionodes[i].started = false;\\r\\n this._audionodes[i].stop();\\r\\n }\\r\\n //this._audionodes[i].disconnect( this.audionode );\\r\\n }\\r\\n this._audionodes.length = 0;\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.pauseAllSounds = function() {\\r\\n LGAudio.getAudioContext().suspend();\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.unpauseAllSounds = function() {\\r\\n LGAudio.getAudioContext().resume();\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.onExecute = function() {\\r\\n if (this.inputs) {\\r\\n for (var i = 0; i < this.inputs.length; ++i) {\\r\\n var input = this.inputs[i];\\r\\n if (input.link == null) {\\r\\n continue;\\r\\n }\\r\\n var v = this.getInputData(i);\\r\\n if (v === undefined) {\\r\\n continue;\\r\\n }\\r\\n if (input.name == \\\"gain\\\") {\\r\\n this.audionode.gain.value = v;\\r\\n } else if (input.name == \\\"playbackRate\\\") {\\r\\n this.properties.playbackRate = v;\\r\\n for (var j = 0; j < this._audionodes.length; ++j) {\\r\\n this._audionodes[j].playbackRate.value = v;\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n\\r\\n if (this.outputs) {\\r\\n for (var i = 0; i < this.outputs.length; ++i) {\\r\\n var output = this.outputs[i];\\r\\n if (output.name == \\\"buffer\\\" && this._audiobuffer) {\\r\\n this.setOutputData(i, this._audiobuffer);\\r\\n }\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.onAction = function(event) {\\r\\n if (this._audiobuffer) {\\r\\n if (event == \\\"Play\\\") {\\r\\n this.playBuffer(this._audiobuffer);\\r\\n } else if (event == \\\"Stop\\\") {\\r\\n this.stopAllSounds();\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"src\\\") {\\r\\n this.loadSound(value);\\r\\n } else if (name == \\\"gain\\\") {\\r\\n this.audionode.gain.value = value;\\r\\n } else if (name == \\\"playbackRate\\\") {\\r\\n for (var j = 0; j < this._audionodes.length; ++j) {\\r\\n this._audionodes[j].playbackRate.value = value;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.playBuffer = function(buffer) {\\r\\n var that = this;\\r\\n var context = LGAudio.getAudioContext();\\r\\n\\r\\n //create a new audionode (this is mandatory, AudioAPI doesnt like to reuse old ones)\\r\\n var audionode = context.createBufferSource(); //create a AudioBufferSourceNode\\r\\n this._last_sourcenode = audionode;\\r\\n audionode.graphnode = this;\\r\\n audionode.buffer = buffer;\\r\\n audionode.loop = this.properties.loop;\\r\\n audionode.playbackRate.value = this.properties.playbackRate;\\r\\n this._audionodes.push(audionode);\\r\\n audionode.connect(this.audionode); //connect to gain\\r\\n this._audionodes.push(audionode);\\r\\n\\r\\n audionode.onended = function() {\\r\\n //console.log(\\\"ended!\\\");\\r\\n that.trigger(\\\"ended\\\");\\r\\n //remove\\r\\n var index = that._audionodes.indexOf(audionode);\\r\\n if (index != -1) {\\r\\n that._audionodes.splice(index, 1);\\r\\n }\\r\\n };\\r\\n\\r\\n if (!audionode.started) {\\r\\n audionode.started = true;\\r\\n audionode.start();\\r\\n }\\r\\n return audionode;\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.loadSound = function(url) {\\r\\n var that = this;\\r\\n\\r\\n //kill previous load\\r\\n if (this._request) {\\r\\n this._request.abort();\\r\\n this._request = null;\\r\\n }\\r\\n\\r\\n this._audiobuffer = null; //points to the audiobuffer once the audio is loaded\\r\\n this._loading_audio = false;\\r\\n\\r\\n if (!url) {\\r\\n return;\\r\\n }\\r\\n\\r\\n this._request = LGAudio.loadSound(url, inner);\\r\\n\\r\\n this._loading_audio = true;\\r\\n this.boxcolor = \\\"#AA4\\\";\\r\\n\\r\\n function inner(buffer) {\\r\\n this.boxcolor = LiteGraph.NODE_DEFAULT_BOXCOLOR;\\r\\n that._audiobuffer = buffer;\\r\\n that._loading_audio = false;\\r\\n //if is playing, then play it\\r\\n if (that.graph && that.graph.status === LGraph.STATUS_RUNNING) {\\r\\n that.onStart();\\r\\n } //this controls the autoplay already\\r\\n }\\r\\n };\\r\\n\\r\\n //Helps connect/disconnect AudioNodes when new connections are made in the node\\r\\n LGAudioSource.prototype.onConnectionsChange = LGAudio.onConnectionsChange;\\r\\n\\r\\n LGAudioSource.prototype.onGetInputs = function() {\\r\\n return [\\r\\n [\\\"playbackRate\\\", \\\"number\\\"],\\r\\n [\\\"Play\\\", LiteGraph.ACTION],\\r\\n [\\\"Stop\\\", LiteGraph.ACTION]\\r\\n ];\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.onGetOutputs = function() {\\r\\n return [[\\\"buffer\\\", \\\"audiobuffer\\\"], [\\\"ended\\\", LiteGraph.EVENT]];\\r\\n };\\r\\n\\r\\n LGAudioSource.prototype.onDropFile = function(file) {\\r\\n if (this._dropped_url) {\\r\\n URL.revokeObjectURL(this._dropped_url);\\r\\n }\\r\\n var url = URL.createObjectURL(file);\\r\\n this.properties.src = url;\\r\\n this.loadSound(url);\\r\\n this._dropped_url = url;\\r\\n };\\r\\n\\r\\n LGAudioSource.title = \\\"Source\\\";\\r\\n LGAudioSource.desc = \\\"Plays audio\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/source\\\", LGAudioSource);\\r\\n\\r\\n //****************************************************\\r\\n\\r\\n function LGAudioMediaSource() {\\r\\n this.properties = {\\r\\n gain: 0.5\\r\\n };\\r\\n\\r\\n this._audionodes = [];\\r\\n this._media_stream = null;\\r\\n\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n this.addInput(\\\"gain\\\", \\\"number\\\");\\r\\n\\r\\n //create gain node to control volume\\r\\n var context = LGAudio.getAudioContext();\\r\\n this.audionode = context.createGain();\\r\\n this.audionode.graphnode = this;\\r\\n this.audionode.gain.value = this.properties.gain;\\r\\n }\\r\\n\\r\\n LGAudioMediaSource.prototype.onAdded = function(graph) {\\r\\n if (graph.status === LGraph.STATUS_RUNNING) {\\r\\n this.onStart();\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.prototype.onStart = function() {\\r\\n if (this._media_stream == null && !this._waiting_confirmation) {\\r\\n this.openStream();\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.prototype.onStop = function() {\\r\\n this.audionode.gain.value = 0;\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.prototype.onPause = function() {\\r\\n this.audionode.gain.value = 0;\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.prototype.onUnpause = function() {\\r\\n this.audionode.gain.value = this.properties.gain;\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.prototype.onRemoved = function() {\\r\\n this.audionode.gain.value = 0;\\r\\n if (this.audiosource_node) {\\r\\n this.audiosource_node.disconnect(this.audionode);\\r\\n this.audiosource_node = null;\\r\\n }\\r\\n if (this._media_stream) {\\r\\n var tracks = this._media_stream.getTracks();\\r\\n if (tracks.length) {\\r\\n tracks[0].stop();\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.prototype.openStream = function() {\\r\\n if (!navigator.mediaDevices) {\\r\\n console.log(\\r\\n \\\"getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags\\\"\\r\\n );\\r\\n return;\\r\\n }\\r\\n\\r\\n this._waiting_confirmation = true;\\r\\n\\r\\n // Not showing vendor prefixes.\\r\\n navigator.mediaDevices\\r\\n .getUserMedia({ audio: true, video: false })\\r\\n .then(this.streamReady.bind(this))\\r\\n .catch(onFailSoHard);\\r\\n\\r\\n var that = this;\\r\\n function onFailSoHard(err) {\\r\\n console.log(\\\"Media rejected\\\", err);\\r\\n that._media_stream = false;\\r\\n that.boxcolor = \\\"red\\\";\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.prototype.streamReady = function(localMediaStream) {\\r\\n this._media_stream = localMediaStream;\\r\\n //this._waiting_confirmation = false;\\r\\n\\r\\n //init context\\r\\n if (this.audiosource_node) {\\r\\n this.audiosource_node.disconnect(this.audionode);\\r\\n }\\r\\n var context = LGAudio.getAudioContext();\\r\\n this.audiosource_node = context.createMediaStreamSource(\\r\\n localMediaStream\\r\\n );\\r\\n this.audiosource_node.graphnode = this;\\r\\n this.audiosource_node.connect(this.audionode);\\r\\n this.boxcolor = \\\"white\\\";\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.prototype.onExecute = function() {\\r\\n if (this._media_stream == null && !this._waiting_confirmation) {\\r\\n this.openStream();\\r\\n }\\r\\n\\r\\n if (this.inputs) {\\r\\n for (var i = 0; i < this.inputs.length; ++i) {\\r\\n var input = this.inputs[i];\\r\\n if (input.link == null) {\\r\\n continue;\\r\\n }\\r\\n var v = this.getInputData(i);\\r\\n if (v === undefined) {\\r\\n continue;\\r\\n }\\r\\n if (input.name == \\\"gain\\\") {\\r\\n this.audionode.gain.value = this.properties.gain = v;\\r\\n }\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.prototype.onAction = function(event) {\\r\\n if (event == \\\"Play\\\") {\\r\\n this.audionode.gain.value = this.properties.gain;\\r\\n } else if (event == \\\"Stop\\\") {\\r\\n this.audionode.gain.value = 0;\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"gain\\\") {\\r\\n this.audionode.gain.value = value;\\r\\n }\\r\\n };\\r\\n\\r\\n //Helps connect/disconnect AudioNodes when new connections are made in the node\\r\\n LGAudioMediaSource.prototype.onConnectionsChange =\\r\\n LGAudio.onConnectionsChange;\\r\\n\\r\\n LGAudioMediaSource.prototype.onGetInputs = function() {\\r\\n return [\\r\\n [\\\"playbackRate\\\", \\\"number\\\"],\\r\\n [\\\"Play\\\", LiteGraph.ACTION],\\r\\n [\\\"Stop\\\", LiteGraph.ACTION]\\r\\n ];\\r\\n };\\r\\n\\r\\n LGAudioMediaSource.title = \\\"MediaSource\\\";\\r\\n LGAudioMediaSource.desc = \\\"Plays microphone\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/media_source\\\", LGAudioMediaSource);\\r\\n\\r\\n //*****************************************************\\r\\n\\r\\n function LGAudioAnalyser() {\\r\\n this.properties = {\\r\\n fftSize: 2048,\\r\\n minDecibels: -100,\\r\\n maxDecibels: -10,\\r\\n smoothingTimeConstant: 0.5\\r\\n };\\r\\n\\r\\n var context = LGAudio.getAudioContext();\\r\\n\\r\\n this.audionode = context.createAnalyser();\\r\\n this.audionode.graphnode = this;\\r\\n this.audionode.fftSize = this.properties.fftSize;\\r\\n this.audionode.minDecibels = this.properties.minDecibels;\\r\\n this.audionode.maxDecibels = this.properties.maxDecibels;\\r\\n this.audionode.smoothingTimeConstant = this.properties.smoothingTimeConstant;\\r\\n\\r\\n this.addInput(\\\"in\\\", \\\"audio\\\");\\r\\n this.addOutput(\\\"freqs\\\", \\\"array\\\");\\r\\n this.addOutput(\\\"samples\\\", \\\"array\\\");\\r\\n\\r\\n this._freq_bin = null;\\r\\n this._time_bin = null;\\r\\n }\\r\\n\\r\\n LGAudioAnalyser.prototype.onPropertyChanged = function(name, value) {\\r\\n this.audionode[name] = value;\\r\\n };\\r\\n\\r\\n LGAudioAnalyser.prototype.onExecute = function() {\\r\\n if (this.isOutputConnected(0)) {\\r\\n //send FFT\\r\\n var bufferLength = this.audionode.frequencyBinCount;\\r\\n if (!this._freq_bin || this._freq_bin.length != bufferLength) {\\r\\n this._freq_bin = new Uint8Array(bufferLength);\\r\\n }\\r\\n this.audionode.getByteFrequencyData(this._freq_bin);\\r\\n this.setOutputData(0, this._freq_bin);\\r\\n }\\r\\n\\r\\n //send analyzer\\r\\n if (this.isOutputConnected(1)) {\\r\\n //send Samples\\r\\n var bufferLength = this.audionode.frequencyBinCount;\\r\\n if (!this._time_bin || this._time_bin.length != bufferLength) {\\r\\n this._time_bin = new Uint8Array(bufferLength);\\r\\n }\\r\\n this.audionode.getByteTimeDomainData(this._time_bin);\\r\\n this.setOutputData(1, this._time_bin);\\r\\n }\\r\\n\\r\\n //properties\\r\\n for (var i = 1; i < this.inputs.length; ++i) {\\r\\n var input = this.inputs[i];\\r\\n if (input.link == null) {\\r\\n continue;\\r\\n }\\r\\n var v = this.getInputData(i);\\r\\n if (v !== undefined) {\\r\\n this.audionode[input.name].value = v;\\r\\n }\\r\\n }\\r\\n\\r\\n //time domain\\r\\n //this.audionode.getFloatTimeDomainData( dataArray );\\r\\n };\\r\\n\\r\\n LGAudioAnalyser.prototype.onGetInputs = function() {\\r\\n return [\\r\\n [\\\"minDecibels\\\", \\\"number\\\"],\\r\\n [\\\"maxDecibels\\\", \\\"number\\\"],\\r\\n [\\\"smoothingTimeConstant\\\", \\\"number\\\"]\\r\\n ];\\r\\n };\\r\\n\\r\\n LGAudioAnalyser.prototype.onGetOutputs = function() {\\r\\n return [[\\\"freqs\\\", \\\"array\\\"], [\\\"samples\\\", \\\"array\\\"]];\\r\\n };\\r\\n\\r\\n LGAudioAnalyser.title = \\\"Analyser\\\";\\r\\n LGAudioAnalyser.desc = \\\"Audio Analyser\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/analyser\\\", LGAudioAnalyser);\\r\\n\\r\\n //*****************************************************\\r\\n\\r\\n function LGAudioGain() {\\r\\n //default\\r\\n this.properties = {\\r\\n gain: 1\\r\\n };\\r\\n\\r\\n this.audionode = LGAudio.getAudioContext().createGain();\\r\\n this.addInput(\\\"in\\\", \\\"audio\\\");\\r\\n this.addInput(\\\"gain\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n }\\r\\n\\r\\n LGAudioGain.prototype.onExecute = function() {\\r\\n if (!this.inputs || !this.inputs.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n for (var i = 1; i < this.inputs.length; ++i) {\\r\\n var input = this.inputs[i];\\r\\n var v = this.getInputData(i);\\r\\n if (v !== undefined) {\\r\\n this.audionode[input.name].value = v;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudio.createAudioNodeWrapper(LGAudioGain);\\r\\n\\r\\n LGAudioGain.title = \\\"Gain\\\";\\r\\n LGAudioGain.desc = \\\"Audio gain\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/gain\\\", LGAudioGain);\\r\\n\\r\\n function LGAudioConvolver() {\\r\\n //default\\r\\n this.properties = {\\r\\n impulse_src: \\\"\\\",\\r\\n normalize: true\\r\\n };\\r\\n\\r\\n this.audionode = LGAudio.getAudioContext().createConvolver();\\r\\n this.addInput(\\\"in\\\", \\\"audio\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n }\\r\\n\\r\\n LGAudio.createAudioNodeWrapper(LGAudioConvolver);\\r\\n\\r\\n LGAudioConvolver.prototype.onRemove = function() {\\r\\n if (this._dropped_url) {\\r\\n URL.revokeObjectURL(this._dropped_url);\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioConvolver.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"impulse_src\\\") {\\r\\n this.loadImpulse(value);\\r\\n } else if (name == \\\"normalize\\\") {\\r\\n this.audionode.normalize = value;\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioConvolver.prototype.onDropFile = function(file) {\\r\\n if (this._dropped_url) {\\r\\n URL.revokeObjectURL(this._dropped_url);\\r\\n }\\r\\n this._dropped_url = URL.createObjectURL(file);\\r\\n this.properties.impulse_src = this._dropped_url;\\r\\n this.loadImpulse(this._dropped_url);\\r\\n };\\r\\n\\r\\n LGAudioConvolver.prototype.loadImpulse = function(url) {\\r\\n var that = this;\\r\\n\\r\\n //kill previous load\\r\\n if (this._request) {\\r\\n this._request.abort();\\r\\n this._request = null;\\r\\n }\\r\\n\\r\\n this._impulse_buffer = null;\\r\\n this._loading_impulse = false;\\r\\n\\r\\n if (!url) {\\r\\n return;\\r\\n }\\r\\n\\r\\n //load new sample\\r\\n this._request = LGAudio.loadSound(url, inner);\\r\\n this._loading_impulse = true;\\r\\n\\r\\n // Decode asynchronously\\r\\n function inner(buffer) {\\r\\n that._impulse_buffer = buffer;\\r\\n that.audionode.buffer = buffer;\\r\\n console.log(\\\"Impulse signal set\\\");\\r\\n that._loading_impulse = false;\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioConvolver.title = \\\"Convolver\\\";\\r\\n LGAudioConvolver.desc = \\\"Convolves the signal (used for reverb)\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/convolver\\\", LGAudioConvolver);\\r\\n\\r\\n function LGAudioDynamicsCompressor() {\\r\\n //default\\r\\n this.properties = {\\r\\n threshold: -50,\\r\\n knee: 40,\\r\\n ratio: 12,\\r\\n reduction: -20,\\r\\n attack: 0,\\r\\n release: 0.25\\r\\n };\\r\\n\\r\\n this.audionode = LGAudio.getAudioContext().createDynamicsCompressor();\\r\\n this.addInput(\\\"in\\\", \\\"audio\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n }\\r\\n\\r\\n LGAudio.createAudioNodeWrapper(LGAudioDynamicsCompressor);\\r\\n\\r\\n LGAudioDynamicsCompressor.prototype.onExecute = function() {\\r\\n if (!this.inputs || !this.inputs.length) {\\r\\n return;\\r\\n }\\r\\n for (var i = 1; i < this.inputs.length; ++i) {\\r\\n var input = this.inputs[i];\\r\\n if (input.link == null) {\\r\\n continue;\\r\\n }\\r\\n var v = this.getInputData(i);\\r\\n if (v !== undefined) {\\r\\n this.audionode[input.name].value = v;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioDynamicsCompressor.prototype.onGetInputs = function() {\\r\\n return [\\r\\n [\\\"threshold\\\", \\\"number\\\"],\\r\\n [\\\"knee\\\", \\\"number\\\"],\\r\\n [\\\"ratio\\\", \\\"number\\\"],\\r\\n [\\\"reduction\\\", \\\"number\\\"],\\r\\n [\\\"attack\\\", \\\"number\\\"],\\r\\n [\\\"release\\\", \\\"number\\\"]\\r\\n ];\\r\\n };\\r\\n\\r\\n LGAudioDynamicsCompressor.title = \\\"DynamicsCompressor\\\";\\r\\n LGAudioDynamicsCompressor.desc = \\\"Dynamics Compressor\\\";\\r\\n LiteGraph.registerNodeType(\\r\\n \\\"audio/dynamicsCompressor\\\",\\r\\n LGAudioDynamicsCompressor\\r\\n );\\r\\n\\r\\n function LGAudioWaveShaper() {\\r\\n //default\\r\\n this.properties = {};\\r\\n\\r\\n this.audionode = LGAudio.getAudioContext().createWaveShaper();\\r\\n this.addInput(\\\"in\\\", \\\"audio\\\");\\r\\n this.addInput(\\\"shape\\\", \\\"waveshape\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n }\\r\\n\\r\\n LGAudioWaveShaper.prototype.onExecute = function() {\\r\\n if (!this.inputs || !this.inputs.length) {\\r\\n return;\\r\\n }\\r\\n var v = this.getInputData(1);\\r\\n if (v === undefined) {\\r\\n return;\\r\\n }\\r\\n this.audionode.curve = v;\\r\\n };\\r\\n\\r\\n LGAudioWaveShaper.prototype.setWaveShape = function(shape) {\\r\\n this.audionode.curve = shape;\\r\\n };\\r\\n\\r\\n LGAudio.createAudioNodeWrapper(LGAudioWaveShaper);\\r\\n\\r\\n /* disabled till I dont find a way to do a wave shape\\r\\nLGAudioWaveShaper.title = \\\"WaveShaper\\\";\\r\\nLGAudioWaveShaper.desc = \\\"Distortion using wave shape\\\";\\r\\nLiteGraph.registerNodeType(\\\"audio/waveShaper\\\", LGAudioWaveShaper);\\r\\n*/\\r\\n\\r\\n function LGAudioMixer() {\\r\\n //default\\r\\n this.properties = {\\r\\n gain1: 0.5,\\r\\n gain2: 0.5\\r\\n };\\r\\n\\r\\n this.audionode = LGAudio.getAudioContext().createGain();\\r\\n\\r\\n this.audionode1 = LGAudio.getAudioContext().createGain();\\r\\n this.audionode1.gain.value = this.properties.gain1;\\r\\n this.audionode2 = LGAudio.getAudioContext().createGain();\\r\\n this.audionode2.gain.value = this.properties.gain2;\\r\\n\\r\\n this.audionode1.connect(this.audionode);\\r\\n this.audionode2.connect(this.audionode);\\r\\n\\r\\n this.addInput(\\\"in1\\\", \\\"audio\\\");\\r\\n this.addInput(\\\"in1 gain\\\", \\\"number\\\");\\r\\n this.addInput(\\\"in2\\\", \\\"audio\\\");\\r\\n this.addInput(\\\"in2 gain\\\", \\\"number\\\");\\r\\n\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n }\\r\\n\\r\\n LGAudioMixer.prototype.getAudioNodeInInputSlot = function(slot) {\\r\\n if (slot == 0) {\\r\\n return this.audionode1;\\r\\n } else if (slot == 2) {\\r\\n return this.audionode2;\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioMixer.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"gain1\\\") {\\r\\n this.audionode1.gain.value = value;\\r\\n } else if (name == \\\"gain2\\\") {\\r\\n this.audionode2.gain.value = value;\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioMixer.prototype.onExecute = function() {\\r\\n if (!this.inputs || !this.inputs.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n for (var i = 1; i < this.inputs.length; ++i) {\\r\\n var input = this.inputs[i];\\r\\n\\r\\n if (input.link == null || input.type == \\\"audio\\\") {\\r\\n continue;\\r\\n }\\r\\n\\r\\n var v = this.getInputData(i);\\r\\n if (v === undefined) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n if (i == 1) {\\r\\n this.audionode1.gain.value = v;\\r\\n } else if (i == 3) {\\r\\n this.audionode2.gain.value = v;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudio.createAudioNodeWrapper(LGAudioMixer);\\r\\n\\r\\n LGAudioMixer.title = \\\"Mixer\\\";\\r\\n LGAudioMixer.desc = \\\"Audio mixer\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/mixer\\\", LGAudioMixer);\\r\\n\\r\\n function LGAudioADSR() {\\r\\n //default\\r\\n this.properties = {\\r\\n A: 0.1,\\r\\n D: 0.1,\\r\\n S: 0.1,\\r\\n R: 0.1\\r\\n };\\r\\n\\r\\n this.audionode = LGAudio.getAudioContext().createGain();\\r\\n this.audionode.gain.value = 0;\\r\\n this.addInput(\\\"in\\\", \\\"audio\\\");\\r\\n this.addInput(\\\"gate\\\", \\\"bool\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n this.gate = false;\\r\\n }\\r\\n\\r\\n LGAudioADSR.prototype.onExecute = function() {\\r\\n var audioContext = LGAudio.getAudioContext();\\r\\n var now = audioContext.currentTime;\\r\\n var node = this.audionode;\\r\\n var gain = node.gain;\\r\\n var current_gate = this.getInputData(1);\\r\\n\\r\\n var A = this.getInputOrProperty(\\\"A\\\");\\r\\n var D = this.getInputOrProperty(\\\"D\\\");\\r\\n var S = this.getInputOrProperty(\\\"S\\\");\\r\\n var R = this.getInputOrProperty(\\\"R\\\");\\r\\n\\r\\n if (!this.gate && current_gate) {\\r\\n gain.cancelScheduledValues(0);\\r\\n gain.setValueAtTime(0, now);\\r\\n gain.linearRampToValueAtTime(1, now + A);\\r\\n gain.linearRampToValueAtTime(S, now + A + D);\\r\\n } else if (this.gate && !current_gate) {\\r\\n gain.cancelScheduledValues(0);\\r\\n gain.setValueAtTime(gain.value, now);\\r\\n gain.linearRampToValueAtTime(0, now + R);\\r\\n }\\r\\n\\r\\n this.gate = current_gate;\\r\\n };\\r\\n\\r\\n LGAudioADSR.prototype.onGetInputs = function() {\\r\\n return [\\r\\n [\\\"A\\\", \\\"number\\\"],\\r\\n [\\\"D\\\", \\\"number\\\"],\\r\\n [\\\"S\\\", \\\"number\\\"],\\r\\n [\\\"R\\\", \\\"number\\\"]\\r\\n ];\\r\\n };\\r\\n\\r\\n LGAudio.createAudioNodeWrapper(LGAudioADSR);\\r\\n\\r\\n LGAudioADSR.title = \\\"ADSR\\\";\\r\\n LGAudioADSR.desc = \\\"Audio envelope\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/adsr\\\", LGAudioADSR);\\r\\n\\r\\n function LGAudioDelay() {\\r\\n //default\\r\\n this.properties = {\\r\\n delayTime: 0.5\\r\\n };\\r\\n\\r\\n this.audionode = LGAudio.getAudioContext().createDelay(10);\\r\\n this.audionode.delayTime.value = this.properties.delayTime;\\r\\n this.addInput(\\\"in\\\", \\\"audio\\\");\\r\\n this.addInput(\\\"time\\\", \\\"number\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n }\\r\\n\\r\\n LGAudio.createAudioNodeWrapper(LGAudioDelay);\\r\\n\\r\\n LGAudioDelay.prototype.onExecute = function() {\\r\\n var v = this.getInputData(1);\\r\\n if (v !== undefined) {\\r\\n this.audionode.delayTime.value = v;\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioDelay.title = \\\"Delay\\\";\\r\\n LGAudioDelay.desc = \\\"Audio delay\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/delay\\\", LGAudioDelay);\\r\\n\\r\\n function LGAudioBiquadFilter() {\\r\\n //default\\r\\n this.properties = {\\r\\n frequency: 350,\\r\\n detune: 0,\\r\\n Q: 1\\r\\n };\\r\\n this.addProperty(\\\"type\\\", \\\"lowpass\\\", \\\"enum\\\", {\\r\\n values: [\\r\\n \\\"lowpass\\\",\\r\\n \\\"highpass\\\",\\r\\n \\\"bandpass\\\",\\r\\n \\\"lowshelf\\\",\\r\\n \\\"highshelf\\\",\\r\\n \\\"peaking\\\",\\r\\n \\\"notch\\\",\\r\\n \\\"allpass\\\"\\r\\n ]\\r\\n });\\r\\n\\r\\n //create node\\r\\n this.audionode = LGAudio.getAudioContext().createBiquadFilter();\\r\\n\\r\\n //slots\\r\\n this.addInput(\\\"in\\\", \\\"audio\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n }\\r\\n\\r\\n LGAudioBiquadFilter.prototype.onExecute = function() {\\r\\n if (!this.inputs || !this.inputs.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n for (var i = 1; i < this.inputs.length; ++i) {\\r\\n var input = this.inputs[i];\\r\\n if (input.link == null) {\\r\\n continue;\\r\\n }\\r\\n var v = this.getInputData(i);\\r\\n if (v !== undefined) {\\r\\n this.audionode[input.name].value = v;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioBiquadFilter.prototype.onGetInputs = function() {\\r\\n return [[\\\"frequency\\\", \\\"number\\\"], [\\\"detune\\\", \\\"number\\\"], [\\\"Q\\\", \\\"number\\\"]];\\r\\n };\\r\\n\\r\\n LGAudio.createAudioNodeWrapper(LGAudioBiquadFilter);\\r\\n\\r\\n LGAudioBiquadFilter.title = \\\"BiquadFilter\\\";\\r\\n LGAudioBiquadFilter.desc = \\\"Audio filter\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/biquadfilter\\\", LGAudioBiquadFilter);\\r\\n\\r\\n function LGAudioOscillatorNode() {\\r\\n //default\\r\\n this.properties = {\\r\\n frequency: 440,\\r\\n detune: 0,\\r\\n type: \\\"sine\\\"\\r\\n };\\r\\n this.addProperty(\\\"type\\\", \\\"sine\\\", \\\"enum\\\", {\\r\\n values: [\\\"sine\\\", \\\"square\\\", \\\"sawtooth\\\", \\\"triangle\\\", \\\"custom\\\"]\\r\\n });\\r\\n\\r\\n //create node\\r\\n this.audionode = LGAudio.getAudioContext().createOscillator();\\r\\n\\r\\n //slots\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n }\\r\\n\\r\\n LGAudioOscillatorNode.prototype.onStart = function() {\\r\\n if (!this.audionode.started) {\\r\\n this.audionode.started = true;\\r\\n try {\\r\\n this.audionode.start();\\r\\n } catch (err) {}\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioOscillatorNode.prototype.onStop = function() {\\r\\n if (this.audionode.started) {\\r\\n this.audionode.started = false;\\r\\n this.audionode.stop();\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioOscillatorNode.prototype.onPause = function() {\\r\\n this.onStop();\\r\\n };\\r\\n\\r\\n LGAudioOscillatorNode.prototype.onUnpause = function() {\\r\\n this.onStart();\\r\\n };\\r\\n\\r\\n LGAudioOscillatorNode.prototype.onExecute = function() {\\r\\n if (!this.inputs || !this.inputs.length) {\\r\\n return;\\r\\n }\\r\\n\\r\\n for (var i = 0; i < this.inputs.length; ++i) {\\r\\n var input = this.inputs[i];\\r\\n if (input.link == null) {\\r\\n continue;\\r\\n }\\r\\n var v = this.getInputData(i);\\r\\n if (v !== undefined) {\\r\\n this.audionode[input.name].value = v;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioOscillatorNode.prototype.onGetInputs = function() {\\r\\n return [\\r\\n [\\\"frequency\\\", \\\"number\\\"],\\r\\n [\\\"detune\\\", \\\"number\\\"],\\r\\n [\\\"type\\\", \\\"string\\\"]\\r\\n ];\\r\\n };\\r\\n\\r\\n LGAudio.createAudioNodeWrapper(LGAudioOscillatorNode);\\r\\n\\r\\n LGAudioOscillatorNode.title = \\\"Oscillator\\\";\\r\\n LGAudioOscillatorNode.desc = \\\"Oscillator\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/oscillator\\\", LGAudioOscillatorNode);\\r\\n\\r\\n //*****************************************************\\r\\n\\r\\n //EXTRA\\r\\n\\r\\n function LGAudioVisualization() {\\r\\n this.properties = {\\r\\n continuous: true,\\r\\n mark: -1\\r\\n };\\r\\n\\r\\n this.addInput(\\\"data\\\", \\\"array\\\");\\r\\n this.addInput(\\\"mark\\\", \\\"number\\\");\\r\\n this.size = [300, 200];\\r\\n this._last_buffer = null;\\r\\n }\\r\\n\\r\\n LGAudioVisualization.prototype.onExecute = function() {\\r\\n this._last_buffer = this.getInputData(0);\\r\\n var v = this.getInputData(1);\\r\\n if (v !== undefined) {\\r\\n this.properties.mark = v;\\r\\n }\\r\\n this.setDirtyCanvas(true, false);\\r\\n };\\r\\n\\r\\n LGAudioVisualization.prototype.onDrawForeground = function(ctx) {\\r\\n if (!this._last_buffer) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var buffer = this._last_buffer;\\r\\n\\r\\n //delta represents how many samples we advance per pixel\\r\\n var delta = buffer.length / this.size[0];\\r\\n var h = this.size[1];\\r\\n\\r\\n ctx.fillStyle = \\\"black\\\";\\r\\n ctx.fillRect(0, 0, this.size[0], this.size[1]);\\r\\n ctx.strokeStyle = \\\"white\\\";\\r\\n ctx.beginPath();\\r\\n var x = 0;\\r\\n\\r\\n if (this.properties.continuous) {\\r\\n ctx.moveTo(x, h);\\r\\n for (var i = 0; i < buffer.length; i += delta) {\\r\\n ctx.lineTo(x, h - (buffer[i | 0] / 255) * h);\\r\\n x++;\\r\\n }\\r\\n } else {\\r\\n for (var i = 0; i < buffer.length; i += delta) {\\r\\n ctx.moveTo(x + 0.5, h);\\r\\n ctx.lineTo(x + 0.5, h - (buffer[i | 0] / 255) * h);\\r\\n x++;\\r\\n }\\r\\n }\\r\\n ctx.stroke();\\r\\n\\r\\n if (this.properties.mark >= 0) {\\r\\n var samplerate = LGAudio.getAudioContext().sampleRate;\\r\\n var binfreq = samplerate / buffer.length;\\r\\n var x = (2 * (this.properties.mark / binfreq)) / delta;\\r\\n if (x >= this.size[0]) {\\r\\n x = this.size[0] - 1;\\r\\n }\\r\\n ctx.strokeStyle = \\\"red\\\";\\r\\n ctx.beginPath();\\r\\n ctx.moveTo(x, h);\\r\\n ctx.lineTo(x, 0);\\r\\n ctx.stroke();\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioVisualization.title = \\\"Visualization\\\";\\r\\n LGAudioVisualization.desc = \\\"Audio Visualization\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/visualization\\\", LGAudioVisualization);\\r\\n\\r\\n function LGAudioBandSignal() {\\r\\n //default\\r\\n this.properties = {\\r\\n band: 440,\\r\\n amplitude: 1\\r\\n };\\r\\n\\r\\n this.addInput(\\\"freqs\\\", \\\"array\\\");\\r\\n this.addOutput(\\\"signal\\\", \\\"number\\\");\\r\\n }\\r\\n\\r\\n LGAudioBandSignal.prototype.onExecute = function() {\\r\\n this._freqs = this.getInputData(0);\\r\\n if (!this._freqs) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var band = this.properties.band;\\r\\n var v = this.getInputData(1);\\r\\n if (v !== undefined) {\\r\\n band = v;\\r\\n }\\r\\n\\r\\n var samplerate = LGAudio.getAudioContext().sampleRate;\\r\\n var binfreq = samplerate / this._freqs.length;\\r\\n var index = 2 * (band / binfreq);\\r\\n var v = 0;\\r\\n if (index < 0) {\\r\\n v = this._freqs[0];\\r\\n }\\r\\n if (index >= this._freqs.length) {\\r\\n v = this._freqs[this._freqs.length - 1];\\r\\n } else {\\r\\n var pos = index | 0;\\r\\n var v0 = this._freqs[pos];\\r\\n var v1 = this._freqs[pos + 1];\\r\\n var f = index - pos;\\r\\n v = v0 * (1 - f) + v1 * f;\\r\\n }\\r\\n\\r\\n this.setOutputData(0, (v / 255) * this.properties.amplitude);\\r\\n };\\r\\n\\r\\n LGAudioBandSignal.prototype.onGetInputs = function() {\\r\\n return [[\\\"band\\\", \\\"number\\\"]];\\r\\n };\\r\\n\\r\\n LGAudioBandSignal.title = \\\"Signal\\\";\\r\\n LGAudioBandSignal.desc = \\\"extract the signal of some frequency\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/signal\\\", LGAudioBandSignal);\\r\\n\\r\\n function LGAudioScript() {\\r\\n if (!LGAudioScript.default_code) {\\r\\n var code = LGAudioScript.default_function.toString();\\r\\n var index = code.indexOf(\\\"{\\\") + 1;\\r\\n var index2 = code.lastIndexOf(\\\"}\\\");\\r\\n LGAudioScript.default_code = code.substr(index, index2 - index);\\r\\n }\\r\\n\\r\\n //default\\r\\n this.properties = {\\r\\n code: LGAudioScript.default_code\\r\\n };\\r\\n\\r\\n //create node\\r\\n var ctx = LGAudio.getAudioContext();\\r\\n if (ctx.createScriptProcessor) {\\r\\n this.audionode = ctx.createScriptProcessor(4096, 1, 1);\\r\\n }\\r\\n //buffer size, input channels, output channels\\r\\n else {\\r\\n console.warn(\\\"ScriptProcessorNode deprecated\\\");\\r\\n this.audionode = ctx.createGain(); //bypass audio\\r\\n }\\r\\n\\r\\n this.processCode();\\r\\n if (!LGAudioScript._bypass_function) {\\r\\n LGAudioScript._bypass_function = this.audionode.onaudioprocess;\\r\\n }\\r\\n\\r\\n //slots\\r\\n this.addInput(\\\"in\\\", \\\"audio\\\");\\r\\n this.addOutput(\\\"out\\\", \\\"audio\\\");\\r\\n }\\r\\n\\r\\n LGAudioScript.prototype.onAdded = function(graph) {\\r\\n if (graph.status == LGraph.STATUS_RUNNING) {\\r\\n this.audionode.onaudioprocess = this._callback;\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioScript[\\\"@code\\\"] = { widget: \\\"code\\\" };\\r\\n\\r\\n LGAudioScript.prototype.onStart = function() {\\r\\n this.audionode.onaudioprocess = this._callback;\\r\\n };\\r\\n\\r\\n LGAudioScript.prototype.onStop = function() {\\r\\n this.audionode.onaudioprocess = LGAudioScript._bypass_function;\\r\\n };\\r\\n\\r\\n LGAudioScript.prototype.onPause = function() {\\r\\n this.audionode.onaudioprocess = LGAudioScript._bypass_function;\\r\\n };\\r\\n\\r\\n LGAudioScript.prototype.onUnpause = function() {\\r\\n this.audionode.onaudioprocess = this._callback;\\r\\n };\\r\\n\\r\\n LGAudioScript.prototype.onExecute = function() {\\r\\n //nothing! because we need an onExecute to receive onStart... fix that\\r\\n };\\r\\n\\r\\n LGAudioScript.prototype.onRemoved = function() {\\r\\n this.audionode.onaudioprocess = LGAudioScript._bypass_function;\\r\\n };\\r\\n\\r\\n LGAudioScript.prototype.processCode = function() {\\r\\n try {\\r\\n var func = new Function(\\\"properties\\\", this.properties.code);\\r\\n this._script = new func(this.properties);\\r\\n this._old_code = this.properties.code;\\r\\n this._callback = this._script.onaudioprocess;\\r\\n } catch (err) {\\r\\n console.error(\\\"Error in onaudioprocess code\\\", err);\\r\\n this._callback = LGAudioScript._bypass_function;\\r\\n this.audionode.onaudioprocess = this._callback;\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioScript.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"code\\\") {\\r\\n this.properties.code = value;\\r\\n this.processCode();\\r\\n if (this.graph && this.graph.status == LGraph.STATUS_RUNNING) {\\r\\n this.audionode.onaudioprocess = this._callback;\\r\\n }\\r\\n }\\r\\n };\\r\\n\\r\\n LGAudioScript.default_function = function() {\\r\\n this.onaudioprocess = function(audioProcessingEvent) {\\r\\n // The input buffer is the song we loaded earlier\\r\\n var inputBuffer = audioProcessingEvent.inputBuffer;\\r\\n\\r\\n // The output buffer contains the samples that will be modified and played\\r\\n var outputBuffer = audioProcessingEvent.outputBuffer;\\r\\n\\r\\n // Loop through the output channels (in this case there is only one)\\r\\n for (\\r\\n var channel = 0;\\r\\n channel < outputBuffer.numberOfChannels;\\r\\n channel++\\r\\n ) {\\r\\n var inputData = inputBuffer.getChannelData(channel);\\r\\n var outputData = outputBuffer.getChannelData(channel);\\r\\n\\r\\n // Loop through the 4096 samples\\r\\n for (var sample = 0; sample < inputBuffer.length; sample++) {\\r\\n // make output equal to the same as the input\\r\\n outputData[sample] = inputData[sample];\\r\\n }\\r\\n }\\r\\n };\\r\\n };\\r\\n\\r\\n LGAudio.createAudioNodeWrapper(LGAudioScript);\\r\\n\\r\\n LGAudioScript.title = \\\"Script\\\";\\r\\n LGAudioScript.desc = \\\"apply script to signal\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/script\\\", LGAudioScript);\\r\\n\\r\\n function LGAudioDestination() {\\r\\n this.audionode = LGAudio.getAudioContext().destination;\\r\\n this.addInput(\\\"in\\\", \\\"audio\\\");\\r\\n }\\r\\n\\r\\n LGAudioDestination.title = \\\"Destination\\\";\\r\\n LGAudioDestination.desc = \\\"Audio output\\\";\\r\\n LiteGraph.registerNodeType(\\\"audio/destination\\\", LGAudioDestination);\\r\\n})(this);\\r\\n\\r\\n//event related nodes\\r\\n(function(global) {\\r\\n var LiteGraph = global.LiteGraph;\\r\\n\\r\\n function LGWebSocket() {\\r\\n this.size = [60, 20];\\r\\n this.addInput(\\\"send\\\", LiteGraph.ACTION);\\r\\n this.addOutput(\\\"received\\\", LiteGraph.EVENT);\\r\\n this.addInput(\\\"in\\\", 0);\\r\\n this.addOutput(\\\"out\\\", 0);\\r\\n this.properties = {\\r\\n url: \\\"\\\",\\r\\n room: \\\"lgraph\\\", //allows to filter messages,\\r\\n only_send_changes: true\\r\\n };\\r\\n this._ws = null;\\r\\n this._last_sent_data = [];\\r\\n this._last_received_data = [];\\r\\n }\\r\\n\\r\\n LGWebSocket.title = \\\"WebSocket\\\";\\r\\n LGWebSocket.desc = \\\"Send data through a websocket\\\";\\r\\n\\r\\n LGWebSocket.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"url\\\") {\\r\\n this.connectSocket();\\r\\n }\\r\\n };\\r\\n\\r\\n LGWebSocket.prototype.onExecute = function() {\\r\\n if (!this._ws && this.properties.url) {\\r\\n this.connectSocket();\\r\\n }\\r\\n\\r\\n if (!this._ws || this._ws.readyState != WebSocket.OPEN) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var room = this.properties.room;\\r\\n var only_changes = this.properties.only_send_changes;\\r\\n\\r\\n for (var i = 1; i < this.inputs.length; ++i) {\\r\\n var data = this.getInputData(i);\\r\\n if (data == null) {\\r\\n continue;\\r\\n }\\r\\n var json;\\r\\n try {\\r\\n json = JSON.stringify({\\r\\n type: 0,\\r\\n room: room,\\r\\n channel: i,\\r\\n data: data\\r\\n });\\r\\n } catch (err) {\\r\\n continue;\\r\\n }\\r\\n if (only_changes && this._last_sent_data[i] == json) {\\r\\n continue;\\r\\n }\\r\\n\\r\\n this._last_sent_data[i] = json;\\r\\n this._ws.send(json);\\r\\n }\\r\\n\\r\\n for (var i = 1; i < this.outputs.length; ++i) {\\r\\n this.setOutputData(i, this._last_received_data[i]);\\r\\n }\\r\\n\\r\\n if (this.boxcolor == \\\"#AFA\\\") {\\r\\n this.boxcolor = \\\"#6C6\\\";\\r\\n }\\r\\n };\\r\\n\\r\\n LGWebSocket.prototype.connectSocket = function() {\\r\\n var that = this;\\r\\n var url = this.properties.url;\\r\\n if (url.substr(0, 2) != \\\"ws\\\") {\\r\\n url = \\\"ws://\\\" + url;\\r\\n }\\r\\n this._ws = new WebSocket(url);\\r\\n this._ws.onopen = function() {\\r\\n console.log(\\\"ready\\\");\\r\\n that.boxcolor = \\\"#6C6\\\";\\r\\n };\\r\\n this._ws.onmessage = function(e) {\\r\\n that.boxcolor = \\\"#AFA\\\";\\r\\n var data = JSON.parse(e.data);\\r\\n if (data.room && data.room != this.properties.room) {\\r\\n return;\\r\\n }\\r\\n if (e.data.type == 1) {\\r\\n if (\\r\\n data.data.object_class &&\\r\\n LiteGraph[data.data.object_class]\\r\\n ) {\\r\\n var obj = null;\\r\\n try {\\r\\n obj = new LiteGraph[data.data.object_class](data.data);\\r\\n that.triggerSlot(0, obj);\\r\\n } catch (err) {\\r\\n return;\\r\\n }\\r\\n } else {\\r\\n that.triggerSlot(0, data.data);\\r\\n }\\r\\n } else {\\r\\n that._last_received_data[e.data.channel || 0] = data.data;\\r\\n }\\r\\n };\\r\\n this._ws.onerror = function(e) {\\r\\n console.log(\\\"couldnt connect to websocket\\\");\\r\\n that.boxcolor = \\\"#E88\\\";\\r\\n };\\r\\n this._ws.onclose = function(e) {\\r\\n console.log(\\\"connection closed\\\");\\r\\n that.boxcolor = \\\"#000\\\";\\r\\n };\\r\\n };\\r\\n\\r\\n LGWebSocket.prototype.send = function(data) {\\r\\n if (!this._ws || this._ws.readyState != WebSocket.OPEN) {\\r\\n return;\\r\\n }\\r\\n this._ws.send(JSON.stringify({ type: 1, msg: data }));\\r\\n };\\r\\n\\r\\n LGWebSocket.prototype.onAction = function(action, param) {\\r\\n if (!this._ws || this._ws.readyState != WebSocket.OPEN) {\\r\\n return;\\r\\n }\\r\\n this._ws.send({\\r\\n type: 1,\\r\\n room: this.properties.room,\\r\\n action: action,\\r\\n data: param\\r\\n });\\r\\n };\\r\\n\\r\\n LGWebSocket.prototype.onGetInputs = function() {\\r\\n return [[\\\"in\\\", 0]];\\r\\n };\\r\\n\\r\\n LGWebSocket.prototype.onGetOutputs = function() {\\r\\n return [[\\\"out\\\", 0]];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"network/websocket\\\", LGWebSocket);\\r\\n\\r\\n //It is like a websocket but using the SillyServer.js server that bounces packets back to all clients connected:\\r\\n //For more information: https://github.com/jagenjo/SillyServer.js\\r\\n\\r\\n function LGSillyClient() {\\r\\n //this.size = [60,20];\\r\\n this.room_widget = this.addWidget(\\r\\n \\\"text\\\",\\r\\n \\\"Room\\\",\\r\\n \\\"lgraph\\\",\\r\\n this.setRoom.bind(this)\\r\\n );\\r\\n this.addWidget(\\r\\n \\\"button\\\",\\r\\n \\\"Reconnect\\\",\\r\\n null,\\r\\n this.connectSocket.bind(this)\\r\\n );\\r\\n\\r\\n this.addInput(\\\"send\\\", LiteGraph.ACTION);\\r\\n this.addOutput(\\\"received\\\", LiteGraph.EVENT);\\r\\n this.addInput(\\\"in\\\", 0);\\r\\n this.addOutput(\\\"out\\\", 0);\\r\\n this.properties = {\\r\\n url: \\\"tamats.com:55000\\\",\\r\\n room: \\\"lgraph\\\",\\r\\n only_send_changes: true\\r\\n };\\r\\n\\r\\n this._server = null;\\r\\n this.connectSocket();\\r\\n this._last_sent_data = [];\\r\\n this._last_received_data = [];\\r\\n }\\r\\n\\r\\n LGSillyClient.title = \\\"SillyClient\\\";\\r\\n LGSillyClient.desc = \\\"Connects to SillyServer to broadcast messages\\\";\\r\\n\\r\\n LGSillyClient.prototype.onPropertyChanged = function(name, value) {\\r\\n if (name == \\\"room\\\") {\\r\\n this.room_widget.value = value;\\r\\n }\\r\\n this.connectSocket();\\r\\n };\\r\\n\\r\\n LGSillyClient.prototype.setRoom = function(room_name) {\\r\\n this.properties.room = room_name;\\r\\n this.room_widget.value = room_name;\\r\\n this.connectSocket();\\r\\n };\\r\\n\\r\\n //force label names\\r\\n LGSillyClient.prototype.onDrawForeground = function() {\\r\\n for (var i = 1; i < this.inputs.length; ++i) {\\r\\n var slot = this.inputs[i];\\r\\n slot.label = \\\"in_\\\" + i;\\r\\n }\\r\\n for (var i = 1; i < this.outputs.length; ++i) {\\r\\n var slot = this.outputs[i];\\r\\n slot.label = \\\"out_\\\" + i;\\r\\n }\\r\\n };\\r\\n\\r\\n LGSillyClient.prototype.onExecute = function() {\\r\\n if (!this._server || !this._server.is_connected) {\\r\\n return;\\r\\n }\\r\\n\\r\\n var only_send_changes = this.properties.only_send_changes;\\r\\n\\r\\n for (var i = 1; i < this.inputs.length; ++i) {\\r\\n var data = this.getInputData(i);\\r\\n if (data != null) {\\r\\n if (only_send_changes && this._last_sent_data[i] == data) {\\r\\n continue;\\r\\n }\\r\\n this._server.sendMessage({ type: 0, channel: i, data: data });\\r\\n this._last_sent_data[i] = data;\\r\\n }\\r\\n }\\r\\n\\r\\n for (var i = 1; i < this.outputs.length; ++i) {\\r\\n this.setOutputData(i, this._last_received_data[i]);\\r\\n }\\r\\n\\r\\n if (this.boxcolor == \\\"#AFA\\\") {\\r\\n this.boxcolor = \\\"#6C6\\\";\\r\\n }\\r\\n };\\r\\n\\r\\n LGSillyClient.prototype.connectSocket = function() {\\r\\n var that = this;\\r\\n if (typeof SillyClient == \\\"undefined\\\") {\\r\\n if (!this._error) {\\r\\n console.error(\\r\\n \\\"SillyClient node cannot be used, you must include SillyServer.js\\\"\\r\\n );\\r\\n }\\r\\n this._error = true;\\r\\n return;\\r\\n }\\r\\n\\r\\n this._server = new SillyClient();\\r\\n this._server.on_ready = function() {\\r\\n console.log(\\\"ready\\\");\\r\\n that.boxcolor = \\\"#6C6\\\";\\r\\n };\\r\\n this._server.on_message = function(id, msg) {\\r\\n var data = null;\\r\\n try {\\r\\n data = JSON.parse(msg);\\r\\n } catch (err) {\\r\\n return;\\r\\n }\\r\\n\\r\\n if (data.type == 1) {\\r\\n //EVENT slot\\r\\n if (\\r\\n data.data.object_class &&\\r\\n LiteGraph[data.data.object_class]\\r\\n ) {\\r\\n var obj = null;\\r\\n try {\\r\\n obj = new LiteGraph[data.data.object_class](data.data);\\r\\n that.triggerSlot(0, obj);\\r\\n } catch (err) {\\r\\n return;\\r\\n }\\r\\n } else {\\r\\n that.triggerSlot(0, data.data);\\r\\n }\\r\\n } //for FLOW slots\\r\\n else {\\r\\n that._last_received_data[data.channel || 0] = data.data;\\r\\n }\\r\\n that.boxcolor = \\\"#AFA\\\";\\r\\n };\\r\\n this._server.on_error = function(e) {\\r\\n console.log(\\\"couldnt connect to websocket\\\");\\r\\n that.boxcolor = \\\"#E88\\\";\\r\\n };\\r\\n this._server.on_close = function(e) {\\r\\n console.log(\\\"connection closed\\\");\\r\\n that.boxcolor = \\\"#000\\\";\\r\\n };\\r\\n\\r\\n if (this.properties.url && this.properties.room) {\\r\\n try {\\r\\n this._server.connect(this.properties.url, this.properties.room);\\r\\n } catch (err) {\\r\\n console.error(\\\"SillyServer error: \\\" + err);\\r\\n this._server = null;\\r\\n return;\\r\\n }\\r\\n this._final_url = this.properties.url + \\\"/\\\" + this.properties.room;\\r\\n }\\r\\n };\\r\\n\\r\\n LGSillyClient.prototype.send = function(data) {\\r\\n if (!this._server || !this._server.is_connected) {\\r\\n return;\\r\\n }\\r\\n this._server.sendMessage({ type: 1, data: data });\\r\\n };\\r\\n\\r\\n LGSillyClient.prototype.onAction = function(action, param) {\\r\\n if (!this._server || !this._server.is_connected) {\\r\\n return;\\r\\n }\\r\\n this._server.sendMessage({ type: 1, action: action, data: param });\\r\\n };\\r\\n\\r\\n LGSillyClient.prototype.onGetInputs = function() {\\r\\n return [[\\\"in\\\", 0]];\\r\\n };\\r\\n\\r\\n LGSillyClient.prototype.onGetOutputs = function() {\\r\\n return [[\\\"out\\\", 0]];\\r\\n };\\r\\n\\r\\n LiteGraph.registerNodeType(\\\"network/sillyclient\\\", LGSillyClient);\\r\\n})(this);\\r\\n\\r\\n\"","module.exports = \"//packer version\\r\\n///@FILE:../src/intro.js\\r\\n///@INFO: BASE\\r\\n//LiteScene by javi.agenjo@gmail.com 2013 @tamats\\r\\n// github.com/jagenjo/litescene\\r\\n// dependencies: litegl.js glMatrix.js (and litegraph.js)\\r\\n//Here goes the licence and some info\\r\\n//************************************************\\r\\n//and the commonJS header...\\r\\n'use strict';\\r\\n\\r\\n(function(global){\\r\\n\\r\\n///@FILE:../src/utils/wbin.js\\r\\n/* WBin: Javi Agenjo javi.agenjo@gmail.com Febrary 2014\\r\\n\\r\\nWBin allows to pack binary information easily\\r\\nWorks similar to WAD file format from ID Software. You have binary lumps with a given name (and a special type code).\\r\\nFirst we store a file header, then info about every lump, then a big binary chunk where all the lumps data is located.\\r\\nThe lump headers contain info to the position of the data in the lump binary chunk (positions are relative to the binary chung starting position)\\r\\n\\r\\nHeader: (64 bytes total)\\r\\n\\t* FOURCC: 4 bytes with \\\"WBIN\\\"\\r\\n\\t* Version: 4 bytes for Float32, represents WBin version used to store\\r\\n\\t* Flags: 2 bytes to store flags (first byte reserved, second is free to use)\\r\\n\\t* Num. lumps: 2 bytes number with the total amount of lumps in this wbin\\r\\n\\t* ClassName: 32 bytes to store a classname, used to know info about the object stored\\r\\n\\t* extra space for future improvements\\r\\n\\r\\nLump header: (64 bytes total)\\r\\n\\t* start: 4 bytes (Uint32), where the lump start in the binary area\\r\\n\\t* length: 4 bytes (Uint32), size of the lump\\r\\n\\t* code: 2 bytes to represent data type using code table (Uint8Array, Float32Array, ...)\\r\\n\\t* name: 54 bytes name for the lump\\r\\n\\r\\nLump binary: all the binary data...\\r\\n\\r\\n*/\\r\\n\\r\\n/**\\r\\n* WBin allows to create binary files easily (similar to WAD format). You can pack lots of resources in one file or extract them.\\r\\n* @class WBin\\r\\n*/\\r\\n\\r\\n(function(global){\\r\\n\\r\\nfunction WBin()\\r\\n{\\r\\n}\\r\\n\\r\\nWBin.classes = {};//if the WBin contains a class it will be seaerch here first (otherwise it will search in the global scope)\\r\\n\\r\\nWBin.HEADER_SIZE = 64; //num bytes per header, some are free to future improvements\\r\\nWBin.FOUR_CC = \\\"WBIN\\\";\\r\\nWBin.VERSION = 0.3; //use numbers, never strings, fixed size in binary\\r\\nWBin.CLASSNAME_SIZE = 32; //32 bytes: stores a type for the object stored inside this binary\\r\\n\\r\\nWBin.LUMPNAME_SIZE = 54; //max size of a lump name, it is big because sometimes some names have urls\\r\\nWBin.LUMPHEADER_SIZE = 4+4+2+WBin.LUMPNAME_SIZE; //32 bytes: 4 start, 4 length, 2 code, 54 name\\r\\n\\r\\nWBin.CODES = {\\r\\n\\t\\\"ArrayBuffer\\\":\\\"AB\\\", \\\"Int8Array\\\":\\\"I1\\\", \\\"Uint8Array\\\":\\\"i1\\\", \\\"Int16Array\\\":\\\"I2\\\", \\\"Uint16Array\\\":\\\"i2\\\", \\\"Int32Array\\\":\\\"I4\\\", \\\"Uint32Array\\\":\\\"i4\\\",\\r\\n\\t\\\"Float32Array\\\":\\\"F4\\\", \\\"Float64Array\\\": \\\"F8\\\", \\\"Object\\\":\\\"OB\\\",\\\"WideObject\\\":\\\"WO\\\",\\\"String\\\":\\\"ST\\\",\\\"WideString\\\":\\\"WS\\\",\\\"Number\\\":\\\"NU\\\", \\\"null\\\":\\\"00\\\"\\r\\n};\\r\\n\\r\\nWBin.REVERSE_CODES = {};\\r\\nfor(var i in WBin.CODES)\\r\\n\\tWBin.REVERSE_CODES[ WBin.CODES[i] ] = i;\\r\\n\\r\\nWBin.FULL_BINARY = 1; //means this binary should be passed as binary, not as object of chunks\\r\\n\\r\\n/**\\r\\n* Allows to check if one Uint8Array contains a WBin file\\r\\n* @method WBin.isWBin\\r\\n* @param {UInt8Array} data\\r\\n* @return {boolean}\\r\\n*/\\r\\nWBin.isWBin = function(data)\\r\\n{\\r\\n\\tvar fourcc = data.subarray(0,4);\\r\\n\\tfor(var i = 0; i < fourcc.length; i++)\\r\\n\\t\\tif(fourcc[i] != 0 && fourcc[i] != WBin.FOUR_CC.charCodeAt(i))\\r\\n\\t\\t\\treturn false;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Builds a WBin data stream from an object (every property of the object will be considered a lump with data)\\r\\n* It supports Numbers, Strings and TypedArrays or ArrayBuffer\\r\\n* @method WBin.create\\r\\n* @param {Object} origin object containing all the lumps, the key will be used as lump name\\r\\n* @param {String} origin_class_name [Optional] allows to add a classname to the WBin, this is used to detect which class to instance when extracting it\\r\\n* @return {Uint8Array} all the bytes\\r\\n*/\\r\\nWBin.create = function( origin, origin_class_name )\\r\\n{\\r\\n\\tif(!origin)\\r\\n\\t\\tthrow(\\\"WBin null origin passed\\\");\\r\\n\\r\\n\\tvar flags = new Uint8Array([0,0]);\\r\\n\\tvar version = new Uint8Array( new Float32Array( [WBin.VERSION] ).buffer );\\r\\n\\torigin_class_name = origin_class_name || \\\"\\\";\\r\\n\\r\\n\\t//use class binary creator\\r\\n\\tif(origin.toBinary)\\r\\n\\t{\\r\\n\\t\\tvar content = origin.toBinary();\\r\\n\\t\\tif(!content)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tif(content.constructor == ArrayBuffer)\\r\\n\\t\\t{\\r\\n\\t\\t\\tflags[0] |= WBin.FULL_BINARY;\\r\\n\\r\\n\\t\\t\\tvar classname = WBin.getObjectClassName( origin );\\r\\n\\t\\t\\t//alloc memory\\r\\n\\t\\t\\tvar data = new Uint8Array(WBin.HEADER_SIZE + content.length);\\r\\n\\t\\t\\t//set fourcc\\r\\n\\t\\t\\tdata.set(WBin.stringToUint8Array( WBin.FOUR_CC ));\\r\\n\\t\\t\\t//set version\\r\\n\\t\\t\\tdata.set(version, 4);\\r\\n\\t\\t\\t//Set flags\\r\\n\\t\\t\\tdata.set(flags, 8);\\r\\n\\t\\t\\t//set classname\\r\\n\\t\\t\\tdata.set(WBin.stringToUint8Array(classname,WBin.CLASSNAME_SIZE), 14);\\r\\n\\t\\t\\t//set data\\r\\n\\t\\t\\tdata.set(content, WBin.HEADER_SIZE);\\r\\n\\t\\t\\treturn data;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\torigin = content;\\r\\n\\t}\\r\\n\\r\\n\\t//create \\r\\n\\tvar total_size = WBin.HEADER_SIZE;\\r\\n\\tvar lumps = [];\\r\\n\\tvar lump_offset = 0;\\r\\n\\r\\n\\t//gather lumps\\r\\n\\tfor(var i in origin)\\r\\n\\t{\\r\\n\\t\\tvar data = origin[i];\\r\\n\\t\\tif(data == null)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif(data.constructor === Blob || data.constructor === File)\\r\\n\\t\\t\\tthrow(\\\"Wbin does not allow Blobs or Files as data to store, conver to ArrayBuffer\\\");\\r\\n\\r\\n\\t\\tvar classname = WBin.getObjectClassName(data);\\r\\n\\r\\n\\t\\tvar code = WBin.CODES[ classname ];\\r\\n\\t\\tif(!code) \\r\\n\\t\\t\\tcode = \\\"OB\\\"; //generic\\r\\n\\r\\n\\t\\t//class specific actions\\r\\n\\t\\tif (code == \\\"NU\\\")\\r\\n\\t\\t\\tdata = new Float64Array([data]); //data.toString(); //numbers are stored as strings\\r\\n\\t\\telse if(code == \\\"OB\\\")\\r\\n\\t\\t\\tdata = JSON.stringify(data); //serialize the data\\r\\n\\r\\n\\t\\tvar data_length = 0;\\r\\n\\r\\n\\t\\t//convert any string related data to typed arrays\\r\\n\\t\\tif( data && data.constructor == String )\\r\\n\\t\\t{\\r\\n\\t\\t\\tdata = WBin.stringToTypedArray( data );\\r\\n\\t\\t\\tif(data.constructor === Uint16Array) //careful when wide characters found (international characters)\\r\\n\\t\\t\\t\\tcode = (code == \\\"OB\\\") ? \\\"WO\\\" : \\\"WS\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//typed array\\r\\n\\t\\tif(data.buffer && data.buffer.constructor == ArrayBuffer)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//clone the data, to avoid problems with shared arrays\\r\\n\\t\\t\\tdata = new Uint8Array( new Uint8Array( data.buffer, data.byteOffset, data.byteLength ) ); \\r\\n\\t\\t\\tdata_length = data.byteLength;\\r\\n\\t\\t}\\r\\n\\t\\telse if(data.constructor == ArrayBuffer) //plain buffer\\r\\n\\t\\t\\tdata_length = data.byteLength;\\r\\n\\t\\telse\\r\\n\\t\\t\\tthrow(\\\"WBin: cannot be anything different to ArrayBuffer\\\");\\r\\n\\r\\n\\t\\tvar lumpname = i.substring(0,WBin.LUMPNAME_SIZE);\\r\\n\\t\\tif(lumpname.length < i.length)\\r\\n\\t\\t\\tconsole.error(\\\"Lump name is too long (max is \\\"+WBin.LUMPNAME_SIZE+\\\"), it has been cut down, this could lead to an error in the future\\\");\\r\\n\\t\\tlumps.push({code: code, name: lumpname, data: data, start: lump_offset, size: data_length});\\r\\n\\t\\tlump_offset += data_length;\\r\\n\\t\\ttotal_size += WBin.LUMPHEADER_SIZE + data_length;\\r\\n\\t}\\r\\n\\r\\n\\t//construct the final file\\r\\n\\tvar data = new Uint8Array(total_size);\\r\\n\\t//set fourcc\\r\\n\\tdata.set(WBin.stringToUint8Array( WBin.FOUR_CC ));\\r\\n\\t//set version\\r\\n\\tdata.set(version, 4);\\r\\n\\t//set flags\\r\\n\\tdata.set(flags, 8);\\t\\r\\n\\t//set num lumps\\r\\n\\tdata.set( new Uint8Array( new Uint16Array([lumps.length]).buffer ), 10);\\t\\r\\n\\t//set origin_class_name\\r\\n\\tif(origin_class_name)\\r\\n\\t\\tdata.set( WBin.stringToUint8Array( origin_class_name, WBin.CLASSNAME_SIZE ), 12);\\r\\n\\r\\n\\tvar lump_data_start = WBin.HEADER_SIZE + lumps.length * WBin.LUMPHEADER_SIZE;\\r\\n\\r\\n\\t//copy lumps to final file\\r\\n\\tvar nextPos = WBin.HEADER_SIZE;\\r\\n\\tfor(var j in lumps)\\r\\n\\t{\\r\\n\\t\\tvar lump = lumps[j];\\r\\n\\t\\tvar buffer = lump.data;\\r\\n\\r\\n\\t\\t//create lump header\\r\\n\\t\\tvar lump_header = new Uint8Array( WBin.LUMPHEADER_SIZE );\\r\\n\\t\\tlump_header.set( new Uint8Array( (new Uint32Array([lump.start])).buffer ), 0);\\r\\n\\t\\tlump_header.set( new Uint8Array( (new Uint32Array([lump.size])).buffer ), 4);\\r\\n\\t\\tlump_header.set( WBin.stringToUint8Array( lump.code, 2), 8);\\r\\n\\t\\tlump_header.set( WBin.stringToUint8Array( lump.name, WBin.LUMPNAME_SIZE), 10);\\r\\n\\r\\n\\t\\t//copy lump header\\r\\n\\t\\tdata.set(lump_header,nextPos); \\r\\n\\t\\tnextPos += WBin.LUMPHEADER_SIZE;\\r\\n\\r\\n\\t\\t//copy lump data\\r\\n\\t\\tvar view = new Uint8Array( lump.data );\\r\\n\\t\\tdata.set(view, lump_data_start + lump.start);\\r\\n\\t}\\r\\n\\r\\n\\treturn data;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Extract the info from a Uint8Array containing WBin info and returns the object with all the lumps.\\r\\n* If the data contains info about the class to instantiate, the WBin instantiates the class and passes the data to it\\r\\n* @method WBin.load\\r\\n* @param {UInt8Array} data_array \\r\\n* @param {bool} skip_classname avoid getting the instance of the class specified in classname, and get only the lumps\\r\\n* @param {String} filename [optional] the filename where this wbin came from (important to mark resources)\\r\\n* @return {*} Could be an Object with all the lumps or an instance to the class specified in the WBin data\\r\\n*/\\r\\nWBin.load = function( data_array, skip_classname, filename )\\r\\n{\\r\\n\\tif(!data_array || ( data_array.constructor !== Uint8Array && data_array.constructor !== ArrayBuffer ) )\\r\\n\\t\\tthrow(\\\"WBin data must be ArrayBuffer or Uint8Array\\\");\\r\\n\\r\\n\\t//clone to avoid possible memory aligment problems\\r\\n\\tdata_array = new Uint8Array(data_array);\\r\\n\\r\\n\\tvar header = WBin.getHeaderInfo(data_array);\\r\\n\\tif(!header)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"Wrong WBin Header\\\");\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tif(header.version > (new Float32Array([WBin.VERSION])[0]) ) //all this because sometimes there are precission problems\\r\\n\\t\\tconsole.log(\\\"ALERT: WBin version is higher that code version\\\");\\r\\n\\r\\n\\tvar object = null;\\r\\n\\r\\n\\t//lump unpacking\\r\\n\\tfor(var i in header.lumps)\\r\\n\\t{\\r\\n\\t\\tif(!object) //we do not create the object unless there is a lump\\r\\n\\t\\t\\tobject = {};\\r\\n\\r\\n\\t\\tvar lump = header.lumps[i];\\r\\n\\t\\tvar lump_data = header.lump_data.subarray( lump.start, lump.start + lump.size );\\r\\n\\r\\n\\t\\tif(lump.size != lump_data.length )\\r\\n\\t\\t\\tthrow(\\\"WBin: incorrect wbin lump size\\\");\\r\\n\\r\\n\\t\\tvar lump_final = null;\\r\\n\\r\\n\\t\\tvar data_class_name = WBin.REVERSE_CODES[ lump.code ];\\r\\n\\t\\tif(!data_class_name)\\r\\n\\t\\t\\tthrow(\\\"WBin: Incorrect data code\\\");\\r\\n\\r\\n\\t\\tswitch(data_class_name)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase \\\"null\\\": break;\\r\\n\\t\\t\\tcase \\\"WideString\\\": \\r\\n\\t\\t\\t\\t\\t\\t\\tlump_data = new Uint16Array( (new Uint8Array( lump_data )).buffer ); //no break\\r\\n\\t\\t\\tcase \\\"String\\\":\\tlump_final = WBin.TypedArrayToString( lump_data ); break;\\r\\n\\t\\t\\tcase \\\"Number\\\": \\r\\n\\t\\t\\t\\t\\tif(header.version < 0.3) //LEGACY: remove\\r\\n\\t\\t\\t\\t\\t\\tlump_final = parseFloat( WBin.TypedArrayToString( lump_data ) );\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\tlump_final = (new Float64Array( lump_data.buffer ))[0];\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase \\\"WideObject\\\": \\r\\n\\t\\t\\t\\t\\t\\t\\tlump_data = new Uint16Array( (new Uint8Array( lump_data )).buffer ); //no break\\r\\n\\t\\t\\tcase \\\"Object\\\":\\t\\r\\n\\t\\t\\t\\tvar str = WBin.TypedArrayToString( lump_data );\\r\\n\\t\\t\\t\\tif(str)\\r\\n\\t\\t\\t\\t\\tlump_final = JSON.parse( str ); \\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"WBIN: lump \\\\\\\"\\\"+ lump.name +\\\"\\\\\\\" string is empty, skipping.\\\");\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase \\\"ArrayBuffer\\\": lump_final = new Uint8Array(lump_data).buffer; break; //clone\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\tlump_data = new Uint8Array(lump_data); //clone to avoid problems with bytes alignment\\r\\n\\t\\t\\t\\tvar ctor = WBin.classes[ data_class_name ] || window[ data_class_name ];\\r\\n\\t\\t\\t\\tif(!ctor)\\r\\n\\t\\t\\t\\t\\tthrow(\\\"WBin referenced class not found: \\\" + data_class_name );\\r\\n\\t\\t\\t\\tif( (lump_data.length / ctor.BYTES_PER_ELEMENT)%1 != 0)\\r\\n\\t\\t\\t\\t\\tthrow(\\\"WBin: size do not match type\\\");\\r\\n\\t\\t\\t\\tlump_final = new ctor(lump_data.buffer);\\r\\n\\t\\t}\\r\\n\\t\\tobject[ lump.name ] = lump_final;\\r\\n\\t}\\r\\n\\r\\n\\t//check if className exists, if it does use internal class parser\\r\\n\\tif(!skip_classname && header.classname)\\r\\n\\t{\\r\\n\\t\\tvar ctor = WBin.classes[ header.classname ] || window[ header.classname ];\\r\\n\\t\\tif(ctor && ctor.fromBinary)\\r\\n\\t\\t\\treturn ctor.fromBinary( object, filename );\\r\\n\\t\\telse if(ctor && ctor.prototype.fromBinary)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar inst = new ctor();\\r\\n\\t\\t\\tinst.fromBinary( object, filename );\\r\\n\\t\\t\\treturn inst;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tobject[\\\"@classname\\\"] = header.classname;\\r\\n\\t\\t}\\r\\n\\t}\\t\\r\\n\\r\\n\\treturn object;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Extract the header info from an ArrayBuffer (it contains version, and lumps info)\\r\\n* @method WBin.getHeaderInfo\\r\\n* @param {UInt8Array} data_array \\r\\n* @return {Object} Header\\r\\n*/\\r\\nWBin.getHeaderInfo = function(data_array)\\r\\n{\\r\\n\\t//check FOURCC\\r\\n\\tvar fourcc = data_array.subarray(0,4);\\r\\n\\tvar good_header = true;\\r\\n\\tfor(var i = 0; i < fourcc.length; i++)\\r\\n\\t\\tif(fourcc[i] != 0 && fourcc[i] != WBin.FOUR_CC.charCodeAt(i))\\r\\n\\t\\t\\treturn null; //wrong fourcc\\r\\n\\r\\n\\tvar version = WBin.readFloat32( data_array, 4);\\r\\n\\tvar flags = new Uint8Array( data_array.subarray(8,10) );\\r\\n\\tvar numlumps = WBin.readUint16(data_array, 10);\\r\\n\\tvar classname = WBin.TypedArrayToString( data_array.subarray(12,12 + WBin.CLASSNAME_SIZE) );\\r\\n\\r\\n\\tvar lumps = [];\\r\\n\\tfor(var i = 0; i < numlumps; ++i)\\r\\n\\t{\\r\\n\\t\\tvar start = WBin.HEADER_SIZE + i * WBin.LUMPHEADER_SIZE;\\r\\n\\t\\tvar lumpheader = data_array.subarray( start, start + WBin.LUMPHEADER_SIZE );\\r\\n\\t\\tvar lump = {};\\r\\n\\t\\tlump.start = WBin.readUint32(lumpheader,0);\\r\\n\\t\\tlump.size = WBin.readUint32(lumpheader,4);\\r\\n\\t\\tlump.code = WBin.TypedArrayToString(lumpheader.subarray(8,10));\\r\\n\\t\\tlump.name = WBin.TypedArrayToString(lumpheader.subarray(10));\\r\\n\\t\\tlumps.push(lump);\\r\\n\\t}\\r\\n\\r\\n\\tvar lump_data = data_array.subarray( WBin.HEADER_SIZE + numlumps * WBin.LUMPHEADER_SIZE );\\r\\n\\r\\n\\treturn {\\r\\n\\t\\tversion: version,\\r\\n\\t\\tflags: flags,\\r\\n\\t\\tclassname: classname,\\r\\n\\t\\tnumlumps: numlumps,\\r\\n\\t\\tlumps: lumps,\\r\\n\\t\\tlump_data: lump_data\\r\\n\\t};\\r\\n}\\r\\n\\r\\nWBin.getObjectClassName = function(obj) {\\r\\n if (obj && obj.constructor && obj.constructor.toString) {\\r\\n var arr = obj.constructor.toString().match(\\r\\n /function\\\\s*(\\\\w+)/);\\r\\n if (arr && arr.length == 2) {\\r\\n return arr[1];\\r\\n }\\r\\n }\\r\\n return undefined;\\r\\n}\\r\\n\\r\\nWBin.stringToUint8Array = function(str, fixed_length)\\r\\n{\\r\\n\\tvar r = new Uint8Array( fixed_length ? fixed_length : str.length);\\r\\n\\tvar warning = false;\\r\\n\\tfor(var i = 0; i < str.length; i++)\\r\\n\\t{\\r\\n\\t\\tvar c = str.charCodeAt(i);\\r\\n\\t\\tif(c > 255)\\r\\n\\t\\t\\twarning = true;\\r\\n\\t\\tr[i] = c;\\r\\n\\t}\\r\\n\\r\\n\\tif(warning)\\r\\n\\t\\tconsole.warn(\\\"WBin: there are characters in the string that cannot be encoded in 1 byte.\\\");\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\nWBin.TypedArrayToString = function(typed_array, same_size)\\r\\n{\\r\\n\\tvar r = \\\"\\\";\\r\\n\\tfor(var i = 0; i < typed_array.length; i++)\\r\\n\\t\\tif (typed_array[i] == 0 && !same_size)\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\telse\\r\\n\\t\\t\\tr += String.fromCharCode( typed_array[i] );\\r\\n\\t//return String.fromCharCode.apply(null,typed_array)\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\nWBin.stringToTypedArray = function(str, fixed_length)\\r\\n{\\r\\n\\tvar r = new Uint8Array( fixed_length ? fixed_length : str.length);\\r\\n\\tvar warning = false;\\r\\n\\tfor(var i = 0; i < str.length; i++)\\r\\n\\t{\\r\\n\\t\\tvar c = str.charCodeAt(i);\\r\\n\\t\\tif(c > 255)\\r\\n\\t\\t\\twarning = true;\\r\\n\\t\\tr[i] = c;\\r\\n\\t}\\r\\n\\r\\n\\tif(!warning)\\r\\n\\t\\treturn r;\\r\\n\\r\\n\\t//convert to 16bits per character\\r\\n\\tvar r = new Uint16Array( fixed_length ? fixed_length : str.length);\\r\\n\\tfor(var i = 0; i < str.length; i++)\\r\\n\\t{\\r\\n\\t\\tvar c = str.charCodeAt(i);\\r\\n\\t\\tr[i] = c;\\r\\n\\t}\\r\\n\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\nWBin.readUint16 = function( buffer, pos )\\r\\n{\\r\\n\\tvar dv = new DataView( buffer.buffer, buffer.byteOffset );\\r\\n\\treturn dv.getUint16( pos, true );\\r\\n}\\r\\n\\r\\nWBin.readUint32 = function(buffer, pos)\\r\\n{\\r\\n\\tvar dv = new DataView( buffer.buffer, buffer.byteOffset);\\r\\n\\treturn dv.getUint32( pos, true );\\r\\n}\\r\\n\\r\\nWBin.readFloat32 = function(buffer, pos)\\r\\n{\\r\\n\\tvar dv = new DataView( buffer.buffer, buffer.byteOffset );\\r\\n\\treturn dv.getFloat32( pos, true );\\r\\n}\\r\\n\\r\\nglobal.WBin = WBin;\\r\\n\\r\\n})( typeof(global) != \\\"undefined\\\" ? global : this );\\r\\n///@FILE:../src/utils/lscript.js\\r\\n///@INFO: SCRIPTS\\r\\n// ******* LScript **************************\\r\\n\\r\\n/**\\r\\n* LScript allows to compile code during execution time having a clean context\\r\\n* It adds some syntactic features and controls errors in a safe way\\r\\n* @class LScript\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nfunction LScript()\\r\\n{\\r\\n\\tthis.code = \\\"function update(dt) {\\\\n\\\\n}\\\";\\r\\n\\tthis.exported_callbacks = []; //detects if there is a function with this name and exports it as a property\\r\\n\\tthis.extracode = \\\"\\\";\\r\\n\\tthis.extra_methods = null; //add object with methods here to attach methods\\r\\n}\\r\\n\\r\\n\\r\\nLScript.onerror = null; //global used to catch errors in scripts\\r\\n\\r\\nLScript.eval = function(argv_names,code) { return eval(\\\"(function(\\\"+argv_names+\\\"){\\\\n\\\"+code+\\\"\\\\n})\\\"); }; //not used\\r\\n\\r\\nLScript.catch_exceptions = false;\\r\\nLScript.show_errors_in_console = true;\\r\\n\\r\\n//compiles the string, tryes to keep the current state\\r\\nLScript.prototype.compile = function( arg_vars, save_context_vars )\\r\\n{\\r\\n\\tvar argv_names = [];\\r\\n\\tvar argv_values = [];\\r\\n\\tif(arg_vars)\\r\\n\\t{\\r\\n\\t\\tfor(var i in arg_vars)\\r\\n\\t\\t{\\r\\n\\t\\t\\targv_names.push(i);\\r\\n\\t\\t\\targv_values.push( arg_vars[i]);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\targv_names = argv_names.join(\\\",\\\");\\r\\n\\r\\n\\tvar code = this.code;\\r\\n\\tcode = LScript.expandCode( code );\\r\\n\\r\\n\\tvar extra_code = \\\"\\\";\\r\\n\\tfor(var i in this.exported_callbacks)\\r\\n\\t{\\r\\n\\t\\tvar callback_name = this.exported_callbacks[i];\\r\\n\\t\\textra_code += \\\"\\tif(typeof(\\\"+callback_name+\\\") != 'undefined' && \\\"+callback_name+\\\" != window[\\\\\\\"\\\"+callback_name+\\\"\\\\\\\"] ) this.\\\"+callback_name + \\\" = \\\"+callback_name+\\\";\\\\n\\\";\\r\\n\\t}\\r\\n\\tcode += extra_code;\\r\\n\\tthis._last_executed_code = code;\\r\\n\\r\\n\\tvar old_context = this._context;\\r\\n\\r\\n\\tif(!LScript.catch_exceptions)\\r\\n\\t{\\r\\n\\t\\tthis._class = new Function(argv_names, code);//<-- PARSING POINT HERE ***************************************\\r\\n\\t\\tvar context_function = LScript.applyToConstructor( this._class, argv_values, this.extra_methods ); //bind globals and methods to context\\r\\n\\t\\tthis._context = new context_function(); //<-- EXECUTION POINT HERE ***************************************\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\ttry\\r\\n\\t\\t{\\r\\n\\t\\t\\t//LScript.eval(argv_names,code);\\r\\n\\t\\t\\tthis._class = new Function(argv_names, code);\\r\\n\\t\\t\\tvar context_function = LScript.applyToConstructor( this._class, argv_values, this.extra_methods ); //bind globals and methods to context\\r\\n\\t\\t\\tthis._context = new context_function(); //<-- EXECUTION POINT HERE ***************************************\\r\\n\\t\\t}\\r\\n\\t\\tcatch (err)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!this._class)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.error(\\\"Parsing error in script\\\\n\\\" + err);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tthis._class = null;\\r\\n\\t\\t\\tthis._context = null;\\r\\n\\t\\t\\tif(LScript.show_errors_in_console)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar error_line = LScript.computeLineFromError(err);\\r\\n\\t\\t\\t\\tconsole.error(\\\"Error in script\\\\n\\\" + err);\\r\\n\\t\\t\\t\\tif( console.groupCollapsed )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.groupCollapsed(\\\"Error line: \\\" + error_line + \\\" Watch code\\\");\\r\\n\\t\\t\\t\\t\\tLScript.showCodeInConsole( this._last_executed_code, error_line );\\r\\n\\t\\t\\t\\t\\tconsole.groupEnd();\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tconsole.error(\\\"Error line: \\\" + error_line);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif(this.onerror)\\r\\n\\t\\t\\t\\tthis.onerror(err, this._last_executed_code);\\r\\n\\t\\t\\tif(LScript.onerror)\\r\\n\\t\\t\\t\\tLScript.onerror(err, this._last_executed_code, this);\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif(save_context_vars && old_context)\\r\\n\\t{\\r\\n\\t\\tfor(var i in old_context)\\r\\n\\t\\t\\tif( this._context[i] !== undefined && old_context[i] && old_context[i].constructor !== Function && (!this._context[i] || this._context[i].constructor !== Function) )\\r\\n\\t\\t\\t\\tthis._context[i] = old_context[i];\\r\\n\\t}\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n//does this script contains this method?\\r\\nLScript.prototype.hasMethod = function(name)\\r\\n{\\r\\n\\tif(!this._context || !this._context[name] || typeof(this._context[name]) != \\\"function\\\") \\r\\n\\t\\treturn false;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n//argv must be an array with parameters, unless skip_expand is true\\r\\nLScript.prototype.callMethod = function( name, argv, expand_parameters, parent_object )\\r\\n{\\r\\n\\tif(!this._context || !this._context[name]) \\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!LScript.catch_exceptions)\\r\\n\\t{\\r\\n\\t\\t//call expanding parameters\\r\\n\\t\\tif(argv && argv.constructor === Array && expand_parameters)\\r\\n\\t\\t\\treturn this._context[name].apply(this._context, argv);\\r\\n\\t\\t//call without expanding parameters\\r\\n\\t\\treturn this._context[name].call(this._context, argv);\\r\\n\\t}\\r\\n\\r\\n\\ttry\\r\\n\\t{\\r\\n\\t\\t//call expanding parameters\\r\\n\\t\\tif(argv && argv.constructor === Array && expand_parameters)\\r\\n\\t\\t\\treturn this._context[name].apply(this._context, argv);\\r\\n\\t\\t//call without expanding parameters\\r\\n\\t\\treturn this._context[name].call(this._context, argv);\\r\\n\\t}\\r\\n\\tcatch(err)\\r\\n\\t{\\r\\n\\t\\t//catch error in script, detect line and show console info\\r\\n\\t\\tvar error_line = LScript.computeLineFromError(err);\\r\\n\\t\\tvar parent_info = \\\"\\\"; \\r\\n\\t\\tif (parent_object && parent_object.toInfoString )\\r\\n\\t\\t\\tparent_info = \\\" from \\\" + parent_object.toInfoString();\\r\\n\\t\\tconsole.error(\\\"Error from function \\\" + name + parent_info + \\\": \\\", err.toString());\\r\\n\\t\\tif( console.groupCollapsed )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.groupCollapsed(\\\"Error line: \\\" + error_line + \\\" Watch code\\\");\\r\\n\\t\\t\\tLScript.showCodeInConsole( this._last_executed_code, error_line );\\r\\n\\t\\t\\tconsole.groupEnd();\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tconsole.error(\\\"Error line: \\\" + error_line);\\r\\n\\t\\tif(this.onerror)\\r\\n\\t\\t\\tthis.onerror({ error: err, msg: err.toString(), line: error_line, lscript: this, code: this._last_executed_code, method_name: name });\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//Given a constructor, it attaches several global arguments and methods (from kybernetikos in stackoverflow)\\r\\nLScript.applyToConstructor = function(constructor, argArray, methods) {\\r\\n var args = [null].concat(argArray);\\r\\n\\tif(methods)\\r\\n\\t\\tfor(var i in methods)\\r\\n\\t\\t\\tObject.defineProperty( constructor.prototype, i, { value: methods[i], enumerable: true });\\r\\n var factoryFunction = constructor.bind.apply(constructor, args);\\r\\n return factoryFunction;\\r\\n}\\r\\n\\r\\n//dumps all the code to the console\\r\\nLScript.showCodeInConsole = function( code, error_line)\\r\\n{\\r\\n\\tif(!code)\\r\\n\\t\\treturn;\\r\\n\\tvar lines = code.split(\\\"\\\\n\\\");\\r\\n\\tvar gutter_style = \\\"display: inline-block; width: 40px; background-color:#999; color: white;\\\";\\r\\n\\tfor(var i = 0; i < lines.length; i++ )\\r\\n\\t\\tif(i == error_line)\\r\\n\\t\\t\\tconsole.log(\\\"%c \\\"+i+\\\". \\\" + lines[i], \\\"background-color: #A33; color: #FAA;\\\" );\\r\\n\\t\\telse\\r\\n\\t\\t\\tconsole.log(\\\"%c \\\"+i+\\\". \\\", gutter_style, lines[i] );\\r\\n}\\r\\n\\r\\n//remove comments and trims empty lines\\r\\nLScript.cleanCode = function(code)\\r\\n{\\r\\n\\tif(!code)\\r\\n\\t\\treturn \\\"\\\";\\r\\n\\t/* this should be removed \\r\\n\\t\\tI write this to test this func using LScript.cleanCode( LScript.cleanCode.toString() );\\r\\n\\t*/\\r\\n\\t//var rx = /(\\\\/\\\\*([^*]|[\\\\r\\\\n]|(\\\\*+([^*\\\\/]|[\\\\r\\\\n])))*\\\\*+\\\\/)|(\\\\/\\\\/.*)/g;\\r\\n\\tvar rx = /\\\\*([^*]|[\\\\r\\\\n]|(\\\\*+([^*/]|[\\\\r\\\\n])))*\\\\*+/g;\\r\\n\\tvar code = code.replace( rx ,\\\"\\\");\\r\\n\\tvar lines = code.split(\\\"\\\\n\\\");\\r\\n\\tvar result = [];\\r\\n\\tfor(var i = 0; i < lines.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar line = lines[i]; \\r\\n\\t\\tvar pos = line.indexOf(\\\"//\\\");\\r\\n\\t\\tif(pos != -1 && line.substr(0,pos).indexOf(\\\"\\\\\\\"\\\") == -1) //avoid removing lines with comments inside strings\\r\\n\\t\\t\\tline = line.substr(0,pos);\\r\\n\\t\\tline = line.trim();\\r\\n\\t\\tif(line.length)\\r\\n\\t\\t\\tresult.push(line);\\r\\n\\t}\\r\\n\\treturn result.join(\\\"\\\\n\\\");\\r\\n}\\r\\n\\r\\n// Adds some extra features to JS like:\\r\\n// - support for multiline strings (this feature was introduced in ES6 but in case is not supported)\\r\\n// - the use of private or public in variables.\\r\\nLScript.expandCode = function(code)\\r\\n{\\r\\n\\tif(!code)\\r\\n\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t//allow support to multiline strings\\r\\n\\tif( code.indexOf(\\\"'''\\\") != -1 )\\r\\n\\t{\\r\\n\\t\\tvar lines = code.split(\\\"'''\\\");\\r\\n\\t\\tcode = \\\"\\\";\\r\\n\\t\\tfor(var i = 0; i < lines.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(i % 2 == 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcode += lines[i];\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tcode += '\\\"' + lines[i].split(\\\"\\\\n\\\").join(\\\"\\\\\\\\n\\\\\\\\\\\\n\\\") + '\\\"';\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//allow to use public var foo = 10;\\r\\n\\tvar lines = code.split(\\\"\\\\n\\\");\\r\\n\\tvar update = false;\\r\\n\\tfor(var i = 0; i < lines.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar line = lines[i].trim();\\r\\n\\t\\tvar pos = line.indexOf(\\\" \\\");\\r\\n\\t\\tvar first_word = line.substr(0,pos);\\r\\n\\r\\n\\t\\t//all this horrendous code to parse \\\"public var name : type = value;\\\" and all the possible combinations\\r\\n\\t\\tif( first_word == \\\"public\\\" || first_word == \\\"private\\\" || first_word == \\\"hidden\\\" )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar index = line.indexOf(\\\"//\\\");\\r\\n\\t\\t\\tif(index != -1)\\r\\n\\t\\t\\t\\tline = line.substr(0,index); //remove one-line comments\\r\\n\\t\\t\\tvar index = line.lastIndexOf(\\\";\\\");\\r\\n\\t\\t\\tif(index != -1)\\r\\n\\t\\t\\t\\tline = line.substr(0,index); //remove semicolon\\r\\n\\t\\t\\tvar t = line.split(\\\" \\\");\\r\\n\\t\\t\\tif( t[1] != 'var')\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar text = line;\\r\\n\\t\\t\\tvar type = null;\\r\\n\\t\\t\\tvar value = \\\"undefined\\\";\\r\\n\\t\\t\\tvar equal_index = text.indexOf(\\\"=\\\");\\r\\n\\t\\t\\tif( equal_index != -1 )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvalue = text.substr( equal_index + 1 ).trim();\\r\\n\\t\\t\\t\\ttext = text.substr( 0, equal_index ).trim();\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar colon_index = text.indexOf(\\\":\\\");\\r\\n\\t\\t\\tif(colon_index != -1)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttype = text.substr( colon_index + 1 ).trim();\\r\\n\\t\\t\\t\\ttext = text.substr( 0, colon_index ).trim();\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tvar keywords = text.split(\\\" \\\");\\r\\n\\r\\n\\t\\t\\tvar varname = keywords[2];\\r\\n\\t\\t\\tif(!varname)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar type_options = {};\\r\\n\\t\\t\\tif(type)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar array_index = type.indexOf('[]');\\r\\n\\t\\t\\t\\tif( array_index != -1 )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\ttype_options.type = LS.TYPES.ARRAY;\\r\\n\\t\\t\\t\\t\\ttype_options.data_type = type.substr( 0, array_index );\\r\\n\\t\\t\\t\\t\\tif(!value || value === \\\"undefined\\\")\\r\\n\\t\\t\\t\\t\\t\\tvalue = \\\"[]\\\";\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\ttype_options.type = type;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif( LS.Components[ type ] ) //for components\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\ttype_options.component_class = type;\\r\\n\\t\\t\\t\\t\\ttype_options.type = LS.TYPES.COMPONENT;\\r\\n\\t\\t\\t\\t\\tif(!value)\\r\\n\\t\\t\\t\\t\\t\\tvalue = \\\"null\\\";\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse if( LS.ResourceClasses[ type ] ) //for resources\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\ttype_options.resource_classname = type;\\r\\n\\t\\t\\t\\t\\ttype_options.type = LS.TYPES.RESOURCE;\\r\\n\\t\\t\\t\\t\\tif(!value)\\r\\n\\t\\t\\t\\t\\t\\tvalue = \\\"null\\\";\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse if( type == \\\"int\\\" || type == \\\"integer\\\")\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\ttype_options.step = 1;\\r\\n\\t\\t\\t\\t\\ttype_options.type = LS.TYPES.NUMBER;\\r\\n\\t\\t\\t\\t\\tif(!value)\\r\\n\\t\\t\\t\\t\\t\\tvalue = 0;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif( keywords[0] == \\\"private\\\" || keywords[0] == \\\"hidden\\\" )\\r\\n\\t\\t\\t\\ttype_options.widget = \\\"null\\\";\\r\\n\\t\\t\\tlines[i] = \\\"this.createProperty('\\\" + varname + \\\"',\\\" + value + \\\", \\\"+JSON.stringify( type_options )+\\\" );\\\";\\r\\n\\t\\t\\tupdate = true;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\tif(update)\\r\\n\\t\\tcode = lines.join(\\\"\\\\n\\\");\\r\\n\\r\\n\\treturn code;\\r\\n}\\r\\n\\r\\n// In case of error inside the scripts, tries to determine the error line (not as easy as it seems)\\r\\n// Doesnt work in all cases\\r\\nLScript.computeLineFromError = function( err )\\r\\n{\\r\\n\\tif(err.lineNumber !== undefined)\\r\\n\\t{\\r\\n\\t\\treturn err.lineNumber;\\r\\n\\t}\\r\\n\\telse if(err.stack)\\r\\n\\t{\\r\\n\\t\\tvar lines = err.stack.split(\\\"\\\\n\\\");\\r\\n\\t\\tvar line = lines[1].trim();\\r\\n\\t\\tif(line.indexOf(\\\"(native)\\\") != -1)\\r\\n\\t\\t\\treturn -1;\\r\\n\\t\\tvar tokens = line.split(\\\" \\\");\\r\\n\\t\\tvar pos = line.lastIndexOf(\\\":\\\");\\r\\n\\t\\tvar pos2 = line.lastIndexOf(\\\":\\\",pos-1);\\r\\n\\t\\tvar num = parseInt( line.substr(pos2+1,pos-pos2-1) );\\r\\n\\t\\tvar ch = parseInt( line.substr(pos+1, line.length - 2 - pos) );\\r\\n\\t\\tif(tokens[1] == \\\"Object.CodingModule.eval\\\")\\r\\n\\t\\t\\treturn -1;\\r\\n\\t\\tif (line.indexOf(\\\"LScript\\\") != -1 || line.indexOf(\\\"\\\") != -1 )\\r\\n\\t\\t\\tnum -= 3; //ignore the header lines of the LScript class\\r\\n\\t\\treturn num;\\r\\n\\t}\\r\\n\\treturn -1;\\r\\n}\\r\\n\\r\\n\\r\\nglobal.LScript = LScript;\\r\\n\\r\\n\\r\\n///@FILE:../src/core.js\\r\\n//Global Scope\\r\\n//better array conversion to string for serializing\\r\\nif( !Uint8Array.prototype.toJSON )\\r\\n{\\r\\n\\tvar typed_arrays = [ Uint8Array, Int8Array, Uint16Array, Int16Array, Uint32Array, Int32Array, Float32Array, Float64Array ];\\r\\n\\ttyped_arrays.forEach( function(v) { \\r\\n\\t\\tv.prototype.toJSON = function(){ return Array.prototype.slice.call(this); }\\r\\n\\t});\\r\\n}\\r\\n\\r\\nif( typeof(GL) === \\\"undefined\\\" )\\r\\n\\tconsole.error(\\\"LiteScene requires to include LiteGL first. More info: https://github.com/jagenjo/litegl.js\\\");\\r\\n\\r\\n\\r\\n/**\\r\\n* LS is the global scope for the global functions and containers of LiteScene\\r\\n*\\r\\n///@INFO: BASE\\r\\n* @class LS\\r\\n* @module LS\\r\\n*/\\r\\n\\r\\nvar LS = {\\r\\n\\r\\n\\t//systems: defined in their own files\\r\\n\\tResourcesManager: null,\\r\\n\\tPicking: null,\\r\\n\\tPlayer: null,\\r\\n\\tGUI: null,\\r\\n\\tNetwork: null,\\r\\n\\tInput: null,\\r\\n\\tRenderer: null,\\r\\n\\tPhysics: null,\\r\\n\\tShadersManager: null,\\r\\n\\tFormats: null,\\r\\n\\tTween: null,\\r\\n\\r\\n\\t//containers\\r\\n\\tClasses: {}, //maps classes name like \\\"Prefab\\\" or \\\"Animation\\\" to its namespace \\\"LS.Prefab\\\". Used in Formats and ResourceManager when reading classnames from JSONs or WBin.\\r\\n\\tResourceClasses: {}, //classes that can contain a resource of the system\\r\\n\\tResourceClasses_by_extension: {}, //used to associate JSONs to resources, only used by GRAPHs\\r\\n\\tGlobals: {}, //global scope to share info among scripts\\r\\n\\r\\n\\t/**\\r\\n\\t* Contains all the registered components\\r\\n\\t* \\r\\n\\t* @property Components\\r\\n\\t* @type {Object}\\r\\n\\t* @default {}\\r\\n\\t*/\\r\\n\\tComponents: {},\\r\\n\\r\\n\\t/**\\r\\n\\t* Contains all the registered material classes\\r\\n\\t* \\r\\n\\t* @property MaterialClasses\\r\\n\\t* @type {Object}\\r\\n\\t* @default {}\\r\\n\\t*/\\r\\n\\tMaterialClasses: {},\\r\\n\\r\\n\\t//vars used for uuid genereration\\r\\n\\t_last_uid: 1,\\r\\n\\t_uid_prefix: \\\"@\\\", //WARNING: must be one character long\\r\\n\\tdebug: false, //enable to see verbose output\\r\\n\\tallow_static: true, //used to disable static instances in the editor\\r\\n\\tallow_scripts: true, //if true, then Script components and Graphs can contain code\\r\\n\\r\\n\\t//for HTML GUI\\r\\n\\t_gui_element: null,\\r\\n\\t_gui_style: null,\\r\\n\\r\\n\\t/**\\r\\n\\t* Generates a UUID based in the user-agent, time, random and sequencial number. Used for Nodes and Components.\\r\\n\\t* @method generateUId\\r\\n\\t* @return {string} uuid\\r\\n\\t*/\\r\\n\\tgenerateUId: function ( prefix, suffix ) {\\r\\n\\t\\tprefix = prefix || \\\"\\\";\\r\\n\\t\\tsuffix = suffix || \\\"\\\";\\r\\n\\t\\tvar str = this._uid_prefix + prefix + (window.navigator.userAgent.hashCode() % 0x1000000).toString(16) + \\\"-\\\"; //user agent\\r\\n\\t\\tstr += (GL.getTime()|0 % 0x1000000).toString(16) + \\\"-\\\"; //date\\r\\n\\t\\tstr += Math.floor((1 + Math.random()) * 0x1000000).toString(16) + \\\"-\\\"; //rand\\r\\n\\t\\tstr += (this._last_uid++).toString(16); //sequence\\r\\n\\t\\tstr += suffix;\\r\\n\\t\\treturn str; \\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* validates name string to ensure there is no forbidden characters\\r\\n\\t* valid characters are letters, numbers, spaces, dash, underscore and dot\\r\\n\\t* @method validateName\\r\\n\\t* @param {string} name\\r\\n\\t* @return {boolean} \\r\\n\\t*/\\r\\n\\tvalidateName: function(v)\\r\\n\\t{\\r\\n\\t\\tvar exp = /^[a-z\\\\s0-9-_.]+$/i; //letters digits and dashes\\r\\n\\t\\treturn v.match(exp);\\r\\n\\t},\\r\\n\\r\\n\\tvalid_property_types: [\\\"String\\\",\\\"Number\\\",\\\"Boolean\\\",\\\"color\\\",\\\"vec2\\\",\\\"vec3\\\",\\\"vec4\\\",\\\"quat\\\",\\\"mat3\\\",\\\"mat4\\\",\\\"Resource\\\",\\\"Animation\\\",\\\"Texture\\\",\\\"Prefab\\\",\\\"Mesh\\\",\\\"ShaderCode\\\",\\\"node\\\",\\\"component\\\"],\\r\\n\\t\\r\\n\\t//used when creating a property to a component, to see if the type is valid\\r\\n\\tvalidatePropertyType: function(v)\\r\\n\\t{\\r\\n\\t\\tif(\\tthis.valid_property_types.indexOf(v) == -1 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error( v + \\\" is not a valid property value type.\\\" );\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t}\\r\\n\\t\\treturn true;\\r\\n\\t},\\r\\n\\r\\n\\t_catch_exceptions: false, //used to try/catch all possible callbacks (used mostly during development inside an editor) It is linked to LScript too\\r\\n\\r\\n\\t/**\\r\\n\\t* Register a component (or several) so it is listed when searching for new components to attach\\r\\n\\t*\\r\\n\\t* @method registerComponent\\r\\n\\t* @param {Component} component component class to register\\r\\n\\t* @param {String} old_classname [optional] the name of the component that this class replaces (in case you are renaming it)\\r\\n\\t*/\\r\\n\\tregisterComponent: function( component, old_classname ) { \\r\\n\\r\\n\\t\\t//allows to register several at the same time\\r\\n\\t\\tvar name = LS.getClassName( component );\\r\\n\\r\\n\\t\\tif(old_classname && old_classname.constructor !== String)\\r\\n\\t\\t\\tthrow(\\\"old_classname must be null or a String\\\");\\r\\n\\r\\n\\t\\t//save the previous class in case we are replacing it\\r\\n\\t\\tvar old_class = this.Components[ old_classname || name ];\\r\\n\\r\\n\\t\\t//register\\r\\n\\t\\tthis.Components[ name ] = component; \\r\\n\\t\\tcomponent.is_component = true;\\t\\r\\n\\t\\tcomponent.resource_type = \\\"Component\\\";\\r\\n\\r\\n\\t\\t//Helper: checks for errors\\r\\n\\t\\tif( !!component.prototype.onAddedToNode != !!component.prototype.onRemovedFromNode ||\\r\\n\\t\\t\\t!!component.prototype.onAddedToScene != !!component.prototype.onRemovedFromScene )\\r\\n\\t\\t\\tconsole.warn(\\\"%c Component \\\"+name+\\\" could have a bug, check events: \\\" + name , \\\"font-size: 2em\\\");\\r\\n\\t\\tif( component.prototype.getResources && !component.prototype.onResourceRenamed )\\r\\n\\t\\t\\tconsole.warn(\\\"%c Component \\\"+name+\\\" could have a bug, it uses resources but doesnt implement onResourceRenamed, this could lead to problems when resources are renamed.\\\", \\\"font-size: 1.2em\\\");\\r\\n\\t\\t//if( !component.prototype.serialize || !component.prototype.configure )\\r\\n\\t\\t//\\tconsole.warn(\\\"%c Component \\\"+name+\\\" could have a bug, it doesnt have a serialize or configure method. No state will be saved.\\\", \\\"font-size: 1.2em\\\");\\r\\n\\r\\n\\t\\t//add stuff to the class\\r\\n\\t\\tif(!component.actions)\\r\\n\\t\\t\\tcomponent.actions = {};\\r\\n\\r\\n\\t\\t//add default methods\\r\\n\\t\\tLS.extendClass( component, LS.BaseComponent );\\r\\n\\t\\tBaseComponent.addExtraMethods( component );\\r\\n\\r\\n\\t\\tif( LS.debug )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar c = new component();\\r\\n\\t\\t\\tvar r = c.serialize();\\r\\n\\t\\t\\tif(!r.object_class)\\r\\n\\t\\t\\t\\tconsole.warn(\\\"%c Component \\\"+name+\\\" could have a bug, serialize() method has object_class missing.\\\", \\\"font-size: 1.2em\\\");\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//event\\r\\n\\t\\tLEvent.trigger(LS, \\\"component_registered\\\", component ); \\r\\n\\r\\n\\t\\tif(LS.GlobalScene) //because main components are create before the global scene is created\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.replaceComponentClass( LS.GlobalScene, old_classname || name, name );\\r\\n\\t\\t\\tif( old_classname != name )\\r\\n\\t\\t\\t\\tthis.unregisterComponent( old_classname );\\r\\n\\t\\t}\\r\\n\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Unregisters a component from the system (although existing instances are kept in the scene)\\r\\n\\t*\\r\\n\\t* @method unregisterComponent\\r\\n\\t* @param {String} name the name of the component to unregister\\r\\n\\t*/\\r\\n\\tunregisterComponent: function( name ) { \\r\\n\\t\\t//not found\\r\\n\\t\\tif(!this.Components[name])\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t//delete from the list of component (existing components will still exists)\\r\\n\\t\\tdelete this.Components[name];\\r\\n\\t},\\r\\n\\r\\n\\r\\n\\t/**\\r\\n\\t* Tells you if one class is a registered component class\\r\\n\\t*\\r\\n\\t* @method isClassComponent\\r\\n\\t* @param {ComponentClass} comp component class to evaluate\\r\\n\\t* @return {boolean} true if the component class is registered\\r\\n\\t*/\\r\\n\\tisClassComponent: function( comp_class )\\r\\n\\t{\\r\\n\\t\\tvar name = this.getClassName( comp_class );\\r\\n\\t\\treturn !!this.Components[name];\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Replaces all components of one class in the scene with components of another class\\r\\n\\t*\\r\\n\\t* @method replaceComponentClass\\r\\n\\t* @param {Scene} scene where to apply the replace\\r\\n\\t* @param {String} old_class_name name of the class to be replaced\\r\\n\\t* @param {String} new_class_name name of the class that will be used instead\\r\\n\\t* @return {Number} the number of components replaced\\r\\n\\t*/\\r\\n\\treplaceComponentClass: function( scene, old_class_name, new_class_name )\\r\\n\\t{\\r\\n\\t\\tvar proposed_class = new_class_name.constructor === String ? LS.Components[ new_class_name ] : new_class_name;\\r\\n\\t\\tif(!proposed_class)\\r\\n\\t\\t\\treturn 0;\\r\\n\\r\\n\\t\\t//this may be a problem if the old class has ben unregistered...\\r\\n\\t\\tvar old_class = null;\\r\\n\\t\\t\\r\\n\\t\\tif(\\told_class_name.constructor === String )\\r\\n\\t\\t{\\r\\n\\t\\t\\told_class = LS.Components[ old_class_name ];\\r\\n\\t\\t\\tif( old_class )\\r\\n\\t\\t\\t\\told_class_name = LS.getClassName( old_class );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar num = 0;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < scene._nodes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar node = scene._nodes[i];\\r\\n\\r\\n\\t\\t\\t//search in current components\\r\\n\\t\\t\\tfor(var j = 0; j < node._components.length; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar comp = node._components[j];\\r\\n\\t\\t\\t\\tvar comp_name = comp.constructor === LS.MissingComponent ? comp._comp_class : LS.getObjectClassName( comp );\\r\\n\\r\\n\\t\\t\\t\\t//it it is the exact same class then skip it\\r\\n\\t\\t\\t\\tif( comp.constructor === proposed_class )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\t//if this component is neither the old comp nor the new one\\r\\n\\t\\t\\t\\tif( comp_name != old_class_name && comp_name != new_class_name ) \\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tvar info = comp.serialize();\\r\\n\\t\\t\\t\\tnode.removeComponent( comp );\\r\\n\\r\\n\\t\\t\\t\\tvar new_comp = null;\\r\\n\\t\\t\\t\\ttry\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tnew_comp = new proposed_class();\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.error(\\\"Error in replacement component constructor\\\");\\r\\n\\t\\t\\t\\t\\tconsole.error(err);\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tnode.addComponent( new_comp, j < node._components.length ? j : undefined );\\r\\n\\t\\t\\t\\tnew_comp.configure( info );\\r\\n\\t\\t\\t\\tnum++;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn num;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Register a resource class so we know which classes could be use as resources\\r\\n\\t*\\r\\n\\t* @method registerResourceClass\\r\\n\\t* @param {ComponentClass} c component class to register\\r\\n\\t*/\\r\\n\\tregisterResourceClass: function( resourceClass )\\r\\n\\t{\\r\\n\\t\\tvar class_name = LS.getClassName( resourceClass );\\r\\n\\t\\tthis.ResourceClasses[ class_name ] = resourceClass;\\r\\n\\t\\tthis.Classes[ class_name ] = resourceClass;\\r\\n\\t\\tresourceClass.is_resource = true;\\r\\n\\r\\n\\t\\tif( !resourceClass.FORMAT )\\r\\n\\t\\t\\tresourceClass.FORMAT = {};\\r\\n\\r\\n\\t\\tresourceClass.FORMAT.resource = class_name;\\r\\n\\t\\tresourceClass.FORMAT.resourceClass = resourceClass;\\r\\n\\r\\n\\t\\tif( resourceClass.EXTENSION )\\r\\n\\t\\t\\tresourceClass.FORMAT.extension = resourceClass.EXTENSION.toLowerCase();\\r\\n\\r\\n\\t\\tvar extension = resourceClass.FORMAT.extension;\\r\\n\\t\\tif(!extension && resourceClass != LS.Resource )\\r\\n\\t\\t\\tconsole.warn(\\\"Resource without extension info? \\\" + class_name );\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.ResourceClasses_by_extension[ extension ] = resourceClass;\\r\\n\\t\\t\\tresourceClass.EXTENSION = extension;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( LS.Formats && extension )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar format_info = LS.Formats.supported[ extension ];\\r\\n\\t\\t\\tif( !format_info )\\r\\n\\t\\t\\t\\tLS.Formats.supported[ extension ] = format_info = resourceClass.FORMAT;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(!format_info.resourceClass)\\r\\n\\t\\t\\t\\t\\tformat_info.resourceClass = resourceClass;\\r\\n\\t\\t\\t\\t//else if(format_info.resourceClass != resourceClass) //animations and prefab use the same file extension\\r\\n\\t\\t\\t\\t//\\tconsole.warn(\\\"format has resourceClass that do not match this resource: \\\", LS.getClassName(format_info.resourceClass), LS.getClassName(resourceClass) );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//some validation here? maybe...\\r\\n\\t},\\r\\n\\r\\n\\t//Coroutines that allow to work with async functions\\r\\n\\tcoroutines: {},\\r\\n\\r\\n\\taddWaitingCoroutine: function( resolve, event )\\r\\n\\t{\\r\\n\\t\\tevent = event || \\\"render\\\";\\r\\n\\t\\tvar coroutines = this.coroutines[ event ];\\r\\n\\t\\tif(!coroutines)\\r\\n\\t\\t\\tcoroutines = this.coroutines[ event ] = [];\\r\\n\\t\\tcoroutines.push( resolve );\\r\\n\\t},\\r\\n\\r\\n\\ttriggerCoroutines: function( event, data )\\r\\n\\t{\\r\\n\\t\\tevent = event || \\\"render\\\";\\r\\n\\t\\tvar coroutines = this.coroutines[ event ];\\r\\n\\t\\tif(!coroutines)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tfor(var i = 0; i < coroutines.length; ++i)\\r\\n\\t\\t\\tLS.safeCall( coroutines[i], data ); //call resolves\\r\\n\\t\\tcoroutines.length = 0;\\r\\n\\t},\\r\\n\\r\\n\\tcreateCoroutine: function( event )\\r\\n\\t{\\r\\n\\t\\treturn new Promise(function(resolve){\\r\\n\\t\\t\\tLS.addWaitingCoroutine( resolve, event );\\r\\n\\t\\t});\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns a Promise that will be fulfilled once the time has passed\\r\\n\\t* @method sleep\\r\\n\\t* @param {Number} ms time in milliseconds\\r\\n\\t* @return {Promise} \\r\\n\\t*/\\r\\n\\tsleep: function(ms) {\\r\\n\\t return new Promise( function(resolve){ setTimeout(resolve, ms); });\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns a Promise that will be fulfilled when the next frame is rendered\\r\\n\\t* @method nextFrame\\r\\n\\t* @return {Promise} \\r\\n\\t*/\\r\\n\\tnextFrame: function( skip_request )\\r\\n\\t{\\r\\n\\t\\tif(!skip_request)\\r\\n\\t\\t\\tLS.GlobalScene.requestFrame();\\r\\n\\t\\treturn new Promise(function(resolve){\\r\\n\\t\\t\\tLS.addWaitingCoroutine( resolve, \\\"render\\\" );\\r\\n\\t\\t});\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Is a wrapper for callbacks that throws an LS \\\"exception\\\" in case something goes wrong (needed to catch the error from the system and editor)\\r\\n\\t* @method safeCall\\r\\n\\t* @param {function} callback\\r\\n\\t* @param {array} params\\r\\n\\t* @param {object} instance\\r\\n\\t*/\\r\\n\\tsafeCall: function(callback, params, instance)\\r\\n\\t{\\r\\n\\t\\tif(!LS.catch_exceptions)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(instance)\\r\\n\\t\\t\\t\\treturn callback.apply( instance, params );\\r\\n\\t\\t\\treturn callback( params );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\ttry\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn callback.apply( instance, params );\\r\\n\\t\\t}\\r\\n\\t\\tcatch (err)\\r\\n\\t\\t{\\r\\n\\t\\t\\tLEvent.trigger(LS,\\\"exception\\\",err);\\r\\n\\t\\t\\t//test this\\r\\n\\t\\t\\t//throw new Error( err.stack );\\r\\n\\t\\t\\tconsole.error( err.stack );\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Is a wrapper for setTimeout that throws an LS \\\"code_error\\\" in case something goes wrong (needed to catch the error from the system)\\r\\n\\t* @method setTimeout\\r\\n\\t* @param {function} callback\\r\\n\\t* @param {number} time in ms\\r\\n\\t* @param {number} timer_id\\r\\n\\t*/\\r\\n\\tsetTimeout: function(callback, time)\\r\\n\\t{\\r\\n\\t\\tif(!LS.catch_exceptions)\\r\\n\\t\\t\\treturn setTimeout( callback,time );\\r\\n\\r\\n\\t\\ttry\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn setTimeout( callback,time );\\r\\n\\t\\t}\\r\\n\\t\\tcatch (err)\\r\\n\\t\\t{\\r\\n\\t\\t\\tLEvent.trigger(LS,\\\"exception\\\",err);\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Is a wrapper for setInterval that throws an LS \\\"code_error\\\" in case something goes wrong (needed to catch the error from the system)\\r\\n\\t* @method setInterval\\r\\n\\t* @param {function} callback\\r\\n\\t* @param {number} time in ms\\r\\n\\t* @param {number} timer_id\\r\\n\\t*/\\r\\n\\tsetInterval: function(callback, time)\\r\\n\\t{\\r\\n\\t\\tif(!LS.catch_exceptions)\\r\\n\\t\\t\\treturn setInterval( callback,time );\\r\\n\\r\\n\\t\\ttry\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn setInterval( callback,time );\\r\\n\\t\\t}\\r\\n\\t\\tcatch (err)\\r\\n\\t\\t{\\r\\n\\t\\t\\tLEvent.trigger(LS,\\\"exception\\\",err);\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* copy the properties (methods and properties) of origin class into target class\\r\\n\\t* @method extendClass\\r\\n\\t* @param {Class} target\\r\\n\\t* @param {Class} origin\\r\\n\\t*/\\r\\n\\textendClass: function( target, origin ) {\\r\\n\\t\\tfor(var i in origin) //copy class properties\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(target.hasOwnProperty(i))\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\ttarget[i] = origin[i];\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(origin.prototype) //copy prototype properties\\r\\n\\t\\t\\tfor(var i in origin.prototype) //only enumerables\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(!origin.prototype.hasOwnProperty(i)) \\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tif(target.prototype.hasOwnProperty(i)) //avoid overwritting existing ones\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\t//copy getters \\r\\n\\t\\t\\t\\tif(origin.prototype.__lookupGetter__(i))\\r\\n\\t\\t\\t\\t\\ttarget.prototype.__defineGetter__(i, origin.prototype.__lookupGetter__(i));\\r\\n\\t\\t\\t\\telse \\r\\n\\t\\t\\t\\t\\ttarget.prototype[i] = origin.prototype[i];\\r\\n\\r\\n\\t\\t\\t\\t//and setters\\r\\n\\t\\t\\t\\tif(origin.prototype.__lookupSetter__(i))\\r\\n\\t\\t\\t\\t\\ttarget.prototype.__defineSetter__(i, origin.prototype.__lookupSetter__(i));\\r\\n\\t\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Clones an object (no matter where the object came from)\\r\\n\\t* - It skip attributes starting with \\\"_\\\" or \\\"jQuery\\\" or functions\\r\\n\\t* - it tryes to see which is the best copy to perform\\r\\n\\t* - to the rest it applies JSON.parse( JSON.stringify ( obj ) )\\r\\n\\t* - use it carefully\\r\\n\\t* @method cloneObject\\r\\n\\t* @param {Object} object the object to clone\\r\\n\\t* @param {Object} target=null optional, the destination object\\r\\n\\t* @param {bool} recursive=false optional, if you want to encode objects recursively\\r\\n\\t* @param {bool} only_existing=false optional, only assign to methods existing in the target object\\r\\n\\t* @param {bool} encode_objets=false optional, if a special object is found, encode it as [\\\"@ENC\\\",node,object]\\r\\n\\t* @return {Object} returns the cloned object (target if it is specified)\\r\\n\\t*/\\r\\n\\tcloneObject: function( object, target, recursive, only_existing, encode_objects )\\r\\n\\t{\\r\\n\\t\\tif(object === undefined)\\r\\n\\t\\t\\treturn undefined;\\r\\n\\t\\tif(object === null)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\t//base type\\r\\n\\t\\tswitch( object.constructor )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase String:\\r\\n\\t\\t\\tcase Number:\\r\\n\\t\\t\\tcase Boolean:\\r\\n\\t\\t\\t\\treturn object;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//typed array\\r\\n\\t\\tif( object.constructor.BYTES_PER_ELEMENT )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!target)\\r\\n\\t\\t\\t\\treturn new object.constructor( object );\\r\\n\\t\\t\\tif(target.set)\\r\\n\\t\\t\\t\\ttarget.set(object);\\r\\n\\t\\t\\telse if(target.construtor === Array)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var i = 0; i < object.length; ++i)\\r\\n\\t\\t\\t\\t\\ttarget[i] = object[i];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthrow(\\\"cloneObject: target has no set method\\\");\\r\\n\\t\\t\\treturn target;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar o = target;\\r\\n\\t\\tif(o === undefined || o === null)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(object.constructor === Array)\\r\\n\\t\\t\\t\\to = [];\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\to = {};\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//copy every property of this object\\r\\n\\t\\tfor(var i in object)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(i[0] == \\\"@\\\" || i[0] == \\\"_\\\" || i.substr(0,6) == \\\"jQuery\\\") //skip vars with _ (they are private) or '@' (they are definitions)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(only_existing && !target.hasOwnProperty(i) && !target.__proto__.hasOwnProperty(i) ) //target[i] === undefined)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//if(o.constructor === Array) //not necessary\\r\\n\\t\\t\\t//\\ti = parseInt(i);\\r\\n\\r\\n\\t\\t\\tvar v = object[i];\\r\\n\\t\\t\\tif(v == null)\\r\\n\\t\\t\\t\\to[i] = null;\\t\\t\\t\\r\\n\\t\\t\\telse if ( isFunction(v) ) //&& Object.getOwnPropertyDescriptor(object, i) && Object.getOwnPropertyDescriptor(object, i).get )\\r\\n\\t\\t\\t\\tcontinue;//o[i] = v;\\r\\n\\t\\t\\telse if (v.constructor === File ) \\r\\n\\t\\t\\t\\to[i] = null;\\r\\n\\t\\t\\telse if (v.constructor === Number || v.constructor === String || v.constructor === Boolean ) //elemental types\\r\\n\\t\\t\\t\\to[i] = v;\\r\\n\\t\\t\\telse if( v.buffer && v.byteLength && v.buffer.constructor === ArrayBuffer ) //typed arrays are ugly when serialized\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(o[i] && v && only_existing) \\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(o[i].length == v.length) //typed arrays force to fit in the same container\\r\\n\\t\\t\\t\\t\\t\\to[i].set( v );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\to[i] = new v.constructor(v); //clone typed array\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if ( v.constructor === Array ) //clone regular array (container and content!)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//not safe to use concat or slice(0) because it doesnt clone content, only container\\r\\n\\t\\t\\t\\tif( o[i] && o[i].set && o[i].length >= v.length ) //reuse old container\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\to[i].set(v);\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif( encode_objects && target && v[0] == \\\"@ENC\\\" ) //encoded object (component, node...)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar decoded_obj = LS.decodeObject(v);\\r\\n\\t\\t\\t\\t\\to[i] = decoded_obj;\\r\\n\\t\\t\\t\\t\\tif(!decoded_obj) //object not found\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tif( LS._pending_encoded_objects )\\r\\n\\t\\t\\t\\t\\t\\t\\tLS._pending_encoded_objects.push([o,i,v]);\\r\\n\\t\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\t\\tconsole.warn( \\\"Object UID referencing object not found in the scene:\\\", v[2] );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\to[i] = LS.cloneObject( v ); \\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse //Objects: \\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( v.constructor.is_resource )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.error(\\\"Resources cannot be saved as a property of a component nor script, they must be saved individually as files in the file system. If assigning them to a component/script use private variables (name start with underscore) to avoid being serialized.\\\");\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif( encode_objects && !target )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\to[i] = LS.encodeObject(v);\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif( v.constructor !== Object && !target && !v.toJSON )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"Cannot clone internal classes:\\\", LS.getObjectClassName( v ),\\\" When serializing an object I found a var with a class that doesnt support serialization. If this var shouldnt be serialized start the name with underscore.'\\\");\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif( v.toJSON )\\r\\n\\t\\t\\t\\t\\to[i] = v.toJSON();\\r\\n\\t\\t\\t\\telse if( recursive )\\r\\n\\t\\t\\t\\t\\to[i] = LS.cloneObject( v, null, true );\\r\\n\\t\\t\\t\\telse {\\r\\n\\t\\t\\t\\t\\tif(v.constructor !== Object && LS.Classes[ LS.getObjectClassName(v) ])\\r\\n\\t\\t\\t\\t\\t\\tconsole.warn(\\\"Cannot clone internal classes:\\\", LS.getObjectClassName(v),\\\" When serializing an object I found a var with a class that doesnt support serialization. If this var shouldnt be serialized start the name with underscore.'\\\" );\\r\\n\\r\\n\\t\\t\\t\\t\\tif(LS.catch_exceptions)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\ttry\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t//prevent circular recursions //slow but safe\\r\\n\\t\\t\\t\\t\\t\\t\\to[i] = JSON.parse( JSON.stringify(v) );\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tconsole.error(err);\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse //slow but safe\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\to[i] = JSON.parse( JSON.stringify(v) );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\treturn o;\\r\\n\\t},\\r\\n\\r\\n\\tencodeObject: function( obj )\\r\\n\\t{\\r\\n\\t\\tif( !obj || obj.constructor === Number || obj.constructor === String || obj.constructor === Boolean || obj.constructor === Object ) //regular objects\\r\\n\\t\\t\\treturn obj;\\r\\n\\t\\tif( obj.constructor.is_component && obj._root) //in case the value of this property is an actual component in the scene\\r\\n\\t\\t\\treturn [ \\\"@ENC\\\", LS.TYPES.COMPONENT, obj.getLocator(), LS.getObjectClassName( obj ) ];\\r\\n\\t\\tif( obj.constructor == LS.SceneNode && obj._in_tree) //in case the value of this property is an actual node in the scene\\r\\n\\t\\t\\treturn [ \\\"@ENC\\\", LS.TYPES.SCENENODE, obj.uid ];\\r\\n\\t\\tif( obj.constructor == LS.Scene)\\r\\n\\t\\t\\treturn [ \\\"@ENC\\\", LS.TYPES.SCENE, obj.fullpath ]; //weird case\\r\\n\\t\\tif( obj.serialize || obj.toJSON )\\r\\n\\t\\t{\\r\\n\\t\\t\\t//return obj.serialize ? obj.serialize() : obj.toJSON(); //why not this?\\r\\n\\t\\t\\treturn [ \\\"@ENC\\\", LS.TYPES.OBJECT, obj.serialize ? obj.serialize() : obj.toJSON(), LS.getObjectClassName( obj ) ];\\r\\n\\t\\t}\\r\\n\\t\\tconsole.warn(\\\"Cannot clone internal classes:\\\", LS.getObjectClassName( obj ),\\\" When serializing an object I found a property with a class that doesnt support serialization. If this property shouldn't be serialized start the name with underscore.'\\\");\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\tdecodeObject: function( data )\\r\\n\\t{\\r\\n\\t\\tif(!data || data.constructor !== Array || data[0] != \\\"@ENC\\\" )\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tswitch( data[1] )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase LS.TYPES.COMPONENT: \\r\\n\\t\\t\\tcase \\\"node\\\": //legacy\\r\\n\\t\\t\\tcase LS.TYPES.SCENENODE: \\r\\n\\t\\t\\t\\tvar obj = LSQ.get( data[2] );\\r\\n\\t\\t\\t\\tif( obj )\\r\\n\\t\\t\\t\\t\\treturn obj;\\r\\n\\t\\t\\t\\treturn null;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t//return break;\\r\\n\\t\\t\\tcase LS.TYPES.SCENE: return null; break; //weird case\\r\\n\\t\\t\\tcase LS.TYPES.OBJECT: \\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\tif( !data[2] || !data[2].object_class )\\r\\n\\t\\t\\t\\t\\treturn null;\\r\\n\\t\\t\\t\\tvar ctor = LS.Classes[ data[2].object_class ];\\r\\n\\t\\t\\t\\tif(!ctor)\\r\\n\\t\\t\\t\\t\\treturn null;\\r\\n\\t\\t\\t\\tvar v = new ctor();\\r\\n\\t\\t\\t\\tv.configure(data[2]);\\r\\n\\t\\t\\t\\treturn v;\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\tresolvePendingEncodedObjects: function()\\r\\n\\t{\\r\\n\\t\\tif(!LS._pending_encoded_objects)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"no pending enconded objects\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tfor(var i = 0; i < LS._pending_encoded_objects.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pending = LS._pending_encoded_objects[i];\\r\\n\\t\\t\\tvar decoded_object = LS.decodeObject(pending[2]);\\r\\n\\t\\t\\tif(decoded_object)\\r\\n\\t\\t\\t\\tpending[0][ pending[1] ] = decoded_object;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Decoded object not found when configuring from JSON\\\");\\r\\n\\t\\t}\\r\\n\\t\\tLS._pending_encoded_objects = null;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Clears all the uids inside this object and children (it also works with serialized object)\\r\\n\\t* @method clearUIds\\r\\n\\t* @param {Object} root could be a node or an object from a node serialization\\r\\n\\t*/\\r\\n\\tclearUIds: function( root, uids_removed )\\r\\n\\t{\\r\\n\\t\\tuids_removed = uids_removed || {};\\r\\n\\r\\n\\t\\tif(root.uid)\\r\\n\\t\\t{\\r\\n\\t\\t\\tuids_removed[ root.uid ] = root;\\r\\n\\t\\t\\tdelete root.uid;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//remove for embeded materials\\r\\n\\t\\tif(root.material && root.material.uid)\\r\\n\\t\\t{\\r\\n\\t\\t\\tuids_removed[ root.material.uid ] = root.material;\\r\\n\\t\\t\\tdelete root.material.uid;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar components = root.components;\\r\\n\\t\\tif(!components && root.getComponents)\\r\\n\\t\\t\\tcomponents = root.getComponents();\\r\\n\\r\\n\\t\\tif(!components)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(components)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i in components)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar comp = components[i];\\r\\n\\t\\t\\t\\tif(comp[1].uid)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tuids_removed[ comp[1].uid ] = comp[1];\\r\\n\\t\\t\\t\\t\\tdelete comp[1].uid;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif(comp[1]._uid)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tuids_removed[ comp[1]._uid ] = comp[1];\\r\\n\\t\\t\\t\\t\\tdelete comp[1]._uid;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar children = root.children;\\r\\n\\t\\tif(!children && root.getChildren)\\r\\n\\t\\t\\tchildren = root.getChildren();\\r\\n\\r\\n\\t\\tif(!children)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tfor(var i in children)\\r\\n\\t\\t\\tLS.clearUIds( children[i], uids_removed );\\r\\n\\r\\n\\t\\treturn uids_removed;\\r\\n\\t},\\r\\n\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns an object class name (uses the constructor toString)\\r\\n\\t* @method getObjectClassName\\r\\n\\t* @param {Object} the object to see the class name\\r\\n\\t* @return {String} returns the string with the name\\r\\n\\t*/\\r\\n\\tgetObjectClassName: function(obj)\\r\\n\\t{\\r\\n\\t\\tif (!obj)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(obj.constructor.fullname) //this is to overwrite the common name \\\"Prefab\\\" for a global name \\\"LS.Prefab\\\"\\r\\n\\t\\t\\treturn obj.constructor.fullname;\\r\\n\\r\\n\\t\\tif(obj.constructor.name)\\r\\n\\t\\t\\treturn obj.constructor.name;\\r\\n\\r\\n\\t\\tvar arr = obj.constructor.toString().match(\\r\\n\\t\\t\\t/function\\\\s*(\\\\w+)/);\\r\\n\\r\\n\\t\\tif (arr && arr.length == 2) {\\r\\n\\t\\t\\treturn arr[1];\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns an string with the class name\\r\\n\\t* @method getClassName\\r\\n\\t* @param {Object} class object\\r\\n\\t* @return {String} returns the string with the name\\r\\n\\t*/\\r\\n\\tgetClassName: function(obj)\\r\\n\\t{\\r\\n\\t\\tif (!obj)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//from function info, but not standard\\r\\n\\t\\tif(obj.name)\\r\\n\\t\\t\\treturn obj.name;\\r\\n\\r\\n\\t\\t//from sourcecode\\r\\n\\t\\tif(obj.toString) {\\r\\n\\t\\t\\tvar arr = obj.toString().match(\\r\\n\\t\\t\\t\\t/function\\\\s*(\\\\w+)/);\\r\\n\\t\\t\\tif (arr && arr.length == 2) {\\r\\n\\t\\t\\t\\treturn arr[1];\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the public properties of one object and the type (not the values)\\r\\n\\t* @method getObjectProperties\\r\\n\\t* @param {Object} object\\r\\n\\t* @return {Object} returns object with attribute name and its type\\r\\n\\t*/\\r\\n\\t//TODO: merge this with the locator stuff\\r\\n\\tgetObjectProperties: function( object )\\r\\n\\t{\\r\\n\\t\\tif(object.getPropertiesInfo)\\r\\n\\t\\t\\treturn object.getPropertiesInfo();\\r\\n\\t\\tvar class_object = object.constructor;\\r\\n\\t\\tif(class_object.properties)\\r\\n\\t\\t\\treturn class_object.properties;\\r\\n\\r\\n\\t\\tvar o = {};\\r\\n\\t\\tfor(var i in object)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//ignore some\\r\\n\\t\\t\\tif(i[0] == \\\"_\\\" || i[0] == \\\"@\\\" || i.substr(0,6) == \\\"jQuery\\\") //skip vars with _ (they are private)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(class_object != Object)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar hint = class_object[\\\"@\\\"+i];\\r\\n\\t\\t\\t\\tif(hint && hint.type)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\to[i] = hint.type;\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar v = object[i];\\r\\n\\t\\t\\tif(v == null)\\r\\n\\t\\t\\t\\to[i] = null;\\r\\n\\t\\t\\telse if ( isFunction(v) )//&& Object.getOwnPropertyDescriptor(object, i) && Object.getOwnPropertyDescriptor(object, i).get )\\r\\n\\t\\t\\t\\tcontinue; //o[i] = v;\\r\\n\\t\\t\\telse if ( v.constructor === Boolean )\\r\\n\\t\\t\\t\\to[i] = LS.TYPES.BOOLEAN;\\r\\n\\t\\t\\telse if ( v.constructor === Number )\\r\\n\\t\\t\\t\\to[i] = LS.TYPES.NUMBER;\\r\\n\\t\\t\\telse if ( v.constructor === String )\\r\\n\\t\\t\\t\\to[i] = LS.TYPES.STRING;\\r\\n\\t\\t\\telse if ( v.buffer && v.buffer.constructor === ArrayBuffer ) //typed array\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(v.length == 2)\\r\\n\\t\\t\\t\\t\\to[i] = LS.TYPES.VEC2;\\r\\n\\t\\t\\t\\telse if(v.length == 3)\\r\\n\\t\\t\\t\\t\\to[i] = LS.TYPES.VEC3;\\r\\n\\t\\t\\t\\telse if(v.length == 4)\\r\\n\\t\\t\\t\\t\\to[i] = LS.TYPES.VEC4;\\r\\n\\t\\t\\t\\telse if(v.length == 9)\\r\\n\\t\\t\\t\\t\\to[i] = LS.TYPES.MAT3;\\r\\n\\t\\t\\t\\telse if(v.length == 16)\\r\\n\\t\\t\\t\\t\\to[i] = LS.TYPES.MAT4;\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\to[i] = 0;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\to[i] = 0;\\r\\n\\t\\t}\\r\\n\\t\\treturn o;\\r\\n\\t},\\r\\n\\r\\n\\t//TODO: merge this with the locator stuff\\r\\n\\tsetObjectProperty: function( obj, name, value )\\r\\n\\t{\\r\\n\\t\\tif(obj.setProperty)\\r\\n\\t\\t\\treturn obj.setProperty(name, value);\\r\\n\\t\\tobj[ name ] = value; //clone�?\\r\\n\\t\\tif(obj.onPropertyChanged)\\r\\n\\t\\t\\tobj.onPropertyChanged( name, value );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Register a Material class so it is listed when searching for new materials to attach\\r\\n\\t*\\r\\n\\t* @method registerMaterialClass\\r\\n\\t* @param {ComponentClass} comp component class to register\\r\\n\\t*/\\r\\n\\tregisterMaterialClass: function( material_class )\\r\\n\\t{ \\r\\n\\t\\tvar class_name = LS.getClassName( material_class );\\r\\n\\r\\n\\t\\t//register\\r\\n\\t\\tthis.MaterialClasses[ class_name ] = material_class;\\r\\n\\t\\tthis.Classes[ class_name ] = material_class;\\r\\n\\r\\n\\t\\t//add extra material methods\\r\\n\\t\\tLS.extendClass( material_class, Material );\\r\\n\\r\\n\\t\\t//event\\r\\n\\t\\tLEvent.trigger( LS, \\\"materialclass_registered\\\", material_class );\\r\\n\\t\\tmaterial_class.resource_type = \\\"Material\\\";\\r\\n\\t\\tmaterial_class.is_material = true;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns an script context using the script name (not the node name), usefull to pass data between scripts.\\r\\n\\t*\\r\\n\\t* @method getScript\\r\\n\\t* @param {String} name the name of the script according to the Script component.\\r\\n\\t* @return {Object} the context of the script.\\r\\n\\t*/\\r\\n\\tgetScript: function( name )\\r\\n\\t{\\r\\n\\t\\tvar script = LS.Script.active_scripts[name];\\r\\n\\t\\tif(script)\\r\\n\\t\\t\\treturn script.context;\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\t//we do it in a function to make it more standard and traceable\\r\\n\\tdispatchCodeError: function( err, line, resource, extra )\\r\\n\\t{\\r\\n\\t\\tvar error_info = { error: err, line: line, resource: resource, extra: extra };\\r\\n\\t\\tconsole.error(error_info);\\r\\n\\t\\tLEvent.trigger( this, \\\"code_error\\\", error_info );\\r\\n\\t},\\r\\n\\r\\n\\tconvertToString: function( data )\\r\\n\\t{\\r\\n\\t\\tif(!data)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\tif(data.constructor === String)\\r\\n\\t\\t\\treturn data;\\r\\n\\t\\tif(data.constructor === Object)\\r\\n\\t\\t\\treturn JSON.stringify( object.serialize ? object.serialize() : object );\\r\\n\\t\\tif(data.constructor === ArrayBuffer)\\r\\n\\t\\t\\tdata = new Uint8Array(data);\\r\\n\\t\\treturn String.fromCharCode.apply(null,data);\\r\\n\\t},\\r\\n\\r\\n\\t\\r\\n\\t/**\\r\\n\\t* Checks if this locator belongs to a property inside a prefab, which could be tricky in some situations\\r\\n\\t* @method checkLocatorBelongsToPrefab\\r\\n\\t**/\\r\\n\\tcheckLocatorBelongsToPrefab: function( locator, root )\\r\\n\\t{\\r\\n\\t\\troot = root || LS.GlobalScene.root;\\r\\n\\t\\tvar property_path = locator.split(\\\"/\\\");\\r\\n\\r\\n\\t\\tif( !property_path.length )\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar node = LSQ.get( property_path[0], root );\\r\\n\\t\\tif(!node)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\treturn node.insidePrefab();\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Used to convert locators so instead of using UIDs for properties it uses Names\\r\\n\\t* This is used when you cannot rely on the UIDs because they belong to prefabs that could change them\\r\\n\\t* @method convertLocatorFromUIDsToName\\r\\n\\t* @param {String} locator string with info about a property (p.e. \\\"my_node/Transform/y\\\")\\r\\n\\t* @param {boolean} use_basename if you want to just use the node name, othewise it uses the fullname (name with path)\\r\\n\\t* @param {LS.SceneNode} root\\r\\n\\t* @return {String} the result name without UIDs\\r\\n\\t*/\\r\\n\\tconvertLocatorFromUIDsToName: function( locator, use_basename, root )\\r\\n\\t{\\r\\n\\t\\troot = root || LS.GlobalScene.root;\\r\\n\\t\\tvar property_path = locator.split(\\\"/\\\");\\r\\n\\r\\n\\t\\tif( !property_path.length )\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tif( property_path[0][0] !== LS._uid_prefix && ( property_path.length == 1 || property_path[1][0] !== LS._uid_prefix))\\r\\n\\t\\t\\treturn null; //is already using names\\r\\n\\r\\n\\t\\tvar node = LSQ.get( property_path[0], root );\\r\\n\\t\\tif(!node)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"getIDasName: node not found in LS.GlobalScene: \\\" + property_path[0] );\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!node.name)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"getIDasName: node without name?\\\");\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//what about the component?\\r\\n\\t\\tif( property_path.length > 1 && property_path[1][0] == LS._uid_prefix )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar comp = LS.GlobalScene.findComponentByUId( property_path[1] );\\r\\n\\t\\t\\tif(comp)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar comp_name = comp.constructor.name;\\r\\n\\t\\t\\t\\tif(comp_name == \\\"Script\\\" || comp_name == \\\"ScriptFromFile\\\")\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tcomp_name = comp.name;\\r\\n\\t\\t\\t\\t\\tif( comp_name == \\\"unnamed\\\" )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tconsole.error(\\\"converting component UIDs to component name, but property belongs to a Script without name. You must name the script to avoid errors.\\\");\\r\\n\\t\\t\\t\\t\\t\\tcomp_name = comp.constructor.name;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tproperty_path[1] = comp_name;\\t\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar result = property_path.concat();\\r\\n\\t\\tif(use_basename)\\r\\n\\t\\t\\tresult[0] = node.name;\\r\\n\\t\\telse\\r\\n\\t\\t\\tresult[0] = node.fullname;\\r\\n\\t\\treturn result.join(\\\"/\\\");\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* clears the global scene and the resources manager\\r\\n\\t*\\r\\n\\t* @method reset\\r\\n\\t*/\\r\\n\\treset: function()\\r\\n\\t{\\r\\n\\t\\tLS.GlobalScene.clear();\\r\\n\\t\\tLS.ResourcesManager.reset();\\r\\n\\t\\tLEvent.trigger( LS, \\\"reset\\\" );\\r\\n\\t},\\r\\n\\r\\n\\tlog: function()\\r\\n\\t{\\r\\n\\t\\tconsole.log.apply( console, arguments );\\r\\n\\t},\\r\\n\\r\\n\\tstringToValue: function( v )\\r\\n\\t{\\r\\n\\t\\tvar value = v;\\r\\n\\t\\ttry\\r\\n\\t\\t{\\r\\n\\t\\t\\tvalue = JSON.parse(v);\\r\\n\\t\\t}\\r\\n\\t\\tcatch (err)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error( \\\"Not a valid value: \\\" + v );\\r\\n\\t\\t}\\r\\n\\t\\treturn value;\\r\\n\\t},\\r\\n\\r\\n\\tisValueOfType: function( value, type )\\r\\n\\t{\\r\\n\\t\\tif(value === null || value === undefined)\\r\\n\\t\\t{\\r\\n\\t\\t\\tswitch (type)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase \\\"float\\\": \\r\\n\\t\\t\\t\\tcase \\\"sampler2D\\\": \\r\\n\\t\\t\\t\\tcase \\\"samplerCube\\\":\\r\\n\\t\\t\\t\\tcase LS.TYPES.NUMBER: \\r\\n\\t\\t\\t\\tcase LS.TYPES.VEC2: \\r\\n\\t\\t\\t\\tcase LS.TYPES.VEC3:\\r\\n\\t\\t\\t\\tcase LS.TYPES.VEC4:\\r\\n\\t\\t\\t\\tcase LS.TYPES.COLOR:\\r\\n\\t\\t\\t\\tcase LS.TYPES.COLOR4:\\r\\n\\t\\t\\t\\tcase \\\"mat3\\\": \\r\\n\\t\\t\\t\\tcase \\\"mat4\\\":\\r\\n\\t\\t\\t\\t\\treturn false;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tswitch (type)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//used to validate shaders\\r\\n\\t\\t\\tcase \\\"float\\\": \\r\\n\\t\\t\\tcase \\\"sampler2D\\\": \\r\\n\\t\\t\\tcase \\\"samplerCube\\\":\\r\\n\\t\\t\\tcase LS.TYPES.NUMBER: return isNumber(value);\\r\\n\\t\\t\\tcase LS.TYPES.VEC2: return value.length === 2;\\r\\n\\t\\t\\tcase LS.TYPES.VEC3: return value.length === 3;\\r\\n\\t\\t\\tcase LS.TYPES.VEC4: return value.length === 4;\\r\\n\\t\\t\\tcase LS.TYPES.COLOR: return value.length === 3;\\r\\n\\t\\t\\tcase LS.TYPES.COLOR4: return value.length === 4;\\r\\n\\t\\t\\tcase \\\"mat3\\\": return value.length === 9;\\r\\n\\t\\t\\tcase \\\"mat4\\\": return value.length === 16;\\r\\n\\t\\t}\\r\\n\\t\\treturn true;\\r\\n\\t},\\r\\n\\r\\n\\t//solution from http://stackoverflow.com/questions/979975/how-to-get-the-value-from-the-url-parameter\\r\\n\\tqueryString: function () {\\r\\n\\t // This function is anonymous, is executed immediately and \\r\\n\\t // the return value is assigned to QueryString!\\r\\n\\t var query_string = {};\\r\\n\\t var query = window.location.search.substring(1);\\r\\n\\t var vars = query.split(\\\"&\\\");\\r\\n\\t for (var i=0;i \\\"node|child/Collider/size\\\"\\r\\n*\\r\\n* @method shortify\\r\\n* @param {String} locator the locator string to shortify\\r\\n* @return {String} the locator using names instead of UIDs\\r\\n*/\\r\\nLSQ.shortify = function( locator, scene )\\r\\n{\\r\\n\\tif(!locator)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar t = locator.split(\\\"/\\\");\\r\\n\\tvar node = null;\\r\\n\\r\\n\\t//already short\\r\\n\\tif( t[0][0] != LS._uid_prefix )\\r\\n\\t\\treturn locator;\\r\\n\\r\\n\\tscene = scene || LS.GlobalScene;\\r\\n\\r\\n\\tnode = scene._nodes_by_uid[ t[0] ];\\r\\n\\tif(!node) //node not found\\r\\n\\t\\treturn locator;\\r\\n\\r\\n\\tt[0] = node.getPathName();\\r\\n\\tif(t[1])\\r\\n\\t{\\r\\n\\t\\tif( t[1][0] == LS._uid_prefix )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar compo = node.getComponentByUId(t[1]);\\r\\n\\t\\t\\tif(compo)\\r\\n\\t\\t\\t\\tt[1] = LS.getObjectClassName( compo );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn t.join(\\\"/\\\");\\r\\n}\\r\\n\\r\\n/**\\r\\n* Assigns a value using the getLocatorInfo object instead of searching it again\\r\\n* This is faster but if the locator points to a different object it wont work.\\r\\n*\\r\\n* @method setFromInfo\\r\\n* @param {Object} info information of a location (obtain using scene.getLocatorInfo\\r\\n* @param {*} value to assign\\r\\n*/\\r\\nLSQ.setFromInfo = function( info, value )\\r\\n{\\r\\n\\tif(!info || !info.target)\\r\\n\\t\\treturn;\\r\\n\\tvar target = info.target;\\r\\n\\tif( target.setPropertyValue )\\r\\n\\t\\tif( target.setPropertyValue( info.name, value ) === true )\\r\\n\\t\\t\\treturn target;\\r\\n\\tif( target[ info.name ] === undefined )\\r\\n\\t\\treturn;\\r\\n\\ttarget[ info.name ] = value;\\t\\r\\n}\\r\\n\\r\\nLSQ.getFromInfo = function( info )\\r\\n{\\r\\n\\tif(!info || !info.target)\\r\\n\\t\\treturn;\\r\\n\\tvar target = info.target;\\r\\n\\tvar varname = info.name;\\r\\n\\tvar v = undefined;\\r\\n\\tif( target.getPropertyValue )\\r\\n\\t\\tv = target.getPropertyValue( varname );\\r\\n\\tif( v === undefined && target[ varname ] === undefined )\\r\\n\\t\\treturn null;\\r\\n\\treturn v !== undefined ? v : target[ varname ];\\r\\n}\\r\\n\\r\\n//register resource classes\\r\\nif(global.GL)\\r\\n{\\r\\n\\tGL.Mesh.EXTENSION = \\\"wbin\\\";\\r\\n\\tGL.Texture.EXTENSION = \\\"png\\\";\\r\\n\\r\\n\\tLS.registerResourceClass( GL.Mesh );\\r\\n\\tLS.registerResourceClass( GL.Texture );\\r\\n\\r\\n\\tLS.Mesh = GL.Mesh;\\r\\n\\tLS.Texture = GL.Texture;\\r\\n\\tLS.Buffer = GL.Buffer;\\r\\n\\t//LS.Shader = GL.Shader; //this could be confussing since there is also ShaderBlocks etc in LiteScene\\r\\n}\\r\\n\\r\\n\\r\\nglobal.LSQ = LSQ;\\r\\n\\r\\n\\r\\n///@FILE:../src/defines.js\\r\\n///@INFO: BASE\\r\\nif(typeof(GL) == \\\"undefined\\\")\\r\\n\\tthrow(\\\"LiteSCENE requires to have litegl.js included before litescene.js\\\");\\r\\n\\r\\n//blending mode\\r\\nvar Blend = {\\r\\n\\tAUTOMATIC: 1,\\r\\n\\tNORMAL: 2,\\r\\n\\tALPHA: 3,\\r\\n\\tADD: 4,\\r\\n\\tMULTIPLY: 5,\\r\\n\\tSCREEN: 6,\\r\\n\\tCUSTOM: 7\\r\\n}\\r\\n\\r\\nLS.Blend = Blend;\\r\\n\\r\\nLS.BlendFunctions = {};\\r\\n\\r\\nLS.BlendFunctions[ Blend.AUTOMATIC ] = [GL.ONE, GL.ZERO];\\r\\nLS.BlendFunctions[ Blend.NORMAL ] = [GL.ONE, GL.ZERO];\\r\\nLS.BlendFunctions[ Blend.ALPHA ] = [GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA];\\r\\nLS.BlendFunctions[ Blend.ADD ] = [GL.SRC_ALPHA, GL.ONE];\\r\\nLS.BlendFunctions[ Blend.MULTIPLY ] = [GL.DST_COLOR, GL.ONE_MINUS_SRC_ALPHA];\\r\\nLS.BlendFunctions[ Blend.SCREEN ] =\\t[GL.SRC_ALPHA, GL.ONE];\\r\\nLS.BlendFunctions[ Blend.CUSTOM ] =\\t[GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA];\\r\\n\\r\\n//Used for interpolation and splines\\r\\nLS.NONE = 0;\\r\\nLS.LINEAR = 1;\\r\\nLS.TRIGONOMETRIC = 2;\\r\\nLS.CUBIC = 3;\\r\\nLS.SPLINE = 4;\\r\\nLS.BEZIER = 5;\\r\\nLS.HERMITE = 6;\\r\\n\\r\\n//used to know the state of the application\\r\\nLS.STOPPED = 0;\\r\\nLS.PLAYING = 1; \\r\\nLS.PAUSED = 2;\\r\\nLS.LOADING = 3;\\r\\n\\r\\nLS.RUNNING = 1; //LEGACY\\r\\n\\r\\n//helpful consts\\r\\nLS.ZEROS = vec3.create();\\r\\nLS.ZEROS4 = vec4.create();\\r\\nLS.ONES = vec3.fromValues(1,1,1);\\r\\nLS.TOP = vec3.fromValues(0,1,0);\\r\\nLS.BOTTOM = vec3.fromValues(0,-1,0);\\r\\nLS.RIGHT = vec3.fromValues(1,0,0);\\r\\nLS.LEFT = vec3.fromValues(-1,0,0);\\r\\nLS.FRONT = vec3.fromValues(0,0,-1);\\r\\nLS.BACK = vec3.fromValues(0,0,1);\\r\\nLS.IDENTITY = mat4.create();\\r\\nLS.QUAT_IDENTITY = quat.create();\\r\\nLS.WHITE = LS.ONES;\\r\\nLS.BLACK = LS.ZEROS;\\r\\n\\r\\nLS.POSX = 1;\\r\\nLS.POSY = 2;\\r\\nLS.POSZ = 3;\\r\\nLS.NEGX = 4;\\r\\nLS.NEGY = 5;\\r\\nLS.NEGZ = 6;\\r\\n\\r\\n//types\\r\\nLS.TYPES = {\\r\\n\\tBOOLEAN: \\\"boolean\\\",\\r\\n\\tNUMBER : \\\"number\\\",\\r\\n\\tSTRING : \\\"string\\\",\\r\\n\\tVEC2 : \\\"vec2\\\",\\r\\n\\tVEC3 : \\\"vec3\\\",\\r\\n\\tVEC4 : \\\"vec4\\\",\\r\\n\\tCOLOR : \\\"color\\\",\\r\\n\\tCOLOR4 : \\\"color4\\\",\\r\\n\\tEVENT : \\\"event\\\",\\r\\n\\tRESOURCE: \\\"resource\\\",\\r\\n\\tTEXTURE : \\\"texture\\\",\\r\\n\\tMESH: \\\"mesh\\\",\\r\\n\\tOBJECT: \\\"object\\\",\\r\\n\\tSCENE: \\\"scene\\\",\\r\\n\\tNODE: \\\"node\\\",\\r\\n\\tSCENENODE: \\\"node\\\",\\r\\n\\tSCENENODE_ID: \\\"node_id\\\",\\r\\n\\tCOMPONENT: \\\"component\\\",\\r\\n\\tCOMPONENT_ID: \\\"component_id\\\",\\r\\n\\tMATERIAL: \\\"material\\\",\\r\\n\\tANIMATION: \\\"animation\\\",\\r\\n\\tARRAY: \\\"array\\\",\\r\\n\\tQUAT : \\\"quat\\\",\\r\\n\\tTRANS10 : \\\"trans10\\\",\\r\\n\\tPOSITION : \\\"position\\\"\\r\\n};\\r\\n\\r\\nLS.TYPES_INDEX = {};\\r\\nvar index = 0;\\r\\nfor(var i in LS.TYPES)\\r\\n{\\r\\n\\tLS.TYPES_INDEX[ LS.TYPES[i] ] = index;\\r\\n\\tLS.TYPES_INDEX[ LS.TYPES[i].toUpperCase() ] = index;\\r\\n\\tindex++\\r\\n}\\r\\n\\r\\nLS.RESOURCE_TYPES = {};\\r\\nLS.RESOURCE_TYPES[ LS.TYPES.RESOURCE ] = true;\\r\\nLS.RESOURCE_TYPES[ LS.TYPES.TEXTURE ] = true;\\r\\nLS.RESOURCE_TYPES[ LS.TYPES.MESH ] = true;\\r\\nLS.RESOURCE_TYPES[ LS.TYPES.ANIMATION ] = true;\\r\\n//audio and video?\\r\\n\\r\\n\\r\\n//Events\\r\\nvar EVENT = LS.EVENT = {};\\r\\n\\r\\n/**\\r\\n* A Ray that contains an origin and a direction (it uses the Ray class from litegl, so to check documentation go to litegl doc\\r\\n* @class Ray\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n* @param {vec3} origin\\r\\n* @param {vec3} direction\\r\\n*/\\r\\nLS.Ray = GL.Ray;\\r\\n\\r\\n///@FILE:../src/network.js\\r\\n///@INFO: BASE\\r\\nvar Network = {\\r\\n\\r\\n\\tdefault_dataType: \\\"arraybuffer\\\",\\r\\n\\tprotocol: null,\\r\\n\\r\\n\\twithCredentials: false, //for CORS urls: not sure which one is the best for every case so I leave it configurable\\r\\n\\r\\n\\t/**\\r\\n\\t* A front-end for XMLHttpRequest so it is simpler and more cross-platform\\r\\n\\t*\\r\\n\\t* @method request\\r\\n\\t* @param {Object} request object with the fields for the request: \\r\\n *\\t\\t\\tdataType: result type {text,xml,json,binary,arraybuffer,image},\\r\\n\\t\\t\\t\\tdata: object with form fields,\\r\\n\\t\\t\\t\\tmethod: \\\"POST\\\",\\\"GET\\\",\\\"DELETE\\\",\\\"PUT\\\", if omited if will use post or get depending on the parameters,\\r\\n\\t\\t\\t\\tmimeType: to overwrite,\\r\\n\\t\\t\\t\\tuse_proxy: if true it will use LiteScene proxy if available\\r\\n\\t\\t\\t\\tcallbacks supported: {success, error, progress}\\r\\n\\t* @return {XMLHttpRequest} the XMLHttpRequest of the petition\\r\\n\\t*/\\r\\n\\trequest: function(request)\\r\\n\\t{\\r\\n\\t\\tif(typeof(request) === \\\"string\\\")\\r\\n\\t\\t\\tthrow(\\\"LS.Network.request expects object, not string. Use LS.Network.requestText or LS.Network.requestJSON\\\");\\r\\n\\r\\n\\t\\t//change protocol when working over https\\r\\n\\t\\tvar url = request.url;\\r\\n\\r\\n\\t\\t//apply proxy\\r\\n\\t\\tif(request.use_proxy)\\r\\n\\t\\t\\turl = LS.ResourcesManager.getFullURL(url);\\r\\n\\r\\n\\t\\tif( this.protocol === null )\\r\\n\\t\\t\\tthis.protocol = LS.ResourcesManager.getProtocol( location.href );\\r\\n\\t\\tvar protocol = LS.ResourcesManager.getProtocol( url );\\r\\n\\t\\tif( this.protocol == \\\"https\\\" && protocol && protocol != \\\"https\\\" )\\r\\n\\t\\t\\turl = \\\"https\\\" + url.substr( url.indexOf(\\\":\\\") );\\r\\n\\r\\n\\t\\t//update dataType\\r\\n\\t\\tvar dataType = request.dataType || this.default_dataType;\\r\\n\\t\\tif(dataType == \\\"json\\\") //parse it locally\\r\\n\\t\\t\\tdataType = \\\"text\\\";\\r\\n\\t\\telse if(dataType == \\\"xml\\\") //parse it locally\\r\\n\\t\\t\\tdataType = \\\"text\\\";\\r\\n\\t\\telse if (dataType == \\\"binary\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\t//request.mimeType = \\\"text/plain; charset=x-user-defined\\\";\\r\\n\\t\\t\\tdataType = \\\"arraybuffer\\\";\\r\\n\\t\\t\\trequest.mimeType = \\\"application/octet-stream\\\";\\r\\n\\t\\t}\\t\\r\\n\\t\\telse if(dataType == \\\"image\\\") //special case: images are loaded using regular images request\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar img = new Image();\\r\\n\\t\\t\\timg.onload = function() {\\r\\n\\t\\t\\t\\tif(request.success)\\r\\n\\t\\t\\t\\t\\trequest.success.call(this);\\r\\n\\t\\t\\t};\\r\\n\\t\\t\\timg.onerror = request.error;\\r\\n\\t\\t\\timg.src = url;\\r\\n\\t\\t\\treturn img;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//regular case, use AJAX call\\r\\n var xhr = new XMLHttpRequest();\\r\\n xhr.open( request.method || (request.data ? 'POST' : 'GET'), url, true);\\r\\n\\t\\txhr.withCredentials = this.withCredentials; //if true doesnt work\\r\\n\\t\\tif(request.withCredentials !== undefined)\\r\\n\\t\\t\\txhr.withCredentials = request.withCredentials;\\r\\n if(dataType)\\r\\n xhr.responseType = dataType;\\r\\n if (request.mimeType)\\r\\n xhr.overrideMimeType( request.mimeType );\\r\\n\\t\\tif(request.nocache)\\r\\n\\t\\t\\txhr.setRequestHeader('Cache-Control', 'no-cache');\\r\\n xhr.onload = function(load)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar response = this.response;\\r\\n\\t\\t\\tif(this.status && this.status != 200) //status 0 is when working with local files\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar err = \\\"Error \\\" + this.status;\\r\\n\\t\\t\\t\\tif(request.error)\\r\\n\\t\\t\\t\\t\\trequest.error(err);\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t}\\r\\n\\t\\r\\n\\t\\t\\t//parse input\\r\\n\\t\\t\\tif(request.dataType == \\\"json\\\") //chrome doesnt support json format\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttry\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tresponse = JSON.parse(response);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(request.error)\\r\\n\\t\\t\\t\\t\\t\\trequest.error(err);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(request.dataType == \\\"xml\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttry\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar xmlparser = new DOMParser();\\r\\n\\t\\t\\t\\t\\tresponse = xmlparser.parseFromString(response,\\\"text/xml\\\");\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(request.error)\\r\\n\\t\\t\\t\\t\\t\\trequest.error(err);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(request.dataType == \\\"blob\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tresponse.name = LS.ResourcesManager.getFilename(url);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//call callback\\r\\n\\t\\t\\tif(LS.catch_errors)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttry\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(request.success)\\r\\n\\t\\t\\t\\t\\t\\trequest.success.call(this, response, request.url );\\r\\n\\t\\t\\t\\t\\tLEvent.trigger(xhr,\\\"done\\\",response);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tLEvent.trigger(LS,\\\"code_error\\\",err);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(request.success)\\r\\n\\t\\t\\t\\t\\trequest.success.call(this, response, request.url );\\r\\n\\t\\t\\t\\tLEvent.trigger(xhr,\\\"done\\\",response);\\r\\n\\t\\t\\t}\\r\\n\\t\\t};\\r\\n xhr.onerror = function(err) {\\r\\n\\t\\t\\tif(request.error)\\r\\n\\t\\t\\t\\trequest.error(err);\\r\\n\\t\\t\\tLEvent.trigger(this,\\\"fail\\\", err);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( request.uploadProgress )\\r\\n\\t\\t{\\r\\n\\t\\t\\txhr.upload.addEventListener(\\\"progress\\\", function(e){\\r\\n\\t\\t\\t\\tvar progress = 0;\\r\\n\\t\\t\\t\\tif (e.lengthComputable)\\r\\n\\t\\t\\t\\t\\tprogress = e.loaded / e.total;\\r\\n\\t\\t\\t\\trequest.uploadProgress( e, progress );\\r\\n\\t\\t\\t}, false);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( request.progress )\\r\\n\\t\\t\\txhr.addEventListener( \\\"progress\\\", function(e){\\r\\n\\t\\t\\t\\tvar progress = 0;\\r\\n\\t\\t\\t\\tif (e.lengthComputable)\\r\\n\\t\\t\\t\\t\\tprogress = e.loaded / e.total;\\r\\n\\t\\t\\t\\trequest.progress( e, progress );\\r\\n\\t\\t\\t});\\r\\n\\r\\n xhr.send(request.data);\\r\\n\\r\\n\\t\\treturn xhr;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* retrieve a text file from url (you can bind LEvents to done and fail)\\r\\n\\t* @method requestText\\r\\n\\t* @param {string} url\\r\\n\\t* @param {object} params form params\\r\\n\\t* @param {function} callback( data )\\r\\n\\t*/\\r\\n\\trequestText: function( url, data, callback, callback_error )\\r\\n\\t{\\r\\n\\t\\tif(typeof(data) == \\\"function\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tcallback_error = callback;\\r\\n\\t\\t\\tcallback = data;\\r\\n\\t\\t\\tdata = null;\\r\\n\\t\\t}\\r\\n\\t\\treturn LS.Network.request({url:url, dataType:\\\"text\\\", success: callback, error: callback_error});\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* retrieve a JSON file from url (you can bind LEvents to done and fail)\\r\\n\\t* @method requestJSON\\r\\n\\t* @param {string} url\\r\\n\\t* @param {object} params form params\\r\\n\\t* @param {function} callback( json )\\r\\n\\t*/\\r\\n\\trequestJSON: function( url, data, callback, callback_error )\\r\\n\\t{\\r\\n\\t\\tif(typeof(data) == \\\"function\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tcallback_error = callback;\\r\\n\\t\\t\\tcallback = data;\\r\\n\\t\\t\\tdata = null;\\r\\n\\t\\t}\\r\\n\\t\\treturn LS.Network.request({url:url, data:data, dataType:\\\"json\\\", success: callback, error: callback_error });\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* retrieve a file from url (you can bind LEvents to done and fail) as a ArrayBuffer or Blob\\r\\n\\t* @method requestFile\\r\\n\\t* @param {string} url\\r\\n\\t* @param {object} params form params\\r\\n\\t* @param {function} callback( file )\\r\\n\\t*/\\r\\n\\trequestFile: function( url, form_data, callback, callback_error, as_blob )\\r\\n\\t{\\r\\n\\t\\tif(typeof(form_data) == \\\"function\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tcallback_error = callback;\\r\\n\\t\\t\\tcallback = form_data;\\r\\n\\t\\t\\tform_data = null;\\r\\n\\t\\t}\\r\\n\\t\\treturn LS.Network.request({ url:url, dataType: as_blob ? \\\"blob\\\" : \\\"binary\\\", data: form_data, success: callback, error: callback_error });\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Request script and inserts it in the DOM\\r\\n\\t* @method requestScript\\r\\n\\t* @param {String} url could be an array with urls to load in order\\r\\n\\t* @param {Function} on_complete\\r\\n\\t* @param {Function} on_error\\r\\n\\t* @param {Function} on_progress (if several files are required, on_progress is called after every file is added to the DOM)\\r\\n\\t**/\\r\\n\\trequestScript: function( url, on_complete, on_error, on_progress )\\r\\n\\t{\\r\\n\\t\\tif( !url )\\r\\n\\t\\t\\tthrow(\\\"No url\\\");\\r\\n\\r\\n\\t\\tif( LS._block_scripts )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"Safety: LS.block_scripts enabled, cannot request script\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( url.constructor === String )\\r\\n\\t\\t\\turl = [url];\\r\\n\\r\\n\\t\\tvar total = url.length;\\r\\n\\t\\tvar size = total;\\r\\n\\t\\tfor( var i in url )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar script = document.createElement('script');\\r\\n\\t\\t\\tscript.num = i;\\r\\n\\t\\t\\tscript.type = 'text/javascript';\\r\\n\\t\\t\\tscript.src = url[i];\\r\\n\\t\\t\\tscript.async = false;\\r\\n\\t\\t\\t//if( script.src.substr(0,5) == \\\"blob:\\\") //local scripts could contain utf-8\\r\\n\\t\\t\\t\\tscript.charset = \\\"UTF-8\\\";\\r\\n\\t\\t\\tscript.onload = inner_script_loaded;\\r\\n\\t\\t\\tif(on_error)\\r\\n\\t\\t\\t\\tscript.onerror = function(err) { \\r\\n\\t\\t\\t\\t\\ton_error(err, this.src, this.num );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\tdocument.getElementsByTagName('head')[0].appendChild( script );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfunction inner_script_loaded(e) { \\r\\n\\t\\t\\ttotal--;\\r\\n\\t\\t\\tif(total)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(on_progress)\\r\\n\\t\\t\\t\\t\\ton_progress(this.src, this.num);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse \\r\\n\\t\\t\\t\\tinner_check_pending();\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tfunction inner_check_pending()\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( LS.Network.pending_scripts.length ) //scripts included from scripts\\r\\n\\t\\t\\t\\tsetTimeout(inner_check_pending,1000); //wait one second\\r\\n\\t\\t\\telse if(on_complete)\\r\\n\\t\\t\\t\\ton_complete();\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t//used to import scripts from scripts\\r\\n\\tpending_scripts: [],\\r\\n\\r\\n\\timportScript: function( url, on_complete, on_error )\\r\\n\\t{\\r\\n\\t\\tvar script = document.createElement('script');\\r\\n\\t\\tscript.type = 'text/javascript';\\r\\n\\t\\tscript.src = url;\\r\\n\\t\\tscript.async = false;\\r\\n\\t\\tscript.charset = \\\"UTF-8\\\";\\r\\n\\t\\tscript.onload = function(e) { \\r\\n\\t\\t\\tvar index = LS.Network.pending_scripts.indexOf( this );\\r\\n\\t\\t\\tif(index != -1)\\r\\n\\t\\t\\t\\tLS.Network.pending_scripts.splice(index);\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\ton_complete();\\r\\n\\t\\t};\\r\\n\\t\\tscript.onerror = function(err) { \\r\\n\\t\\t\\tvar index = LS.Network.pending_scripts.indexOf( this );\\r\\n\\t\\t\\tif(index != -1)\\r\\n\\t\\t\\t\\tLS.Network.pending_scripts.splice(index);\\r\\n\\t\\t\\tif(on_error)\\r\\n\\t\\t\\t\\ton_error();\\r\\n\\t\\t}\\r\\n\\t\\tthis.pending_scripts.push( script );\\r\\n\\t\\tdocument.getElementsByTagName('head')[0].appendChild( script );\\r\\n\\t\\treturn script;\\r\\n\\t},\\r\\n\\r\\n\\trequestFont: function( name, url )\\r\\n\\t{\\r\\n\\t\\tif(!name || !url)\\r\\n\\t\\t\\tthrow(\\\"LS.Network.requestFont: Wrong font name or url\\\");\\r\\n\\r\\n\\t\\tvar fonts = this._loaded_fonts;\\r\\n\\t\\tif(!fonts)\\r\\n\\t\\t\\tfonts = this._loaded_fonts = {};\\r\\n\\r\\n\\t\\tif(fonts[name] == url)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tfonts[name] = url;\\r\\n\\r\\n\\t\\tvar style = document.getElementById(\\\"ls_fonts\\\");\\r\\n\\t\\tif(!style)\\r\\n\\t\\t{\\r\\n\\t\\t\\tstyle = document.createElement(\\\"style\\\");\\r\\n\\t\\t\\tstyle.id = \\\"ls_fonts\\\";\\r\\n\\t\\t\\tstyle.setAttribute(\\\"type\\\",\\\"text/css\\\");\\r\\n\\t\\t\\tdocument.head.appendChild(style);\\r\\n\\t\\t}\\r\\n\\t\\tvar str = \\\"\\\";\\r\\n\\t\\tfor(var i in fonts)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar url = fonts[i];\\r\\n\\t\\t\\tstr += \\\"@font-face {\\\\n\\\" +\\r\\n\\t\\t\\t\\t\\t\\\"\\\\tfont-family: \\\\\\\"\\\"+i+\\\"\\\\\\\";\\\\n\\\" + \\r\\n\\t\\t\\t\\t\\t\\\"\\\\tsrc: url('\\\"+url+\\\"');\\\\n\\\" + \\r\\n\\t\\t\\t\\\"}\\\\n\\\";\\r\\n\\t\\t}\\r\\n\\t\\tstyle.innerHTML = str;\\r\\n\\t}\\r\\n\\r\\n\\t//NOT TESTED: to load script asyncronously, not finished. similar to require.js\\r\\n\\t/*\\r\\n\\trequireScript: function(files, on_complete)\\r\\n\\t{\\r\\n\\t\\tif(typeof(files) == \\\"string\\\")\\r\\n\\t\\t\\tfiles = [files];\\r\\n\\r\\n\\t\\t//store for the callback\\r\\n\\t\\tvar last = files[ files.length - 1];\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!ResourcesManager._waiting_callbacks[ last ])\\r\\n\\t\\t\\t\\tResourcesManager._waiting_callbacks[ last ] = [on_complete];\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tResourcesManager._waiting_callbacks[ last ].push(on_complete);\\r\\n\\t\\t}\\r\\n\\t\\trequire_file(files);\\r\\n\\r\\n\\t\\tfunction require_file(files)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//avoid require twice a file\\r\\n\\t\\t\\tvar url = files.shift(1); \\r\\n\\t\\t\\twhile( ResourcesManager._required_files[url] && url )\\r\\n\\t\\t\\t\\turl = files.shift(1);\\r\\n\\r\\n\\t\\t\\tResourcesManager._required_files[url] = true;\\r\\n\\r\\n\\t\\t\\tLS.Network.request({\\r\\n\\t\\t\\t\\turl: url,\\r\\n\\t\\t\\t\\tsuccess: function(response)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\teval(response);\\r\\n\\t\\t\\t\\t\\tif( ResourcesManager._waiting_callbacks[ url ] )\\r\\n\\t\\t\\t\\t\\t\\tfor(var i in ResourcesManager._waiting_callbacks[ url ])\\r\\n\\t\\t\\t\\t\\t\\t\\tResourcesManager._waiting_callbacks[ url ][i]();\\r\\n\\t\\t\\t\\t\\trequire_file(files);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\t_required_files: {},\\r\\n\\t_waiting_callbacks: {}\\r\\n\\t*/\\r\\n};\\r\\n\\r\\nLS.Network = Network;\\r\\n///@FILE:../src/input.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* Input is a static class used to read the input state (keyboard, mouse, gamepad, etc)\\r\\n*\\r\\n* @class Input\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\n//help info:\\r\\n//mouse.mousey 0 is top\\r\\n//mouse.canvasy 0 is bottom\\r\\n//mouse.y is mousey\\r\\n\\r\\nvar Input = {\\r\\n\\tmapping: {\\r\\n\\r\\n\\t\\t//xbox\\r\\n\\t\\tA_BUTTON: 0,\\r\\n\\t\\tB_BUTTON: 1,\\r\\n\\t\\tX_BUTTON: 2,\\r\\n\\t\\tY_BUTTON: 3,\\r\\n\\t\\tLB_BUTTON: 4,\\r\\n\\t\\tRB_BUTTON: 5,\\r\\n\\t\\tBACK_BUTTON: 6,\\r\\n\\t\\tSTART_BUTTON: 7,\\r\\n\\t\\tLS_BUTTON: 8,\\r\\n\\t\\tRS_BUTTON: 9,\\r\\n\\r\\n\\t\\tLX: 0,\\r\\n\\t\\tLY: 1,\\r\\n\\t\\tRX: 2,\\r\\n\\t\\tRY: 3,\\r\\n\\t\\tTRIGGERS: 4,\\r\\n\\t\\tLEFT_TRIGGER: 4,\\r\\n\\t\\tRIGHT_TRIGGER: 5,\\r\\n\\r\\n\\t\\t//generic\\r\\n\\t\\tJUMP:0,\\r\\n\\t\\tFIRE:1,\\r\\n\\r\\n\\t\\t//mouse\\r\\n\\t\\tLEFT:0,\\r\\n\\t\\tMIDDLE:1,\\r\\n\\t\\tRIGHT:2\\r\\n\\t},\\r\\n\\r\\n\\tLEFT_MOUSE_BUTTON: 1,\\r\\n\\tMIDDLE_MOUSE_BUTTON: 2,\\r\\n\\tRIGHT_MOUSE_BUTTON: 3,\\r\\n\\r\\n\\tKeyboard: [],\\r\\n\\tKeyboard_previous: [],\\r\\n\\r\\n\\tMouse: {},\\r\\n\\tGamepads: [],\\r\\n\\r\\n\\t//used for GUI elements\\r\\n\\tlast_mouse: null,\\r\\n\\tlast_click: null,\\r\\n\\tcurrent_click: null,\\r\\n\\tcurrent_key: null,\\r\\n\\tkeys_buffer: [], //array of keys that have been pressed from the last frame\\r\\n\\r\\n\\t//_mouse_event_offset: [0,0],\\r\\n\\t_last_frame: -1, //internal\\r\\n\\r\\n\\tinit: function()\\r\\n\\t{\\r\\n\\t\\tthis.Keyboard = gl.keys;\\r\\n\\t\\tthis.Mouse = gl.mouse;\\r\\n\\t\\tthis.Gamepads = gl.getGamepads();\\r\\n\\t},\\r\\n\\r\\n\\treset: function()\\r\\n\\t{\\r\\n\\t\\tthis.Gamepads = gl.gamepads = []; //force reset so they send new events \\r\\n\\t},\\r\\n\\r\\n\\tupdate: function()\\r\\n\\t{\\r\\n\\t\\t//copy prev keys state\\r\\n\\t\\tfor(var i = 0, l = this.Keyboard.length; i < l; ++i)\\r\\n\\t\\t\\tthis.Keyboard_previous[i] = this.Keyboard[i];\\r\\n\\r\\n\\t\\t//copy prev mouse state (this is only necessary if the update is not called from litegl main loop)\\r\\n\\t\\tthis.Mouse.last_buttons = this.Mouse.buttons;\\r\\n\\r\\n\\t\\t//capture gamepads snapshot\\r\\n\\t\\tthis.Gamepads = gl.getGamepads();\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* returns true is the key is pressed now\\r\\n\\t*\\r\\n\\t* @method isKeyPressed\\r\\n\\t* @param {Number} key_code\\r\\n\\t* @return {boolean}\\r\\n\\t*/\\r\\n\\tisKeyPressed: function(key_code)\\r\\n\\t{\\r\\n\\t\\treturn !!this.Keyboard[ key_code ];\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* returns true is the key was pressed between previous frame and now\\r\\n\\t*\\r\\n\\t* @method wasKeyPressed\\r\\n\\t* @param {Number} key_code as in https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Browser_compatibility\\r\\n\\t* @return {boolean}\\r\\n\\t*/\\r\\n\\twasKeyPressed: function(key_code)\\r\\n\\t{\\r\\n\\t\\treturn this.Keyboard[ key_code ] && !this.Keyboard_previous[ key_code ];\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* returns true is the mouse button is pressed now\\r\\n\\t*\\r\\n\\t* @method isMouseButtonPressed\\r\\n\\t* @param {Number} button could be \\\"left\\\",\\\"middle\\\",\\\"right\\\" or GL.LEFT_MOUSE_BUTTON, GL.MIDDLE_MOUSE_BUTTON, GL.RIGHT_MOUSE_BUTTON\\r\\n\\t* @return {boolean}\\r\\n\\t*/\\r\\n\\tisMouseButtonPressed: function(button)\\r\\n\\t{\\r\\n\\t\\tvar num = 0;\\r\\n\\t\\tif(button && button.constructor === String)\\r\\n\\t\\t\\tnum = this.mapping[button];\\r\\n\\t\\telse\\r\\n\\t\\t\\tnum = button;\\r\\n\\t\\tif(button === undefined)\\r\\n\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\treturn this.Mouse.isButtonPressed(num);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* returns true is the mouse button was pressed between previous frame and now\\r\\n\\t*\\r\\n\\t* @method wasMouseButtonPressed\\r\\n\\t* @param {Number} button could be \\\"left\\\",\\\"middle\\\",\\\"right\\\" or GL.LEFT_MOUSE_BUTTON, GL.MIDDLE_MOUSE_BUTTON, GL.RIGHT_MOUSE_BUTTON\\r\\n\\t* @return {boolean}\\r\\n\\t*/\\r\\n\\twasMouseButtonPressed: function(button)\\r\\n\\t{\\r\\n\\t\\tvar num = 0;\\r\\n\\t\\tif(button && button.constructor === String)\\r\\n\\t\\t\\tnum = this.mapping[button];\\r\\n\\t\\telse\\r\\n\\t\\t\\tnum = button;\\r\\n\\t\\tif(button === undefined)\\r\\n\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\treturn this.Mouse.wasButtonPressed(num);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* locks the mouse (to use with first person view cameras) so when the mouse moves, the cameras moves\\r\\n\\t*\\r\\n\\t* @method lockMouse\\r\\n\\t* @param {Boolean} v if true, the camera is locked, otherwise unlocked\\r\\n\\t* @return {boolean}\\r\\n\\t*/\\r\\n\\tlockMouse: function(v)\\r\\n\\t{\\r\\n\\t\\tif(v)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.canvas.requestPointerLock();\\r\\n\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tdocument.exitPointerLock();\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* returns true is mouse is in pointer lock mode\\r\\n\\t*\\r\\n\\t* @method isMouseLocked\\r\\n\\t* @return {boolean}\\r\\n\\t*/\\r\\n\\tisMouseLocked: function()\\r\\n\\t{\\r\\n\\t\\treturn !!document.pointerLockElement;\\r\\n\\t},\\r\\n\\r\\n\\t//called from LS.Player when onmouse\\r\\n\\t//returns true if the event was blocked\\r\\n\\tonMouse: function(e)\\r\\n\\t{\\r\\n\\t\\tthis.last_mouse = e;\\r\\n\\r\\n\\t\\tif( this.isMouseLocked() )\\r\\n\\t\\t{\\r\\n\\t\\t\\te.canvasx = e.mousex = (gl.canvas.width * 0.5)|0;\\r\\n\\t\\t\\te.canvasy = e.mousey = (gl.canvas.height * 0.5)|0;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//mousey is from top\\r\\n\\t\\tthis.Mouse.x = this.Mouse.mousex = e.mousex;\\r\\n\\t\\tthis.Mouse.y = this.Mouse.mousey = e.mousey;\\r\\n\\t\\t//canvasy is from bottom\\r\\n\\t\\tthis.Mouse.canvasx = e.canvasx;\\r\\n\\t\\tthis.Mouse.canvasy = e.canvasy;\\r\\n\\r\\n\\t\\t//save it in case we need to know where was the last click\\r\\n\\t\\tif(e.type == \\\"mousedown\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.current_click = e;\\r\\n\\t\\t\\tLS.triggerCoroutines( \\\"click\\\", e );\\r\\n\\t\\t}\\r\\n\\t\\telse if(e.type == \\\"mouseup\\\")\\r\\n\\t\\t\\tthis.current_click = null;\\r\\n\\r\\n\\t\\t//we test if this event should be sent to the components or it was blocked by the GUI\\r\\n\\t\\treturn LS.GUI.testEventInBlockedArea(e);\\r\\n\\t},\\r\\n\\r\\n\\t//called from LS.Player when onkey\\r\\n\\tonKey: function(e)\\r\\n\\t{\\r\\n\\t\\tif(e.type == \\\"keydown\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.current_key = e;\\r\\n\\t\\t\\tif( LS.Renderer._frame != this._last_frame )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.keys_buffer.length = 0;\\r\\n\\t\\t\\t\\tLS.Renderer._frame = this._last_frame;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif( this.keys_buffer.length < 10 ) //safety first!\\r\\n\\t\\t\\t\\tthis.keys_buffer.push(e);\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.current_key = null;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* returns if the mouse is inside the rect defined by x,y, width,height\\r\\n\\t*\\r\\n\\t* @method isMouseInRect\\r\\n\\t* @param {Number} x x coordinate of the mouse in canvas coordinates \\r\\n\\t* @param {Number} y y coordinate of the mouse in canvas coordinates (0 is bottom)\\r\\n\\t* @param {Number} width rectangle width in pixels\\r\\n\\t* @param {Number} height rectangle height in pixels\\r\\n\\t* @param {boolean} flip [optional] if you want to flip the y coordinate\\r\\n\\t* @return {boolean}\\r\\n\\t*/\\r\\n\\tisMouseInRect: function( x, y, width, height, flip_y )\\r\\n\\t{\\r\\n\\t\\treturn this.Mouse.isInsideRect(x,y,width,height,flip_y);\\r\\n\\t},\\r\\n\\r\\n\\t//uses {x,y}, instead of mousex,mousey\\r\\n\\tisEventInRect: function( mouse, area, offset )\\r\\n\\t{\\r\\n\\t\\tvar x = mouse.mousex != null ? mouse.mousex : mouse.x;\\r\\n\\t\\tvar y = mouse.mousey != null ? mouse.mousey : mouse.y;\\r\\n\\t\\tif(offset)\\r\\n\\t\\t{\\r\\n\\t\\t\\tx -= offset[0];\\r\\n\\t\\t\\ty -= offset[1];\\r\\n\\t\\t}\\r\\n\\t\\treturn ( x >= area[0] && x < (area[0] + area[2]) && y >= area[1] && y < (area[1] + area[3]) );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the axis based on the gamepad or the keyboard cursors. Useful when you do now know if the player will use keyboard of gamepad\\r\\n\\t*\\r\\n\\t* @method getAxis\\r\\n\\t* @param {String} \\\"vertical\\\" or \\\"horizontal\\\"\\r\\n\\t* @return {Number} the value of the axis\\r\\n\\t*/\\r\\n\\tgetAxis: function( axis )\\r\\n\\t{\\r\\n\\t\\tif( axis == \\\"vertical\\\" )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.isKeyPressed( 38 )\\t|| this.isKeyPressed( \\\"W\\\" )) //up\\r\\n\\t\\t\\t\\treturn 1;\\r\\n\\t\\t\\tif( this.isKeyPressed( 40 )\\t|| this.isKeyPressed( \\\"S\\\" )) //down\\r\\n\\t\\t\\t\\treturn -1;\\r\\n\\t\\t}\\r\\n\\t\\telse if( axis == \\\"horizontal\\\" )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.isKeyPressed( 37 )\\t|| this.isKeyPressed( \\\"A\\\" )) //left\\r\\n\\t\\t\\t\\treturn -1;\\r\\n\\t\\t\\telse if( this.isKeyPressed( 39 ) || this.isKeyPressed( \\\"D\\\" )) //right\\r\\n\\t\\t\\t\\treturn 1;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar gamepad = this.Gamepads[0];\\r\\n\\t\\tif(gamepad)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(axis == \\\"horizontal\\\")\\r\\n\\t\\t\\t\\treturn gamepad.axes[0];\\r\\n\\t\\t\\telse if(axis == \\\"vertical\\\")\\r\\n\\t\\t\\t\\treturn gamepad.axes[1];\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn 0;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns a gamepad snapshot if it is connected\\r\\n\\t*\\r\\n\\t* @method getGamepad\\r\\n\\t* @param {Number} index the index of the gamepad\\r\\n\\t* @return {Object} gamepad snapshot with all the info\\r\\n\\t*/\\r\\n\\tgetGamepad: function(index)\\r\\n\\t{\\r\\n\\t\\tindex = index || 0;\\r\\n\\t\\treturn this.Gamepads[index];\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns a gamepad snapshot if it is connected\\r\\n\\t*\\r\\n\\t* @method getGamepadAxis\\r\\n\\t* @param {Number} index the index of the gamepad\\r\\n\\t* @param {String} name the name of the axis (also you could specify the number)\\r\\n\\t* @param {boolean} raw [optional] if you want the data unfiltered\\r\\n\\t* @return {Number} axis value from -1 to 1\\r\\n\\t*/\\r\\n\\tgetGamepadAxis: function(index, name, raw)\\r\\n\\t{\\r\\n\\t\\tvar gamepad = this.Gamepads[index];\\r\\n\\t\\tif(!gamepad)\\r\\n\\t\\t\\treturn 0;\\r\\n\\r\\n\\t\\tvar num = 0;\\r\\n\\t\\tif(name && name.constructor === String)\\r\\n\\t\\t\\tnum = this.mapping[name];\\r\\n\\t\\telse\\r\\n\\t\\t\\tnum = name;\\r\\n\\t\\tif(num === undefined)\\r\\n\\t\\t\\treturn 0;\\r\\n\\t\\tvar v = gamepad.axes[num];\\r\\n\\t\\tif(!raw && v > -0.1 && v < 0.1 ) //filter\\r\\n\\t\\t\\treturn 0;\\r\\n\\t\\treturn v;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns if the given button of the specified gamepad is pressed\\r\\n\\t*\\r\\n\\t* @method isGamepadButtonPressed\\r\\n\\t* @param {Number} index the index of the gamepad\\r\\n\\t* @param {String} name the name of the button \\\"A\\\",\\\"B\\\",\\\"X\\\",\\\"Y\\\",\\\"LB\\\",\\\"RB\\\",\\\"BACK\\\",\\\"START\\\",\\\"LS\\\",\\\"RS\\\" (also you could specify the number)\\r\\n\\t* @return {Boolean} if the button is pressed\\r\\n\\t*/\\r\\n\\tisGamepadButtonPressed: function(input, name)\\r\\n\\t{\\r\\n\\t\\tvar gamepad = this.Gamepads[input];\\r\\n\\t\\tif(!gamepad)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar num = 0;\\r\\n\\t\\tif(name && name.constructor === String)\\r\\n\\t\\t\\tnum = this.mapping[name];\\r\\n\\t\\telse\\r\\n\\t\\t\\tnum = name;\\r\\n\\t\\tif(num === undefined)\\r\\n\\t\\t\\treturn 0;\\r\\n\\t\\tvar button = gamepad.buttons[num];\\r\\n\\t\\treturn button && button.pressed;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns a Promise that will be fulfilled when the user clicks the screen\\r\\n\\t* @method mouseClick\\r\\n\\t* @return {Promise} \\r\\n\\t*/\\r\\n\\tmouseClick: function()\\r\\n\\t{\\r\\n\\t\\treturn new Promise(function(resolve){\\r\\n\\t\\t\\tLS.addWaitingCoroutine( resolve, \\\"click\\\" );\\r\\n\\t\\t});\\r\\n\\t}\\r\\n};\\r\\n\\r\\n\\r\\nObject.defineProperty( MouseEvent.prototype, \\\"getRay\\\", { value: function(){\\r\\n\\t\\t//get camera under position\\r\\n\\t\\tvar camera = LS.Renderer.getCameraAtPosition( this.mousex, this.mousey, LS.Renderer._visible_cameras );\\r\\n\\t\\tif(!camera)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t//get ray\\r\\n\\t\\treturn camera.getRay( this.mousex, this.mousey );\\r\\n\\t},\\r\\n\\tenumerable: false \\r\\n});\\r\\n\\r\\nLS.Input = Input;\\r\\n///@FILE:../src/gui.js\\r\\n\\r\\n/**\\r\\n* GUI is a static class used to create two kinds of GUIs: HTML GUIs on top of the 3D Canvas (in a safe way) or Immediate GUI using a Canvas2D (fast gui)\\r\\n* For HTML GUIs check the getHTMLRoot function.\\r\\n* For Immediate GUIs check the Box,Button,Toggle,Textfield,HorizontalSlider,VerticalSlider and Toolbar.\\r\\n* To change colors of the immediate GUI check the LS.GUI.GUIStyle\\r\\n*\\r\\n* @class GUI\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\nvar GUI = {\\r\\n\\r\\n\\t_root: null, //root DOM element containing the GUI\\r\\n\\t_allow_change_cursor: true,\\r\\n\\t_is_on_top_of_immediate_widget: false,\\r\\n\\r\\n\\tdefaultGUIStyle: {\\r\\n\\t\\tfont: \\\"Arial\\\",\\r\\n\\t\\tcolor: \\\"#FFF\\\",\\r\\n\\t\\tcolorTextOver: \\\"#FFF\\\",\\r\\n\\t\\tbackgroundColor: \\\"#333\\\",\\r\\n\\t\\tbackgroundColorOver: \\\"#AAA\\\",\\r\\n\\t\\tselected: \\\"#AAF\\\",\\r\\n\\t\\tunselected: \\\"#AAA\\\",\\r\\n\\t\\toutline: \\\"#000\\\",\\r\\n\\t\\tmargin: 0.2\\r\\n\\t},\\r\\n\\r\\n\\tGUIStyle: null,\\r\\n\\t_style_stack: [],\\r\\n\\r\\n\\t_offset: [0,0],\\r\\n\\r\\n\\t_gui_areas: {\\r\\n\\t\\tdata: new Float32Array(1024),\\r\\n\\t\\toffset: 0\\r\\n\\t},\\r\\n\\r\\n\\t_ctx: null, //\\r\\n\\r\\n\\tpressed_enter: false,\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the DOM element responsible for the HTML GUI of the app. This is helpful because this GUI will be automatically removed if the app finishes.\\r\\n\\t* Any HTML must be attached to this element, otherwise it may have problems with the editor.\\r\\n\\t*\\r\\n\\t* @method getHTMLRoot\\r\\n\\t* @return {HTMLElement} \\r\\n\\t*/\\r\\n\\tgetHTMLRoot: function()\\r\\n\\t{\\r\\n\\t\\tif( this._root )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!this._root.parentNode && gl.canvas.parentNode)\\r\\n\\t\\t\\t\\tgl.canvas.parentNode.appendChild( gui );\\r\\n\\t\\t\\treturn this._root;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(LS.GlobalScene._state != LS.PLAYING)\\r\\n\\t\\t\\tconsole.warn(\\\"GUI element created before the scene is playing will be deleted once the app starts. Only create the GUI elements from onStart or after, otherwise the GUI elements will be lost.\\\");\\r\\n\\r\\n\\t\\tvar gui = document.createElement(\\\"div\\\");\\r\\n\\t\\tgui.className = \\\"litescene-gui\\\";\\r\\n\\t\\tgui.style.position = \\\"absolute\\\";\\r\\n\\t\\tgui.style.top = \\\"0\\\";\\r\\n\\t\\tgui.style.left = \\\"0\\\";\\r\\n\\r\\n\\t\\t//normalize\\r\\n\\t\\tgui.style.color = \\\"#999\\\";\\r\\n\\t\\tgui.style.font = \\\"20px Arial\\\";\\r\\n\\r\\n\\t\\t//make it fullsize\\r\\n\\t\\tgui.style.width = \\\"100%\\\";\\r\\n\\t\\tgui.style.height = \\\"100%\\\";\\r\\n\\t\\tgui.style.overflow = \\\"hidden\\\";\\r\\n\\t\\tgui.style.pointerEvents = \\\"none\\\";\\r\\n\\r\\n\\t\\tif(!this._style)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar style = this._style = document.createElement(\\\"style\\\");\\r\\n\\t\\t\\tstyle.appendChild(document.createTextNode(\\\"\\\"));\\r\\n\\t\\t\\tdocument.head.appendChild(style);\\r\\n\\t\\t\\tstyle.sheet.insertRule(\\\".litescene-gui button, .litescene-gui input { pointer-events: auto; }\\\",0);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//append on top of the canvas\\r\\n\\t\\tgl.canvas.parentNode.appendChild( gui );\\r\\n\\t\\t\\r\\n\\t\\tthis._root = gui;\\r\\n\\t\\treturn gui;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Creates a HTMLElement of the tag_type and adds it to the DOM on top of the canvas\\r\\n\\t*\\r\\n\\t* @method createElement\\r\\n\\t* @param {String} tag_type the tag type \\\"div\\\"\\r\\n\\t* @param {String} anchor \\\"top-left\\\", \\\"top-right\\\", \\\"bottom-left\\\", \\\"bottom-right\\\" or \\\"none\\\"\\r\\n\\t* @return {HTMLElement} \\r\\n\\t*/\\r\\n\\tcreateElement: function( tag_type, anchor )\\r\\n\\t{\\r\\n\\t\\ttag_type = tag_type || \\\"div\\\";\\r\\n\\r\\n\\t\\tvar element = document.createElement(tag_type);\\r\\n\\t\\telement.style.pointerEvents = \\\"auto\\\";\\r\\n\\t\\treturn this.attach( element, anchor );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* attach HTMLElement to HTML GUI Root in the anchor position specified\\r\\n\\t*\\r\\n\\t* @method attach\\r\\n\\t* @param {HTMLElement} element\\r\\n\\t* @param {String} anchor \\\"top-left\\\", \\\"top-right\\\", \\\"bottom-left\\\", \\\"bottom-right\\\" or \\\"none\\\"\\r\\n\\t*/\\r\\n\\tattach: function( element, anchor )\\r\\n\\t{\\r\\n\\t\\tif(!element)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"attachToGUI: element cannot be null\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\telement.style.position = \\\"absolute\\\";\\r\\n\\r\\n\\t\\tanchor = anchor || \\\"none\\\"; //\\\"top-left\\\";\\r\\n\\r\\n\\t\\tswitch(anchor)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase \\\"bottom\\\":\\r\\n\\t\\t\\tcase \\\"bottom-left\\\":\\r\\n\\t\\t\\t\\telement.style.bottom = \\\"0\\\";\\r\\n\\t\\t\\t\\telement.style.left = \\\"0\\\";\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase \\\"bottom-right\\\":\\r\\n\\t\\t\\t\\telement.style.bottom = \\\"0\\\";\\r\\n\\t\\t\\t\\telement.style.right = \\\"0\\\";\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase \\\"bottom-middle\\\":\\r\\n\\t\\t\\t\\telement.style.bottom = \\\"0\\\";\\r\\n\\t\\t\\t\\telement.style.width = \\\"50%\\\";\\r\\n\\t\\t\\t\\telement.style.margin = \\\"0 auto\\\";\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase \\\"right\\\":\\r\\n\\t\\t\\tcase \\\"top-right\\\":\\r\\n\\t\\t\\t\\telement.style.top = \\\"0\\\";\\r\\n\\t\\t\\t\\telement.style.right = \\\"0\\\";\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase \\\"top-middle\\\":\\r\\n\\t\\t\\t\\telement.style.top = \\\"0\\\";\\r\\n\\t\\t\\t\\telement.style.width = \\\"50%\\\";\\r\\n\\t\\t\\t\\telement.style.margin = \\\"0 auto\\\";\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase \\\"left\\\":\\r\\n\\t\\t\\tcase \\\"top\\\":\\r\\n\\t\\t\\tcase \\\"top-left\\\":\\r\\n\\t\\t\\t\\telement.style.top = \\\"0\\\";\\r\\n\\t\\t\\t\\telement.style.left = \\\"0\\\";\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase \\\"none\\\": break;\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\tconsole.warn(\\\"invalid GUI anchor position: \\\",anchor);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar gui_root = this.getHTMLRoot();\\r\\n\\t\\tgui_root.appendChild( element );\\r\\n\\t\\treturn element;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Removes an element from the GUI (same as element.parentNode.removeChild( element ); )\\r\\n\\t*\\r\\n\\t* @method detach\\r\\n\\t* @param {HTMLElement} element HTML element to detach from the GUI\\r\\n\\t*/\\r\\n\\tdetach: function( element )\\r\\n\\t{\\r\\n\\t\\tif(element && element.parentNode )\\r\\n\\t\\t\\telement.parentNode.removeChild( element );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Removes all the GUI elements from the DOM\\r\\n\\t*\\r\\n\\t* @method reset\\r\\n\\t*/\\r\\n\\treset: function()\\r\\n\\t{\\r\\n\\t\\tif( !this._root )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(this._root.parentNode)\\r\\n\\t\\t\\tthis._root.parentNode.removeChild( this._root );\\r\\n\\t\\tthis._root = null;\\r\\n\\r\\n\\t\\tif(this._style)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._style.parentNode.removeChild( this._style );\\r\\n\\t\\t\\tthis._style = null;\\t\\t\\r\\n\\t\\t}\\r\\n\\t\\treturn;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* shows the HTML GUI \\r\\n\\t*\\r\\n\\t* @method showHTML\\r\\n\\t*/\\r\\n\\tshowHTML: function()\\r\\n\\t{\\r\\n\\t\\tif(!this._root)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._root.style.display = \\\"\\\";\\r\\n\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* hides the HTML GUI (but it is still existing) \\r\\n\\t*\\r\\n\\t* @method hideHTML\\r\\n\\t*/\\r\\n\\thideHTML: function()\\r\\n\\t{\\r\\n\\t\\tif(!this._root)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._root.style.display = \\\"none\\\";\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Loads resource containing the HTML code for the GUI and attachs it inside a div to the hud\\r\\n\\t*\\r\\n\\t* @method loadHTML\\r\\n\\t* @param {String} url the url of the resource containing all the HTML code\\r\\n\\t* @param {Function} on_complete callback that will be called once the HTML has been loaded and attached to the doom, it receives the HTMLElement containing all the HTML\\r\\n\\t*/\\r\\n\\tloadHTML: function( url, on_complete )\\r\\n\\t{\\r\\n\\t\\tLS.ResourcesManager.load( url, function(res){\\r\\n\\t\\t\\tvar gui_root = LS.GUI.getHTMLRoot();\\r\\n\\t\\t\\tvar html = res.getAsHTML();\\r\\n\\t\\t\\tif(!html)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.error(\\\"html resource is not a string\\\");\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\thtml.style.pointerEvents = \\\"none\\\";\\r\\n\\t\\t\\thtml.style.width = \\\"100%\\\";\\r\\n\\t\\t\\thtml.style.height = \\\"100%\\\";\\r\\n\\t\\t\\tgui_root.appendChild( html );\\r\\n\\r\\n\\t\\t\\tLS.GUI.replaceHTMLSources( gui_root );\\r\\n\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\ton_complete( html, res );\\r\\n\\t\\t});\\r\\n\\t},\\r\\n\\r\\n\\t//WIP: allows to use resources \\r\\n\\treplaceHTMLSources: function(root)\\r\\n\\t{\\r\\n\\t\\t//fetch all the tags with a src attribute\\r\\n\\t\\tvar elements = root.querySelectorAll(\\\"*[src]\\\");\\r\\n\\t\\tfor(var i = 0; i < elements.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar element = elements[i];\\r\\n\\t\\t\\tvar src = element.getAttribute(\\\"src\\\");\\r\\n\\r\\n\\t\\t\\t//check if the src contains a @\\r\\n\\t\\t\\tif(!src || src[0] != \\\"@\\\" )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tsrc = src.substr(1);\\r\\n\\t\\t\\t//replace that with a local URL to that resource in case is loaded\\r\\n\\t\\t\\tvar resource = LS.ResourcesManager.getResource( src );\\r\\n\\t\\t\\tif( resource && resource._local_url )\\r\\n\\t\\t\\t\\tsrc = resource._local_url;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tsrc = LS.ResourcesManager.getFullURL( src );\\r\\n\\t\\t\\telement.setAttribute(\\\"src\\\", src );\\r\\n\\t\\t}\\r\\n\\r\\n\\t},\\r\\n\\r\\n\\t//IMMEDIATE GUI STUFF\\r\\n\\r\\n\\t/**\\r\\n\\t* Called by the LS.Renderer to clear inmediate stuff\\r\\n\\t*\\r\\n\\t* @method ResetImmediateGUI\\r\\n\\t*/\\r\\n\\tResetImmediateGUI: function( skip_redraw )\\r\\n\\t{\\r\\n\\t\\tthis._is_on_top_of_immediate_widget = false;\\r\\n\\t\\tthis.setCursor(null);\\r\\n\\t\\tthis.pressed_enter = false;\\r\\n\\t\\tthis._offset[0] = 0;\\r\\n\\t\\tthis._offset[1] = 0;\\r\\n\\t\\tthis._gui_areas.offset = 0;\\r\\n\\t\\tthis._ctx = gl;\\r\\n\\t\\tthis.GUIStyle = this.defaultGUIStyle;\\r\\n\\t\\tthis._style_stack.length = 0;\\r\\n\\t\\tif(!skip_redraw)\\r\\n\\t\\t\\tLS.GlobalScene.requestFrame(); //force redraws\\r\\n\\t},\\r\\n\\r\\n\\t//this is done so when clicking in the area where there is an immediate GUI widget the events are not send to the app\\r\\n\\tblockEventArea: function( area )\\r\\n\\t{\\r\\n\\t\\tvar data = this._gui_areas.data;\\r\\n\\t\\tvar offset = this._gui_areas.offset;\\r\\n\\r\\n\\t\\tif(offset > data.length)\\r\\n\\t\\t\\treturn; //too many guis?\\r\\n\\r\\n\\t\\tdata[ offset ] = area[0] + this._offset[0];\\r\\n\\t\\tdata[ offset + 1] = area[1] + this._offset[1];\\r\\n\\t\\tdata[ offset + 2] = area[2];\\r\\n\\t\\tdata[ offset + 3] = area[3];\\r\\n\\t\\tthis._gui_areas.offset += 4;\\r\\n\\r\\n\\t\\t//double the size (weird situation)\\r\\n\\t\\tif( this._gui_areas.offset >= data.length && data.length < 1024*24 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._gui_areas.data = new Float32Array( data.length * 2 );\\r\\n\\t\\t\\tthis._gui_areas.data.set(data);\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\ttestEventInBlockedArea: function( e )\\r\\n\\t{\\r\\n\\t\\tif(e.type != \\\"mousedown\\\")\\r\\n\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\tvar data = this._gui_areas.data;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < this._gui_areas.offset; i+=4)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( e.mousex >= data[i] && \\r\\n\\t\\t\\t\\te.mousex < (data[i] + data[i+2]) &&\\r\\n\\t\\t\\t\\te.mousey >= data[i+1] && \\r\\n\\t\\t\\t\\te.mousey < (data[i+1] + data[i+3]))\\r\\n\\t\\t\\t\\treturn true;\\r\\n\\t\\t}\\r\\n\\t\\treturn false;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders an immediate gui BOX, used as background\\r\\n\\t*\\r\\n\\t* @method Box\\r\\n\\t* @param {Array} area [x,y,width,height]\\r\\n\\t* @param {String} color a color in string format \\\"#AFAFAF\\\"\\r\\n\\t*/\\r\\n\\tBox: function( area, color )\\r\\n\\t{\\r\\n\\t\\tif(!area)\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tthis.blockEventArea( area );\\r\\n\\r\\n\\t\\tvar ctx = gl;\\r\\n\\t\\tctx.fillStyle = color || \\\"#333\\\";\\r\\n\\t\\tctx.fillRect( area[0] + this._offset[0], area[1] + this._offset[1], area[2], area[3] );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a text (or a texture)\\r\\n\\t*\\r\\n\\t* @method Label\\r\\n\\t* @param {Array} area [x,y,width,height]\\r\\n\\t* @param {String|GL.Texture} content could be a string or a GL.Texture\\r\\n\\t*/\\r\\n\\tLabel: function( area, content )\\r\\n\\t{\\r\\n\\t\\tif(!area)\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tif(content == null)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar ctx = this._ctx;\\r\\n\\r\\n\\t\\tif(content.constructor === GL.Texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(ctx.constructor === CanvasRenderingContext2D) //canvas 2D cannot render images\\r\\n\\t\\t\\t\\tcontent = content.data && (content.data.constructor === HTMLImageElement || content.data.constructor === Image) ? content.data : null;\\r\\n\\t\\t\\tif(content)\\r\\n\\t\\t\\t\\tctx.drawImage( content, area[0] + this._offset[0], area[1] + this._offset[1], area[2], area[3] );\\r\\n\\t\\t}\\r\\n\\t\\telse if(content.constructor === HTMLImageElement || content.constructor === Image)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(ctx.constructor === CanvasRenderingContext2D)\\r\\n\\t\\t\\t\\tctx.drawImage( content, area[0] + this._offset[0], area[1] + this._offset[1], area[2], area[3] );\\r\\n\\t\\t}\\r\\n\\t\\telse \\r\\n\\t\\t{\\r\\n\\t\\t\\tif(content.constructor === Number)\\r\\n\\t\\t\\t\\tcontent = content.toFixed(3);\\r\\n\\t\\t\\telse if (content.constructor !== String)\\r\\n\\t\\t\\t\\tcontent = String(content);\\r\\n\\t\\t\\tctx.fillStyle = this.GUIStyle.color;\\r\\n\\t\\t\\tctx.font = (area[3]*0.75).toFixed(0) + \\\"px \\\" + this.GUIStyle.font;\\r\\n\\t\\t\\tctx.textAlign = \\\"left\\\";\\r\\n\\t\\t\\tctx.fillText( content, area[0] + area[3] * 0.2 + this._offset[0], area[1] + area[3] * 0.75 + this._offset[1]);\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Just defines an area that could be clicked\\r\\n\\t*\\r\\n\\t* @method ClickArea\\r\\n\\t* @param {Array} area [x,y,width,height]\\r\\n\\t* @return {Boolean} true if the button was pressed inside the area\\r\\n\\t*/\\r\\n\\tClickArea: function( area )\\r\\n\\t{\\r\\n\\t\\tif(!area)\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tthis.blockEventArea( area );\\r\\n\\t\\tvar is_over = LS.Input.isEventInRect( LS.Input.Mouse, area, this._offset );\\r\\n\\t\\tif(is_over)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._is_on_top_of_immediate_widget = true;\\r\\n\\t\\t\\tthis.setCursor(\\\"pointer\\\");\\r\\n\\t\\t}\\r\\n\\t\\tvar mouse = LS.Input.current_click;\\r\\n\\t\\tvar clicked = false;\\r\\n\\t\\tif( mouse )\\r\\n\\t\\t{\\r\\n\\t\\t\\tclicked = LS.Input.isEventInRect( mouse, area, this._offset );\\r\\n\\t\\t\\tif(clicked)\\r\\n\\t\\t\\t\\tLS.Input.current_click = false; //consume event\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn clicked;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a Button and returns if the button was pressed\\r\\n\\t*\\r\\n\\t* @method Button\\r\\n\\t* @param {Array} area [x,y,width,height]\\r\\n\\t* @param {String|GL.Texture} content could be a string or a GL.Texture (if null the button will be invisible)\\r\\n\\t* @param {String|GL.Texture} content_over same as before but in case the mouse is over\\r\\n\\t* @return {Boolean} true if the button was pressed \\r\\n\\t*/\\r\\n\\tButton: function( area, content, content_over )\\r\\n\\t{\\r\\n\\t\\tif(!area)\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tthis.blockEventArea( area );\\r\\n\\r\\n\\t\\tvar ctx = this._ctx;\\r\\n\\t\\tvar is_over = LS.Input.isEventInRect( LS.Input.Mouse, area, this._offset );\\r\\n\\t\\tif(is_over)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._is_on_top_of_immediate_widget = true;\\r\\n\\t\\t\\tthis.setCursor(\\\"pointer\\\");\\r\\n\\t\\t}\\r\\n\\t\\tvar mouse = LS.Input.current_click;\\r\\n\\t\\tvar clicked = false;\\r\\n\\t\\tif( mouse )\\r\\n\\t\\t{\\r\\n\\t\\t\\tclicked = LS.Input.isEventInRect( mouse, area, this._offset );\\r\\n\\t\\t\\tif(clicked)\\r\\n\\t\\t\\t\\tLS.Input.current_click = false; //consume event\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(content == null) //allows to create invisible buttons\\r\\n\\t\\t\\treturn clicked;\\r\\n\\r\\n\\t\\tif( content.constructor === String )\\r\\n\\t\\t{\\r\\n\\t\\t\\tctx.fillStyle = clicked ? \\\"#FFF\\\" : (is_over ? this.GUIStyle.backgroundColorOver : this.GUIStyle.backgroundColor );\\r\\n\\t\\t\\tctx.fillRect( area[0] + this._offset[0], area[1] + this._offset[1], area[2], area[3] );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(content.constructor === GL.Texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar texture = content;\\r\\n\\t\\t\\tif( is_over && content_over && content_over.constructor === GL.Texture)\\r\\n\\t\\t\\t\\ttexture = content_over;\\r\\n\\t\\t\\tctx.drawImage( texture, area[0] + this._offset[0], area[1] + this._offset[0], area[2], area[3] );\\r\\n\\t\\t}\\r\\n\\t\\telse if(content.constructor === String)\\r\\n\\t\\t{\\r\\n\\t\\t\\tctx.fillStyle = is_over ? this.GUIStyle.colorTextOver : this.GUIStyle.color;\\r\\n\\t\\t\\tctx.font = (area[3]*0.75).toFixed(0) + \\\"px \\\" + this.GUIStyle.font;\\r\\n\\t\\t\\tctx.textAlign = \\\"center\\\";\\r\\n\\t\\t\\tctx.fillText( content, area[0] + area[2] * 0.5 + this._offset[0], area[1] + area[3] * 0.75 + this._offset[1]);\\r\\n\\t\\t\\tctx.textAlign = \\\"left\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn clicked;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a Toolbar (list of buttons) and returns the active one\\r\\n\\t*\\r\\n\\t* @method Toolbar\\r\\n\\t* @param {Array} area [x,y,width,height]\\r\\n\\t* @param {Number} selected the index of the selected option\\r\\n\\t* @param {Array[String|GL.Texture]} options an array containing either strings or GL.Texture\\r\\n\\t* @return {Number} the selected index\\r\\n\\t*/\\r\\n\\tToolbar: function( area, selected, options )\\r\\n\\t{\\r\\n\\t\\tif( !area )\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tif( !options || options.constructor !== Array )\\r\\n\\t\\t\\tthrow(\\\"No options\\\");\\r\\n\\t\\tthis.blockEventArea( area );\\r\\n\\r\\n\\t\\tvar ctx = this._ctx;\\r\\n\\t\\tvar is_over = LS.Input.isEventInRect( LS.Input.Mouse, area, this._offset );\\r\\n\\t\\tif(is_over)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._is_on_top_of_immediate_widget = true;\\r\\n\\t\\t\\tthis.setCursor(\\\"pointer\\\");\\r\\n\\t\\t}\\r\\n\\t\\tvar mouse = LS.Input.current_click;\\r\\n\\t\\tvar num = options.length;\\r\\n\\t\\tvar x = area[0];\\r\\n\\t\\tvar w = area[2];\\r\\n\\t\\tarea[2] = w/num;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < num; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar content = options[i];\\r\\n\\t\\t\\tvar is_selected = selected == i;\\r\\n\\t\\t\\tvar clicked = false;\\r\\n\\t\\t\\tarea[0] = x + area[2] * i;\\r\\n\\r\\n\\t\\t\\tif( mouse )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tclicked = LS.Input.isEventInRect( mouse, area, this._offset );\\r\\n\\t\\t\\t\\tif(clicked)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tselected = i;\\r\\n\\t\\t\\t\\t\\tis_selected = true;\\r\\n\\t\\t\\t\\t\\tLS.Input.current_click = false; //consume event\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif( !content || content.constructor === String )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tctx.fillStyle = is_selected ? this.GUIStyle.backgroundColorOver : this.GUIStyle.backgroundColor;\\r\\n\\t\\t\\t\\tctx.fillRect( area[0] + this._offset[0], area[1] + this._offset[1], area[2], area[3] );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(content)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(content.constructor === GL.Texture)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar texture = content;\\r\\n\\t\\t\\t\\t\\tif(!is_selected)\\r\\n\\t\\t\\t\\t\\t\\tctx.globalAlpha = 0.5;\\r\\n\\t\\t\\t\\t\\tctx.drawImage( texture, area[0] + this._offset[0], area[1] + this._offset[1], area[2], area[3] );\\r\\n\\t\\t\\t\\t\\tctx.globalAlpha = 1;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse if(content.constructor === String)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tctx.fillStyle = this.GUIStyle.color;\\r\\n\\t\\t\\t\\t\\tctx.font = (area[3]*0.75).toFixed(0) + \\\"px \\\" + this.GUIStyle.font;\\r\\n\\t\\t\\t\\t\\tctx.textAlign = \\\"center\\\";\\r\\n\\t\\t\\t\\t\\tctx.fillText( content, area[0] + area[2] * 0.5 + this._offset[0], area[1] + area[3] * 0.75 + this._offset[1] );\\r\\n\\t\\t\\t\\t\\tctx.textAlign = \\\"left\\\";\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tarea[0] = x;\\r\\n\\t\\tarea[2] = w;\\r\\n\\r\\n\\t\\treturn selected;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a checkbox widget, and returns the current state\\r\\n\\t* Remember: you must pass as value the same value returned by this function in order to work propertly\\r\\n\\t*\\r\\n\\t* @method Toggle\\r\\n\\t* @param {Array} area [x,y,width,height]\\r\\n\\t* @param {Boolean} value if the checkbox is on or off\\r\\n\\t* @param {String|GL.Texture} content an string or image in case the checkbox is on\\r\\n\\t* @param {String|GL.Texture} content_off an string or image in case the checkbox is off \\r\\n\\t* @param {Boolean} circle if true the checkboxes are circles instead of squares\\r\\n\\t* @return {Boolean} the current state of the checkbox (will be different from value if it was pressed)\\r\\n\\t*/\\r\\n\\tToggle: function( area, value, content, content_off, circle )\\r\\n\\t{\\r\\n\\t\\tif(!area)\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tvalue = !!value;\\r\\n\\t\\tthis.blockEventArea( area );\\r\\n\\r\\n\\t\\tvar ctx = this._ctx;\\r\\n\\t\\tvar is_over = LS.Input.isEventInRect( LS.Input.Mouse, area, this._offset );\\r\\n\\t\\tif(is_over)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._is_on_top_of_immediate_widget = true;\\r\\n\\t\\t\\tthis.setCursor(\\\"pointer\\\");\\r\\n\\t\\t}\\r\\n\\t\\tvar mouse = LS.Input.current_click;\\r\\n\\t\\tvar clicked = false;\\r\\n\\t\\tif( mouse )\\r\\n\\t\\t{\\r\\n\\t\\t\\tclicked = LS.Input.isEventInRect( mouse, area, this._offset );\\r\\n\\t\\t\\tif(clicked)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tLS.Input.current_click = false; //consume event\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar margin = (area[3]*0.2)\\r\\n\\r\\n\\t\\tif(content)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(content.constructor === GL.Texture)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar texture = content;\\r\\n\\t\\t\\t\\tif( !value && content_off && content_off.constructor === GL.Texture)\\r\\n\\t\\t\\t\\t\\ttexture = content_off;\\r\\n\\t\\t\\t\\tctx.drawImage( texture, area[0] + this._offset[0], area[1] + this._offset[1], area[2], area[3] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(content.constructor === String)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tctx.fillStyle = this.GUIStyle.color;\\r\\n\\t\\t\\t\\tctx.font = (area[3]*0.75).toFixed(0) + \\\"px \\\" + this.GUIStyle.font;\\r\\n\\t\\t\\t\\tctx.textAlign = \\\"left\\\";\\r\\n\\t\\t\\t\\tctx.fillText( content, area[0] + margin + this._offset[0], area[1] + area[3] * 0.75 + this._offset[1]);\\r\\n\\r\\n\\t\\t\\t\\tvar w = area[3] * 0.6;\\r\\n\\t\\t\\t\\tctx.fillStyle = this.GUIStyle.backgroundColor;\\r\\n\\t\\t\\t\\tvar x = area[0] + area[2] - margin*1.5 - w + this._offset[0];\\r\\n\\t\\t\\t\\tvar y = area[1] + margin*0.5 + this._offset[1];\\r\\n\\r\\n\\t\\t\\t\\tif(circle)\\r\\n\\t\\t\\t\\t\\tctx.fillCircle( x, y, area[3] - margin );\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tctx.fillRect(x, y, w+margin, area[3] - margin );\\r\\n\\t\\t\\t\\tctx.fillStyle = value ? this.GUIStyle.selected : \\\"#000\\\";\\r\\n\\t\\t\\t\\tif(circle)\\r\\n\\t\\t\\t\\t\\tctx.fillCircle( area[0] + area[2] - margin - w + this._offset[0], area[1] + margin + this._offset[1], area[3] - margin*2 );\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tctx.fillRect( area[0] + area[2] - margin - w + this._offset[0], area[1] + margin + this._offset[1], w, area[3] - margin*2 );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn clicked ? !value : value;\\r\\n\\t},\\r\\n\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a textfield widget and returns the current text value\\r\\n\\t* Remember: you must pass as text the same text returned by this function in order to work propertly\\r\\n\\t*\\r\\n\\t* @method TextField\\r\\n\\t* @param {Array} area [x,y,width,height]\\r\\n\\t* @param {String} text the text to show in the textfield\\r\\n\\t* @param {Number} max_length to limit the text, otherwise leave blank\\r\\n\\t* @param {Boolean} is_password set to true to show as password\\r\\n\\t* @return {Boolean} the current state of the checkbox (will be different from value if it was pressed)\\r\\n\\t*/\\r\\n\\tTextField: function( area, text, max_length, is_password )\\r\\n\\t{\\r\\n\\t\\tif(!area)\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tthis.blockEventArea( area );\\r\\n\\r\\n\\t\\ttext = text === undefined ? \\\"\\\" : String(text);\\r\\n\\t\\tmax_length = max_length || 1024;\\r\\n\\r\\n\\t\\tvar ctx = this._ctx;\\r\\n\\t\\tvar is_over = LS.Input.isEventInRect( LS.Input.Mouse, area, this._offset );\\r\\n\\t\\tif(is_over)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._is_on_top_of_immediate_widget = true;\\r\\n\\t\\t\\tthis.setCursor(\\\"pointer\\\");\\r\\n\\t\\t}\\r\\n\\t\\tvar mouse = LS.Input.current_click;\\r\\n\\t\\tvar clicked = false;\\r\\n\\t\\tif( mouse )\\r\\n\\t\\t{\\r\\n\\t\\t\\tclicked = LS.Input.isEventInRect( mouse, area, this._offset );\\r\\n\\t\\t\\tif(clicked)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tLS.Input.current_click = null; //consume event\\r\\n\\t\\t\\t\\tLS.Input.last_click = mouse;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tvar is_selected = false;\\r\\n\\t\\tif( LS.Input.last_click && LS.Input.isEventInRect( LS.Input.last_click, area, this._offset ) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tis_selected = true;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.pressed_enter = false;\\r\\n\\t\\tif(is_selected)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar keys = LS.Input.keys_buffer;\\r\\n\\t\\t\\tfor( var i = 0; i < keys.length; ++i )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar key = keys[i];\\r\\n\\t\\t\\t\\tswitch(key.keyCode)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tcase 8: text = text.substr(0, text.length - 1 ); break; //backspace\\r\\n\\t\\t\\t\\t\\tcase 13: this.pressed_enter = true; break; //return\\r\\n\\t\\t\\t\\t\\tcase 32: if(text.length < max_length) text += \\\" \\\"; break;\\r\\n\\t\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\t\\tif(text.length < max_length && key.key && key.key.length == 1) //length because control keys send a string like \\\"Shift\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\ttext += key.key;\\r\\n\\t\\t\\t\\t\\t\\t/*\\r\\n\\t\\t\\t\\t\\t\\tif( key.keyCode >= 65 && key.keyCode <= 122 ) //letters\\r\\n\\t\\t\\t\\t\\t\\t\\ttext += key.shiftKey ? key.character.toUpperCase() : key.character.toLowerCase();\\r\\n\\t\\t\\t\\t\\t\\t*/\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t//console.log(key.charCode, key.keyCode, key.character, key.which, key );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tkeys.length = 0; //consume them\\r\\n\\t\\t\\tLS.Input.current_key = null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar line = (area[3]*0.02);\\r\\n\\t\\tvar margin = (area[3]*0.2);\\r\\n\\r\\n\\t\\t//contour\\r\\n\\t\\tctx.fillStyle = this.GUIStyle.backgroundColor;\\r\\n\\t\\tctx.fillRect( area[0] + this._offset[0], area[1] + this._offset[1], area[2], area[3] );\\r\\n\\t\\tctx.fillStyle = \\\"#000\\\";\\r\\n\\t\\tctx.fillRect( area[0] + line + this._offset[0], area[1] + line + this._offset[1], area[2] - line*2, area[3] - line*2 );\\r\\n\\r\\n\\t\\tctx.fillStyle = this.GUIStyle.color;\\r\\n\\t\\tctx.font = (area[3]*0.75).toFixed(0) + \\\"px \\\" + this.GUIStyle.font;\\r\\n\\t\\tctx.textAlign = \\\"left\\\";\\r\\n\\r\\n\\t\\tvar cursor = \\\"\\\";\\r\\n\\t\\tif( is_selected && (((getTime() * 0.002)|0) % 2) == 0 )\\r\\n\\t\\t\\tcursor = \\\"|\\\";\\r\\n\\r\\n\\t\\tvar final_text = text;\\r\\n\\t\\tif(is_password)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfinal_text = \\\"\\\";\\r\\n\\t\\t\\tfor(var i = 0; i < text.length; ++i)\\r\\n\\t\\t\\t\\tfinal_text += \\\"*\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tctx.fillText( final_text + cursor, area[0] + margin*2 + this._offset[0], area[1] + area[3] * 0.75 + this._offset[1] );\\r\\n\\r\\n\\t\\treturn text;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders an horizontal slider widget, returns the current value\\r\\n\\t* Remember: you must pass as value the same value returned by this function in order to work propertly\\r\\n\\t*\\r\\n\\t* @method HorizontalSlider\\r\\n\\t* @param {Array} area [x,y,width,height]\\r\\n\\t* @param {Number} value the value to show in the slider\\r\\n\\t* @param {Number} left_value the minimum value for the slider\\r\\n\\t* @param {Number} right_value the maximum value for the slider\\r\\n\\t* @param {Boolean} show_value if you want to see a caption in text format with the value\\r\\n\\t* @return {Number} the current value of the slider (will be different from value if it was clicked)\\r\\n\\t*/\\r\\n\\tHorizontalSlider: function( area, value, left_value, right_value, show_value )\\r\\n\\t{\\r\\n\\t\\tif(!area)\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tthis.blockEventArea( area );\\r\\n\\r\\n\\t\\tif(left_value === undefined)\\r\\n\\t\\t\\tleft_value = 0;\\r\\n\\t\\tif(right_value === undefined)\\r\\n\\t\\t\\tright_value = 1;\\r\\n\\t\\tvalue = Number(value);\\r\\n\\t\\tleft_value = Number(left_value);\\r\\n\\t\\tright_value = Number(right_value);\\r\\n\\r\\n\\t\\tvar ctx = this._ctx;\\r\\n\\t\\tvar is_over = LS.Input.isEventInRect( LS.Input.Mouse, area, this._offset );\\r\\n\\t\\tif(is_over)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._is_on_top_of_immediate_widget = true;\\r\\n\\t\\t\\tthis.setCursor(\\\"pointer\\\");\\r\\n\\t\\t}\\r\\n\\t\\tvar mouse = LS.Input.current_click;\\r\\n\\t\\tvar clicked = false;\\r\\n\\t\\tvar range = right_value - left_value;\\r\\n\\t\\tvar norm_value = (value - left_value) / range;\\r\\n\\t\\tif(norm_value < 0) norm_value = 0;\\r\\n\\t\\tif(norm_value > 1) norm_value = 1;\\r\\n\\r\\n\\t\\tvar margin = (area[3]*this.GUIStyle.margin);\\r\\n\\r\\n\\t\\tif( mouse )\\r\\n\\t\\t{\\r\\n\\t\\t\\tclicked = LS.Input.isEventInRect( mouse, area, this._offset );\\r\\n\\t\\t\\tif(clicked)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnorm_value = ( (LS.Input.Mouse.x - this._offset[0]) - (area[0] + margin)) / (area[2] - margin*2);\\r\\n\\t\\t\\t\\tif(norm_value < 0) norm_value = 0;\\r\\n\\t\\t\\t\\tif(norm_value > 1) norm_value = 1;\\r\\n\\t\\t\\t\\tvalue = norm_value * range + left_value;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//bg\\r\\n\\t\\tctx.fillStyle = this.GUIStyle.backgroundColor;\\r\\n\\t\\tctx.fillRect( area[0] + this._offset[0], area[1] + this._offset[1], area[2], area[3] );\\r\\n\\t\\t//slider\\r\\n\\t\\tctx.fillStyle = is_over ? this.GUIStyle.selected : this.GUIStyle.unselected;\\r\\n\\t\\tctx.fillRect( area[0] + margin + this._offset[0], area[1] + margin + this._offset[1], Math.max(2, (area[2] - margin*2) * norm_value ), area[3] - margin*2 );\\r\\n\\r\\n\\t\\tif(show_value)\\r\\n\\t\\t{\\r\\n\\t\\t\\tctx.textAlign = \\\"center\\\";\\r\\n\\t\\t\\tctx.fillStyle = this.GUIStyle.color;\\r\\n\\t\\t\\tctx.font = (area[3]*0.5).toFixed(0) + \\\"px \\\" + this.GUIStyle.font;\\r\\n\\t\\t\\tctx.fillText( value.toFixed(2), area[0] + area[2] * 0.5 + this._offset[0], area[1] + area[3] * 0.7 + this._offset[1] );\\r\\n\\t\\t\\tctx.textAlign = \\\"left\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn value;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders an vertical slider widget, returns the current value\\r\\n\\t* Remember: you must pass as value the same value returned by this function in order to work propertly\\r\\n\\t*\\r\\n\\t* @method VerticalSlider\\r\\n\\t* @param {Array} area [x,y,width,height]\\r\\n\\t* @param {Number} value the value to show in the slider\\r\\n\\t* @param {Number} bottom_value the minimum value for the slider\\r\\n\\t* @param {Number} top_value the maximum value for the slider\\r\\n\\t* @return {Number} the current value of the slider (will be different from value if it was clicked)\\r\\n\\t*/\\r\\n\\tVerticalSlider: function( area, value, bottom_value, top_value )\\r\\n\\t{\\r\\n\\t\\tif(!area)\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tthis.blockEventArea( area );\\r\\n\\r\\n\\t\\tvalue = Number(value);\\r\\n\\t\\tif(bottom_value === undefined)\\r\\n\\t\\t\\tbottom_value = 0;\\r\\n\\t\\tif(top_value === undefined)\\r\\n\\t\\t\\ttop_value = 1;\\r\\n\\t\\tbottom_value = Number(bottom_value);\\r\\n\\t\\ttop_value = Number(top_value);\\r\\n\\r\\n\\t\\tvar ctx = this._ctx;\\r\\n\\t\\tvar is_over = LS.Input.isEventInRect( LS.Input.Mouse, area, this._offset );\\r\\n\\t\\tif(is_over)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._is_on_top_of_immediate_widget = true;\\r\\n\\t\\t\\tthis.setCursor(\\\"pointer\\\");\\r\\n\\t\\t}\\r\\n\\t\\tvar mouse = LS.Input.current_click;\\r\\n\\t\\tvar clicked = false;\\r\\n\\t\\tvar range = top_value - bottom_value;\\r\\n\\t\\tvar norm_value = (value - bottom_value) / range;\\r\\n\\t\\tif(norm_value < 0) norm_value = 0;\\r\\n\\t\\tif(norm_value > 1) norm_value = 1;\\r\\n\\r\\n\\t\\tvar margin = (area[2]*this.GUIStyle.margin)\\r\\n\\r\\n\\t\\tif( mouse )\\r\\n\\t\\t{\\r\\n\\t\\t\\tclicked = LS.Input.isEventInRect( mouse, area, this._offset );\\r\\n\\t\\t\\tif(clicked)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnorm_value = ( (LS.Input.Mouse.y - this._offset[1]) - (area[1] + margin)) / (area[3] - margin*2);\\r\\n\\t\\t\\t\\tif(norm_value < 0) norm_value = 0;\\r\\n\\t\\t\\t\\tif(norm_value > 1) norm_value = 1;\\r\\n\\t\\t\\t\\tnorm_value = 1 - norm_value; //reverse slider\\r\\n\\t\\t\\t\\tvalue = norm_value * range + bottom_value;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\r\\n\\t\\t//bg\\r\\n\\t\\tctx.fillStyle = this.GUIStyle.backgroundColor;\\r\\n\\t\\tctx.fillRect( area[0] + this._offset[0], area[1] + this._offset[1], area[2], area[3] );\\r\\n\\t\\t//slider\\r\\n\\t\\tctx.fillStyle = is_over ? this.GUIStyle.selected : this.GUIStyle.unselected;\\r\\n\\t\\tvar slider_height = Math.max(2, (area[3] - margin*2) * norm_value);\\r\\n\\t\\tctx.fillRect( area[0] + margin + this._offset[0], area[1] + area[3] - slider_height - margin + this._offset[1], area[2] - margin*2, slider_height );\\r\\n\\r\\n\\t\\treturn value;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders an knob slider widget, returns the current value\\r\\n\\t* Remember: you must pass as value the same value returned by this function in order to work propertly\\r\\n\\t*\\r\\n\\t* @method Knob\\r\\n\\t* @param {Array} area [x,y,width,height]\\r\\n\\t* @param {Number} value the value to show in the slider\\r\\n\\t* @param {Number} bottom_value the minimum value for the slider\\r\\n\\t* @param {Number} top_value the maximum value for the slider\\r\\n\\t* @param {Number} steps [optional] the numeber of steps (if 0 then infinite)\\r\\n\\t* @param {Image|GL.Texture} content [optional] a texture or image to use as the knob\\r\\n\\t* @return {Number} the current value of the slider (will be different from value if it was clicked)\\r\\n\\t*/\\r\\n\\tKnob: function( area, value, bottom_value, top_value, steps, content )\\r\\n\\t{\\r\\n\\t\\tif(!area)\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tthis.blockEventArea( area );\\r\\n\\r\\n\\t\\tvalue = Number(value);\\r\\n\\t\\tif(bottom_value === undefined)\\r\\n\\t\\t\\tbottom_value = 0;\\r\\n\\t\\tif(top_value === undefined)\\r\\n\\t\\t\\ttop_value = 1;\\r\\n\\t\\tsteps = steps || 0;\\r\\n\\t\\tbottom_value = Number(bottom_value);\\r\\n\\t\\ttop_value = Number(top_value);\\r\\n\\r\\n\\t\\tvar ctx = this._ctx;\\r\\n\\t\\tvar is_over = LS.Input.isEventInRect( LS.Input.Mouse, area, this._offset );\\r\\n\\t\\tif(is_over)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._is_on_top_of_immediate_widget = true;\\r\\n\\t\\t\\tthis.setCursor(\\\"pointer\\\");\\r\\n\\t\\t}\\r\\n\\t\\tvar mouse = LS.Input.current_click;\\r\\n\\t\\tvar clicked = false;\\r\\n\\t\\tvar range = top_value - bottom_value;\\r\\n\\t\\tvar norm_value = (value - bottom_value) / range;\\r\\n\\t\\tif(norm_value < 0) norm_value = 0;\\r\\n\\t\\tif(norm_value > 1) norm_value = 1;\\r\\n\\r\\n\\t\\tvar margin = (area[2]*this.GUIStyle.margin)\\r\\n\\t\\tvar start_angle = -Math.PI*0.75;\\r\\n\\t\\tvar total_angle = 1.5*Math.PI;\\r\\n\\r\\n\\t\\tif( mouse )\\r\\n\\t\\t{\\r\\n\\t\\t\\tclicked = LS.Input.isEventInRect( mouse, area, this._offset );\\r\\n\\t\\t\\tif(clicked)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar dx = LS.Input.Mouse.x - (area[0] + area[2] * 0.5) - this._offset[0];\\r\\n\\t\\t\\t\\tvar dy = LS.Input.Mouse.y - (area[1] + area[3] * 0.5) - this._offset[1];\\r\\n\\t\\t\\t\\t//var angle = Math.atan2( dx, -dy ) / Math.PI;\\r\\n\\t\\t\\t\\tvar angle = ( Math.atan2( dx, -dy ) - start_angle ) / total_angle;\\r\\n\\t\\t\\t\\tnorm_value = angle;\\r\\n\\t\\t\\t\\t//norm_value = ( (LS.Input.Mouse.y - this._offset[1]) - (area[1] + margin)) / (area[3] - margin*2);\\r\\n\\t\\t\\t\\t//norm_value = 1 - norm_value; //reverse slider\\r\\n\\t\\t\\t\\tif(norm_value < 0) norm_value = 0;\\r\\n\\t\\t\\t\\tif(norm_value > 1) norm_value = 1;\\r\\n\\t\\t\\t\\tvalue = norm_value * range + bottom_value;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(steps)\\r\\n\\t\\t\\tnorm_value = Math.round(norm_value * steps) / steps;\\r\\n\\r\\n\\t\\tif( content !== undefined ) //texture\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( content !== null ) // in case we are loading the texture\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar texture = null;\\r\\n\\t\\t\\t\\tif(content.constructor === GL.Texture)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(ctx.constructor === CanvasRenderingContext2D) //canvas 2D cannot render images\\r\\n\\t\\t\\t\\t\\t\\tcontent = content.data && (content.data.constructor === HTMLImageElement || content.data.constructor === Image) ? content.data : null;\\r\\n\\t\\t\\t\\t\\tif(content)\\r\\n\\t\\t\\t\\t\\t\\ttexture = content;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse if(content.constructor === HTMLImageElement || content.constructor === Image)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(ctx.constructor === CanvasRenderingContext2D)\\r\\n\\t\\t\\t\\t\\t\\ttexture = content;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif(texture)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tctx.save();\\r\\n\\t\\t\\t\\t\\tctx.translate( area[0] + area[2] * 0.5 + this._offset[0], area[1] + area[3] * 0.5 + this._offset[1] );\\r\\n\\t\\t\\t\\t\\tctx.rotate( norm_value * total_angle + start_angle );\\r\\n\\t\\t\\t\\t\\tctx.scale( area[3] / texture.height , area[3] / texture.height );\\r\\n\\t\\t\\t\\t\\tctx.drawImage( texture, -texture.width * 0.5, -texture.height * 0.5 );\\r\\n\\t\\t\\t\\t\\tctx.restore();\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\t//bg\\r\\n\\t\\t\\tctx.strokeStyle = this.GUIStyle.outline;\\r\\n\\t\\t\\tctx.fillStyle = this.GUIStyle.backgroundColor;\\r\\n\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\tctx.arc( area[0] + area[2] * 0.5 + this._offset[0], area[1] + area[3] * 0.5 + this._offset[1], area[3] * 0.45, 0, 2 * Math.PI, false );\\r\\n\\t\\t\\tctx.fill();\\r\\n\\t\\t\\tctx.stroke();\\r\\n\\r\\n\\t\\t\\t//slider\\r\\n\\t\\t\\tctx.lineWidth = area[3]*0.1;\\r\\n\\t\\t\\tctx.strokeStyle = is_over ? this.GUIStyle.selected : this.GUIStyle.unselected;\\r\\n\\t\\t\\tctx.beginPath();\\r\\n\\r\\n\\t\\t\\tstart_angle = -Math.PI*1.25;\\r\\n\\t\\t\\tctx.arc( area[0] + area[2] * 0.5 + this._offset[0], area[1] + area[3] * 0.5 + this._offset[1], area[3] * 0.35, start_angle, start_angle + Math.max(DEG2RAD,total_angle * norm_value), false );\\r\\n\\t\\t\\tctx.stroke();\\r\\n\\t\\t\\tctx.lineWidth = 1;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn value;\\r\\n\\t},\\r\\n\\r\\n\\t//*\\r\\n\\tDragArea: function( area, value )\\r\\n\\t{\\r\\n\\t\\tif(!area)\\r\\n\\t\\t\\tthrow(\\\"No area\\\");\\r\\n\\t\\tif(!value)\\r\\n\\t\\t\\tthrow(\\\"No value\\\");\\r\\n\\t\\tthis.blockEventArea( area );\\r\\n\\r\\n\\t\\tvar ctx = this._ctx;\\r\\n\\t\\tvar is_over = LS.Input.isEventInRect( LS.Input.Mouse, area, this._offset );\\r\\n\\t\\tif(is_over)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._is_on_top_of_immediate_widget = true;\\r\\n\\t\\t\\tthis.setCursor(\\\"pointer\\\");\\r\\n\\t\\t}\\r\\n\\t\\tvar mouse = LS.Input.current_click;\\r\\n\\t\\tvar clicked = false;\\r\\n\\t\\tif( mouse )\\r\\n\\t\\t{\\r\\n\\t\\t\\tclicked = LS.Input.isEventInRect( mouse, area, this._offset );\\r\\n\\t\\t\\tif(clicked)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tLS.Input.current_click = null; //consume event\\r\\n\\t\\t\\t\\tLS.Input.last_click = mouse;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tvar is_selected = false;\\r\\n\\t\\tif( LS.Input.last_click && LS.Input.isEventInRect( LS.Input.last_click, area, this._offset ) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tis_selected = true;\\r\\n\\t\\t\\tif( LS.Input.Mouse.dragging )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvalue[0] += LS.Input.Mouse.deltax || 0;\\r\\n\\t\\t\\t\\tvalue[1] += LS.Input.Mouse.deltay || 0;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn value;\\r\\n\\t},\\r\\n\\t//*/\\r\\n\\r\\n\\tpushStyle: function()\\r\\n\\t{\\r\\n\\t\\tvar new_style = LS.cloneObject( this.GUIStyle );\\r\\n\\t\\tthis._style_stack.push(this.GUIStyle);\\r\\n\\t\\tthis.GUIStyle = new_style;\\r\\n\\t},\\r\\n\\r\\n\\tpopStyle: function()\\r\\n\\t{\\r\\n\\t\\tif(this._style_stack.length)\\r\\n\\t\\t\\tthis.GUIStyle = this._style_stack.pop();\\r\\n\\t},\\r\\n\\r\\n\\tsetCursor: function(type)\\r\\n\\t{\\r\\n\\t\\tif(!this._allow_change_cursor)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tgl.canvas.style.cursor = type || \\\"\\\";\\r\\n\\t}\\r\\n};\\r\\n\\r\\nObject.defineProperty( GUI, \\\"GUIOffset\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tif(!v.length || v.length < 2)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._offset[0] = v[0];\\r\\n\\t\\tthis._offset[1] = v[1];\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._offset;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n//LEGACY API\\r\\nGUI.show = GUI.showHTML;\\r\\nGUI.hide = GUI.hideHTML;\\r\\nGUI.load = GUI.loadHTML;\\r\\n\\r\\nGUI.getRoot = function()\\r\\n{\\r\\n\\tconsole.warn(\\\"LS.GUI.getRoot() deprecated, use LS.GUI.getHTMLRoot() instead.\\\");\\r\\n\\treturn LS.GUI.getHTMLRoot();\\r\\n}\\r\\n\\r\\nLS.GUI = GUI;\\r\\n///@FILE:../src/resourcesManager.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* Static class that contains all the resources loaded, parsed and ready to use.\\r\\n* It also contains the parsers and methods in charge of processing them\\r\\n*\\r\\n* @class ResourcesManager\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\n// **** RESOURCES MANANGER *********************************************\\r\\n// Resources should follow the text structure:\\r\\n// + id: number, if stored in remote server\\r\\n// + resource_type: string (\\\"Mesh\\\",\\\"Texture\\\",...) or if omitted the classname will be used\\r\\n// + filename: string (this string will be used to get the filetype)\\r\\n// + fullpath: the full path to reach the file on the server (folder + filename)\\r\\n// + preview: img url\\r\\n// + toBinary: generates a binary version to store on the server\\r\\n// + serialize: generates an stringifible object to store on the server\\r\\n\\r\\n// + _original_data: ArrayBuffer with the bytes form the original file\\r\\n// + _original_file: File with the original file where this res came from\\r\\n\\r\\nvar ResourcesManager = {\\r\\n\\r\\n\\tpath: \\\"\\\", //url to retrieve resources relative to the index.html\\r\\n\\tproxy: \\\"\\\", //url to retrieve resources outside of this host\\r\\n\\tignore_cache: false, //change to true to ignore client cache\\r\\n\\tfree_data: false, //free all data once it has been uploaded to the VRAM\\r\\n\\tkeep_files: false, //keep the original files inside the resource (used mostly in the editor)\\r\\n\\tkeep_urls: false, //keep the local URLs of loaded files\\r\\n\\tallow_base_files: false, //allow to load files that are not in a subfolder\\r\\n\\r\\n\\tscene_external_repository: null, //this is used by some scenes to specify where are the resources located\\r\\n\\r\\n\\t//some containers\\r\\n\\tresources: {}, //filename associated to a resource (texture,meshes,audio,script...)\\r\\n\\tmeshes: {}, //loadead meshes\\r\\n\\ttextures: {}, //loadead textures\\r\\n\\tmaterials: {}, //shared materials (indexed by name)\\r\\n\\tmaterials_by_uid: {}, //shared materials (indexed by uid)\\r\\n\\r\\n\\tresources_not_found: {}, //resources that will be skipped because they werent found\\r\\n\\tresources_being_loaded: {}, //resources waiting to be loaded\\r\\n\\tresources_being_processed: {}, //used to avoid loading stuff that is being processes\\r\\n\\tresources_renamed_recently: {}, //used to find resources with old names\\r\\n\\tnum_resources_being_loaded: 0,\\r\\n\\tMAX_TEXTURE_SIZE: 4096,\\r\\n\\r\\n\\tresource_pre_callbacks: {}, //used to extract resource info from a file -> \\\"obj\\\":callback\\r\\n\\tresource_post_callbacks: {}, //used to post process a resource type -> \\\"Mesh\\\":callback\\r\\n\\tresource_once_callbacks: {}, //callback called once\\r\\n\\r\\n\\tvirtual_file_systems: {}, //protocols associated to urls \\\"VFS\\\":\\\"../\\\"\\r\\n\\tskip_proxy_extensions: [\\\"mp3\\\",\\\"wav\\\",\\\"ogg\\\"], //this file formats should not be passed through the proxy\\r\\n\\tforce_nocache_extensions: [\\\"js\\\",\\\"glsl\\\",\\\"json\\\"], //this file formats should be reloaded without using the cache\\r\\n\\tnocache_files: {}, //this is used by the editor to avoid using cached version of recently loaded files\\r\\n\\r\\n\\tvalid_resource_name_reg: /^[A-Za-z\\\\d\\\\s\\\\/\\\\_\\\\-\\\\.]+$/,\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns a string to append to any url that should use the browser cache (when updating server info)\\r\\n\\t*\\r\\n\\t* @method getNoCache\\r\\n\\t* @param {Boolean} force force to return a nocache string ignoring the default configuration\\r\\n\\t* @return {String} a string to attach to a url so the file wont be cached\\r\\n\\t*/\\r\\n\\r\\n\\tgetNoCache: function(force) { return (!this.ignore_cache && !force) ? \\\"\\\" : \\\"nocache=\\\" + getTime() + Math.floor(Math.random() * 1000); },\\r\\n\\r\\n\\t/**\\r\\n\\t* Resets all the resources cached, so it frees the memory\\r\\n\\t*\\r\\n\\t* @method reset\\r\\n\\t*/\\r\\n\\treset: function()\\r\\n\\t{\\r\\n\\t\\tthis.resources = {};\\r\\n\\t\\tthis.meshes = {};\\r\\n\\t\\tthis.textures = {};\\r\\n\\t\\tthis.materials = {};\\r\\n\\t\\tthis.materials_by_uid = {};\\r\\n\\r\\n\\t\\tthis.scene_external_repository = null;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Resources need to be parsed once the data has been received, some formats could be parsed using native functions (like images) others \\r\\n\\t* require to pass the data through a series of functions (extract raw content, parse it, upload it to the GPU...\\r\\n\\t* Registering a resource preprocessor the data will be converted once it is in memory \\r\\n\\t*\\r\\n\\t* @method registerResourcePreProcessor\\r\\n\\t* @param {String} fileformats the extension of the formats that this function will parse\\r\\n\\t* @param {Function} callback the function to call once the data must be processed, if the process is async it must return true\\r\\n\\t* @param {string} data_type \\r\\n\\t* @param {string} resource_type \\r\\n\\t*/\\r\\n\\tregisterResourcePreProcessor: function( fileformats, callback, data_type, resource_type )\\r\\n\\t{\\r\\n\\t\\tif(!fileformats)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar ext = fileformats.split(\\\",\\\");\\r\\n\\t\\tfor(var i in ext)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar extension = ext[i].toLowerCase();\\r\\n\\t\\t\\tthis.resource_pre_callbacks[ extension ] = callback;\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Some resources require to be post-processed right after being parsed to validate, extend, register (meshes need to have the AABB computed...)\\r\\n\\t* This job could be done inside the parser but it is better to do it separatedly so it can be reused among different parsers.\\r\\n\\t*\\r\\n\\t* @method registerResourcePostProcessor\\r\\n\\t* @param {String} resource_type the name of the class of the resource\\r\\n\\t* @param {Function} callback the function to call once the data has been processed\\r\\n\\t*/\\r\\n\\tregisterResourcePostProcessor: function(resource_type, callback)\\r\\n\\t{\\r\\n\\t\\tthis.resource_post_callbacks[ resource_type ] = callback;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the filename extension from an url\\r\\n\\t*\\r\\n\\t* @method getExtension\\r\\n\\t* @param {String} fullpath url or filename\\r\\n\\t* @param {boolean} complex_extension [optional] returns the extension from the first dot, otherwise only the part from last dot\\r\\n\\t* @return {String} filename extension\\r\\n\\t*/\\r\\n\\tgetExtension: function( fullpath, complex_extension )\\r\\n\\t{\\r\\n\\t\\tif(!fullpath)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\tvar question = fullpath.indexOf(\\\"?\\\");\\r\\n\\t\\tif(question != -1)\\r\\n\\t\\t\\tfullpath = fullpath.substr(0,question);\\r\\n\\r\\n\\t\\tvar point = complex_extension ? fullpath.indexOf(\\\".\\\") : fullpath.lastIndexOf(\\\".\\\");\\r\\n\\t\\tif(point == -1)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\treturn fullpath.substr(point+1).toLowerCase().trim();\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the url without the extension\\r\\n\\t*\\r\\n\\t* @method removeExtension\\r\\n\\t* @param {String} fullpath url or filename\\r\\n\\t* @param {boolean} complex_extension [optional] removes the extension from the first dot, otherwise only the part from last dot\\r\\n\\t* @return {String} url without extension\\r\\n\\t*/\\r\\n\\tremoveExtension: function( fullpath, complex_extension )\\r\\n\\t{\\r\\n\\t\\tif(!fullpath)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\tvar question = fullpath.indexOf(\\\"?\\\");\\r\\n\\t\\tif(question != -1)\\r\\n\\t\\t\\tfullpath = fullpath.substr(0,question);\\r\\n\\t\\tvar point = complex_extension ? fullpath.indexOf(\\\".\\\") : fullpath.lastIndexOf(\\\".\\\");\\r\\n\\t\\tif(point == -1)\\r\\n\\t\\t\\treturn fullpath;\\r\\n\\t\\treturn fullpath.substr(0,point);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Replaces the extension of a filename\\r\\n\\t*\\r\\n\\t* @method replaceExtension\\r\\n\\t* @param {String} fullpath url or filename\\r\\n\\t* @param {String} extension\\r\\n\\t* @return {String} url with the new extension\\r\\n\\t*/\\r\\n\\treplaceExtension: function( fullpath, extension )\\r\\n\\t{\\r\\n\\t\\tif(!fullpath)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\textension = extension || \\\"\\\";\\r\\n\\t\\tvar folder = this.getFolder( fullpath );\\r\\n\\t\\tvar filename = this.getFilename( fullpath );\\r\\n\\t\\treturn this.cleanFullpath( (folder ? ( folder + \\\"/\\\" ) : \\\"\\\") + filename + \\\".\\\" + extension );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the filename from a full path\\r\\n\\t*\\r\\n\\t* @method getFilename\\r\\n\\t* @param {String} fullpath\\r\\n\\t* @return {String} filename extension\\r\\n\\t*/\\r\\n\\tgetFilename: function( fullpath )\\r\\n\\t{\\r\\n\\t\\tif(!fullpath)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\tvar pos = fullpath.lastIndexOf(\\\"/\\\");\\r\\n\\t\\tvar question = fullpath.lastIndexOf(\\\"?\\\"); //to avoid problems with URLs line scene.json?nocache=...\\r\\n\\t\\tquestion = (question == -1 ? fullpath.length : (question - 1) ) - pos;\\r\\n\\t\\treturn fullpath.substr(pos+1,question);\\r\\n\\t},\\t\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the folder from a fullpath\\r\\n\\t*\\r\\n\\t* @method getFolder\\r\\n\\t* @param {String} fullpath\\r\\n\\t* @return {String} folder name\\r\\n\\t*/\\r\\n\\tgetFolder: function(fullpath)\\r\\n\\t{\\r\\n\\t\\tif(!fullpath)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\tvar pos = fullpath.lastIndexOf(\\\"/\\\");\\r\\n\\t\\treturn fullpath.substr(0,pos);\\r\\n\\t},\\t\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the filename without the folder or the extension\\r\\n\\t*\\r\\n\\t* @method getBasename\\r\\n\\t* @param {String} fullpath\\r\\n\\t* @return {String} filename extension\\r\\n\\t*/\\r\\n\\tgetBasename: function( fullpath )\\r\\n\\t{\\r\\n\\t\\tif(!fullpath)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\tvar name = this.getFilename(fullpath);\\r\\n\\t\\tvar pos = name.indexOf(\\\".\\\");\\r\\n\\t\\tif(pos == -1)\\r\\n\\t\\t\\treturn name;\\r\\n\\t\\treturn name.substr(0,pos);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the url protocol (http, https) or empty string if no protocol was found\\r\\n\\t*\\r\\n\\t* @method getProtocol\\r\\n\\t* @param {String} url\\r\\n\\t* @return {String} protocol\\r\\n\\t*/\\r\\n\\tgetProtocol: function( url )\\r\\n\\t{\\r\\n\\t\\tif(!url)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\tvar pos = url.substr(0,10).indexOf(\\\":\\\");\\r\\n\\t\\tvar protocol = \\\"\\\";\\r\\n\\t\\tif(pos != -1)\\r\\n\\t\\t\\tprotocol = url.substr(0,pos);\\r\\n\\t\\treturn protocol;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Cleans resource name (removing double slashes to avoid problems) \\r\\n\\t* It is slow, so use it only in changes, not in getters\\r\\n\\t*\\r\\n\\t* @method cleanFullpath\\r\\n\\t* @param {String} fullpath\\r\\n\\t* @return {String} fullpath cleaned\\r\\n\\t*/\\r\\n\\tcleanFullpath: function(fullpath)\\r\\n\\t{\\r\\n\\t\\tif(!fullpath)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\tif( fullpath.indexOf(\\\"//\\\") == -1 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(fullpath.charCodeAt(0) == 47) // the '/' char\\r\\n\\t\\t\\t\\treturn fullpath.substr(1);\\r\\n\\t\\t\\treturn fullpath;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//clean up the filename (to avoid problems with //)\\r\\n\\t\\tif(fullpath.indexOf(\\\"://\\\") == -1)\\r\\n\\t\\t\\treturn fullpath.split(\\\"/\\\").filter(function(v){ return !!v; }).join(\\\"/\\\");\\r\\n\\t\\treturn fullpath;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Loads all the resources in the Object (it uses an object to store not only the filename but also the type)\\r\\n\\t*\\r\\n\\t* @method loadResources\\r\\n\\t* @param {Object|Array} resources contains all the resources, associated with its type\\r\\n\\t* @param {Object}[options={}] options to apply to the loaded resources\\r\\n\\t* @return {number} the actual amount of resources being loaded (this differs fromt he resources passed because some could be already in memory)\\r\\n\\t*/\\r\\n\\tloadResources: function( resources, options )\\r\\n\\t{\\r\\n\\t\\tif(!resources)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(resources.constructor === Array)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor( var i = 0; i < resources.length; ++i )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar name = resources[i];\\r\\n\\t\\t\\t\\tif( !name || name[0] == \\\":\\\" || name[0] == \\\"_\\\" )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tthis.load( name, options );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse //object\\r\\n\\t\\t\\tfor(var i in resources)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( !i || i[0] == \\\":\\\" || i[0] == \\\"_\\\" )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tthis.load( i, options );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\tthis._total_resources_to_load = this.num_resources_being_loaded;\\r\\n\\t\\tLEvent.trigger( this, \\\"start_loading_resources\\\", this._total_resources_to_load );\\r\\n\\t\\tif(!this._total_resources_to_load) //all resources were already in memory\\r\\n\\t\\t\\tLEvent.trigger( this, \\\"end_loading_resources\\\" );\\r\\n\\t\\treturn this._total_resources_to_load;\\r\\n\\t},\\t\\r\\n\\r\\n\\t/**\\r\\n\\t* Set the base path where all the resources will be fetched (unless they have absolute URL)\\r\\n\\t* By default it will use the website home address\\r\\n\\t*\\r\\n\\t* @method setPath\\r\\n\\t* @param {String} url\\r\\n\\t*/\\r\\n\\tsetPath: function( url )\\r\\n\\t{\\r\\n\\t\\tthis.path = url;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Set a proxy url where all non-local resources will be requested, allows to fetch assets to other servers.\\r\\n\\t* request will be in this form: proxy_url + \\\"/\\\" + url_with_protocol: -> http://myproxy.com/google.com/images/...\\r\\n\\t*\\r\\n\\t* @method setProxy\\r\\n\\t* @param {String} proxy_url\\r\\n\\t*/\\r\\n\\tsetProxy: function( proxy_url )\\r\\n\\t{\\r\\n\\t\\tif(!proxy_url)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.proxy = null;\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( proxy_url.indexOf(\\\"@\\\") != -1 )\\r\\n\\t\\t\\tthis.proxy = location.protocol + \\\"//\\\" + proxy_url.replace(\\\"@\\\", window.location.host );\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.proxy = proxy_url;\\r\\n\\r\\n\\t\\tif(\\ttypeof(LiteGraph) !== \\\"undefined\\\" )\\r\\n\\t\\t\\tLiteGraph.proxy = this.proxy;\\r\\n\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* transform a url to a full url taking into account proxy, virtual file systems and external_repository\\r\\n\\t* used only when requesting a resource to be loaded\\r\\n\\t*\\r\\n\\t* @method getFullURL\\r\\n\\t* @param {String} url\\r\\n\\t* @param {Object} options\\r\\n\\t* @return {String} full url\\r\\n\\t*/\\r\\n\\tgetFullURL: function( url, options )\\r\\n\\t{\\r\\n\\t\\tif(!url)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar pos = url.substr(0,10).indexOf(\\\":\\\");\\r\\n\\t\\tvar protocol = \\\"\\\";\\r\\n\\t\\tif(pos != -1)\\r\\n\\t\\t\\tprotocol = url.substr(0,pos);\\r\\n\\r\\n\\t\\tvar resources_path = this.path;\\r\\n\\r\\n\\t\\t//from scene.external_repository\\r\\n\\t\\tif(this.scene_external_repository) \\r\\n\\t\\t\\tresources_path = this.scene_external_repository;\\r\\n\\r\\n\\t\\tif(options && options.force_local_url)\\r\\n\\t\\t\\tresources_path = \\\".\\\";\\r\\n\\r\\n\\t\\t//used special repository\\r\\n\\t\\tif(options && options.external_repository)\\r\\n\\t\\t\\tresources_path = options.external_repository;\\r\\n\\r\\n\\t\\tif(protocol)\\r\\n\\t\\t{\\r\\n\\t\\t\\tswitch(protocol)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//external urls\\r\\n\\t\\t\\t\\tcase 'http':\\r\\n\\t\\t\\t\\tcase 'https':\\r\\n\\t\\t\\t\\t\\tvar full_url = url;\\r\\n\\t\\t\\t\\t\\tvar extension = this.getExtension( url ).toLowerCase();\\r\\n\\t\\t\\t\\t\\tif(this.proxy && this.skip_proxy_extensions.indexOf( extension ) == -1 && (!options || (options && !options.ignore_proxy)) ) //proxy external files\\r\\n\\t\\t\\t\\t\\t\\treturn this.proxy + url; //this.proxy + url.substr(pos+3); //\\\"://\\\"\\r\\n\\t\\t\\t\\t\\treturn full_url;\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase 'blob':\\r\\n\\t\\t\\t\\t\\treturn url; //special case for local urls like URL.createObjectURL\\r\\n\\t\\t\\t\\tcase '': //strange case\\r\\n\\t\\t\\t\\t\\treturn url;\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\tif(url[0] == \\\":\\\" || url[0] == \\\"_\\\") //local resource\\r\\n\\t\\t\\t\\t\\t\\treturn url;\\r\\n\\t\\t\\t\\t\\t//test for virtual file system address\\r\\n\\t\\t\\t\\t\\tvar root_path = this.virtual_file_systems[ protocol ] || resources_path;\\r\\n\\t\\t\\t\\t\\treturn root_path + \\\"/\\\" + url.substr(pos+1);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\treturn resources_path + \\\"/\\\" + url;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Allows to associate a resource path like \\\"vfs:myfile.png\\\" to an url according to the value before the \\\":\\\".\\r\\n\\t* This way we can have alias for different folders where the assets are stored.\\r\\n\\t* P.e: \\\"e\\\",\\\"http://domain.com\\\" -> will transform \\\"e:myfile.png\\\" in \\\"http://domain.com/myfile.png\\\"\\r\\n\\t*\\r\\n\\t* @method registerFileSystem\\r\\n\\t* @param {String} name the filesystem name (the string before the colons in the path)\\r\\n\\t* @param {String} url the url to attach before \\r\\n\\t*/\\r\\n\\tregisterFileSystem: function(name, url)\\r\\n\\t{\\r\\n\\t\\tthis.virtual_file_systems[ name ] = url;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the resource if it has been loaded, if you want to force to load it, use load\\r\\n\\t*\\r\\n\\t* @method getResource\\r\\n\\t* @param {String} url where the resource is located (if its a relative url it depends on the path attribute)\\r\\n\\t* @param {Function} constructor [optional] allows to specify the class expected for this resource, if the resource doesnt match, it returns null\\r\\n\\t* @return {*} the resource\\r\\n\\t*/\\r\\n\\tgetResource: function( url, constructor )\\r\\n\\t{\\r\\n\\t\\tif(!url)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\turl = this.cleanFullpath( url );\\r\\n\\t\\tif(!constructor)\\r\\n\\t\\t\\treturn this.resources[ url ];\\r\\n\\t\\t\\r\\n\\t\\tvar res = this.resources[ url ];\\r\\n\\t\\tif(res && res.constructor === constructor )\\r\\n\\t\\t\\treturn res;\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the resource type (\\\"Mesh\\\",\\\"Texture\\\",\\\"Material\\\",\\\"SceneNode\\\",...) of a given resource\\r\\n\\t*\\r\\n\\t* @method getResourceType\\r\\n\\t* @param {*} resource\\r\\n\\t* @return {String} the type in string format\\r\\n\\t*/\\r\\n\\tgetResourceType: function( resource )\\r\\n\\t{\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tif(resource.object_class)\\r\\n\\t\\t\\treturn resource.object_class;\\r\\n\\t\\tif(resource.constructor.resource_type)\\r\\n\\t\\t\\treturn resource.constructor.resource_type;\\r\\n\\t\\treturn LS.getObjectClassName( resource );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns an object containig all the resources and its data (used to export resources)\\r\\n\\t*\\r\\n\\t* @method getResourcesData\\r\\n\\t* @param {Array} resource_names an array containing the resources names\\r\\n\\t* @param {bool} allow_files [optional] used to allow to retrieve the data in File or Blob, otherwise only String and ArrayBuffer is supported\\r\\n\\t* @return {Object} object with name:data\\r\\n\\t*/\\r\\n\\tgetResourcesData: function( resource_names, allow_files )\\r\\n\\t{\\r\\n\\t\\tif( resource_names.constructor !== Array )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"getResourcesData expects Array\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar result = {};\\r\\n\\r\\n\\t\\tfor(var i = 0; i < resource_names.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar res_name = resource_names[i];\\r\\n\\t\\t\\tvar resource = LS.ResourcesManager.resources[ res_name ];\\r\\n\\t\\t\\tif(!resource)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar data = null;\\r\\n\\t\\t\\tif(resource._original_data) //must be string or bytes\\r\\n\\t\\t\\t\\tdata = resource._original_data;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar data_info = LS.Resource.getDataToStore( resource );\\r\\n\\t\\t\\t\\tdata = data_info.data;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(!data)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Wrong data in resource\\\");\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(data.constructor === Blob || data.constructor === File)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( !allow_files && (!data.data || data.data.constructor !== ArrayBuffer) )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"Not support to store File or Blob, please, use ArrayBuffer\\\");\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tdata = data.data; //because files have an arraybuffer with the data if it was read\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tresult[ res_name ] = data;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\r\\n\\tcreateResource: function( filename, data, must_register )\\r\\n\\t{\\r\\n\\t\\tvar resource = null;\\r\\n\\r\\n\\t\\tvar extension = this.getExtension( filename );\\r\\n\\t\\t//get all the info about this file format\\r\\n\\t\\tvar format_info = null;\\r\\n\\t\\tif(extension)\\r\\n\\t\\t\\tformat_info = LS.Formats.supported[ extension ];\\r\\n\\r\\n\\t\\t//has this resource an special class specified?\\r\\n\\t\\tif(format_info && format_info.resourceClass)\\r\\n\\t\\t\\tresource = new format_info.resourceClass();\\r\\n\\t\\telse //otherwise create a generic LS.Resource (they store data or scripts)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//if we already have a LS.Resource, reuse it (this is to avoid garbage and solve a problem with the editor\\r\\n\\t\\t\\tvar old_res = this.resources[ filename ];\\r\\n\\t\\t\\tif( old_res && old_res.constructor === LS.Resource )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tresource = old_res;\\r\\n\\t\\t\\t\\tdelete resource._original_data;\\r\\n\\t\\t\\t\\tdelete resource._original_file;\\r\\n\\t\\t\\t\\tresource._modified = false;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tresource = new LS.Resource();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(data)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(resource.setData)\\r\\n\\t\\t\\t\\tresource.setData( data, true );\\r\\n\\t\\t\\telse if(resource.fromData)\\r\\n\\t\\t\\t\\tresource.fromData( data );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthrow(\\\"Resource without setData, cannot assign\\\");\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(must_register)\\r\\n\\t\\t\\tLS.ResourcesManager.registerResource( filename, resource );\\r\\n\\r\\n\\t\\treturn resource;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Marks the resource as modified, used in editor to know when a resource data should be updated\\r\\n\\t*\\r\\n\\t* @method resourceModified\\r\\n\\t* @param {Object} resource\\r\\n\\t*/\\r\\n\\tresourceModified: function( resource )\\r\\n\\t{\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(resource.constructor === String)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"resourceModified parameter must be a resource, not a string\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//if the file has been modified we cannot keep using the original data\\r\\n\\t\\tdelete resource._original_data;\\r\\n\\t\\tdelete resource._original_file;\\r\\n\\r\\n\\t\\tresource._version = (resource._version || 0) + 1;\\r\\n\\r\\n\\t\\tif( resource.remotepath )\\r\\n\\t\\t\\tresource._modified = true;\\r\\n\\r\\n\\t\\tLEvent.trigger(this, \\\"resource_modified\\\", resource );\\r\\n\\r\\n\\t\\t//TODO: from_prefab and from_pack should be the sabe property\\r\\n\\t\\tif(resource.from_pack)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif (resource.from_pack.constructor === String)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar pack = LS.ResourcesManager.getResource( resource.from_pack );\\r\\n\\t\\t\\t\\tif(pack)\\r\\n\\t\\t\\t\\t\\tthis.resourceModified(pack);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tif(resource.from_prefab)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif (resource.from_prefab.constructor === String)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar prefab = LS.ResourcesManager.getResource( resource.from_prefab );\\r\\n\\t\\t\\t\\tif(prefab)\\r\\n\\t\\t\\t\\t\\tthis.resourceModified(prefab);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Unmarks the resource as modified\\r\\n\\t*\\r\\n\\t* @method resourceSaved\\r\\n\\t* @param {Object} resource\\r\\n\\t*/\\r\\n\\tresourceSaved: function(resource)\\r\\n\\t{\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tdelete resource._modified;\\r\\n\\t\\tresource.remotepath = resource.fullpath;\\r\\n\\t\\tLEvent.trigger(this, \\\"resource_saved\\\", resource );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Loads a generic resource, the type will be infered from the extension, if it is json or wbin it will be processed\\r\\n\\t* Do not use to load regular files (txts, csv, etc), instead use the LS.Network methods\\r\\n\\t*\\r\\n\\t* @method load\\r\\n\\t* @param {String} url where the resource is located (if its a relative url it depends on the path attribute)\\r\\n\\t* @param {Object}[options={}] options to apply to the loaded resource when processing it { force: to force a reload }\\r\\n\\t* @param {Function} [on_complete=null] callback when the resource is loaded and cached, params: callback( resource, url ) //( url, resource, options )\\r\\n\\t* @param {Boolean} [force_load=false] if true it will load the resource, even if it already exists\\r\\n\\t* @param {Function} [on_error=null] callback in case the file wasnt found\\r\\n\\t*/\\r\\n\\tload: function( url, options, on_complete, force_load, on_error )\\r\\n\\t{\\r\\n\\t\\tif(!url)\\r\\n\\t\\t\\treturn console.error(\\\"LS.ResourcesManager.load requires url\\\");\\r\\n\\r\\n\\t\\t//parameter swap...\\r\\n\\t\\tif(options && options.constructor === Function && !on_complete )\\r\\n\\t\\t{\\r\\n\\t\\t\\ton_complete = options;\\r\\n\\t\\t\\toptions = null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//if we already have it, then nothing to do\\r\\n\\t\\tvar resource = this.resources[url];\\r\\n\\t\\tif( resource != null && !resource.is_preview && (!options || !options.force) && !force_load )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\ton_complete(resource,url);\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\toptions = options || {};\\r\\n\\r\\n\\t\\t//extract the filename extension\\r\\n\\t\\tvar extension = this.getExtension( url );\\r\\n\\t\\tif(!extension && !this.resources[url] ) //unknown file type and didnt came from a pack or prefab\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"Cannot load a file without extension: \\\" + url );\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//we already tryed to load it and we couldnt find it, better not try again\\r\\n\\t\\tif( this.resources_not_found[url] )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//if it is already being loaded, then add the callback and wait\\r\\n\\t\\tif(this.resources_being_loaded[url])\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\tthis.resources_being_loaded[url].push( { options: options, callback: on_complete } );\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(this.resources_being_processed[url])\\r\\n\\t\\t\\treturn; //nothing to load, just waiting for the callback to process it\\r\\n\\r\\n\\t\\tif(!this.allow_base_files && url.indexOf(\\\"/\\\") == -1)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!this._parsing_local_file) //to avoid showing this warning when parsing scenes with local resources\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Cannot load resource, filename has no folder and LS.ResourcesManager.allow_base_files is set to false: \\\", url );\\r\\n\\t\\t\\treturn; //this is not a valid file to load\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//otherwise we have to load it\\r\\n\\t\\t//set the callback\\r\\n\\t\\tthis.resources_being_loaded[url] = [{options: options, callback: on_complete}];\\r\\n\\r\\n\\t\\tLEvent.trigger( LS.ResourcesManager, \\\"resource_loading\\\", url );\\r\\n\\t\\t//send an event if we are starting to load (used for loading icons)\\r\\n\\t\\t//if(this.num_resources_being_loaded == 0)\\r\\n\\t\\t//\\tLEvent.trigger( LS.ResourcesManager,\\\"start_loading_resources\\\", url );\\r\\n\\t\\tthis.num_resources_being_loaded++;\\r\\n\\t\\tvar full_url = this.getFullURL(url);\\r\\n\\r\\n\\t\\t//which type?\\r\\n\\t\\tvar format_info = LS.Formats.getFileFormatInfo( extension );\\r\\n\\t\\tif(format_info && format_info.has_preview && !options.is_preview )\\r\\n\\t\\t\\tLEvent.trigger( this, \\\"load_resource_preview\\\", url );\\r\\n\\r\\n\\t\\t//create the ajax request\\r\\n\\t\\tvar settings = {\\r\\n\\t\\t\\turl: full_url,\\r\\n\\t\\t\\tsuccess: function(response){\\r\\n\\t\\t\\t\\tLS.ResourcesManager.processResource( url, response, options, LS.ResourcesManager._resourceLoadedEnd, true );\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\terror: function(err) { \\t\\r\\n\\t\\t\\t\\tLS.ResourcesManager._resourceLoadedError(url,err);\\r\\n\\t\\t\\t\\tif(on_error)\\r\\n\\t\\t\\t\\t\\ton_error(url);\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\tprogress: function(e) { \\r\\n\\t\\t\\t\\tvar partial_load = 0;\\r\\n\\t\\t\\t\\tif(e.total) //sometimes we dont have the total so we dont know the amount\\r\\n\\t\\t\\t\\t\\tpartial_load = e.loaded / e.total;\\r\\n\\t\\t\\t\\tif( LEvent.hasBind( LS.ResourcesManager, \\\"resource_loading_progress\\\" ) ) //used to avoid creating objects during loading\\r\\n\\t\\t\\t\\t\\tLEvent.trigger( LS.ResourcesManager, \\\"resource_loading_progress\\\", { url: url, event: e, progress: partial_load } );\\r\\n\\t\\t\\t\\tif( LEvent.hasBind( LS.ResourcesManager, \\\"loading_resources_progress\\\" ) ) //used to avoid creating objects during loading\\r\\n\\t\\t\\t\\t\\tLEvent.trigger( LS.ResourcesManager, \\\"loading_resources_progress\\\", 1.0 - (LS.ResourcesManager.num_resources_being_loaded - partial_load) / LS.ResourcesManager._total_resources_to_load );\\r\\n\\t\\t\\t}\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\t//force no cache by request\\r\\n\\t\\tsettings.nocache = this.ignore_cache || (this.force_nocache_extensions.indexOf[ extension ] != -1) || this.nocache_files[ url ];\\r\\n\\r\\n\\t\\t//in case we need to force a response format \\r\\n\\t\\tvar format_info = LS.Formats.supported[ extension ];\\r\\n\\t\\tif( format_info )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( format_info.dataType ) //force dataType, otherwise it will be set by http server\\r\\n\\t\\t\\t\\tsettings.dataType = format_info.dataType;\\r\\n\\t\\t\\tif( format_info.mimeType ) //force mimeType\\r\\n\\t\\t\\t\\tsettings.mimeType = format_info.mimeType;\\r\\n\\t\\t\\tif( format_info[\\\"native\\\"] )\\r\\n\\t\\t\\t\\tsettings.dataType = null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//send the REQUEST\\r\\n\\t\\tLS.Network.request( settings ); //ajax call\\r\\n\\t\\treturn false;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Takes some resource data and transforms it to a resource (and Object ready to be used by the engine) and REGISTERs it in the ResourcesManager.\\r\\n\\t* In most cases the process involves parsing and uploading to the GPU\\r\\n\\t* It is called for every single resource that comes from an external source (URL) right after being loaded\\r\\n\\t*\\r\\n\\t* @method processResource\\r\\n\\t* @param {String} url where the resource is located (if its a relative url it depends on the path attribute)\\r\\n\\t* @param {*} data the data of the resource (could be string, arraybuffer, image... )\\r\\n\\t* @param {Object}[options={}] options to apply to the loaded resource\\r\\n\\t* @param {Function} on_complete once the resource is ready\\r\\n\\t*/\\r\\n\\tprocessResource: function( url, data, options, on_complete, was_loaded )\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\r\\n\\t\\tif(options && options.constructor !== Object)\\r\\n\\t\\t\\tthrow(\\\"processResource options must be object\\\");\\r\\n\\t\\tif( data === null || data === undefined )\\r\\n\\t\\t\\tthrow(\\\"No data found when processing resource: \\\" + url);\\r\\n\\r\\n\\t\\tvar resource = null;\\r\\n\\t\\tvar extension = this.getExtension( url );\\r\\n\\t\\t//get all the info about this file format\\r\\n\\t\\tvar format_info = null;\\r\\n\\t\\t\\r\\n\\t\\tif(extension)\\r\\n\\t\\t\\tformat_info = LS.Formats.supported[ extension ];\\r\\n\\r\\n\\t\\t//callback to embed a parameter, ugly but I dont see a work around to create this\\r\\n\\t\\tvar process_final = function( url, resource, options ){\\r\\n\\t\\t\\tif(!resource)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tLS.ResourcesManager._resourceLoadedEnd( url, null ); //to remove it from loading \\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//convert format\\r\\n\\t\\t\\tif( format_info && format_info.convert_to && extension != format_info.convert_to )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\turl += \\\".\\\" + format_info.convert_to;\\r\\n\\t\\t\\t\\tresource.filename += \\\".\\\" + format_info.convert_to;\\r\\n\\t\\t\\t\\tif( resource.fullpath )\\r\\n\\t\\t\\t\\t\\tresource.fullpath += \\\".\\\" + format_info.convert_to;\\r\\n\\t\\t\\t\\tif(options.filename)\\r\\n\\t\\t\\t\\t\\toptions.filename += \\\".\\\" + format_info.convert_to;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//apply last changes: add to containers, remove from pending_loads, add special properties like fullpath, load associated resources...\\r\\n\\t\\t\\tLS.ResourcesManager.processFinalResource( url, resource, options, on_complete, was_loaded );\\r\\n\\r\\n\\t\\t\\t//Keep original file inside the resource in case we want to save it\\r\\n\\t\\t\\tif(LS.ResourcesManager.keep_files && (data.constructor == ArrayBuffer || data.constructor == String) && (!resource._original_data && !resource._original_file) )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( extension == LS.ResourcesManager.getExtension( resource.filename ) )\\r\\n\\t\\t\\t\\t\\tresource._original_data = data;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//this.resources_being_loaded[url] = [];\\r\\n\\t\\tthis.resources_being_processed[url] = true;\\r\\n\\r\\n\\t\\t//no extension, then or it is a JSON, or an object with object_class or a WBin\\r\\n\\t\\tif(!extension)\\r\\n\\t\\t\\treturn this.processDataResource( url, data, options, process_final );\\r\\n\\r\\n\\r\\n\\t\\t// PRE-PROCESSING Stage (transform raw data in a resource) \\r\\n\\t\\t// *******************************************************\\r\\n\\r\\n\\t\\t//special preprocessor\\r\\n\\t\\tvar preprocessor_callback = this.resource_pre_callbacks[ extension.toLowerCase() ];\\r\\n\\t\\tif( preprocessor_callback )\\r\\n\\t\\t{\\r\\n\\t\\t\\t//this callback should return the resource or true if it is processing it\\r\\n\\t\\t\\tvar resource = preprocessor_callback( url, data, options, process_final );\\r\\n\\t\\t\\tif(resource === true)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tif( resource )\\r\\n\\t\\t\\t\\tprocess_final( url, resource, options );\\r\\n\\t\\t\\telse //resource is null\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"resource preprocessor_callback returned null\\\");\\r\\n\\t\\t\\t\\tthis._resourceLoadedError( url, \\\"Resource couldn't be processed\\\" );\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse if( format_info && (format_info.type || format_info.parse) ) //or you can rely on the format info parser\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar resource = null;\\r\\n\\t\\t\\tswitch( format_info.type )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase \\\"scene\\\":\\r\\n\\t\\t\\t\\t\\tresource = LS.ResourcesManager.processScene( url, data, options, process_final );\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"mesh\\\":\\r\\n\\t\\t\\t\\t\\tresource = LS.ResourcesManager.processTextMesh( url, data, options, process_final );\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"texture\\\":\\r\\n\\t\\t\\t\\tcase \\\"image\\\":\\r\\n\\t\\t\\t\\t\\tresource = LS.ResourcesManager.processImage( url, data, options, process_final );\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"data\\\":\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\tif( format_info.parse )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t//console.warn(\\\"Fallback to default parser\\\");\\r\\n\\t\\t\\t\\t\\t\\tvar resource = format_info.parse( data );\\r\\n\\t\\t\\t\\t\\t\\tif(resource)\\r\\n\\t\\t\\t\\t\\t\\t\\tprocess_final( url, resource, options );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\tconsole.warn(\\\"Format Info without parse function\\\");\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//we have a resource\\r\\n\\t\\t\\tif( resource && resource !== true )\\r\\n\\t\\t\\t\\tprocess_final( url, resource, options );\\r\\n\\t\\t}\\r\\n\\t\\telse if( format_info && format_info.resourceClass) //this format has a class associated\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar resource = new format_info.resourceClass();\\r\\n\\t\\t\\tif(resource.fromData)\\r\\n\\t\\t\\t\\tresource.fromData( data );\\r\\n\\t\\t\\telse if(resource.configure)\\r\\n\\t\\t\\t\\tresource.configure( JSON.parse(data) );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tconsole.error(\\\"Resource Class doesnt have a function to process data after loading: \\\", format_info.resourceClass.name );\\r\\n\\r\\n\\t\\t\\t//we have a resource\\r\\n\\t\\t\\tif( resource && resource !== true )\\r\\n\\t\\t\\t\\tprocess_final( url, resource, options );\\r\\n\\t\\t}\\r\\n\\t\\telse //or just store the resource as a plain data buffer\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar resource = LS.ResourcesManager.createResource( url, data );\\r\\n\\t\\t\\tif(resource)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tresource.filename = resource.fullpath = url;\\r\\n\\t\\t\\t\\tprocess_final( url, resource, options );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Takes a resource instance, and adds some extra properties and register it\\r\\n\\t*\\r\\n\\t* @method processFinalResource\\r\\n\\t* @param {String} url where the resource is located (if its a relative url it depends on the path attribute)\\r\\n\\t* @param {*} the resource class\\r\\n\\t* @param {Object}[options={}] options to apply to the loaded resource\\r\\n\\t* @param {Function} on_complete once the resource is ready\\r\\n\\t*/\\r\\n\\tprocessFinalResource: function( fullpath, resource, options, on_complete, was_loaded )\\r\\n\\t{\\r\\n\\t\\tif(!resource || resource.constructor === String)\\r\\n\\t\\t\\treturn LS.ResourcesManager._resourceLoadedError( fullpath, \\\"error processing the resource\\\" );\\r\\n\\r\\n\\t\\t//EXTEND add properties as basic resource ********************************\\r\\n\\t\\tresource.filename = fullpath;\\r\\n\\t\\tif(options.filename) //used to overwrite\\r\\n\\t\\t\\tresource.filename = options.filename;\\r\\n\\t\\tif(!options.is_local)\\r\\n\\t\\t\\tresource.fullpath = fullpath;\\r\\n\\t\\telse\\r\\n\\t\\t\\tfullpath = resource.fullpath = resource.filename;\\r\\n\\t\\tif(options.from_prefab)\\r\\n\\t\\t\\tresource.from_prefab = options.from_prefab;\\r\\n\\t\\tif(options.from_pack)\\r\\n\\t\\t\\tresource.from_pack = options.from_pack;\\r\\n\\t\\tif(was_loaded)\\r\\n\\t\\t\\tresource.remotepath = fullpath; //it was url but is the same as fullpath?\\r\\n\\t\\tif(options.is_preview)\\r\\n\\t\\t\\tresource.is_preview = true;\\r\\n\\r\\n\\t\\t//Remove from temporal containers\\r\\n\\t\\tif( LS.ResourcesManager.resources_being_processed[ fullpath ] )\\r\\n\\t\\t\\tdelete LS.ResourcesManager.resources_being_processed[ fullpath ];\\r\\n\\r\\n\\t\\t//Load associated resources (some resources like LS.Prefab or LS.Scene have other resources associated that must be loaded too)\\r\\n\\t\\tif( resource.getResources )\\r\\n\\t\\t\\tLS.ResourcesManager.loadResources( resource.getResources({}) );\\r\\n\\r\\n\\t\\t//REGISTER adds to containers *******************************************\\r\\n\\t\\tLS.ResourcesManager.registerResource( fullpath, resource );\\r\\n\\t\\tif(options.preview_of)\\r\\n\\t\\t\\tLS.ResourcesManager.registerResource( options.preview_of, resource );\\r\\n\\r\\n\\t\\t//POST-PROCESS is done from inside registerResource, this way we ensure that every registered resource\\r\\n\\t\\t//has been post-processed, not only the loaded ones.\\r\\n\\r\\n\\t\\t//READY ***************************************\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete( fullpath, resource, options );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Stores the resource inside the manager containers. This way it will be retrieveble by anybody who needs it.\\r\\n\\t*\\r\\n\\t* @method registerResource\\r\\n\\t* @param {String} filename fullpath \\r\\n\\t* @param {Object} resource \\r\\n\\t*/\\r\\n\\tregisterResource: function( filename, resource )\\r\\n\\t{\\r\\n\\t\\tif(!filename || !resource)\\r\\n\\t\\t\\tthrow(\\\"registerResource missing filename or resource\\\");\\r\\n\\r\\n\\t\\t//test filename is valid (alphanumeric with spaces, dot or underscore and dash and slash\\r\\n\\t\\tif( filename[0] != \\\":\\\" && this.valid_resource_name_reg.test( filename ) == false )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( filename.substr(0,4) != \\\"http\\\" )\\r\\n\\t\\t\\t\\tconsole.warn( \\\"invalid filename for resource: \\\", filename );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//clean up the filename (to avoid problems with //)\\r\\n\\t\\tfilename = this.cleanFullpath( filename );\\r\\n\\r\\n\\t\\tif( this.resources[ filename ] === resource )\\r\\n\\t\\t\\treturn; //already registered\\r\\n\\r\\n\\t\\tif(resource.is_preview && this.resources[ filename ] )\\r\\n\\t\\t\\treturn; //previews cannot overwrite resources\\r\\n\\r\\n\\t\\tresource.filename = filename; //filename is a given name\\r\\n\\t\\t//resource.fullpath = filename; //fullpath only if they are in the server\\r\\n\\r\\n\\t\\t//Compute resource type\\r\\n\\t\\tif(!resource.object_class)\\r\\n\\t\\t\\tresource.object_class = LS.getObjectClassName( resource );\\r\\n\\t\\tvar type = resource.object_class;\\r\\n\\t\\tif(resource.constructor.resource_type)\\r\\n\\t\\t\\ttype = resource.constructor.resource_type;\\r\\n\\r\\n\\t\\t//Add to global container\\r\\n\\t\\tthis.resources[ filename ] = resource;\\r\\n\\r\\n\\t\\t//POST-PROCESS resources extra final action (done here to ensure any registered resource is post-processed)\\r\\n\\t\\tvar post_callback = this.resource_post_callbacks[ type ];\\r\\n\\t\\tif(post_callback)\\r\\n\\t\\t\\tpost_callback( filename, resource );\\r\\n\\r\\n\\t\\t//send message to inform new resource is available\\r\\n\\t\\tif(!resource.is_preview)\\r\\n\\t\\t\\tLEvent.trigger(this,\\\"resource_registered\\\", resource);\\r\\n\\r\\n\\t\\tLS.GlobalScene.requestFrame(); //render scene\\r\\n\\t},\\t\\r\\n\\r\\n\\t/**\\r\\n\\t* removes the resources from all the containers\\r\\n\\t*\\r\\n\\t* @method unregisterResource\\r\\n\\t* @param {String} filename \\r\\n\\t* @return {boolean} true is removed, false if not found\\r\\n\\t*/\\r\\n\\tunregisterResource: function(filename)\\r\\n\\t{\\r\\n\\t\\tvar resource = this.resources[filename];\\r\\n\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\treturn false; //not found\\r\\n\\r\\n\\t\\tdelete this.resources[filename];\\r\\n\\r\\n\\t\\t//ugly: too hardcoded, maybe implement unregister_callbacks\\r\\n\\t\\tif( this.meshes[filename] )\\r\\n\\t\\t\\tdelete this.meshes[ filename ];\\r\\n\\t\\tif( this.textures[filename] )\\r\\n\\t\\t\\tdelete this.textures[ filename ];\\r\\n\\t\\tif( this.materials[filename] )\\r\\n\\t\\t\\tdelete this.materials[ filename ];\\r\\n\\r\\n\\t\\tif(resource.constructor === LS.Pack || resource.constructor === LS.Prefab)\\r\\n\\t\\t\\tresource.setResourcesLink(null);\\r\\n\\r\\n\\t\\tLEvent.trigger(this,\\\"resource_unregistered\\\", resource);\\r\\n\\t\\tLS.GlobalScene.requestFrame(); //render scene\\r\\n\\t\\treturn true;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Used to load files and get them as File (or Blob)\\r\\n\\t* @method getURLasFile\\r\\n\\t* @param {String} filename \\r\\n\\t* @return {File} the file\\r\\n\\t*/\\r\\n\\tgetURLasFile: function( url, on_complete )\\r\\n\\t{\\r\\n\\t\\tvar oReq = new XMLHttpRequest();\\r\\n\\t\\toReq.open(\\\"GET\\\", this.getFullURL(url), true);\\r\\n\\t\\toReq.responseType = \\\"blob\\\";\\r\\n\\t\\toReq.onload = function(oEvent) {\\r\\n\\t\\t var blob = oReq.response;\\r\\n\\t\\t if(on_complete)\\r\\n\\t\\t\\t on_complete(blob, url);\\r\\n\\t\\t};\\r\\n\\t\\toReq.send();\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Changes the name of a resource and sends an event to all components to change it accordingly\\r\\n\\t* @method renameResource\\r\\n\\t* @param {String} old_name \\r\\n\\t* @param {String} new_name\\r\\n\\t* @param {Boolean} [skip_event=false] ignore sending an event to all components to rename the resource\\r\\n\\t* @return {boolean} if the file was found\\r\\n\\t*/\\r\\n\\trenameResource: function( old_name, new_name, skip_event )\\t\\r\\n\\t{\\r\\n\\t\\tvar res = this.resources[ old_name ];\\r\\n\\t\\tif(!res)\\r\\n\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\tif(this.resources[ new_name ])\\r\\n\\t\\t\\tconsole.warn(\\\"There is a resource already with this name, overwritting it: \\\" + new_name );\\r\\n\\r\\n\\t\\tres.filename = new_name;\\r\\n\\t\\tif(res.fullpath)\\r\\n\\t\\t\\tres.fullpath = new_name;\\r\\n\\r\\n\\t\\tthis.resources[new_name] = res;\\r\\n\\t\\tdelete this.resources[ old_name ];\\r\\n\\r\\n\\t\\t//inform everybody in the scene\\r\\n\\t\\tif(!skip_event)\\r\\n\\t\\t\\tLS.GlobalScene.sendResourceRenamedEvent( old_name, new_name, res );\\r\\n\\r\\n\\t\\t//inform prefabs and packs...\\r\\n\\t\\tfor(var i in this.resources)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar alert_res = this.resources[i];\\r\\n\\t\\t\\tif( alert_res != res && alert_res.onResourceRenamed )\\r\\n\\t\\t\\t\\tif( alert_res.onResourceRenamed( old_name, new_name, res ) )\\r\\n\\t\\t\\t\\t\\tthis.resourceModified(alert_res);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//ugly: too hardcoded\\r\\n\\t\\tif( this.meshes[old_name] ) {\\r\\n\\t\\t\\tdelete this.meshes[ old_name ];\\r\\n\\t\\t\\tthis.meshes[ new_name ] = res;\\r\\n\\t\\t}\\r\\n\\t\\tif( this.textures[old_name] ) {\\r\\n\\t\\t\\tdelete this.textures[ old_name ];\\r\\n\\t\\t\\tthis.textures[ new_name ] = res;\\r\\n\\t\\t}\\r\\n\\t\\tif( this.materials[old_name] ) {\\r\\n\\t\\t\\tdelete this.materials[ old_name ];\\r\\n\\t\\t\\tthis.materials[ new_name ] = res;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//in case somebody needs to know where a resource has gone\\r\\n\\t\\tthis.resources_renamed_recently[ old_name ] = new_name;\\r\\n\\r\\n\\t\\tif(!skip_event)\\r\\n\\t\\t\\tLEvent.trigger( LS.ResourcesManager, \\\"resource_renamed\\\", [ old_name, new_name, res ] );\\r\\n\\t\\treturn true;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Tells if it is loading resources (or an specific resource)\\r\\n\\t*\\r\\n\\t* @method isLoading\\r\\n\\t* @return {Boolean}\\r\\n\\t*/\\r\\n\\tisLoading: function( fullpath )\\r\\n\\t{\\r\\n\\t\\tif(!fullpath)\\r\\n\\t\\t\\treturn this.num_resources_being_loaded > 0;\\r\\n\\t\\tif(this.resources_being_loaded[ fullpath ] || this.resources_being_processed[ fullpath ])\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\treturn false;\\r\\n\\t},\\t\\r\\n\\r\\n\\t/**\\r\\n\\t* forces to try to reload again resources not found\\r\\n\\t*\\r\\n\\t* @method isLoading\\r\\n\\t* @return {Boolean}\\r\\n\\t*/\\r\\n\\tclearNotFoundResources: function()\\r\\n\\t{\\r\\n\\t\\tthis.resources_not_found = {};\\r\\n\\t},\\r\\n\\r\\n\\tcomputeImageMetadata: function(texture)\\r\\n\\t{\\r\\n\\t\\tvar metadata = { width: texture.width, height: texture.height };\\r\\n\\t\\treturn metadata;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* returns a mesh resource if it is loaded\\r\\n\\t*\\r\\n\\t* @method getMesh\\r\\n\\t* @param {String} filename \\r\\n\\t* @return {Mesh}\\r\\n\\t*/\\r\\n\\tgetMesh: function(name) {\\r\\n\\t\\tif(!name)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tif(name.constructor === String)\\r\\n\\t\\t\\treturn this.meshes[name];\\r\\n\\t\\tif(name.constructor === GL.Mesh)\\r\\n\\t\\t\\treturn name;\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* returns a texture resource if it is loaded\\r\\n\\t*\\r\\n\\t* @method getTexture\\r\\n\\t* @param {String} filename could be a texture itself in which case returns the same texture\\r\\n\\t* @return {Texture} \\r\\n\\t*/\\r\\n\\tgetTexture: function(name) {\\r\\n\\t\\tif(!name)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tif(name.constructor === String)\\r\\n\\t\\t\\treturn this.textures[name];\\r\\n\\t\\tif(name.constructor === GL.Texture)\\r\\n\\t\\t\\treturn name;\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\tgetMaterial: function( name_or_id )\\r\\n\\t{\\r\\n\\t\\tif(!name_or_id)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(name_or_id[0] == \\\"@\\\")\\r\\n\\t\\t\\treturn this.materials_by_uid[ name_or_id ];\\r\\n\\t\\treturn this.materials[ name_or_id ];\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Binds a callback for when a resource is loaded (in case you need to do something special)\\r\\n\\t*\\r\\n\\t* @method onceLoaded\\r\\n\\t* @param {String} fullpath of the resource you want to get the notification once is loaded\\r\\n\\t* @param {Function} callback the function to call, it will be called as callback( fullpath, resource )\\r\\n\\t* @return (number) index of the position in the array, use this index to cancel the event \\r\\n\\t*/\\r\\n\\tonceLoaded: function( fullpath, callback )\\r\\n\\t{\\r\\n\\t\\tvar array = this.resource_once_callbacks[ fullpath ];\\r\\n\\t\\tif(!array)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.resource_once_callbacks[ fullpath ] = [ callback ];\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//avoid repeating\\r\\n\\t\\tif( array.indexOf( callback ) != -1 )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tarray.push( callback );\\r\\n\\t\\treturn array.length - 1;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Cancels the binding of a onceLoaded\\r\\n\\t*\\r\\n\\t* @method cancelOnceLoaded\\r\\n\\t* @param {String} fullpath fullpath of the resource you want to cancel the binding\\r\\n\\t* @param {number} index the index of the callback to cancel (as it was returned by onceLoaded)\\r\\n\\t*/\\r\\n\\tcancelOnceLoaded: function( fullpath, index )\\r\\n\\t{\\r\\n\\t\\tvar array = this.resource_once_callbacks[ fullpath ];\\r\\n\\t\\tif(!array)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tarray[ index ] = null;\\r\\n\\t},\\r\\n\\r\\n\\t//*************************************\\r\\n\\r\\n\\t//Called after a resource has been loaded and processed\\r\\n\\t_resourceLoadedEnd: function(url,res)\\r\\n\\t{\\r\\n\\t\\tif( LS.ResourcesManager.debug )\\r\\n\\t\\t\\tconsole.log(\\\"RES: \\\" + url + \\\" ---> \\\" + LS.ResourcesManager.num_resources_being_loaded);\\r\\n\\r\\n\\t\\tif(res)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//trigger all associated load callbacks\\r\\n\\t\\t\\tvar callbacks_array = LS.ResourcesManager.resources_being_loaded[url];\\r\\n\\t\\t\\tif(callbacks_array)\\r\\n\\t\\t\\t\\tfor(var i = 0; i < callbacks_array.length; ++i )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif( callbacks_array[i].callback != null )\\r\\n\\t\\t\\t\\t\\t\\tcallbacks_array[i].callback( res, url );\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//triggers 'once' callbacks\\r\\n\\t\\t\\tvar callbacks_array = LS.ResourcesManager.resource_once_callbacks[url];\\r\\n\\t\\t\\tif(callbacks_array)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var i = 0; i < callbacks_array.length; ++i)\\r\\n\\t\\t\\t\\t\\tif(callbacks_array[i]) //could be null if it has been canceled\\r\\n\\t\\t\\t\\t\\t\\tcallbacks_array[i](url, res);\\r\\n\\t\\t\\t\\tdelete LS.ResourcesManager.resource_once_callbacks[url];\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//two pases, one for launching, one for removing\\r\\n\\t\\tif( LS.ResourcesManager.resources_being_loaded[url] )\\r\\n\\t\\t{\\r\\n\\t\\t\\tdelete LS.ResourcesManager.resources_being_loaded[url];\\r\\n\\t\\t\\tLS.ResourcesManager.num_resources_being_loaded--;\\r\\n\\t\\t\\tif(res)\\r\\n\\t\\t\\t\\tLEvent.trigger( LS.ResourcesManager, \\\"resource_loaded\\\", url );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tLEvent.trigger( LS.ResourcesManager, \\\"resource_problem_loading\\\", url );\\r\\n\\t\\t\\tLEvent.trigger( LS.ResourcesManager, \\\"loading_resources_progress\\\", 1.0 - LS.ResourcesManager.num_resources_being_loaded / LS.ResourcesManager._total_resources_to_load );\\r\\n\\t\\t\\tif( LS.ResourcesManager.num_resources_being_loaded == 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tLEvent.trigger( LS.ResourcesManager, \\\"end_loading_resources\\\", true);\\r\\n\\t\\t\\t\\tLS.ResourcesManager._total_resources_to_load = 0;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//request frame\\r\\n\\t\\tLS.GlobalScene.requestFrame(); \\r\\n\\t},\\r\\n\\r\\n\\t_resourceLoadedError: function( url, error )\\r\\n\\t{\\r\\n\\t\\tconsole.log(\\\"Error loading \\\" + url);\\r\\n\\t\\tdelete LS.ResourcesManager.resources_being_loaded[url];\\r\\n\\t\\tdelete LS.ResourcesManager.resource_once_callbacks[url];\\r\\n\\t\\tLS.ResourcesManager.resources_not_found[url] = true;\\r\\n\\t\\tLEvent.trigger( LS.ResourcesManager, \\\"resource_not_found\\\", url);\\r\\n\\t\\tLS.ResourcesManager.num_resources_being_loaded--;\\r\\n\\t\\tif( LS.ResourcesManager.num_resources_being_loaded == 0 )\\r\\n\\t\\t\\tLEvent.trigger( LS.ResourcesManager, \\\"end_loading_resources\\\", false);\\r\\n\\t\\t\\t//$(ResourcesManager).trigger(\\\"end_loading_resources\\\");\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.RM = LS.ResourcesManager = ResourcesManager;\\r\\n\\r\\nLS.getTexture = function( name_or_texture ) {\\r\\n\\treturn LS.ResourcesManager.getTexture( name_or_texture );\\r\\n}\\t\\r\\n\\r\\n\\r\\n// Resources readers and processors *********************************************\\r\\n// When loading resources there are two stages:\\r\\n// * Pre-process: extract from a container, parse raw data and transform it in a LS resource class (Texture,Mesh,SceneNode,Resource, ...)\\r\\n// * Post-processed: validate, add extra metadata, and register\\r\\n// This actions depend on the resource type, and format, and it is open so future formats are easy to implement.\\r\\n\\r\\n//global formats: take a file and extract info\\r\\nLS.ResourcesManager.registerResourcePreProcessor(\\\"wbin\\\", function( filename, data, options) {\\r\\n\\t//this object has already been expanded, it happens with objects created from parsers that encode the wbin extension\\r\\n\\tif(data.constructor === Object && data.object_class )\\r\\n\\t{\\r\\n\\t\\tif( LS.Classes[ data.object_class ] )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar ctor = LS.Classes[ data.object_class ] || window[ data.object_class ];\\r\\n\\t\\t\\tif( ctor && ctor.fromBinary )\\r\\n\\t\\t\\t\\treturn ctor.fromBinary( data, filename );\\r\\n\\t\\t\\telse if(ctor && ctor.prototype.fromBinary)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar inst = new ctor();\\r\\n\\t\\t\\t\\tinst.fromBinary( object, filename );\\r\\n\\t\\t\\t\\treturn inst;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\treturn data; \\r\\n\\t}\\r\\n\\t//WBin will detect if there is a class name inside the data and do the conversion to the specified class (p.e. a Prefab or a Mesh)\\r\\n\\tvar final_data = WBin.load( data, false, filename );\\r\\n\\treturn final_data;\\r\\n},\\\"binary\\\");\\r\\n\\r\\nLS.ResourcesManager.registerResourcePreProcessor(\\\"json\\\", function(filename, data, options) {\\r\\n\\tvar resource = data;\\r\\n\\tif( data.constructor === String )\\r\\n\\t{\\r\\n\\t\\ttry\\r\\n\\t\\t{\\r\\n\\t\\t\\tdata = JSON.parse( data );\\r\\n\\t\\t}\\r\\n\\t\\tcatch (err)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"invalid JSON\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar class_name = data.object_class || data.object_type; //object_type for LEGACY\\r\\n\\tif(!class_name && data.material_class)\\r\\n\\t\\tclass_name = data.material_class; //HACK to fix one error\\r\\n\\r\\n\\tif(!class_name)\\r\\n\\t{\\r\\n\\t\\tvar complex = LS.ResourcesManager.getExtension( filename, true );\\r\\n\\t\\tvar ctor = LS.ResourceClasses_by_extension[ complex ];\\r\\n\\t\\tif(ctor)\\r\\n\\t\\t\\tclass_name = LS.getClassName( ctor );\\r\\n\\t}\\r\\n\\r\\n\\tif( class_name && !data.is_data )\\r\\n\\t{\\r\\n\\t\\tvar ctor = LS.Classes[ class_name ] || window[ class_name ];\\r\\n\\t\\tif(ctor)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(ctor.prototype.configure)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tresource = new ctor();\\r\\n\\t\\t\\t\\tresource.configure( data );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tresource = new ctor( data );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error( \\\"JSON object_class class not found: \\\" + class_name );\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\t//unknown JSON, create a resource\\r\\n\\t\\tresource = new LS.Resource();\\r\\n\\t\\tresource.filename = filename;\\r\\n\\t\\tresource._data = data;\\r\\n\\t\\tresource.type = \\\"json\\\";\\r\\n\\t\\tresource.category = \\\"json\\\";\\r\\n\\t}\\r\\n\\treturn resource;\\r\\n});\\r\\n\\r\\n//global formats: take a file and extract info\\r\\nLS.ResourcesManager.registerResourcePreProcessor(\\\"zip\\\", function( filename, data, options ) {\\r\\n\\t\\r\\n\\tif(!global.JSZip)\\r\\n\\t\\tthrow(\\\"JSZip not found. To use ZIPs you must have the JSZip.js library included in the website.\\\");\\r\\n\\r\\n\\tvar zip = new JSZip();\\r\\n\\tzip.loadAsync( data ).then(function(zip){\\r\\n\\t\\tzip.forEach(function (relativePath, file){\\r\\n\\t\\t\\tif(file.dir)\\r\\n\\t\\t\\t\\treturn; //ignore folders\\r\\n\\t\\t\\tvar ext = LS.ResourcesManager.getExtension( relativePath );\\r\\n\\t\\t\\tvar format = LS.Formats.supported[ ext ];\\r\\n\\t\\t\\tfile.async( format && format.dataType == \\\"text\\\" ? \\\"string\\\" : \\\"arraybuffer\\\").then( function(filedata){\\r\\n\\t\\t\\t\\tif( relativePath == \\\"scene.json\\\" && (!options || !options.to_memory) )\\r\\n\\t\\t\\t\\t\\tLS.GlobalScene.configure( JSON.parse( filedata ) );\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tLS.ResourcesManager.processResource( relativePath, filedata );\\r\\n\\t\\t\\t});\\r\\n\\t\\t});\\r\\n\\t});\\r\\n\\r\\n\\treturn true;\\r\\n\\r\\n},\\\"binary\\\");\\r\\n\\r\\n//For resources without file extension (JSONs and WBINs)\\r\\nLS.ResourcesManager.processDataResource = function( url, data, options, callback )\\r\\n{\\r\\n\\t//JSON?\\r\\n\\tif( data.constructor === String )\\r\\n\\t\\tdata = JSON.parse(data);\\r\\n\\r\\n\\t//WBIN?\\r\\n\\tif(data.constructor == ArrayBuffer)\\r\\n\\t{\\r\\n\\t\\tif(!data.byteLength) //empty file?\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"Empty WBin?\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tresource = WBin.load(data);\\r\\n\\t\\tif(callback)\\r\\n\\t\\t\\tcallback(url, resource, options);\\r\\n\\t\\treturn resource;\\r\\n\\t}\\r\\n\\r\\n\\t//JS OBJECT?\\r\\n\\tvar class_name = data.object_class;\\r\\n\\tif(class_name && LS.Classes[class_name] )\\r\\n\\t{\\r\\n\\t\\tvar ctor = LS.Classes[class_name];\\r\\n\\t\\tvar resource = null;\\r\\n\\t\\tif(ctor.prototype.configure)\\r\\n\\t\\t{\\r\\n\\t\\t\\tresource = new LS.Classes[class_name]();\\r\\n\\t\\t\\tresource.configure( data );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tresource = new LS.Classes[class_name]( data );\\r\\n\\t\\tif(callback)\\r\\n\\t\\t\\tcallback(url, resource, options);\\r\\n\\t\\treturn resource;\\r\\n\\t}\\r\\n\\r\\n\\tconsole.warn(\\\"Resource Class name unknown: \\\" + class_name );\\r\\n\\treturn false;\\r\\n}\\r\\n\\r\\n//Images ********\\r\\n\\r\\n//Called after the http request for an image\\r\\n//Takes image data in some raw format and transforms it in regular image data, then converts it to GL.Texture\\r\\nLS.ResourcesManager.processImage = function( filename, data, options, callback ) {\\r\\n\\r\\n\\tvar extension = LS.ResourcesManager.getExtension(filename);\\r\\n\\tvar mimetype = \\\"application/octet-stream\\\";\\r\\n\\tif(extension == \\\"jpg\\\" || extension == \\\"jpeg\\\")\\r\\n\\t\\tmimetype = \\\"image/jpg\\\";\\r\\n\\telse if(extension == \\\"webp\\\")\\r\\n\\t\\tmimetype = \\\"image/webp\\\";\\r\\n\\telse if(extension == \\\"gif\\\")\\r\\n\\t\\tmimetype = \\\"image/gif\\\";\\r\\n\\telse if(extension == \\\"png\\\")\\r\\n\\t\\tmimetype = \\\"image/png\\\";\\r\\n\\telse {\\r\\n\\t\\tvar format = LS.Formats.supported[ extension ];\\r\\n\\t\\tif(format.mimetype)\\r\\n\\t\\t\\tmimetype = format.mimetype;\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar texture = this.processImageNonNative( filename, data, options );\\r\\n\\t\\t\\tinner_on_texture( texture );\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//blob and load\\r\\n\\tvar blob = new Blob([data],{type: mimetype});\\r\\n\\tvar objectURL = URL.createObjectURL( blob );\\r\\n\\r\\n\\t//regular image\\r\\n\\tvar image = new Image();\\r\\n\\timage.src = objectURL;\\r\\n\\timage.real_filename = filename; //hard to get the original name from the image\\r\\n\\timage.onload = function()\\r\\n\\t{\\r\\n\\t\\tvar filename = this.real_filename;\\r\\n\\t\\tvar texture = LS.ResourcesManager.processTexture( filename, this, options );\\r\\n\\t\\tinner_on_texture( texture );\\r\\n\\t}\\r\\n\\timage.onerror = function(err){\\r\\n\\t\\tURL.revokeObjectURL(objectURL); //free memory\\r\\n\\t\\tif(callback)\\r\\n\\t\\t\\tcallback( filename, null, options );\\r\\n\\t\\tconsole.error(\\\"Error while loading image, file is not native image format: \\\" + filename); //error if image is not an image I guess\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_on_texture( texture )\\r\\n\\t{\\r\\n\\t\\tif(texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//LS.ResourcesManager.registerResource( filename, texture ); //this is done already by processResource\\r\\n\\t\\t\\tif(LS.ResourcesManager.keep_files)\\r\\n\\t\\t\\t\\ttexture._original_data = data;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( !LS.ResourcesManager.keep_urls )\\r\\n\\t\\t\\tURL.revokeObjectURL( objectURL ); //free memory\\r\\n\\t\\telse\\r\\n\\t\\t\\ttexture._local_url = objectURL; //used in strange situations\\r\\n\\r\\n\\t\\tif(callback)\\r\\n\\t\\t\\tcallback(filename,texture,options);\\r\\n\\t}\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n//Similar to processImage but for non native file formats\\r\\nLS.ResourcesManager.processImageNonNative = function( filename, data, options ) {\\r\\n\\r\\n\\t//clone because DDS changes the original data\\r\\n\\tvar cloned_data = new Uint8Array(data).buffer;\\r\\n\\tvar texture_data = LS.Formats.parse( filename, cloned_data, options );\\r\\n\\r\\n\\tif(!texture_data)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"Cannot parse image format\\\");\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tif(texture_data.constructor == GL.Texture)\\r\\n\\t{\\r\\n\\t\\tvar texture = texture_data;\\r\\n\\t\\ttexture.filename = filename;\\r\\n\\t\\ttexture._original_data = cloned_data;\\r\\n\\t\\treturn texture;\\r\\n\\t}\\r\\n\\r\\n\\t//texture in object format\\r\\n\\tvar texture = LS.ResourcesManager.processTexture( filename, texture_data );\\r\\n\\treturn texture;\\r\\n}\\r\\n\\r\\n//Takes one image (or canvas or object with width,height,pixels) as input and creates a GL.Texture\\r\\nLS.ResourcesManager.processTexture = function(filename, img, options)\\r\\n{\\r\\n\\tif(img.width == (img.height / 6) || filename.indexOf(\\\"CUBECROSS\\\") != -1) //cubemap\\r\\n\\t{\\r\\n\\t\\tvar cubemap_options = { wrapS: gl.MIRROR, wrapT: gl.MIRROR, magFilter: gl.LINEAR, minFilter: gl.LINEAR_MIPMAP_LINEAR };\\r\\n\\t\\tif( filename.indexOf(\\\"CUBECROSSL\\\") != -1 )\\r\\n\\t\\t\\tcubemap_options.is_cross = 1;\\r\\n\\t\\tvar texture = GL.Texture.cubemapFromImage( img, cubemap_options );\\r\\n\\t\\tif(!texture) //happens if the image is not a cubemap\\r\\n\\t\\t\\treturn;\\r\\n\\t\\ttexture.img = img;\\r\\n\\t}\\r\\n\\telse //regular texture\\r\\n\\t{\\r\\n\\t\\tvar default_mag_filter = gl.LINEAR;\\r\\n\\t\\tvar default_wrap = gl.REPEAT;\\r\\n\\t\\t//var default_min_filter = img.width == img.height ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR;\\r\\n\\t\\tvar default_min_filter = gl.LINEAR_MIPMAP_LINEAR;\\r\\n\\t\\tif( !isPowerOfTwo(img.width) || !isPowerOfTwo(img.height) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tdefault_min_filter = gl.LINEAR;\\r\\n\\t\\t\\tdefault_wrap = gl.CLAMP_TO_EDGE; \\r\\n\\t\\t}\\r\\n\\t\\tvar texture = null;\\r\\n\\r\\n\\t\\t//from TGAs...\\r\\n\\t\\tif(img.pixels) //not a real image, just an object with width,height and a buffer with all the pixels\\r\\n\\t\\t\\ttexture = GL.Texture.fromMemory(img.width, img.height, img.pixels, { format: (img.bpp == 24 ? gl.RGB : gl.RGBA), no_flip: img.flipY, wrapS: default_wrap, wrapT: default_wrap, magFilter: default_mag_filter, minFilter: default_min_filter });\\r\\n\\t\\telse //default format is RGBA (because particles have alpha)\\r\\n\\t\\t\\ttexture = GL.Texture.fromImage(img, { format: gl.RGBA, wrapS: default_wrap, wrapT: default_wrap, magFilter: default_mag_filter, minFilter: default_min_filter });\\r\\n\\t\\tif(!texture)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\ttexture.img = img;\\r\\n\\t}\\r\\n\\r\\n\\ttexture.filename = filename;\\r\\n\\ttexture.generateMetadata(); //useful\\r\\n\\treturn texture;\\r\\n}\\r\\n\\r\\n//Transform text mesh data in a regular GL.Mesh\\r\\nLS.ResourcesManager.processTextMesh = function( filename, data, options ) {\\r\\n\\r\\n\\tvar mesh_data = LS.Formats.parse( filename, data, options );\\r\\n\\r\\n\\tif(mesh_data == null)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"Error parsing mesh: \\\" + filename);\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tvar mesh = GL.Mesh.load( mesh_data );\\r\\n\\treturn mesh;\\r\\n}\\r\\n\\r\\n//this is called when loading a scene from a format that is not the regular serialize of our engine (like from ASE, G3DJ, BVH,...)\\r\\n//converts scene data in a SceneNode\\r\\nLS.ResourcesManager.processScene = function( filename, data, options ) {\\r\\n\\t//options = options || {};\\r\\n\\r\\n\\tvar scene_data = LS.Formats.parse( filename, data, options );\\r\\n\\r\\n\\tif(scene_data == null)\\r\\n\\t{\\r\\n\\t\\tconsole.error( \\\"Error parsing scene: \\\" + filename );\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tif( scene_data && scene_data.constructor === LS.Scene )\\r\\n\\t\\tthrow(\\\"processScene must receive object, no Scene\\\");\\r\\n\\r\\n\\tif(!scene_data.root)\\r\\n\\t\\tthrow(\\\"this is not an scene, root property missing\\\");\\r\\n\\r\\n\\tLS.ResourcesManager._parsing_local_file = true;\\r\\n\\r\\n\\t//resources (meshes, textures...)\\r\\n\\tfor(var i in scene_data.meshes)\\r\\n\\t{\\r\\n\\t\\tvar mesh = scene_data.meshes[i];\\r\\n\\t\\tLS.ResourcesManager.processResource( i, mesh );\\r\\n\\t}\\r\\n\\r\\n\\t//used for anims mostly\\r\\n\\tfor(var i in scene_data.resources)\\r\\n\\t{\\r\\n\\t\\tvar res = scene_data.resources[i];\\r\\n\\t\\tLS.ResourcesManager.processResource(i,res);\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i in scene_data.materials)\\r\\n\\t{\\r\\n\\t\\tvar material = scene_data.materials[i];\\r\\n\\t\\tLS.ResourcesManager.processResource(i,material);\\r\\n\\t}\\r\\n\\r\\n\\tvar node = new LS.SceneNode();\\r\\n\\tnode.configure( scene_data.root );\\r\\n\\r\\n\\t//make it a pack or prefab\\r\\n\\tif(options && options.filename)\\r\\n\\t{\\r\\n\\t\\tvar ext = LS.RM.getExtension( options.filename );\\r\\n\\t\\tif(ext != \\\"json\\\")\\r\\n\\t\\t\\toptions.filename += \\\".json\\\";\\r\\n\\t}\\r\\n\\r\\n\\tLS.ResourcesManager._parsing_local_file = false;\\r\\n\\r\\n\\treturn node;\\r\\n}\\r\\n\\r\\nLS.ResourcesManager.loadTextureAtlas = function( atlas_info, on_complete, force )\\r\\n{\\r\\n\\tvar image = new Image();\\r\\n\\timage.src = this.getFullURL( atlas_info.filename );\\r\\n\\timage.onload = inner_process;\\r\\n\\r\\n\\tfunction inner_process()\\r\\n\\t{\\r\\n\\t\\tvar size = atlas_info.thumbnail_size;\\r\\n\\t\\tvar canvas = document.createElement(\\\"canvas\\\");\\r\\n\\t\\tcanvas.width = size;\\r\\n\\t\\tcanvas.height = size;\\r\\n\\t\\tvar ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\r\\n\\t\\tfor(var i in atlas_info.textures )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar info = atlas_info.textures[i];\\r\\n\\t\\t\\tctx.clearRect(0,0,canvas.width,canvas.height);\\r\\n\\t\\t\\tctx.drawImage( this, -info.pos[0], -info.pos[1] );\\r\\n\\t\\t\\tvar texture = GL.Texture.fromImage( canvas, { format: GL.RGBA, magFilter: gl.LINEAR, minFilter: gl.LINEAR_MIPMAP_LINEAR, wrap: gl.REPEAT } );\\r\\n\\t\\t\\tif(!force)\\r\\n\\t\\t\\t\\ttexture.is_preview = true;\\r\\n\\t\\t\\tLS.ResourcesManager.registerResource( info.name, texture );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLS.GlobalScene.requestFrame();\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete();\\r\\n\\t}\\r\\n}\\r\\n\\r\\n// Post processors **********************************************************************************\\r\\n// Take a resource already processed and does some final actions (like validate, register or compute metadata)\\r\\n\\r\\nLS.ResourcesManager.registerResourcePostProcessor(\\\"Mesh\\\", function(filename, mesh ) {\\r\\n\\r\\n\\tmesh.object_class = \\\"Mesh\\\"; //useful\\r\\n\\tif(mesh.metadata)\\r\\n\\t{\\r\\n\\t\\tmesh.metadata = {};\\r\\n\\t\\tmesh.generateMetadata(); //useful\\r\\n\\t}\\r\\n\\t//force to regenerate boundings\\r\\n\\tif(!mesh.bounding || mesh.bounding.length != BBox.data_length || (mesh.info && mesh.info.groups && mesh.info.groups.length && !mesh.info.groups[0].bounding) )\\r\\n\\t{\\r\\n\\t\\tmesh.bounding = null; //remove bad one (just in case)\\r\\n\\t\\tmesh.updateBoundingBox();\\r\\n\\t}\\r\\n\\tif(!mesh.getBuffer(\\\"normals\\\"))\\r\\n\\t\\tmesh.computeNormals();\\r\\n\\r\\n\\tif(LS.ResourcesManager.free_data) //free buffers to reduce memory usage\\r\\n\\t\\tmesh.freeData();\\r\\n\\r\\n\\tLS.ResourcesManager.meshes[filename] = mesh;\\r\\n});\\r\\n\\r\\nLS.ResourcesManager.registerResourcePostProcessor(\\\"Texture\\\", function( filename, texture ) {\\r\\n\\t//store in appropiate container\\r\\n\\tLS.ResourcesManager.textures[filename] = texture;\\r\\n});\\r\\n\\r\\nLS.ResourcesManager.registerResourcePostProcessor(\\\"Material\\\", function( filename, material ) {\\r\\n\\t//store in appropiate containers\\r\\n\\tLS.ResourcesManager.materials[filename] = material;\\r\\n\\tLS.ResourcesManager.materials_by_uid[ material.uid ] = material;\\r\\n\\tif(material.prepare)\\r\\n\\t\\tmaterial.prepare( LS.GlobalScene );\\r\\n});\\r\\n\\r\\nLS.ResourcesManager.registerResourcePostProcessor(\\\"Pack\\\", function( filename, pack ) {\\r\\n\\t//flag contents to specify where do they come from\\r\\n\\tpack.flagResources();\\r\\n});\\r\\n\\r\\nLS.ResourcesManager.registerResourcePostProcessor(\\\"Prefab\\\", function( filename, prefab ) {\\r\\n\\t//apply to nodes in the scene that use this prefab\\r\\n\\tprefab.applyToNodes();\\r\\n});\\r\\n\\r\\nLS.ResourcesManager.registerResourcePostProcessor(\\\"ShaderCode\\\", function( filename, shader_code ) {\\r\\n\\t//apply to materials that are using this ShaderCode\\r\\n\\tshader_code.applyToMaterials();\\r\\n});\\r\\n\\r\\n\\r\\n//load priority\\r\\nGL.Mesh.load_priority = -10;\\r\\n\\r\\n///@FILE:../src/shaders.js\\r\\n///@INFO: BASE\\r\\n/* Basic shader manager \\r\\n\\t- Allows to load all shaders from XML\\r\\n\\t- Allows to use a global shader\\r\\n*/\\r\\n\\r\\n//************************************\\r\\n/**\\r\\n* Shaders is the static class in charge of loading, compiling and storing shaders for reuse.\\r\\n*\\r\\n* @class Shaders\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nvar Shaders = {\\r\\n\\r\\n\\tsnippets: {},//to save source snippets\\r\\n\\tshader_blocks_by_id: new Map(),//to save shader block\\r\\n\\tshader_blocks: [],\\r\\n\\tnum_shaderblocks: 0, //used to know the index\\r\\n\\r\\n\\tglobal_extra_shader_code: \\\"\\\",\\r\\n\\tdump_compile_errors: true, //dump errors in console\\r\\n\\ton_compile_error: null, //callback \\r\\n\\r\\n\\r\\n\\t/**\\r\\n\\t* Initializes the shader manager\\r\\n\\t*\\r\\n\\t* @method init\\r\\n\\t* @param {string} url a url to a shaders.xml can be specified to load the shaders\\r\\n\\t*/\\r\\n\\tinit: function(url, ignore_cache)\\r\\n\\t{\\r\\n\\t\\t//this.shader_blocks = {};//do not initialize, or we will loose all\\r\\n\\r\\n\\t\\t//base intro code for shaders\\r\\n\\t\\tvar supported_features = []; //[name, webgl1_extension_name, enabling code]\\r\\n\\t\\tsupported_features.push( [\\\"STANDARD_DERIVATIVES\\\", \\\"OES_standard_derivatives\\\", \\\"#extension GL_OES_standard_derivatives : enable\\\"] );\\r\\n\\t\\tsupported_features.push( [\\\"DRAW_BUFFERS\\\",\\\"WEBGL_draw_buffers\\\"] ); //#extension GL_EXT_draw_buffers : require\\r\\n\\r\\n\\t\\tthis.global_extra_shader_code = String.fromCharCode(10) + \\\"#define WEBGL_VERSION \\\"+gl.webgl_version+\\\"\\\\n\\\";\\r\\n\\r\\n\\t\\tfor(var i in supported_features)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar feature = supported_features[i];\\r\\n\\t\\t\\tif( gl.webgl_version == 2 || gl.extensions[ feature[1] ] )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.global_extra_shader_code += \\\"#define \\\" + feature[0] + \\\"\\\\n\\\";\\r\\n\\t\\t\\t\\tif(gl.webgl_version == 1 && feature[2]) \\r\\n\\t\\t\\t\\t\\tthis.global_extra_shader_code += feature[2] + \\\"\\\\n\\\";\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Reloads the XML file with the shaders, useful when editing the file\\r\\n\\t*\\r\\n\\t* @method reloadShaders\\r\\n\\t* @param {function} on_complete call when the shaders have been reloaded\\r\\n\\t*/\\r\\n\\treloadShaders: function(on_complete)\\r\\n\\t{\\r\\n\\t\\t//TODO: crawl all materials and clear shaders\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Compiles a shader, the vertex and fragment shader are cached indepently to speed up compilations but a unique name must be provided\\r\\n\\t*\\r\\n\\t* @method compileShader\\r\\n\\t* @param {string} vs_code the final source code for the vertex shader\\r\\n\\t* @param {string} fs_code the final source code for the fragment shader\\r\\n\\t* @param {string} name an unique name that should be associated with this shader\\r\\n\\t* @return {GL.Shader} shader\\r\\n\\t*/\\r\\n\\tcompile: function( vs_code, fs_code, name )\\r\\n\\t{\\r\\n\\t\\tif(!name)\\r\\n\\t\\t\\tthrow(\\\"compileShader must have a name specified\\\");\\r\\n\\r\\n\\t\\tif(!gl)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tvar shader = null;\\r\\n\\t\\ttry\\r\\n\\t\\t{\\r\\n\\t\\t\\tvs_code = this.global_extra_shader_code + vs_code;\\r\\n\\t\\t\\tfs_code = this.global_extra_shader_code + fs_code;\\r\\n\\r\\n\\t\\t\\t//speed up compilations by caching shaders compiled\\r\\n\\t\\t\\tvar vs_shader = this.compiled_shaders[name + \\\":VS\\\"];\\r\\n\\t\\t\\tif(!vs_shader)\\r\\n\\t\\t\\t\\tvs_shader = this.compiled_shaders[name + \\\":VS\\\"] = GL.Shader.compileSource(gl.VERTEX_SHADER, vs_code);\\r\\n\\t\\t\\tvar fs_shader = this.compiled_shaders[name + \\\":FS\\\"];\\r\\n\\t\\t\\tif(!fs_shader)\\r\\n\\t\\t\\t\\tfs_shader = this.compiled_shaders[name + \\\":FS\\\"] = GL.Shader.compileSource(gl.FRAGMENT_SHADER, fs_code);\\r\\n\\r\\n\\t\\t\\tvar old = getTime();\\r\\n\\t\\t\\tshader = new GL.Shader( vs_shader, fs_shader );\\r\\n\\t\\t\\tif(this.debug)\\r\\n\\t\\t\\t\\tconsole.log(\\\"Shader compile time: \\\", (getTime() - old).toFixed(3), \\\"ms\\\");\\r\\n\\t\\t\\tshader.name = name;\\r\\n\\t\\t\\t//console.log(\\\"Shader compiled: \\\" + name);\\r\\n\\t\\t}\\r\\n\\t\\tcatch (err)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(this.dump_compile_errors)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.dumpShaderError(name, err, vs_code, fs_code );\\r\\n\\t\\t\\t\\tthis.dump_compile_errors = false; //disable so the console dont get overflowed\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(this.on_compile_error)\\r\\n\\t\\t\\t\\tthis.on_compile_error(err);\\r\\n\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\t\\treturn shader;\\r\\n\\t},\\r\\n\\r\\n\\tclearShaderCodeCache: function()\\r\\n\\t{\\r\\n\\t\\tvar scs = [];\\r\\n\\r\\n\\t\\t//get all shadercodes...\\r\\n\\t\\tvar shadercodes = LS.StandardMaterial.shader_codes;\\r\\n\\t\\tfor(var i in shadercodes)\\r\\n\\t\\t\\tscs.push( shadercodes[i] );\\r\\n\\r\\n\\t\\tvar res = LS.ResourcesManager.resources;\\r\\n\\t\\tfor(var i in res)\\r\\n\\t\\t\\tif( res[i].constructor === LS.ShaderCode )\\r\\n\\t\\t\\t\\tscs.push( res[i] );\\r\\n\\r\\n\\t\\t//clear caches\\r\\n\\t\\tfor(var i in scs)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar sb = scs[i];\\r\\n\\t\\t\\tsb.clearCache();\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\tdumpShaderError: function( name, err, vs_code, fs_code )\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"Error compiling shader: \\\" + name);\\r\\n\\t\\tconsole.log(err);\\r\\n\\t\\tconsole.groupCollapsed(\\\"Vertex Shader Code\\\");\\r\\n\\t\\t//console.log(\\\"VS CODE\\\\n************\\\");\\r\\n\\t\\tvar lines = vs_code.split(\\\"\\\\n\\\");\\r\\n\\t\\tfor(var i in lines)\\r\\n\\t\\t\\tconsole.log(i + \\\": \\\" + lines[i]);\\r\\n\\t\\tconsole.groupEnd();\\r\\n\\r\\n\\t\\tconsole.groupCollapsed(\\\"Fragment Shader Code\\\");\\r\\n\\t\\t//console.log(\\\"FS CODE\\\\n************\\\");\\r\\n\\t\\tlines = fs_code.split(\\\"\\\\n\\\");\\r\\n\\t\\tfor(var i in lines)\\r\\n\\t\\t\\tconsole.log(i + \\\": \\\" + lines[i]);\\r\\n\\t\\tconsole.groupEnd();\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Register a code snippet ready to be used by the #import clause in the shader\\r\\n\\t*\\r\\n\\t* @method registerSnippet\\r\\n\\t* @param {string} id\\r\\n\\t* @param {string} code\\r\\n\\t*/\\r\\n\\tregisterSnippet: function(id, code)\\r\\n\\t{\\r\\n\\t\\tthis.snippets[ id ] = { id: id, code: code };\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the code of a snipper\\r\\n\\t*\\r\\n\\t* @method getSnippet\\r\\n\\t* @param {string} id\\r\\n\\t* @return {string} code\\r\\n\\t*/\\r\\n\\tgetSnippet: function(id)\\r\\n\\t{\\r\\n\\t\\treturn this.snippets[ id ];\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* register a shaderblock in the global container so it can be used by shadermaterials\\r\\n\\t*\\r\\n\\t* @method registerShaderBlock\\r\\n\\t* @param {string} id\\r\\n\\t* @param {LS.ShaderBlock} shader_block\\r\\n\\t*/\\r\\n\\tregisterShaderBlock: function( id, shader_block, ignore_warning )\\r\\n\\t{\\r\\n\\t\\tvar block_id = -1;\\r\\n\\r\\n\\t\\tif( !ignore_warning && this.shader_blocks_by_id.get( id ) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"There is already a ShaderBlock with that name, replacing it: \\\", id);\\r\\n\\t\\t\\tblock_id = this.shader_blocks_by_id.get(id).flag_id;\\r\\n\\t\\t\\tthis.clearShaderCodeCache();\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tblock_id = this.num_shaderblocks++;\\r\\n\\t\\tif(block_id >= 64)\\r\\n\\t\\t\\tconsole.warn(\\\"Too many shaderblocks registered, not enought bits in a 64bits variable\\\");\\r\\n\\r\\n\\t\\tshader_block.flag_id = block_id;\\r\\n\\t\\tshader_block.flag_mask = 1< spotParams.x) // spotDot > cos theta/2\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t\\t// vertex lies somewhere beyond the two regions\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat ifallof = pow( (spotDot-spotParams.y)*spotParams.w,spotParams.z );\\\\n\\\\\\r\\n\\t\\t\\t\\treturn ifallof;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\\");\\r\\n\\r\\nLS.Shaders.registerSnippet(\\\"getFlatNormal\\\",\\\"\\\\n\\\\\\r\\n\\t\\t\\t#ifdef STANDARD_DERIVATIVES\\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 getFlatNormal(vec3 pos)\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 A = dFdx( pos );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 B = dFdy( pos );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn normalize( cross(A,B) );\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 getFlatNormal(vec3 pos)\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn vec3(0.0);\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\\");\\r\\n\\r\\n\\r\\nLS.Shaders.registerSnippet(\\\"PackDepth32\\\",\\\"\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tfloat linearDepth(float z, float near, float far)\\\\n\\\\\\r\\n\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\treturn (z - near) / (far - near);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\tfloat linearDepthNormalized(float z, float near, float far)\\\\n\\\\\\r\\n\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat z_n = 2.0 * z - 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\treturn 2.0 * near * far / (far + near - z_n * (far - near));\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t//packs depth normalized \\\\n\\\\\\r\\n\\t\\t\\tvec4 PackDepth32(float depth)\\\\n\\\\\\r\\n\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t const vec4 bitShift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\\\\n\\\\\\r\\n\\t\\t\\t const vec4 bitMask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\\\\n\\\\\\r\\n\\t\\t\\t vec4 comp = fract(depth * bitShift);\\\\n\\\\\\r\\n\\t\\t\\t comp -= comp.xxyz * bitMask;\\\\n\\\\\\r\\n\\t\\t\\t return comp;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\\");\\r\\n\\r\\n\\r\\nLS.Shaders.registerSnippet(\\\"perturbNormal\\\",\\\"\\\\n\\\\\\r\\n\\t\\t\\t\\tmat3 cotangent_frame(vec3 N, vec3 p, vec2 uv)\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t// get edge vectors of the pixel triangle\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t#ifdef STANDARD_DERIVATIVES\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 dp1 = dFdx( p );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 dp2 = dFdy( p );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 duv1 = dFdx( uv );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 duv2 = dFdy( uv );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t// solve the linear system\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 dp2perp = cross( dp2, N );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 dp1perp = cross( N, dp1 );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 T = dp2perp * duv1.x + dp1perp * duv2.x;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 B = dp2perp * duv1.y + dp1perp * duv2.y;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 T = vec3(1.0,0.0,0.0); //this is wrong but its a fake solution\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 B = cross(N,T);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tT = cross(B,N);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t \\\\n\\\\\\r\\n\\t\\t\\t\\t\\t// construct a scale-invariant frame \\\\n\\\\\\r\\n\\t\\t\\t\\t\\tfloat invmax = inversesqrt( max( dot(T,T), dot(B,B) ) );\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn mat3( T * invmax, B * invmax, N );\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 perturbNormal( vec3 N, vec3 V, vec2 texcoord, vec3 normal_pixel )\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t#ifdef USE_POINTS\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn N;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t// assume N, the interpolated vertex normal and \\\\n\\\\\\r\\n\\t\\t\\t\\t\\t// V, the view vector (vertex to eye)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t//vec3 normal_pixel = texture2D(normalmap, texcoord ).xyz;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tnormal_pixel = normal_pixel * 255./127. - 128./127.;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tmat3 TBN = cotangent_frame(N, V, texcoord);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn normalize(TBN * normal_pixel);\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\");\\r\\n\\r\\nLS.Shaders.registerSnippet(\\\"bumpNormal\\\",\\\"\\\\n\\\\\\r\\n\\t\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t\\t// Calculate the surface normal using screen-space partial derivatives of the height field\\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 bumpNormal(vec3 position, vec3 normal, sampler2D texture, vec2 uvs, float factor)\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t#ifdef STANDARD_DERIVATIVES\\\\n\\\\\\r\\n\\t\\t\\t vec3 dpdx = dFdx(position);\\\\n\\\\\\r\\n\\t\\t\\t vec3 dpdy = dFdy(position);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 r1 = cross(dpdy, normal);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec3 r2 = cross(normal, dpdx);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 dtdx = dFdx(uvs) * factor;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tvec2 dtdy = dFdy(uvs) * factor;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t float h = texture2D( texture, uvs ).r;\\\\n\\\\\\r\\n\\t\\t\\t float hdx = texture2D( texture, uvs + dtdx ).r;\\\\n\\\\\\r\\n\\t\\t\\t float hdy = texture2D( texture, uvs + dtdy ).r;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn normalize(normal + (r1 * (hdx - h) - r2 * (hdy - h)) / dot(dpdx, r1));\\\\n\\\\\\r\\n\\t\\t\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn normal;\\\\n\\\\\\r\\n\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\");\\r\\n\\r\\nLS.Shaders.registerSnippet(\\\"testClippingPlane\\\",\\\"\\\\n\\\\\\r\\n\\t\\t\\tfloat testClippingPlane(vec4 plane, vec3 p)\\\\n\\\\\\r\\n\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\tif(plane.x == 0.0 && plane.y == 0.0 && plane.z == 0.0)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\treturn (dot(plane.xyz, p) - plane.w) / dot(plane.xyz,plane.xyz);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\\");\\r\\n\\r\\nLS.Shaders.registerSnippet(\\\"computePointSize\\\",\\\"\\\\n\\\\\\r\\n\\t\\t\\tfloat computePointSize(float radius, float w)\\\\n\\\\\\r\\n\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\tif(radius < 0.0)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn -radius;\\\\n\\\\\\r\\n\\t\\t\\t\\treturn u_viewport.w * u_camera_perspective.z * radius / w;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\\");\\r\\n\\r\\n\\r\\n//base blocks that behave more like booleans \\r\\n\\r\\n//used to have the BLOCK_FIRSTPASS macro\\r\\nvar firstpass_block = LS.Shaders.firstpass_block = new LS.ShaderBlock(\\\"firstPass\\\");\\r\\nfirstpass_block.addCode( GL.FRAGMENT_SHADER, \\\"\\\", \\\"\\\" );\\r\\nfirstpass_block.register();\\r\\n\\r\\n//used to have the BLOCK_LASTPASS macro\\r\\nvar lastpass_block = LS.Shaders.lastpass_block = new LS.ShaderBlock(\\\"lastPass\\\");\\r\\nlastpass_block.addCode( GL.FRAGMENT_SHADER, \\\"\\\", \\\"\\\" );\\r\\nlastpass_block.register();\\r\\n\\r\\n//used when a mesh contains color info by vertex\\r\\nvar vertex_color_block = LS.Shaders.vertex_color_block = new LS.ShaderBlock(\\\"vertex_color\\\");\\r\\nvertex_color_block.register();\\r\\n\\r\\n//used when a mesh contains extra uv set\\r\\nvar coord1_block = LS.Shaders.coord1_block = new LS.ShaderBlock(\\\"coord1\\\");\\r\\ncoord1_block.register();\\r\\n\\r\\n//used to render normalinfo to buffer\\r\\nvar normalbuffer_block = LS.Shaders.normalbuffer_block = new LS.ShaderBlock(\\\"normalBuffer\\\");\\r\\nnormalbuffer_block.addCode( GL.FRAGMENT_SHADER, \\\"\\\", \\\"\\\" );\\r\\nnormalbuffer_block.register();\\r\\n\\r\\n\\r\\n///@FILE:../src/formats.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* Formats is the class where all the info about what is every format, how to parse it, etc, is located\\r\\n*\\r\\n* @class LS.Formats\\r\\n* @param{String} id the id (otherwise a random one is computed)\\r\\n* @constructor\\r\\n*/\\r\\nLS.Formats = {\\r\\n\\r\\n\\t//all the supported file formats and their parsers\\r\\n\\tsupported: {},\\r\\n\\r\\n\\tsafe_parsing: false, //catch exceptions during parsing\\r\\n\\tmerge_smoothgroups: false,\\r\\n\\r\\n\\t/**\\r\\n\\t* Tells the system info about this file format\\r\\n\\t* Info should contain fields like type:\\\"image\\\", resource: \\\"Mesh|Texture\\\", format: \\\"text|binary\\\", parse: function, native: true|false\\r\\n\\t* \\r\\n\\t* @method addFormat\\r\\n\\t*/\\r\\n\\taddSupportedFormat: function( extensions, info )\\r\\n\\t{\\r\\n\\t\\tif( extensions.constructor === String )\\r\\n\\t\\t\\textensions = extensions.split(\\\",\\\");\\r\\n\\r\\n\\t\\tfor(var i = 0; i < extensions.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar extension = extensions[i].toLowerCase();\\r\\n\\t\\t\\tif( this.supported[ extension ] )\\r\\n\\t\\t\\t\\tconsole.warn(\\\"There is already another parser associated to this extension: \\\" + extension);\\r\\n\\t\\t\\tthis.supported[ extension ] = info;\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Parse some data and returns the resulting resource\\r\\n\\t* \\r\\n\\t* @method parse\\r\\n\\t* @param {string} filename\\r\\n\\t* @param {*} data could be a string, binary, arraybuffer, xml...\\r\\n\\t* @param {Object} options how the file should be parsed\\r\\n\\t* @return {*} the final resource, could be a Texture, a Mesh, or an object\\r\\n\\t*/\\r\\n\\tparse: function( filename, data, options)\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\t\\tvar info = this.getFileFormatInfo( filename );\\r\\n\\t\\tif(!info) //unsupported extension\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tif(options.extension)\\r\\n\\t\\t\\tinfo.extension = options.extension; //force a format\\r\\n\\t\\telse\\r\\n\\t\\t\\tinfo.extension = LS.ResourcesManager.getExtension( filename );\\r\\n\\r\\n\\t\\tvar format = this.supported[ info.extension ];\\r\\n\\t\\tif(!format || !format.parse)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"Parser Error: No parser found for \\\" + info.extension + \\\" format\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar result = null;\\r\\n\\t\\tif(!this.safe_parsing)\\r\\n\\t\\t\\tresult = format.parse( data, options, filename );\\r\\n\\t\\telse\\r\\n\\t\\t\\ttry\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tresult = format.parse( data, options, filename );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.error(\\\"Error parsing content\\\", err );\\r\\n\\t\\t\\t\\treturn null;\\r\\n\\t\\t\\t}\\r\\n\\t\\tif(result)\\r\\n\\t\\t\\tresult.name = filename;\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\r\\n\\t//Returns info about a resource according to its filename\\r\\n\\tTEXT_FORMAT: \\\"text\\\",\\r\\n\\tJSON_FORMAT: \\\"json\\\",\\r\\n\\tXML_FORMAT: \\\"xml\\\",\\r\\n\\tBINARY_FORMAT: \\\"binary\\\",\\r\\n\\r\\n\\tMESH_DATA: \\\"MESH\\\",\\r\\n\\tIMAGE_DATA: \\\"IMAGE\\\",\\r\\n\\tNONATIVE_IMAGE_DATA: \\\"NONATIVE_IMAGE\\\",\\r\\n\\tSCENE_DATA: \\\"SCENE\\\",\\r\\n\\tGENERIC_DATA: \\\"GENERIC\\\",\\r\\n\\t\\r\\n\\tgetFileFormatInfo: function( filename )\\r\\n\\t{\\r\\n\\t\\tvar extension = filename.substr( filename.lastIndexOf(\\\".\\\") + 1).toLowerCase();\\r\\n\\t\\treturn this.supported[ extension ];\\r\\n\\t},\\r\\n\\r\\n\\tguessType: function( filename )\\r\\n\\t{\\r\\n\\t\\tif(!filename)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar ext = LS.RM.getExtension( filename ).toLowerCase();\\r\\n\\t\\tvar info = this.supported[ ext ];\\r\\n\\t\\tif(!info)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn info.resource;\\r\\n\\t},\\r\\n\\r\\n\\t//Helpers ******************************\\r\\n\\r\\n\\t//gets raw image information {width,height,pixels:ArrayBuffer} and create a dataurl to use in images\\r\\n\\tconvertToDataURL: function( img_data )\\r\\n\\t{\\r\\n\\t\\tvar canvas = document.createElement(\\\"canvas\\\");\\r\\n\\t\\tcanvas.width = img_data.width;\\r\\n\\t\\tcanvas.height = img_data.height;\\r\\n\\t\\t//document.body.appendChild(canvas);\\r\\n\\t\\tvar ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tvar pixelsData = ctx.createImageData(img_data.width, img_data.height);\\r\\n\\t\\tvar num_pixels = canvas.width * canvas.height;\\r\\n\\r\\n\\t\\t//flip and copy the pixels\\r\\n\\t\\tif(img_data.bytesPerPixel == 3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i = 0; i < canvas.width; ++i)\\r\\n\\t\\t\\t\\tfor(var j = 0; j < canvas.height; ++j)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar pos = j*canvas.width*4 + i*4;\\r\\n\\t\\t\\t\\t\\tvar pos2 = (canvas.height - j - 1)*canvas.width*3 + i*3;\\r\\n\\t\\t\\t\\t\\tpixelsData.data[pos+2] = img_data.pixels[pos2];\\r\\n\\t\\t\\t\\t\\tpixelsData.data[pos+1] = img_data.pixels[pos2+1];\\r\\n\\t\\t\\t\\t\\tpixelsData.data[pos+0] = img_data.pixels[pos2+2];\\r\\n\\t\\t\\t\\t\\tpixelsData.data[pos+3] = 255;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse {\\r\\n\\t\\t\\tfor(var i = 0; i < canvas.width; ++i)\\r\\n\\t\\t\\t\\tfor(var j = 0; j < canvas.height; ++j)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar pos = j*canvas.width*4 + i*4;\\r\\n\\t\\t\\t\\t\\tvar pos2 = (canvas.height - j - 1)*canvas.width*4 + i*4;\\r\\n\\t\\t\\t\\t\\tpixelsData.data[pos+0] = img_data.pixels[pos2+2];\\r\\n\\t\\t\\t\\t\\tpixelsData.data[pos+1] = img_data.pixels[pos2+1];\\r\\n\\t\\t\\t\\t\\tpixelsData.data[pos+2] = img_data.pixels[pos2+0];\\r\\n\\t\\t\\t\\t\\tpixelsData.data[pos+3] = img_data.pixels[pos2+3];\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tctx.putImageData(pixelsData,0,0);\\r\\n\\t\\timg_data.dataurl = canvas.toDataURL(\\\"image/png\\\");\\r\\n\\t\\treturn img_data.dataurl;\\r\\n\\t},\\r\\n\\r\\n\\t/* extract important Mesh info from vertices (center, radius, bouding box) */\\r\\n\\tcomputeMeshBounding: function(vertices)\\r\\n\\t{\\r\\n\\t\\t//compute AABB and useful info\\r\\n\\t\\tvar min = [vertices[0],vertices[1],vertices[2]];\\r\\n\\t\\tvar max = [vertices[0],vertices[1],vertices[2]];\\r\\n\\t\\tfor(var i = 0; i < vertices.length; i += 3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar v = [vertices[i],vertices[i+1],vertices[i+2]];\\r\\n\\t\\t\\tif (v[0] < min[0]) min[0] = v[0];\\r\\n\\t\\t\\telse if (v[0] > max[0]) max[0] = v[0];\\r\\n\\t\\t\\tif (v[1] < min[1]) min[1] = v[1];\\r\\n\\t\\t\\telse if (v[1] > max[1]) max[1] = v[1];\\r\\n\\t\\t\\tif (v[2] < min[2]) min[2] = v[2];\\r\\n\\t\\t\\telse if (v[2] > max[2]) max[2] = v[2];\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar center = [(min[0] + max[0]) * 0.5,(min[1] + max[1]) * 0.5, (min[2] + max[2]) * 0.5];\\r\\n\\t\\tvar halfsize = [ min[0] - center[0], min[1] - center[1], min[2] - center[2]];\\r\\n\\t\\treturn BBox.setCenterHalfsize( BBox.create(), center, halfsize );\\r\\n\\t}\\r\\n};\\r\\n\\r\\n//native formats do not need parser\\r\\nLS.Formats.addSupportedFormat( \\\"png,jpg,jpeg,webp,bmp,gif\\\", { \\\"native\\\": true, dataType: \\\"arraybuffer\\\", resource: \\\"Texture\\\", \\\"resourceClass\\\": GL.Texture, has_preview: true, type: \\\"image\\\" } );\\r\\nLS.Formats.addSupportedFormat( \\\"wbin\\\", { dataType: \\\"arraybuffer\\\" } );\\r\\nLS.Formats.addSupportedFormat( \\\"json,js,txt,html,css,csv\\\", { dataType: \\\"text\\\" } );\\r\\nLS.Formats.addSupportedFormat( \\\"glsl\\\", { dataType: \\\"text\\\", resource: \\\"ShaderCode\\\", \\\"resourceClass\\\": LS.ShaderCode } );\\r\\nLS.Formats.addSupportedFormat( \\\"zip\\\", { dataType: \\\"arraybuffer\\\" } );\\r\\nWBin.classes = LS.Classes; //WBin need to know which classes are accesible to be instantiated right from the WBin data info, in case the class is not a global class\\r\\n\\r\\n\\r\\nvar parserMESH = {\\r\\n\\textension: 'mesh',\\r\\n\\ttype: 'mesh',\\r\\n\\tresource: 'Mesh',\\r\\n\\tformat: 'text',\\r\\n\\tdataType:'text',\\r\\n\\r\\n\\tparse: function(text, options)\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\t\\tvar support_uint = true;\\r\\n\\r\\n\\t\\tvar parser = GL.Mesh.parsers[\\\"mesh\\\"];\\r\\n\\t\\tvar mesh = parser(text, options);\\r\\n\\t\\tif( mesh.bounding.radius == 0 || isNaN(mesh.bounding.radius))\\r\\n\\t\\t\\tconsole.log(\\\"no radius found in mesh\\\");\\r\\n\\t\\t//console.log(mesh);\\r\\n\\t\\treturn mesh;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nLS.Formats.addSupportedFormat( \\\"mesh\\\", parserMESH );\\r\\n///@FILE:../src/utils/litegl-extra.js\\r\\n///@INFO: BASE\\r\\n//when working with animations sometimes you want the bones to be referenced by node name and no node uid, because otherwise you cannot reuse\\r\\n//the same animation with different characters in the same scene.\\r\\nGL.Mesh.prototype.convertBoneNames = function( root_node, use_uids )\\r\\n{\\r\\n\\tif(!this.bones || !this.bones.length)\\r\\n\\t\\treturn 0;\\r\\n\\r\\n\\troot_node = root_node || LS.GlobalScene;\\r\\n\\tif( root_node.constructor == LS.Scene )\\r\\n\\t\\troot_node = root_node.root;\\r\\n\\tif(!root_node.findNode)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"convertBoneNames first parameter must be node or scene\\\");\\r\\n\\t\\treturn 0;\\r\\n\\t}\\r\\n\\r\\n\\tvar modified = false;\\r\\n\\r\\n\\t//Rename the id to a relative name\\r\\n\\tfor(var i = 0; i < this.bones.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar bone = this.bones[i];\\r\\n\\t\\tvar bone_name = bone[0];\\r\\n\\r\\n\\t\\tif( !use_uids )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( bone_name[0] != LS._uid_prefix)\\r\\n\\t\\t\\t\\tcontinue; //already using a name, not a uid\\r\\n\\t\\t\\tvar node = root_node.findNode( bone_name );\\r\\n\\t\\t\\tif(!node)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Bone node not found: \\\" + bone_name );\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tbone[0] = node.name;\\r\\n\\t\\t\\tmodified = true;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( bone_name[0] == LS._uid_prefix)\\r\\n\\t\\t\\t\\tcontinue; //already using a uid\\r\\n\\t\\t\\tvar node = root_node.findNode( bone_name );\\r\\n\\t\\t\\tif(!node)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Bone node not found: \\\" + bone_name );\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tbone[0] = node.uid;\\r\\n\\t\\t\\tmodified = true;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//flag it\\r\\n\\tif(modified)\\r\\n\\t\\tLS.RM.resourceModified( this );\\r\\n}\\r\\n///@FILE:../src/materials/material.js\\r\\n///@INFO: BASE\\r\\n\\r\\n//Material class **************************\\r\\n/**\\r\\n* A Material is a class in charge of defining how to render an object, there are several classes for Materials\\r\\n* but this class is more like a template for other material classes.\\r\\n* The rendering of a material is handled by the material itself, if not provided then uses the Renderer default one\\r\\n* @namespace LS\\r\\n* @class Material\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\n\\r\\nfunction Material( o )\\r\\n{\\r\\n\\tthis.uid = LS.generateUId(\\\"MAT-\\\");\\r\\n\\tthis._must_update = true;\\r\\n\\r\\n\\t//used locally during rendering\\r\\n\\tthis._index = -1;\\r\\n\\tthis._local_id = Material.last_index++;\\r\\n\\tthis._last_frame_update = -1;\\r\\n\\r\\n\\t/**\\r\\n\\t* materials have at least a basic color property and opacity\\r\\n\\t* @property color\\r\\n\\t* @type {[[r,g,b]]}\\r\\n\\t* @default [1,1,1]\\r\\n\\t*/\\r\\n\\tthis._color = new Float32Array([1.0,1.0,1.0,1.0]);\\r\\n\\r\\n\\t/**\\r\\n\\t* render queue: which order should this be rendered\\r\\n\\t* @property queue\\r\\n\\t* @type {Number}\\r\\n\\t* @default LS.RenderQueue.DEFAULT\\r\\n\\t*/\\r\\n\\tthis._queue = LS.RenderQueue.DEFAULT;\\r\\n\\r\\n\\t/**\\r\\n\\t* render state: which flags should be used (in StandardMaterial this is overwritten due to the multipass lighting)\\r\\n\\t* TODO: render states should be moved to render passes defined by the shadercode in the future to allow multipasses like cellshading outline render\\r\\n\\t* @property render_state\\r\\n\\t* @type {LS.RenderState}\\r\\n\\t*/\\r\\n\\tthis._render_state = new LS.RenderState();\\r\\n\\r\\n\\r\\n\\tthis._light_mode = LS.Material.NO_LIGHTS;\\r\\n\\r\\n\\t/**\\r\\n\\t* matrix used to define texture tiling in the shader (passed as u_texture_matrix)\\r\\n\\t* @property uvs_matrix\\r\\n\\t* @type {mat3}\\r\\n\\t* @default [1,0,0, 0,1,0, 0,0,1]\\r\\n\\t*/\\r\\n\\tthis.uvs_matrix = new Float32Array([1,0,0, 0,1,0, 0,0,1]);\\r\\n\\r\\n\\t/**\\r\\n\\t* texture channels\\r\\n\\t* contains info about the samplers for every texture channel\\r\\n\\t* @property textures\\r\\n\\t* @type {Object}\\r\\n\\t*/\\r\\n\\tthis.textures = {};\\r\\n\\r\\n\\t/**\\r\\n\\t* used internally by LS.StandardMaterial\\r\\n\\t* This will be gone in the future in order to use the new ShaderMaterial rendering system\\r\\n\\t* @property query\\r\\n\\t* @type {LS.ShaderQuery}\\r\\n\\t*/\\r\\n\\t//this._query = new LS.ShaderQuery();\\r\\n\\r\\n\\t/**\\r\\n\\t* flags to control cast_shadows, receive_shadows or ignore_frustum\\r\\n\\t* @property flags\\r\\n\\t* @type {Object}\\r\\n\\t* @default { cast_shadows: true, receive_shadows: true, ignore_frutum: false }\\r\\n\\t*/\\r\\n\\tthis.flags = {\\r\\n\\t\\tcast_shadows: true,\\r\\n\\t\\treceive_shadows: true,\\r\\n\\t\\tignore_frustum: false\\r\\n\\t};\\r\\n\\r\\n\\t//properties with special storage (multiple vars shared among single properties)\\r\\n\\r\\n\\tObject.defineProperty( this, 'color', {\\r\\n\\t\\tget: function() { return this._color.subarray(0,3); },\\r\\n\\t\\tset: function(v) { vec3.copy( this._color, v ); },\\r\\n\\t\\tenumerable: true\\r\\n\\t});\\r\\n\\r\\n\\t/**\\r\\n\\t* The alpha component to control opacity\\r\\n\\t* @property opacity\\r\\n\\t* @default 1\\r\\n\\t**/\\r\\n\\tObject.defineProperty( this, 'opacity', {\\r\\n\\t\\tget: function() { return this._color[3]; },\\r\\n\\t\\tset: function(v) { this._color[3] = v; },\\r\\n\\t\\tenumerable: true\\r\\n\\t});\\r\\n\\r\\n\\t/**\\r\\n\\t* the render queue id where this instance belongs\\r\\n\\t* @property queue\\r\\n\\t* @default LS.RenderQueue.DEFAULT;\\r\\n\\t**/\\r\\n\\tObject.defineProperty( this, 'queue', {\\r\\n\\t\\tget: function() { return this._queue; },\\r\\n\\t\\tset: function(v) { \\r\\n\\t\\t\\tif( isNaN(v) || !isNumber(v) )\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tthis._queue = v;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: true\\r\\n\\t});\\r\\n\\r\\n\\t/**\\r\\n\\t* the render state flags to control how the GPU behaves\\r\\n\\t* @property render_state\\r\\n\\t**/\\r\\n\\tObject.defineProperty( this, 'render_state', {\\r\\n\\t\\tget: function() { return this._render_state; },\\r\\n\\t\\tset: function(v) { \\r\\n\\t\\t\\tif(!v)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tfor(var i in v) //copy from JSON object\\r\\n\\t\\t\\t\\tthis._render_state[i] = v[i];\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: true\\r\\n\\t});\\r\\n\\r\\n\\r\\n\\tif(o) \\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nMaterial[\\\"@color\\\"] = { type:\\\"color\\\" };\\r\\n\\r\\nMaterial.icon = \\\"mini-icon-material.png\\\";\\r\\nMaterial.last_index = 0;\\r\\n\\r\\nMaterial.NO_LIGHTS = 0;\\r\\nMaterial.ONE_LIGHT = 1;\\r\\nMaterial.SEVERAL_LIGHTS = 2;\\r\\n\\r\\nMaterial.EXTENSION = \\\"json\\\";\\r\\n\\r\\n//material info attributes, use this to avoid errors when settings the attributes of a material\\r\\n\\r\\n/**\\r\\n* Surface color\\r\\n* @property color\\r\\n* @type {vec3}\\r\\n* @default [1,1,1]\\r\\n*/\\r\\nMaterial.COLOR = \\\"color\\\";\\r\\n/**\\r\\n* Opacity. It must be < 1 to enable alpha sorting. If it is <= 0 wont be visible.\\r\\n* @property opacity\\r\\n* @type {number}\\r\\n* @default 1\\r\\n*/\\r\\nMaterial.OPACITY = \\\"opacity\\\";\\r\\n\\r\\nMaterial.SPECULAR_FACTOR = \\\"specular_factor\\\";\\r\\n/**\\r\\n* Specular glossiness: the glossines (exponent) of specular light\\r\\n* @property specular_gloss\\r\\n* @type {number}\\r\\n* @default 10\\r\\n*/\\r\\nMaterial.SPECULAR_GLOSS = \\\"specular_gloss\\\";\\r\\n\\r\\nMaterial.OPACITY_TEXTURE = \\\"opacity\\\";\\t//used for baked GI\\r\\nMaterial.COLOR_TEXTURE = \\\"color\\\";\\t//material color\\r\\nMaterial.AMBIENT_TEXTURE = \\\"ambient\\\";\\r\\nMaterial.SPECULAR_TEXTURE = \\\"specular\\\"; //defines specular factor and glossiness per pixel\\r\\nMaterial.EMISSIVE_TEXTURE = \\\"emissive\\\";\\r\\nMaterial.ENVIRONMENT_TEXTURE = \\\"environment\\\";\\r\\nMaterial.IRRADIANCE_TEXTURE = \\\"irradiance\\\";\\r\\n\\r\\nMaterial.COORDS_UV0 = 0;\\r\\nMaterial.COORDS_UV1 = 1;\\r\\nMaterial.COORDS_UV0_TRANSFORMED = 2;\\r\\nMaterial.COORDS_UV1_TRANSFORMED = 3;\\r\\n\\r\\nMaterial.TEXTURE_COORDINATES = { \\\"uv0\\\":Material.COORDS_UV0, \\\"uv1\\\":Material.COORDS_UV1, \\\"uv0_transformed\\\":Material.COORDS_UV0_TRANSFORMED, \\\"uv1_transformed\\\":Material.COORDS_UV1_TRANSFORMED };\\r\\n\\r\\nMaterial.available_shaders = [\\\"default\\\",\\\"global\\\",\\\"lowglobal\\\",\\\"phong_texture\\\",\\\"flat\\\",\\\"normal\\\",\\\"phong\\\",\\\"flat_texture\\\",\\\"cell_outline\\\"];\\r\\n\\r\\nMaterial.prototype.fillUniforms = function( scene, options )\\r\\n{\\r\\n\\tvar uniforms = {};\\r\\n\\tvar samplers = [];\\r\\n\\r\\n\\tuniforms.u_material_color = this._color;\\r\\n\\tuniforms.u_ambient_color = scene.info ? scene.info.ambient_color : LS.ONES;\\r\\n\\tuniforms.u_texture_matrix = this.uvs_matrix;\\r\\n\\r\\n\\tuniforms.u_specular = vec2.create([1,50]);\\r\\n\\tuniforms.u_reflection = 0.0;\\r\\n\\r\\n\\t//iterate through textures in the material\\r\\n\\tvar last_texture_slot = 0;\\r\\n\\tfor(var i in this.textures) \\r\\n\\t{\\r\\n\\t\\tvar sampler = this.getTextureSampler(i);\\r\\n\\t\\tif(!sampler)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tvar texture = Material.getTextureFromSampler( sampler );\\r\\n\\t\\tif(!texture) //loading or non-existant\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tsamplers[ last_texture_slot ] = sampler;\\r\\n\\t\\tvar uniform_name = i + (texture.texture_type == gl.TEXTURE_2D ? \\\"_texture\\\" : \\\"_cubemap\\\");\\r\\n\\t\\tuniforms[ uniform_name ] = last_texture_slot;\\r\\n\\t\\tlast_texture_slot++;\\r\\n\\t}\\r\\n\\r\\n\\t//add extra uniforms\\r\\n\\tfor(var i in this.extra_uniforms)\\r\\n\\t\\tuniforms[i] = this.extra_uniforms[i];\\r\\n\\r\\n\\tthis._uniforms = uniforms;\\r\\n\\tthis._samplers = samplers; //samplers without fixed slot\\r\\n}\\r\\n\\r\\n/**\\r\\n* Configure the material getting the info from the object\\r\\n* @method configure\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\nMaterial.prototype.configure = function(o)\\r\\n{\\r\\n\\tfor(var i in o)\\r\\n\\t{\\r\\n\\t\\tif(typeof (o[i]) === \\\"function\\\")\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(!this.setProperty( i, o[i] ) && LS.debug)\\r\\n\\t\\t\\tconsole.warn(\\\"Material property not assigned: \\\" + i );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Serialize this material \\r\\n* @method serialize\\r\\n* @return {Object} object with the serialization info\\r\\n*/\\r\\nMaterial.prototype.serialize = function( simplified )\\r\\n{\\r\\n\\tvar o = LS.cloneObject(this);\\r\\n\\tdelete o.filename;\\r\\n\\tdelete o.fullpath;\\r\\n\\tdelete o.remotepath;\\r\\n\\to.material_class = LS.getObjectClassName(this);\\r\\n\\r\\n\\tif( simplified )\\r\\n\\t{\\r\\n\\t\\tdelete o.render_state;\\r\\n\\t\\tdelete o.flags;\\r\\n\\t\\tif( o.uvs_matrix && o.uvs_matrix.equal([1,0,0, 0,1,0, 0,0,1]) )\\r\\n\\t\\t\\tdelete o.uvs_matrix;\\r\\n\\t}\\r\\n\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Clone this material (keeping the class)\\r\\n* @method clone\\r\\n* @return {Material} Material instance\\r\\n*/\\r\\nMaterial.prototype.clone = function()\\r\\n{\\r\\n\\tvar data = this.serialize();\\r\\n\\tif(data.uid)\\r\\n\\t\\tdelete data.uid;\\r\\n\\treturn new this.constructor( JSON.parse( JSON.stringify( data )) );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Loads and assigns a texture to a channel\\r\\n* @method loadAndSetTexture\\r\\n* @param {Texture || url} texture_or_filename\\r\\n* @param {String} channel\\r\\n*/\\r\\nMaterial.prototype.loadAndSetTexture = function( channel, texture_or_filename, options )\\r\\n{\\r\\n\\toptions = options || {};\\r\\n\\tvar that = this;\\r\\n\\r\\n\\tif( texture_or_filename && texture_or_filename.constructor === String ) //it could be the url or the internal texture name \\r\\n\\t{\\r\\n\\t\\tif(texture_or_filename[0] != \\\":\\\")//load if it is not an internal texture\\r\\n\\t\\t\\tLS.ResourcesManager.load(texture_or_filename,options, function(texture) {\\r\\n\\t\\t\\t\\tthat.setTexture(channel, texture);\\r\\n\\t\\t\\t\\tif(options.on_complete)\\r\\n\\t\\t\\t\\t\\toptions.on_complete();\\r\\n\\t\\t\\t});\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.setTexture(channel, texture_or_filename);\\r\\n\\t}\\r\\n\\telse //otherwise just assign whatever\\r\\n\\t{\\r\\n\\t\\tthis.setTexture( channel, texture_or_filename );\\r\\n\\t\\tif(options.on_complete)\\r\\n\\t\\t\\toptions.on_complete();\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* gets all the properties and its types\\r\\n* @method getPropertiesInfo\\r\\n* @return {Object} object with name:type\\r\\n*/\\r\\nMaterial.prototype.getPropertiesInfo = function()\\r\\n{\\r\\n\\tvar o = {\\r\\n\\t\\tcolor:\\\"vec3\\\",\\r\\n\\t\\topacity:\\\"number\\\",\\r\\n\\t\\tuvs_matrix:\\\"mat3\\\"\\r\\n\\t};\\r\\n\\r\\n\\tvar textures = this.getTextureChannels();\\r\\n\\tfor(var i in textures)\\r\\n\\t\\to[\\\"tex_\\\" + textures[i]] = \\\"Texture\\\"; //changed from Sampler\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\n/**\\r\\n* gets all the properties and its types\\r\\n* @method getProperty\\r\\n* @return {Object} object with name:type\\r\\n*/\\r\\nMaterial.prototype.getProperty = function(name)\\r\\n{\\r\\n\\tif(name.substr(0,4) == \\\"tex_\\\")\\r\\n\\t\\treturn this.textures[ name.substr(4) ];\\r\\n\\treturn this[name];\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* gets all the properties and its types\\r\\n* @method getProperty\\r\\n* @return {Object} object with name:type\\r\\n*/\\r\\nMaterial.prototype.setProperty = function( name, value )\\r\\n{\\r\\n\\tif( value === undefined )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( name.substr(0,4) == \\\"tex_\\\" )\\r\\n\\t{\\r\\n\\t\\tif( (value && (value.constructor === String || value.constructor === GL.Texture)) || !value)\\r\\n\\t\\t\\tthis.setTexture( name.substr(4), value );\\r\\n\\t\\treturn true;\\r\\n\\t}\\r\\n\\r\\n\\tswitch( name )\\r\\n\\t{\\r\\n\\t\\t//numbers\\r\\n\\t\\tcase \\\"queue\\\": \\r\\n\\t\\tcase \\\"opacity\\\": \\r\\n\\t\\t\\tif(value !== null && value.constructor === Number)\\r\\n\\t\\t\\t\\tthis[name] = value; \\r\\n\\t\\t\\tbreak;\\r\\n\\t\\t//bools\\r\\n\\t\\t//strings\\r\\n\\t\\tcase \\\"uid\\\":\\r\\n\\t\\t\\tthis[name] = value; \\r\\n\\t\\t\\tbreak;\\r\\n\\t\\t//vectors\\r\\n\\t\\tcase \\\"uvs_matrix\\\":\\r\\n\\t\\tcase \\\"color\\\": \\r\\n\\t\\t\\tif(this[name].length == value.length)\\r\\n\\t\\t\\t\\tthis[name].set( value );\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase \\\"textures\\\":\\r\\n\\t\\t\\tfor(var i in value)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar tex = value[i];\\r\\n\\t\\t\\t\\tif( tex && tex.constructor === String )\\r\\n\\t\\t\\t\\t\\ttex = { texture: tex, uvs: 0, wrap: 0, minFilter: 0, magFilter: 0 };\\r\\n\\t\\t\\t\\ttex._must_update = true;\\r\\n\\t\\t\\t\\tthis.textures[i] = tex;\\r\\n\\t\\t\\t\\tif( tex.uvs != null && tex.uvs.constructor === String )\\r\\n\\t\\t\\t\\t\\ttex.uvs = 0;\\r\\n\\t\\t\\t\\t//this is to ensure there are no wrong characters in the texture name\\r\\n\\t\\t\\t\\tif( this.textures[i] && this.textures[i].texture )\\r\\n\\t\\t\\t\\t\\tthis.textures[i].texture = LS.ResourcesManager.cleanFullpath( this.textures[i].texture );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t//this.textures = cloneObject(value);\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase \\\"flags\\\":\\r\\n\\t\\t\\tfor(var i in value)\\r\\n\\t\\t\\t\\tthis.flags[i] = value[i];\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase \\\"transparency\\\": //special cases\\r\\n\\t\\t\\tthis.opacity = 1 - value;\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase \\\"render_state\\\":\\r\\n\\t\\t\\tthis._render_state.configure( value );\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\t//ignore\\r\\n\\t\\tcase \\\"material_class\\\":\\r\\n\\t\\tcase \\\"object_type\\\":\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\treturn false;\\r\\n\\t}\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\nMaterial.prototype.setPropertyValueFromPath = function( path, value, offset )\\r\\n{\\r\\n\\toffset = offset || 0;\\r\\n\\r\\n\\tif( path.length < (offset+1) )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//maybe check if path is texture?\\r\\n\\t//TODO\\r\\n\\r\\n\\t//assign\\r\\n\\tthis.setProperty( path[ offset ], value );\\r\\n}\\r\\n\\r\\nMaterial.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif( path.length < 1)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar varname = path[0];\\r\\n\\tvar type = null;\\r\\n\\r\\n\\tswitch(varname)\\r\\n\\t{\\r\\n\\t\\tcase \\\"queue\\\": \\r\\n\\t\\tcase \\\"opacity\\\": \\r\\n\\t\\tcase \\\"transparency\\\":\\r\\n\\t\\t\\ttype = \\\"number\\\"; break;\\r\\n\\t\\t//vectors\\r\\n\\t\\tcase \\\"uvs_matrix\\\":\\r\\n\\t\\t\\ttype = \\\"mat3\\\"; break;\\r\\n\\t\\tcase \\\"color\\\": \\r\\n\\t\\t\\ttype = \\\"vec3\\\"; break;\\r\\n\\t\\tcase \\\"textures\\\":\\r\\n\\t\\t\\ttype = \\\"Texture\\\"; break;\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\treturn {\\r\\n\\t\\tnode: this._root,\\r\\n\\t\\ttarget: this,\\r\\n\\t\\tname: varname,\\r\\n\\t\\tvalue: this[varname],\\r\\n\\t\\ttype: type\\r\\n\\t};\\r\\n}\\r\\n\\r\\n/**\\r\\n* gets all the texture channels supported by this material\\r\\n* @method getTextureChannels\\r\\n* @return {Array} array with the name of every channel supported by this material\\r\\n*/\\r\\nMaterial.prototype.getTextureChannels = function()\\r\\n{\\r\\n\\t//console.warn(\\\"this function should never be called, it should be overwritten\\\");\\r\\n\\treturn [];\\r\\n}\\r\\n\\r\\n/**\\r\\n* Assigns a texture to a channel and its sampling parameters\\r\\n* @method setTexture\\r\\n* @param {String} channel for a list of supported channels by this material call getTextureChannels()\\r\\n* @param {Texture} texture\\r\\n* @param {Object} sampler_options\\r\\n*/\\r\\nMaterial.prototype.setTexture = function( channel, texture, sampler_options ) {\\r\\n\\r\\n\\tif(!channel)\\r\\n\\t\\tthrow(\\\"Material.prototype.setTexture channel must be specified\\\");\\r\\n\\r\\n\\tif(!texture)\\r\\n\\t{\\r\\n\\t\\tdelete this.textures[ channel ];\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//clean to avoid names with double slashes\\r\\n\\tif( texture.constructor === String )\\r\\n\\t\\ttexture = LS.ResourcesManager.cleanFullpath( texture );\\r\\n\\r\\n\\t//get current info\\r\\n\\tvar sampler = this.textures[ channel ];\\r\\n\\tif(!sampler)\\r\\n\\t\\tthis.textures[channel] = sampler = { \\r\\n\\t\\t\\ttexture: texture, \\r\\n\\t\\t\\tuvs: 0, \\r\\n\\t\\t\\twrap: 0, \\r\\n\\t\\t\\tminFilter: 0, \\r\\n\\t\\t\\tmagFilter: 0,\\r\\n\\t\\t\\tmissing: \\\"white\\\"\\r\\n\\t\\t};\\r\\n\\telse if(sampler.texture == texture && !sampler_options)\\r\\n\\t\\treturn sampler;\\r\\n\\telse\\r\\n\\t\\tsampler.texture = texture;\\r\\n\\r\\n\\tif(sampler_options)\\r\\n\\t\\tfor(var i in sampler_options)\\r\\n\\t\\t\\tsampler[i] = sampler_options[i];\\r\\n\\tsampler._must_update = true;\\r\\n\\r\\n\\tif(texture.constructor === String && texture[0] != \\\":\\\")\\r\\n\\t\\tLS.ResourcesManager.load( texture );\\r\\n\\r\\n\\treturn sampler;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Set a property of the sampling (wrap, uvs, filter)\\r\\n* @method setTextureProperty\\r\\n* @param {String} channel for a list of supported channels by this material call getTextureChannels()\\r\\n* @param {String} property could be \\\"uvs\\\", \\\"filter\\\", \\\"wrap\\\"\\r\\n* @param {*} value the value, for uvs check Material.TEXTURE_COORDINATES, filter is gl.NEAREST or gl.LINEAR and wrap gl.CLAMP_TO_EDGE, gl.MIRROR or gl.REPEAT\\r\\n*/\\r\\nMaterial.prototype.setTextureProperty = function( channel, property, value )\\r\\n{\\r\\n\\tvar sampler = this.textures[channel];\\r\\n\\r\\n\\tif(!sampler)\\r\\n\\t{\\r\\n\\t\\tif(property == \\\"texture\\\")\\r\\n\\t\\t\\tthis.textures[channel] = sampler = { texture: value, uvs: 0, wrap: 0, minFilter: 0, magFilter: 0 };\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tsampler[ property ] = value;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the texture in a channel\\r\\n* @method getTexture\\r\\n* @param {String} channel default is COLOR\\r\\n* @return {Texture}\\r\\n*/\\r\\nMaterial.prototype.getTexture = function( channel ) {\\r\\n\\tchannel = channel || Material.COLOR_TEXTURE;\\r\\n\\r\\n\\tvar v = this.textures[channel];\\r\\n\\tif(!v) \\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tif(v.constructor === String)\\r\\n\\t\\treturn LS.ResourcesManager.textures[v];\\r\\n\\r\\n\\tvar tex = v.texture;\\r\\n\\tif(!tex)\\r\\n\\t\\treturn null;\\r\\n\\tif(tex.constructor === String)\\r\\n\\t\\treturn LS.ResourcesManager.textures[tex];\\r\\n\\telse if(tex.constructor == Texture)\\r\\n\\t\\treturn tex;\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the texture sampler info of one texture channel (filter, wrap, uvs)\\r\\n* @method getTextureSampler\\r\\n* @param {String} channel get available channels using getTextureChannels\\r\\n* @return {Texture}\\r\\n*/\\r\\nMaterial.prototype.getTextureSampler = function(channel) {\\r\\n\\treturn this.textures[ channel ];\\r\\n}\\r\\n\\r\\nMaterial.getTextureFromSampler = function(sampler)\\r\\n{\\r\\n\\tvar texture = sampler.constructor === String ? sampler : sampler.texture;\\r\\n\\tif(!texture) //weird case\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\t//fetch\\r\\n\\tif(texture.constructor === String)\\r\\n\\t\\ttexture = LS.ResourcesManager.textures[ texture ];\\r\\n\\t\\r\\n\\tif (!texture || texture.constructor != GL.Texture)\\r\\n\\t\\treturn null;\\r\\n\\treturn texture;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Assigns a texture sampler to one texture channel (filter, wrap, uvs)\\r\\n* @method setTextureInfo\\r\\n* @param {String} channel default is COLOR\\r\\n* @param {Object} sampler { texture, uvs, wrap, filter }\\r\\n*/\\r\\nMaterial.prototype.setTextureSampler = function(channel, sampler) {\\r\\n\\tif(!channel)\\r\\n\\t\\tthrow(\\\"Cannot call Material setTextureSampler without channel\\\");\\r\\n\\tif(!sampler)\\r\\n\\t\\tdelete this.textures[ channel ];\\r\\n\\telse\\r\\n\\t\\tthis.textures[ channel ] = sampler;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Collects all the resources needed by this material (textures)\\r\\n* @method getResources\\r\\n* @param {Object} resources object where all the resources are stored\\r\\n* @return {Texture}\\r\\n*/\\r\\nMaterial.prototype.getResources = function (res)\\r\\n{\\r\\n\\tfor(var i in this.textures)\\r\\n\\t{\\r\\n\\t\\tvar sampler = this.textures[i];\\r\\n\\t\\tif(!sampler) \\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(typeof(sampler.texture) == \\\"string\\\")\\r\\n\\t\\t\\tres[ sampler.texture ] = GL.Texture;\\r\\n\\t}\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Event used to inform if one resource has changed its name\\r\\n* @method onResourceRenamed\\r\\n* @param {Object} resources object where all the resources are stored\\r\\n* @return {Boolean} true if something was modified\\r\\n*/\\r\\nMaterial.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tvar v = false;\\r\\n\\tfor(var i in this.textures)\\r\\n\\t{\\r\\n\\t\\tvar sampler = this.textures[i];\\r\\n\\t\\tif(!sampler)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(sampler.texture == old_name)\\r\\n\\t\\t{\\r\\n\\t\\t\\tsampler.texture = new_name;\\r\\n\\t\\t\\tv = true;\\r\\n\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn v;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Loads all the textures inside this material, by sending the through the ResourcesManager\\r\\n* @method loadTextures\\r\\n*/\\r\\n\\r\\nMaterial.prototype.loadTextures = function ()\\r\\n{\\r\\n\\tvar res = this.getResources({});\\r\\n\\tfor(var i in res)\\r\\n\\t\\tLS.ResourcesManager.load( i );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Register this material in a materials pool to be shared with other nodes\\r\\n* @method registerMaterial\\r\\n* @param {String} name name given to this material, it must be unique\\r\\n*/\\r\\nMaterial.prototype.registerMaterial = function(name)\\r\\n{\\r\\n\\tthis.name = name;\\r\\n\\tLS.ResourcesManager.registerResource(name, this);\\r\\n\\tthis.material = name;\\r\\n}\\r\\n\\r\\nMaterial.prototype.getCategory = function()\\r\\n{\\r\\n\\treturn this.category || \\\"Material\\\";\\r\\n}\\r\\n\\r\\nMaterial.prototype.getLocator = function()\\r\\n{\\r\\n\\tif(this._root)\\r\\n\\t\\treturn this._root.uid + \\\"/material\\\";\\r\\n\\treturn this.uid;\\r\\n}\\r\\n\\r\\nMaterial.prototype.assignToNode = function(node)\\r\\n{\\r\\n\\tif(!node)\\r\\n\\t\\treturn false;\\r\\n\\tvar filename = this.fullpath || this.filename;\\r\\n\\tnode.material = filename ? filename : this;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Creates a new property in this material class. Helps with some special cases\\r\\n* like when we have a Float32Array property and we dont want it to be replaced by another array, but setted\\r\\n* @method createProperty\\r\\n* @param {String} name the property name as it should be accessed ( p.e. \\\"color\\\" -> material.color )\\r\\n* @param {*} value\\r\\n* @param {String} type a valid value type (\\\"Number\\\",\\\"Boolean\\\",\\\"Texture\\\",...)\\r\\n*/\\r\\nMaterial.prototype.createProperty = function( name, value, type, options )\\r\\n{\\r\\n\\tif(type)\\r\\n\\t{\\r\\n\\t\\tLS.validatePropertyType(type);\\r\\n\\t\\tthis.constructor[ \\\"@\\\" + name ] = { type: type };\\r\\n\\t}\\r\\n\\r\\n\\tif(options)\\r\\n\\t{\\r\\n\\t\\tif(!this.constructor[ \\\"@\\\" + name ])\\r\\n\\t\\t\\tthis.constructor[ \\\"@\\\" + name ] = {};\\r\\n\\t\\tLS.cloneObject( options, this.constructor[ \\\"@\\\" + name ] );\\r\\n\\t}\\r\\n\\r\\n\\tif(value == null)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//basic type\\r\\n\\tif(value.constructor === Number || value.constructor === String || value.constructor === Boolean)\\r\\n\\t{\\r\\n\\t\\tthis[ name ] = value;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//for vector type\\r\\n\\tif(value.constructor === Float32Array )\\r\\n\\t{\\r\\n\\t\\tvar private_name = \\\"_\\\" + name;\\r\\n\\t\\tvalue = new Float32Array( value ); //clone\\r\\n\\t\\tthis[ private_name ] = value; //this could be removed...\\r\\n\\r\\n\\t\\tObject.defineProperty( this, name, {\\r\\n\\t\\t\\tget: function() { return value; },\\r\\n\\t\\t\\tset: function(v) { value.set( v ); },\\r\\n\\t\\t\\tenumerable: true,\\r\\n\\t\\t\\tconfigurable: true\\r\\n\\t\\t});\\r\\n\\t}\\r\\n}\\r\\n\\r\\nMaterial.prototype.prepare = function( scene )\\r\\n{\\r\\n\\tif(!this._uniforms)\\r\\n\\t{\\r\\n\\t\\tthis._uniforms = {};\\r\\n\\t\\tthis._samplers = [];\\r\\n\\t}\\r\\n\\r\\n\\tif(this.onPrepare)\\r\\n\\t\\tthis.onPrepare(scene);\\r\\n\\r\\n\\t//this.fillShaderQuery( scene ); //update shader macros on this material\\r\\n\\tthis.fillUniforms( scene ); //update uniforms\\r\\n}\\r\\n\\r\\nMaterial.prototype.getShader = function( pass_name )\\r\\n{\\r\\n\\tvar shader = Material._shader_color;\\r\\n\\tif(!shader)\\r\\n\\t\\tshader = Material._shader_color = new GL.Shader( LS.Shaders.common_vscode + \\\"void main(){ vec4 vertex = u_model * a_vertex;\\\\ngl_Position = u_viewprojection * vertex;\\\\n }\\\", LS.Shaders.common_vscode + \\\"uniform vec4 u_color;\\\\n\\\\void main(){ gl_FragColor = u_color;\\\\n }\\\");\\r\\n\\treturn shader;\\r\\n}\\r\\n\\r\\n//main function called to render an object\\r\\nMaterial.prototype.renderInstance = function( instance, render_settings, pass )\\r\\n{\\r\\n\\t//some globals\\r\\n\\tvar renderer = LS.Renderer;\\r\\n\\tvar camera = LS.Renderer._current_camera;\\r\\n\\tvar scene = LS.Renderer._current_scene;\\r\\n\\tvar model = instance.matrix;\\r\\n\\r\\n\\t//node matrix info\\r\\n\\tvar instance_final_query = instance._final_query;\\r\\n\\tvar instance_final_samplers = instance._final_samplers;\\r\\n\\tvar render_uniforms = LS.Renderer._render_uniforms;\\r\\n\\r\\n\\t//maybe this two should be somewhere else\\r\\n\\trender_uniforms.u_model = model; \\r\\n\\trender_uniforms.u_normal_model = instance.normal_matrix; \\r\\n\\r\\n\\t//global stuff\\r\\n\\tthis._render_state.enable();\\r\\n\\tLS.Renderer.bindSamplers( this._samplers );\\r\\n\\tvar global_flags = 0;\\r\\n\\r\\n\\tif(this.onRenderInstance)\\r\\n\\t\\tthis.onRenderInstance( instance );\\r\\n\\r\\n\\t//extract shader compiled\\r\\n\\tvar shader = shader_code.getShader( pass.name );\\r\\n\\tif(!shader)\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\t//assign\\r\\n\\tshader.uniformsArray( [ scene._uniforms, camera._uniforms, render_uniforms, this._uniforms, instance.uniforms ] ); \\r\\n\\r\\n\\t//render\\r\\n\\tinstance.render( shader, this._primitive != -1 ? this._primitive : undefined );\\r\\n\\trenderer._rendercalls += 1;\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n\\r\\n//LS.registerMaterialClass( Material );\\r\\nLS.registerResourceClass( Material );\\r\\nLS.Material = Material;\\r\\n///@FILE:../src/materials/shaderMaterial.js\\r\\n\\r\\n/**\\r\\n* ShaderMaterial allows to use your own shader from scratch\\r\\n* @namespace LS\\r\\n* @class ShaderMaterial\\r\\n* @constructor\\r\\n* @param {Object} object [optional] to configure from\\r\\n*/\\r\\nfunction ShaderMaterial( o )\\r\\n{\\r\\n\\tMaterial.call( this, null );\\r\\n\\r\\n\\tthis._shader = \\\"\\\"; //resource filename to a GL.ShaderCode\\r\\n\\tthis._shader_version = -1; //if the shader gets modified, the material should be modified too\\r\\n\\tthis._shader_flags = 0; //not used\\r\\n\\tthis._shader_code = null; //here the final code is stored (for debug)\\r\\n\\r\\n\\tthis._uniforms = {};\\t//uniforms to send to the shader\\r\\n\\tthis._samplers = [];\\t//textures to send to the shader\\r\\n\\tthis._properties = [];\\t//public properties to manipulate this material \\r\\n\\tthis._properties_by_name = {};\\r\\n\\r\\n\\tthis._passes = {};\\t\\t//the same ShaderCode is used for different render passes (like color, shadowmap, picking), so here we cache the final GL.Shader for every type of pass\\r\\n\\tthis._light_mode = 0;\\t//info if this material should be rendered using lights: Material.NO_LIGHTS, Material.SEVERAL_LIGHTS \\r\\n\\tthis._primitive = -1;\\t//which primitive to use when rendering this material\\r\\n\\tthis._allows_instancing = false;\\t//not supported yet\\r\\n\\r\\n\\tthis._version = -1;\\t\\r\\n\\r\\n\\tif(o) \\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\n//assign a shader from a filename to a shadercode and reprocesses the code\\r\\nObject.defineProperty( ShaderMaterial.prototype, \\\"shader\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._shader;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(v)\\r\\n\\t\\t\\tv = LS.ResourcesManager.cleanFullpath(v);\\r\\n\\t\\tif(this._shader == v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._shader_code = null;\\r\\n\\t\\tthis._shader = v;\\r\\n\\t\\tthis.processShaderCode();\\r\\n\\t}\\r\\n});\\r\\n\\r\\n//allows to assign a shader code that doesnt come from a resource (used from StandardMaterial)\\r\\nObject.defineProperty( ShaderMaterial.prototype, \\\"shader_code\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._shader_code;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._shader = null;\\r\\n\\t\\tthis._shader_code = v;\\r\\n\\t\\tthis.processShaderCode();\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( ShaderMaterial.prototype, \\\"properties\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._properties;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(!v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._properties = v;\\r\\n\\t\\tthis._properties_by_name = {};\\r\\n\\t\\tfor(var i in this._properties)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar p = this._properties[i];\\r\\n\\t\\t\\tthis._properties_by_name[ p.name ] = p;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( ShaderMaterial.prototype, \\\"enableLights\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._light_mode != 0;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._light_mode = v ? 1 : 0;\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( ShaderMaterial.prototype, \\\"version\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._version;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tconsole.error(\\\"version cannot be set manually\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\nShaderMaterial.prototype.addPass = function( name, vertex_shader, fragment_shader, macros )\\r\\n{\\r\\n\\tthis._passes[ name ] = {\\r\\n\\t\\tvertex: vertex_shader,\\r\\n\\t\\tfragment: fragment_shader,\\r\\n\\t\\tmacros: macros\\r\\n\\t};\\r\\n}\\r\\n\\r\\n//called when preparing materials before rendering the scene\\r\\nShaderMaterial.prototype.prepare = function( scene )\\r\\n{\\r\\n\\tthis.fillUniforms();\\r\\n\\r\\n\\tif( this.onPrepare )\\r\\n\\t\\tthis.onPrepare( scene );\\r\\n}\\r\\n\\r\\n//called when filling uniforms from this.prepare\\r\\nShaderMaterial.prototype.fillUniforms = function()\\r\\n{\\r\\n\\t//gather uniforms & samplers\\r\\n\\tvar samplers = this._samplers;\\r\\n\\tsamplers.length = 0;\\r\\n\\r\\n\\tthis._uniforms.u_material_color = this._color;\\r\\n\\r\\n\\tfor(var i = 0; i < this._properties.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar p = this._properties[i];\\r\\n\\t\\tif(p.internal) //internal is a property that is not for the shader (is for internal computations)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif(p.is_texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._uniforms[ p.uniform ] = samplers.length;\\r\\n\\t\\t\\tif(p.value)\\r\\n\\t\\t\\t\\tsamplers.push( p.value );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tsamplers.push( \\\" \\\" ); //force missing texture\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis._uniforms[ p.uniform ] = p.value;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//assigns a value to a property\\r\\nShaderMaterial.prototype.setProperty = function(name, value)\\r\\n{\\r\\n\\t//redirect to base material\\r\\n\\tif( Material.prototype.setProperty.call(this,name,value) )\\r\\n\\t\\treturn true;\\r\\n\\r\\n\\tif(name == \\\"shader\\\")\\r\\n\\t\\tthis.shader = value;\\r\\n\\telse if(name == \\\"properties\\\")\\r\\n\\t{\\r\\n\\t\\tthis.properties.length = 0;\\r\\n\\t\\tthis._properties_by_name = {};\\r\\n\\t\\tfor(var i = 0; i < value.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar prop = value[i];\\r\\n\\t\\t\\tif(prop.is_texture && prop.value && prop.value.constructor === String)\\r\\n\\t\\t\\t\\tprop.value = { texture: prop.value };\\r\\n\\t\\t\\tthis.properties[i] = prop;\\r\\n\\t\\t\\tthis._properties_by_name[ prop.name ] = prop;\\r\\n\\t\\t\\t//if(prop.is_texture)\\r\\n\\t\\t\\t//\\tthis._samplers.push( prop.value );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if( this._properties_by_name[ name ] )\\r\\n\\t{\\r\\n\\t\\tvar prop = this._properties_by_name[ name ];\\r\\n\\t\\tif( !prop.value || prop.value.constructor === String || !prop.value.length )\\r\\n\\t\\t\\tprop.value = value;\\r\\n\\t\\telse\\r\\n\\t\\t\\tprop.value.set( value );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\treturn false;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n//check the ShaderCode associated and applies it to this material (keeping the state of the properties)\\r\\nShaderMaterial.prototype.processShaderCode = function()\\r\\n{\\r\\n\\tif(!this._shader_code && !this._shader)\\r\\n\\t{\\r\\n\\t\\tthis._properties.length = 0;\\r\\n\\t\\tthis._properties_by_name = {};\\r\\n\\t\\tthis._passes = {};\\r\\n\\t\\tthis._samplers.length = 0;\\r\\n\\t\\treturn false;\\r\\n\\t}\\r\\n\\r\\n\\t//get shader code\\r\\n\\tvar shader_code = this._shader_code;\\r\\n\\t\\r\\n\\tif( !shader_code && this._shader )\\r\\n\\t\\tshader_code = LS.ResourcesManager.getResource( this.shader );\\r\\n\\r\\n\\tif( !shader_code || shader_code.constructor !== LS.ShaderCode )\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\tvar old_properties = this._properties_by_name;\\r\\n\\tthis._properties.length = 0;\\r\\n\\tthis._properties_by_name = {};\\r\\n\\tthis._passes = {};\\r\\n\\tthis._samplers.length = 0;\\r\\n\\tthis._light_mode = 0;\\r\\n\\tthis._primitive = -1;\\r\\n\\r\\n\\t//reset material properties\\r\\n\\tthis._queue = LS.RenderQueue.GEOMETRY;\\r\\n\\tthis._render_state.init();\\r\\n\\r\\n\\t//clear old functions\\r\\n\\tfor(var i in this)\\r\\n\\t{\\r\\n\\t\\tif(!this.hasOwnProperty(i))\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif( this[i] && this[i].constructor === Function )\\r\\n\\t\\t\\tdelete this[i];\\r\\n\\t}\\r\\n\\r\\n\\t//apply init \\r\\n\\tif( shader_code._functions.init )\\r\\n\\t{\\r\\n\\t\\tif(!LS.catch_exceptions)\\r\\n\\t\\t\\tshader_code._functions.init.call( this );\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\ttry\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tshader_code._functions.init.call( this );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tLS.dispatchCodeError(err);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i in shader_code._global_uniforms)\\r\\n\\t{\\r\\n\\t\\tvar global = shader_code._global_uniforms[i];\\r\\n\\t\\tif( global.disabled ) //in case this var is not found in the shader\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tthis.createUniform( global.name, global.uniform, global.type, global.value, global.options );\\r\\n\\t}\\r\\n\\r\\n\\t//set version before asssignOldProperties\\r\\n\\tthis._shader_version = shader_code._version;\\r\\n\\tthis._version++;\\r\\n\\r\\n\\t//restore old values\\r\\n\\tthis.assignOldProperties( old_properties );\\r\\n}\\r\\n\\r\\n//used after changing the code of the ShaderCode and wanting to reload the material keeping the old properties\\r\\nShaderMaterial.prototype.assignOldProperties = function( old_properties )\\r\\n{\\r\\n\\t//get shader code\\r\\n\\tvar shader = null;\\r\\n\\tvar shader_code = this.getShaderCode(); //no parameters because we just want the render_state and init stuff\\r\\n\\tif( shader_code )\\r\\n\\t\\tshader = shader_code.getShader();\\r\\n\\r\\n\\tfor(var i = 0; i < this._properties.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar new_prop = this._properties[i];\\r\\n\\r\\n\\t\\tif(!old_properties[ new_prop.name ])\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar old = old_properties[ new_prop.name ];\\r\\n\\t\\tif(old.value === undefined)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t//validate (avoids error if we change the type of a uniform and try to reassign a value)\\r\\n\\t\\tif( !old.internal && shader && !new_prop.is_texture ) //textures are not validated (because they are samplers, not values)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar uniform_info = shader.uniformInfo[ new_prop.uniform ];\\r\\n\\t\\t\\tif(!uniform_info)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif(new_prop.value !== undefined)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( !GL.Shader.validateValue( new_prop.value, uniform_info ) )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tnew_prop.value = undefined;\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//this is to keep current values when coding the shader from the editor\\r\\n\\t\\tif( new_prop.value && new_prop.value.set ) //special case for typed arrays avoiding generating GC\\r\\n\\t\\t{\\r\\n\\t\\t\\t//this is to be careful when an array changes sizes\\r\\n\\t\\t\\tif( old.value && old.value.length && new_prop.value.length && old.value.length <= new_prop.value.length)\\r\\n\\t\\t\\t\\tnew_prop.value.set( old.value );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tnew_prop.value = old.value;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tnew_prop.value = old.value;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nShaderMaterial.nolights_vec4 = new Float32Array([0,0,0,1]);\\r\\n\\r\\n//called from LS.Renderer when rendering an instance\\r\\nShaderMaterial.prototype.renderInstance = function( instance, render_settings, pass )\\r\\n{\\r\\n\\t//get shader code\\r\\n\\tvar shader_code = this.getShaderCode( instance, render_settings, pass );\\r\\n\\tif(!shader_code || shader_code.constructor !== LS.ShaderCode )\\r\\n\\t\\treturn true; //skip rendering\\r\\n\\r\\n\\t//this is in case the shader has been modified in the editor (reapplies the shadercode to the material)\\r\\n\\tif( shader_code._version !== this._shader_version && this.processShaderCode )\\r\\n\\t\\tthis.processShaderCode();\\r\\n\\r\\n\\t//some globals\\r\\n\\tvar renderer = LS.Renderer;\\r\\n\\tvar camera = LS.Renderer._current_camera;\\r\\n\\tvar scene = LS.Renderer._current_scene;\\r\\n\\tvar model = instance.matrix;\\r\\n\\tvar renderer_uniforms = LS.Renderer._uniforms;\\r\\n\\r\\n\\t//maybe this two should be somewhere else\\r\\n\\trenderer_uniforms.u_model = model; \\r\\n\\trenderer_uniforms.u_normal_model = instance.normal_matrix; \\r\\n\\r\\n\\t//compute flags: checks the ShaderBlocks attached to this instance and resolves the flags\\r\\n\\tvar block_flags = instance.computeShaderBlockFlags();\\r\\n\\tvar global_flags = LS.Renderer._global_shader_blocks_flags;\\r\\n\\r\\n\\t//find environment texture\\r\\n\\tif( pass == COLOR_PASS ) //allow reflections only in color pass\\r\\n\\t{\\r\\n\\t\\tglobal_flags |= LS.ShaderMaterial.reflection_block.flag_mask;\\r\\n\\r\\n\\t\\tvar environment_sampler = this.textures[\\\"environment\\\"];\\r\\n\\t\\tvar environment_texture = environment_sampler && environment_sampler.texture ? environment_sampler.texture : null;\\r\\n\\r\\n\\t\\tif( !environment_texture ) //use global\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( LS.Renderer._global_textures.environment )\\r\\n\\t\\t\\t\\tenvironment_texture = LS.Renderer._global_textures.environment;\\r\\n\\t\\t\\tif( instance._nearest_reflection_probe )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( instance._nearest_reflection_probe._texture )\\r\\n\\t\\t\\t\\t\\tenvironment_texture = instance._nearest_reflection_probe._tex_id;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( environment_texture )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar tex = LS.ResourcesManager.textures[ environment_texture ];\\r\\n\\t\\t\\tif( tex && tex.texture_type == GL.TEXTURE_2D )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( tex._is_planar )\\r\\n\\t\\t\\t\\t\\tglobal_flags |= environment_planar_block.flag_mask;\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tglobal_flags |= environment_2d_block.flag_mask;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tglobal_flags |= environment_cubemap_block.flag_mask;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._samplers[ LS.Renderer.ENVIRONMENT_TEXTURE_SLOT ] = environment_texture;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tthis._samplers[ LS.Renderer.ENVIRONMENT_TEXTURE_SLOT ] = null;\\r\\n\\t}\\r\\n\\r\\n\\t//global stuff\\r\\n\\tthis._render_state.enable( render_settings );\\r\\n\\tLS.Renderer.bindSamplers( this._samplers ); //material samplers\\r\\n\\tLS.Renderer.bindSamplers( instance.samplers ); //RI samplers (like morph targets encoded in textures)\\r\\n\\r\\n\\t//blocks for extra streams\\r\\n\\tif( instance.vertex_buffers[\\\"colors\\\"] )\\r\\n\\t\\tblock_flags |= LS.Shaders.vertex_color_block.flag_mask;\\r\\n\\tif( instance.vertex_buffers[\\\"coords1\\\"] )\\r\\n\\t\\tblock_flags |= LS.Shaders.coord1_block.flag_mask;\\r\\n\\r\\n\\t//for those cases\\r\\n\\tif(this.onRenderInstance)\\r\\n\\t\\tthis.onRenderInstance( instance );\\r\\n\\r\\n\\tif( pass == SHADOW_PASS )\\r\\n\\t{\\r\\n\\t\\t//global flags (like environment maps, irradiance, etc)\\r\\n\\t\\tblock_flags |= LS.Shaders.firstpass_block.flag_mask;\\r\\n\\t\\tblock_flags |= LS.Shaders.lastpass_block.flag_mask;\\r\\n\\t\\t//extract shader compiled\\r\\n\\t\\tvar shader = shader_code.getShader( pass.name, block_flags ); //pass.name\\r\\n\\t\\tif(!shader)\\r\\n\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\t//assign\\r\\n\\t\\tshader.uniformsArray( [ scene._uniforms, camera._uniforms, renderer_uniforms, this._uniforms, instance.uniforms ] ); //removed, why this was in?? light ? light._uniforms : null, \\r\\n\\r\\n\\t\\t//render\\r\\n\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\tinstance.render( shader, this._primitive != -1 ? this._primitive : undefined );\\r\\n\\t\\trenderer._rendercalls += 1;\\r\\n\\t\\r\\n\\t\\treturn true;\\r\\n\\t}\\r\\n\\r\\n\\t//add flags related to lights\\r\\n\\tvar lights = null;\\r\\n\\r\\n\\t//ignore lights renders the object with flat illumination\\r\\n\\tvar ignore_lights = pass != COLOR_PASS || render_settings.lights_disabled || this._light_mode === Material.NO_LIGHTS;\\r\\n\\r\\n\\tif( !ignore_lights )\\r\\n\\t\\tlights = LS.Renderer.getNearLights( instance );\\r\\n\\r\\n\\tif(LS.Renderer._use_normalbuffer)\\r\\n\\t\\tblock_flags |= LS.Shaders.normalbuffer_block.flag_mask;\\r\\n\\r\\n\\t//if no lights are set or the render mode is flat\\r\\n\\tif( !lights || lights.length == 0 || ignore_lights )\\r\\n\\t{\\r\\n\\t\\t//global flags (like environment maps, irradiance, etc)\\r\\n\\t\\tif( !ignore_lights )\\r\\n\\t\\t\\tblock_flags |= global_flags;\\r\\n\\t\\tblock_flags |= LS.Shaders.firstpass_block.flag_mask;\\r\\n\\t\\tblock_flags |= LS.Shaders.lastpass_block.flag_mask;\\r\\n\\r\\n\\t\\t//extract shader compiled\\r\\n\\t\\tvar shader = shader_code.getShader( null, block_flags ); //pass.name\\r\\n\\t\\tif(!shader)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//var shader = shader_code.getShader( \\\"surface\\\", block_flags );\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//assign\\r\\n\\t\\tshader.uniformsArray( [ scene._uniforms, camera._uniforms, renderer_uniforms, this._uniforms, instance.uniforms ] ); //removed, why this was in?? light ? light._uniforms : null, \\r\\n\\r\\n\\t\\tshader.setUniform( \\\"u_light_info\\\", ShaderMaterial.nolights_vec4 );\\r\\n\\t\\tif( ignore_lights )\\r\\n\\t\\t\\tshader.setUniform( \\\"u_ambient_light\\\", LS.ONES );\\r\\n\\r\\n\\t\\t//render\\r\\n\\t\\tinstance.render( shader, this._primitive != -1 ? this._primitive : undefined );\\r\\n\\t\\trenderer._rendercalls += 1;\\r\\n\\t\\r\\n\\t\\treturn true;\\r\\n\\t}\\r\\n\\r\\n\\tvar base_block_flags = block_flags;\\r\\n\\r\\n\\tvar uniforms_array = [ scene._uniforms, camera._uniforms, renderer_uniforms, null, this._uniforms, instance.uniforms ];\\r\\n\\r\\n\\t//render multipass with several lights\\r\\n\\tvar prev_shader = null;\\r\\n\\tfor(var i = 0, l = lights.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar light = lights[i];\\r\\n\\t\\tblock_flags = light.applyShaderBlockFlags( base_block_flags, pass, render_settings );\\r\\n\\r\\n\\t\\t//global\\r\\n\\t\\tblock_flags |= global_flags;\\r\\n\\r\\n\\t\\t//shaders require to know in which pass they are (ambient is applied in the first, reflections in the last)\\r\\n\\t\\tif(i == 0)\\r\\n\\t\\t\\tblock_flags |= LS.Shaders.firstpass_block.flag_mask;\\r\\n\\t\\tif(i == l - 1)\\r\\n\\t\\t\\tblock_flags |= LS.Shaders.lastpass_block.flag_mask;\\r\\n\\r\\n\\t\\t//extract shader compiled\\r\\n\\t\\tvar shader = shader_code.getShader( null, block_flags );\\r\\n\\t\\tif(!shader)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"material without pass: \\\" + pass.name );\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//light texture like shadowmap and cookie\\r\\n\\t\\tLS.Renderer.bindSamplers( light._samplers );\\r\\n\\r\\n\\t\\t//light parameters (like index of pass or num passes)\\r\\n\\t\\tlight._uniforms.u_light_info[2] = i; //num pass\\r\\n\\t\\tlight._uniforms.u_light_info[3] = lights.length; //total passes\\r\\n\\t\\tuniforms_array[3] = light._uniforms;\\r\\n\\r\\n\\t\\t//assign\\r\\n\\t\\tif(prev_shader != shader)\\r\\n\\t\\t\\tshader.uniformsArray( uniforms_array );\\r\\n\\t\\telse\\r\\n\\t\\t\\tshader.uniforms( light._uniforms );\\r\\n\\t\\tprev_shader = shader;\\r\\n\\r\\n\\t\\tif(i == 1)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.depthMask( false );\\r\\n\\t\\t\\tgl.depthFunc( gl.EQUAL );\\r\\n\\t\\t\\tgl.enable( gl.BLEND );\\r\\n\\t\\t\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//render\\r\\n\\t\\tinstance.render( shader, this._primitive != -1 ? this._primitive : undefined );\\r\\n\\t\\trenderer._rendercalls += 1;\\r\\n\\t}\\r\\n\\r\\n\\t//optimize this\\r\\n\\tgl.disable( gl.BLEND );\\r\\n\\tgl.depthMask( true );\\r\\n\\tgl.depthFunc( gl.LESS );\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\nShaderMaterial.prototype.renderPickingInstance = function( instance, render_settings, pass )\\r\\n{\\r\\n\\t//get shader code\\r\\n\\tvar shader_code = this.getShaderCode( instance, render_settings, pass );\\r\\n\\tif(!shader_code || shader_code.constructor !== LS.ShaderCode )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//some globals\\r\\n\\tvar renderer = LS.Renderer;\\r\\n\\tvar camera = LS.Renderer._current_camera;\\r\\n\\tvar scene = LS.Renderer._current_scene;\\r\\n\\tvar model = instance.matrix;\\r\\n\\tvar node = instance.node;\\r\\n\\tvar renderer_uniforms = LS.Renderer._uniforms;\\r\\n\\r\\n\\t//maybe this two should be somewhere else\\r\\n\\trenderer_uniforms.u_model = model; \\r\\n\\trenderer_uniforms.u_normal_model = instance.normal_matrix; \\r\\n\\r\\n\\t//compute flags\\r\\n\\tvar block_flags = instance.computeShaderBlockFlags();\\r\\n\\r\\n\\t//global stuff\\r\\n\\tthis._render_state.enable( render_settings );\\r\\n\\tLS.Renderer.bindSamplers( this._samplers );\\r\\n\\tLS.Renderer.bindSamplers( instance.samplers );\\r\\n\\r\\n\\t//extract shader compiled\\r\\n\\tvar shader = shader_code.getShader( pass.name, block_flags );\\r\\n\\tif(!shader)\\r\\n\\t{\\r\\n\\t\\tshader_code = LS.ShaderMaterial.getDefaultPickingShaderCode();\\r\\n\\t\\tshader = shader_code.getShader( pass.name, block_flags );\\r\\n\\t\\tif(!shader)\\r\\n\\t\\t\\treturn false; //??!\\r\\n\\t}\\r\\n\\r\\n\\t//assign uniforms\\r\\n\\tshader.uniformsArray( [ camera._uniforms, renderer_uniforms, this._uniforms, instance.uniforms ] );\\r\\n\\r\\n\\t//set color\\r\\n\\tvar pick_color = LS.Picking.getNextPickingColor( instance.picking_node || node );\\r\\n\\tshader.setUniform(\\\"u_material_color\\\", pick_color );\\r\\n\\r\\n\\t//render\\r\\n\\tinstance.render( shader, this._primitive != -1 ? this._primitive : undefined );\\r\\n\\trenderer._rendercalls += 1;\\r\\n\\r\\n\\t//optimize this\\r\\n\\tgl.disable( gl.BLEND );\\r\\n\\tgl.depthMask( true );\\r\\n\\tgl.depthFunc( gl.LESS );\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n//used by the editor to know which possible texture channels are available\\r\\nShaderMaterial.prototype.getTextureChannels = function()\\r\\n{\\r\\n\\tvar channels = [];\\r\\n\\r\\n\\tfor(var i in this._properties)\\r\\n\\t{\\r\\n\\t\\tvar p = this._properties[i];\\r\\n\\t\\tif(p.is_texture)\\r\\n\\t\\t\\tchannels.push( p.name );\\r\\n\\t}\\r\\n\\r\\n\\treturn channels;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Collects all the resources needed by this material (textures)\\r\\n* @method getResources\\r\\n* @param {Object} resources object where all the resources are stored\\r\\n* @return {Texture}\\r\\n*/\\r\\nShaderMaterial.prototype.getResources = function ( res )\\r\\n{\\r\\n\\tif(this.shader)\\r\\n\\t\\tres[ this.shader ] = LS.ShaderCode;\\r\\n\\r\\n\\tfor(var i in this._properties)\\r\\n\\t{\\r\\n\\t\\tvar p = this._properties[i];\\r\\n\\t\\tif(p.value && p.is_texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!p.value)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar name = null;\\r\\n\\t\\t\\tif(p.value.texture)\\r\\n\\t\\t\\t\\tname = \\tp.value.texture;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tname = res[ p.value ];\\r\\n\\t\\t\\tif(name && name.constructor === String)\\r\\n\\t\\t\\t\\tres[name] = GL.Texture;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\n\\r\\nShaderMaterial.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif( path.length < 1)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar info = Material.prototype.getPropertyInfoFromPath.call(this,path);\\r\\n\\tif(info)\\r\\n\\t\\treturn info;\\r\\n\\r\\n\\tvar varname = path[0];\\r\\n\\r\\n\\tfor(var i = 0, l = this.properties.length; i < l; ++i )\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.name != varname)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tnode: this._root,\\r\\n\\t\\t\\ttarget: this,\\r\\n\\t\\t\\tname: prop.name,\\r\\n\\t\\t\\tvalue: prop.value,\\r\\n\\t\\t\\ttype: prop.type\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\treturn;\\r\\n}\\r\\n\\r\\n//get shader code\\r\\nShaderMaterial.prototype.getShaderCode = function( instance, render_settings, pass )\\r\\n{\\r\\n\\tvar shader_code = this._shader_code || LS.ResourcesManager.getResource( this._shader );\\r\\n\\tif(!shader_code || shader_code.constructor !== LS.ShaderCode )\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\t//this is in case the shader has been modified in the editor (reapplies the shadercode to the material)\\r\\n\\tif( shader_code._version !== this._shader_version && this.processShaderCode )\\r\\n\\t{\\r\\n\\t\\tshader_code._version = this._shader_version;\\r\\n\\t\\tthis.processShaderCode();\\r\\n\\t}\\r\\n\\r\\n\\treturn shader_code;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Takes an input texture and applies the ShaderMaterial, the result is shown on the viewport or stored in the output_texture\\r\\n* The ShaderCode must contain a \\\"fx\\\" method.\\r\\n* Similar to the method BlitTexture in Unity\\r\\n* @method applyToTexture\\r\\n* @param {Texture} input_texture\\r\\n* @param {Texture} output_texture [optional] where to store the result, if omitted it will be shown in the viewport\\r\\n*/\\r\\nShaderMaterial.prototype.applyToTexture = function( input_texture, output_texture )\\r\\n{\\r\\n\\tif( !this.shader || !input_texture )\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\t//get shader code\\r\\n\\tvar shader_code = this.getShaderCode(); //special use\\r\\n\\tif(!shader_code)\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\t//extract shader compiled\\r\\n\\tvar shader = shader_code.getShader(\\\"fx\\\");\\r\\n\\tif(!shader)\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\t//global vars\\r\\n\\tthis.fillUniforms();\\r\\n\\tthis._uniforms.u_time = LS.GlobalScene._time;\\r\\n\\tthis._uniforms.u_viewport = gl.viewport_data;\\r\\n\\r\\n\\t//bind samplers\\r\\n\\tLS.Renderer.bindSamplers( this._samplers );\\r\\n\\r\\n\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\tgl.disable( gl.CULL_FACE );\\r\\n\\r\\n\\t//render\\r\\n\\tif(!output_texture)\\r\\n\\t\\tinput_texture.toViewport( shader, this._uniforms );\\r\\n\\telse\\r\\n\\t\\toutput_texture.drawTo( function(){\\r\\n\\t\\t\\tinput_texture.toViewport( shader, this._uniforms );\\r\\n\\t\\t});\\r\\n}\\r\\n\\r\\n/**\\r\\n* Makes one shader variable (uniform) public so it can be assigned from the engine (or edited from the editor)\\r\\n* @method createUniform\\r\\n* @param {String} name the property name as it should be shown\\r\\n* @param {String} uniform the uniform name in the shader\\r\\n* @param {String} type the var type in case we want to edit it (use LS.TYPES)\\r\\n* @param {*} value\\r\\n* @param {Object} options an object containing all the possible options (used mostly for widgets)\\r\\n*/\\r\\nShaderMaterial.prototype.createUniform = function( name, uniform, type, value, options )\\r\\n{\\r\\n\\tif(!name || !uniform)\\r\\n\\t\\tthrow(\\\"parameter missing in createUniform\\\");\\r\\n\\r\\n\\t//\\r\\n\\ttype = type || \\\"Number\\\";\\r\\n\\tif( type.constructor !== String )\\r\\n\\t\\tthrow(\\\"type must be string\\\");\\r\\n\\r\\n\\t//cast to typed-array\\r\\n\\tvalue = value || 0;\\r\\n\\tif(value && value.length)\\r\\n\\t\\tvalue = new Float32Array( value );//cast them always\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\t//create a value, otherwise is null\\r\\n\\t\\tswitch (type)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase \\\"vec2\\\": value = vec2.create(); break;\\r\\n\\t\\t\\tcase \\\"color\\\":\\r\\n\\t\\t\\tcase \\\"vec3\\\": value = vec3.create(); break;\\r\\n\\t\\t\\tcase \\\"color4\\\":\\r\\n\\t\\t\\tcase \\\"vec4\\\": value = vec4.create(); break;\\r\\n\\t\\t\\tcase \\\"mat3\\\": value = mat3.create(); break;\\r\\n\\t\\t\\tcase \\\"mat4\\\": value = mat4.create(); break;\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//define info\\r\\n\\tvar prop = { name: name, uniform: uniform, value: value, type: type, is_texture: 0 };\\r\\n\\r\\n\\t//mark as texture (because this need to go to the textures container so they are binded)\\r\\n\\tif(type.toLowerCase() == \\\"texture\\\" || type == \\\"sampler2D\\\" || type == \\\"samplerCube\\\" || type == \\\"sampler\\\")\\r\\n\\t\\tprop.is_texture = (type == \\\"samplerCube\\\") ? 2 : 1;\\r\\n\\r\\n\\tif(prop.is_texture)\\r\\n\\t{\\r\\n\\t\\tprop.sampler = {};\\r\\n\\t\\tprop.type = \\\"sampler\\\";\\r\\n\\t\\tprop.sampler_slot = this._samplers.length;\\r\\n\\t\\tthis._samplers.push( prop.sampler );\\r\\n\\t}\\r\\n\\r\\n\\tif(options)\\r\\n\\t\\tfor(var i in options)\\r\\n\\t\\t\\tprop[i] = options[i];\\r\\n\\r\\n\\tthis._properties.push( prop );\\r\\n\\tthis._properties_by_name[ name ] = prop;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Similar to createUniform but for textures, it helps specifying sampler options\\r\\n* @method createSampler\\r\\n* @param {String} name the property name as it should be shown\\r\\n* @param {String} uniform the uniform name in the shader\\r\\n* @param {Object} options an object containing all the possible options (used mostly for widgets)\\r\\n* @param {String} value default value (texture name)\\r\\n*/\\r\\nShaderMaterial.prototype.createSampler = function( name, uniform, sampler_options, value )\\r\\n{\\r\\n\\tif(!name || !uniform)\\r\\n\\t\\tthrow(\\\"parameter missing in createSampler\\\");\\r\\n\\r\\n\\tvar type = \\\"sampler\\\";\\r\\n\\tif( sampler_options && sampler_options.type )\\r\\n\\t\\ttype = sampler_options.type;\\r\\n\\r\\n\\tvar sampler = null;\\r\\n\\r\\n\\t//do not overwrite\\r\\n\\tif( this._properties_by_name[ name ] )\\r\\n\\t{\\r\\n\\t\\tvar current_prop = this._properties_by_name[ name ];\\r\\n\\t\\tif( current_prop.type == type && current_prop.value )\\r\\n\\t\\t\\tsampler = current_prop.value;\\r\\n\\t}\\r\\n\\r\\n\\tif(!sampler)\\r\\n\\t\\tsampler = {\\r\\n\\t\\t\\ttexture: value\\r\\n\\t\\t};\\r\\n\\r\\n\\tvar prop = { name: name, uniform: uniform, value: sampler, type: type, is_texture: 1, sampler_slot: -1 };\\r\\n\\r\\n\\tif(sampler_options)\\r\\n\\t{\\r\\n\\t\\tif(sampler_options.filter)\\r\\n\\t\\t{\\r\\n\\t\\t\\tsampler.magFilter = sampler_options.filter;\\r\\n\\t\\t\\tsampler.minFilter = sampler_options.filter;\\r\\n\\t\\t\\tdelete sampler_options.filter;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(sampler_options.wrap)\\r\\n\\t\\t{\\r\\n\\t\\t\\tsampler.wrapS = sampler_options.wrap;\\r\\n\\t\\t\\tsampler.wrapT = sampler_options.wrap;\\r\\n\\t\\t\\tdelete sampler_options.wrap;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var i in sampler_options)\\r\\n\\t\\t\\tsampler[i] = sampler_options[i];\\r\\n\\t}\\r\\n\\tprop.sampler_slot = this._samplers.length;\\r\\n\\tthis._properties.push( prop );\\r\\n\\tthis._properties_by_name[ name ] = prop;\\r\\n\\tthis._samplers.push( prop.value );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Creates a property for this material, this property wont be passed to the shader but can be used from source code.\\r\\n* You must used this function if you want the data to be stored when serializing or changing the ShaderCode\\r\\n* @method createProperty\\r\\n* @param {String} name the property name as it should be shown\\r\\n* @param {*} value the default value\\r\\n* @param {String} type the data type (use LS.TYPES)\\r\\n* @param {Object} options an object containing all the possible options (used mostly for widgets)\\r\\n*/\\r\\nShaderMaterial.prototype.createProperty = function( name, value, type, options )\\r\\n{\\r\\n\\tvar prop = this._properties_by_name[ name ];\\r\\n\\tif(prop && prop.type == type) //already exist with the same type\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tprop = { name: name, type: type, internal: true, value: value };\\r\\n\\tif(options)\\r\\n\\t\\tfor(var i in options)\\r\\n\\t\\t\\tprop[i] = options[i];\\r\\n\\r\\n\\tthis._properties.push( prop );\\r\\n\\tthis._properties_by_name[ name ] = prop;\\r\\n\\r\\n\\tObject.defineProperty( this, name, {\\r\\n\\t\\tget: function() { \\r\\n\\t\\t\\tvar prop = this._properties_by_name[ name ]; //fetch it because could have been overwritten\\r\\n\\t\\t\\tif(prop)\\r\\n\\t\\t\\t\\treturn prop.value;\\r\\n\\t\\t},\\r\\n\\t\\tset: function(v) { \\r\\n\\t\\t\\tvar prop = this._properties_by_name[ name ]; //fetch it because could have been overwritten\\r\\n\\t\\t\\tif(!prop)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tif(prop.value && prop.value.set) //for typed arrays\\r\\n\\t\\t\\t\\tprop.value.set( v );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tprop.value = v;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false, //must not be serialized\\r\\n\\t\\tconfigurable: true //allows to overwrite this property\\r\\n\\t});\\r\\n}\\r\\n\\r\\n/**\\r\\n* Event used to inform if one resource has changed its name\\r\\n* @method onResourceRenamed\\r\\n* @param {Object} resources object where all the resources are stored\\r\\n* @return {Boolean} true if something was modified\\r\\n*/\\r\\nShaderMaterial.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tvar v = Material.prototype.onResourceRenamed.call(this, old_name, new_name, resource );\\r\\n\\tif( this.shader == old_name)\\r\\n\\t{\\r\\n\\t\\tthis.shader = new_name;\\r\\n\\t\\tv = true;\\r\\n\\t}\\r\\n\\r\\n\\t//change texture also in shader values... (this should be automatic but it is not)\\r\\n\\tfor(var i = 0; i < this._properties.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar p = this._properties[i];\\r\\n\\t\\tif(p.internal) //internal is a property that is not for the shader (is for internal computations)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif( !p.is_texture || !p.value )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif( p.value.texture != old_name )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tp.value.texture = new_name;\\r\\n\\t\\tv = true;\\r\\n\\t}\\r\\n\\r\\n\\treturn v;\\r\\n}\\r\\n\\r\\nShaderMaterial.getDefaultPickingShaderCode = function()\\r\\n{\\r\\n\\tif( ShaderMaterial.default_picking_shader_code )\\r\\n\\t\\treturn ShaderMaterial.default_picking_shader_code;\\r\\n\\tvar sc = new LS.ShaderCode();\\r\\n\\tsc.code = LS.ShaderCode.flat_code;\\r\\n\\tShaderMaterial.default_picking_shader_code = sc;\\r\\n\\treturn sc;\\r\\n}\\r\\n\\r\\nLS.registerMaterialClass( ShaderMaterial );\\r\\nLS.ShaderMaterial = ShaderMaterial;\\r\\n\\r\\n///@FILE:../src/materials/standardMaterial.js\\r\\n//modes\\r\\n//- per texture\\r\\n//- texture coordinates\\r\\n//- vertex color and extras\\r\\n//- alpha test\\r\\n\\r\\n//StandardMaterial class **************************\\r\\n/* Warning: a material is not a component, because it can be shared by multiple nodes */\\r\\n\\r\\n/**\\r\\n* StandardMaterial class improves the material class\\r\\n* @namespace LS\\r\\n* @class StandardMaterial\\r\\n* @constructor\\r\\n* @param {Object} object [optional] to configure from\\r\\n*/\\r\\n\\r\\nfunction StandardMaterial(o)\\r\\n{\\r\\n\\tShaderMaterial.call(this,null); //do not pass the data object, it is called later\\r\\n\\r\\n\\tthis.blend_mode = LS.Blend.NORMAL;\\r\\n\\r\\n\\tthis.createProperty( \\\"diffuse\\\", new Float32Array([1.0,1.0,1.0]), \\\"color\\\" );\\r\\n\\tthis.createProperty( \\\"ambient\\\", new Float32Array([1.0,1.0,1.0]), \\\"color\\\" );\\r\\n\\tthis.createProperty( \\\"emissive\\\", new Float32Array([0,0,0,0]), \\\"color\\\" ); //fourth component to control if emissive is affected by albedo\\r\\n\\r\\n\\tthis._specular_data = vec4.fromValues( 0.1, 10.0, 0.0, 0.0 ); //specular factor, glossiness, specular_on_top\\r\\n\\tthis.specular_on_top = false;\\r\\n\\tthis.specular_on_alpha = false;\\r\\n\\r\\n\\tthis.backlight_factor = 0;\\r\\n\\r\\n\\tthis.reflection_factor = 0.0;\\r\\n\\tthis.reflection_fresnel = 1.0;\\r\\n\\tthis.reflection_specular = false;\\r\\n\\r\\n\\tthis.createProperty( \\\"velvet\\\", new Float32Array([0.5,0.5,0.5]), \\\"color\\\" );\\r\\n\\tthis.velvet_exp = 0.0;\\r\\n\\tthis.velvet_additive = false;\\r\\n\\tthis._velvet_info = vec4.create();\\r\\n\\r\\n\\tthis._detail = new Float32Array([0.0, 10, 10]);\\r\\n\\r\\n\\tthis.normalmap_factor = 1.0;\\r\\n\\tthis.normalmap_tangent = true;\\r\\n\\tthis.bumpmap_factor = 1.0;\\r\\n\\r\\n\\tthis.displacementmap_factor = 0.1;\\r\\n\\tthis._texture_settings = new Uint8Array(9);\\r\\n\\r\\n\\tthis.use_scene_ambient = true;\\r\\n\\r\\n\\tthis.createProperty( \\\"extra\\\", new Float32Array([1,1,1,1]), \\\"color\\\" ); //used in special situations\\r\\n\\r\\n\\t//used to change the render state\\r\\n\\tthis.flags = {\\r\\n\\t\\talpha_test: false,\\r\\n\\t\\talpha_test_shadows: false,\\r\\n\\t\\ttwo_sided: false,\\r\\n\\t\\tflip_normals: false,\\r\\n\\t\\tdepth_test: true,\\r\\n\\t\\tdepth_write: true,\\r\\n\\t\\tignore_lights: false,\\r\\n\\t\\tcast_shadows: true,\\r\\n\\t\\treceive_shadows: true,\\r\\n//\\t\\tflat_normals: false,\\r\\n\\t\\tignore_frustum: false\\r\\n\\t};\\r\\n\\r\\n\\t//used for special fx \\r\\n\\tthis._uniforms = {\\r\\n\\t\\tu_material_color: this._color,\\r\\n\\t\\tu_ambient_color: this._ambient,\\r\\n\\t\\tu_emissive_color: this._emissive,\\r\\n\\t\\tu_specular: this._specular_data,\\r\\n\\t\\tu_reflection_info: vec2.create(), //factor and fresnel\\r\\n\\t\\tu_velvet_info: vec4.create(),\\r\\n\\t\\tu_normal_info: vec2.create(),\\r\\n\\t\\tu_detail_info: this._detail,\\r\\n\\t\\tu_texture_matrix: this.uvs_matrix,\\r\\n\\t\\tu_extra_color: this._extra,\\r\\n\\t\\tu_texture_settings: this._texture_settings\\r\\n\\t};\\r\\n\\r\\n\\tthis._samplers = [];\\r\\n\\r\\n\\tthis._allows_instancing = true;\\r\\n\\tthis.needsUpdate = true;\\r\\n\\r\\n\\tif(o) \\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\n\\r\\nObject.defineProperty( StandardMaterial.prototype, 'detail_factor', {\\r\\n\\tget: function() { return this._detail[0]; },\\r\\n\\tset: function(v) { this._detail[0] = v; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( StandardMaterial.prototype, 'detail_scale', {\\r\\n\\tget: function() { return this._detail.subarray(1,3); },\\r\\n\\tset: function(v) { this._detail[1] = v[0]; this._detail[2] = v[1]; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( StandardMaterial.prototype, 'emissive_extra', {\\r\\n\\tget: function() { return this._emissive[3]; },\\r\\n\\tset: function(v) { this._emissive[3] = v; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( StandardMaterial.prototype, 'specular_factor', {\\r\\n\\tget: function() { return this._specular_data[0]; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif( v != null && v.constructor === Number)\\r\\n\\t\\t\\tthis._specular_data[0] = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( StandardMaterial.prototype, 'specular_gloss', {\\r\\n\\tget: function() { return this._specular_data[1]; },\\r\\n\\tset: function(v) { this._specular_data[1] = v; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nStandardMaterial[\\\"@blend_mode\\\"] = { type: \\\"enum\\\", values: LS.Blend };\\r\\nStandardMaterial.actions = {};\\r\\n\\r\\nStandardMaterial.DETAIL_TEXTURE = \\\"detail\\\";\\r\\nStandardMaterial.NORMAL_TEXTURE = \\\"normal\\\";\\r\\nStandardMaterial.DISPLACEMENT_TEXTURE = \\\"displacement\\\";\\r\\nStandardMaterial.BUMP_TEXTURE = \\\"bump\\\";\\r\\nStandardMaterial.REFLECTIVITY_TEXTURE = \\\"reflectivity\\\";\\r\\nStandardMaterial.EXTRA_TEXTURE = \\\"extra\\\";\\r\\nStandardMaterial.IRRADIANCE_TEXTURE = \\\"irradiance\\\";\\r\\n\\r\\nStandardMaterial.TEXTURES_INDEX = { \\\"color\\\":0, \\\"opacity\\\":1, \\\"ambient\\\":2, \\\"specular\\\":3, \\\"emissive\\\":4, \\\"detail\\\":5, \\\"normal\\\":6, \\\"displacement\\\":7, \\\"bump\\\":8, \\\"reflectivity\\\":9, \\\"extra\\\":10, \\\"environment\\\":11 };\\r\\n\\r\\nStandardMaterial.prototype.renderInstance = ShaderMaterial.prototype.renderInstance;\\r\\nStandardMaterial.prototype.renderShadowInstance = ShaderMaterial.prototype.renderShadowInstance;\\r\\nStandardMaterial.prototype.renderPickingInstance = ShaderMaterial.prototype.renderPickingInstance;\\r\\n\\r\\n//called from LS.Renderer.processVisibleData\\r\\nStandardMaterial.prototype.prepare = function( scene )\\r\\n{\\r\\n\\tvar flags = this.flags;\\r\\n\\r\\n\\tvar render_state = this._render_state;\\r\\n\\r\\n\\tif(!this._texture_settings) //HACK to fix BUG\\r\\n\\t\\tthis._texture_settings = this._uniforms.u_texture_settings = new Uint8Array(9);\\r\\n\\r\\n\\t//set flags in render state\\r\\n\\trender_state.cull_face = !flags.two_sided;\\r\\n\\trender_state.front_face = flags.flip_normals ? GL.CW : GL.CCW;\\r\\n\\trender_state.depth_test = flags.depth_test;\\r\\n\\trender_state.depth_mask = flags.depth_write;\\r\\n\\r\\n\\trender_state.blend = this.blend_mode != LS.Blend.NORMAL;\\r\\n\\tif( this.blend_mode != LS.Blend.NORMAL )\\r\\n\\t{\\r\\n\\t\\tvar func = LS.BlendFunctions[ this.blend_mode ];\\r\\n\\t\\tif(func)\\r\\n\\t\\t{\\r\\n\\t\\t\\trender_state.blendFunc0 = func[0];\\r\\n\\t\\t\\trender_state.blendFunc1 = func[1];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i in this.textures)\\r\\n\\t{\\r\\n\\t\\tvar tex = this.textures[i];\\r\\n\\t\\tif(!tex)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(tex.index == null)\\r\\n\\t\\t\\ttex.index = StandardMaterial.TEXTURES_INDEX[i];\\r\\n\\t\\tthis._texture_settings[ tex.index ] = tex.uvs;\\r\\n\\t}\\r\\n\\r\\n\\tthis._light_mode = this.flags.ignore_lights ? Material.NO_LIGHTS : 1;\\r\\n\\r\\n\\tthis.fillUniforms( scene ); //update uniforms\\r\\n}\\r\\n\\r\\n//options vec4: channel, degamma, transform, contrast\\r\\n\\r\\nStandardMaterial.FLAGS = {\\r\\n\\tCOLOR_TEXTURE: 1<<1,\\r\\n\\tOPACITY_TEXTURE: 1<<2,\\r\\n\\tSPECULAR_TEXTURE: 1<<3,\\r\\n\\tREFLECTIVITY_TEXTURE: 1<<4,\\r\\n\\tAMBIENT_TEXTURE: 1<<5,\\r\\n\\tEMISSIVE_TEXTURE: 1<<6,\\r\\n\\tDETAIL_TEXTURE: 1<<7,\\r\\n\\tNORMAL_TEXTURE: 1<<8,\\r\\n\\tDISPLACEMENT_TEXTURE: 1<<9,\\r\\n\\tEXTRA_TEXTURE: 1<<10,\\r\\n\\tENVIRONMENT_TEXTURE: 1<<11,\\r\\n\\tENVIRONMENT_CUBEMAP: 1<<12,\\r\\n\\tIRRADIANCE_CUBEMAP: 1<<13,\\r\\n\\r\\n\\tDEGAMMA_COLOR: 1<<26,\\r\\n\\tSPEC_ON_ALPHA: 1<<27,\\r\\n\\tSPEC_ON_TOP: 1<<28,\\r\\n\\tALPHA_TEST: 1<<29\\r\\n}; //max is 32\\t\\r\\n\\r\\n\\r\\n\\r\\nStandardMaterial.shader_codes = {};\\r\\n\\r\\n//returns the LS.ShaderCode required to render\\r\\n//here we cannot filter by light pass because this is done before applying shaderblocks\\r\\n//in the StandardMaterial we cache versions of the ShaderCode according to the settings\\r\\nStandardMaterial.prototype.getShaderCode = function( instance, render_settings, pass )\\r\\n{\\r\\n\\tvar FLAGS = StandardMaterial.FLAGS;\\r\\n\\r\\n\\t//lets check which code flags are active according to the configuration of the shader\\r\\n\\tvar code_flags = 0;\\r\\n\\tvar scene = LS.Renderer._current_scene;\\r\\n\\r\\n\\t//TEXTURES\\r\\n\\tif( this.textures.color )\\r\\n\\t{\\r\\n\\t\\tcode_flags |= FLAGS.COLOR_TEXTURE;\\r\\n\\t\\tif( this.textures.color.degamma )\\r\\n\\t\\t\\tcode_flags |= FLAGS.DEGAMMA_COLOR;\\r\\n\\t}\\r\\n\\tif( this.textures.opacity )\\r\\n\\t\\tcode_flags |= FLAGS.OPACITY_TEXTURE;\\r\\n\\tif( this.textures.displacement )\\r\\n\\t\\tcode_flags |= FLAGS.DISPLACEMENT_TEXTURE;\\r\\n\\tif( this.textures.normal )\\r\\n\\t\\tcode_flags |= FLAGS.NORMAL_TEXTURE;\\r\\n\\tif( this.textures.specular )\\r\\n\\t\\tcode_flags |= FLAGS.SPECULAR_TEXTURE;\\r\\n\\tif( this.reflection_factor > 0 )\\r\\n\\t{\\r\\n\\t\\t//code_flags |= FLAGS.REFLECTION;\\r\\n\\t\\tif( this.textures.reflectivity )\\r\\n\\t\\t\\tcode_flags |= FLAGS.REFLECTIVITY_TEXTURE;\\r\\n\\t}\\r\\n\\tif( this.textures.emissive )\\r\\n\\t\\tcode_flags |= FLAGS.EMISSIVE_TEXTURE;\\r\\n\\tif( this.textures.ambient )\\r\\n\\t\\tcode_flags |= FLAGS.AMBIENT_TEXTURE;\\r\\n\\tif( this.textures.detail )\\r\\n\\t\\tcode_flags |= FLAGS.DETAIL_TEXTURE;\\r\\n\\tif( this.textures.extra )\\r\\n\\t\\tcode_flags |= FLAGS.EXTRA_TEXTURE;\\r\\n\\tif( this.specular_on_alpha )\\r\\n\\t\\tcode_flags |= FLAGS.SPEC_ON_ALPHA;\\r\\n\\tif( this.specular_on_top )\\r\\n\\t\\tcode_flags |= FLAGS.SPEC_ON_TOP;\\r\\n\\r\\n\\t//flags\\r\\n\\tif( this.flags.alpha_test )\\r\\n\\t\\tcode_flags |= FLAGS.ALPHA_TEST;\\r\\n\\r\\n\\t//check if we already have this ShaderCode created\\r\\n\\tvar shader_code = LS.StandardMaterial.shader_codes[ code_flags ];\\r\\n\\r\\n\\t//reuse shader codes when possible **************************************\\r\\n\\tif(shader_code)\\r\\n\\t\\treturn shader_code;\\r\\n\\r\\n\\t//generate code\\r\\n\\tvar code = {\\r\\n\\t\\tvs_local: \\\"\\\",\\r\\n\\t\\tfs: \\\"\\\",\\r\\n\\t\\tfs_shadows: \\\"\\\"\\r\\n\\t};\\r\\n\\r\\n\\tif( code_flags & FLAGS.DISPLACEMENT_TEXTURE )\\r\\n\\t\\tcode.vs_local += \\\"\\tvertex4.xyz += v_normal * texture2D( displacement_texture, v_uvs ).x * u_displacementmap_factor;\\\\n\\\";\\t\\r\\n\\r\\n\\t//uvs\\r\\n\\tvar uvs_common = \\\"\\\\n\\\\\\r\\n\\tuvs[0] = IN.uv;\\\\n\\\\\\r\\n\\tuvs[1] = IN.uv1;\\\\n\\\\\\r\\n\\tuvs[2] = (u_texture_matrix * vec3(uvs[0],1.0)).xy;\\\\n\\\\\\r\\n\\t#ifdef COORD1_BLOCK\\\\n\\\\\\r\\n\\t\\tuvs[3] = (vec3(uvs[1],1.0) * u_texture_matrix).xy;\\\\n\\\\\\r\\n\\t#else\\\\n\\\\\\r\\n\\t\\tuvs[3] = uvs[2];\\\\n\\\\\\r\\n\\t#endif\\\\n\\\";\\r\\n\\tcode.fs += uvs_common;\\r\\n\\tcode.fs_shadows += uvs_common;\\r\\n\\r\\n\\tif( code_flags & FLAGS.NORMAL_TEXTURE )\\r\\n\\t{\\r\\n\\t\\tcode.fs += \\\"vec2 normal_uv = getUVs( u_texture_settings[\\\"+StandardMaterial.TEXTURES_INDEX[\\\"normal\\\"]+\\\"]);\\\\n\\\\\\r\\n\\t\\tvec3 normal_pixel = texture2D( normal_texture, normal_uv ).xyz;\\\\n\\\\\\r\\n\\t\\tif( u_normal_info.y > 0.0 )\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tnormal_pixel.xy = vec2(1.0) - normal_pixel.xy;\\\\n\\\\\\r\\n\\t\\t\\tnormal_pixel = normalize( perturbNormal( IN.worldNormal, IN.viewDir, normal_uv, normal_pixel ));\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\telse\\\\n\\\\\\r\\n\\t\\t\\tnormal_pixel = normal_pixel * 2.0 - vec3(1.0);\\\\n\\\\\\r\\n\\t\\to.Normal = normalize( mix( o.Normal, normal_pixel, u_normal_info.x ) );\\\\n\\\";\\r\\n\\t}\\r\\n\\r\\n\\tif( code_flags & FLAGS.COLOR_TEXTURE )\\r\\n\\t{\\r\\n\\t\\tvar str = \\\"\\tvec4 tex_color = texture2D( color_texture, getUVs( u_texture_settings[\\\"+StandardMaterial.TEXTURES_INDEX[\\\"color\\\"]+\\\"] ) );\\\\n\\\";\\r\\n\\t\\tcode.fs += str;\\r\\n\\t\\tcode.fs_shadows += str;\\r\\n\\r\\n\\t\\tif( code_flags & FLAGS.DEGAMMA_COLOR )\\r\\n\\t\\t\\tcode.fs += \\\"\\ttex_color.xyz = pow( tex_color.xyz, vec3(2.0) );\\\\n\\\";\\r\\n\\t\\tstr = \\\"\\to.Albedo *= tex_color.xyz;\\\\n\\\\\\r\\n\\to.Alpha *= tex_color.w;\\\\n\\\";\\r\\n\\t\\tcode.fs += str;\\r\\n\\t\\tcode.fs_shadows += str;\\r\\n\\t}\\r\\n\\tif( code_flags & FLAGS.OPACITY_TEXTURE )\\r\\n\\t{\\r\\n\\t\\tvar str = \\\"\\to.Alpha *= texture2D( opacity_texture, getUVs( u_texture_settings[\\\"+StandardMaterial.TEXTURES_INDEX[\\\"opacity\\\"]+\\\"]) ).x;\\\\n\\\";\\r\\n\\t\\tcode.fs += str;\\r\\n\\t\\tcode.fs_shadows += str;\\r\\n\\t}\\r\\n\\tif( code_flags & FLAGS.SPECULAR_TEXTURE )\\r\\n\\t{\\r\\n\\t\\tcode.fs += \\\"\\tvec4 spec_info = texture2D( specular_texture, getUVs( u_texture_settings[\\\"+StandardMaterial.TEXTURES_INDEX[\\\"specular\\\"]+\\\"]) );\\\\n\\\\\\r\\n\\to.Specular *= spec_info.x;\\\\n\\\\\\r\\n\\to.Gloss *= spec_info.y;\\\\n\\\";\\r\\n\\t}\\r\\n\\tif( code_flags & FLAGS.REFLECTIVITY_TEXTURE )\\r\\n\\t\\tcode.fs += \\\"\\to.Reflectivity *= texture2D( reflectivity_texture, getUVs( u_texture_settings[\\\"+StandardMaterial.TEXTURES_INDEX[\\\"reflectivity\\\"]+\\\"]) ).x;\\\\n\\\";\\r\\n\\tif( code_flags & FLAGS.EMISSIVE_TEXTURE )\\r\\n\\t\\tcode.fs += \\\"\\to.Emission *= texture2D( emissive_texture, getUVs( u_texture_settings[\\\"+StandardMaterial.TEXTURES_INDEX[\\\"emissive\\\"]+\\\"]) ).xyz;\\\\n\\\";\\r\\n\\tif( code_flags & FLAGS.AMBIENT_TEXTURE )\\r\\n\\t\\tcode.fs += \\\"\\to.Ambient *= texture2D( ambient_texture, getUVs( u_texture_settings[\\\"+StandardMaterial.TEXTURES_INDEX[\\\"ambient\\\"]+\\\"]) ).xyz;\\\\n\\\";\\r\\n\\tif( code_flags & FLAGS.DETAIL_TEXTURE )\\r\\n\\t\\tcode.fs += \\\"\\to.Albedo += (texture2D( detail_texture, getUVs( u_texture_settings[\\\"+StandardMaterial.TEXTURES_INDEX[\\\"detail\\\"]+\\\"]) * u_detail_info.yz).xyz - vec3(0.5)) * u_detail_info.x;\\\\n\\\";\\r\\n\\tif( code_flags & FLAGS.EXTRA_TEXTURE )\\r\\n\\t\\tcode.fs += \\\"\\tif(u_light_info.z == 0.0) o.Extra = u_extra_color * texture2D( extra_texture, getUVs( u_texture_settings[\\\"+StandardMaterial.TEXTURES_INDEX[\\\"extra\\\"]+\\\"] ) );\\\\n\\\";\\r\\n\\r\\n\\t//flags\\r\\n\\tif( code_flags & FLAGS.ALPHA_TEST )\\r\\n\\t{\\r\\n\\t\\tvar str = \\\"\\tif(o.Alpha < 0.01) discard;\\\\n\\\";\\r\\n\\t\\tcode.fs += str;\\r\\n\\t\\tcode.fs_shadows += str;\\r\\n\\t}\\r\\n\\r\\n\\tif( code_flags & FLAGS.SPEC_ON_TOP )\\r\\n\\t\\tcode.fs += \\\"\\t#define SPEC_ON_TOP\\\\n\\\";\\r\\n\\r\\n\\tif( code_flags & FLAGS.SPEC_ON_ALPHA )\\r\\n\\t\\tcode.fs += \\\"\\t#define SPEC_ON_ALPHA\\\\n\\\";\\r\\n\\r\\n\\t//if( code_flags & FLAGS.FLAT_NORMALS )\\r\\n\\t//\\tflat_normals += \\\"\\\";\\r\\n\\r\\n\\t//compile shader and cache\\r\\n\\tshader_code = new LS.ShaderCode();\\r\\n\\tvar final_code = StandardMaterial.code_template;\\r\\n\\r\\n\\tif( StandardMaterial.onShaderCode )\\r\\n\\t\\tStandardMaterial.onShaderCode( code, this, code_flags );\\r\\n\\r\\n\\tshader_code.code = ShaderCode.replaceCode( final_code, code );\\r\\n\\t/*\\r\\n\\tshader_code.code = final_code.replace(/\\\\{\\\\{[a-zA-Z0-9_]*\\\\}\\\\}/g, function(v){\\r\\n\\t\\tv = v.replace( /[\\\\{\\\\}]/g, \\\"\\\" );\\r\\n\\t\\treturn code[v] || \\\"\\\";\\r\\n\\t});\\r\\n\\t*/\\r\\n\\r\\n\\tLS.StandardMaterial.shader_codes[ code_flags ] = shader_code;\\r\\n\\treturn shader_code;\\r\\n}\\r\\n\\r\\nStandardMaterial.prototype.fillUniforms = function( scene, options )\\r\\n{\\r\\n\\tvar uniforms = this._uniforms;\\r\\n\\r\\n\\tuniforms.u_reflection_info[0] = this.reflection_factor;\\r\\n\\tuniforms.u_reflection_info[1] = this.reflection_fresnel;\\r\\n\\tuniforms.u_backlight_factor = this.backlight_factor;\\r\\n\\tuniforms.u_normal_info[0] = this.normalmap_factor;\\r\\n\\tuniforms.u_normal_info[1] = this.normalmap_tangent ? 1 : 0;\\r\\n\\tuniforms.u_displacementmap_factor = this.displacementmap_factor;\\r\\n\\tuniforms.u_velvet_info.set( this._velvet );\\r\\n\\tuniforms.u_velvet_info[3] = this.velvet_additive ? this.velvet_exp : -this.velvet_exp;\\r\\n\\r\\n\\t//iterate through textures in the material\\r\\n\\tvar last_texture_slot = 0;\\r\\n\\tvar samplers = this._samplers;\\r\\n\\tsamplers.length = 0; //clear\\r\\n\\tfor(var i in this.textures) \\r\\n\\t{\\r\\n\\t\\tvar sampler = this.getTextureSampler(i);\\r\\n\\t\\tif(!sampler)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tvar texture = sampler.texture;\\r\\n\\t\\tif(!texture)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif(texture.constructor === String) //name of texture\\r\\n\\t\\t\\ttexture = LS.ResourcesManager.textures[texture];\\r\\n\\t\\telse if (texture.constructor != Texture)\\r\\n\\t\\t\\tcontinue;\\t\\t\\r\\n\\t\\t\\r\\n\\t\\tif(!texture) //loading or non-existant\\r\\n\\t\\t\\tsampler = { texture: \\\":missing\\\" };\\r\\n\\r\\n\\t\\tvar slot = last_texture_slot;\\r\\n\\t\\tif( i == \\\"environment\\\" )\\r\\n\\t\\t\\tslot = LS.Renderer.ENVIRONMENT_TEXTURE_SLOT;\\r\\n\\t\\telse if( i == \\\"irradiance\\\" )\\r\\n\\t\\t\\tslot = LS.Renderer.IRRADIANCE_TEXTURE_SLOT;\\r\\n\\t\\telse\\r\\n\\t\\t\\tlast_texture_slot++;\\r\\n\\r\\n\\t\\tsamplers[ slot ] = sampler;\\r\\n\\t\\t//var uniform_name = i + ( (!texture || texture.texture_type == gl.TEXTURE_2D) ? \\\"_texture\\\" : \\\"_cubemap\\\");\\r\\n\\t\\tuniforms[ i + \\\"_texture\\\" ] = slot;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nStandardMaterial.prototype.getTextureChannels = function()\\r\\n{\\r\\n\\treturn [ Material.COLOR_TEXTURE, Material.OPACITY_TEXTURE, Material.AMBIENT_TEXTURE, Material.SPECULAR_TEXTURE, Material.EMISSIVE_TEXTURE, StandardMaterial.DETAIL_TEXTURE, StandardMaterial.NORMAL_TEXTURE, StandardMaterial.DISPLACEMENT_TEXTURE, StandardMaterial.BUMP_TEXTURE, StandardMaterial.REFLECTIVITY_TEXTURE, StandardMaterial.EXTRA_TEXTURE, Material.ENVIRONMENT_TEXTURE, StandardMaterial.IRRADIANCE_TEXTURE ];\\r\\n}\\r\\n\\r\\n/**\\r\\n* assign a value to a property in a safe way\\r\\n* @method setProperty\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\nStandardMaterial.prototype.setProperty = function(name, value)\\r\\n{\\r\\n\\t//redirect to base material\\r\\n\\tif( Material.prototype.setProperty.call(this,name,value) )\\r\\n\\t\\treturn true;\\r\\n\\r\\n\\t//regular\\r\\n\\tswitch(name)\\r\\n\\t{\\r\\n\\t\\t//objects\\r\\n\\t\\tcase \\\"render_state\\\":\\r\\n\\t\\t//numbers\\r\\n\\t\\tcase \\\"specular_factor\\\":\\r\\n\\t\\tcase \\\"specular_gloss\\\":\\r\\n\\t\\tcase \\\"backlight_factor\\\":\\r\\n\\t\\tcase \\\"reflection_factor\\\":\\r\\n\\t\\tcase \\\"reflection_fresnel\\\":\\r\\n\\t\\tcase \\\"velvet_exp\\\":\\r\\n\\t\\tcase \\\"velvet_additive\\\":\\r\\n\\t\\tcase \\\"normalmap_tangent\\\":\\r\\n\\t\\tcase \\\"normalmap_factor\\\":\\r\\n\\t\\tcase \\\"bumpmap_factor\\\":\\r\\n\\t\\tcase \\\"displacementmap_factor\\\":\\r\\n\\t\\tcase \\\"detail_factor\\\":\\r\\n\\t\\tcase \\\"emissive_extra\\\":\\r\\n\\t\\t//strings\\r\\n\\t\\tcase \\\"shader_name\\\":\\r\\n\\t\\t//bools\\r\\n\\t\\tcase \\\"specular_on_top\\\":\\r\\n\\t\\tcase \\\"specular_on_alpha\\\":\\r\\n\\t\\tcase \\\"normalmap_tangent\\\":\\r\\n\\t\\tcase \\\"reflection_specular\\\":\\r\\n\\t\\tcase \\\"use_scene_ambient\\\":\\r\\n\\t\\tcase \\\"blend_mode\\\":\\r\\n\\t\\t\\tif(value !== null)\\r\\n\\t\\t\\t\\tthis[name] = value; \\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase \\\"flags\\\":\\r\\n\\t\\t\\tif(value)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var i in value)\\r\\n\\t\\t\\t\\t\\tthis.flags[i] = value[i];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\t//vectors\\r\\n\\t\\tcase \\\"ambient\\\":\\t\\r\\n\\t\\tcase \\\"emissive\\\": \\r\\n\\t\\tcase \\\"velvet\\\":\\r\\n\\t\\tcase \\\"extra\\\":\\r\\n\\t\\tcase \\\"detail_scale\\\":\\r\\n\\t\\t\\tif(this[name].length >= value.length)\\r\\n\\t\\t\\t\\tthis[name].set(value);\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\treturn false;\\r\\n\\t}\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* gets all the properties and its types\\r\\n* @method getPropertiesInfo\\r\\n* @return {Object} object with name:type\\r\\n*/\\r\\nStandardMaterial.prototype.getPropertiesInfo = function()\\r\\n{\\r\\n\\t//get from the regular material\\r\\n\\tvar o = Material.prototype.getPropertiesInfo.call(this);\\r\\n\\r\\n\\t//add some more\\r\\n\\to.merge({\\r\\n\\t\\tshader_name: LS.TYPES.STRING,\\r\\n\\r\\n\\t\\tblend_mode: LS.TYPES.NUMBER,\\r\\n\\t\\tspecular_factor: LS.TYPES.NUMBER,\\r\\n\\t\\tspecular_gloss: LS.TYPES.NUMBER,\\r\\n\\t\\tbacklight_factor: LS.TYPES.NUMBER,\\r\\n\\t\\treflection_factor: LS.TYPES.NUMBER,\\r\\n\\t\\treflection_fresnel: LS.TYPES.NUMBER,\\r\\n\\t\\tvelvet_exp: LS.TYPES.NUMBER,\\r\\n\\r\\n\\t\\tnormalmap_factor: LS.TYPES.NUMBER,\\r\\n\\t\\tbumpmap_factor: LS.TYPES.NUMBER,\\r\\n\\t\\tdisplacementmap_factor: LS.TYPES.NUMBER,\\r\\n\\t\\temissive_extra: LS.TYPES.NUMBER,\\r\\n\\r\\n\\t\\tambient: LS.TYPES.VEC3,\\r\\n\\t\\temissive: LS.TYPES.VEC3,\\r\\n\\t\\tvelvet: LS.TYPES.VEC3,\\r\\n\\t\\textra: LS.TYPES.VEC4,\\r\\n\\t\\tdetail_factor: LS.TYPES.NUMBER,\\r\\n\\t\\tdetail_scale: LS.TYPES.VEC2,\\r\\n\\r\\n\\t\\tspecular_on_top: LS.TYPES.BOOLEAN,\\r\\n\\t\\tnormalmap_tangent: LS.TYPES.BOOLEAN,\\r\\n\\t\\treflection_specular: LS.TYPES.BOOLEAN,\\r\\n\\t\\tuse_scene_ambient: LS.TYPES.BOOLEAN,\\r\\n\\t\\tvelvet_additive: LS.TYPES.BOOLEAN\\r\\n\\t});\\r\\n\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\nStandardMaterial.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif( path.length < 1)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar info = Material.prototype.getPropertyInfoFromPath.call(this,path);\\r\\n\\tif(info)\\r\\n\\t\\treturn info;\\r\\n\\r\\n\\tvar varname = path[0];\\r\\n\\tvar type;\\r\\n\\r\\n\\tswitch(varname)\\r\\n\\t{\\r\\n\\t\\tcase \\\"blend_mode\\\":\\r\\n\\t\\tcase \\\"backlight_factor\\\":\\r\\n\\t\\tcase \\\"reflection_factor\\\":\\r\\n\\t\\tcase \\\"reflection_fresnel\\\":\\r\\n\\t\\tcase \\\"velvet_exp\\\":\\r\\n\\t\\tcase \\\"normalmap_factor\\\":\\r\\n\\t\\tcase \\\"bumpmap_factor\\\":\\r\\n\\t\\tcase \\\"displacementmap_factor\\\":\\r\\n\\t\\tcase \\\"emissive_extra\\\":\\r\\n\\t\\tcase \\\"detail_factor\\\":\\r\\n\\t\\t\\ttype = LS.TYPES.NUMBER; break;\\r\\n\\t\\tcase \\\"extra\\\":\\r\\n\\t\\t\\ttype = LS.TYPES.VEC4; break;\\r\\n\\t\\tcase \\\"ambient\\\":\\r\\n\\t\\tcase \\\"emissive\\\":\\r\\n\\t\\tcase \\\"velvet\\\":\\r\\n\\t\\t\\ttype = LS.TYPES.VEC3; break;\\r\\n\\t\\tcase \\\"detail_scale\\\":\\r\\n\\t\\t\\ttype = LS.TYPES.VEC2; break;\\r\\n\\t\\tcase \\\"specular_on_top\\\":\\r\\n\\t\\tcase \\\"specular_on_alpha\\\":\\r\\n\\t\\tcase \\\"normalmap_tangent\\\":\\r\\n\\t\\tcase \\\"reflection_specular\\\":\\r\\n\\t\\tcase \\\"use_scene_ambient\\\":\\r\\n\\t\\tcase \\\"velvet_additive\\\":\\r\\n\\t\\t\\ttype = LS.TYPES.BOOLEAN; break;\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\treturn {\\r\\n\\t\\tnode: this._root,\\r\\n\\t\\ttarget: this,\\r\\n\\t\\tname: varname,\\r\\n\\t\\tvalue: this[varname],\\r\\n\\t\\ttype: type\\r\\n\\t};\\r\\n}\\r\\n\\r\\nStandardMaterial.clearShadersCache = function()\\r\\n{\\r\\n\\tLS.log(\\\"StandardMaterial ShaderCode cache cleared\\\");\\r\\n\\tStandardMaterial.shader_codes = {};\\r\\n}\\r\\n\\r\\nLS.registerMaterialClass( StandardMaterial );\\r\\nLS.StandardMaterial = StandardMaterial;\\r\\n\\r\\n//legacy\\r\\nLS.Classes[\\\"newStandardMaterial\\\"] = StandardMaterial;\\r\\n//LS.newStandardMaterial = StandardMaterial;\\r\\n//LS.MaterialClasses.newStandardMaterial = StandardMaterial;\\r\\n\\r\\n//**********************************************\\r\\nvar UVS_CODE = \\\"\\\\n\\\\\\r\\nuniform int u_texture_settings[11];\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvec2 uvs[4];\\\\n\\\\\\r\\nvec2 getUVs(int index)\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n\\tif(index == 0)\\\\n\\\\\\r\\n\\t\\treturn uvs[0];\\\\n\\\\\\r\\n\\tif(index == 1)\\\\n\\\\\\r\\n\\t\\treturn uvs[1];\\\\n\\\\\\r\\n\\tif(index == 2)\\\\n\\\\\\r\\n\\t\\treturn uvs[2];\\\\n\\\\\\r\\n\\tif(index == 3)\\\\n\\\\\\r\\n\\t\\treturn uvs[3];\\\\n\\\\\\r\\n\\treturn uvs[0];\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nStandardMaterial.code_template = \\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\\\\\color.vs\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nprecision mediump float;\\\\n\\\\\\r\\n//global defines from blocks\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"vertex_color\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"coord1\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nattribute vec3 a_vertex;\\\\n\\\\\\r\\nattribute vec3 a_normal;\\\\n\\\\\\r\\nattribute vec2 a_coord;\\\\n\\\\\\r\\n#ifdef BLOCK_COORD1\\\\n\\\\\\r\\n\\tattribute vec2 a_coord1;\\\\n\\\\\\r\\n\\tvarying vec2 v_uvs1;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n#ifdef BLOCK_VERTEX_COLOR\\\\n\\\\\\r\\n\\tattribute vec4 a_color;\\\\n\\\\\\r\\n\\tvarying vec4 v_vertex_color;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//varyings\\\\n\\\\\\r\\nvarying vec3 v_pos;\\\\n\\\\\\r\\nvarying vec3 v_normal;\\\\n\\\\\\r\\nvarying vec2 v_uvs;\\\\n\\\\\\r\\nvarying vec3 v_local_pos;\\\\n\\\\\\r\\nvarying vec3 v_local_normal;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//matrices\\\\n\\\\\\r\\n#ifdef BLOCK_INSTANCING\\\\n\\\\\\r\\n\\tattribute mat4 u_model;\\\\n\\\\\\r\\n#else\\\\n\\\\\\r\\n\\tuniform mat4 u_model;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\nuniform mat4 u_normal_model;\\\\n\\\\\\r\\nuniform mat4 u_view;\\\\n\\\\\\r\\nuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n//material\\\\n\\\\\\r\\nuniform float u_displacementmap_factor;\\\\n\\\\\\r\\nuniform sampler2D displacement_texture;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//globals\\\\n\\\\\\r\\nuniform float u_time;\\\\n\\\\\\r\\nuniform vec4 u_viewport;\\\\n\\\\\\r\\nuniform float u_point_size;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"light\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"morphing\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"skinning\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//camera\\\\n\\\\\\r\\nuniform vec3 u_camera_eye;\\\\n\\\\\\r\\nuniform vec2 u_camera_planes;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma event \\\\\\\"vs_functions\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//special cases\\\\n\\\\\\r\\n{{vs_out}}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvoid main() {\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvec4 vertex4 = vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\tv_local_pos = a_vertex;\\\\n\\\\\\r\\n\\tv_local_normal = a_normal;\\\\n\\\\\\r\\n\\tv_normal = a_normal;\\\\n\\\\\\r\\n\\tv_uvs = a_coord;\\\\n\\\\\\r\\n\\t#ifdef BLOCK_COORD1\\\\n\\\\\\r\\n\\t\\tv_uvs1 = a_coord1;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef BLOCK_VERTEX_COLOR\\\\n\\\\\\r\\n\\t\\tv_vertex_color = a_color;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t//local deforms\\\\n\\\\\\r\\n\\t{{vs_local}}\\\\n\\\\\\r\\n\\tapplyMorphing( vertex4, v_normal );\\\\n\\\\\\r\\n\\tapplySkinning( vertex4, v_normal );\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t//vertex\\\\n\\\\\\r\\n\\tv_pos = (u_model * vertex4).xyz;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tapplyLight(v_pos);\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t//normal\\\\n\\\\\\r\\n\\t#ifdef SHADERBLOCK_INSTANCING\\\\n\\\\\\r\\n\\t\\tv_normal = (u_model * vec4(v_normal,0.0)).xyz;\\\\n\\\\\\r\\n\\t#else\\\\n\\\\\\r\\n\\t\\tv_normal = (u_normal_model * vec4(v_normal,0.0)).xyz;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t//world deform\\\\n\\\\\\r\\n\\t{{vs_global}}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t#pragma event \\\\\\\"vs_final_pass\\\\\\\"\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tgl_Position = u_viewprojection * vec4(v_pos,1.0);\\\\n\\\\\\r\\n\\tgl_PointSize = u_point_size;\\\\n\\\\\\r\\n\\t#pragma event \\\\\\\"vs_final\\\\\\\"\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\\\\\color.fs\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#ifdef DRAW_BUFFERS\\\\n\\\\\\r\\n\\t#extension GL_EXT_draw_buffers : require \\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nprecision mediump float;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//global defines from blocks\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"vertex_color\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"coord1\\\\\\\"\\\\n\\\\\\r\\n//#pragma shaderblock \\\\\\\"firstPass\\\\\\\"\\\\n\\\\\\r\\n//#pragma shaderblock \\\\\\\"lastPass\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//varyings\\\\n\\\\\\r\\nvarying vec3 v_pos;\\\\n\\\\\\r\\nvarying vec3 v_normal;\\\\n\\\\\\r\\nvarying vec2 v_uvs;\\\\n\\\\\\r\\nvarying vec3 v_local_pos;\\\\n\\\\\\r\\nvarying vec3 v_local_normal;\\\\n\\\\\\r\\n#ifdef BLOCK_COORD1\\\\n\\\\\\r\\n\\tvarying vec2 v_uvs1;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n#ifdef BLOCK_VERTEX_COLOR\\\\n\\\\\\r\\n\\tvarying vec4 v_vertex_color;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//globals\\\\n\\\\\\r\\nuniform vec4 u_viewport;\\\\n\\\\\\r\\nuniform mat4 u_view;\\\\n\\\\\\r\\nuniform vec3 u_camera_eye;\\\\n\\\\\\r\\nuniform vec4 u_clipping_plane;\\\\n\\\\\\r\\nuniform vec4 u_background_color;\\\\n\\\\\\r\\nuniform vec4 u_material_color;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nuniform vec3 u_ambient_color;\\\\n\\\\\\r\\nuniform vec4 u_emissive_color;\\\\n\\\\\\r\\nuniform vec4 u_specular;\\\\n\\\\\\r\\nuniform vec2 u_reflection_info;\\\\n\\\\\\r\\nuniform vec4 u_velvet_info;\\\\n\\\\\\r\\nuniform vec2 u_normal_info;\\\\n\\\\\\r\\nuniform vec3 u_detail_info;\\\\n\\\\\\r\\nuniform mat3 u_texture_matrix;\\\\n\\\\\\r\\nuniform vec4 u_extra_color;\\\\n\\\\\\r\\nuniform float u_backlight_factor;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nuniform sampler2D color_texture;\\\\n\\\\\\r\\nuniform sampler2D opacity_texture;\\\\n\\\\\\r\\nuniform sampler2D specular_texture;\\\\n\\\\\\r\\nuniform sampler2D ambient_texture;\\\\n\\\\\\r\\nuniform sampler2D emissive_texture;\\\\n\\\\\\r\\nuniform sampler2D reflectivity_texture;\\\\n\\\\\\r\\nuniform sampler2D detail_texture;\\\\n\\\\\\r\\nuniform sampler2D normal_texture;\\\\n\\\\\\r\\nuniform sampler2D extra_texture;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"light\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"light_texture\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"applyReflection\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"normalBuffer\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma snippet \\\\\\\"perturbNormal\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"extraBuffers\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\"+ UVS_CODE +\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvoid surf(in Input IN, out SurfaceOutput o)\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n\\to.Albedo = u_material_color.xyz;\\\\n\\\\\\r\\n\\to.Alpha = u_material_color.a;\\\\n\\\\\\r\\n\\t#ifdef BLOCK_VERTEX_COLOR\\\\n\\\\\\r\\n\\to.Albedo *= IN.color.xyz;\\\\n\\\\\\r\\n\\to.Alpha *= IN.color.a;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\to.Normal = normalize( v_normal );\\\\n\\\\\\r\\n\\to.Specular = u_specular.x;\\\\n\\\\\\r\\n\\to.Gloss = u_specular.y;\\\\n\\\\\\r\\n\\to.Ambient = u_ambient_color;\\\\n\\\\\\r\\n\\to.Emission = u_emissive_color.xyz;\\\\n\\\\\\r\\n\\to.Reflectivity = u_reflection_info.x;\\\\n\\\\\\r\\n\\to.Extra = u_extra_color;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t{{fs}}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tif(u_velvet_info.w > 0.0)\\\\n\\\\\\r\\n\\t\\to.Albedo += u_velvet_info.xyz * ( 1.0 - pow( max(0.0, dot( IN.viewDir, o.Normal )), u_velvet_info.w ));\\\\n\\\\\\r\\n\\telse if(u_velvet_info.w < 0.0)\\\\n\\\\\\r\\n\\t\\to.Albedo = mix( o.Albedo, u_velvet_info.xyz, 1.0 - pow( max(0.0, dot( IN.viewDir, o.Normal )), abs(u_velvet_info.w) ) );\\\\n\\\\\\r\\n\\tif(u_emissive_color.w > 0.0)\\\\n\\\\\\r\\n\\t\\to.Emission *= o.Albedo;\\\\n\\\\\\r\\n\\to.Reflectivity *= max(0.0, pow( 1.0 - clamp(0.0, dot(IN.viewDir,o.Normal),1.0), u_reflection_info.y ));\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma event \\\\\\\"fs_functions\\\\\\\"\\\\n\\\\\\r\\n#pragma snippet \\\\\\\"testClippingPlane\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n{{fs_out}}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvoid main() {\\\\n\\\\\\r\\n\\tInput IN = getInput();\\\\n\\\\\\r\\n\\tif(testClippingPlane(u_clipping_plane,IN.worldPos) < 0.0)\\\\n\\\\\\r\\n\\t\\tdiscard;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tIN.vertex = v_local_pos;\\\\n\\\\\\r\\n\\tIN.normal = v_local_normal;\\\\n\\\\\\r\\n\\tSurfaceOutput o = getSurfaceOutput();\\\\n\\\\\\r\\n\\t#ifdef BLOCK_VERTEX_COLOR\\\\n\\\\\\r\\n\\t\\tIN.color = v_vertex_color;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef BLOCK_COORD1\\\\n\\\\\\r\\n\\t\\tIN.uv1 = v_uvs1;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\tsurf(IN,o);\\\\n\\\\\\r\\n\\tLight LIGHT = getLight();\\\\n\\\\\\r\\n\\tapplyLightTexture( IN, LIGHT );\\\\n\\\\\\r\\n\\tif( !gl_FrontFacing )\\\\n\\\\\\r\\n\\t\\to.Normal *= -1.0;\\\\n\\\\\\r\\n\\tFinalLight FINALLIGHT = computeLight( o, IN, LIGHT );\\\\n\\\\\\r\\n\\tFINALLIGHT.Diffuse += u_backlight_factor * max(0.0, dot(FINALLIGHT.Vector, -o.Normal));\\\\n\\\\\\r\\n\\tvec4 final_color = vec4( 0.0,0.0,0.0, o.Alpha );\\\\n\\\\\\r\\n\\t#ifdef SPEC_ON_ALPHA\\\\n\\\\\\r\\n\\t\\tfinal_color.a += FINALLIGHT.Specular;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef SPEC_ON_TOP\\\\n\\\\\\r\\n\\t\\tfloat specular = FINALLIGHT.Specular;\\\\n\\\\\\r\\n\\t\\tFINALLIGHT.Specular = 0.0;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\tfinal_color.xyz = applyLight( o, FINALLIGHT );\\\\n\\\\\\r\\n\\t#ifdef SPEC_ON_TOP\\\\n\\\\\\r\\n\\t\\tfinal_color.xyz += specular * LIGHT.Color * FINALLIGHT.Shadow;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\tfinal_color = applyReflection( IN, o, final_color );\\\\n\\\\\\r\\n\\t#pragma event \\\\\\\"fs_final_pass\\\\\\\"\\\\n\\\\\\r\\n\\t{{fs_encode}}\\\\n\\\\\\r\\n\\t#ifdef DRAW_BUFFERS\\\\n\\\\\\r\\n\\t gl_FragData[0] = final_color;\\\\n\\\\\\r\\n\\t #ifdef BLOCK_FIRSTPASS\\\\n\\\\\\r\\n\\t\\t #ifdef BLOCK_NORMALBUFFER\\\\n\\\\\\r\\n\\t\\t\\t gl_FragData[1] = vec4( o.Normal * 0.5 + vec3(0.5), 1.0 );\\\\n\\\\\\r\\n\\t\\t #else\\\\n\\\\\\r\\n\\t\\t\\t gl_FragData[1] = o.Extra;\\\\n\\\\\\r\\n\\t\\t #endif\\\\n\\\\\\r\\n\\t #else\\\\n\\\\\\r\\n\\t\\t gl_FragData[1] = vec4(0.0);\\\\n\\\\\\r\\n\\t #endif\\\\n\\\\\\r\\n\\t#else\\\\n\\\\\\r\\n\\t gl_FragColor = final_color;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#pragma event \\\\\\\"fs_final\\\\\\\"\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\\\\\shadow.vs\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nprecision mediump float;\\\\n\\\\\\r\\nattribute vec3 a_vertex;\\\\n\\\\\\r\\nattribute vec3 a_normal;\\\\n\\\\\\r\\nattribute vec2 a_coord;\\\\n\\\\\\r\\nvarying vec3 v_local_pos;\\\\n\\\\\\r\\nvarying vec3 v_local_normal;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//varyings\\\\n\\\\\\r\\nvarying vec3 v_pos;\\\\n\\\\\\r\\nvarying vec3 v_normal;\\\\n\\\\\\r\\nvarying vec2 v_uvs;\\\\n\\\\\\r\\nvarying vec4 v_screenpos;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//matrices\\\\n\\\\\\r\\n#ifdef BLOCK_INSTANCING\\\\n\\\\\\r\\n\\tattribute mat4 u_model;\\\\n\\\\\\r\\n#else\\\\n\\\\\\r\\n\\tuniform mat4 u_model;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\nuniform mat4 u_normal_model;\\\\n\\\\\\r\\nuniform mat4 u_view;\\\\n\\\\\\r\\nuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n//material\\\\n\\\\\\r\\nuniform float u_displacementmap_factor;\\\\n\\\\\\r\\nuniform sampler2D displacement_texture;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//globals\\\\n\\\\\\r\\nuniform float u_time;\\\\n\\\\\\r\\nuniform vec4 u_viewport;\\\\n\\\\\\r\\nuniform float u_point_size;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"light\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"morphing\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"skinning\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//camera\\\\n\\\\\\r\\nuniform vec3 u_camera_eye;\\\\n\\\\\\r\\nuniform vec2 u_camera_planes;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n{{vs_out}}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvoid main() {\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvec4 vertex4 = vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\tv_local_pos = a_vertex;\\\\n\\\\\\r\\n\\tv_local_normal = a_normal;\\\\n\\\\\\r\\n\\tv_normal = a_normal;\\\\n\\\\\\r\\n\\tv_uvs = a_coord;\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n //deforms\\\\n\\\\\\r\\n {{vs_local}}\\\\n\\\\\\r\\n applyMorphing( vertex4, v_normal );\\\\n\\\\\\r\\n applySkinning( vertex4, v_normal );\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t//vertex\\\\n\\\\\\r\\n\\tv_pos = (u_model * vertex4).xyz;\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n applyLight(v_pos);\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n\\t//normal\\\\n\\\\\\r\\n\\t#ifdef SHADERBLOCK_INSTANCING\\\\n\\\\\\r\\n\\t\\tv_normal = (u_model * vec4(v_normal,0.0)).xyz;\\\\n\\\\\\r\\n\\t#else\\\\n\\\\\\r\\n\\t\\tv_normal = (u_normal_model * vec4(v_normal,0.0)).xyz;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t{{vs_global}}\\\\n\\\\\\r\\n\\tv_screenpos = u_viewprojection * vec4(v_pos,1.0);\\\\n\\\\\\r\\n\\tgl_Position = v_screenpos;\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\\\\\shadow.fs\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nprecision mediump float;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//varyings\\\\n\\\\\\r\\nvarying vec3 v_pos;\\\\n\\\\\\r\\nvarying vec3 v_normal;\\\\n\\\\\\r\\nvarying vec2 v_uvs;\\\\n\\\\\\r\\nvarying vec4 v_screenpos;\\\\n\\\\\\r\\nvarying vec3 v_local_pos;\\\\n\\\\\\r\\nvarying vec3 v_local_normal;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//globals\\\\n\\\\\\r\\nuniform vec3 u_camera_eye;\\\\n\\\\\\r\\nuniform vec2 u_camera_planes;\\\\n\\\\\\r\\nuniform vec4 u_clipping_plane;\\\\n\\\\\\r\\nuniform vec4 u_material_color;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nuniform mat3 u_texture_matrix;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\"+ UVS_CODE +\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nuniform sampler2D color_texture;\\\\n\\\\\\r\\nuniform sampler2D opacity_texture;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma snippet \\\\\\\"input\\\\\\\"\\\\n\\\\\\r\\n#pragma snippet \\\\\\\"surface\\\\\\\"\\\\n\\\\\\r\\n#pragma snippet \\\\\\\"PackDepth32\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvoid surf(in Input IN, out SurfaceOutput o)\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n\\to.Albedo = u_material_color.xyz;\\\\n\\\\\\r\\n\\to.Alpha = u_material_color.a;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t{{fs_shadows}}\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n{{fs_shadow_out}}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvoid main() {\\\\n\\\\\\r\\n\\tInput IN = getInput();\\\\n\\\\\\r\\n\\tIN.vertex = v_local_pos;\\\\n\\\\\\r\\n\\tIN.normal = v_local_normal;\\\\n\\\\\\r\\n\\tSurfaceOutput o = getSurfaceOutput();\\\\n\\\\\\r\\n\\tsurf(IN,o);\\\\n\\\\\\r\\n\\t//float depth = length( IN.worldPos - u_camera_eye );\\\\n\\\\\\r\\n\\t//depth = linearDepth( depth, u_camera_planes.x, u_camera_planes.y );\\\\n\\\\\\r\\n\\tfloat depth = (v_screenpos.z / v_screenpos.w) * 0.5 + 0.5;\\\\n\\\\\\r\\n\\t//depth = linearDepthNormalized( depth, u_camera_planes.x, u_camera_planes.y );\\\\n\\\\\\r\\n\\tvec4 final_color;\\\\n\\\\\\r\\n\\tfinal_color = PackDepth32(depth);\\\\n\\\\\\r\\n\\t{{fs_shadow_encode}}\\\\n\\\\\\r\\n\\tgl_FragColor = final_color;\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\\\\\picking.vs\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nprecision mediump float;\\\\n\\\\\\r\\nattribute vec3 a_vertex;\\\\n\\\\\\r\\nattribute vec3 a_normal;\\\\n\\\\\\r\\nattribute vec2 a_coord;\\\\n\\\\\\r\\n#ifdef VERTEX_COLOR_BLOCK\\\\n\\\\\\r\\n\\tattribute vec4 a_color;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//varyings\\\\n\\\\\\r\\nvarying vec3 v_pos;\\\\n\\\\\\r\\nvarying vec3 v_normal;\\\\n\\\\\\r\\nvarying vec2 v_uvs;\\\\n\\\\\\r\\nvarying vec3 v_local_pos;\\\\n\\\\\\r\\nvarying vec3 v_local_normal;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//matrices\\\\n\\\\\\r\\n#ifdef BLOCK_INSTANCING\\\\n\\\\\\r\\n\\tattribute mat4 u_model;\\\\n\\\\\\r\\n#else\\\\n\\\\\\r\\n\\tuniform mat4 u_model;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\nuniform mat4 u_normal_model;\\\\n\\\\\\r\\nuniform mat4 u_view;\\\\n\\\\\\r\\nuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n//material\\\\n\\\\\\r\\nuniform float u_displacementmap_factor;\\\\n\\\\\\r\\nuniform sampler2D displacement_texture;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//globals\\\\n\\\\\\r\\nuniform float u_time;\\\\n\\\\\\r\\nuniform vec4 u_viewport;\\\\n\\\\\\r\\nuniform float u_point_size;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"light\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"morphing\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"skinning\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//camera\\\\n\\\\\\r\\nuniform vec3 u_camera_eye;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n{{vs_out}}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma event \\\\\\\"vs_functions\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvoid main() {\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvec4 vertex4 = vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\tv_local_pos = a_vertex;\\\\n\\\\\\r\\n\\tv_local_normal = a_normal;\\\\n\\\\\\r\\n\\tv_normal = a_normal;\\\\n\\\\\\r\\n\\tv_uvs = a_coord;\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n //deforms\\\\n\\\\\\r\\n {{vs_local}}\\\\n\\\\\\r\\n applyMorphing( vertex4, v_normal );\\\\n\\\\\\r\\n applySkinning( vertex4, v_normal );\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t//vertex\\\\n\\\\\\r\\n\\tv_pos = (u_model * vertex4).xyz;\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n applyLight(v_pos);\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n\\t//normal\\\\n\\\\\\r\\n\\t#ifdef SHADERBLOCK_INSTANCING\\\\n\\\\\\r\\n\\t\\tv_normal = (u_model * vec4(v_normal,0.0)).xyz;\\\\n\\\\\\r\\n\\t#else\\\\n\\\\\\r\\n\\t\\tv_normal = (u_normal_model * vec4(v_normal,0.0)).xyz;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n {{vs_global}}\\\\n\\\\\\r\\n\\tgl_Position = u_viewprojection * vec4(v_pos,1.0);\\\\n\\\\\\r\\n\\t#pragma event \\\\\\\"vs_final\\\\\\\"\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\\\\\picking.fs\\\\n\\\\\\r\\n\\tprecision mediump float;\\\\n\\\\\\r\\n\\tuniform vec4 u_material_color;\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tgl_FragColor = u_material_color;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\n\\r\\n/* example to inject code in the standardMaterial without having to edit it\\r\\n//hooks are vs_out (out of main), vs_local (vertex4 to deform vertices localy), vs_global (v_pos to deform final position), fs_out (out of main), fs_encode (final_color before being written)\\r\\nthis.onStart = function()\\r\\n{\\r\\n LS.StandardMaterial.onShaderCode = function(code,mat)\\r\\n {\\r\\n \\tcode.fs_encode = \\\"final_color.x = final_color.y;\\\";\\r\\n }\\r\\n\\tLS.StandardMaterial.clearShadersCache();\\r\\n}\\r\\n*/\\r\\n///@FILE:../src/materials/surfaceMaterial.js\\r\\nfunction SurfaceMaterial( o )\\r\\n{\\r\\n\\tMaterial.call( this, null );\\r\\n\\r\\n\\tthis.shader_name = \\\"surface\\\";\\r\\n\\r\\n\\tthis.blend_mode = LS.Blend.NORMAL;\\r\\n\\tthis._light_mode = 1;\\r\\n\\r\\n\\tthis.flags = {\\r\\n\\t\\talpha_test: false,\\r\\n\\t\\talpha_test_shadows: false,\\r\\n\\t\\ttwo_sided: false,\\r\\n\\t\\tflip_normals: false,\\r\\n\\t\\tdepth_test: true,\\r\\n\\t\\tdepth_write: true,\\r\\n\\t\\tignore_lights: false,\\r\\n\\t\\tcast_shadows: true,\\r\\n\\t\\treceive_shadows: true,\\r\\n\\t\\tignore_frustum: false\\r\\n\\t};\\r\\n\\r\\n\\tthis._code = \\\"void surf(in Input IN, inout SurfaceOutput o) {\\\\n\\\\\\r\\n\\to.Albedo = vec3(1.0) * IN.color.xyz;\\\\n\\\\\\r\\n\\to.Normal = IN.worldNormal;\\\\n\\\\\\r\\n\\to.Emission = vec3(0.0);\\\\n\\\\\\r\\n\\to.Specular = 1.0;\\\\n\\\\\\r\\n\\to.Gloss = 40.0;\\\\n\\\\\\r\\n\\to.Reflectivity = max(0.0, 0.5 - dot(IN.viewDir,o.Normal));\\\\n\\\\\\r\\n\\to.Alpha = IN.color.a;\\\\n}\\\\n\\\";\\r\\n\\r\\n\\tthis._uniforms = {};\\r\\n\\tthis._samplers = [];\\r\\n\\r\\n\\tthis._mustUpdate = false;\\r\\n\\r\\n\\tthis.properties = []; //array of configurable properties\\r\\n\\tif(o) \\r\\n\\t\\tthis.configure(o);\\r\\n\\r\\n\\tthis.computeCode();\\r\\n}\\r\\n\\r\\n\\r\\nSurfaceMaterial.prototype.prepare = StandardMaterial.prototype.prepare;\\r\\n\\r\\nSurfaceMaterial.icon = \\\"mini-icon-material.png\\\";\\r\\n\\r\\nSurfaceMaterial.prototype.onCodeChange = function()\\r\\n{\\r\\n\\tthis._mustUpdate = true;\\r\\n\\t//this.computeCode();\\r\\n}\\r\\n\\r\\nObject.defineProperty( SurfaceMaterial.prototype, \\\"code\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._code;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._code = String(v);\\r\\n\\t\\tthis._mustUpdate = true;\\r\\n\\t}\\r\\n});\\r\\n\\r\\nSurfaceMaterial.prototype.getCode = function()\\r\\n{\\r\\n\\treturn this._code;\\r\\n}\\r\\n\\r\\nSurfaceMaterial.prototype.computeCode = function()\\r\\n{\\r\\n\\tvar uniforms_code = \\\"\\\";\\r\\n\\tfor(var i = 0, l = this.properties.length; i < l; ++i )\\r\\n\\t{\\r\\n\\t\\tvar code = \\\"uniform \\\";\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tswitch(prop.type)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase 'number': code += \\\"float \\\"; break;\\r\\n\\t\\t\\tcase 'vec2': code += \\\"vec2 \\\"; break;\\r\\n\\t\\t\\tcase 'color':\\r\\n\\t\\t\\tcase 'vec3': code += \\\"vec3 \\\"; break;\\r\\n\\t\\t\\tcase 'color4':\\r\\n\\t\\t\\tcase 'vec4': code += \\\"vec4 \\\"; break;\\r\\n\\t\\t\\tcase 'sampler':\\r\\n\\t\\t\\tcase 'texture': code += \\\"sampler2D \\\"; break;\\r\\n\\t\\t\\tcase 'cubemap': code += \\\"samplerCube \\\"; break;\\r\\n\\t\\t\\tdefault: \\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\t\\tcode += prop.name + \\\";\\\\n\\\";\\r\\n\\t\\tuniforms_code += code;\\r\\n\\t}\\r\\n\\r\\n\\t/*\\r\\n\\tvar lines = this._code.split(\\\"\\\\n\\\");\\r\\n\\tfor(var i = 0, l = lines.length; i < l; ++i )\\r\\n\\t\\tlines[i] = lines[i].split(\\\"//\\\")[0]; //remove comments\\r\\n\\t*/\\r\\n\\r\\n\\tthis.surf_code = uniforms_code + \\\"\\\\n\\\" + this._code;\\r\\n\\r\\n\\tvar context = {\\r\\n\\t\\tfs_out: this.surf_code\\r\\n\\t};\\r\\n\\r\\n\\tvar final_code = LS.ShaderCode.replaceCode( LS.SurfaceMaterial.code_template, context );\\r\\n\\t//var final_code = LS.SurfaceMaterial.code_template.replace( /{{}}/gi, this.surf_code );\\r\\n\\r\\n\\tif(!this._shadercode)\\r\\n\\t\\tthis._shadercode = new LS.ShaderCode();\\r\\n\\tthis._shadercode.code = final_code;\\r\\n\\tthis._mustUpdate = false;\\r\\n}\\r\\n\\r\\nSurfaceMaterial.prototype.renderInstance = ShaderMaterial.prototype.renderInstance;\\r\\n\\r\\nSurfaceMaterial.prototype.getShaderCode = function( instance, render_settings, pass )\\r\\n{\\r\\n\\tif(!this._shadercode || this._mustUpdate )\\r\\n\\t\\tthis.computeCode();\\r\\n\\treturn this._shadercode;\\r\\n}\\r\\n\\r\\nSurfaceMaterial.prototype.fillUniforms = function( scene, options )\\r\\n{\\r\\n\\tvar samplers = this._samplers;\\r\\n\\tsamplers.length = 0;\\r\\n\\r\\n\\tvar last_texture_slot = 0;\\r\\n\\tfor(var i = 0, l = this.properties.length; i < l; ++i )\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.type == \\\"texture\\\" || prop.type == \\\"cubemap\\\" || prop.type == \\\"sampler\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar texture = prop.value;\\r\\n\\t\\t\\tsamplers[ last_texture_slot ] = texture;\\r\\n\\t\\t\\tthis._uniforms[ prop.name ] = last_texture_slot;\\r\\n\\t\\t\\tlast_texture_slot++;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis._uniforms[ prop.name ] = prop.value;\\r\\n\\t}\\r\\n\\r\\n\\tthis._uniforms.u_material_color = this._color;\\r\\n}\\r\\n\\r\\nSurfaceMaterial.prototype.configure = function(o) { \\r\\n\\tif(o.flags !== undefined && o.flags.constructor === Number)\\r\\n\\t\\tdelete o[\\\"flags\\\"]; //LEGACY\\r\\n\\tMaterial.prototype.configure.call( this, o ); //it will call setProperty\\r\\n\\t//LS.cloneObject( o, this );\\r\\n\\tif(o.properties)\\r\\n\\t\\tthis.properties = LS.cloneObject( o.properties );\\r\\n\\tthis.computeCode();\\r\\n}\\r\\n\\r\\n/**\\r\\n* gets all the properties and its types\\r\\n* @method getPropertiesInfo\\r\\n* @return {Object} object with name:type\\r\\n*/\\r\\nSurfaceMaterial.prototype.getPropertiesInfo = function()\\r\\n{\\r\\n\\tvar o = {\\r\\n\\t\\tcolor: LS.TYPES.VEC3,\\r\\n\\t\\topacity: LS.TYPES.NUMBER,\\r\\n\\t\\tshader_name: LS.TYPES.STRING,\\r\\n\\t\\tblend_mode: LS.TYPES.NUMBER,\\r\\n\\t\\tcode: LS.TYPES.STRING\\r\\n\\t};\\r\\n\\r\\n\\t//from this material\\r\\n\\tfor(var i in this.properties)\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\to[prop.name] = prop.type;\\r\\n\\t}\\t\\r\\n\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Event used to inform if one resource has changed its name\\r\\n* @method onResourceRenamed\\r\\n* @param {Object} resources object where all the resources are stored\\r\\n* @return {Texture}\\r\\n*/\\r\\nSurfaceMaterial.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\t//global\\r\\n\\tMaterial.prototype.onResourceRenamed.call( this, old_name, new_name, resource );\\r\\n\\r\\n\\t//specific\\r\\n\\tfor(var i = 0, l = this.properties.length; i < l; ++i )\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif( prop.value == old_name)\\r\\n\\t\\t\\tprop.value = new_name;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* gets all the properties and its types\\r\\n* @method getProperty\\r\\n* @return {Object} object with name:type\\r\\n*/\\r\\nSurfaceMaterial.prototype.getProperty = function( name )\\r\\n{\\r\\n\\tif(this[name])\\r\\n\\t\\treturn this[name];\\r\\n\\r\\n\\tif( name.substr(0,4) == \\\"tex_\\\")\\r\\n\\t{\\r\\n\\t\\tvar tex = this.textures[ name.substr(4) ];\\r\\n\\t\\tif(!tex) return null;\\r\\n\\t\\treturn tex.texture;\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i = 0, l = this.properties.length; i < l; ++i )\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.name == name)\\r\\n\\t\\t\\treturn prop.value;\\r\\n\\t}\\t\\r\\n\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* assign a value to a property in a safe way\\r\\n* @method setProperty\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\nSurfaceMaterial.prototype.setProperty = function(name, value)\\r\\n{\\r\\n\\t//redirect to base material\\r\\n\\tif( Material.prototype.setProperty.call(this,name,value) )\\r\\n\\t\\treturn true;\\r\\n\\r\\n\\tif(name == \\\"shader_name\\\")\\r\\n\\t\\tthis.shader_name = value;\\r\\n\\r\\n\\tfor(var i = 0, l = this.properties.length; i < l; ++i )\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.name != name)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tprop.value = value;\\r\\n\\t\\treturn true;\\r\\n\\t}\\r\\n\\r\\n\\tif( this[name] !== undefined)\\r\\n\\t\\tthis[name] = value;\\r\\n\\telse\\r\\n\\t\\treturn false;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/*\\r\\nSurfaceMaterial.prototype.setPropertyValueFromPath = function( path, value, offset )\\r\\n{\\r\\n\\toffset = offset || 0;\\r\\n\\tif( path.length < (offset+1) )\\r\\n\\t\\treturn;\\r\\n\\treturn this.setProperty( path[offset], value );\\r\\n}\\r\\n*/\\r\\n\\r\\nSurfaceMaterial.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif( path.length < 1)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar info = Material.prototype.getPropertyInfoFromPath.call(this,path);\\r\\n\\tif(info)\\r\\n\\t\\treturn info;\\r\\n\\r\\n\\tvar varname = path[0];\\r\\n\\r\\n\\tfor(var i = 0, l = this.properties.length; i < l; ++i )\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.name != varname)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tnode: this._root,\\r\\n\\t\\t\\ttarget: this,\\r\\n\\t\\t\\tname: prop.name,\\r\\n\\t\\t\\tvalue: prop.value,\\r\\n\\t\\t\\ttype: prop.type\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\treturn;\\r\\n}\\r\\n\\r\\n\\r\\nSurfaceMaterial.prototype.getTextureChannels = function()\\r\\n{\\r\\n\\tvar channels = [];\\r\\n\\r\\n\\tfor(var i = 0, l = this.properties.length; i < l; ++i )\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.type != \\\"texture\\\" && prop.type != \\\"cubemap\\\" && prop.type != \\\"sampler\\\" )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tchannels.push( prop.name );\\r\\n\\t}\\r\\n\\r\\n\\treturn channels;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Assigns a texture to a channel\\r\\n* @method setTexture\\r\\n* @param {String} channel \\r\\n* @param {Texture} texture\\r\\n*/\\r\\nSurfaceMaterial.prototype.setTexture = function( channel, texture, sampler_options ) {\\r\\n\\tif(!channel)\\r\\n\\t\\tthrow(\\\"SurfaceMaterial.prototype.setTexture channel must be specified\\\");\\r\\n\\r\\n\\tvar sampler = null;\\r\\n\\r\\n\\r\\n\\t//special case\\r\\n\\tif(channel == \\\"environment\\\")\\r\\n\\t\\treturn Material.prototype.setTexture.call(this, channel, texture, sampler_options );\\r\\n\\r\\n\\tfor(var i = 0; i < this.properties.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.type != \\\"texture\\\" && prop.type != \\\"cubemap\\\" && prop.type != \\\"sampler\\\")\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif(channel && prop.name != channel) //assign to the channel or if there is no channel just to the first one\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t//assign sampler\\r\\n\\t\\tsampler = this.textures[ channel ];\\r\\n\\t\\tif(!sampler)\\r\\n\\t\\t\\tsampler = this.textures[channel] = { texture: texture, uvs: \\\"0\\\", wrap: 0, minFilter: 0, magFilter: 0 }; //sampler\\r\\n\\r\\n\\t\\tif(sampler_options)\\r\\n\\t\\t\\tfor(var i in sampler_options)\\r\\n\\t\\t\\t\\tsampler[i] = sampler_options[i];\\r\\n\\r\\n\\t\\tprop.value = prop.type == \\\"sampler\\\" ? sampler : texture;\\r\\n\\t\\tbreak;\\r\\n\\t}\\r\\n\\r\\n\\t//preload texture\\r\\n\\tif(texture && texture.constructor == String && texture[0] != \\\":\\\")\\r\\n\\t\\tLS.ResourcesManager.load( texture );\\r\\n\\r\\n\\treturn sampler;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Collects all the resources needed by this material (textures)\\r\\n* @method getResources\\r\\n* @param {Object} resources object where all the resources are stored\\r\\n* @return {Texture}\\r\\n*/\\r\\nSurfaceMaterial.prototype.getResources = function (res)\\r\\n{\\r\\n\\tfor(var i = 0, l = this.properties.length; i < l; ++i )\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.type != \\\"texture\\\" && prop.type != \\\"cubemap\\\" && prop.type != \\\"sampler\\\")\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(!prop.value)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tvar texture = prop.type == \\\"sampler\\\" ? prop.value.texture : prop.value;\\r\\n\\t\\tif( typeof( texture ) == \\\"string\\\" )\\r\\n\\t\\t\\tres[ texture ] = GL.Texture;\\r\\n\\t}\\r\\n\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nLS.registerMaterialClass( SurfaceMaterial );\\r\\nLS.SurfaceMaterial = SurfaceMaterial;\\r\\n\\r\\nSurfaceMaterial.code_template = \\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\\\\\color.vs\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nprecision mediump float;\\\\n\\\\\\r\\nattribute vec3 a_vertex;\\\\n\\\\\\r\\nattribute vec3 a_normal;\\\\n\\\\\\r\\nattribute vec2 a_coord;\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"vertex_color\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"coord1\\\\\\\"\\\\n\\\\\\r\\n#ifdef BLOCK_COORD1\\\\n\\\\\\r\\n\\tattribute vec2 a_coord1;\\\\n\\\\\\r\\n\\tvarying vec2 v_uvs1;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n#ifdef BLOCK_VERTEX_COLOR\\\\n\\\\\\r\\n\\tattribute vec4 a_color;\\\\n\\\\\\r\\n\\tvarying vec4 v_vertex_color;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//varyings\\\\n\\\\\\r\\nvarying vec3 v_pos;\\\\n\\\\\\r\\nvarying vec3 v_normal;\\\\n\\\\\\r\\nvarying vec2 v_uvs;\\\\n\\\\\\r\\nvarying vec3 v_local_pos;\\\\n\\\\\\r\\nvarying vec3 v_local_normal;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//matrices\\\\n\\\\\\r\\nuniform mat4 u_model;\\\\n\\\\\\r\\nuniform mat4 u_normal_model;\\\\n\\\\\\r\\nuniform mat4 u_view;\\\\n\\\\\\r\\nuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//globals\\\\n\\\\\\r\\nuniform float u_time;\\\\n\\\\\\r\\nuniform vec4 u_viewport;\\\\n\\\\\\r\\nuniform float u_point_size;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"light\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"morphing\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"skinning\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//camera\\\\n\\\\\\r\\nuniform vec3 u_camera_eye;\\\\n\\\\\\r\\n{{vs_out}}\\\\n\\\\\\r\\nvoid main() {\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvec4 vertex4 = vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\tv_local_pos = a_vertex;\\\\n\\\\\\r\\n\\tv_local_normal = a_normal;\\\\n\\\\\\r\\n\\tv_normal = a_normal;\\\\n\\\\\\r\\n\\tv_uvs = a_coord;\\\\n\\\\\\r\\n\\t#ifdef BLOCK_COORD1\\\\n\\\\\\r\\n\\t\\tv_uvs1 = a_coord1;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef BLOCK_VERTEX_COLOR\\\\n\\\\\\r\\n\\t\\tv_vertex_color = a_color;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n //deforms\\\\n\\\\\\r\\n {{vs_local}}\\\\n\\\\\\r\\n applyMorphing( vertex4, v_normal );\\\\n\\\\\\r\\n applySkinning( vertex4, v_normal );\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t//vertex\\\\n\\\\\\r\\n\\tv_pos = (u_model * vertex4).xyz;\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n applyLight(v_pos);\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n\\t//normal\\\\n\\\\\\r\\n\\tv_normal = (u_normal_model * vec4(v_normal,0.0)).xyz;\\\\n\\\\\\r\\n {{vs_global}}\\\\n\\\\\\r\\n\\tgl_Position = u_viewprojection * vec4(v_pos,1.0);\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\\\\\color.fs\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nprecision mediump float;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//varyings\\\\n\\\\\\r\\nvarying vec3 v_pos;\\\\n\\\\\\r\\nvarying vec3 v_normal;\\\\n\\\\\\r\\nvarying vec3 v_local_pos;\\\\n\\\\\\r\\nvarying vec3 v_local_normal;\\\\n\\\\\\r\\nvarying vec2 v_uvs;\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"vertex_color\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"coord1\\\\\\\"\\\\n\\\\\\r\\n#ifdef BLOCK_COORD1\\\\n\\\\\\r\\n\\tvarying vec2 v_uvs1;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n#ifdef BLOCK_VERTEX_COLOR\\\\n\\\\\\r\\n\\tvarying vec4 v_vertex_color;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//globals\\\\n\\\\\\r\\nuniform vec3 u_camera_eye;\\\\n\\\\\\r\\nuniform vec4 u_clipping_plane;\\\\n\\\\\\r\\nuniform float u_time;\\\\n\\\\\\r\\nuniform vec4 u_background_color;\\\\n\\\\\\r\\nuniform vec4 u_material_color;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"light\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"applyReflection\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma snippet \\\\\\\"perturbNormal\\\\\\\"\\\\n\\\\\\r\\n#pragma snippet \\\\\\\"testClippingPlane\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n{{fs_out}}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvoid main() {\\\\n\\\\\\r\\n\\tInput IN = getInput();\\\\n\\\\\\r\\n\\tif(testClippingPlane(u_clipping_plane,IN.worldPos) < 0.0)\\\\n\\\\\\r\\n\\t\\tdiscard;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tIN.vertex = v_local_pos;\\\\n\\\\\\r\\n\\tIN.normal = v_local_normal;\\\\n\\\\\\r\\n\\t#ifdef BLOCK_VERTEX_COLOR\\\\n\\\\\\r\\n\\t\\tIN.color = v_vertex_color;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef BLOCK_COORD1\\\\n\\\\\\r\\n\\t\\tIN.uv1 = v_uvs1;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\tSurfaceOutput o = getSurfaceOutput();\\\\n\\\\\\r\\n\\tsurf(IN,o);\\\\n\\\\\\r\\n\\tvec4 final_color = vec4(0.0);\\\\n\\\\\\r\\n\\tLight LIGHT = getLight();\\\\n\\\\\\r\\n\\tFinalLight final_light = computeLight( o, IN, LIGHT );\\\\n\\\\\\r\\n\\tfinal_color.xyz = applyLight( o, final_light );\\\\n\\\\\\r\\n\\tfinal_color.a = o.Alpha;\\\\n\\\\\\r\\n\\tif( o.Reflectivity > 0.0 )\\\\n\\\\\\r\\n\\t\\tfinal_color = applyReflection( IN, o, final_color );\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tgl_FragColor = final_color;\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\\\\\shadow.vs\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nprecision mediump float;\\\\n\\\\\\r\\nattribute vec3 a_vertex;\\\\n\\\\\\r\\nattribute vec3 a_normal;\\\\n\\\\\\r\\nattribute vec2 a_coord;\\\\n\\\\\\r\\n#ifdef USE_COLORS\\\\n\\\\\\r\\nattribute vec4 a_color;\\\\n\\\\\\r\\n#endif\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//varyings\\\\n\\\\\\r\\nvarying vec3 v_pos;\\\\n\\\\\\r\\nvarying vec3 v_normal;\\\\n\\\\\\r\\nvarying vec3 v_local_pos;\\\\n\\\\\\r\\nvarying vec3 v_local_normal;\\\\n\\\\\\r\\nvarying vec2 v_uvs;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//matrices\\\\n\\\\\\r\\nuniform mat4 u_model;\\\\n\\\\\\r\\nuniform mat4 u_normal_model;\\\\n\\\\\\r\\nuniform mat4 u_view;\\\\n\\\\\\r\\nuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//globals\\\\n\\\\\\r\\nuniform float u_time;\\\\n\\\\\\r\\nuniform vec4 u_viewport;\\\\n\\\\\\r\\nuniform float u_point_size;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"light\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"morphing\\\\\\\"\\\\n\\\\\\r\\n#pragma shaderblock \\\\\\\"skinning\\\\\\\"\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//camera\\\\n\\\\\\r\\nuniform vec3 u_camera_eye;\\\\n\\\\\\r\\n{{vs_out}}\\\\n\\\\\\r\\nvoid main() {\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvec4 vertex4 = vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\tv_local_pos = a_vertex;\\\\n\\\\\\r\\n\\tv_local_normal = a_normal;\\\\n\\\\\\r\\n\\tv_normal = a_normal;\\\\n\\\\\\r\\n\\tv_uvs = a_coord;\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n //deforms\\\\n\\\\\\r\\n {{vs_local}}\\\\n\\\\\\r\\n applyMorphing( vertex4, v_normal );\\\\n\\\\\\r\\n applySkinning( vertex4, v_normal );\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t//vertex\\\\n\\\\\\r\\n\\tv_pos = (u_model * vertex4).xyz;\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n applyLight(v_pos);\\\\n\\\\\\r\\n \\\\n\\\\\\r\\n\\t//normal\\\\n\\\\\\r\\n\\tv_normal = (u_normal_model * vec4(v_normal,0.0)).xyz;\\\\n\\\\\\r\\n {{vs_global}}\\\\n\\\\\\r\\n\\tgl_Position = u_viewprojection * vec4(v_pos,1.0);\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\\\\\shadow.fs\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nprecision mediump float;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//varyings\\\\n\\\\\\r\\nvarying vec3 v_pos;\\\\n\\\\\\r\\nvarying vec3 v_normal;\\\\n\\\\\\r\\nvarying vec2 v_uvs;\\\\n\\\\\\r\\nvarying vec3 v_local_pos;\\\\n\\\\\\r\\nvarying vec3 v_local_normal;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n//globals\\\\n\\\\\\r\\nuniform vec3 u_camera_eye;\\\\n\\\\\\r\\nuniform vec4 u_clipping_plane;\\\\n\\\\\\r\\nuniform vec4 u_material_color;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nuniform mat3 u_texture_matrix;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n#pragma snippet \\\\\\\"input\\\\\\\"\\\\n\\\\\\r\\n#pragma snippet \\\\\\\"surface\\\\\\\"\\\\n\\\\\\r\\n#pragma snippet \\\\\\\"perturbNormal\\\\\\\"\\\\n\\\\\\r\\n#define SHADOWMAP\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n{{fs_out}}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvoid main() {\\\\n\\\\\\r\\n\\tInput IN = getInput();\\\\n\\\\\\r\\n\\tIN.vertex = v_local_pos;\\\\n\\\\\\r\\n\\tIN.normal = v_local_normal;\\\\n\\\\\\r\\n\\tSurfaceOutput o = getSurfaceOutput();\\\\n\\\\\\r\\n\\tsurf(IN,o);\\\\n\\\\\\r\\n\\tgl_FragColor = vec4(o.Albedo,o.Alpha);\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\";\\r\\n///@FILE:../src/materials/graphMaterial.js\\r\\nfunction GraphMaterial(o)\\r\\n{\\r\\n\\tShaderMaterial.call(this,null); //do not pass the data object, it is called later\\r\\n\\r\\n\\tthis.blend_mode = LS.Blend.NORMAL;\\r\\n\\r\\n\\tthis._filename = \\\"\\\";\\r\\n\\r\\n\\tthis._shader = \\\"\\\";\\r\\n\\tthis._shader_version = -1;\\r\\n\\tthis._shader_flags = 0; //?\\r\\n\\r\\n\\tthis._graphcode = null; //resource that contains the graph json\\r\\n\\r\\n\\tthis._uniforms = {};\\r\\n\\tthis._samplers = [];\\r\\n\\tthis._properties = [];\\r\\n\\tthis._properties_by_name = {};\\r\\n\\r\\n\\tthis._passes = {};\\r\\n\\tthis._light_mode = Material.ONE_LIGHT;\\r\\n\\tthis._primitive = -1;\\r\\n\\tthis._allows_instancing = false;\\r\\n\\r\\n\\tthis._version = -1;\\r\\n\\tthis._shader_version = -1;\\r\\n\\r\\n\\tthis._loading = false;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nGraphMaterial.icon = \\\"mini-icon-graph.png\\\";\\r\\n\\r\\nGraphMaterial[\\\"@filename\\\"] = { type:\\\"resource\\\", data_type: \\\"graph\\\" };\\r\\n\\r\\nGraphMaterial.prototype.renderInstance = ShaderMaterial.prototype.renderInstance;\\r\\nGraphMaterial.prototype.renderShadowInstance = ShaderMaterial.prototype.renderShadowInstance;\\r\\nGraphMaterial.prototype.renderPickingInstance = ShaderMaterial.prototype.renderPickingInstance;\\r\\n\\r\\nObject.defineProperty( GraphMaterial.prototype, \\\"filename\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._filename;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(this._filename == v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(v) //to avoid double slashes\\r\\n\\t\\t\\tv = LS.ResourcesManager.cleanFullpath( v );\\r\\n\\t\\tthis._filename = v;\\r\\n\\t\\tthis._loading = false;\\r\\n\\t\\tthis.processGraph();\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( GraphMaterial.prototype, \\\"graphcode\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._graphcode;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\t//if(this._graphcode == v) return; //disabled because sometimes we want to force reload\\r\\n\\t\\tthis._loading = false;\\r\\n\\t\\tthis._graphcode = v;\\r\\n\\t\\tif( this._graphcode )\\r\\n\\t\\t\\tthis._filename = this._graphcode.fullpath || this._graphcode.filename;\\r\\n\\t\\telse \\r\\n\\t\\t\\tthis._filename = null;\\r\\n\\t\\t//this._graph_properties = this.serializeProperties();\\r\\n\\t\\tthis.processGraph();\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( GraphMaterial.prototype, \\\"graph\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._graphcode ? this._graphcode.graph : null;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"graph cannot be set to a material, you must assign a graphcode instead\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\nGraphMaterial.shader_codes = {};\\r\\n\\r\\n//returns the LS.ShaderCode required to render\\r\\n//here we cannot filter by light pass because this is done before applying shaderblocks\\r\\n//in the StandardMaterial we cache versions of the ShaderCode according to the settings\\r\\nGraphMaterial.prototype.getShaderCode = function( instance, render_settings, pass )\\r\\n{\\r\\n\\tif(!this._graphcode || !this._graphcode.getShaderCode)\\r\\n\\t\\treturn null;\\r\\n\\treturn this._graphcode.getShaderCode();\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Collects all the resources needed by this material (textures)\\r\\n* @method getResources\\r\\n* @param {Object} resources object where all the resources are stored\\r\\n* @return {Texture}\\r\\n*/\\r\\nGraphMaterial.prototype.getResources = function (res)\\r\\n{\\r\\n\\tif(!this._graphcode)\\r\\n\\t\\treturn res;\\r\\n\\r\\n\\tres[ this.filename ] = true;\\r\\n\\tif(this._graphcode)\\r\\n\\t\\tthis._graphcode.graph.sendEventToAllNodes(\\\"getResources\\\",res);\\r\\n\\t\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nGraphMaterial.prototype.serialize = function() { \\r\\n\\t//var o = LS.Material.prototype.serialize.apply(this);\\r\\n\\treturn {\\r\\n\\t\\tuid: this.uid,\\r\\n\\t\\tmaterial_class: LS.getObjectClassName(this),\\r\\n\\t\\tfilename: this.filename,\\r\\n\\t\\tproperties: null //TO DO\\r\\n\\t}\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\n\\r\\nGraphMaterial.prototype.configure = function(o) { \\r\\n\\tLS.cloneObject(o, this);\\r\\n\\tthis.processGraph();\\r\\n}\\r\\n\\r\\nGraphMaterial.prototype.processGraph = function( skip_events, on_complete )\\r\\n{\\r\\n\\tif(!this._filename)\\r\\n\\t{\\r\\n\\t\\tthis._graphcode = null;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar that = this;\\r\\n\\tthis._graphcode = LS.ResourcesManager.getResource( this._filename );\\r\\n\\tif(!this._graphcode && !this._loading) //must be loaded\\r\\n\\t{\\r\\n\\t\\tthis._loading = true;\\r\\n\\t\\tLS.ResourcesManager.load( this._filename, null, function( res, url ){\\r\\n\\t\\t\\tthis._loading = false;\\r\\n\\t\\t\\tif( url != that.filename )\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tif( res && res.type == GraphCode.SHADER_GRAPH )\\r\\n\\t\\t\\t\\tthat._graphcode = res;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tconsole.error(\\\"Shader Graph not found or now a Shader Graph\\\");\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\ton_complete(that);\\r\\n\\t\\t});\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* gets all the properties and its types\\r\\n* @method getProperties\\r\\n* @return {Object} object with name:type\\r\\n*/\\r\\nGraphMaterial.prototype.getProperties = function()\\r\\n{\\r\\n\\tvar o = {\\r\\n\\t\\tcolor:\\\"vec3\\\",\\r\\n\\t\\topacity:\\\"number\\\",\\r\\n\\t\\tshader_name: \\\"string\\\",\\r\\n\\t\\tblend_mode: \\\"number\\\",\\r\\n\\t\\tcode: \\\"string\\\"\\r\\n\\t};\\r\\n\\r\\n\\t//from this material\\r\\n\\tfor(var i in this.properties)\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\to[prop.name] = prop.type;\\r\\n\\t}\\t\\r\\n\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Event used to inform if one resource has changed its name\\r\\n* @method onResourceRenamed\\r\\n* @param {Object} resources object where all the resources are stored\\r\\n* @return {Texture}\\r\\n*/\\r\\nGraphMaterial.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\t//global\\r\\n\\tMaterial.prototype.onResourceRenamed.call( this, old_name, new_name, resource );\\r\\n\\r\\n\\t//specific\\r\\n\\tfor(var i in this.properties)\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif( prop.value == old_name)\\r\\n\\t\\t\\tprop.value = new_name;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* gets all the properties and its types\\r\\n* @method getProperty\\r\\n* @return {Object} object with name:type\\r\\n*/\\r\\nGraphMaterial.prototype.getProperty = function(name)\\r\\n{\\r\\n\\tif(this[name])\\r\\n\\t\\treturn this[name];\\r\\n\\r\\n\\tif( name.substr(0,4) == \\\"tex_\\\")\\r\\n\\t\\treturn this.textures[ name.substr(4) ];\\r\\n\\r\\n\\tfor(var i in this.properties)\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.name == name)\\r\\n\\t\\t\\treturn prop.value;\\r\\n\\t}\\t\\r\\n\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* assign a value to a property in a safe way\\r\\n* @method setProperty\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\nGraphMaterial.prototype.setProperty = function(name, value)\\r\\n{\\r\\n\\t//redirect to base material\\r\\n\\tif( Material.prototype.setProperty.call(this,name,value) )\\r\\n\\t\\treturn true;\\r\\n\\r\\n\\tfor(var i in this.properties)\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.name != name)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tprop.value = value;\\r\\n\\t\\treturn true;\\r\\n\\t}\\r\\n\\r\\n\\treturn false;\\r\\n}\\r\\n\\r\\n\\r\\nGraphMaterial.prototype.getTextureChannels = function()\\r\\n{\\r\\n\\tvar channels = [];\\r\\n\\r\\n\\tfor(var i in this.properties)\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.type != \\\"texture\\\" && prop.type != \\\"cubemap\\\")\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tchannels.push(prop.name);\\r\\n\\t}\\r\\n\\r\\n\\treturn channels;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Assigns a texture to a channel\\r\\n* @method setTexture\\r\\n* @param {Texture} texture\\r\\n* @param {String} channel default is COLOR\\r\\n*/\\r\\nGraphMaterial.prototype.setTexture = function(texture, channel, uvs) {\\r\\n\\r\\n\\tfor(var i in this.properties)\\r\\n\\t{\\r\\n\\t\\tvar prop = this.properties[i];\\r\\n\\t\\tif(prop.type != \\\"texture\\\" && prop.type != \\\"cubemap\\\")\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(channel && prop.name != channel) //assign to the channel or if there is no channel just to the first one\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tprop.value = texture;\\r\\n\\t\\tif(this.textures)\\r\\n\\t\\t\\tthis.textures[channel] = texture;\\r\\n\\t\\tif(!channel)\\r\\n\\t\\t\\tbreak;\\r\\n\\t}\\r\\n\\r\\n\\tif(!texture) return;\\r\\n\\tif(texture.constructor == String && texture[0] != \\\":\\\")\\r\\n\\t\\tResourcesManager.load(texture);\\r\\n}\\r\\n\\r\\nLS.registerMaterialClass( GraphMaterial );\\r\\nLS.GraphMaterial = GraphMaterial;\\r\\n\\r\\n\\r\\n///@FILE:../src/componentContainer.js\\r\\n///@INFO: BASE\\r\\n/*\\r\\n\\tA component container is someone who could have components attached to it.\\r\\n\\tMostly used for SceneNodes but it could be used for other classes (like Scene or Project).\\r\\n*/\\r\\n\\r\\n/**\\r\\n* ComponentContainer class allows to add component based properties to any other class\\r\\n* @class ComponentContainer\\r\\n* @constructor\\r\\n*/\\r\\nfunction ComponentContainer()\\r\\n{\\r\\n\\t//this function never will be called (because only the methods are attached to other classes)\\r\\n\\t//unless you instantiate this class directly, something that would be weird\\r\\n\\tthis._components = [];\\r\\n\\t//this._components_by_uid = {}; //TODO\\r\\n}\\r\\n\\r\\n/*\\r\\nObject.defineProperty( ComponentContainer.prototype, \\\"components\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._components;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"Components cannot be set, you must use addComponent\\\");\\r\\n\\t}\\r\\n});\\r\\n*/\\r\\n\\r\\n/**\\r\\n* Adds a component to this node.\\r\\n* @method configureComponents\\r\\n* @param {Object} info object containing all the info from a previous serialization\\r\\n*/\\r\\nComponentContainer.prototype.configureComponents = function( info )\\r\\n{\\r\\n\\tif(!info.components)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar to_configure = [];\\r\\n\\r\\n\\t//attach first, configure later\\r\\n\\tfor(var i = 0, l = info.components.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar comp_info = info.components[i];\\r\\n\\t\\tvar comp_class = comp_info[0];\\r\\n\\t\\tvar comp = null;\\r\\n\\r\\n\\t\\t//special case: this is the only component that comes by default\\r\\n\\t\\tif(comp_class == \\\"Transform\\\" && i == 0 && this.transform) \\r\\n\\t\\t{\\r\\n\\t\\t\\tcomp = this.transform;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\t//search for the class\\r\\n\\t\\t\\tvar classObject = LS.Components[ comp_class ];\\r\\n\\t\\t\\tif(!classObject){\\r\\n\\t\\t\\t\\tconsole.error(\\\"Unknown component found: \\\" + comp_class);\\r\\n\\t\\t\\t\\tclassObject = LS.MissingComponent;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t//create component\\r\\n\\t\\t\\tcomp = new classObject();\\r\\n\\t\\t\\t//attach to node\\r\\n\\t\\t\\tthis.addComponent( comp );\\r\\n\\r\\n\\t\\t\\tif( comp.constructor === LS.MissingComponent )\\r\\n\\t\\t\\t\\tcomp._comp_class = comp_class;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//what about configure the comp after adding it? \\r\\n\\t\\t//comp.configure( comp_info[1] );\\r\\n\\t\\tto_configure.push( comp, comp_info[1] );\\r\\n\\r\\n\\t\\t//HACK very special case: due to requireScript\\r\\n\\t\\tif( comp.constructor === LS.Components.ScriptFromFile )\\r\\n\\t\\t\\tcomp._filename = comp_info[1].filename;\\r\\n\\r\\n\\t\\t//editor stuff\\r\\n\\t\\tif( comp_info[1].editor )\\r\\n\\t\\t\\tcomp._editor = comp_info[1].editor;\\r\\n\\r\\n\\t\\t//ensure the component uid is stored, some components may forgot about it\\r\\n\\t\\tif( comp_info[1].uid && comp_info[1].uid !== comp.uid )\\r\\n\\t\\t\\tcomp.uid = comp_info[1].uid;\\r\\n\\t}\\r\\n\\r\\n\\t//configure components now that all of them are created\\r\\n\\t//this is to avoid problems with components that check if the node has other components and if not they create it\\r\\n\\tfor(var i = 0, l = to_configure.length; i < l; i+=2)\\r\\n\\t{\\r\\n\\t\\tto_configure[i].configure( to_configure[i+1] );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Adds a component to this node.\\r\\n* @method serializeComponents\\r\\n* @param {Object} o container where the components will be stored\\r\\n*/\\r\\nComponentContainer.prototype.serializeComponents = function( o, simplified )\\r\\n{\\r\\n\\tif(!this._components)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\to.components = [];\\r\\n\\tfor(var i = 0, l = this._components.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar comp = this._components[i];\\r\\n\\t\\tif( !comp.serialize || comp.skip_serialize )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar obj = comp.serialize( simplified );\\r\\n\\r\\n\\t\\t//check for bad stuff inside the component\\r\\n\\t\\t/*\\r\\n\\t\\tfor(var j in obj)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar v = obj[j];\\r\\n\\t\\t\\tif( !v || v.constructor === Number || v.constructor === String || v.constructor === Boolean || v.constructor === Object || v.constructor === Array ) //regular data\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tobj[j] = LS.encodeObject(v);\\r\\n\\t\\t}\\r\\n\\t\\t*/\\r\\n\\r\\n\\t\\tif(comp._editor && !simplified )\\r\\n\\t\\t\\tobj.editor = comp._editor;\\r\\n\\r\\n\\t\\t//enforce uid storage\\r\\n\\t\\tif(comp.hasOwnProperty(\\\"_uid\\\") && !obj.uid)\\r\\n\\t\\t\\tobj.uid = comp.uid;\\r\\n\\r\\n\\t\\tvar object_class = null;\\r\\n\\t\\tif( comp.constructor === LS.MissingComponent )\\r\\n\\t\\t\\tobject_class = comp._comp_class;\\r\\n\\t\\telse\\r\\n\\t\\t\\tobject_class = LS.getObjectClassName( comp );\\r\\n\\r\\n\\t\\tif( LS.debug && object_class != obj.object_class )\\r\\n\\t\\t\\tconsole.warn(\\\"Component serialize without object_class: \\\", object_class );\\r\\n\\t\\tif(!obj.object_class)\\r\\n\\t\\t\\tobj.object_class = object_class; //enforce\\r\\n\\t\\t\\r\\n\\t\\to.components.push([ object_class, obj ]);\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns an array with all the components\\r\\n* @method getComponents\\r\\n* @return {Array} all the components\\r\\n*/\\r\\nComponentContainer.prototype.getComponents = function( class_type )\\r\\n{\\r\\n\\tif(class_type)\\r\\n\\t{\\r\\n\\t\\tvar result = [];\\r\\n\\t\\tif(class_type.constructor === String)\\r\\n\\t\\t\\tclass_type = LS.Components[class_type];\\r\\n\\t\\tfor(var i = 0, l = this._components.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar compo = this._components[i];\\r\\n\\t\\t\\tif( compo.constructor === class_type )\\r\\n\\t\\t\\t\\tresult.push( compo );\\r\\n\\t\\t}\\r\\n\\t\\treturn result;\\r\\n\\t}\\r\\n\\r\\n\\treturn this._components;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Adds a component to this node. (maybe attach would been a better name)\\r\\n* @method addComponent\\r\\n* @param {Object} component\\r\\n* @return {Object} component added\\r\\n*/\\r\\nComponentContainer.prototype.addComponent = function( component, index )\\r\\n{\\r\\n\\tif(!component)\\r\\n\\t\\tthrow(\\\"addComponent cannot receive null\\\");\\r\\n\\r\\n\\t//you may pass a component class instead of an instance\\r\\n\\tif(component.constructor === String)\\r\\n\\t{\\r\\n\\t\\tcomponent = LS.Components[ component ];\\r\\n\\t\\tif(!component)\\r\\n\\t\\t\\tthrow(\\\"component class not found: \\\" + arguments[0] );\\r\\n\\t}\\r\\n\\tif(component.is_component)\\r\\n\\t\\tcomponent = new component();\\r\\n\\t\\r\\n\\t//link component with container\\r\\n\\tcomponent._root = this;\\r\\n\\r\\n\\t//must have uid\\r\\n\\tif( !component.uid )\\r\\n\\t\\tcomponent.uid = LS.generateUId(\\\"COMP-\\\");\\r\\n\\r\\n\\t//not very clean, ComponetContainer shouldnt know about LS.SceneNode, but this is more simple\\r\\n\\tif( component.onAddedToNode)\\r\\n\\t\\tcomponent.onAddedToNode(this);\\r\\n\\r\\n\\tif( this._in_tree )\\r\\n\\t{\\r\\n\\t\\tif( component.uid )\\r\\n\\t\\t\\tthis._in_tree._components_by_uid[ component.uid ] = component;\\r\\n\\t\\telse\\r\\n\\t\\t\\tconsole.warn(\\\"component without uid?\\\", component);\\r\\n\\t\\tif(\\tcomponent.onAddedToScene )\\r\\n\\t\\t\\tcomponent.onAddedToScene( this.constructor == LS.Scene ? this : this._in_tree );\\r\\n\\t}\\r\\n\\r\\n\\t//link node with component\\r\\n\\tif(!this._components) \\r\\n\\t\\tObject.defineProperty( this, \\\"_components\\\", { value: [], enumerable: false });\\r\\n\\tif(this._components.indexOf(component) != -1)\\r\\n\\t\\tthrow(\\\"inserting the same component twice\\\");\\r\\n\\r\\n\\tif(index !== undefined && index <= this._components.length )\\r\\n\\t\\tthis._components.splice(index,0,component);\\r\\n\\telse\\r\\n\\t\\tthis._components.push( component );\\r\\n\\r\\n\\tLEvent.trigger( this, \\\"componentAdded\\\", component );\\r\\n\\r\\n\\treturn component;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Removes a component from this node.\\r\\n* @method removeComponent\\r\\n* @param {Object} component\\r\\n*/\\r\\nComponentContainer.prototype.removeComponent = function(component)\\r\\n{\\r\\n\\tif(!component)\\r\\n\\t\\tthrow(\\\"removeComponent cannot receive null\\\");\\r\\n\\r\\n\\t//unlink component with container\\r\\n\\tcomponent._root = null;\\r\\n\\r\\n\\t//not very clean, ComponetContainer shouldnt know about LS.SceneNode, but this is more simple\\r\\n\\tif( component.onRemovedFromNode )\\r\\n\\t\\tcomponent.onRemovedFromNode(this);\\r\\n\\r\\n\\tif( this._in_tree )\\r\\n\\t{\\r\\n\\t\\tdelete this._in_tree._components_by_uid[ component.uid ];\\r\\n\\t\\tif(component.onRemovedFromScene)\\r\\n\\t\\t\\tcomponent.onRemovedFromScene( this._in_tree );\\r\\n\\t}\\r\\n\\r\\n\\t//remove all events\\r\\n\\tLEvent.unbindAll(this,component);\\r\\n\\r\\n\\t//remove from components list\\r\\n\\tvar pos = this._components.indexOf(component);\\r\\n\\tif(pos != -1)\\r\\n\\t\\tthis._components.splice(pos,1);\\r\\n\\telse\\r\\n\\t\\tconsole.warn(\\\"removeComponent: Component not found in node\\\");\\r\\n\\r\\n\\tLEvent.trigger( this, \\\"componentRemoved\\\", component );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Removes all components from this node.\\r\\n* @method removeAllComponents\\r\\n* @param {Object} component\\r\\n*/\\r\\nComponentContainer.prototype.removeAllComponents = function()\\r\\n{\\r\\n\\twhile(this._components.length)\\r\\n\\t\\tthis.removeComponent( this._components[0] );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns if the container has a component of this class\\r\\n* @method hasComponent\\r\\n* @param {String|Class} component_class the component to search for, could be a string or the class itself\\r\\n*/\\r\\nComponentContainer.prototype.hasComponent = function( component_class )\\r\\n{\\r\\n\\tif(!this._components)\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\t//string\\r\\n\\tif( component_class.constructor === String )\\r\\n\\t{\\r\\n\\t\\tcomponent_class = LS.Components[ component_class ];\\r\\n\\t\\tif(!component_class)\\r\\n\\t\\t\\treturn false;\\r\\n\\t}\\r\\n\\r\\n\\t//search in components\\r\\n\\tfor(var i = 0, l = this._components.length; i < l; ++i)\\r\\n\\t\\tif( this._components[i].constructor === component_class )\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\r\\n\\treturn false;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns the first component of this container that is of the same class\\r\\n* @method getComponent\\r\\n* @param {Object|String} component_class the class to search a component from (could be the class or the name)\\r\\n* @param {Number} index [optional] if you want the Nth component of this class\\r\\n*/\\r\\nComponentContainer.prototype.getComponent = function( component_class, index )\\r\\n{\\r\\n\\tif(!this._components || !component_class)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\t//convert string to class\\r\\n\\tif( component_class.constructor === String )\\r\\n\\t{\\r\\n\\t\\t//special case, locator by name (the locator starts with an underscore if it is meant to be a name)\\r\\n\\t\\tif( component_class[0] == \\\"_\\\" ) \\r\\n\\t\\t{\\r\\n\\t\\t\\tcomponent_class = component_class.substr(1); //remove underscore\\r\\n\\t\\t\\tfor(var i = 0, l = this._components.length; i < l; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( this._components[i].name == component_class )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(index !== undefined && index > 0)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tindex--;\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\treturn this._components[i];\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//otherwise the string represents the class name\\r\\n\\t\\tcomponent_class = LS.Components[ component_class ];\\r\\n\\t\\tif(!component_class)\\r\\n\\t\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//search components\\r\\n\\tfor(var i = 0, l = this._components.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tif( this._components[i].constructor === component_class )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(index !== undefined && index > 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tindex--;\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn this._components[i];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the component with the given uid\\r\\n* @method getComponentByUId\\r\\n* @param {string} uid the uid to search \\r\\n*/\\r\\nComponentContainer.prototype.getComponentByUId = function(uid)\\r\\n{\\r\\n\\tif(!this._components)\\r\\n\\t\\treturn null;\\r\\n\\tfor(var i = 0, l = this._components.length; i < l; ++i)\\r\\n\\t\\tif( this._components[i].uid == uid )\\r\\n\\t\\t\\treturn this._components[i];\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the position in the components array of this component\\r\\n* @method getIndexOfComponent\\r\\n* @param {Number} position in the array, -1 if not found\\r\\n*/\\r\\nComponentContainer.prototype.getIndexOfComponent = function(component)\\r\\n{\\r\\n\\tif(!this._components)\\r\\n\\t\\treturn -1;\\r\\n\\treturn this._components.indexOf( component );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the component at index position\\r\\n* @method getComponentByIndex\\r\\n* @param {Object} component\\r\\n*/\\r\\nComponentContainer.prototype.getComponentByIndex = function(index)\\r\\n{\\r\\n\\tif(!this._components)\\r\\n\\t\\treturn null;\\r\\n\\treturn this._components[index];\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a list of components matching the search, it search in the node and child nodes\\r\\n* @method findComponent\\r\\n* @param {Class|String} component the component class or the class name\\r\\n* @return {Array} an array with all the components of the same class\\r\\n*/\\r\\nComponentContainer.prototype.findComponents = function( comp_name, out )\\r\\n{\\r\\n\\tout = out || [];\\r\\n\\tif(!comp_name)\\r\\n\\t\\treturn out;\\r\\n\\tif( comp_name.constructor === String )\\r\\n\\t\\tcomp_name = LS.Components[ comp_name ];\\r\\n\\tif(!comp_name)\\r\\n\\t\\treturn out;\\r\\n\\r\\n\\tfor(var i = 0; i < this._components.length; ++i )\\r\\n\\t{\\r\\n\\t\\tvar comp = this._components[i];\\r\\n\\t\\tif( comp && comp.constructor === comp_name )\\r\\n\\t\\t\\tout.push( comp );\\r\\n\\t}\\r\\n\\r\\n\\tif(this._children)\\r\\n\\t\\tfor(var i = 0; i < this._children.length; ++i )\\r\\n\\t\\t\\tthis._children[i].findComponents( comp_name, out );\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Changes the order of a component\\r\\n* @method setComponentIndex\\r\\n* @param {Object} component\\r\\n*/\\r\\nComponentContainer.prototype.setComponentIndex = function( component, index )\\r\\n{\\r\\n\\tif(!this._components)\\r\\n\\t\\treturn null;\\r\\n\\tif(index < 0)\\r\\n\\t\\tindex = 0;\\r\\n\\tvar old_index = this._components.indexOf( component );\\r\\n\\tif (old_index == -1)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._components.splice( old_index, 1 );\\r\\n\\r\\n\\t/*\\r\\n\\tif(index >= old_index)\\r\\n\\t\\tindex--; \\r\\n\\t*/\\r\\n\\tif(index >= this._components.length)\\r\\n\\t\\tthis._components.push( component );\\r\\n\\telse\\r\\n\\t\\tthis._components.splice( index, 0, component );\\r\\n\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Ensures this node has a component of the specified class, if not it creates one and attaches it\\r\\n* @method requireComponent\\r\\n* @param {Object|String} component_class the class to search a component from (could be the class or the name)\\r\\n* @param {Object} data [optional] the object to configure the component from\\r\\n* @return {Component} the component found or created\\r\\n*/\\r\\nComponentContainer.prototype.requireComponent = function( component_class, data )\\r\\n{\\r\\n\\tif(!component_class)\\r\\n\\t\\tthrow(\\\"no component class specified\\\");\\r\\n\\r\\n\\t//convert string to class\\r\\n\\tif( component_class.constructor === String )\\r\\n\\t{\\r\\n\\t\\tcomponent_class = LS.Components[ component_class ];\\r\\n\\t\\tif(!component_class)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"component class not found:\\\", arguments[0] );\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//search component\\r\\n\\tvar l = this._components.length;\\r\\n\\tfor(var i = 0; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tif( this._components[i].constructor === component_class )\\r\\n\\t\\t\\treturn this._components[i];\\r\\n\\t}\\r\\n\\r\\n\\tvar compo = new component_class();\\r\\n\\tthis.addComponent(compo, l ); //insert before the latest scripts, to avoid situations where when partially parsed the components the component is attached but not parsed yet\\r\\n\\tif(data)\\r\\n\\t\\tcompo.configure(data);\\r\\n\\treturn compo;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Ensures this node has a ScriptFromFile component of the specified script url, if not it creates one and attaches it\\r\\n* @method requireScript\\r\\n* @param {String} url the url to the script\\r\\n* @return {Component} the ScriptFromFile component found or created\\r\\n*/\\r\\nComponentContainer.prototype.requireScript = function( url )\\r\\n{\\r\\n\\tif(!url)\\r\\n\\t\\tthrow(\\\"no url specified\\\");\\r\\n\\r\\n\\tvar component_class = LS.Components.ScriptFromFile;\\r\\n\\turl = LS.ResourcesManager.cleanFullpath( url ); //remove double slashes or spaces\\r\\n\\r\\n\\t//search component\\r\\n\\tvar l = this._components.length;\\r\\n\\tfor(var i = 0; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar comp = this._components[i];\\r\\n\\t\\tif( comp.constructor === component_class && comp._filename == url )\\r\\n\\t\\t\\treturn comp;\\r\\n\\t}\\r\\n\\r\\n\\tvar compo = new component_class();\\r\\n\\tcompo.filename = url;\\r\\n\\tthis.addComponent( compo, l );\\r\\n\\treturn compo;\\r\\n}\\r\\n\\r\\n/**\\r\\n* executes the method with a given name in all the components\\r\\n* @method processActionInComponents\\r\\n* @param {String} method_name the name of the function to execute in all components (in string format)\\r\\n* @param {Array} params array with every parameter that the function may need\\r\\n* @param {Boolean} skip_scripts [optional] skip scripts\\r\\n*/\\r\\nComponentContainer.prototype.processActionInComponents = function( method_name, params, skip_scripts )\\r\\n{\\r\\n\\tif(this._components && this._components.length)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0, l = this._components.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar comp = this._components[i];\\r\\n\\t\\t\\tif( comp[method_name] && comp[method_name].constructor === Function )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(!params || params.constructor !== Array)\\r\\n\\t\\t\\t\\t\\tcomp[method_name].call(comp, params);\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tcomp[method_name].apply(comp, params);\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(skip_scripts)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(comp._script)\\r\\n\\t\\t\\t\\tcomp._script.callMethod( method_name, params, true );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* executes the method with a given name in all the components and its children\\r\\n* @method broadcastMessage\\r\\n* @param {String} method_name the name of the function to execute in all components (in string format)\\r\\n* @param {Array} params array with every parameter that the function may need\\r\\n*/\\r\\nComponentContainer.prototype.broadcastMessage = function( method_name, params )\\r\\n{\\r\\n\\tthis.processActionInComponents( method_name, params );\\r\\n\\r\\n\\tif(this._children && this._children.length )\\r\\n\\t\\tfor(var i = 0, l = this._children.length; i < l; ++i)\\r\\n\\t\\t\\tthis._children[i].broadcastMessage( method_name, params );\\r\\n}\\r\\n\\r\\n\\r\\n///@FILE:../src/compositePattern.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* CompositePattern implements the Composite Pattern, which allows to one class to contain instances of its own class\\r\\n* creating a tree-like structure.\\r\\n* @class CompositePattern\\r\\n* @constructor\\r\\n*/\\r\\nfunction CompositePattern()\\r\\n{\\r\\n\\t//WARNING! do not add anything here, it will never be called\\r\\n}\\r\\n\\r\\nCompositePattern.prototype.compositeCtor = function()\\r\\n{\\r\\n\\t//this method is not called by SceneNode \\r\\n\\tthis._parentNode = null;\\r\\n\\tthis._children = null;\\r\\n\\tthis._in_tree = null;\\r\\n}\\r\\n\\r\\n/* use .scene instead\\r\\nCompositePattern.prototype.getScene = function()\\r\\n{\\r\\n\\tthis._in_tree;\\r\\n}\\r\\n*/\\r\\n\\r\\n/**\\r\\n* Adds one child to this instance\\r\\n* @method addChild\\r\\n* @param {*} child\\r\\n* @param {number} index [optional] in which position you want to insert it, if not specified it goes to the last position\\r\\n* @param {*} options [optional] data to be passed when adding it, used for special cases when moving nodes around\\r\\n**/\\r\\n\\r\\nCompositePattern.prototype.addChild = function(node, index, options)\\r\\n{\\r\\n\\tif( !node )\\r\\n\\t\\tthrow(\\\"cannot addChild of null\\\");\\r\\n\\r\\n\\tif( node.constructor !== this.constructor )\\r\\n\\t\\tthrow(\\\"added child must be of the same type\\\");\\r\\n\\r\\n\\t//be careful with weird recursions...\\r\\n\\tvar aux = this;\\r\\n\\twhile( aux._parentNode )\\r\\n\\t{\\r\\n\\t\\tif(aux == node)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"addChild: Cannot insert a node as his own child\\\");\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t}\\r\\n\\t\\taux = aux._parentNode;\\r\\n\\t}\\r\\n\\r\\n\\t//siblings\\r\\n\\tif( node._parentNode && node._parentNode == this._parentNode && index !== undefined)\\r\\n\\t{\\r\\n\\t\\tvar prev_index = this._parentNode._children.indexOf( node );\\r\\n\\t\\tif(prev_index < index) //in case it was in the position before\\r\\n\\t\\t\\tindex--;\\r\\n\\t}\\r\\n\\r\\n\\t//has a parent\\r\\n\\tif(node._parentNode)\\r\\n\\t\\tnode._parentNode.removeChild(node, options);\\r\\n\\r\\n\\t/*\\r\\n\\tvar moved = false;\\r\\n\\tif(node._parentNode)\\r\\n\\t{\\r\\n\\t\\tmoved = true;\\r\\n\\t\\tnode._onChangeParent(this, options);\\r\\n\\t\\t//remove from parent children\\r\\n\\t\\tvar pos = node._parentNode._children.indexOf(node);\\r\\n\\t\\tif(pos != -1)\\r\\n\\t\\t\\tnode._parentNode._children.splice(pos,1);\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\t//attach to this\\r\\n\\tnode._parentNode = this;\\r\\n\\tif( !this._children )\\r\\n\\t\\tthis._children = [node];\\r\\n\\telse if(index == undefined)\\r\\n\\t\\tthis._children.push(node);\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tthis._children.splice(index,0,node);\\r\\n\\t}\\r\\n\\r\\n\\t//the same as scene but we called tree to make it more generic\\r\\n\\tvar tree = this._in_tree;\\r\\n\\r\\n\\t//this would never fire but just in case\\r\\n\\tif(tree && node._in_tree && node._in_tree != tree)\\r\\n\\t\\tthrow(\\\"Cannot add a node that belongs to another scene tree\\\");\\r\\n\\r\\n\\t//Same tree\\r\\n\\tnode._in_tree = tree;\\r\\n\\r\\n\\t//overwritten from SceneNode\\r\\n\\tif(this._onChildAdded)\\r\\n\\t\\tthis._onChildAdded(node, options);\\r\\n\\r\\n\\tLEvent.trigger(this,\\\"childAdded\\\", node);\\r\\n\\tif(tree)\\r\\n\\t{\\r\\n\\t\\t//added to scene tree\\r\\n\\t\\tLEvent.trigger(tree, \\\"treeItemAdded\\\", node);\\r\\n\\t\\tinner_recursive(node);\\r\\n\\t}\\r\\n\\r\\n\\t//recursive action\\r\\n\\tfunction inner_recursive(item)\\r\\n\\t{\\r\\n\\t\\tif(!item._children) return;\\r\\n\\t\\tfor(var i in item._children)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar child = item._children[i];\\r\\n\\t\\t\\tif(!child._in_tree)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//added to scene tree\\r\\n\\t\\t\\t\\tLEvent.trigger( tree, \\\"treeItemAdded\\\", child );\\r\\n\\t\\t\\t\\tchild._in_tree = tree;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tinner_recursive( child );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Removes the node from its parent (and from the scene tree)\\r\\n*\\r\\n* @method removeChild\\r\\n* @param {Node} node this child to remove\\r\\n* @param {*} param1 data passed to onChildRemoved\\r\\n* @param {*} param2 data passed to onChildRemoved as second parameter\\r\\n* @return {Boolean} returns true if it was found and removed\\r\\n*/\\r\\nCompositePattern.prototype.removeChild = function(node, param1, param2)\\r\\n{\\r\\n\\tif(!node)\\r\\n\\t\\tthrow(\\\"cannot removeChild of null\\\");\\r\\n\\r\\n\\tif(!this._children || node._parentNode != this)\\r\\n\\t\\treturn false;\\r\\n\\tif( node._parentNode != this)\\r\\n\\t\\treturn false; //not his son\\r\\n\\tvar pos = this._children.indexOf(node);\\r\\n\\tif(pos == -1)\\r\\n\\t\\treturn false; //not his son �?\\r\\n\\tthis._children.splice(pos,1);\\r\\n\\r\\n\\tif(this._onChildRemoved)\\r\\n\\t\\tthis._onChildRemoved(node, param1, param2);\\r\\n\\r\\n\\tLEvent.trigger(this,\\\"childRemoved\\\", node);\\r\\n\\r\\n\\tif(node._in_tree)\\r\\n\\t{\\r\\n\\t\\tLEvent.trigger(node._in_tree, \\\"treeItemRemoved\\\", node);\\r\\n\\t\\t//propagate to childs\\r\\n\\t\\tinner_recursive(node);\\r\\n\\t}\\r\\n\\tnode._in_tree = null;\\r\\n\\r\\n\\t//recursive action to remove tree\\r\\n\\tfunction inner_recursive(item)\\r\\n\\t{\\r\\n\\t\\tif(!item._children)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tfor(var i = 0, l = item._children.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar child = item._children[i];\\r\\n\\t\\t\\tif(child._in_tree)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tLEvent.trigger( child._in_tree, \\\"treeItemRemoved\\\", child );\\r\\n\\t\\t\\t\\tchild._in_tree = null;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tinner_recursive( child );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Remove every child node\\r\\n*\\r\\n* @method removeAllChildren\\r\\n*/\\r\\nCompositePattern.prototype.removeAllChildren = function( param1, param2 )\\r\\n{\\r\\n\\tif(this._children)\\r\\n\\t\\twhile( this._children.length )\\r\\n\\t\\t\\tthis.removeChild( this._children[0], param1, param2 );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Serialize the data from all the children\\r\\n*\\r\\n* @method serializeChildren\\r\\n* @return {Array} array containing all serialized data from every children\\r\\n*/\\r\\nCompositePattern.prototype.serializeChildren = function( simplified )\\r\\n{\\r\\n\\tvar r = [];\\r\\n\\tif(this._children)\\r\\n\\t\\tfor(var i in this._children)\\r\\n\\t\\t\\tr.push( this._children[i].serialize( false, simplified ) ); //serialize calls serializeChildren\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Configure every children with the data\\r\\n*\\r\\n* @method configureChildren\\r\\n* @return {Array} o array containing all serialized data \\r\\n*/\\r\\nCompositePattern.prototype.configureChildren = function(o)\\r\\n{\\r\\n\\tif(!o.children)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < o.children.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar c = o.children[i];\\r\\n\\r\\n\\t\\t//create instance\\r\\n\\t\\tvar node = new this.constructor(c.id); //id is hardcoded...\\r\\n\\t\\t//we do this before because otherwise the event fired by addChild wont have all the info which is crucial in some cases in the editor\\r\\n\\t\\tif(c.uid) \\r\\n\\t\\t\\tnode.uid = c.uid;\\r\\n\\t\\tif(c.editor) \\r\\n\\t\\t\\tnode._editor = c.editor;\\r\\n\\t\\t//add before configure, so every child has a scene tree\\r\\n\\t\\tthis.addChild(node);\\r\\n\\t\\t//we configure afterwards otherwise children wouldnt have a scene tree to bind anything\\r\\n\\t\\tnode.configure(c);\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns parent node\\r\\n*\\r\\n* @method getParent\\r\\n* @return {SceneNode} parent node\\r\\n*/\\r\\nCompositePattern.prototype.getParent = function()\\r\\n{\\r\\n\\treturn this._parentNode;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns a list with all direct children (if you want below that use getDescendants)\\r\\n* @method getChildren\\r\\n* @param {Array} Original array containing the children\\r\\n**/\\r\\nCompositePattern.prototype.getChildren = function()\\r\\n{\\r\\n\\treturn this._children || [];\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the index of a child in the children array\\r\\n* @method getChildIndex\\r\\n* @param {SceneNode} child the child to search for\\r\\n* @return {number} the index of this child in the array, if it is not inside returns -1\\r\\n**/\\r\\nCompositePattern.prototype.getChildIndex = function( child )\\r\\n{\\r\\n\\treturn this._children ? this._children.indexOf( child ) : -1;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the child in the index position\\r\\n* @method getChildByIndex\\r\\n* @param {number} index the index in the array \\r\\n* @return {SceneNode} the child in that position\\r\\n**/\\r\\nCompositePattern.prototype.getChildByIndex = function( index )\\r\\n{\\r\\n\\treturn this._children && this._children.length > index ? this._children[ index ] : null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the child that matches that name\\r\\n* @method getChildByName\\r\\n* @param {String} name\\r\\n* @return {SceneNode} the child with that name otherwise returns null;\\r\\n**/\\r\\nCompositePattern.prototype.getChildByName = function( name )\\r\\n{\\r\\n\\tif(!this._children)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tfor(var i = 0; i < this._children.length; ++i)\\r\\n\\t\\tif(this._children[i].name == name )\\r\\n\\t\\t\\treturn this._children[i];\\r\\n\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the path name of the node (a path name is a concatenation of the name of the nodes an its ancestors: \\\"root|parent|child\\\"\\r\\n* @method getPathName\\r\\n* @return {String} the pathname\\r\\n**/\\r\\nCompositePattern.prototype.getPathName = function()\\r\\n{\\r\\n\\tif(!this._in_tree)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tif(this === this._in_tree.root )\\r\\n\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\tvar path = this.name;\\r\\n\\tvar parent = this._parentNode;\\r\\n\\twhile(parent)\\r\\n\\t{\\r\\n\\t\\tif(parent === this._in_tree.root )\\r\\n\\t\\t\\treturn path;\\r\\n\\t\\tpath = parent.name + \\\"|\\\" + path;\\r\\n\\t\\tparent = parent._parentNode;\\r\\n\\t}\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n//DOM style\\r\\nObject.defineProperty( CompositePattern.prototype, \\\"childNodes\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._children || [];\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\t//TODO\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( CompositePattern.prototype, \\\"parentNode\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._parentNode;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\t//TODO\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( CompositePattern.prototype, \\\"scene\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._in_tree;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"Scene cannot be set, you must use addChild in parent\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\n\\r\\n/**\\r\\n* get all nodes above this in his hierarchy (parent, parent of parent, ...)\\r\\n*\\r\\n* @method getAncestors\\r\\n* @param {Boolean} include_itself if it must include first itself\\r\\n* @return {Array} array containing all descendants\\r\\n*/\\r\\nCompositePattern.prototype.getAncestors = function( include_itself )\\r\\n{\\r\\n\\tvar r = [];\\r\\n\\tvar aux = this._parentNode;\\r\\n\\tif(include_itself)\\r\\n\\t\\tr.push(this);\\r\\n\\twhile( aux )\\r\\n\\t{\\r\\n\\t\\tr.push(aux);\\r\\n\\t\\taux = aux._parentNode;\\r\\n\\t}\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\n/**\\r\\n* get all nodes below this in the hierarchy (children and children of children)\\r\\n*\\r\\n* @method getDescendants\\r\\n* @return {Array} array containing all descendants\\r\\n*/\\r\\nCompositePattern.prototype.getDescendants = function()\\r\\n{\\r\\n\\tif(!this._children || this._children.length == 0)\\r\\n\\t\\treturn [];\\r\\n\\tvar r = this._children.concat();\\r\\n\\tfor(var i = 0; i < this._children.length; ++i)\\r\\n\\t\\tr = r.concat( this._children[i].getDescendants() );\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Swaps the index in the children array so it is before \\r\\n* @method moveBefore\\r\\n* @param {SceneNode} sibling [optional] allows to put before given node, otherwise it will be moved one position before of current position\\r\\n* @return {number} new index\\r\\n**/\\r\\nCompositePattern.prototype.moveBefore = function( sibling )\\r\\n{\\r\\n\\tif(!this._parentNode || (sibling && this._parentNode !== sibling._parentNode) )\\r\\n\\t\\treturn -1;\\r\\n\\r\\n\\tvar parent_children = this._parentNode._children;\\r\\n\\tvar index = parent_children.indexOf( this );\\r\\n\\tif(index == -1)\\r\\n\\t\\tthrow(\\\"moveBefore node not found in parent, this is impossible\\\");\\r\\n\\r\\n\\tvar new_index = index - 1;\\r\\n\\tif(sibling)\\r\\n\\t{\\r\\n\\t\\tnew_index = parent_children.indexOf( sibling );\\r\\n\\t\\tif(new_index == -1)\\r\\n\\t\\t\\treturn -1;\\r\\n\\t\\tnew_index = new_index - 1; //before\\r\\n\\t}\\r\\n\\r\\n\\tif(index == new_index || new_index < 0)\\r\\n\\t\\treturn new_index; //nothing to do\\r\\n\\r\\n\\tparent_children.splice( index, 1 ); //remove\\r\\n\\tif(new_index > index) //sibling is after\\r\\n\\t\\tnew_index -= 1;\\r\\n\\tparent_children.splice( new_index, 0, this); //insert\\r\\n\\tLEvent.trigger(this._in_tree,\\\"node_rearranged\\\", this );\\r\\n\\treturn new_index;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Swaps the index in the children array so it is before \\r\\n* @method moveAfter\\r\\n* @param {SceneNode} sibling [optional] allows to put after given node, otherwise it will be moved one position after current position\\r\\n* @return {number} new index\\r\\n**/\\r\\nCompositePattern.prototype.moveAfter = function( sibling )\\r\\n{\\r\\n\\tif(!this._parentNode || (sibling && this._parentNode !== sibling._parentNode) )\\r\\n\\t\\treturn -1;\\r\\n\\r\\n\\tvar parent_children = this._parentNode._children;\\r\\n\\tvar index = parent_children.indexOf( this );\\r\\n\\tif(index == -1)\\r\\n\\t\\tthrow(\\\"moveBefore node not found in parent, this is impossible\\\");\\r\\n\\r\\n\\tvar new_index = index + 1;\\r\\n\\tif(sibling)\\r\\n\\t{\\r\\n\\t\\tnew_index = parent_children.indexOf( sibling );\\r\\n\\t\\tif(new_index == -1)\\r\\n\\t\\t\\treturn -1;\\r\\n\\t\\tnew_index = new_index + 1; //before\\r\\n\\t}\\r\\n\\r\\n\\tif( index == new_index || new_index >= parent_children.length )\\r\\n\\t\\treturn new_index; //nothing to do\\r\\n\\r\\n\\tparent_children.splice( index, 1 ); //remove\\r\\n\\tif(new_index > index) //sibling is after\\r\\n\\t\\tnew_index -= 1;\\r\\n\\tparent_children.splice( new_index, 0, this); //insert\\r\\n\\tLEvent.trigger(this._in_tree,\\\"node_rearranged\\\", this );\\r\\n\\treturn new_index;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Search for a node using a string that could be a name, a fullname or a uid\\r\\n* @method findNode\\r\\n* @param {String} name_or_uid\\r\\n* @return {SceneNode} the node or null\\r\\n**/\\r\\nCompositePattern.prototype.findNode = function( name_or_uid )\\r\\n{\\r\\n\\tif(name_or_uid == \\\"\\\")\\r\\n\\t\\treturn this;\\r\\n\\tif(!name_or_uid)\\r\\n\\t\\treturn null;\\r\\n\\tif(name_or_uid.charAt(0) != LS._uid_prefix)\\r\\n\\t\\treturn this.findNodeByName( name_or_uid );\\r\\n\\treturn this.findNodeByUId( name_or_uid );\\r\\n}\\r\\n\\r\\n/**\\r\\n* search a node by its name\\r\\n* this function gets called a lot when using animations\\r\\n* @method findNodeByName\\r\\n* @param {String} name\\r\\n* @return {SceneNode} the node or null\\r\\n**/\\r\\nCompositePattern.prototype.findNodeByName = function( name )\\r\\n{\\r\\n\\tif(!name)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tif(this.name == name)\\r\\n\\t\\treturn this;\\r\\n\\r\\n\\tvar children = this._children;\\r\\n\\r\\n\\tif(children)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0, l = children.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar node = children[i];\\r\\n\\t\\t\\tif( node.name == name )\\r\\n\\t\\t\\t\\treturn node;\\r\\n\\t\\t\\tif(node._children)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar r = node.findNodeByName( name );\\r\\n\\t\\t\\t\\tif(r)\\r\\n\\t\\t\\t\\t\\treturn r;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* search a node by its uid\\r\\n* @method findNodeByUId\\r\\n* @param {String} id\\r\\n* @return {SceneNode} the node or null\\r\\n**/\\r\\nCompositePattern.prototype.findNodeByUId = function( uid )\\r\\n{\\r\\n\\tif(!uid)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tif(this.uid == uid)\\r\\n\\t\\treturn this;\\r\\n\\r\\n\\tvar children = this._children;\\r\\n\\r\\n\\tif(children)\\r\\n\\t\\tfor(var i = 0; i < children.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar node = children[i];\\r\\n\\t\\t\\tif( node.uid == uid )\\r\\n\\t\\t\\t\\treturn node;\\r\\n\\t\\t\\tif(node._children)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar r = node.findNodeByUId(uid);\\r\\n\\t\\t\\t\\tif(r)\\r\\n\\t\\t\\t\\t\\treturn r;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns how many levels deep is the node in the hierarchy\\r\\n* @method getHierarchyLevel\\r\\n* @return {Number} the level, 0 if it is the root\\r\\n**/\\r\\nCompositePattern.prototype.getHierarchyLevel = function()\\r\\n{\\r\\n\\tif(!this._parentNode)\\r\\n\\t\\treturn 0;\\r\\n\\treturn this._parentNode.getHierarchyLevel() + 1;\\r\\n}\\r\\n\\r\\n///@FILE:../src/baseComponent.js\\r\\n///@INFO: BASE\\r\\n/*\\r\\n* Components are elements that attach to Nodes or other objects to add functionality\\r\\n* Some important components are Transform, Light or Camera\\r\\n*\\r\\n*\\t* ctor: must accept an optional parameter with the serialized data\\r\\n*\\t* onAddedToNode: triggered when added to node\\r\\n*\\t* onRemovedFromNode: triggered when removed from node\\r\\n*\\t* onAddedToScene: triggered when the node is added to the scene\\r\\n*\\t* onRemovedFromScene: triggered when the node is removed from the scene\\r\\n*\\t* serialize: returns a serialized version packed in an object\\r\\n*\\t* configure: recieves an object to unserialize and configure this instance\\r\\n*\\t* getResources: adds to the object the resources to load\\r\\n*\\t* _root contains the node where the component is added\\r\\n*\\r\\n*\\t* use the LEvent system to hook events to the node or the scene\\r\\n*\\t* never share the same component instance between two nodes\\r\\n*\\r\\n*/\\r\\n\\r\\n/**\\r\\n* This is an example class for a component, should never be instantiated by itself, \\r\\n* instead components get all the methods from this class attached when the component is registered.\\r\\n* Components can overwrite this methods if they want.\\r\\n*\\r\\n* @class BaseComponent\\r\\n* @namespace LS\\r\\n*/\\r\\nfunction BaseComponent(o)\\r\\n{\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the node where this components is attached\\r\\n* @method getRootNode\\r\\n**/\\r\\nBaseComponent.prototype.getRootNode = function()\\r\\n{\\r\\n\\treturn this._root;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Configures the components based on an object that contains the serialized info\\r\\n* @method configure\\r\\n* @param {Object} o object with the serialized info\\r\\n**/\\r\\nBaseComponent.prototype.configure = function(o)\\r\\n{ \\r\\n\\tif( !o )\\r\\n\\t\\treturn;\\r\\n\\tif( o.uid ) \\r\\n\\t\\tthis.uid = o.uid;\\r\\n\\tLS.cloneObject( o, this, false, true ); \\r\\n\\r\\n\\tif( this.onConfigure )\\r\\n\\t\\tthis.onConfigure( o );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns an object with all the info about this component in an object form\\r\\n* @method serialize\\r\\n* @return {Object} object with the serialized info\\r\\n**/\\r\\nBaseComponent.prototype.serialize = function()\\r\\n{\\r\\n\\tvar o = LS.cloneObject(this,null,false,false,true);\\r\\n\\tif(this.uid) //special case, not enumerable\\r\\n\\t\\to.uid = this.uid;\\r\\n\\tif(!o.object_class)\\r\\n\\t\\to.object_class = LS.getObjectClassName( this );\\r\\n\\r\\n\\tif( this.onSerialize )\\r\\n\\t\\tthis.onSerialize( o );\\r\\n\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Create a clone of this node (the UID is removed to avoid collisions)\\r\\n* @method clone\\r\\n* @return {*} component clone\\r\\n**/\\r\\nBaseComponent.prototype.clone = function()\\r\\n{\\r\\n\\tvar data = this.serialize();\\r\\n\\tdata.uid = null; //remove id when cloning\\r\\n\\tvar new_component = new this.constructor( data );\\r\\n\\treturn new_component;\\r\\n}\\r\\n\\r\\n/**\\r\\n* To create a new property for this component adding some extra useful info to help the editor\\r\\n* @method createProperty\\r\\n* @param {String} name the name of the property as it will be accessed\\r\\n* @param {*} value the value to assign by default to this property\\r\\n* @param {String|Object} type [optional] an string identifying the type of the variable, could be \\\"number\\\",\\\"string\\\",\\\"Texture\\\",\\\"vec3\\\",\\\"mat4\\\", or an object with all the info\\r\\n* @param {Function} setter [optional] setter function, otherwise one will be created\\r\\n* @param {Function} getter [optional] getter function, otherwise one will be created\\r\\n**/\\r\\nBaseComponent.prototype.createProperty = function( name, value, type, setter, getter )\\r\\n{\\r\\n\\tif(this[name] !== undefined)\\r\\n\\t\\treturn; //console.warn(\\\"createProperty: this component already has a property called \\\" + name );\\r\\n\\r\\n\\t//if we have type info, we must store it in the constructor, useful for GUIs\\r\\n\\tif(type)\\r\\n\\t{\\r\\n\\t\\t//control errors\\r\\n\\t\\tif(type == \\\"String\\\" || type == \\\"Number\\\" || type == \\\"Boolean\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"createProperty: Basic types must be in lowercase -> \\\" + type );\\r\\n\\t\\t\\ttype = type.toLowerCase();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( typeof(type) == \\\"object\\\" )\\r\\n\\t\\t\\tthis.constructor[ \\\"@\\\" + name ] = type;\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.constructor[ \\\"@\\\" + name ] = { type: type };\\r\\n\\r\\n\\t\\t//is a component\\r\\n\\t\\tif( type == LS.TYPES.COMPONENT || LS.Components[ type ] || type.constructor.is_component || type.type == LS.TYPES.COMPONENT )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar property_root = this; //with proto is problematic, because the getters cannot do this.set (this is the proto, not the component)\\r\\n\\t\\t\\tvar private_name = \\\"_\\\" + name;\\r\\n\\t\\t\\tObject.defineProperty( property_root, name, {\\r\\n\\t\\t\\t\\tget: function() { \\r\\n\\t\\t\\t\\t\\tif( !this[ private_name ] )\\r\\n\\t\\t\\t\\t\\t\\treturn null;\\r\\n\\t\\t\\t\\t\\tvar scene = this._root && this._root.scene ? this._root._in_tree : LS.GlobalScene;\\r\\n\\t\\t\\t\\t\\treturn LSQ.get( this[ private_name ], null, scene );\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\tset: function(v) { \\r\\n\\t\\t\\t\\t\\tif(!v)\\r\\n\\t\\t\\t\\t\\t\\tthis[ private_name ] = v;\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\tthis[ private_name ] = v.constructor === String ? v : v.uid;\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\tenumerable: true\\r\\n\\t\\t\\t\\t//writable: false //cannot be set to true if setter/getter\\r\\n\\t\\t\\t});\\r\\n\\r\\n\\t\\t\\tif( LS.Components[ type ] || type.constructor.is_component ) //passing component class name or component class constructor\\r\\n\\t\\t\\t\\ttype = { type: LS.TYPES.COMPONENT, component_class: type.constructor === String ? type : LS.getClassName( type ) };\\r\\n\\r\\n\\t\\t\\tif( typeof(type) == \\\"object\\\" )\\r\\n\\t\\t\\t\\tthis.constructor[ \\\"@\\\" + name ] = type;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis.constructor[ \\\"@\\\" + name ] = { type: type };\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//basic type\\r\\n\\tif( (value === null || value === undefined || value.constructor === Number || value.constructor === String || value.constructor === Boolean) && !setter && !getter )\\r\\n\\t{\\r\\n\\t\\tthis[ name ] = value;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar private_name = \\\"_\\\" + name;\\r\\n\\r\\n\\tif( Object.hasOwnProperty( this, private_name ) )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\r\\n\\tvar property_root = this; //with proto is problematic, because the getters cannot do this.set (this is the proto, not the component)\\r\\n\\r\\n\\t//vector type has special type with setters and getters to avoid replacing the container during assignations\\r\\n\\tif(value && value.constructor === Float32Array)\\r\\n\\t{\\r\\n\\t\\tvalue = new Float32Array( value ); //clone\\r\\n\\t\\tthis[ private_name ] = value; //this could be removed...\\r\\n\\r\\n\\t\\t//create setter\\r\\n\\t\\tObject.defineProperty( property_root, name, {\\r\\n\\t\\t\\tget: getter || function() { return value; },\\r\\n\\t\\t\\tset: setter || function(v) { value.set( v ); },\\r\\n\\t\\t\\tenumerable: true\\r\\n\\t\\t\\t//writable: false //cannot be set to true if setter/getter\\r\\n\\t\\t});\\r\\n\\t}\\r\\n\\telse //this is for vars that has their own setter/getter\\r\\n\\t{\\r\\n\\t\\t//define private (writable because it can be overwriten with different values)\\r\\n\\t\\tObject.defineProperty( property_root, private_name, { \\r\\n\\t\\t\\tvalue: value, \\r\\n\\t\\t\\tenumerable: false,\\r\\n\\t\\t\\twritable: true \\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tvar that = this;\\r\\n\\r\\n\\t\\t//define public\\r\\n\\t\\tObject.defineProperty( property_root, name, {\\r\\n\\t\\t\\tget: getter || function() { \\r\\n\\t\\t\\t\\treturn this[ private_name ];\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\tset: setter || function(v) { \\r\\n\\t\\t\\t\\tthis[ private_name ] = v;\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\tenumerable: true\\r\\n\\t\\t\\t//writable: false //cannot be set to true if setter/getter\\r\\n\\t\\t});\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//not finished\\r\\nBaseComponent.prototype.createAction = function( name, callback, options )\\r\\n{\\r\\n\\tif(!callback)\\r\\n\\t\\tconsole.error(\\\"action '\\\" + name + \\\"' with no callback associated. Remember to create the action after the callback is defined.\\\");\\r\\n\\tvar safe_name = name.replace(/ /gi,\\\"_\\\"); //replace spaces\\r\\n\\tthis[ safe_name ] = callback;\\r\\n\\tthis.constructor[\\\"@\\\" + safe_name ] = options || { type: \\\"function\\\", button_text: name, widget:\\\"button\\\", callback: callback };\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns the locator string of this component\\r\\n* @method getLocator\\r\\n* @param {string} property_name [optional] you can pass the name of a property in this component\\r\\n* @return {String} the locator string of this component\\r\\n**/\\r\\nBaseComponent.prototype.getLocator = function( property_name )\\r\\n{\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn \\\"\\\";\\r\\n\\tif(property_name)\\r\\n\\t{\\r\\n\\t\\tif(this[ property_name ] === undefined )\\r\\n\\t\\t\\tconsole.warn(\\\"No property found in this component with that name:\\\",property_name);\\r\\n\\t\\treturn this._root.uid + \\\"/\\\" + this.uid + \\\"/\\\" + property_name;\\r\\n\\t}\\r\\n\\treturn this._root.uid + \\\"/\\\" + this.uid;\\r\\n}\\r\\n\\r\\nBaseComponent.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif( !path.length )\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar v;\\r\\n\\tvar varname = path[0];\\r\\n\\r\\n\\t//to know the value of a property of the given target\\r\\n\\tif( this.getPropertyValue )\\r\\n\\t\\tv = this.getPropertyValue( varname );\\r\\n\\r\\n\\t//special case when the component doesnt specify any locator info but the property referenced does\\r\\n\\t//used in TextureFX\\r\\n\\tif (v === undefined && path.length > 1 && this[ varname ] && this[ varname ].getPropertyInfoFromPath )\\r\\n\\t{\\r\\n\\t\\tvar r = this[ varname ].getPropertyInfoFromPath( path.slice(1) );\\r\\n\\t\\tif(r)\\r\\n\\t\\t{\\r\\n\\t\\t\\tr.node = this.root;\\r\\n\\t\\t\\treturn r;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif( v === undefined && Object.hasOwnProperty( this, varname ) )//this[ varname ] === undefined )\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\t//if we dont have a value yet then take it directly from the object\\r\\n\\tvar value = v !== undefined ? v : this[ varname ];\\r\\n\\r\\n\\tvar extra_info = this.constructor[ \\\"@\\\" + varname ];\\r\\n\\tvar type = \\\"\\\";\\r\\n\\tif(extra_info)\\r\\n\\t\\ttype = extra_info.type;\\r\\n\\tif(!type && value !== null && value !== undefined)\\r\\n\\t{\\r\\n\\t\\tif(value.constructor === String)\\r\\n\\t\\t\\ttype = \\\"string\\\";\\r\\n\\t\\telse if(value.constructor === Boolean)\\r\\n\\t\\t\\ttype = \\\"boolean\\\";\\r\\n\\t\\telse if(value.length)\\r\\n\\t\\t\\ttype = \\\"vec\\\" + value.length;\\r\\n\\t\\telse if(value.constructor === Number)\\r\\n\\t\\t\\ttype = \\\"number\\\";\\r\\n\\t}\\r\\n\\r\\n\\treturn {\\r\\n\\t\\tnode: this.root,\\r\\n\\t\\ttarget: this,\\r\\n\\t\\tname: varname,\\r\\n\\t\\tvalue: value,\\r\\n\\t\\ttype: type\\r\\n\\t};\\t\\r\\n}\\r\\n\\r\\n/**\\r\\n* calls a method in all components in this node and all the children nodes\\r\\n* @method broadcastMessage\\r\\n* @param {String} method_name \\r\\n* @param {*} data\\r\\n**/\\r\\nBaseComponent.prototype.broadcastMessage = function( method_name, data )\\r\\n{\\r\\n\\tvar node = this._root;\\r\\n\\tif(!node)\\r\\n\\t\\treturn;\\r\\n\\tnode.broadcastMessage( method_name, data );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the first component of type class_name of the SceneNode where this component belongs\\r\\n* @method getComponent\\r\\n* @param {String|Component} class_name the name of the class in string format or the component class itself\\r\\n* @return {*} Component or null\\r\\n**/\\r\\nBaseComponent.prototype.getComponent = function( class_name )\\r\\n{\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn null;\\r\\n\\treturn this._root.getComponent( class_name );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Bind one object event to a method in this component\\r\\n* @method bind\\r\\n* @param {*} object the dispatcher of the event you want to react to\\r\\n* @param {String} event the name of the event to bind to\\r\\n* @param {Function} callback the callback to call\\r\\n* @param {String|Object} type [optional] an string identifying the type of the variable, could be \\\"number\\\",\\\"string\\\",\\\"Texture\\\",\\\"vec3\\\",\\\"mat4\\\", or an object with all the info\\r\\n* @param {Function} setter [optional] setter function, otherwise one will be created\\r\\n* @param {Function} getter [optional] getter function, otherwise one will be created\\r\\n**/\\r\\nBaseComponent.prototype.bind = function( object, method, callback )\\r\\n{\\r\\n\\tvar instance = this;\\r\\n\\tif(arguments.length > 3 )\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"Component.bind cannot use a fourth parameter, all callbacks will be binded to the component\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tif(!object)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"Cannot bind to null.\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tif(!callback)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"You cannot bind a method before defining it.\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t/*\\r\\n\\tvar found = false;\\r\\n\\tfor(var i in this)\\r\\n\\t{\\r\\n\\t\\tif(this[i] == callback)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfound = true;\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\tif(!found)\\r\\n\\t\\tconsole.warn(\\\"Callback function not found in this object, this is dangerous, remember to unbind it manually or use LEvent instead.\\\");\\r\\n\\t*/\\r\\n\\r\\n\\t//store info about which objects have events pointing to this instance\\r\\n\\tif(!this.__targeted_instances)\\r\\n\\t\\tObject.defineProperty( this,\\\"__targeted_instances\\\", { value: [], enumerable: false, writable: true });\\r\\n\\tvar index = this.__targeted_instances.indexOf( object );\\r\\n\\tif(index == -1)\\r\\n\\t\\tthis.__targeted_instances.push( object );\\r\\n\\r\\n\\treturn LEvent.bind( object, method, callback, instance );\\r\\n}\\r\\n\\r\\nBaseComponent.prototype.unbind = function( object, method, callback )\\r\\n{\\r\\n\\tvar instance = this;\\r\\n\\r\\n\\tvar r = LEvent.unbind( object, method, callback, instance );\\r\\n\\r\\n\\t//erase from targeted instances\\r\\n\\tif( this.__targeted_instances )\\r\\n\\t{\\r\\n\\t\\tif( !LEvent.hasBindTo( object, this ) )\\r\\n\\t\\t\\treturn r;\\r\\n\\r\\n\\t\\tvar index = this.__targeted_instances.indexOf( object );\\r\\n\\t\\tif(index == -1)\\r\\n\\t\\t\\tthis.__targeted_instances.splice( index, 1 );\\r\\n\\t\\tif(this.__targeted_instances.length == 0)\\r\\n\\t\\t\\tdelete this.__targeted_instances;\\r\\n\\t}\\r\\n\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\nBaseComponent.prototype.unbindAll = function()\\r\\n{\\r\\n\\tif( !this.__targeted_instances )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor( var i = 0; i < this.__targeted_instances.length; ++i )\\r\\n\\t\\tLEvent.unbindAll( this.__targeted_instances[i], this );\\r\\n\\tthis.__targeted_instances = null; //delete dont work??\\r\\n}\\r\\n\\r\\n//called by register component to add setters and getters to registered Component Classes\\r\\nBaseComponent.addExtraMethods = function( component )\\r\\n{\\r\\n\\t//add uid property\\r\\n\\tObject.defineProperty( component.prototype, 'uid', {\\r\\n\\t\\tset: function( uid )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!uid)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t\\tif(uid[0] != LS._uid_prefix)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Invalid UID, renaming it to: \\\" + uid );\\r\\n\\t\\t\\t\\tuid = LS._uid_prefix + uid;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(uid == this._uid)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t//if( this._root && this._root._components_by_uid[ this.uid ] )\\r\\n\\t\\t\\t//\\tdelete this._root && this._root._components_by_uid[ this.uid ];\\r\\n\\t\\t\\tthis._uid = uid;\\r\\n\\t\\t\\t//if( this._root )\\r\\n\\t\\t\\t//\\tthis._root && this._root._components_by_uid[ this.uid ] = this;\\r\\n\\t\\t},\\r\\n\\t\\tget: function(){\\r\\n\\t\\t\\treturn this._uid;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false //uid better not be enumerable (so it doesnt show in the editor)\\r\\n\\t});\\r\\n\\r\\n\\tObject.defineProperty( component.prototype, 'root', {\\r\\n\\t\\tset: function(v)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthrow(\\\"root cannot be set, call addComponent to the root\\\");\\r\\n\\t\\t},\\r\\n\\t\\tget: function(){\\r\\n\\t\\t\\treturn this._root;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false //uid better not be enumerable (so it doesnt show in the editor)\\r\\n\\t});\\r\\n\\r\\n\\t//same as root...\\r\\n\\tObject.defineProperty( component.prototype, 'parentNode', {\\r\\n\\t\\tset: function()\\r\\n\\t\\t{\\r\\n\\t\\t\\tthrow(\\\"parentNode cannot be set, call addComponent to the parentNode\\\");\\r\\n\\t\\t},\\r\\n\\t\\tget: function(){\\r\\n\\t\\t\\treturn this._root;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false //uid better not be enumerable (so it doesnt show in the editor)\\r\\n\\t});\\r\\n\\r\\n};\\r\\n\\r\\nLS.BaseComponent = BaseComponent;\\r\\n\\r\\n//Used when a component is missing\\r\\nfunction MissingComponent()\\r\\n{\\r\\n\\tthis._last_serialization = null;\\r\\n\\tthis._comp_class = \\\"\\\";\\r\\n}\\r\\n\\r\\nMissingComponent.prototype.configure = function(o)\\r\\n{\\r\\n\\tthis.uid = o.uid;\\r\\n\\tthis._last_serialization = o;\\r\\n}\\r\\n\\r\\nMissingComponent.prototype.serialize = function()\\r\\n{\\r\\n\\treturn this._last_serialization;\\r\\n}\\r\\n\\r\\nLS.MissingComponent = MissingComponent;\\r\\n\\r\\n///@FILE:../src/resources/resource.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* This class contains all the info about a resource and it works as a template for any resource class\\r\\n* Keep in mind that there are many resource classes like Meshes or Textures that DONT INHERIT FROM THIS CLASS.\\r\\n* This class is used mainly to generic file resources like text files (scripts, csvs, etc)\\r\\n*\\r\\n* @class Resource\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nfunction Resource()\\r\\n{\\r\\n\\tthis.filename = null; //name of file without folder or path\\r\\n\\tthis.fullpath = null; //contains the unique name as is to be used to fetch it by the resources manager\\r\\n\\tthis.remotepath = null; //the string to fetch this resource in internet (local resources do not have this name)\\r\\n\\tthis._data = null;\\r\\n\\t//this.type = 0;\\r\\n}\\r\\n\\r\\n//Resource.DATA = 1;\\r\\n//Resource.SCRIPT = 2;\\r\\n\\r\\nObject.defineProperty( Resource.prototype, \\\"data\\\", {\\r\\n\\tset: function(v){ \\r\\n\\t\\tthis._data = v;\\r\\n\\t\\tthis._modified = true;\\r\\n\\t},\\r\\n\\tget: function() { \\r\\n\\t\\treturn this._data;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/** makes this resource available by registering in the resources manager\\r\\n* @method rename\\r\\n*/\\r\\nResource.prototype.register = function()\\r\\n{\\r\\n\\tLS.ResourcesManager.registerResource( this.fullpath || this.filename, this );\\r\\n}\\r\\n\\r\\n/** Renames the resource and ensures the resources manager is updated accordingly\\r\\n* @method rename\\r\\n* @param {String} new_filename the new filename\\r\\n*/\\r\\nResource.prototype.rename = function( new_filename )\\r\\n{\\r\\n\\tLS.ResourcesManager.renameResource( this.fullpath || this.filename, new_filename );\\r\\n}\\r\\n\\r\\nObject.defineProperty( Resource.prototype, \\\"uid\\\", { \\r\\n\\tget: function(){ return this.fullpath || this.filename },\\r\\n\\tset: function(v){},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* Static method: Returns an object with a representation of the resource internal data\\r\\n* The order to obtain that object is:\\r\\n* 0. checks if getDataToStore function in resource\\r\\n* 1. test for _original_file (File or Blob)\\r\\n* 2. test for _original_data (ArrayBuffer)\\r\\n* 3. toBinary() (ArrayBuffer)\\r\\n* 4. toBlob() (Blob)\\r\\n* 5. toBase64() (String)\\r\\n* 6. serialize() (Object in JSON format)\\r\\n* 7. data property \\r\\n* 8. JSON.stringify(...)\\r\\n*\\r\\n* @method Resource.getDataToStore\\r\\n* @param {Object} resource \\r\\n* @param {Boolean} allow_blob [optional] \\r\\n* @return {Object} it has two fields: data and encoding\\r\\n*/\\r\\nResource.getDataToStore = function( resource, allow_blob )\\r\\n{\\r\\n\\tvar data = null;\\r\\n\\tvar encoding = \\\"text\\\";\\r\\n\\tvar extension = \\\"\\\";\\r\\n\\r\\n\\t//get the data\\r\\n\\tif (resource.getDataToStore) //function\\r\\n\\t{\\r\\n\\t\\tdata = resource.getDataToStore();\\r\\n\\t\\tif(data && data.constructor == ArrayBuffer)\\r\\n\\t\\t\\tencoding = \\\"binary\\\";\\r\\n\\t}\\r\\n\\telse if (resource._original_file) //file\\r\\n\\t{\\r\\n\\t\\tdata = resource._original_file;\\r\\n\\t\\tif(data && data.constructor !== File && data.constructor !== Blob)\\r\\n\\t\\t\\tconsole.warn(\\\"Resource._original_file is not File or Blob\\\");\\r\\n\\t\\tencoding = \\\"file\\\";\\r\\n\\t}\\r\\n\\telse if( resource._original_data ) //file in ArrayBuffer format\\r\\n\\t{\\r\\n\\t\\tdata = resource._original_data;\\r\\n\\t\\tif( data && data.constructor === ArrayBuffer )\\r\\n\\t\\t\\tencoding = \\\"binary\\\";\\r\\n\\t}\\r\\n\\telse if(resource.toBinary) //a function to compute the ArrayBuffer format\\r\\n\\t{\\r\\n\\t\\tif( resource.constructor === GL.Texture ) //HACK: textures require that extra parameter...\\r\\n\\t\\t\\tdata = resource.toBinary(true);\\r\\n\\t\\telse\\r\\n\\t\\t\\tdata = resource.toBinary();\\r\\n\\t\\tencoding = \\\"binary\\\";\\r\\n\\t\\tif(resource.constructor.binary_extension) //special case, textures are in PNG to keep alpha\\r\\n\\t\\t\\textension = resource.constructor.binary_extension;\\r\\n\\t\\telse\\r\\n\\t\\t\\textension = \\\"wbin\\\";\\r\\n\\t}\\r\\n\\telse if(resource.toBlob && allow_blob) //a blob (Canvas should have this)\\r\\n\\t{\\r\\n\\t\\tdata = resource.toBlob();\\r\\n\\t\\tencoding = \\\"file\\\";\\r\\n\\t}\\r\\n\\telse if(resource.toBase64) //a base64 string\\r\\n\\t{\\r\\n\\t\\tdata = resource.toBase64();\\r\\n\\t\\tencoding = \\\"base64\\\";\\r\\n\\t}\\r\\n\\telse if(resource.serialize) //a json object\\r\\n\\t{\\r\\n\\t\\tvar obj = resource.serialize();\\r\\n\\t\\t//remove inner stuff from the editor\\r\\n\\t\\tdelete obj.filename;\\r\\n\\t\\tdelete obj.fullpath;\\r\\n\\t\\tdelete obj.remotepath;\\r\\n\\t\\tdelete obj.preview_url; //just in case is an old resource\\r\\n\\t\\t//convert to string\\r\\n\\t\\tdata = JSON.stringify( obj );\\r\\n\\t}\\r\\n\\telse if(resource.data) //regular string data\\r\\n\\t\\tdata = resource.data;\\r\\n\\telse\\r\\n\\t\\tdata = JSON.stringify( resource );\\r\\n\\r\\n\\tif(data.buffer && data.buffer.constructor == ArrayBuffer)\\r\\n\\t\\tdata = data.buffer; //store the data in the arraybuffer\\r\\n\\r\\n\\treturn { data:data, encoding: encoding, extension: extension };\\r\\n}\\r\\n\\r\\n//used in the coding pad to assign content to generic text files\\r\\nResource.prototype.getData = function()\\r\\n{\\r\\n\\treturn this._data;\\r\\n}\\r\\n\\r\\nResource.prototype.setData = function( v, skip_modified_flag )\\r\\n{\\r\\n\\t//remove old file\\r\\n\\tif( this._original_data )\\r\\n\\t\\tthis._original_data = null;\\r\\n\\tthis._data = v;\\r\\n\\tif(!skip_modified_flag)\\r\\n\\t\\tthis._modified = true;\\r\\n}\\r\\n\\r\\nResource.prototype.getDataToStore = function()\\r\\n{\\r\\n\\tvar data = this.data || \\\"\\\";\\r\\n\\tif(data.constructor === Object )\\r\\n\\t\\tdata = JSON.stringify( data );\\r\\n\\treturn data;\\r\\n}\\r\\n\\r\\n/** Clone the resource\\r\\n* @method clone\\r\\n* @return {LS.Resource} the clone of the resource\\r\\n*/\\r\\nResource.prototype.clone = function()\\r\\n{\\r\\n\\tvar r = new LS.Resource();\\r\\n\\tr._data = this._data;\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\n/** Returns a string representing to which category this resource belongs\\r\\n* @method getCategory\\r\\n*/\\r\\nResource.prototype.getCategory = function()\\r\\n{\\r\\n\\tvar filename = this.fullpath || this.filename;\\r\\n\\tvar ext = LS.ResourcesManager.getExtension( filename );\\r\\n\\tif(ext == \\\"js\\\")\\r\\n\\t\\treturn \\\"Script\\\";\\r\\n\\treturn \\\"Data\\\";\\r\\n}\\r\\n\\r\\n/** When dropping this resource into a SceneNode\\r\\n* @method assignToNode\\r\\n*/\\r\\nResource.prototype.assignToNode = function(node)\\r\\n{\\r\\n\\tif(!node) \\r\\n\\t\\treturn false;\\r\\n\\r\\n\\tvar filename = this.fullpath || this.filename;\\r\\n\\tvar category = this.getCategory();\\r\\n\\r\\n\\tif( category == \\\"Script\\\" )\\r\\n\\t{\\r\\n\\t\\tvar script_component = new LS.Components.ScriptFromFile({ filename: filename });\\r\\n\\t\\tnode.addComponent( script_component );\\r\\n\\t}\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/** Parses the resource data as subfiles (subfiles are fragments of the code identified by a slash followed by name string), used by ShaderCode\\r\\n* @method getAsSubfiles\\r\\n* @return {Object} the object that contains every subfile\\r\\n*/\\r\\nResource.prototype.getAsSubfiles = function()\\r\\n{\\r\\n\\tif(!this._data)\\r\\n\\t\\treturn null;\\r\\n\\treturn GL.processFileAtlas( this._data );\\r\\n}\\r\\n\\r\\n/** Parses the resource as HTML code and returns a HTMLElement containing the html code\\r\\n* @method getAsHTML\\r\\n* @return {HTMLElement} the root HTMLElement that contains the code\\r\\n*/\\r\\nResource.prototype.getAsHTML = function()\\r\\n{\\r\\n\\tif(!this._data || this._data.constructor !== String)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar container = document.createElement(\\\"div\\\");\\r\\n\\tcontainer.innerHTML = this._data;\\r\\n\\treturn container;\\r\\n}\\r\\n\\r\\n/** Used by the editor to know if it can be edited in the text editor\\r\\n* @method hasEditableText\\r\\n*/\\r\\nResource.prototype.hasEditableText = function()\\r\\n{\\r\\n\\treturn this._data && this._data.constructor === String;\\r\\n}\\r\\n\\r\\nResource.hasPreview = false; //should this resource use a preview image?\\r\\n\\r\\nLS.Resource = Resource;\\r\\nLS.registerResourceClass( Resource );\\r\\n///@FILE:../src/resources/animation.js\\r\\n///@INFO: ANIMATION\\r\\n\\r\\n/**\\r\\n* An Animation is a resource that contains samples of properties over time, similar to animation curves\\r\\n* Values could be associated to an specific node.\\r\\n* Data is contained in tracks\\r\\n*\\r\\n* @class Animation\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nfunction Animation(o)\\r\\n{\\r\\n\\tthis.name = \\\"\\\";\\r\\n\\tthis.takes = {}; //packs of tracks\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nLS.Classes[\\\"Animation\\\"] = LS.Animation = Animation;\\r\\n\\r\\n//Animation.KEYFRAME_ANIMATION = 0;\\r\\n//Animation.CLIP_ANIMATION = 1;\\r\\n\\r\\nAnimation.EXTENSION = \\\"wbin\\\";\\r\\nAnimation.DEFAULT_SCENE_NAME = \\\"@scene\\\";\\r\\nAnimation.DEFAULT_DURATION = 10;\\r\\n\\r\\n/**\\r\\n* Create a new take inside this animation (a take contains all the tracks)\\r\\n* @method createTake\\r\\n* @param {String} name the name\\r\\n* @param {Number} duration\\r\\n* @return {LS.Animation.Take} the take\\r\\n*/\\r\\nAnimation.prototype.createTake = function( name, duration )\\r\\n{\\r\\n\\tif(!name)\\r\\n\\t\\tthrow(\\\"Animation Take name missing\\\");\\r\\n\\r\\n\\tvar take = new Animation.Take();\\r\\n\\ttake.name = name;\\r\\n\\ttake.duration = duration;\\r\\n\\tif(duration === undefined)\\r\\n\\t\\ttake.duration = Animation.DEFAULT_DURATION;\\r\\n\\tthis.addTake( take );\\r\\n\\treturn take;\\r\\n}\\r\\n\\r\\n/**\\r\\n* adds an existing take\\r\\n* @method addTake\\r\\n* @param {LS.Animation.Take} name the name\\r\\n*/\\r\\nAnimation.prototype.addTake = function(take)\\r\\n{\\r\\n\\tthis.takes[ take.name ] = take;\\r\\n\\treturn take;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the take with a given name\\r\\n* @method getTake\\r\\n* @param {String} name\\r\\n* @return {LS.Animation.Take} the take\\r\\n*/\\r\\nAnimation.prototype.getTake = function( name )\\r\\n{\\r\\n\\treturn this.takes[ name ];\\r\\n}\\r\\n\\r\\n/**\\r\\n* renames a take name\\r\\n* @method renameTake\\r\\n* @param {String} old_name\\r\\n* @param {String} new_name\\r\\n*/\\r\\nAnimation.prototype.renameTake = function( old_name, new_name )\\r\\n{\\r\\n\\tvar take = this.takes[ old_name ];\\r\\n\\tif(!take)\\r\\n\\t\\treturn;\\r\\n\\tdelete this.takes[ old_name ];\\r\\n\\ttake.name = new_name;\\r\\n\\tthis.takes[ new_name ] = take;\\r\\n\\tLEvent.trigger( this, \\\"take_renamed\\\", [old_name, new_name] );\\r\\n}\\r\\n\\r\\n/**\\r\\n* removes a take\\r\\n* @method removeTake\\r\\n* @param {String} name\\r\\n*/\\r\\nAnimation.prototype.removeTake = function( name )\\r\\n{\\r\\n\\tvar take = this.takes[ name ];\\r\\n\\tif(!take)\\r\\n\\t\\treturn;\\r\\n\\tdelete this.takes[ name ];\\r\\n\\tLEvent.trigger( this, \\\"take_removed\\\", name );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the number of takes\\r\\n* @method getNumTakes\\r\\n* @return {Number} the number of takes\\r\\n*/\\r\\nAnimation.prototype.getNumTakes = function()\\r\\n{\\r\\n\\tvar num = 0;\\r\\n\\tfor(var i in this.takes)\\r\\n\\t\\tnum++;\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\nAnimation.prototype.addTrackToTake = function(takename, track)\\r\\n{\\r\\n\\tvar take = this.takes[ takename ];\\r\\n\\tif(!take)\\r\\n\\t\\ttake = this.createTake( takename );\\r\\n\\ttake.addTrack( track );\\r\\n}\\r\\n\\r\\n\\r\\nAnimation.prototype.configure = function(data)\\r\\n{\\r\\n\\tif(data.name)\\r\\n\\t\\tthis.name = data.name;\\r\\n\\r\\n\\tif(data.takes)\\r\\n\\t{\\r\\n\\t\\tvar num_takes = 0;\\r\\n\\t\\tthis.takes = {};\\r\\n\\t\\tfor(var i in data.takes)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar take = new LS.Animation.Take( data.takes[i] );\\r\\n\\t\\t\\tif(!take.name)\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Take without name\\\");\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.addTake( take );\\r\\n\\t\\t\\t\\ttake.loadResources(); //load associated resources\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tnum_takes++;\\r\\n\\t\\t}\\r\\n\\t\\tif(!num_takes)\\r\\n\\t\\t\\tthis.createTake(\\\"default\\\", LS.Animation.DEFAULT_DURATION );\\r\\n\\t}\\r\\n}\\r\\n\\r\\nAnimation.prototype.serialize = function()\\r\\n{\\r\\n\\treturn LS.cloneObject(this, null, true);\\r\\n}\\r\\n\\r\\nAnimation.fromBinary = function( data )\\r\\n{\\r\\n\\tif(data.constructor == ArrayBuffer)\\r\\n\\t\\tdata = WBin.load(data, true);\\r\\n\\r\\n\\tvar o = data[\\\"@json\\\"];\\r\\n\\tif(!o) //sometimes the data already comes extractedin the object itself\\r\\n\\t\\to = data;\\r\\n\\r\\n\\tfor(var i in o.takes)\\r\\n\\t{\\r\\n\\t\\tvar take = o.takes[i];\\r\\n\\t\\tfor(var j in take.tracks)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar track = take.tracks[j];\\r\\n\\t\\t\\tvar name = \\\"@take_\\\" + i + \\\"_track_\\\" + j;\\r\\n\\t\\t\\tif( data[name] )\\r\\n\\t\\t\\t\\ttrack.data = data[name];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn new LS.Animation( o );\\r\\n}\\r\\n\\r\\nAnimation.prototype.toBinary = function()\\r\\n{\\r\\n\\tvar o = {};\\r\\n\\tvar tracks_data = [];\\r\\n\\r\\n\\t//we need to remove the bin data to generate the JSON\\r\\n\\tfor(var i in this.takes)\\r\\n\\t{\\r\\n\\t\\tvar take = this.takes[i];\\r\\n\\t\\tfor(var j in take.tracks)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar track = take.tracks[j];\\r\\n\\t\\t\\ttrack.packData(); //reduce storage space and speeds up loading\\r\\n\\r\\n\\t\\t\\tif(track.packed_data)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar bindata = track.data;\\r\\n\\t\\t\\t\\tvar name = \\\"@take_\\\" + i + \\\"_track_\\\" + j;\\r\\n\\t\\t\\t\\to[name] = bindata;\\r\\n\\t\\t\\t\\ttrack.data = null;\\r\\n\\t\\t\\t\\ttracks_data.push( bindata );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//create the binary\\r\\n\\to[\\\"@json\\\"] = LS.cloneObject(this, null, true);\\r\\n\\tvar bin = WBin.create(o, \\\"Animation\\\");\\r\\n\\r\\n\\t//restore the bin data state in this instance\\r\\n\\tfor(var i in this.takes)\\r\\n\\t{\\r\\n\\t\\tvar take = this.takes[i];\\r\\n\\t\\tfor(var j in take.tracks)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar track = take.tracks[j];\\r\\n\\t\\t\\tvar name = \\\"@take_\\\" + i + \\\"_track_\\\" + j;\\r\\n\\t\\t\\tif(o[name])\\r\\n\\t\\t\\t\\ttrack.data = o[name];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn bin;\\r\\n}\\r\\n\\r\\n//Used when the animation tracks use names instead of node ids\\r\\n//to convert the track locator to node names, so they affect to only one node\\r\\nAnimation.prototype.convertNamesToIDs = function( use_basename, root )\\r\\n{\\r\\n\\tvar num = 0;\\r\\n\\tfor(var i in this.takes)\\r\\n\\t{\\r\\n\\t\\tvar take = this.takes[i];\\r\\n\\t\\tnum += take.convertNamesToIDs( use_basename, root );\\r\\n\\t}\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\n//Used when the animation tracks use UIDs instead of node names\\r\\n//to convert the track locator to node names, so they can be reused between nodes in the same scene\\r\\nAnimation.prototype.convertIDsToNames = function( use_basename, root )\\r\\n{\\r\\n\\tvar num = 0;\\r\\n\\tfor(var i in this.takes)\\r\\n\\t{\\r\\n\\t\\tvar take = this.takes[i];\\r\\n\\t\\tnum += take.convertIDsToNames( use_basename, root );\\r\\n\\t}\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\n/**\\r\\n* changes the packing mode of the tracks inside all takes\\r\\n* @method setTracksPacking\\r\\n* @param {boolean} pack if true the tracks will be packed (used a typed array)\\r\\n* @return {Number} te number of modifyed tracks\\r\\n*/\\r\\nAnimation.prototype.setTracksPacking = function(v)\\r\\n{\\r\\n\\tvar num = 0;\\r\\n\\tfor(var i in this.takes)\\r\\n\\t{\\r\\n\\t\\tvar take = this.takes[i];\\r\\n\\t\\tnum += take.setTracksPacking(v);\\r\\n\\t}\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\n/**\\r\\n* optimize all the tracks in all the takes, so they take less space and are faster to compute (when possible)\\r\\n* @method optimizeTracks\\r\\n* @return {Number} the number of takes\\r\\n*/\\r\\nAnimation.prototype.optimizeTracks = function()\\r\\n{\\r\\n\\tvar num = 0;\\r\\n\\tfor(var i in this.takes)\\r\\n\\t{\\r\\n\\t\\tvar take = this.takes[i];\\r\\n\\t\\tnum += take.optimizeTracks();\\r\\n\\t}\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\n/**\\r\\n* It creates a PlayAnimation component to the node (or reuse and old existing one). Used when a resource is assigned to a node\\r\\n* @method assignToNode\\r\\n* @param {LS.SceneNode} node node where to assign this animation\\r\\n*/\\r\\nAnimation.prototype.assignToNode = function(node)\\r\\n{\\r\\n\\tvar component = node.getComponent( LS.Components.PlayAnimation );\\r\\n\\tif(!component)\\r\\n\\t\\tcomponent = node.addComponent( LS.Components.PlayAnimation );\\r\\n\\tcomponent.animation = this.fullpath || this.filename;\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/** \\r\\n* Represents a set of animations\\r\\n*\\r\\n* @class Take\\r\\n* @namespace LS.Animation\\r\\n* @constructor\\r\\n*/\\r\\nfunction Take(o)\\r\\n{\\r\\n\\t/** \\r\\n\\t* @property name {String}\\r\\n\\t**/\\r\\n\\tthis.name = null;\\r\\n\\t/** \\r\\n\\t* @property tracks {Array}\\r\\n\\t**/\\r\\n\\tthis.tracks = [];\\r\\n\\t/** \\r\\n\\t* @property duration {Number} in seconds\\r\\n\\t**/\\r\\n\\tthis.duration = LS.Animation.DEFAULT_DURATION;\\r\\n\\t\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n\\r\\n}\\r\\n\\r\\nTake.prototype.configure = function( o )\\r\\n{\\r\\n\\tif( o.name )\\r\\n\\t\\tthis.name = o.name;\\r\\n\\tif( o.tracks ) \\r\\n\\t{\\r\\n\\t\\tthis.tracks = []; //clear\\r\\n\\t\\tfor(var i in o.tracks)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar track = new LS.Animation.Track( o.tracks[i] );\\r\\n\\t\\t\\tthis.addTrack( track );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\tif( o.duration )\\r\\n\\t\\tthis.duration = o.duration;\\r\\n}\\r\\n\\r\\nTake.prototype.serialize = Take.prototype.toJSON = function()\\r\\n{\\r\\n\\treturn LS.cloneObject(this, null, true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* creates a new track from a given data\\r\\n* @method createTrack\\r\\n* @param {Object} data in serialized format\\r\\n* @return {LS.Animation.Track} the track\\r\\n*/\\r\\nTake.prototype.createTrack = function( data )\\r\\n{\\r\\n\\tif(!data)\\r\\n\\t\\tthrow(\\\"Data missing when creating track\\\");\\r\\n\\r\\n\\tvar track = this.getTrack( data.property );\\r\\n\\tif( track )\\r\\n\\t\\treturn track;\\r\\n\\r\\n\\tvar track = new LS.Animation.Track( data );\\r\\n\\tthis.addTrack( track );\\r\\n\\treturn track;\\r\\n}\\r\\n\\r\\n/**\\r\\n* For every track, gets the interpolated value between keyframes and applies the value to the property associated with the track locator\\r\\n* Locators are in the form of \\\"{NODE_UID}/{COMPONENT_UID}/{property_name}\\\"\\r\\n*\\r\\n* @method applyTracks\\r\\n* @param {number} current_time the time of the anim to sample\\r\\n* @param {number} last_time this is used for events, we need to know where you were before\\r\\n* @param {boolean} ignore_interpolation in case you want to sample the nearest one\\r\\n* @param {SceneNode} weight [Optional] allows to blend animations with current value (default is 1)\\r\\n* @param {Number} root [Optional] if you want to limit the locator to search inside a node\\r\\n* @param {Function} on_pre_apply [Optional] a callback called per track to see if this track should be applyed, if it returns false it is skipped. callback receives (track, current_time, root_node, weight)\\r\\n* @param {Function} on_apply_sample [Optional] a callback called before applying a keyframe, if the callback returns false the keyframe will be skipped. callback parameters ( track, sample, root_node, weight )\\r\\n* @return {Component} the target where the action was performed\\r\\n*/\\r\\nTake.prototype.applyTracks = function( current_time, last_time, ignore_interpolation, root_node, scene, weight, on_pre_apply, on_apply_sample )\\r\\n{\\r\\n\\tscene = scene || LS.GlobalScene;\\r\\n\\tif(weight === 0)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tweight = weight || 1;\\r\\n\\r\\n\\tfor(var i = 0; i < this.tracks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar track = this.tracks[i];\\r\\n\\t\\tif( track.enabled === false || !track.data )\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif(on_pre_apply && on_pre_apply( track, current_time, root_node, weight ) === false)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t//events are an special kind of tracks, they execute actions\\r\\n\\t\\tif( track._type_index == Track.EVENT )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar keyframe = track.getKeyframeByTime( current_time );\\r\\n\\t\\t\\tif( !keyframe || keyframe[0] < last_time || keyframe[0] > current_time )\\r\\n\\t\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t\\t//need info to search for node\\r\\n\\t\\t\\tvar info = scene.getPropertyInfoFromPath( track._property_path );\\r\\n\\t\\t\\tif(!info)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t\\tvar value = keyframe[1];\\r\\n\\r\\n\\t\\t\\tif(value[2] == 1) //on function call events the thirth value [2] is 1\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//functions\\r\\n\\t\\t\\t\\tif(info.node && info.target && info.target[ value[0] ] )\\r\\n\\t\\t\\t\\t\\tinfo.target[ value[0] ].call( info.target, value[1] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//events\\r\\n\\t\\t\\t\\tif(info.target) //components\\r\\n\\t\\t\\t\\t\\tLEvent.trigger( info.target, keyframe[1], keyframe[1][1] );\\r\\n\\t\\t\\t\\telse if(info.node) //nodes\\r\\n\\t\\t\\t\\t\\tLEvent.trigger( info.node, keyframe[1][0], keyframe[1][1] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse //regular tracks\\r\\n\\t\\t{\\r\\n\\t\\t\\t//read from the animation track the value\\r\\n\\t\\t\\tvar sample = track.getSample( current_time, !ignore_interpolation );\\r\\n\\r\\n\\t\\t\\t//to blend between animations...\\r\\n\\t\\t\\tif(weight !== 1)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar current_value = scene.getPropertyValueFromPath( track._property_path, sample, root_node, 0 );\\r\\n\\t\\t\\t\\tsample = LS.Animation.interpolateLinear( sample, current_value, weight, null, track._type, track.value_size, track );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//apply the value to the property specified by the locator\\r\\n\\t\\t\\tif( sample !== undefined ) \\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( on_apply_sample && on_apply_sample( track, sample, root_node, weight ) === false)\\r\\n\\t\\t\\t\\t\\tcontinue; //skip\\r\\n\\t\\t\\t\\ttrack._target = scene.setPropertyValueFromPath( track._property_path, sample, root_node, 0 );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\nTake.prototype.addTrack = function( track )\\r\\n{\\r\\n\\tthis.tracks.push( track );\\r\\n}\\r\\n\\r\\nTake.prototype.getTrack = function( property )\\r\\n{\\r\\n\\tfor(var i = 0; i < this.tracks.length; ++i)\\r\\n\\t\\tif(this.tracks[i].property == property)\\r\\n\\t\\t\\treturn this.tracks[i];\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\nTake.prototype.removeTrack = function( track )\\r\\n{\\r\\n\\tfor(var i = 0; i < this.tracks.length; ++i)\\r\\n\\t\\tif(this.tracks[i] == track)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.tracks.splice( i, 1 );\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n}\\r\\n\\r\\n\\r\\nTake.prototype.getPropertiesSample = function( time, result )\\r\\n{\\r\\n\\tresult = result || [];\\r\\n\\tfor(var i = 0; i < this.tracks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar track = this.tracks[i];\\r\\n\\t\\tvar value = track.getSample( time );\\r\\n\\t\\tresult.push([track.property, value]);\\r\\n\\t}\\r\\n\\treturn result;\\r\\n}\\r\\n\\r\\nTake.prototype.actionPerSample = function(time, callback, options)\\r\\n{\\r\\n\\tfor(var i = 0; i < this.tracks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar track = this.tracks[i];\\r\\n\\t\\tvar value = track.getSample(time, true);\\r\\n\\t\\tif( options.disabled_tracks && options.disabled_tracks[ track.property ] )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tcallback(track.property, value, options);\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//Ensures all the resources associated to keyframes are loaded in memory\\r\\nTake.prototype.loadResources = function()\\r\\n{\\r\\n\\tfor(var i = 0; i < this.tracks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar track = this.tracks[i];\\r\\n\\t\\tif(track._type == \\\"texture\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar l = track.getNumberOfKeyframes();\\r\\n\\t\\t\\tfor(var j = 0; j < l; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar keyframe = track.getKeyframe(j);\\r\\n\\t\\t\\t\\tif(keyframe && keyframe[1] && keyframe[1][0] != \\\":\\\")\\r\\n\\t\\t\\t\\t\\tLS.ResourcesManager.load( keyframe[1] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//convert track locators from using UIDs to use node names (this way the same animation can be used in several parts of the scene)\\r\\nTake.prototype.convertNamesToIDs = function( use_basename, root )\\r\\n{\\r\\n\\tvar num = 0;\\r\\n\\tfor(var j = 0; j < this.tracks.length; ++j)\\r\\n\\t{\\r\\n\\t\\tvar track = this.tracks[j];\\r\\n\\t\\tnum += track.convertNameToID( use_basename, root )\\r\\n\\t}\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\n//convert track locators from using UIDs to use node names (this way the same animation can be used in several parts of the scene)\\r\\nTake.prototype.convertIDsToNames = function( use_basename, root )\\r\\n{\\r\\n\\tvar num = 0;\\r\\n\\tfor(var j = 0; j < this.tracks.length; ++j)\\r\\n\\t{\\r\\n\\t\\tvar track = this.tracks[j];\\r\\n\\t\\tnum += track.convertIDtoName( use_basename, root )\\r\\n\\t}\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\nTake.prototype.setTracksPacking = function(v)\\r\\n{\\r\\n\\tvar num = 0;\\r\\n\\tfor(var i = 0; i < this.tracks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar track = this.tracks[i];\\r\\n\\t\\tif( track.packed_data == v)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(v)\\r\\n\\t\\t\\ttrack.packData();\\r\\n\\t\\telse\\r\\n\\t\\t\\ttrack.unpackData();\\r\\n\\t\\tnum += 1;\\r\\n\\t}\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Optimizes the tracks by changing the Matrix tracks to Trans10 tracks which are way faster and use less space\\r\\n* @method optimizeTracks\\r\\n*/\\r\\nTake.prototype.optimizeTracks = function()\\r\\n{\\r\\n\\tvar num = 0;\\r\\n\\tvar temp = new Float32Array(10);\\r\\n\\r\\n\\tfor(var i = 0; i < this.tracks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar track = this.tracks[i];\\r\\n\\t\\tif( track.convertToTrans10() )\\r\\n\\t\\t\\tnum += 1;\\r\\n\\t}\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\nTake.prototype.setInterpolationToAllTracks = function( interpolation )\\r\\n{\\r\\n\\tvar num = 0;\\r\\n\\tfor(var i = 0; i < this.tracks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar track = this.tracks[i];\\r\\n\\t\\tif(track.interpolation == interpolation)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\ttrack.interpolation = interpolation;\\r\\n\\t\\tnum += 1;\\r\\n\\t}\\r\\n\\r\\n\\treturn num;\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n\\r\\nAnimation.Take = Take;\\r\\n\\r\\n\\r\\n/**\\r\\n* Represents one track with data over time about one property\\r\\n* Data could be stored in two forms, or an array containing arrays of [time,data] (unpacked data) or in a single typed array (packed data), depends on the attribute typed_mode\\r\\n*\\r\\n* @class Animation.Track\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\n//KeyframesTrack: 0\\r\\n//ClipsTrack: 1\\r\\n\\r\\nfunction Track(o)\\r\\n{\\r\\n\\t/** \\r\\n\\t* @property enabled {Boolean} if it must be applied\\r\\n\\t**/\\r\\n\\tthis.enabled = true;\\r\\n\\t/** \\r\\n\\t* @property name {String} title to show in the editor\\r\\n\\t**/\\r\\n\\tthis.name = \\\"\\\"; //title\\r\\n\\t/** \\r\\n\\t* @property type {String} if the data is number, vec2, color, etc\\r\\n\\t**/\\r\\n\\tthis._type = null; //type of data (number, vec2, color, texture, etc)\\r\\n\\tthis._type_index = null; //type in number format (to optimize)\\r\\n\\t/** \\r\\n\\t* @property interpolation {Number} type of interpolation LS.NONE, LS.LINEAR, LS.TRIGONOMETRIC, LS.CUBIC, LS.SPLICE\\r\\n\\t**/\\r\\n\\tthis.interpolation = LS.NONE;\\r\\n\\t/** \\r\\n\\t* @property looped {Boolean} if the last and the first keyframe should be connected\\r\\n\\t**/\\r\\n\\tthis.looped = false; //interpolate last keyframe with first\\r\\n\\r\\n\\t//data\\r\\n\\tthis.packed_data = false; //this means the data is stored in one continuous datatype, faster to load but not editable\\r\\n\\tthis.value_size = 0; //how many numbers contains every sample of this property, 0 means basic type (string, boolean)\\r\\n\\t/** \\r\\n\\t* @property data {*} contains all the keyframes, could be an array or a typed array\\r\\n\\t**/\\r\\n\\tthis.data = null; //array or typed array where you have the time value followed by this.value_size bytes of data\\r\\n\\tthis.data_table = null; //used to index data when storing it\\r\\n\\r\\n\\t//to speed up sampling\\r\\n\\tObject.defineProperty( this, '_property', {\\r\\n\\t\\tvalue: \\\"\\\",\\r\\n\\t\\tenumerable: false,\\r\\n\\t\\twritable: true\\r\\n\\t});\\r\\n\\r\\n\\tObject.defineProperty( this, '_property_path', {\\r\\n\\t\\tvalue: [],\\r\\n\\t\\tenumerable: false,\\r\\n\\t\\twritable: true\\r\\n\\t});\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nAnimation.Track = Track;\\r\\n\\r\\nTrack.FRAMERATE = 30;\\r\\n\\r\\n//for optimization\\r\\nTrack.QUAT = LS.TYPES_INDEX[\\\"quat\\\"];\\r\\nTrack.TRANS10 = LS.TYPES_INDEX[\\\"trans10\\\"];\\r\\nTrack.EVENT = LS.TYPES_INDEX[\\\"event\\\"];\\r\\n\\r\\n\\r\\n/** \\r\\n* @property property {String} the locator to the property this track should modify ( \\\"node/component_uid/property\\\" )\\r\\n**/\\r\\nObject.defineProperty( Track.prototype, 'property', {\\r\\n\\tset: function( property )\\r\\n\\t{\\r\\n\\t\\tthis._property = property.trim();\\r\\n\\t\\tthis._property_path = this._property.split(\\\"/\\\");\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this._property;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( Track.prototype, 'type', {\\r\\n\\tset: function( t )\\r\\n\\t{\\r\\n\\t\\tthis._type = t;\\r\\n\\t\\tthis._type_index = LS.TYPES_INDEX[t];\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this._type;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nTrack.prototype.configure = function( o )\\r\\n{\\r\\n\\tif(!o.property)\\r\\n\\t\\tconsole.warn(\\\"Track with property name\\\");\\r\\n\\r\\n\\tif(o.enabled !== undefined) this.enabled = o.enabled;\\r\\n\\tif(o.name) this.name = o.name;\\r\\n\\tif(o.property) this.property = o.property;\\r\\n\\tif(o.type) this.type = o.type;\\r\\n\\tif(o.looped) this.looped = o.looped;\\r\\n\\tif(o.interpolation !== undefined)\\r\\n\\t\\tthis.interpolation = o.interpolation;\\r\\n\\telse\\r\\n\\t\\tthis.interpolation = LS.LINEAR;\\r\\n\\r\\n\\tif(o.data_table) this.data_table = o.data_table;\\r\\n\\r\\n\\tif(o.value_size) this.value_size = o.value_size;\\r\\n\\r\\n\\t//data\\r\\n\\tif(o.data)\\r\\n\\t{\\r\\n\\t\\tthis.data = o.data;\\r\\n\\r\\n\\t\\t//this is ugly but makes it easy to work with the collada importer\\r\\n\\t\\tif(o.packed_data === undefined && this.data.constructor !== Array)\\r\\n\\t\\t\\tthis.packed_data = true;\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.packed_data = !!o.packed_data;\\r\\n\\r\\n\\t\\tif( o.data.constructor == Array )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.packed_data )\\r\\n\\t\\t\\t\\tthis.data = new Float32Array( o.data );\\r\\n\\t\\t}\\r\\n\\t\\t//else\\r\\n\\t\\t//\\tthis.unpackData();\\r\\n\\t}\\r\\n\\r\\n\\tif(o.interpolation && !this.value_size)\\r\\n\\t\\to.interpolation = LS.NONE;\\r\\n}\\r\\n\\r\\nTrack.prototype.serialize = function()\\r\\n{\\r\\n\\tvar o = {\\r\\n\\t\\tenabled: this.enabled,\\r\\n\\t\\tname: this.name,\\r\\n\\t\\tproperty: this.property, \\r\\n\\t\\ttype: this._type,\\r\\n\\t\\tinterpolation: this.interpolation,\\r\\n\\t\\tlooped: this.looped,\\r\\n\\t\\tvalue_size: this.value_size,\\r\\n\\t\\tpacked_data: this.packed_data,\\r\\n\\t\\tdata_table: this.data_table\\r\\n\\t}\\r\\n\\r\\n\\tif(this.data)\\r\\n\\t{\\r\\n\\t\\tif(this.value_size <= 1)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(this.data.concat)\\r\\n\\t\\t\\t\\to.data = this.data.concat(); //regular array, clone it\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\to.data = new this.data.constructor( o.data ); //clone for typed arrays (weird, this should never happen but it does)\\r\\n\\t\\t}\\r\\n\\t\\telse //pack data\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.packData();\\r\\n\\t\\t\\to.data = new Float32Array( this.data ); //clone it\\r\\n\\t\\t\\to.packed_data = this.packed_data;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\nTrack.prototype.toJSON = Track.prototype.serialize;\\r\\n\\r\\nTrack.prototype.clear = function()\\r\\n{\\r\\n\\tthis.data = [];\\r\\n\\tthis.packed_data = false;\\r\\n}\\r\\n\\r\\n/**\\r\\n* used to change every track so instead of using UIDs for properties it uses names\\r\\n* this is used when you want to apply the same animation to different nodes in the scene\\r\\n* @method getIDasName\\r\\n* @param {boolean} use_basename if you want to just use the node name, othewise it uses the fullname (name with path)\\r\\n* @param {LS.SceneNode} root\\r\\n* @return {String} the result name\\r\\n*/\\r\\nTrack.prototype.getIDasName = function( use_basename, root )\\r\\n{\\r\\n\\tif( !this._property_path || !this._property_path.length )\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\treturn LS.convertLocatorFromUIDsToName( this._property, use_basename, root );\\r\\n}\\r\\n\\r\\n//used to change every track so instead of using node names for properties it uses node uids\\r\\n//this is used when you want to apply an animation to an specific node\\r\\nTrack.prototype.convertNameToID = function( root )\\r\\n{\\r\\n\\tif(this._property_path[0][0] === LS._uid_prefix)\\r\\n\\t\\treturn false; //is already using UIDs\\r\\n\\r\\n\\tvar node = LSQ.get( this._property_path[0], root );\\r\\n\\tif(!node)\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\t//convert node uid\\r\\n\\tthis._property_path[0] = node.uid;\\r\\n\\r\\n\\t//convert component uid\\r\\n\\tif( this._property_path.length > 1)\\r\\n\\t{\\r\\n\\t\\tvar comp = node.getComponent( this._property_path[1] );\\r\\n\\t\\tif(comp)\\r\\n\\t\\t\\tthis._property_path[1] = comp.uid;\\r\\n\\t}\\r\\n\\r\\n\\tthis._property = this._property_path.join(\\\"/\\\");\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n//used to change every track so instead of using UIDs for properties it uses node names\\r\\n//this is used when you want to apply the same animation to different nodes in the scene\\r\\nTrack.prototype.convertIDtoName = function( use_basename, root )\\r\\n{\\r\\n\\tvar name = this.getIDasName( use_basename, root );\\r\\n\\tif(!name)\\r\\n\\t\\treturn false;\\r\\n\\tthis._property = name;\\r\\n\\tthis._property_path = this._property.split(\\\"/\\\");\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* If the track used matrices, it transform them to position,quaternion and scale (10 floats, also known as trans10)\\r\\n* this makes working with animations faster\\r\\n* @method convertToTrans10\\r\\n*/\\r\\nTrack.prototype.convertToTrans10 = function()\\r\\n{\\r\\n\\tif( this.value_size != 16 )\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\t//convert samples\\r\\n\\tif(!this.packed_data)\\r\\n\\t\\tthis.packData();\\r\\n\\r\\n\\t//convert locator\\r\\n\\tvar path = this.property.split(\\\"/\\\");\\r\\n\\tif( path[ path.length - 1 ] != \\\"matrix\\\") //from \\\"nodename/matrix\\\" to \\\"nodename/transform/data\\\"\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\tpath[ path.length - 1 ] = \\\"Transform/data\\\";\\r\\n\\tthis.property = path.join(\\\"/\\\");\\r\\n\\tthis.type = \\\"trans10\\\";\\r\\n\\tthis.value_size = 10;\\r\\n\\tvar temp = new Float32Array(10);\\r\\n\\r\\n\\tvar data = this.data;\\r\\n\\tvar num_samples = data.length / 17;\\r\\n\\tfor(var k = 0; k < num_samples; ++k)\\r\\n\\t{\\r\\n\\t\\tvar sample = data.subarray(k*17+1,(k*17)+17);\\r\\n\\t\\tLS.Transform.fromMatrix4ToTransformData( sample, temp );\\r\\n\\t\\tdata[k*11] = data[k*17]; //timestamp\\r\\n\\t\\tdata.set(temp,k*11+1); //overwrite inplace (because the output is less big that the input)\\r\\n\\t}\\r\\n\\tthis.data = new Float32Array( data.subarray(0,num_samples*11) );\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Adds a new keyframe from the current value of that property\\r\\n* @method addKeyframeFromCurrent\\r\\n* @param {Number} time time stamp in seconds\\r\\n* @param {LS.SceneTree} scene \\r\\n*/\\r\\nTrack.prototype.addKeyframeFromCurrent = function( time, scene )\\r\\n{\\r\\n\\tscene = scene || LS.GlobalScene;\\r\\n\\tvar info = scene.getPropertyInfoFromPath( this._property_path );\\r\\n\\tif(!info)\\r\\n\\t\\treturn null;\\r\\n\\treturn this.addKeyframe( time, info.value );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Adds a new keyframe to this track given a value\\r\\n* @method addKeyframe\\r\\n* @param {Number} time time stamp in seconds\\r\\n* @param {*} value anything you want to store, if omited then the current value is used\\r\\n* @param {Boolean} skip_replace if you want to replace existing keyframes at same time stamp or add it next to that\\r\\n* @return {Number} index of keyframe\\r\\n*/\\r\\nTrack.prototype.addKeyframe = function( time, value, skip_replace )\\r\\n{\\r\\n\\tif(this.value_size > 1)\\r\\n\\t\\tvalue = new Float32Array( value ); //clone\\r\\n\\r\\n\\tif(this.packed_data)\\r\\n\\t\\tthis.unpackData();\\r\\n\\r\\n\\tif(!this.data)\\r\\n\\t\\tthis.data = [];\\r\\n\\r\\n\\tfor(var i = 0; i < this.data.length; ++i)\\r\\n\\t{\\r\\n\\t\\tif(this.data[i][0] < time )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(this.data[i][0] == time && !skip_replace )\\r\\n\\t\\t\\tthis.data[i][1] = value;\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.data.splice(i,0, [time,value]);\\r\\n\\t\\treturn i;\\r\\n\\t}\\r\\n\\r\\n\\tthis.data.push( [time,value] );\\r\\n\\treturn this.data.length - 1;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns a keyframe given an index\\r\\n* @method getKeyframe\\r\\n* @param {Number} index\\r\\n* @return {Array} the keyframe in [time,data] format\\r\\n*/\\r\\nTrack.prototype.getKeyframe = function( index )\\r\\n{\\r\\n\\tif(index < 0 || index >= this.data.length)\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"keyframe index out of bounds\\\");\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tif(this.packed_data)\\r\\n\\t{\\r\\n\\t\\tvar pos = index * (1 + this.value_size );\\r\\n\\t\\tif(pos > (this.data.length - this.value_size) )\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn [ this.data[pos], this.data.subarray(pos+1, pos+this.value_size+1) ];\\r\\n\\t\\t//return this.data.subarray(pos, pos+this.value_size+1) ];\\r\\n\\t}\\r\\n\\r\\n\\treturn this.data[ index ];\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the first keyframe that matches this time\\r\\n* @method getKeyframeByTime\\r\\n* @param {Number} time\\r\\n* @return {Array} keyframe in [time,value]\\r\\n*/\\r\\nTrack.prototype.getKeyframeByTime = function( time )\\r\\n{\\r\\n\\tvar index = this.findTimeIndex( time );\\r\\n\\tif(index == -1)\\r\\n\\t\\treturn;\\r\\n\\treturn this.getKeyframe( index );\\r\\n}\\r\\n\\r\\n/**\\r\\n* changes a keyframe time and rearranges it\\r\\n* @method moveKeyframe\\r\\n* @param {Number} index\\r\\n* @param {Number} new_time\\r\\n* @return {Number} new index\\r\\n*/\\r\\nTrack.prototype.moveKeyframe = function(index, new_time)\\r\\n{\\r\\n\\tif(this.packed_data)\\r\\n\\t{\\r\\n\\t\\t//TODO\\r\\n\\t\\tconsole.warn(\\\"Cannot move keyframes if packed\\\");\\r\\n\\t\\treturn -1;\\r\\n\\t}\\r\\n\\r\\n\\tif(index < 0 || index >= this.data.length)\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"keyframe index out of bounds\\\");\\r\\n\\t\\treturn -1;\\r\\n\\t}\\r\\n\\r\\n\\tvar new_index = this.findTimeIndex( new_time );\\r\\n\\tvar keyframe = this.data[ index ];\\r\\n\\tvar old_time = keyframe[0];\\r\\n\\tif(old_time == new_time)\\r\\n\\t\\treturn index;\\r\\n\\tkeyframe[0] = new_time; //set time\\r\\n\\tif(old_time > new_time)\\r\\n\\t\\tnew_index += 1;\\r\\n\\tif(index == new_index)\\r\\n\\t{\\r\\n\\t\\t//console.warn(\\\"same index\\\");\\r\\n\\t\\treturn index;\\r\\n\\t}\\r\\n\\r\\n\\t//extract\\r\\n\\tthis.data.splice(index, 1);\\r\\n\\t//reinsert\\r\\n\\tindex = this.addKeyframe( keyframe[0], keyframe[1], true );\\r\\n\\r\\n\\tthis.sortKeyframes();\\r\\n\\treturn index;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Sometimes when moving keyframes they could end up not sorted by timestamp, which will cause problems when sampling, to avoid it, we can force to sort all keyframes\\r\\n* @method sortKeyframes\\r\\n*/\\r\\nTrack.prototype.sortKeyframes = function()\\r\\n{\\r\\n\\tif(this.packed_data)\\r\\n\\t{\\r\\n\\t\\tthis.unpackData();\\r\\n\\t\\tthis.sortKeyframes();\\r\\n\\t\\tthis.packData();\\r\\n\\t}\\r\\n\\tthis.data.sort( function(a,b){ return a[0] - b[0]; });\\r\\n}\\r\\n\\r\\n/**\\r\\n* removes one keyframe\\r\\n* @method removeKeyframe\\r\\n* @param {Number} index\\r\\n*/\\r\\nTrack.prototype.removeKeyframe = function(index)\\r\\n{\\r\\n\\tif(this.packed_data)\\r\\n\\t\\tthis.unpackData();\\r\\n\\r\\n\\tif(index < 0 || index >= this.data.length)\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"keyframe index out of bounds\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tthis.data.splice(index, 1);\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the number of keyframes\\r\\n* @method getNumberOfKeyframes\\r\\n*/\\r\\n\\r\\nTrack.prototype.getNumberOfKeyframes = function()\\r\\n{\\r\\n\\tif(!this.data || this.data.length == 0)\\r\\n\\t\\treturn 0;\\r\\n\\r\\n\\tif(this.packed_data)\\r\\n\\t\\treturn this.data.length / (1 + this.value_size );\\r\\n\\treturn this.data.length;\\r\\n}\\r\\n\\r\\n//check for the last sample time\\r\\nTrack.prototype.computeDuration = function()\\r\\n{\\r\\n\\tif(!this.data || this.data.length == 0)\\r\\n\\t\\treturn 0;\\r\\n\\r\\n\\tif(this.packed_data)\\r\\n\\t{\\r\\n\\t\\tvar time = this.data[ this.data.length - 2 - this.value_size ];\\r\\n\\t\\tthis.duration = time;\\r\\n\\t\\treturn time;\\r\\n\\t}\\r\\n\\r\\n\\t//not typed\\r\\n\\tvar last = this.data[ this.data.length - 1 ];\\r\\n\\tif(last)\\r\\n\\t\\treturn last[0];\\r\\n\\treturn 0;\\r\\n}\\r\\n\\r\\nTrack.prototype.isInterpolable = function()\\r\\n{\\r\\n\\tif( this.value_size > 0 || LS.Interpolators[ this._type ] )\\r\\n\\t\\treturn true;\\r\\n\\treturn false;\\r\\n}\\r\\n\\r\\n/**\\r\\n* takes all the keyframes and stores them inside a typed-array so they are faster to store in server or work with\\r\\n* @method packData\\r\\n*/\\r\\nTrack.prototype.packData = function()\\r\\n{\\r\\n\\tif(!this.data || this.data.length == 0)\\r\\n\\t\\treturn 0;\\r\\n\\r\\n\\tif(this.packed_data)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(this.value_size == 0)\\r\\n\\t\\treturn; //cannot be packed (bools and strings cannot be packed)\\r\\n\\r\\n\\tvar offset = this.value_size + 1;\\r\\n\\tvar data = this.data;\\r\\n\\tvar typed_data = new Float32Array( data.length * offset );\\r\\n\\r\\n\\tfor(var i = 0; i < data.length; ++i)\\r\\n\\t{\\r\\n\\t\\ttyped_data[i*offset] = data[i][0];\\r\\n\\t\\tif( this.value_size == 1 )\\r\\n\\t\\t\\ttyped_data[i*offset+1] = data[i][1];\\r\\n\\t\\telse\\r\\n\\t\\t\\ttyped_data.set( data[i][1], i*offset+1 );\\r\\n\\t}\\r\\n\\r\\n\\tthis.data = typed_data;\\r\\n\\tthis.packed_data = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* takes all the keyframes and unpacks them so they are in a simple array, easier to work with\\r\\n* @method unpackData\\r\\n*/\\r\\nTrack.prototype.unpackData = function()\\r\\n{\\r\\n\\tif(!this.data || this.data.length == 0)\\r\\n\\t\\treturn 0;\\r\\n\\r\\n\\tif(!this.packed_data)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar offset = this.value_size + 1;\\r\\n\\tvar typed_data = this.data;\\r\\n\\tvar data = Array( typed_data.length / offset );\\r\\n\\r\\n\\tfor(var i = 0; i < typed_data.length; i += offset )\\r\\n\\t\\tdata[i/offset] = [ typed_data[i], typed_data.subarray( i+1, i+offset ) ];\\r\\n\\r\\n\\tthis.data = data;\\r\\n\\tthis.packed_data = false;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns nearest index of keyframe with time equal or less to specified time (Dichotimic search)\\r\\n* @method findTimeIndex\\r\\n* @param {number} time\\r\\n* @return {number} the nearest index (lower-bound)\\r\\n*/\\r\\nTrack.prototype.findTimeIndex = function(time)\\r\\n{\\r\\n\\tvar data = this.data;\\r\\n\\tif(!data || data.length == 0)\\r\\n\\t\\treturn -1;\\r\\n\\r\\n\\tif(this.packed_data)\\r\\n\\t{\\r\\n\\t\\tvar offset = this.value_size + 1; //data size plus timestamp\\r\\n\\t\\tvar l = data.length;\\r\\n\\t\\tvar n = l / offset; //num samples\\r\\n\\t\\tvar imin = 0;\\r\\n\\t\\tvar imid = 0;\\r\\n\\t\\tvar imax = n;\\r\\n\\r\\n\\t\\tif(n == 0)\\r\\n\\t\\t\\treturn -1;\\r\\n\\t\\tif(n == 1)\\r\\n\\t\\t\\treturn 0;\\r\\n\\r\\n\\t\\t//time out of duration\\r\\n\\t\\tif( data[ (imax - 1) * offset ] < time )\\r\\n\\t\\t\\treturn (imax - 1);\\r\\n\\r\\n\\t\\t//dichotimic search\\r\\n\\t\\t// continue searching while [imin,imax] are continuous\\r\\n\\t\\twhile (imax >= imin)\\r\\n\\t\\t{\\r\\n\\t\\t\\t// calculate the midpoint for roughly equal partition\\r\\n\\t\\t\\timid = ((imax + imin)*0.5)|0;\\r\\n\\t\\t\\tvar t = data[ imid * offset ]; //get time\\r\\n\\t\\t\\tif( t == time )\\r\\n\\t\\t\\t\\treturn imid; \\r\\n\\t\\t\\t//when there are no more elements to search\\r\\n\\t\\t\\tif( imin == (imax - 1) )\\r\\n\\t\\t\\t\\treturn imin;\\r\\n\\t\\t\\t// determine which subarray to search\\r\\n\\t\\t\\tif (t < time)\\r\\n\\t\\t\\t\\t// change min index to search upper subarray\\r\\n\\t\\t\\t\\timin = imid;\\r\\n\\t\\t\\telse \\r\\n\\t\\t\\t\\t// change max index to search lower subarray\\r\\n\\t\\t\\t\\timax = imid;\\r\\n\\t\\t}\\r\\n\\t\\treturn imid;\\r\\n\\t}\\r\\n\\r\\n\\t//unpacked data\\r\\n\\tvar n = data.length; //num samples\\r\\n\\tvar imin = 0;\\r\\n\\tvar imid = 0;\\r\\n\\tvar imax = n;\\r\\n\\r\\n\\tif(n == 0)\\r\\n\\t\\treturn -1;\\r\\n\\tif(n == 1)\\r\\n\\t\\treturn 0;\\r\\n\\r\\n\\t//time out of duration\\r\\n\\tif( data[ (imax - 1) ][0] < time )\\r\\n\\t\\treturn (imax - 1);\\r\\n\\r\\n\\twhile (imax >= imin)\\r\\n\\t{\\r\\n\\t\\t// calculate the midpoint for roughly equal partition\\r\\n\\t\\timid = ((imax + imin)*0.5)|0;\\r\\n\\t\\tvar t = data[ imid ][0]; //get time\\r\\n\\t\\tif( t == time )\\r\\n\\t\\t\\treturn imid; \\r\\n\\t\\t//when there are no more elements to search\\r\\n\\t\\tif( imin == (imax - 1) )\\r\\n\\t\\t\\treturn imin;\\r\\n\\t\\t// determine which subarray to search\\r\\n\\t\\tif (t < time)\\r\\n\\t\\t\\t// change min index to search upper subarray\\r\\n\\t\\t\\timin = imid;\\r\\n\\t\\telse \\r\\n\\t\\t\\t// change max index to search lower subarray\\r\\n\\t\\t\\timax = imid;\\r\\n\\t}\\r\\n\\r\\n\\treturn imid;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Samples the data in one time, taking into account interpolation.\\r\\n* Warning: if no result container is provided the same container is reused between samples to avoid garbage, be careful.\\r\\n* @method getSample\\r\\n* @param {number} time\\r\\n* @param {number} interpolation [optional] the interpolation method could be LS.NONE, LS.LINEAR, LS.CUBIC\\r\\n* @param {*} result [optional] the container where to store the data (in case is an array). IF NOT CONTAINER IS PROVIDED THE SAME ONE IS RETURNED EVERY TIME!\\r\\n* @return {*} data\\r\\n*/\\r\\nTrack.prototype.getSample = function( time, interpolate, result )\\r\\n{\\r\\n\\tif(!this.data || this.data.length === 0)\\r\\n\\t\\treturn undefined;\\r\\n\\r\\n\\tif(this.packed_data)\\r\\n\\t\\treturn this.getSamplePacked( time, interpolate, result );\\r\\n\\treturn this.getSampleUnpacked( time, interpolate, result );\\r\\n}\\r\\n\\r\\n//used when sampling from a unpacked track (where data is an array of arrays)\\r\\nTrack.prototype.getSampleUnpacked = function( time, interpolate, result )\\r\\n{\\r\\n\\ttime = Math.clamp( time, 0, this.duration );\\r\\n\\r\\n\\tvar index = this.findTimeIndex( time );\\r\\n\\tif(index === -1)\\r\\n\\t\\tindex = 0;\\r\\n\\r\\n\\tvar index_a = index;\\r\\n\\tvar index_b = index + 1;\\r\\n\\tvar data = this.data;\\r\\n\\tvar value_size = this.value_size;\\r\\n\\r\\n\\tinterpolate = interpolate && this.interpolation && (this.value_size > 0 || LS.Interpolators[ this._type ] );\\r\\n\\r\\n\\tif(!interpolate || (data.length == 1) || index_b == data.length || (index_a == 0 && this.data[0][0] > time)) //(index_b == this.data.length && !this.looped)\\r\\n\\t\\treturn this.data[ index ][1];\\r\\n\\r\\n\\tvar a = data[ index_a ];\\r\\n\\tvar b = data[ index_b ];\\r\\n\\r\\n\\tvar t = (b[0] - time) / (b[0] - a[0]);\\r\\n\\r\\n\\t//multiple data\\r\\n\\tif( value_size > 1 )\\r\\n\\t{\\r\\n\\t\\tresult = result || this._result;\\r\\n\\t\\tif( !result || result.length != value_size )\\r\\n\\t\\t\\tresult = this._result = new Float32Array( value_size );\\r\\n\\t}\\r\\n\\r\\n\\tif(this.interpolation === LS.LINEAR)\\r\\n\\t{\\r\\n\\t\\tif( value_size == 1 )\\r\\n\\t\\t\\treturn a[1] * t + b[1] * (1-t);\\r\\n\\r\\n\\t\\treturn LS.Animation.interpolateLinear( a[1], b[1], t, result, this._type, value_size, this );\\r\\n\\t}\\r\\n\\telse if(this.interpolation === LS.CUBIC)\\r\\n\\t{\\r\\n\\t\\t//cubic not implemented for interpolators\\r\\n\\t\\tif(value_size === 0 && LS.Interpolators[ this._type ] )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar func = LS.Interpolators[ this._type ];\\r\\n\\t\\t\\tvar r = func( a[1], b[1], t, this._last_value );\\r\\n\\t\\t\\tthis._last_value = r;\\r\\n\\t\\t\\treturn r;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar pre_a = index > 0 ? data[ index - 1 ] : a;\\r\\n\\t\\tvar post_b = index < data.length - 2 ? data[ index + 2 ] : b;\\r\\n\\r\\n\\t\\tif(value_size === 1)\\r\\n\\t\\t\\treturn Animation.EvaluateHermiteSpline(a[1],b[1],pre_a[1],post_b[1], 1 - t );\\r\\n\\r\\n\\t\\tresult = Animation.EvaluateHermiteSplineVector( a[1], b[1], pre_a[1], post_b[1], 1 - t, result );\\r\\n\\r\\n\\t\\tif(this._type_index == Track.QUAT)\\r\\n\\t\\t{\\r\\n\\t\\t\\tquat.slerp( result, b[1], a[1], t ); //force quats without CUBIC interpolation\\r\\n\\t\\t\\tquat.normalize( result, result );\\r\\n\\t\\t}\\r\\n\\t\\telse if(this._type_index == Track.TRANS10)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar rotR = result.subarray(3,7);\\r\\n\\t\\t\\tvar rotA = a[1].subarray(3,7);\\r\\n\\t\\t\\tvar rotB = b[1].subarray(3,7);\\r\\n\\t\\t\\tquat.slerp( rotR, rotB, rotA, t );\\r\\n\\t\\t\\tquat.normalize( rotR, rotR );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn result;\\r\\n\\t}\\r\\n\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n//used when sampling from a packed track (where data is a typed-array)\\r\\nTrack.prototype.getSamplePacked = function( time, interpolate, result )\\r\\n{\\r\\n\\ttime = Math.clamp( time, 0, this.duration );\\r\\n\\r\\n\\tvar index = this.findTimeIndex( time );\\r\\n\\tif(index == -1)\\r\\n\\t\\tindex = 0;\\r\\n\\r\\n\\tvar value_size = this.value_size;\\r\\n\\tvar offset = (value_size+1);\\r\\n\\tvar index_a = index;\\r\\n\\tvar index_b = index + 1;\\r\\n\\tvar data = this.data;\\r\\n\\tvar num_keyframes = data.length / offset;\\r\\n\\r\\n\\tinterpolate = interpolate && this.interpolation && (value_size > 0 || LS.Interpolators[ this._type ] );\\r\\n\\r\\n\\tif( !interpolate || num_keyframes == 1 || index_b == num_keyframes || (index_a == 0 && this.data[0] > time)) //(index_b == this.data.length && !this.looped)\\r\\n\\t\\treturn this.getKeyframe( index )[1];\\r\\n\\r\\n\\t//multiple data\\r\\n\\tif( value_size > 1 )\\r\\n\\t{\\r\\n\\t\\tresult = result || this._result;\\r\\n\\t\\tif( !result || result.length != value_size )\\r\\n\\t\\t\\tresult = this._result = new Float32Array( value_size );\\r\\n\\t}\\r\\n\\r\\n\\tvar a = data.subarray( index_a * offset, (index_a + 1) * offset );\\r\\n\\tvar b = data.subarray( index_b * offset, (index_b + 1) * offset );\\r\\n\\r\\n\\tvar t = (b[0] - time) / (b[0] - a[0]);\\r\\n\\r\\n\\tif(this.interpolation === LS.LINEAR)\\r\\n\\t{\\r\\n\\t\\tif( value_size == 1 ) //simple case\\r\\n\\t\\t\\treturn a[1] * t + b[1] * (1-t);\\r\\n\\r\\n\\t\\tvar a_data = a.subarray(1, value_size + 1 );\\r\\n\\t\\tvar b_data = b.subarray(1, value_size + 1 );\\r\\n\\t\\treturn LS.Animation.interpolateLinear( a_data, b_data, t, result, this._type, value_size, this );\\r\\n\\t}\\r\\n\\telse if(this.interpolation === LS.CUBIC)\\r\\n\\t{\\r\\n\\t\\tif( value_size === 0 ) //CUBIC not supported in interpolators\\r\\n\\t\\t\\treturn a[1];\\r\\n\\r\\n\\t\\tvar pre_a = index > 0 ? data.subarray( (index-1) * offset, (index) * offset ) : a;\\r\\n\\t\\tvar post_b = index_b < (num_keyframes - 1) ? data.subarray( (index_b+1) * offset, (index_b+2) * offset ) : b;\\r\\n\\r\\n\\t\\tif( value_size === 1 )\\r\\n\\t\\t\\treturn Animation.EvaluateHermiteSpline( a[1], b[1], pre_a[1], post_b[1], 1 - t );\\r\\n\\r\\n\\t\\tvar a_value = a.subarray(1,offset);\\r\\n\\t\\tvar b_value = b.subarray(1,offset);\\r\\n\\r\\n\\t\\tresult = Animation.EvaluateHermiteSplineVector( a_value, b_value, pre_a.subarray(1,offset), post_b.subarray(1,offset), 1 - t, result );\\r\\n\\r\\n\\t\\tif(this._type_index == Track.QUAT )\\r\\n\\t\\t{\\r\\n\\t\\t\\tquat.slerp( result, b_value, a_value, t );\\r\\n\\t\\t\\tquat.normalize( result, result ); //is necesary?\\r\\n\\t\\t}\\r\\n\\t\\telse if(this._type_index == Track.TRANS10 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar rotR = result.subarray(3,7);\\r\\n\\t\\t\\tvar rotA = a_value.subarray(3,7);\\r\\n\\t\\t\\tvar rotB = b_value.subarray(3,7);\\r\\n\\t\\t\\tquat.slerp( rotR, rotB, rotA, t );\\r\\n\\t\\t\\tquat.normalize( rotR, rotR ); //is necesary?\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn result;\\r\\n\\t}\\r\\n\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns information about the object being affected by this track based on its locator\\r\\n* the object contains a reference to the object, the property name, the type of the data\\r\\n* @method getPropertyInfo\\r\\n* @param {LS.Scene} scene [optional]\\r\\n* @return {Object} an object with the info { target, name, type, value }\\r\\n*/\\r\\nTrack.prototype.getPropertyInfo = function( scene )\\r\\n{\\r\\n\\tscene = scene || LS.GlobalScene;\\r\\n\\r\\n\\treturn scene.getPropertyInfo( this.property );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the node to which this track is affecting (in case it is a node, if it is something else it returns null)\\r\\n* @method getPropertyNode\\r\\n* @param {LS.Scene} scene [optional]\\r\\n* @return {LS.SceneNode} the node being affected by the track\\r\\n*/\\r\\nTrack.prototype.getPropertyNode = function( scene )\\r\\n{\\r\\n\\treturn (scene || LS.GlobalScene).getNode( this.property.split(\\\"/\\\")[0] );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* returns an array containing N samples for this property over time using the interpolation of the track\\r\\n* @method getSampledData\\r\\n* @param {Number} start_time when to start sampling\\r\\n* @param {Number} end_time when to finish sampling\\r\\n* @param {Number} num_samples the number of samples\\r\\n* @return {Array} an array containing all the samples\\r\\n*/\\r\\nTrack.prototype.getSampledData = function( start_time, end_time, num_samples )\\r\\n{\\r\\n\\tvar delta = (end_time - start_time) / num_samples;\\r\\n\\tif(delta <= 0)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar samples = [];\\r\\n\\tfor(var i = 0; i < num_samples; ++i)\\r\\n\\t{\\r\\n\\t\\tvar t = i * delta + start_time;\\r\\n\\t\\tvar sample = this.getSample( t, true );\\r\\n\\t\\tif(this.value_size > 1)\\r\\n\\t\\t\\tsample = new sample.constructor( sample );\\r\\n\\t\\tsamples.push(sample);\\r\\n\\t}\\r\\n\\r\\n\\treturn samples;\\r\\n}\\r\\n\\r\\nAnimation.interpolateLinear = function( a, b, t, result, type, value_size, track )\\r\\n{\\r\\n\\tif(value_size == 1)\\r\\n\\t\\treturn a * t + b * (1-t);\\r\\n\\r\\n\\tif( LS.Interpolators[ type ] )\\r\\n\\t{\\r\\n\\t\\tvar func = LS.Interpolators[ type ];\\r\\n\\t\\tvar r = func( a, b, t, track._last_v );\\r\\n\\t\\ttrack._last_v = r;\\r\\n\\t\\treturn r;\\r\\n\\t}\\r\\n\\r\\n\\tresult = result || track._result;\\r\\n\\r\\n\\tif(!result || result.length != value_size)\\r\\n\\t\\tresult = track._result = new Float32Array( value_size );\\r\\n\\r\\n\\tvar type_index = LS.TYPES_INDEX[ type ];\\r\\n\\r\\n\\tswitch( type_index )\\r\\n\\t{\\r\\n\\t\\tcase Track.QUAT:\\r\\n\\t\\t\\tquat.slerp( result, b, a, t );\\r\\n\\t\\t\\tquat.normalize( result, result );\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase Track.TRANS10: \\r\\n\\t\\t\\tfor(var i = 0; i < 3; i++) //this.value_size should be 10\\r\\n\\t\\t\\t\\tresult[i] = a[i] * t + b[i] * (1-t);\\r\\n\\t\\t\\tfor(var i = 7; i < 10; i++) //this.value_size should be 10\\r\\n\\t\\t\\t\\tresult[i] = a[i] * t + b[i] * (1-t);\\r\\n\\t\\t\\tvar rotA = a.subarray(3,7);\\r\\n\\t\\t\\tvar rotB = b.subarray(3,7);\\r\\n\\t\\t\\tvar rotR = result.subarray(3,7);\\r\\n\\t\\t\\tquat.slerp( rotR, rotB, rotA, t );\\r\\n\\t\\t\\tquat.normalize( rotR, rotR );\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\tfor(var i = 0; i < value_size; i++)\\r\\n\\t\\t\\t\\tresult[i] = a[i] * t + b[i] * (1-t);\\r\\n\\t}\\r\\n\\treturn result;\\r\\n}\\r\\n\\r\\nAnimation.EvaluateHermiteSpline = function( p0, p1, pre_p0, post_p1, s )\\r\\n{\\r\\n\\tvar s2 = s * s;\\r\\n\\tvar s3 = s2 * s;\\r\\n\\tvar h1 = 2*s3 - 3*s2 + 1; // calculate basis function 1\\r\\n\\tvar h2 = -2*s3 + 3*s2; // calculate basis function 2\\r\\n\\tvar h3 = s3 - 2*s2 + s; // calculate basis function 3\\r\\n\\tvar h4 = s3 - s2; // calculate basis function 4\\r\\n\\tvar t0 = p1 - pre_p0;\\r\\n\\tvar t1 = post_p1 - p0;\\r\\n\\r\\n\\treturn h1 * p0 + h2 * p1 + h3 * t0 + h4 * t1;\\r\\n}\\r\\n\\r\\nAnimation.EvaluateHermiteSplineVector = function( p0, p1, pre_p0, post_p1, s, result )\\r\\n{\\r\\n\\tresult = result || new Float32Array( result.length );\\r\\n\\r\\n\\tvar s2 = s * s;\\r\\n\\tvar s3 = s2 * s;\\r\\n\\tvar h1 = 2*s3 - 3*s2 + 1; // calculate basis function 1\\r\\n\\tvar h2 = -2*s3 + 3*s2; // calculate basis function 2\\r\\n\\tvar h3 = s3 - 2*s2 + s; // calculate basis function 3\\r\\n\\tvar h4 = s3 - s2; // calculate basis function 4\\r\\n\\r\\n\\tfor(var i = 0, l = result.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar t0 = p1[i] - pre_p0[i];\\r\\n\\t\\tvar t1 = post_p1[i] - p0[i];\\r\\n\\t\\tresult[i] = h1 * p0[i] + h2 * p1[i] + h3 * t0 + h4 * t1;\\r\\n\\t}\\r\\n\\r\\n\\treturn result;\\r\\n}\\r\\n\\r\\nLS.registerResourceClass( Animation );\\r\\n\\r\\n//extra interpolators ***********************************\\r\\nLS.Interpolators = {};\\r\\n\\r\\nLS.Interpolators[\\\"texture\\\"] = function( a, b, t, last )\\r\\n{\\r\\n\\tvar texture_a = a ? LS.getTexture( a ) : null;\\r\\n\\tvar texture_b = b ? LS.getTexture( b ) : null;\\r\\n\\r\\n\\tif(a && !texture_a && a[0] != \\\":\\\" )\\r\\n\\t\\tLS.ResourcesManager.load(a);\\r\\n\\tif(b && !texture_b && b[0] != \\\":\\\" )\\r\\n\\t\\tLS.ResourcesManager.load(b);\\r\\n\\r\\n\\tvar texture = texture_a || texture_b;\\r\\n\\r\\n\\tvar black = gl.textures[\\\":black\\\"];\\r\\n\\tif(!black)\\r\\n\\t\\tblack = gl.textures[\\\":black\\\"] = new GL.Texture(1,1, { format: gl.RGB, pixel_data: [0,0,0], filter: gl.NEAREST });\\r\\n\\r\\n\\tif(!texture)\\r\\n\\t\\treturn black;\\r\\n\\r\\n\\tvar w = texture ? texture.width : 256;\\r\\n\\tvar h = texture ? texture.height : 256;\\r\\n\\r\\n\\tif(!texture_a)\\r\\n\\t\\ttexture_a = black;\\r\\n\\tif(!texture_b)\\r\\n\\t\\ttexture_b = black;\\r\\n\\r\\n\\tif(!last || last.width != w || last.height != h || last.format != texture.format )\\r\\n\\t\\tlast = new GL.Texture( w, h, { format: texture.format, type: texture.type, filter: gl.LINEAR } );\\r\\n\\r\\n\\tvar shader = gl.shaders[\\\":interpolate_texture\\\"];\\r\\n\\tif(!shader)\\r\\n\\t\\tshader = gl.shaders[\\\":interpolate_texture\\\"] = GL.Shader.createFX(\\\"color = mix( texture2D( u_texture_b, uv ), color , u_factor );\\\", \\\"uniform sampler2D u_texture_b; uniform float u_factor;\\\" );\\r\\n\\r\\n\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\tlast.drawTo( function() {\\r\\n\\t\\tgl.clearColor(0,0,0,0);\\r\\n\\t\\tgl.clear( gl.COLOR_BUFFER_BIT );\\r\\n\\t\\ttexture_b.bind(1);\\r\\n\\t\\ttexture_a.toViewport( shader, { u_texture_b: 1, u_factor: t } );\\r\\n\\t});\\r\\n\\r\\n\\treturn last;\\r\\n}\\r\\n\\r\\n///@FILE:../src/resources/skeletalAnimation.js\\r\\n//standalone class for skeletal animations\\r\\n\\r\\nfunction lerp(a,b,f) { return a*(1.0-f)+b*f; }\\r\\n\\r\\nif(!Math.lerp)\\r\\n\\tMath.lerp = lerp;\\r\\n\\r\\nfunction Skeleton()\\r\\n{\\r\\n\\tthis.bones = []; //array of bones\\r\\n\\tthis.global_bone_matrices = []; //internal array of mat4\\r\\n\\tthis.bones_by_name = new Map(); //map of nodenames and index in the bones array\\r\\n}\\r\\n\\r\\n//Skeleton.EXTENSION = \\\"skanim\\\";\\r\\n\\r\\n\\r\\nfunction Bone()\\r\\n{\\r\\n\\tthis.name = \\\"\\\";\\r\\n\\tthis.model = mat4.create();\\r\\n\\tthis.parent = -1;\\r\\n\\tthis.layer = 0;\\r\\n\\tthis.num_children = 0;\\r\\n\\tthis.children = new Int8Array(16);\\r\\n}\\r\\n\\r\\nBone.prototype.serialize = function()\\r\\n{\\r\\n\\treturn {\\r\\n\\t\\tname: this.name,\\r\\n\\t\\tmodel: typedArrayToArray( this.model ),\\r\\n\\t\\tparent: this.parent,\\r\\n\\t\\tlayer: this.layer,\\r\\n\\t\\tchildren: this.num_children ? typedArrayToArray( this.children.subarray(0,this.num_children) ) : null\\r\\n\\t};\\r\\n}\\r\\n\\r\\nBone.prototype.configure = function(o)\\r\\n{\\r\\n\\tthis.name = o.name;\\r\\n\\tthis.model.set( o.model );\\r\\n\\tthis.parent = o.parent;\\r\\n\\tthis.layer = o.layer;\\r\\n\\tthis.num_children = 0;\\r\\n\\tif(o.children)\\r\\n\\t{\\r\\n\\t\\tthis.children.set(o.children);\\r\\n\\t\\tif(o.children.constructor === Array)\\r\\n\\t\\t\\tthis.num_children = o.children.length;\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.num_children = o.num_children;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nBone.prototype.copyFrom = Bone.prototype.configure;\\r\\n\\r\\nSkeleton.Bone = Bone;\\r\\n\\r\\n//given a bone name and matrix, it multiplies the matrix to the bone\\r\\nSkeleton.prototype.applyTransformToBones = function(root, transform)\\r\\n{\\r\\n\\tvar bone = this.getBone(root);\\r\\n\\tif (!bone)\\r\\n\\t\\treturn;\\r\\n\\tmat4.multiply( bone.model, bone.model, transform );\\r\\n};\\r\\n\\r\\nSkeleton.prototype.getBone = function(name)\\r\\n{\\r\\n\\treturn this.bones[ this.bones_by_name.get(name) ];\\r\\n}\\r\\n\\r\\nSkeleton.identity = mat4.create();\\r\\n\\r\\nSkeleton.prototype.getBoneMatrix = function( name, local )\\r\\n{\\r\\n\\tif(local === undefined)\\r\\n\\t\\tlocal = true;\\r\\n\\tvar index = this.bones_by_name.get(name);\\r\\n\\tif( index === undefined )\\r\\n\\t\\treturn Skeleton.identity;\\r\\n\\tif(local)\\r\\n\\t\\treturn this.bones[ index ].model;\\r\\n\\treturn this.global_bone_matrices[ index ];\\r\\n}\\r\\n\\r\\nSkeleton.temp_mat4 = mat4.create();\\r\\nSkeleton.temp_mat43 = Skeleton.temp_mat4.subarray(0,12);\\r\\n\\r\\n//fills the array with the bones ready for the shader\\r\\n//simplify allows to store as mat4x3 instead of mat4x4 (because the last column is always 0,0,0,1)\\r\\nSkeleton.prototype.computeFinalBoneMatrices = function( bone_matrices, mesh, simplify )\\r\\n{\\r\\n\\tif(!this.bones.length || !mesh || !mesh.bones)\\r\\n\\t\\treturn bone_matrices || [];\\r\\n\\r\\n\\tthis.updateGlobalMatrices();\\r\\n\\r\\n\\tvar size = simplify ? mesh.bones.length * 12 : mesh.bones.length * 16;\\r\\n\\r\\n\\tif(!bone_matrices || bone_matrices.length != size )\\r\\n\\t\\tbone_matrices = new Float32Array( size );\\r\\n\\r\\n\\tif(simplify) //convert to mat4x3\\r\\n\\t{\\r\\n\\t\\tvar m = Skeleton.temp_mat4;\\r\\n\\t\\tvar m43 = Skeleton.temp_mat43;\\r\\n\\t\\tfor (var i = 0; i < mesh.bones.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar bone_info = mesh.bones[i];\\r\\n\\t\\t\\tmat4.multiply( temp_mat4, this.getBoneMatrix( bone_info[0], false ), bone_info[1] ); //use globals\\r\\n\\t\\t\\tmat4.transpose( temp_mat4, temp_mat4 );\\r\\n\\t\\t\\tbone_matrices.set(m43,i*12);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tfor (var i = 0; i < mesh.bones.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar bone_info = mesh.bones[i];\\r\\n\\t\\t\\tvar m = bone_matrices.subarray(i*16,i*16+16);\\r\\n\\t\\t\\tmat4.multiply( m, this.getBoneMatrix( bone_info[0], false ), bone_info[1] ); //use globals\\r\\n\\t\\t}\\r\\n\\r\\n\\treturn bone_matrices;\\r\\n}\\r\\n\\r\\n//returns an array with the final global bone matrix in the order specified by the mesh, global_model is optional\\r\\nSkeleton.prototype.computeFinalBoneMatricesAsArray = function( bone_matrices, mesh, global_model )\\r\\n{\\r\\n\\tif(!this.bones.length || !mesh || !mesh.bones)\\r\\n\\t\\treturn bone_matrices || [];\\r\\n\\r\\n\\tthis.updateGlobalMatrices();\\r\\n\\r\\n\\tbone_matrices = bone_matrices || [];\\r\\n\\tbone_matrices.length = mesh.bones.length;\\r\\n\\r\\n\\tfor (var i = 0; i < mesh.bones.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar bone_info = mesh.bones[i];\\r\\n\\t\\tif(!bone_matrices[i])\\r\\n\\t\\t\\tbone_matrices[i] = mat4.create();\\r\\n\\t\\tvar m = bone_matrices[i];\\r\\n\\t\\tmat4.multiply( m, this.getBoneMatrix( bone_info[0], false ), bone_info[1] ); //use globals\\r\\n\\t\\tif(mesh.bind_matrix)\\r\\n\\t\\t\\tmat4.multiply( m, m, mesh.bind_matrix );\\r\\n\\t\\tif(global_model)\\r\\n\\t\\t\\tmat4.multiply( m, global_model, m );\\r\\n\\t}\\r\\n\\r\\n\\treturn bone_matrices;\\r\\n}\\r\\n\\r\\n//updates the list of global matrices according to the local matrices\\r\\nSkeleton.prototype.updateGlobalMatrices = function()\\r\\n{\\r\\n\\tvar bones = this.bones;\\r\\n\\tif(!bones.length)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar num_bones = this.bones.length;\\r\\n\\r\\n\\t//compute global matrices\\r\\n\\tthis.global_bone_matrices[0].set( bones[0].model );\\r\\n\\t//order dependant\\r\\n\\tfor (var i = 1; i < num_bones; ++i)\\r\\n\\t{\\r\\n\\t\\tvar bone = bones[i];\\r\\n\\t\\tmat4.multiply( this.global_bone_matrices[i], this.global_bone_matrices[ bone.parent ], bone.model );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//assigns a layer to a node and all its children\\r\\nSkeleton.prototype.assignLayer = function(bone, layer)\\r\\n{\\r\\n\\t//TODO\\r\\n}\\r\\n\\r\\n//for rendering the skeleton\\r\\nSkeleton.prototype.getVertices = function()\\r\\n{\\r\\n\\tif(!this.bones.length)\\r\\n\\t\\treturn null;\\r\\n\\tvar size = (this.bones.length - 1) * 3 * 2;\\r\\n\\tif(!this._vertices || this._vertices.length != size)\\r\\n\\t\\tthis._vertices = new Float32Array( size );\\r\\n\\tvar vertices = this._vertices;\\r\\n\\tfor (var i = 0; i < this.bones.length - 1; ++i)\\r\\n\\t{\\r\\n\\t\\tvar bone = this.bones[i+1];\\r\\n\\t\\tvar parent_global_matrix = this.global_bone_matrices[ bone.parent ];\\r\\n\\t\\tvar global_matrix = this.global_bone_matrices[i+1];\\r\\n\\t\\tvar v1 = vertices.subarray(i*6,i*6+3);\\r\\n\\t\\tvar v2 = vertices.subarray(i*6+3,i*6+6);\\r\\n\\t\\tmat4.getTranslation( v1, global_matrix );\\r\\n\\t\\tmat4.getTranslation( v2, parent_global_matrix );\\r\\n\\t}\\r\\n\\treturn vertices;\\r\\n}\\r\\n\\r\\nSkeleton.prototype.resizeBones = function(num)\\r\\n{\\r\\n\\tif(this.bones.length == num)\\r\\n\\t\\treturn;\\r\\n\\tif(this.bones.length > num)\\r\\n\\t{\\r\\n\\t\\tthis.bones.length = num;\\r\\n\\t\\tthis.global_bone_matrices.length = num;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar old_num = this.bones.length;\\r\\n\\tthis.bones.length = num;\\r\\n\\tfor(var i = old_num; i < num; ++i)\\r\\n\\t{\\r\\n\\t\\tthis.bones[i] = new Bone();\\r\\n\\t\\tthis.global_bone_matrices[i] = mat4.create();\\r\\n\\t}\\r\\n}\\r\\n\\r\\nSkeleton.prototype.copyFrom = function( skeleton )\\r\\n{\\r\\n\\tthis.resizeBones( skeleton.bones.length );\\r\\n\\tfor(var i = 0; i < skeleton.bones.length; ++i)\\r\\n\\t{\\r\\n\\t\\tthis.bones[i].copyFrom( skeleton.bones[i] );\\r\\n\\t\\tthis.global_bone_matrices[i].set( skeleton.global_bone_matrices[i] );\\r\\n\\t}\\r\\n\\tthis.bones_by_name = new Map( skeleton.bones_by_name );\\r\\n}\\r\\n\\r\\nSkeleton.prototype.serialize = function()\\r\\n{\\r\\n\\tvar o = {\\r\\n\\t\\tbones: [],\\r\\n\\t\\tbone_names: {}\\r\\n\\t};\\r\\n\\r\\n\\tfor(var i = 0; i < this.bones.length; ++i)\\r\\n\\t\\to.bones.push(this.bones[i].serialize());\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\nSkeleton.prototype.configure = function(o)\\r\\n{\\r\\n\\tthis.resizeBones( o.bones.length );\\r\\n\\tif(o.bones_by_name)\\r\\n\\t\\tthis.bones_by_name = new Map( o.bones_by_name );\\r\\n\\telse\\r\\n\\t\\tthis.bones_by_name.clear();\\r\\n\\tfor(var i = 0; i < o.bones.length; ++i)\\r\\n\\t{\\r\\n\\t\\tthis.bones[i].copyFrom( o.bones[i] );\\r\\n\\t\\tif(o.global_bone_matrices) //is an skeleton\\r\\n\\t\\t\\tthis.global_bone_matrices[i].set( o.global_bone_matrices[i] );\\r\\n\\t\\telse //is an object\\r\\n\\t\\t\\tthis.bones_by_name[this.bones[i].name] = i;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nvar temp_axis = vec3.create();\\r\\n\\r\\n//blends between two skeletons\\r\\nSkeleton.blend = function(a, b, w, result, layer, skip_normalize )\\r\\n{\\r\\n\\tif(a.bones.length != b.bones.length)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"skeleton must contain the same number of bones\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tw = Math.clamp(w, 0.0, 1.0);//safety\\r\\n\\r\\n\\tif (layer == 0xFF)\\r\\n\\t{\\r\\n\\t\\tif (w == 0.0)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(result == a) //nothing to do\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tresult.copyFrom(a); //copy A in Result\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tif (w == 1.0) //copy B in result\\r\\n\\t\\t{\\r\\n\\t\\t\\tresult.copyFrom(b);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif (result != a) //copy bone names\\r\\n\\t{\\r\\n\\t\\tresult.bones.length = a.bones.length;\\r\\n\\t\\tfor (var i = 0; i < result.bones.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar b = result.bones[i];\\r\\n\\t\\t\\tif(!b)\\r\\n\\t\\t\\t\\tb = new Skeleton.Bone();\\r\\n\\t\\t\\tb.copyFrom(a.bones[i]);\\r\\n\\t\\t}\\r\\n\\t\\tresult.bones_by_name = new Map(a.bones_by_name); //TODO: IMPROVE!\\r\\n\\t}\\r\\n\\r\\n\\t//blend bones locally\\r\\n\\tfor (var i = 0; i < result.bones.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar bone = result.bones[i];\\r\\n\\t\\tvar boneA = a.bones[i];\\r\\n\\t\\tvar boneB = b.bones[i];\\r\\n\\t\\t//if ( layer != 0xFF && !(bone.layer & layer) ) //not in the same layer\\r\\n\\t\\t//\\tcontinue;\\r\\n\\t\\tfor (var j = 0; j < 16; ++j)\\r\\n\\t\\t\\tbone.model[j] = Math.lerp( boneA.model[j], boneB.model[j], w);\\r\\n\\r\\n\\t\\tif(!skip_normalize)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar m = bone.model;\\r\\n\\t\\t\\t//not sure which one is the right one, row major or column major\\r\\n\\t\\t\\t//vec3.normalize(m.subarray(0,3),\\tm.subarray(0,3) );\\r\\n\\t\\t\\t//vec3.normalize(m.subarray(4,7),\\tm.subarray(4,7) );\\r\\n\\t\\t\\t//vec3.normalize(m.subarray(8,11), m.subarray(8,11) );\\r\\n\\t\\t\\t//*\\r\\n\\t\\t\\tfor(var j = 0; j < 3; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttemp_axis[0] = m[0+j]; temp_axis[1] = m[4+j]; temp_axis[2] = m[8+j];\\r\\n\\t\\t\\t\\tvec3.normalize(temp_axis,temp_axis);\\r\\n\\t\\t\\t\\tm[0+j] = temp_axis[0]; m[4+j] = temp_axis[1]; m[8+j] = temp_axis[2];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t//*/\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nSkeleton.shader_code = '\\\\n\\\\\\r\\n\\tattribute vec4 a_bone_indices;\\\\n\\\\\\r\\n\\tattribute vec4 a_weights;\\\\n\\\\\\r\\n\\tuniform mat4 u_bones[64];\\\\n\\\\\\r\\n\\tvoid computeSkinning(inout vec3 vertex, inout vec3 normal)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tvec4 v = vec4(vertex,1.0);\\\\n\\\\\\r\\n\\t\\tvertex = (u_bones[int(a_bone_indices.x)] * a_weights.x * v + \\\\n\\\\\\r\\n\\t\\t\\t\\tu_bones[int(a_bone_indices.y)] * a_weights.y * v + \\\\n\\\\\\r\\n\\t\\t\\t\\tu_bones[int(a_bone_indices.z)] * a_weights.z * v + \\\\n\\\\\\r\\n\\t\\t\\t\\tu_bones[int(a_bone_indices.w)] * a_weights.w * v).xyz;\\\\n\\\\\\r\\n\\t\\tvec4 N = vec4(normal,0.0);\\\\n\\\\\\r\\n\\t\\tnormal =\\t(u_bones[int(a_bone_indices.x)] * a_weights.x * N + \\\\n\\\\\\r\\n\\t\\t\\t\\tu_bones[int(a_bone_indices.y)] * a_weights.y * N + \\\\n\\\\\\r\\n\\t\\t\\t\\tu_bones[int(a_bone_indices.z)] * a_weights.z * N + \\\\n\\\\\\r\\n\\t\\t\\t\\tu_bones[int(a_bone_indices.w)] * a_weights.w * N).xyz;\\\\n\\\\\\r\\n\\t\\tnormal = normalize(normal);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n';\\r\\n\\r\\n//*******************************************************\\r\\n\\r\\nfunction SkeletalAnimation()\\r\\n{\\r\\n\\tthis.skeleton = new Skeleton();\\r\\n\\r\\n\\tthis.duration = 0;\\r\\n\\tthis.samples_per_second = 0;\\r\\n\\tthis.num_animated_bones = 0;\\r\\n\\tthis.num_keyframes = 0;\\r\\n\\tthis.bones_map = new Uint8Array(64); //maps from keyframe data index to bone\\r\\n\\r\\n\\tthis.keyframes = null; //mat4 array\\r\\n}\\r\\n\\r\\nSkeletalAnimation.FORMAT = { extension: \\\"skanim\\\", dataType: \\\"text\\\" };\\r\\n\\r\\n//change the skeleton to the given pose according to time\\r\\nSkeletalAnimation.prototype.assignTime = function(time, loop, interpolate, layers )\\r\\n{\\r\\n\\tif(!this.duration)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif (loop || loop === undefined)\\r\\n\\t{\\r\\n\\t\\ttime = time % this.duration;\\r\\n\\t\\tif (time < 0)\\r\\n\\t\\t\\ttime = this.duration + time;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\ttime = Math.clamp( time, 0.0, this.duration - (1.0/this.samples_per_second) );\\r\\n\\r\\n\\tif(interpolate === undefined)\\r\\n\\t\\tinterpolate = true;\\r\\n\\r\\n\\tvar v = this.samples_per_second * time;\\r\\n\\tvar index = Math.floor(v);\\r\\n\\tvar index2 = (index + 1) % this.num_keyframes;\\r\\n\\tindex = index % this.num_keyframes;\\r\\n\\tvar f = v - Math.floor(v);\\r\\n\\tvar num_animated_bones = this.num_animated_bones;\\r\\n\\r\\n\\tvar offset = 16 * num_animated_bones;\\r\\n\\tvar k = index * offset;\\r\\n\\tvar k2 = index2 * offset;\\r\\n\\tvar skeleton = this.skeleton;\\r\\n\\tvar keyframes = this.keyframes;\\r\\n\\tvar bones_map = this.bones_map;\\r\\n\\r\\n\\t//compute local bones\\r\\n\\tfor (var i = 0; i < num_animated_bones; ++i)\\r\\n\\t{\\r\\n\\t\\tvar bone_index = bones_map[i];\\r\\n\\t\\tvar bone = skeleton.bones[bone_index];\\r\\n\\t\\tvar offset = i*16;\\r\\n\\t\\t//if (layers != 0xFF && !(bone.layer & layers))\\r\\n\\t\\t//\\tcontinue;\\r\\n\\t\\tif(!interpolate)\\r\\n\\t\\t\\tbone.model.set( keyframes.subarray( k + offset, k + offset + 16) );\\r\\n\\t\\telse\\r\\n\\t\\t\\tfor (var j = 0; j < 16; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//lerp matrix\\r\\n\\t\\t\\t\\tbone.model[j] = lerp( keyframes[ k + offset + j ], keyframes[ k2 + offset + j ], f );\\r\\n\\t\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nSkeletalAnimation.prototype.resize = function( num_keyframes, num_animated_bones )\\r\\n{\\r\\n\\tthis.num_keyframes = num_keyframes;\\r\\n\\tthis.num_animated_bones = num_animated_bones;\\r\\n\\tthis.keyframes = new Float32Array( num_keyframes * num_animated_bones * 16);\\r\\n}\\r\\n\\r\\nSkeletalAnimation.prototype.fromData = function(txt)\\r\\n{\\r\\n\\tvar lines = txt.split(\\\"\\\\n\\\");\\r\\n\\tvar header = lines[0].split(\\\",\\\");\\r\\n\\tthis.duration = Number(header[0]);\\r\\n\\tthis.samples_per_second = Number(header[1]);\\r\\n\\tthis.num_keyframes = Number(header[2]);\\r\\n\\t\\r\\n\\tthis.skeleton.resizeBones( Number(header[3]) );\\r\\n\\tvar current_keyframe = 0;\\r\\n\\tfor(var i = 1; i < lines.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar line = lines[i];\\r\\n\\t\\tvar type = line[0];\\r\\n\\t\\tvar t = line.substr(1).split(\\\",\\\");\\r\\n\\t\\tif( type == 'B')\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar index = Number(t[0]);\\r\\n\\t\\t\\tvar bone = this.skeleton.bones[index];\\r\\n\\t\\t\\tbone.name = t[1];\\r\\n\\t\\t\\tbone.parent = Number(t[2]);\\r\\n\\t\\t\\tfor(var j = 0; j < 16; ++j)\\r\\n\\t\\t\\t\\tbone.model[j] = Number(t[3+j]);\\r\\n\\t\\t\\tif (bone.parent != -1)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar parent_bone = this.skeleton.bones[ bone.parent ];\\r\\n\\t\\t\\t\\tif(parent_bone.num_children >= 16)\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"too many child bones, max is 16\\\");\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tparent_bone.children[ parent_bone.num_children++ ] = index;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.skeleton.bones_by_name.set(bone.name,index);\\r\\n\\t\\t}\\r\\n\\t\\telse if( type == '@')\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.num_animated_bones = Number(t[0]);\\r\\n\\t\\t\\tfor(var j = 0; j < this.num_animated_bones; ++j)\\r\\n\\t\\t\\t\\tthis.bones_map[j] = Number(t[j+1]);\\r\\n\\t\\t\\tthis.resize( this.num_keyframes, this.num_animated_bones );\\r\\n\\t\\t}\\r\\n\\t\\telse if( type == 'K')\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pos = current_keyframe * this.num_animated_bones * 16;\\r\\n\\t\\t\\tfor(var j = 0, l = this.num_animated_bones * 16; j < l; ++j)\\r\\n\\t\\t\\t\\tthis.keyframes[ pos + j ] = Number( t[j+1] );\\r\\n\\t\\t\\tcurrent_keyframe++;\\r\\n\\t\\t}\\r\\n\\t\\telse \\r\\n\\t\\t\\tbreak;\\r\\n\\t}\\r\\n\\r\\n\\tthis.assignTime(0,false,false);\\r\\n}\\r\\n\\r\\nSkeletalAnimation.prototype.toData = function()\\r\\n{\\r\\n\\tvar str = \\\"\\\";\\r\\n\\t//this is currently done from WebGLStudio in the AnimationModule exportTakeInSKANIM\\r\\n\\tconsole.error(\\\"no toData in Skeletal Animation\\\");\\r\\n\\treturn str;\\r\\n}\\r\\n\\r\\n//so it can be used from LiteScene or Rendeer\\r\\nif(typeof(LS) != \\\"undefined\\\")\\r\\n{\\r\\n\\tLS.Skeleton = Skeleton;\\r\\n\\tLS.Classes[\\\"SkeletalAnimation\\\"] = LS.SkeletalAnimation = SkeletalAnimation;\\r\\n\\tLS.registerResourceClass( SkeletalAnimation );\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n///@FILE:../src/resources/pack.js\\r\\n///@INFO: PACK\\r\\n//defined before Prefab\\r\\n\\r\\n/**\\r\\n* Pack is an object that contain several resources, helpful when you want to carry a whole scene in one single file\\r\\n* \\r\\n* @class Pack\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nfunction Pack(o)\\r\\n{\\r\\n\\tthis.resource_names = []; \\r\\n\\tthis.metadata = null;\\r\\n\\tthis._data = {}; //the original chunks from the WBin, including the @JSON and @resource_names\\r\\n\\tthis._resources_data = {}; //every resource in arraybuffer format\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nPack.version = \\\"0.2\\\"; //used to know where the file comes from \\r\\nPack.EXTENSION = \\\"wbin\\\";\\r\\n\\r\\n/**\\r\\n* configure the pack from an unpacked WBin\\r\\n* @method configure\\r\\n* @param {Object} data an unpacked WBIN (object with every chunk)\\r\\n**/\\r\\nPack.prototype.configure = function( data )\\r\\n{\\r\\n\\tthis._data = LS.cloneObject( data );\\r\\n\\r\\n\\t//extract resource names\\r\\n\\tthis.resource_names = data[\\\"@resource_names\\\"];\\r\\n\\tthis._resources_data = {};\\r\\n\\tif(this.resource_names)\\r\\n\\t{\\r\\n\\t\\tdelete this._data[\\\"@resource_names\\\"];\\r\\n\\t\\tfor(var i in this.resource_names)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._resources_data[ this.resource_names[i] ] = data[ \\\"@RES_\\\" + i ];\\r\\n\\t\\t\\tdelete this._data[ \\\"@RES_\\\" + i ];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//store resources in LS.ResourcesManager\\r\\n\\tthis.processResources();\\r\\n}\\r\\n\\r\\nObject.defineProperty( Pack.prototype, 'bindata', {\\r\\n\\tset: function(name)\\r\\n\\t{\\r\\n\\t\\tthrow(\\\"Pack bindata cannot be assigned\\\");\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\tif(!this._original_data)\\r\\n\\t\\t\\tthis._original_data = LS.Pack.packResources( this.resource_names, this._data );\\r\\n\\t\\treturn this._original_data;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\nPack.fromBinary = function(data)\\r\\n{\\r\\n\\tif(data.constructor == ArrayBuffer)\\r\\n\\t\\tdata = WBin.load(data, true);\\r\\n\\treturn new LS.Pack(data);\\r\\n}\\r\\n\\r\\n//given a list of resources that come from the Pack (usually a wbin) it extracts, process and register them \\r\\nPack.prototype.processResources = function()\\r\\n{\\r\\n\\tif(!this.resource_names)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar pack_filename = this.fullpath || this.filename;\\r\\n\\r\\n\\t//block this resources of being loaded, this is to avoid chain reactions when a resource uses \\r\\n\\t//another one contained in this pack\\r\\n\\tfor(var i = 0; i < this.resource_names.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar resname = this.resource_names[i];\\r\\n\\t\\tif( LS.ResourcesManager.resources[ resname ] )\\r\\n\\t\\t\\tcontinue; //already loaded\\r\\n\\t\\tLS.ResourcesManager.resources_being_processed[ resname ] = true;\\r\\n\\t}\\r\\n\\r\\n\\t//process and store in LS.ResourcesManager\\r\\n\\tfor(var i = 0; i < this.resource_names.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar resname = this.resource_names[i];\\r\\n\\t\\tif( LS.ResourcesManager.resources[resname] )\\r\\n\\t\\t\\tcontinue; //already loaded\\r\\n\\r\\n\\t\\tvar resdata = this._resources_data[ resname ];\\r\\n\\t\\tif(!resdata)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"resource data in Pack is undefined, skipping it:\\\" + resname);\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\t\\tvar resource = LS.ResourcesManager.processResource( resname, resdata, { is_local: true, from_pack: pack_filename } );\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPack.prototype.setResources = function( resource_names, mark_them )\\r\\n{\\r\\n\\tthis.resource_names = [];\\r\\n\\tthis._resources_data = {};\\r\\n\\r\\n\\tvar pack_filename = this.fullpath || this.filename;\\r\\n\\r\\n\\t//get resources\\r\\n\\tfor(var i = 0; i < resource_names.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar res_name = resource_names[i];\\r\\n\\t\\tif(this.resource_names.indexOf(res_name) != -1)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar resource = LS.ResourcesManager.resources[ res_name ];\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(mark_them)\\r\\n\\t\\t\\tresource.from_pack = pack_filename;\\r\\n\\t\\tthis.resource_names.push( res_name );\\r\\n\\t}\\r\\n\\r\\n\\t//repack the pack info\\r\\n\\tthis._original_data = LS.Pack.packResources( resource_names, this.getBaseData() );\\r\\n\\tthis._modified = true;\\r\\n}\\r\\n\\r\\nPack.prototype.getBaseData = function()\\r\\n{\\r\\n\\treturn { \\\"@metadata\\\": this.metadata, \\\"@version\\\": LS.Pack.version };\\r\\n}\\r\\n\\r\\n//adds to every resource in this pack info about where it came from (the pack)\\r\\nPack.prototype.setResourcesLink = function( value )\\r\\n{\\r\\n\\tfor(var i = 0; i < this.resource_names.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar res_name = this.resource_names[i];\\r\\n\\t\\tvar resource = LS.ResourcesManager.resources[ res_name ];\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(value)\\r\\n\\t\\t\\tresource.from_pack = value;\\r\\n\\t\\telse\\r\\n\\t\\t\\tdelete resource.from_pack;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//adds a new resource (or array of resources) to this pack\\r\\nPack.prototype.addResources = function( resource_names, mark_them )\\r\\n{\\r\\n\\tif(!resource_names)\\r\\n\\t\\treturn;\\r\\n\\tif(resource_names.constructor !== Array)\\r\\n\\t\\tresource_names = [ resource_names ];\\r\\n\\tthis.setResources( this.resource_names.concat( resource_names ), mark_them );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Adds a resource to the prefab\\r\\n* @method addResource\\r\\n* @param {String} filename filename of the resource\\r\\n**/\\r\\nPack.prototype.addResource = function( filename )\\r\\n{\\r\\n\\tfilename = LS.ResourcesManager.cleanFullpath( filename );\\r\\n\\tvar index = this.resource_names.indexOf(filename);\\r\\n\\tif(index == -1)\\r\\n\\t\\tthis.resource_names.push( filename );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Remove a resource to the prefab\\r\\n* @method removeResource\\r\\n* @param {String} filename filename of the resource\\r\\n**/\\r\\nPack.prototype.removeResource = function(filename)\\r\\n{\\r\\n\\tfilename = LS.ResourcesManager.cleanFullpath( filename );\\r\\n\\tvar index = this.resource_names.indexOf(filename);\\r\\n\\tif(index != -1)\\r\\n\\t\\tthis.resource_names.splice( index, 1 );\\r\\n}\\r\\n\\r\\n/**\\r\\n* to create a WBin containing all the resource and metadata\\r\\n* @method Pack.createWBin\\r\\n* @param {String} fullpath for the pack\\r\\n* @param {Array} resource_names array with the names of all the resources to store\\r\\n* @param {Object} metadata [optional] extra data to store\\r\\n* @param {boolean} mark_them [optional] marks all the resources as if they come from a pack\\r\\n* @return object containing the pack data ready to be converted to WBin\\r\\n**/\\r\\nPack.createPack = function( filename, resource_names, extra_data, mark_them )\\r\\n{\\r\\n\\tif(!filename)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!resource_names || resource_names.constructor !== Array)\\r\\n\\t\\tthrow(\\\"Pack.createPack resources must be array with names\\\");\\r\\n\\tif(extra_data && extra_data.constructor !== Object)\\r\\n\\t\\tthrow(\\\"Pack.createPack extra_data must be an object with the chunks to store\\\");\\r\\n\\r\\n\\tfilename = filename.replace(/ /gi,\\\"_\\\");\\r\\n\\r\\n\\tvar pack = new LS.Pack();\\r\\n\\tfilename += \\\".wbin\\\";\\r\\n\\tpack.filename = filename;\\r\\n\\tif(extra_data)\\r\\n\\t\\tpack._data = extra_data;\\r\\n\\r\\n\\tpack.resource_names = resource_names;\\r\\n\\tfor(var i = 0; i < resource_names.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar res_name = resource_names[i];\\r\\n\\t\\tvar resource = LS.ResourcesManager.resources[ res_name ];\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(mark_them)\\r\\n\\t\\t\\tresource.from_pack = pack.filename;\\r\\n\\t}\\r\\n\\r\\n\\t//create the WBIN in case this pack gets stored\\r\\n\\tthis.metadata = extra_data;\\r\\n\\tvar bindata = LS.Pack.packResources( resource_names, pack.getBaseData() );\\r\\n\\tpack._original_data = bindata;\\r\\n\\r\\n\\treturn pack;\\r\\n}\\r\\n\\r\\n//Given a bunch of resource names it creates a WBin with all inside\\r\\nPack.packResources = function( resource_names, base_object )\\r\\n{\\r\\n\\tvar to_binary = base_object || {};\\r\\n\\tvar final_resource_names = [];\\r\\n\\r\\n\\tfor(var i = 0; i < resource_names.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar res_name = resource_names[i];\\r\\n\\t\\tvar resource = LS.ResourcesManager.resources[ res_name ];\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tvar data = null;\\r\\n\\t\\tif(resource._original_data) //must be string or bytes\\r\\n\\t\\t\\tdata = resource._original_data;\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar data_info = LS.Resource.getDataToStore( resource );\\r\\n\\t\\t\\tdata = data_info.data;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!data)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"Wrong data in resource\\\");\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(data.constructor === Blob || data.constructor === File)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!data.data || data.data.constructor !== ArrayBuffer )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"WBin does not support to store File or Blob, please, use ArrayBuffer\\\");\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tdata = data.data; //because files have an arraybuffer with the data if it was read\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tto_binary[\\\"@RES_\\\" + final_resource_names.length ] = data;\\r\\n\\t\\tfinal_resource_names.push( res_name );\\r\\n\\t\\t//to_binary[res_name] = data;\\r\\n\\t}\\r\\n\\r\\n\\tto_binary[\\\"@resource_names\\\"] = final_resource_names;\\r\\n\\treturn WBin.create( to_binary, \\\"Pack\\\" );\\r\\n}\\r\\n\\r\\n//just tells the resources where they come from, we cannot do that before because we didnt have the name of the pack\\r\\nPack.prototype.flagResources = function()\\r\\n{\\r\\n\\tif(!this.resource_names)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < this.resource_names.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar res_name = this.resource_names[i];\\r\\n\\t\\tvar resource = LS.ResourcesManager.resources[ res_name ];\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tresource.from_pack = this.fullpath || this.filename;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPack.prototype.getDataToStore = function()\\r\\n{\\r\\n\\treturn LS.Pack.packResources( this.resource_names, this.getBaseData() );\\r\\n}\\r\\n\\r\\nPack.prototype.checkResourceNames = function()\\r\\n{\\r\\n\\tif(!this.resource_names)\\r\\n\\t\\treturn 0;\\r\\n\\r\\n\\tvar changed = 0;\\r\\n\\r\\n\\tfor(var i = 0; i < this.resource_names.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar res_name = this.resource_names[i];\\r\\n\\t\\tvar old_name = res_name;\\r\\n\\t\\tvar resource = LS.ResourcesManager.resources[ res_name ];\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t//avoid problematic symbols\\r\\n\\t\\tif( LS.ResourcesManager.valid_resource_name_reg.test( res_name ) == false )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"Invalid filename in pack/prefab: \\\", res_name );\\r\\n\\t\\t\\tres_name = res_name.replace( /[^a-zA-Z0-9-_\\\\.\\\\/]/g, '_' );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//ensure extensions\\r\\n\\t\\tvar extension = LS.ResourcesManager.getExtension( res_name );\\r\\n\\t\\tif(!extension)\\r\\n\\t\\t{\\r\\n\\t\\t\\textension = resource.constructor.EXTENSION;\\r\\n\\t\\t\\tif(!extension)\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Resource without extension and not known default extension: \\\", res_name , resource.constructor.name );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tres_name = res_name + \\\".\\\" + extension;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(old_name == res_name)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tthis.resource_names[i] = res_name;\\r\\n\\t\\tLS.ResourcesManager.renameResource( old_name, res_name ); //force change\\r\\n\\t\\tchanged++;\\r\\n\\t}\\r\\n\\r\\n\\tif(changed)\\r\\n\\t\\tLS.ResourcesManager.resourceModified( this );\\r\\n\\r\\n\\treturn changed;\\r\\n}\\r\\n\\r\\nPack.prototype.onResourceRenamed = function( old_name, new_name, resource )\\r\\n{\\r\\n\\tif(!this.resource_names)\\r\\n\\t\\treturn;\\r\\n\\tvar index = this.resource_names[ old_name ];\\r\\n\\tif( index == -1 )\\r\\n\\t\\treturn;\\r\\n\\tthis.resource_names[ index ] = new_name;\\r\\n\\tLS.ResourcesManager.resourceModified( this );\\r\\n}\\r\\n\\r\\nPack.prototype.containsResources = function()\\r\\n{\\r\\n\\treturn this.resource_names && this.resource_names.length > 0 ? true : false;\\r\\n}\\r\\n\\r\\nPack.prototype.getSizeInBytes = function()\\r\\n{\\r\\n\\tif(this._original_data)\\r\\n\\t\\treturn this._original_data.byteLength;\\r\\n\\treturn 0;\\r\\n}\\r\\n\\r\\nLS.Pack = Pack;\\r\\nLS.registerResourceClass( Pack );\\r\\n\\r\\n\\r\\n///@FILE:../src/resources/prefab.js\\r\\n///@INFO: PACK\\r\\n/**\\r\\n* Prefab work in two ways: \\r\\n* - It can contain a node structure and all the associated resources (textures, meshes, animations, etc)\\r\\n* - When a node in the scene was created from a Prefab, the prefab is loaded so the associated resources are recovered, but the node structure is not modified.\\r\\n* \\r\\n* @class Prefab\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nfunction Prefab( o, filename )\\r\\n{\\r\\n\\tthis.filename = filename || null; //base file\\r\\n\\tthis.fullpath = filename || null; //full path \\r\\n\\tthis.resource_names = []; \\r\\n\\tthis.prefab_json = null;\\r\\n\\tthis.prefab_data = null; //json object\\r\\n\\tthis._resources_data = {}; //data as it was loaded from the WBin\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nPrefab.version = \\\"0.2\\\"; //used to know where the file comes from \\r\\nPrefab.EXTENSION = \\\"wbin\\\";\\r\\n\\r\\n/**\\r\\n* assign the json object\\r\\n* @method setData\\r\\n* @param {object|SceneNode} data\\r\\n**/\\r\\nPrefab.prototype.setData = function(data)\\r\\n{\\r\\n\\tif( data && data.constructor === LS.SceneNode )\\r\\n\\t\\tdata = data.serialize();\\r\\n\\tdata.object_class = \\\"SceneNode\\\";\\r\\n\\tthis.prefab_data = data;\\r\\n\\tthis.prefab_json = JSON.stringify( data );\\r\\n}\\r\\n\\r\\n/**\\r\\n* configure the prefab\\r\\n* @method configure\\r\\n* @param {*} data\\r\\n**/\\r\\n\\r\\nPrefab.prototype.configure = function(data)\\r\\n{\\r\\n\\tif(!data)\\r\\n\\t\\tthrow(\\\"No prefab data found\\\");\\r\\n\\r\\n\\tif(data.hasOwnProperty(\\\"prefab_data\\\"))\\r\\n\\t{\\r\\n\\t\\tthis.prefab_data = data.prefab_data;\\r\\n\\t\\tthis.prefab_json = data.prefab_json || JSON.stringify( this.prefab_data );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\t//read from WBin info\\r\\n\\t\\tvar prefab_json = data[\\\"@json\\\"];\\r\\n\\t\\tif(!prefab_json)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"No JSON found in prefab\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar version = data[\\\"@version\\\"]; //not used yet\\r\\n\\t\\tthis.prefab_json = prefab_json;\\r\\n\\t\\tthis.prefab_data = JSON.parse( prefab_json );\\r\\n\\t}\\r\\n\\r\\n\\tthis.resource_names = data[\\\"@resources_name\\\"] || data.resource_names || [];\\r\\n\\r\\n\\t//extract resource names\\r\\n\\tif(this.resource_names)\\r\\n\\t{\\r\\n\\t\\tvar resources = {};\\r\\n\\t\\tfor(var i in this.resource_names)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!version) //legacy\\r\\n\\t\\t\\t\\tresources[ this.resource_names[i] ] = data[ this.resource_names[i] ];\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tresources[ this.resource_names[i] ] = data[ \\\"@RES_\\\" + i ];\\r\\n\\t\\t}\\r\\n\\t\\tthis._resources_data = resources;\\r\\n\\t}\\r\\n\\r\\n\\t//store resources in ResourcesManager\\r\\n\\tthis.processResources();\\r\\n}\\r\\n\\r\\nPrefab.fromBinary = function( data, filename )\\r\\n{\\r\\n\\tif(data.constructor == ArrayBuffer)\\r\\n\\t\\tdata = WBin.load(data, true);\\r\\n\\r\\n\\treturn new LS.Prefab( data, filename );\\r\\n}\\r\\n\\r\\n//given a list of resources that come from a Prefab (usually a wbin) it extracts, process and register them \\r\\nPrefab.prototype.processResources = function()\\r\\n{\\r\\n\\tif(!this._resources_data)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar pack_filename = this.fullpath || this.filename;\\r\\n\\r\\n\\tvar resources = this._resources_data;\\r\\n\\r\\n\\t//block this resources of being loaded, this is to avoid chain reactions when a resource uses \\r\\n\\t//another one contained in this Prefab\\r\\n\\tfor(var resname in resources)\\r\\n\\t{\\r\\n\\t\\tif( LS.ResourcesManager.resources[ resname ] )\\r\\n\\t\\t\\tcontinue; //already loaded\\r\\n\\t\\tLS.ResourcesManager.resources_being_processed[ resname ] = true;\\r\\n\\t}\\r\\n\\r\\n\\t//process and store in ResourcesManager\\r\\n\\tfor(var resname in resources)\\r\\n\\t{\\r\\n\\t\\tif( LS.ResourcesManager.resources[resname] )\\r\\n\\t\\t\\tcontinue; //already loaded\\r\\n\\r\\n\\t\\tvar resdata = resources[resname];\\r\\n\\t\\tif(!resdata)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn( \\\"resource data in prefab is undefined, skipping it:\\\" + resname );\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\t\\tvar resource = LS.ResourcesManager.processResource( resname, resdata, { is_local: true, from_prefab: pack_filename } );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Creates an instance of the object inside the prefab\\r\\n* @method createObject\\r\\n* @return object contained \\r\\n**/\\r\\n\\r\\nPrefab.prototype.createObject = function()\\r\\n{\\r\\n\\tif(!this.prefab_json)\\r\\n\\t\\tthrow(\\\"No prefab_json data found\\\");\\r\\n\\r\\n\\tvar conf_data = JSON.parse(this.prefab_json);\\r\\n\\r\\n\\tif(!conf_data)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"Prefab data is null\\\");\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tvar node = new LS.SceneNode();\\r\\n\\tnode.configure( conf_data );\\r\\n\\tLS.ResourcesManager.loadResources( node.getResources({},true) );\\r\\n\\r\\n\\tif(this.fullpath)\\r\\n\\t\\tnode.prefab = this.fullpath;\\r\\n\\r\\n\\treturn node;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Adds a resource to the prefab\\r\\n* @method addResource\\r\\n* @param {String} filename filename of the resource\\r\\n**/\\r\\nPrefab.prototype.addResource = function( filename )\\r\\n{\\r\\n\\tfilename = LS.ResourcesManager.cleanFullpath( filename );\\r\\n\\tvar index = this.resource_names.indexOf(filename);\\r\\n\\tif(index == -1)\\r\\n\\t\\tthis.resource_names.push( filename );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Remove a resource to the prefab\\r\\n* @method removeResource\\r\\n* @param {String} filename filename of the resource\\r\\n**/\\r\\nPrefab.prototype.removeResource = function(filename)\\r\\n{\\r\\n\\tfilename = LS.ResourcesManager.cleanFullpath( filename );\\r\\n\\tvar index = this.resource_names.indexOf(filename);\\r\\n\\tif(index != -1)\\r\\n\\t\\tthis.resource_names.splice( index, 1 );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* to create a new prefab, it packs all the data an instantiates the resource\\r\\n* @method Prefab.createPrefab\\r\\n* @param {String} filename a name for this prefab (if wbin is not appended, it will)\\r\\n* @param {Object} node_data an object containing all the node data to store\\r\\n* @param {Array} resource_names_list an array with the name of the resources to store\\r\\n* @return object containing the prefab data ready to be converted to WBin (it also stores _original_data with the WBin)\\r\\n**/\\r\\nPrefab.createPrefab = function( filename, node_data, resource_names_list )\\r\\n{\\r\\n\\tif(!filename)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!node_data)\\r\\n\\t\\tthrow(\\\"No node_data in prefab\\\");\\r\\n\\r\\n\\tfilename = filename.replace(/ /gi,\\\"_\\\");\\r\\n\\tresource_names_list = resource_names_list || [];\\r\\n\\r\\n\\tvar prefab = new LS.Prefab();\\r\\n\\tvar ext = LS.ResourcesManager.getExtension(filename);\\r\\n\\tif( ext != \\\"wbin\\\" )\\r\\n\\t\\tfilename += \\\".wbin\\\";\\r\\n\\r\\n\\t//checkfilenames and rename them to short names\\r\\n\\tprefab.filename = filename;\\r\\n\\tprefab.resource_names = resource_names_list;\\r\\n\\r\\n\\t//assign data\\r\\n\\tprefab.setData( node_data );\\r\\n\\r\\n\\t//get all the resources and store them in a WBin\\r\\n\\tvar bindata = LS.Prefab.packResources( resource_names_list, { \\\"@json\\\": prefab.prefab_json, \\\"@version\\\": Prefab.version }, this );\\r\\n\\tprefab._original_data = bindata;\\r\\n\\r\\n\\treturn prefab;\\r\\n}\\r\\n\\r\\n//Given a list of resources and some base data, it creates a WBin with all the data\\r\\nPrefab.packResources = function( resource_names_list, base_data, from_prefab )\\r\\n{\\r\\n\\tvar to_binary = base_data || {};\\r\\n\\tvar resource_names = [];\\r\\n\\r\\n\\tif(resource_names_list && resource_names_list.length)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < resource_names_list.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar res_name = resource_names_list[i];\\r\\n\\t\\t\\tvar resource = LS.ResourcesManager.resources[ res_name ];\\r\\n\\t\\t\\tif(!resource)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar data = null;\\r\\n\\t\\t\\tif(resource._original_data) //must be string or bytes\\r\\n\\t\\t\\t\\tdata = resource._original_data;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar data_info = LS.Resource.getDataToStore( resource );\\r\\n\\t\\t\\t\\tif(!data_info)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"Data to store from resource is null, skipping: \\\", res_name );\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t//HACK: resource could be renamed to extract the binary info (this happens in jpg textures that are converted to png) or meshes that add wbin\\r\\n\\t\\t\\t\\tif(data_info.extension && data_info.extension != LS.ResourcesManager.getExtension( res_name ))\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"The resource extension has changed while saving, this could lead to problems: \\\", res_name, data_info.extension );\\r\\n\\t\\t\\t\\t\\t//after this change all the references will be wrong\\r\\n\\t\\t\\t\\t\\tvar old_name = res_name;\\r\\n\\t\\t\\t\\t\\tres_name = res_name + \\\".\\\" + data_info.extension;\\r\\n\\t\\t\\t\\t\\tresource_names_list[i] = res_name;\\r\\n\\t\\t\\t\\t\\tLS.GlobalScene.sendResourceRenamedEvent( old_name, res_name, resource ); //force change\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tdata = data_info.data;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(!data)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Wrong data in resource\\\");\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(data.constructor === Blob || data.constructor === File)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"WBin does not support to store File or Blob, please convert to ArrayBuffer using FileReader\\\");\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tto_binary[\\\"@RES_\\\" + resource_names.length ] = data;\\r\\n\\t\\t\\tresource_names.push( res_name );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tto_binary[\\\"@resources_name\\\"] = resource_names;\\r\\n\\treturn WBin.create( to_binary, \\\"Prefab\\\" );\\r\\n}\\r\\n\\r\\nPrefab.prototype.updateFromNode = function( node, clear_uids )\\r\\n{\\r\\n\\tvar data = node.serialize(true);\\r\\n\\tif(clear_uids)\\r\\n\\t\\tLS.clearUIds(data); //remove UIDs\\r\\n\\tthis.prefab_data = data;\\r\\n\\tthis.prefab_json = JSON.stringify( data );\\r\\n}\\r\\n\\r\\nPrefab.prototype.flagResources = function()\\r\\n{\\r\\n\\tif(!this.resource_names)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < this.resource_names.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar res_name = this.resource_names[i];\\r\\n\\t\\tvar resource = LS.ResourcesManager.resources[ res_name ];\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tresource.from_prefab = this.fullpath || this.filename;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPrefab.prototype.setResourcesLink = function( value )\\r\\n{\\r\\n\\tif(!this.resource_names)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < this.resource_names.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar res_name = this.resource_names[i];\\r\\n\\t\\tvar resource = LS.ResourcesManager.resources[ res_name ];\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(value)\\r\\n\\t\\t\\tresource.from_prefab = value;\\r\\n\\t\\telse\\r\\n\\t\\t\\tdelete resource.from_prefab;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//search for nodes using this prefab and creates the nodes\\r\\nPrefab.prototype.applyToNodes = function( scene )\\r\\n{\\r\\n\\tscene = scene || LS.GlobalScene;\\t\\r\\n\\tvar name = this.fullpath || this.filename;\\r\\n\\r\\n\\tfor(var i = 0; i < scene._nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar node = scene._nodes[i];\\r\\n\\t\\tif(node.prefab != name)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tnode.reloadFromPrefab();\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPrefab.prototype.getDataToStore = function()\\r\\n{\\r\\n\\tthis.prefab_json = JSON.stringify( this.prefab_data );\\r\\n\\tvar filename = this.fullpath || this.filename;\\r\\n\\r\\n\\t//prefab in json format\\r\\n\\tif( !(this.resource_names && this.resource_names.length) && filename && LS.RM.getExtension(filename) == \\\"json\\\" )\\r\\n\\t\\treturn JSON.stringify( { object_class: LS.getObjectClassName( this ), \\\"@json\\\": this.prefab_json } );\\r\\n\\r\\n\\t//return the binary data of the wbin\\r\\n\\treturn LS.Prefab.packResources( this.resource_names, this.getBaseData(), this );\\r\\n}\\r\\n\\r\\nPrefab.prototype.getBaseData = function()\\r\\n{\\r\\n\\treturn { \\\"@json\\\": this.prefab_json, \\\"@version\\\": LS.Prefab.version };\\r\\n}\\r\\n\\r\\nPrefab.prototype.recomputeData = function()\\r\\n{\\r\\n\\tvar data = this.getDataToStore();\\r\\n\\tif(data)\\r\\n\\t\\tthis._original_data = data.buffer;\\r\\n}\\r\\n\\r\\n//inheritet methods from Pack\\r\\nPrefab.prototype.containsResources = Pack.prototype.containsResources;\\r\\nPrefab.prototype.onResourceRenamed = Pack.prototype.onResourceRenamed;\\r\\nPrefab.prototype.checkResourceNames = Pack.prototype.checkResourceNames;\\r\\nPrefab.prototype.setResources = Pack.prototype.setResources;\\r\\nPrefab.prototype.getSizeInBytes = Pack.prototype.getSizeInBytes;\\r\\n\\r\\nLS.Prefab = Prefab;\\r\\nLS.registerResourceClass( Prefab );\\r\\n\\r\\n///@FILE:../src/resources/shaderCode.js\\r\\n/**\\r\\n* ShaderCode is a resource containing all the code associated to a shader\\r\\n* It is used to define special ways to render scene objects, having full control of the rendering algorithm\\r\\n* Having a special class helps to parse the data in advance and share it between different materials\\r\\n* \\r\\n* @class ShaderCode\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nfunction ShaderCode( code )\\r\\n{\\r\\n\\tthis._code = null;\\r\\n\\r\\n\\tthis._functions = {};\\r\\n\\tthis._global_uniforms = {};\\r\\n\\tthis._code_parts = {};\\r\\n\\tthis._subfiles = {};\\r\\n\\tthis._compiled_shaders = {}; //all shaders compiled using this ShaderCode\\r\\n\\r\\n\\tthis._shaderblock_flags_num = 0; //used to assign flags to dependencies\\r\\n\\tthis._shaderblock_flags = {}; //used to store which shaderblock represent to every flag bit\\r\\n\\r\\n\\tthis._version = 0;\\r\\n\\r\\n\\tif(code)\\r\\n\\t\\tthis.code = code;\\r\\n}\\r\\n\\r\\nShaderCode.help_url = \\\"https://github.com/jagenjo/litescene.js/blob/master/guides/shaders.md\\\";\\r\\n\\r\\n//block types\\r\\nShaderCode.CODE = 1;\\r\\nShaderCode.PRAGMA = 2;\\r\\n\\r\\n//pargma types\\r\\nShaderCode.INCLUDE = 1;\\r\\nShaderCode.SHADERBLOCK = 2;\\r\\nShaderCode.SNIPPET = 3;\\r\\n\\r\\nShaderCode.EXTENSION = \\\"glsl\\\";\\r\\n\\r\\nObject.defineProperty( ShaderCode.prototype, \\\"code\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._code;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(this._code == v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._code = v;\\r\\n\\t\\tthis.processCode();\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( ShaderCode.prototype, \\\"version\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._version;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tconsole.error(\\\"version cannot be set manually\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\n\\r\\n//parse the code\\r\\n//store in a easy to use way\\r\\nShaderCode.prototype.processCode = function()\\r\\n{\\r\\n\\tvar code = this._code;\\r\\n\\tthis._global_uniforms = {};\\r\\n\\tthis._code_parts = {};\\r\\n\\tthis._compiled_shaders = {};\\r\\n\\tthis._functions = {};\\r\\n\\tthis._shaderblock_flags_num = 0;\\r\\n\\tthis._shaderblock_flags = {};\\r\\n\\tthis._shaderblock_vars = null;\\r\\n\\tthis._has_error = false;\\r\\n\\r\\n\\tvar subfiles = GL.processFileAtlas( this._code );\\r\\n\\tthis._subfiles = subfiles;\\r\\n\\r\\n\\tvar num_subfiles = 0;\\r\\n\\tvar init_code = null; \\r\\n\\r\\n\\tfor(var i in subfiles)\\r\\n\\t{\\r\\n\\t\\tvar subfile_name = i;\\r\\n\\t\\tvar subfile_data = subfiles[i];\\r\\n\\t\\tnum_subfiles++;\\r\\n\\r\\n\\t\\tif(!subfile_name)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif(subfile_name == \\\"js\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tinit_code = subfile_data;\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//used to declare uniforms without using javascript\\r\\n\\t\\tif(subfile_name == \\\"uniforms\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar lines = subfile_data.split(\\\"/n\\\");\\r\\n\\t\\t\\tfor(var j = 0; j < lines.length; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar line = lines[j].trim();\\r\\n\\t\\t\\t\\tvar words = line.split(\\\" \\\");\\r\\n\\t\\t\\t\\tvar varname = words[0];\\r\\n\\t\\t\\t\\tvar uniform_name = words[1];\\r\\n\\t\\t\\t\\tvar property_type = words[2];\\r\\n\\t\\t\\t\\tvar value = words[3];\\r\\n\\t\\t\\t\\tif( value !== undefined )\\r\\n\\t\\t\\t\\t\\tvalue = LS.stringToValue(value);\\r\\n\\t\\t\\t\\tvar options = null;\\r\\n\\t\\t\\t\\tvar options_index = line.indexOf(\\\"{\\\");\\r\\n\\t\\t\\t\\tif(options_index != -1)\\r\\n\\t\\t\\t\\t\\toptions = LS.stringToValue( line.substr(options_index) );\\r\\n\\t\\t\\t\\tthis._global_uniforms[ varname ] = { name: varname, uniform: uniform_name, type: property_type, value: value, options: options };\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar name = LS.ResourcesManager.removeExtension( subfile_name );\\r\\n\\t\\tif(name == \\\"default\\\")\\r\\n\\t\\t\\tname = \\\"color\\\"; //LEGACY fix\\r\\n\\t\\tvar extension = LS.ResourcesManager.getExtension( subfile_name );\\r\\n\\r\\n\\t\\tif(extension == \\\"vs\\\" || extension == \\\"fs\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar code_part = this._code_parts[name];\\r\\n\\t\\t\\tif(!code_part)\\r\\n\\t\\t\\t\\tcode_part = this._code_parts[name] = {};\\r\\n\\r\\n\\t\\t\\t//parse data (extract pragmas and stuff)\\r\\n\\t\\t\\tvar glslcode = new GLSLCode( subfile_data );\\r\\n\\t\\t\\tfor(var j in glslcode.blocks)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar pragma_info = glslcode.blocks[j];\\r\\n\\t\\t\\t\\tif(!pragma_info || pragma_info.type != ShaderCode.PRAGMA)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t//assign a flag position in case this block is enabled\\r\\n\\t\\t\\t\\tpragma_info.shader_block_flag = this._shaderblock_flags_num; \\r\\n\\t\\t\\t\\tthis._shaderblock_flags[ pragma_info.shader_block ] = pragma_info.shader_block_flag;\\r\\n\\t\\t\\t\\tthis._shaderblock_flags_num += 1;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tcode_part[ extension ] = glslcode;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//compile the shader before using it to ensure there is no errors\\r\\n\\tvar shader = this.getShader();\\r\\n\\tif(!shader)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//process init code\\r\\n\\tif(init_code)\\r\\n\\t{\\r\\n\\t\\t//clean code\\r\\n\\t\\tinit_code = LS.ShaderCode.removeComments( init_code );\\r\\n\\r\\n\\t\\tif(init_code) //still some code? (we test it because if there is a single line of code the behaviour changes)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(LS.catch_exceptions)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttry\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tthis._functions.init = new Function( init_code );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tLS.dispatchCodeError( err, LScript.computeLineFromError(err), this );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis._functions.init = new Function( init_code );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//check that all uniforms are correct\\r\\n\\tthis.validatePublicUniforms( shader );\\r\\n\\r\\n\\r\\n\\t//to alert all the materials out there using this shader that they must update themselves.\\r\\n\\tLEvent.trigger( LS.ShaderCode, \\\"modified\\\", this );\\r\\n\\tthis._version += 1;\\r\\n}\\r\\n\\r\\n//used when storing/retrieving the resource\\r\\nShaderCode.prototype.setData = function(v, skip_modified_flag)\\r\\n{\\r\\n\\tthis.code = v;\\r\\n\\tif(!skip_modified_flag)\\r\\n\\t\\tthis._modified = true;\\r\\n}\\r\\n\\r\\nShaderCode.prototype.getData = function()\\r\\n{\\r\\n\\treturn this._code;\\r\\n}\\r\\n\\r\\nShaderCode.prototype.fromData = ShaderCode.prototype.setData;\\r\\nShaderCode.prototype.toData = ShaderCode.prototype.getData;\\r\\n\\r\\nShaderCode.prototype.getDataToStore = function()\\r\\n{\\r\\n\\treturn this._code;\\r\\n}\\r\\n\\r\\n//compile the shader, cache and return\\r\\nShaderCode.prototype.getShader = function( render_mode, block_flags )\\r\\n{\\r\\n\\tif( this._has_error )\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\trender_mode = render_mode || \\\"color\\\";\\r\\n\\tblock_flags = block_flags || 0;\\r\\n\\r\\n\\t//search for a compiled version of the shader (by render_mode and block_flags)\\r\\n\\tvar shaders_map = this._compiled_shaders[ render_mode ];\\r\\n\\tif(shaders_map)\\r\\n\\t{\\r\\n\\t\\tvar shader = shaders_map.get( block_flags );\\r\\n\\t\\tif(shader)\\r\\n\\t\\t\\treturn shader;\\r\\n\\t}\\r\\n\\r\\n\\t//search for the code\\r\\n\\tvar code = this._code_parts[ render_mode ];\\r\\n\\tif(!code)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar context = {}; //used to store metaprogramming defined vars in the shader\\r\\n\\r\\n\\t//compute context defines\\r\\n\\tfor(var i = 0, l = LS.Shaders.shader_blocks.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tif( !(block_flags & 1<= this.outputs.length )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar output = this.outputs[i];\\r\\n\\t\\tif( !output.links || !output.links.length || output.type == LiteGraph.EVENT )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar compo = this.getComponent();\\r\\n\\t\\tif(!compo)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(output.name == \\\"Component\\\")\\r\\n\\t\\t\\tthis.setOutputData( slot, compo );\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.setOutputData( slot, compo[ output.name ] );\\r\\n\\t}\\r\\n\\r\\n\\tLGraphComponent.prototype.onDrawBackground = function()\\r\\n\\t{\\r\\n\\t\\tvar compo = this.getComponent();\\r\\n\\t\\tif(compo)\\r\\n\\t\\t\\tthis.title = LS.getClassName( compo.constructor );\\r\\n\\t}\\r\\n\\r\\n\\tLGraphComponent.prototype.onConnectionsChange = function( type, slot, created, link_info, slot_info )\\r\\n\\t{\\r\\n\\t\\tif(type == LiteGraph.INPUT && slot_info && slot_info.name == \\\"Component\\\" )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar node = this.getInputNode(slot);\\r\\n\\t\\t\\tif(node && node.onExecute)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnode.onExecute();\\r\\n\\t\\t\\t\\tthis.setDirtyCanvas(true,true);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphComponent.prototype.getComponent = function()\\r\\n\\t{\\r\\n\\t\\tvar scene = this.graph._scene || LS.GlobalScene;\\r\\n\\t\\t//TODO: if no graph found, then crawl up in the graph hierarchy because probalby it is a subgraph\\r\\n\\r\\n\\t\\tvar node_id = this.properties.node_id;\\r\\n\\t\\tif(!node_id)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.inputs && this.inputs.length )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar slot = this.findInputSlot(\\\"Component\\\");\\r\\n\\t\\t\\t\\tif(slot != -1)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar component = this.getInputData(slot);\\r\\n\\t\\t\\t\\t\\treturn component ? component : null;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//find node\\r\\n\\t\\tvar node = scene.getNode( node_id );\\r\\n\\t\\tif(!node)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\t//find compo\\r\\n\\t\\tvar compo_id = this.properties.component_id;\\r\\n\\t\\tvar compo = null;\\r\\n\\t\\tif(compo_id.charAt(0) == \\\"@\\\")\\r\\n\\t\\t\\tcompo = node.getComponentByUId( compo_id );\\r\\n\\t\\telse if( LS.Components[ compo_id ] )\\r\\n\\t\\t\\tcompo = node.getComponent( LS.Components[ compo_id ] );\\r\\n\\t\\telse\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tif(compo && !compo.constructor.is_component)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tif(this._component != compo)\\r\\n\\t\\t\\tthis.bindComponentEvents( compo );\\r\\n\\r\\n\\t\\tthis._component = compo;\\r\\n\\t\\treturn compo;\\r\\n\\t}\\r\\n\\r\\n\\t//bind events attached to this component\\r\\n\\tLGraphComponent.prototype.bindComponentEvents = function( component )\\r\\n\\t{\\r\\n\\t\\tif(this._component)\\r\\n\\t\\t\\tLEvent.unbindAll( this._component, this );\\r\\n\\r\\n\\t\\tthis._component = component;\\r\\n\\t\\tif( !this._component )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t\\r\\n\\t\\t//iterate outputs\\r\\n\\t\\tif(this.outputs && this.outputs.length)\\r\\n\\t\\t\\tfor(var i = 0; i < this.outputs.length; ++i )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar output = this.outputs[i];\\r\\n\\t\\t\\t\\tif( output.type !== LiteGraph.EVENT )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tvar event_name = output.name.substr(3);\\r\\n\\t\\t\\t\\tLEvent.bind( this._component, event_name, this.onComponentEvent, this );\\r\\n\\t\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphComponent.prototype.onComponentEvent = function ( e, params )\\r\\n\\t{\\r\\n\\t\\tthis.trigger( \\\"on_\\\" + e, params );\\r\\n\\t}\\r\\n\\r\\n\\tLGraphComponent.prototype.getComponentProperties = function( get_inputs, result )\\r\\n\\t{\\r\\n\\t\\tvar compo = this.getComponent();\\r\\n\\t\\tif(!compo)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar attrs = null;\\r\\n\\t\\tif(compo.getPropertiesInfo)\\r\\n\\t\\t\\tattrs = compo.getPropertiesInfo( get_inputs );\\r\\n\\t\\telse\\r\\n\\t\\t\\tattrs = LS.getObjectProperties( compo );\\r\\n\\r\\n\\t\\tresult = result || [];\\r\\n\\t\\tfor(var i in attrs)\\r\\n\\t\\t\\tresult.push( [i, attrs[i]] );\\r\\n\\r\\n\\t\\tif(compo.getExtraProperties)\\r\\n\\t\\t\\tcompo.getExtraProperties( result );\\r\\n\\r\\n\\t\\treturn result;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphComponent.prototype.onAction = function( action_name, params ) { \\r\\n\\t\\tif(!action_name)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar compo = this.getComponent();\\r\\n\\t\\tif(!compo)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(compo.onAction)\\r\\n\\t\\t\\tcompo.onAction( action_name, params );\\r\\n\\t\\telse if( compo[ action_name ] )\\r\\n\\t\\t\\tcompo[ action_name ](); //params will be mostly MouseEvent, so for now I wont pass it\\r\\n\\t}\\r\\n\\r\\n\\t//used by the LGraphSetValue node\\r\\n\\tLGraphComponent.prototype.onSetValue = function( property_name, value ) { \\r\\n\\t\\tvar compo = this.getComponent();\\r\\n\\t\\tif(!compo)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar current = compo[ property_name ];\\r\\n\\t\\tvar final_value;\\r\\n\\r\\n\\t\\tif( current == null)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(value && value.constructor === String)\\r\\n\\t\\t\\t\\tfinal_value = value;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tswitch( current.constructor )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase Number: final_value = Number( value ); break;\\r\\n\\t\\t\\t\\tcase Boolean: final_value = (value == \\\"true\\\" || value == \\\"1\\\"); break;\\r\\n\\t\\t\\t\\tcase String: final_value = String( value ); break;\\r\\n\\t\\t\\t\\tcase Array:\\r\\n\\t\\t\\t\\tcase Float32Array: \\r\\n\\t\\t\\t\\t\\tif( value != null )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tif( value.constructor === String )\\r\\n\\t\\t\\t\\t\\t\\t\\tfinal_value = JSON.parse(\\\"[\\\"+value+\\\"]\\\");\\r\\n\\t\\t\\t\\t\\t\\telse if( value.constructor === Number )\\r\\n\\t\\t\\t\\t\\t\\t\\tfinal_value = [value];\\r\\n\\t\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\t\\tfinal_value = value;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\tfinal_value = value;\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(final_value === undefined)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(compo.setPropertyValue)\\r\\n\\t\\t\\tcompo.setPropertyValue( property_name, final_value );\\r\\n\\t\\telse\\r\\n\\t\\t\\tcompo[ property_name ] = final_value;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphComponent.prototype.onGetInputs = function()\\r\\n\\t{ \\r\\n\\t\\tvar inputs = [[\\\"Node\\\",0],[\\\"Component\\\",0],null];\\r\\n\\r\\n\\t\\tthis.getComponentProperties(\\\"input\\\", inputs);\\r\\n\\r\\n\\t\\tvar compo = this.getComponent();\\r\\n\\t\\tif(compo && compo.getActions)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar actions = compo.getActions({});\\r\\n\\t\\t\\tif(actions)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(actions.constructor === Array)\\r\\n\\t\\t\\t\\t\\tfor(var i = 0; i < actions.length; ++i)\\r\\n\\t\\t\\t\\t\\t\\tinputs.push( [ actions[i], LiteGraph.ACTION ] );\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tfor(var i in actions)\\r\\n\\t\\t\\t\\t\\t\\tinputs.push( [i, LiteGraph.ACTION ] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn inputs;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphComponent.prototype.onGetOutputs = function()\\r\\n\\t{ \\r\\n\\t\\tvar outputs = [];\\r\\n\\t\\toutputs.push( [\\\"Component\\\", \\\"Component\\\" ], null ); //compo + separator\\r\\n\\r\\n\\t\\tthis.getComponentProperties( \\\"output\\\", outputs);\\r\\n\\r\\n\\t\\tvar compo = this.getComponent();\\r\\n\\t\\tif(compo && compo.getEvents)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar events = compo.getEvents();\\r\\n\\t\\t\\tif(events)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(events.constructor === Array)\\r\\n\\t\\t\\t\\t\\tfor(var i = 0; i < events.length; ++i)\\r\\n\\t\\t\\t\\t\\t\\toutputs.push( [\\\"on_\\\" + events[i], LiteGraph.EVENT ] );\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tfor(var i in events)\\r\\n\\t\\t\\t\\t\\t\\toutputs.push( [\\\"on_\\\" + i, LiteGraph.EVENT ] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\treturn outputs;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphComponent.prototype.onInspect = function( inspector )\\r\\n\\t{ \\r\\n\\t\\tvar that = this;\\r\\n\\r\\n\\t\\tvar component = this.getComponent();\\r\\n\\t\\tif(component && component.onInspectNode )\\r\\n\\t\\t\\tcomponent.onInspectNode( inspector, this );\\r\\n\\r\\n\\t\\tinspector.addButton(null, \\\"Inspect Component\\\", function(){\\r\\n\\t\\t\\tvar compo = that.getComponent();\\r\\n\\t\\t\\tif(!compo)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tEditorModule.inspect( compo );\\r\\n\\t\\t});\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"scene/component\\\", LGraphComponent );\\r\\n\\t\\r\\n\\t//********************************************************\\r\\n\\r\\n\\t/* LGraphNode representing an object in the Scene */\\r\\n\\r\\n\\tglobal.LGraphTransform = function()\\r\\n\\t{\\r\\n\\t\\tthis.properties = {node_id:\\\"\\\"};\\r\\n\\t\\tif(LGraphSceneNode._current_node_id)\\r\\n\\t\\t\\tthis.properties.node_id = LGraphSceneNode._current_node_id;\\r\\n\\t\\tthis.addInput(\\\"Transform\\\", \\\"Transform\\\", { locked: true });\\r\\n\\t\\tthis.addOutput(\\\"Position\\\",\\\"vec3\\\");\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTransform.title = \\\"Transform\\\";\\r\\n\\tLGraphTransform.desc = \\\"Transform info of a node\\\";\\r\\n\\r\\n\\tLGraphTransform.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tvar transform = null;\\r\\n\\r\\n\\t\\tif(this.inputs && this.inputs[0])\\r\\n\\t\\t\\ttransform = this.getInputData(0);\\r\\n\\r\\n\\t\\tif(!transform)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar scene = this.graph.getScene ? this.graph.getScene() : LS.GlobalScene;\\r\\n\\r\\n\\t\\t\\tvar node = this._node;\\r\\n\\t\\t\\tif(\\tthis.properties.node_id )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnode = scene.getNode( this.properties.node_id );\\r\\n\\t\\t\\t\\tif(!node)\\r\\n\\t\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(!node)\\r\\n\\t\\t\\t\\tnode = this.graph._scenenode;\\r\\n\\r\\n\\t\\t\\ttransform = node.transform;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!transform)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//read inputs\\r\\n\\t\\tif(this.inputs)\\r\\n\\t\\tfor(var i = 1; i < this.inputs.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar input = this.inputs[i];\\r\\n\\t\\t\\tvar v = this.getInputData(i);\\r\\n\\t\\t\\tif(v === undefined)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tswitch( input.name )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase \\\"x\\\": transform.x = v; break;\\r\\n\\t\\t\\t\\tcase \\\"y\\\": transform.y = v; break;\\r\\n\\t\\t\\t\\tcase \\\"z\\\": transform.z = v; break;\\r\\n\\t\\t\\t\\tcase \\\"Position\\\": transform.setPosition(v); break;\\r\\n\\t\\t\\t\\tcase \\\"Rotation\\\": transform.setRotation(v); break;\\r\\n\\t\\t\\t\\tcase \\\"Scale\\\": transform.setScale(v); break;\\r\\n\\t\\t\\t\\tcase \\\"Matrix\\\": transform.fromMatrix(v); break;\\r\\n\\t\\t\\t\\tcase \\\"Mult.Matrix\\\": transform.applyTransformMatrix(v); break;\\r\\n\\t\\t\\t\\tcase \\\"Translate\\\": transform.translate(v); break;\\r\\n\\t\\t\\t\\tcase \\\"Translate Global\\\": transform.translateGlobal(v); break;\\r\\n\\t\\t\\t\\tcase \\\"Rotate\\\": quat.multiply( transform._rotation, transform._rotation, v ); transform._must_update = true; break;\\r\\n\\t\\t\\t\\tcase \\\"RotateX\\\": transform.rotateX(v); break;\\r\\n\\t\\t\\t\\tcase \\\"RotateY\\\": transform.rotateY(v); break;\\r\\n\\t\\t\\t\\tcase \\\"RotateZ\\\": transform.rotateZ(v); break;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//write outputs\\r\\n\\t\\tif(this.outputs)\\r\\n\\t\\tfor(var i = 0; i < this.outputs.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar output = this.outputs[i];\\r\\n\\t\\t\\tif(!output.links || !output.links.length)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar value = undefined;\\r\\n\\t\\t\\tswitch( output.name )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase \\\"x\\\": value = transform.x; break;\\r\\n\\t\\t\\t\\tcase \\\"y\\\": value = transform.y; break;\\r\\n\\t\\t\\t\\tcase \\\"z\\\": value = transform.z; break;\\r\\n\\t\\t\\t\\tcase \\\"Position\\\": value = transform.position; break;\\r\\n\\t\\t\\t\\tcase \\\"Global Position\\\": value = transform.getGlobalPosition(); break;\\r\\n\\t\\t\\t\\tcase \\\"Rotation\\\": value = transform.rotation; break;\\r\\n\\t\\t\\t\\tcase \\\"Global Rotation\\\": value = transform.getGlobalRotation(); break;\\r\\n\\t\\t\\t\\tcase \\\"Scale\\\": value = transform.scaling; break;\\r\\n\\t\\t\\t\\tcase \\\"Matrix\\\": value = transform.getMatrix(); break;\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(value !== undefined)\\r\\n\\t\\t\\t\\tthis.setOutputData( i, value );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTransform.prototype.onGetInputs = function()\\r\\n\\t{\\r\\n\\t\\treturn [[\\\"Position\\\",\\\"vec3\\\"],[\\\"Rotation\\\",\\\"quat\\\"],[\\\"Scale\\\",\\\"number\\\"],[\\\"x\\\",\\\"number\\\"],[\\\"y\\\",\\\"number\\\"],[\\\"z\\\",\\\"number\\\"],[\\\"Global Position\\\",\\\"vec3\\\"],[\\\"Global Rotation\\\",\\\"quat\\\"],[\\\"Matrix\\\",\\\"mat4\\\"],[\\\"Mult.Matrix\\\",\\\"mat4\\\"],[\\\"Translate\\\",\\\"vec3\\\"],[\\\"Translate Global\\\",\\\"vec3\\\"],[\\\"Rotate\\\",\\\"quat\\\"],[\\\"RotateX\\\",\\\"number\\\"],[\\\"RotateY\\\",\\\"number\\\"],[\\\"RotateZ\\\",\\\"number\\\"]];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTransform.prototype.onGetOutputs = function()\\r\\n\\t{\\r\\n\\t\\treturn [[\\\"Position\\\",\\\"vec3\\\"],[\\\"Rotation\\\",\\\"quat\\\"],[\\\"Scale\\\",\\\"number\\\"],[\\\"x\\\",\\\"number\\\"],[\\\"y\\\",\\\"number\\\"],[\\\"z\\\",\\\"number\\\"],[\\\"Global Position\\\",\\\"vec3\\\"],[\\\"Global Rotation\\\",\\\"quat\\\"],[\\\"Matrix\\\",\\\"mat4\\\"]];\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"scene/transform\\\", LGraphTransform );\\r\\n\\r\\n};\\r\\n\\r\\n///@FILE:../src/graph/logic.js\\r\\n///@INFO: GRAPHS\\r\\nif(typeof(LiteGraph) != \\\"undefined\\\")\\r\\n{\\r\\n\\t//special kind of node\\r\\n\\tglobal.LGraphSetValue = function LGraphSetValue()\\r\\n\\t{\\r\\n\\t\\tthis.properties = { property_name: \\\"\\\", value: \\\"\\\", type: \\\"String\\\" };\\r\\n\\t\\tthis.addInput(\\\"on_set\\\", LiteGraph.ACTION );\\r\\n\\t\\tthis.addInput(\\\"value\\\", \\\"\\\" );\\r\\n\\t\\tthis.addOutput(\\\"on\\\", LiteGraph.EVENT ); //to chain\\r\\n\\t\\tthis.addOutput(\\\"node\\\", 0 );\\r\\n\\t\\tthis.mode = LiteGraph.ON_TRIGGER;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphSetValue.prototype.onAction = function( action_name, params ) { \\r\\n\\t\\t//is on_set\\r\\n\\r\\n\\t\\tif(!this.properties.property_name)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//get the connected node\\r\\n\\t\\tvar nodes = this.getOutputNodes(1);\\r\\n\\t\\tif(!nodes)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar value = this.getInputOrProperty(\\\"value\\\");\\r\\n\\r\\n\\t\\t//check for a setValue method, otherwise assign to property if exists one with that name\\r\\n\\t\\tfor(var i = 0; i < nodes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar node = nodes[i];\\r\\n\\t\\t\\t//call it\\r\\n\\t\\t\\tif(node.onSetValue)\\r\\n\\t\\t\\t\\tnode.onSetValue( this.properties.property_name, value );\\r\\n\\t\\t\\telse if(node.properties && node.properties.value !== undefined)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnode.properties[ this.properties.property_name ] = value;\\r\\n\\t\\t\\t\\tif(node.onPropertyChanged)\\r\\n\\t\\t\\t\\t\\tnode.onPropertyChanged( this.properties.property_name, value );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.trigger(\\\"on\\\");\\r\\n\\t}\\r\\n\\r\\n\\tLGraphSetValue.title = \\\"SetValue\\\";\\r\\n\\tLGraphSetValue.desc = \\\"sets a value to a node (could be a property)\\\";\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"logic/setValue\\\", LGraphSetValue );\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n///@FILE:../src/graph/fx.js\\r\\n///@INFO: GRAPHS\\r\\nif(typeof(LiteGraph) != \\\"undefined\\\")\\r\\n{\\r\\n\\r\\nvar litegraph_texture_found = false;\\r\\nif(typeof(LGraphTexture) == \\\"undefined\\\")\\r\\n\\tconsole.error(\\\"LiteGraph found but no LGraphTexture, this means LiteGL wasnt not included BEFORE litegraph. Be sure to include LiteGL before LiteGraph to ensure all functionalities.\\\");\\r\\nelse\\r\\n\\tlitegraph_texture_found = true;\\r\\n\\r\\n\\r\\n\\r\\n// Texture Blur *****************************************\\r\\nfunction LGraphFXStack()\\r\\n{\\r\\n\\tthis.addInput(\\\"Color\\\",\\\"Texture\\\");\\r\\n\\tthis.addInput(\\\"Depth\\\",\\\"Texture\\\");\\r\\n\\tthis.addInput(\\\"Intensity\\\",\\\"number\\\");\\r\\n\\tthis.addOutput(\\\"Final\\\",\\\"Texture\\\");\\r\\n\\tthis.properties = { intensity: 1, preserve_aspect: true };\\r\\n\\r\\n\\tthis._fx_stack = new LS.FXStack();\\r\\n\\tthis._fx_options = {};\\r\\n}\\r\\n\\r\\nLGraphFXStack.title = \\\"FXStack\\\";\\r\\nLGraphFXStack.desc = \\\"Apply FXs to Texture\\\";\\r\\n\\r\\nLGraphFXStack.prototype.onExecute = function()\\r\\n{\\r\\n\\tvar tex = this.getInputData(0);\\r\\n\\tif(!tex)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this.isOutputConnected(0))\\r\\n\\t\\treturn; //saves work\\r\\n\\r\\n\\tvar temp = this._final_texture;\\r\\n\\r\\n\\tif(!temp || temp.width != tex.width || temp.height != tex.height || temp.type != tex.type )\\r\\n\\t{\\r\\n\\t\\t//we need two textures to do the blurring\\r\\n\\t\\tthis._final_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR });\\r\\n\\t}\\r\\n\\r\\n\\tif( this.isInputConnected(1) )\\r\\n\\t\\tthis._fx_options.depth_texture = this.getInputData(1);\\r\\n\\r\\n\\tvar intensity = this.properties.intensity;\\r\\n\\tif( this.isInputConnected(2) )\\r\\n\\t{\\r\\n\\t\\tintensity = this.getInputData(2);\\r\\n\\t\\tthis.properties.intensity = intensity;\\r\\n\\t}\\r\\n\\r\\n\\t//blur sometimes needs an aspect correction\\r\\n\\tvar aspect = LiteGraph.camera_aspect;\\r\\n\\tif(!aspect && window.gl !== undefined)\\r\\n\\t\\taspect = gl.canvas.height / gl.canvas.width;\\r\\n\\tif(!aspect)\\r\\n\\t\\taspect = 1;\\r\\n\\taspect = this.properties.preserve_aspect ? aspect : 1;\\r\\n\\r\\n\\tthis._fx_stack.applyFX( tex, this._final_texture, this._fx_options );\\r\\n\\r\\n\\tthis.setOutputData(0, this._final_texture);\\r\\n}\\r\\n\\r\\nLGraphFXStack.prototype.onInspect = function( inspector )\\r\\n{\\r\\n\\treturn this._fx_stack.inspect( inspector );\\r\\n}\\r\\n\\r\\nLGraphFXStack.prototype.getResources = function( resources )\\r\\n{\\r\\n\\treturn this._fx_stack.getResources( resources );\\r\\n}\\r\\n\\r\\nLGraphFXStack.prototype.onSerialize = function( o )\\r\\n{\\r\\n\\to.stack = this._fx_stack.serialize();\\r\\n}\\r\\n\\r\\nLGraphFXStack.prototype.onConfigure = function( o )\\r\\n{\\r\\n\\tif(o.stack)\\r\\n\\t\\tthis._fx_stack.configure( o.stack );\\r\\n}\\r\\n\\r\\nLiteGraph.registerNodeType(\\\"texture/fxstack\\\", LGraphFXStack );\\r\\n\\r\\n\\r\\nfunction LGraphHistogram()\\r\\n{\\r\\n\\tthis.addInput(\\\"in\\\",\\\"Texture\\\");\\r\\n\\tthis.addInput(\\\"enabled\\\",\\\"Boolean\\\");\\r\\n\\tthis.addOutput(\\\"out\\\",\\\"Texture\\\");\\r\\n\\tthis.properties = { enabled: true, scale: 0.5, position: [0,0], size: [1,1] };\\r\\n}\\r\\n\\r\\nLGraphHistogram.title = \\\"Histogram\\\";\\r\\nLGraphHistogram.desc = \\\"Overlaps an histogram\\\";\\r\\nLGraphHistogram.priority = 2; //render at the end\\r\\n\\r\\nLGraphHistogram.prototype.onExecute = function()\\r\\n{\\r\\n\\tvar tex = this.getInputData(0);\\r\\n\\r\\n\\tif( !tex )\\r\\n\\t\\treturn; //saves work\\r\\n\\r\\n\\tvar enabled = this.getInputData(1);\\r\\n\\tif(enabled != null)\\r\\n\\t\\tthis.properties.enabled = Boolean( enabled );\\r\\n\\r\\n\\tif(this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false )\\r\\n\\t{\\r\\n\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tif(!this._points_mesh)\\r\\n\\t{\\r\\n\\t\\tvar w = 512;\\r\\n\\t\\tvar h = 256;\\r\\n\\t\\tvar vertices = new Float32Array(w*h*3);\\r\\n\\t\\tfor(var y = 0; y < h; ++y)\\r\\n\\t\\t\\tfor(var x = 0; x < w; ++x)\\r\\n\\t\\t\\t\\tvertices.set([x/w,y/h,0], y*w*3 + x*3);\\r\\n\\t\\tthis._points_mesh = GL.Mesh.load({ vertices: vertices });\\r\\n\\t}\\r\\n\\r\\n\\tvar histogram_bins = 256;\\r\\n\\r\\n\\tif(!this._texture)\\r\\n\\t\\tthis._texture = new GL.Texture(histogram_bins,1,{ type: gl.FLOAT, magFilter: gl.LINEAR, format: gl.RGB});\\r\\n\\r\\n\\tif(!LGraphHistogram._shader)\\r\\n\\t\\tLGraphHistogram._shader = new GL.Shader( LGraphHistogram.vertex_shader, LGraphHistogram.pixel_shader );\\r\\n\\r\\n\\tvar mesh = this._points_mesh;\\r\\n\\tvar shader = LGraphHistogram._shader;\\r\\n\\tvar scale = this.properties.scale;\\r\\n\\tshader.setUniform(\\\"u_texture\\\",0);\\r\\n\\tshader.setUniform(\\\"u_factor\\\",1/512);\\r\\n\\ttex.bind(0);\\r\\n\\r\\n\\tgl.disable(gl.DEPTH_TEST);\\r\\n\\tgl.enable(gl.BLEND);\\r\\n\\tgl.blendFunc(gl.ONE,gl.ONE);\\r\\n\\r\\n\\t//compute\\r\\n\\tthis._texture.drawTo( function(){\\r\\n\\t\\tgl.clearColor(0,0,0,1);\\r\\n\\t\\tgl.clear( gl.COLOR_BUFFER_BIT );\\r\\n\\t\\tfor(var i = 0; i < 3; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.colorMask( i == 0, i == 1, i == 2, true );\\r\\n\\t\\t\\tshader.setUniform(\\\"u_mask\\\",LGraphHistogram.masks[i]);\\r\\n\\t\\t\\tshader.draw( mesh, gl.POINTS );\\r\\n\\t\\t}\\r\\n\\t\\tgl.colorMask( true,true,true, true );\\r\\n\\t});\\r\\n\\r\\n\\tif(!this._line_mesh)\\r\\n\\t{\\r\\n\\t\\tvar vertices = new Float32Array(histogram_bins*3);\\r\\n\\t\\tfor(var x = 0; x < histogram_bins; ++x)\\r\\n\\t\\t\\tvertices.set([x/histogram_bins,0,0], x*3);\\r\\n\\t\\tthis._line_mesh = GL.Mesh.load({ vertices: vertices });\\r\\n\\t}\\r\\n\\r\\n\\tif(!LGraphHistogram._line_shader)\\r\\n\\t\\tLGraphHistogram._line_shader = new GL.Shader( LGraphHistogram.line_vertex_shader, LGraphHistogram.line_pixel_shader );\\r\\n\\r\\n\\tvar mesh = this._line_mesh;\\r\\n\\tvar shader = LGraphHistogram._line_shader;\\r\\n\\tshader.setUniform(\\\"u_texture\\\",0);\\r\\n\\tshader.setUniform(\\\"u_scale\\\",scale);\\r\\n\\tthis._texture.bind(0);\\r\\n\\tgl.disable(gl.BLEND);\\r\\n\\r\\n\\tgl.viewport( this.properties.position[0] * gl.canvas.width, this.properties.position[1] * gl.canvas.height, \\r\\n\\t\\t\\t\\tthis.properties.size[0] * gl.canvas.width, this.properties.size[1] * gl.canvas.height );\\r\\n\\r\\n\\tfor(var i = 0; i < 3; ++i)\\r\\n\\t{\\r\\n\\t\\tshader.setUniform(\\\"u_mask\\\",LGraphHistogram.masks[i]);\\r\\n\\t\\tshader.draw( mesh, gl.LINE_STRIP );\\r\\n\\t}\\r\\n\\r\\n\\tthis.setOutputData(0, this._texture );\\r\\n\\r\\n\\tgl.viewport( 0,0, gl.canvas.width, gl.canvas.height );\\r\\n}\\r\\n\\r\\nLGraphHistogram.masks = [vec3.fromValues(1,0,0),vec3.fromValues(0,1,0),vec3.fromValues(0,0,1)];\\r\\n\\r\\nLGraphHistogram.vertex_shader = \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\tuniform vec3 u_mask;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tvec3 color = texture2D( u_texture, a_vertex.xy ).xyz * u_mask;\\\\n\\\\\\r\\n\\t\\tfloat pos = min(1.0,length(color));\\\\n\\\\\\r\\n\\t\\tgl_Position = vec4(pos*2.0-1.0,0.5,0.0,1.0);\\\\n\\\\\\r\\n\\t\\tgl_PointSize = 1.0;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nLGraphHistogram.pixel_shader = \\\"precision highp float;\\\\n\\\\\\r\\n\\tuniform float u_factor;\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tgl_FragColor = vec4(u_factor);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nLGraphHistogram.line_vertex_shader = \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\tuniform vec3 u_mask;\\\\n\\\\\\r\\n\\tuniform float u_scale;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tvec3 color = texture2D( u_texture, a_vertex.xy ).xyz * u_mask;\\\\n\\\\\\r\\n\\t\\tfloat pos = length(color);\\\\n\\\\\\r\\n\\t\\tgl_Position = vec4(a_vertex.x*2.0-1.0, u_scale*pos*2.0-1.0,0.0,1.0);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nLGraphHistogram.line_pixel_shader = \\\"precision highp float;\\\\n\\\\\\r\\n\\tuniform vec3 u_mask;\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tgl_FragColor = vec4(u_mask,1.0);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nLiteGraph.registerNodeType(\\\"texture/histogram\\\", LGraphHistogram );\\r\\n\\r\\n\\r\\nfunction LGraphCameraMotionBlur()\\r\\n{\\r\\n\\tthis.addInput(\\\"color\\\",\\\"Texture\\\");\\r\\n\\tthis.addInput(\\\"depth\\\",\\\"Texture\\\");\\r\\n\\tthis.addInput(\\\"camera\\\",\\\"Camera\\\");\\r\\n\\tthis.addOutput(\\\"out\\\",\\\"Texture\\\");\\r\\n\\tthis.properties = { enabled: true, intensity: 1, ghosting_mitigation: true, ghosting_threshold: 0.4, freeze_camera: false, low_quality: false, precision: LGraphTexture.DEFAULT };\\r\\n\\r\\n\\tthis._inv_matrix = mat4.create();\\r\\n\\tthis._previous_viewprojection_matrix = mat4.create();\\r\\n\\r\\n\\tthis._uniforms = { \\r\\n\\t\\tu_color_texture:0,\\r\\n\\t\\tu_depth_texture:1,\\r\\n\\t\\tu_inv_vp: this._inv_matrix,\\r\\n\\t\\tu_intensity: 1,\\r\\n\\t\\tu_camera_planes: null,\\r\\n\\t\\tu_viewprojection_matrix: null,\\r\\n\\t\\tu_previous_viewprojection_matrix: this._previous_viewprojection_matrix\\r\\n\\t};\\r\\n}\\r\\n\\r\\nLGraphCameraMotionBlur.widgets_info = {\\r\\n\\t\\\"precision\\\": { widget:\\\"combo\\\", values: litegraph_texture_found ? LGraphTexture.MODE_VALUES : [] }\\r\\n};\\r\\n\\r\\nLGraphCameraMotionBlur.title = \\\"Camera Motion Blur\\\";\\r\\nLGraphCameraMotionBlur.desc = \\\"A motion blur but only for camera movement\\\";\\r\\n\\r\\nLGraphCameraMotionBlur.prototype.onExecute = function()\\r\\n{\\r\\n\\tvar tex = this.getInputData(0);\\r\\n\\tvar depth = this.getInputData(1);\\r\\n\\tvar camera = this.getInputData(2);\\r\\n\\r\\n\\tif( !this.isOutputConnected(0) || !tex || !depth || !camera)\\r\\n\\t\\treturn; //saves work\\r\\n\\r\\n\\tvar enabled = this.getInputData(3);\\r\\n\\tif(enabled != null)\\r\\n\\t\\tthis.properties.enabled = Boolean( enabled );\\r\\n\\r\\n\\tif(this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false )\\r\\n\\t{\\r\\n\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar width = tex.width;\\r\\n\\tvar height = tex.height;\\r\\n\\tvar type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT;\\r\\n\\tif (this.precision === LGraphTexture.DEFAULT)\\r\\n\\t\\ttype = tex.type;\\r\\n\\tif(!this._tex || this._tex.width != width || this._tex.height != height || this._tex.type != type )\\r\\n\\t\\tthis._tex = new GL.Texture( width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR });\\r\\n\\r\\n\\tif( this.properties.low_quality )\\r\\n\\t{\\r\\n\\t\\tif(!LGraphCameraMotionBlur._shader_low)\\r\\n\\t\\t{\\r\\n\\t\\t\\tLGraphCameraMotionBlur._shader_low = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphCameraMotionBlur.pixel_shader, { SAMPLES:\\\"4\\\" } );\\r\\n\\t\\t\\tLGraphCameraMotionBlur._shader_no_ghosting_low = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphCameraMotionBlur.pixel_shader, { GHOST_CORRECTION: \\\"\\\", SAMPLES:\\\"4\\\" } );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tif(!LGraphCameraMotionBlur._shader)\\r\\n\\t\\t{\\r\\n\\t\\t\\tLGraphCameraMotionBlur._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphCameraMotionBlur.pixel_shader );\\r\\n\\t\\t\\tLGraphCameraMotionBlur._shader_no_ghosting = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphCameraMotionBlur.pixel_shader, { GHOST_CORRECTION: \\\"\\\" } );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar shader = null;\\r\\n\\t\\r\\n\\tif( this.properties.low_quality )\\r\\n\\t\\tshader = this.properties.ghosting_mitigation ? LGraphCameraMotionBlur._shader_no_ghosting_low : LGraphCameraMotionBlur._shader_low;\\r\\n\\telse\\r\\n\\t\\tshader = this.properties.ghosting_mitigation ? LGraphCameraMotionBlur._shader_no_ghosting : LGraphCameraMotionBlur._shader;\\r\\n\\r\\n\\tvar inv = this._inv_matrix;\\r\\n\\tvar vp = camera._viewprojection_matrix;\\r\\n\\tvar prev = this._previous_viewprojection_matrix;\\r\\n\\tvar optimize = false; //skip algorithm when camera hasnt moved\\r\\n\\tvar intensity = this.properties.intensity;\\r\\n\\r\\n\\tvar uniforms = this._uniforms;\\r\\n\\tuniforms.u_intensity = intensity;\\r\\n\\tuniforms.u_viewprojection_matrix = camera._viewprojection_matrix;\\r\\n\\tuniforms.u_camera_planes = camera._uniforms.u_camera_planes;\\r\\n\\tuniforms.u_ghosting_threshold = this.properties.ghosting_threshold || 0.4;\\r\\n\\r\\n\\tvar diff = 0;\\r\\n\\tfor(var i = 0; i < prev.length; ++i)\\r\\n\\t\\tdiff += Math.abs( vp[i] - prev[i] );\\r\\n\\tif(diff < 0.0001 && optimize) //no camera movement, skip process\\r\\n\\t{\\r\\n\\t\\ttex.copyTo( this._tex );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tmat4.invert( inv, camera._viewprojection_matrix );\\r\\n\\t\\tthis._tex.drawTo(function() {\\r\\n\\t\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\t\\tgl.disable( gl.CULL_FACE );\\r\\n\\t\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tdepth.bind(1);\\r\\n\\t\\t\\tvar mesh = GL.Mesh.getScreenQuad();\\r\\n\\t\\t\\tshader.uniforms( uniforms ).draw( mesh );\\r\\n\\t\\t});\\r\\n\\t}\\r\\n\\r\\n\\tif(!this.properties.freeze_camera)\\r\\n\\t\\tprev.set( camera._viewprojection_matrix );\\r\\n\\r\\n\\tthis.setOutputData( 0, this._tex );\\r\\n}\\r\\n\\r\\nLGraphCameraMotionBlur.prototype.onGetInputs = function()\\r\\n{\\r\\n\\treturn [[\\\"enabled\\\",\\\"boolean\\\"]];\\r\\n}\\r\\n\\r\\nLGraphCameraMotionBlur.pixel_shader = \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_color_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_depth_texture;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_inv_vp;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_viewprojection_matrix;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_previous_viewprojection_matrix;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_camera_planes;\\\\n\\\\\\r\\n\\t\\tuniform float u_intensity;\\\\n\\\\\\r\\n\\t\\tuniform float u_ghosting_threshold;\\\\n\\\\\\r\\n\\t\\t#ifndef SAMPLES\\\\n\\\\\\r\\n\\t\\t\\t#define SAMPLES 16\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv = v_coord;\\\\n\\\\\\r\\n\\t\\t\\tfloat depth = texture2D(u_depth_texture, uv).x;\\\\n\\\\\\r\\n\\t\\t\\tfloat zNear = u_camera_planes.x;\\\\n\\\\\\r\\n\\t\\t\\tfloat zFar = u_camera_planes.y;\\\\n\\\\\\r\\n\\t\\t\\t//float z = (2.0 * zNear) / (zFar + zNear - depth * (zFar - zNear));\\\\n\\\\\\r\\n\\t\\t\\tdepth = depth * 2.0 - 1.0;\\\\n\\\\\\r\\n\\t\\t\\tfloat z = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\\\\n\\\\\\r\\n\\t\\t\\tvec4 screenpos = vec4( uv * 2.0 - vec2(1.0), depth, 1.0 );\\\\n\\\\\\r\\n\\t\\t\\tvec4 pos = u_inv_vp * screenpos;\\\\n\\\\\\r\\n\\t\\t\\tpos /= pos.w;\\\\n\\\\\\r\\n\\t\\t\\tvec4 old_screenpos = u_previous_viewprojection_matrix * pos;\\\\n\\\\\\r\\n\\t\\t\\told_screenpos /= old_screenpos.w;\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv_final = old_screenpos.xy * 0.5 + vec2(0.5);\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv_delta = (uv_final - uv);\\\\n\\\\\\r\\n\\t\\t\\tuv -= uv_delta * 0.5;\\\\n\\\\\\r\\n\\t\\t\\t//uv_delta *= 1.0 - z;\\\\n\\\\\\r\\n\\t\\t\\tuv_delta *= u_intensity / float(SAMPLES);\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = vec4(0.0);\\\\n\\\\\\r\\n\\t\\t\\tfloat total = 0.0;\\\\n\\\\\\r\\n\\t\\t\\tfloat amount = 1.0;\\\\n\\\\\\r\\n\\t\\t\\tfor(int i = 0; i < SAMPLES; ++i)\\\\n\\\\\\r\\n\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t#ifdef GHOST_CORRECTION\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tfloat old_depth = texture2D(u_depth_texture, uv).x;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\told_depth = old_depth * 2.0 - 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tfloat old_z = zNear * (old_depth + 1.0) / (zFar + zNear - old_depth * (zFar - zNear));\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif( abs(old_z - z) > u_ghosting_threshold )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tuv += uv_delta;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor += texture2D( u_color_texture, uv );\\\\n\\\\\\r\\n\\t\\t\\t\\tuv += uv_delta;\\\\n\\\\\\r\\n\\t\\t\\t\\ttotal += amount;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = color / total;\\\\n\\\\\\r\\n\\t\\t\\t//gl_FragColor = vec4( abs( old_screenpos.xy ), 1.0, 1.0 );\\\\n\\\\\\r\\n\\t\\t\\t//gl_FragColor = texture2D( u_color_texture, uv_final );\\\\n\\\\\\r\\n\\t\\t\\t//gl_FragColor = vec4( abs( pos.xyz * 0.001 ), 1.0 );\\\\n\\\\\\r\\n\\t\\t\\t//gl_FragColor = vec4( vec3( abs(old_z - z) ), 1.0);\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\nLiteGraph.registerNodeType(\\\"texture/motionBlur\\\", LGraphCameraMotionBlur );\\r\\n\\r\\n\\r\\nfunction LGraphVolumetricLight()\\r\\n{\\r\\n\\tthis.addInput(\\\"color\\\",\\\"Texture\\\");\\r\\n\\tthis.addInput(\\\"depth\\\",\\\"Texture\\\");\\r\\n\\tthis.addInput(\\\"camera\\\",\\\"Camera\\\");\\r\\n\\tthis.addInput(\\\"light\\\",\\\"Light,Component\\\");\\r\\n\\tthis.addOutput(\\\"out\\\",\\\"Texture\\\");\\r\\n\\tthis.properties = { enabled: true, intensity: 1, attenuation: 2.0, precision: LGraphTexture.DEFAULT };\\r\\n\\r\\n\\tthis._inv_matrix = mat4.create();\\r\\n\\r\\n\\tthis._uniforms = { \\r\\n\\t\\tu_color_texture:0,\\r\\n\\t\\tu_depth_texture:1,\\r\\n\\t\\tu_shadow_texture:2,\\r\\n\\t\\tu_noise_texture:3,\\r\\n\\t\\tu_intensity: 1,\\r\\n\\t\\tu_camera_planes: null,\\r\\n\\t\\tu_inv_vp: this._inv_matrix,\\r\\n\\t\\tu_rand: vec2.create()\\r\\n\\t};\\r\\n}\\r\\n\\r\\nLGraphVolumetricLight.widgets_info = {\\r\\n\\t\\\"precision\\\": { widget:\\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n};\\r\\n\\r\\nLGraphVolumetricLight.title = \\\"Volumetric Light\\\";\\r\\nLGraphVolumetricLight.desc = \\\"Adds fog with volumetric light\\\";\\r\\n\\r\\nLGraphVolumetricLight.prototype.onExecute = function()\\r\\n{\\r\\n\\tvar tex = this.getInputData(0);\\r\\n\\tvar depth = this.getInputData(1);\\r\\n\\tvar camera = this.getInputData(2);\\r\\n\\tvar light = this.getInputData(3);\\r\\n\\r\\n\\tif( !this.isOutputConnected(0))\\r\\n\\t\\treturn; //saves work\\r\\n\\r\\n\\tvar enabled = this.getInputData(4);\\r\\n\\tif(enabled != null)\\r\\n\\t\\tthis.properties.enabled = Boolean( enabled );\\r\\n\\r\\n\\tvar shadowmap = null;\\r\\n\\tif(light && light._shadowmap)\\r\\n\\t\\tshadowmap = light._shadowmap.texture;\\r\\n\\r\\n\\tif(this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false || !depth || !camera || !light || !shadowmap )\\r\\n\\t{\\r\\n\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar width = depth.width;\\r\\n\\tvar height = depth.height;\\r\\n\\tvar type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT;\\r\\n\\tif (this.precision === LGraphTexture.DEFAULT)\\r\\n\\t\\ttype = tex ? tex.type : gl.UNSIGNED_BYTE;\\r\\n\\tif(!this._tex || this._tex.width != width || this._tex.height != height || this._tex.type != type )\\r\\n\\t\\tthis._tex = new GL.Texture( width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR });\\r\\n\\r\\n\\tif(!LGraphVolumetricLight._shader_spot)\\r\\n\\t{\\r\\n\\t\\tLGraphVolumetricLight._shader_spot = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphVolumetricLight.pixel_shader, { USE_SPOT:\\\"\\\" } );\\r\\n\\t\\tLGraphVolumetricLight._shader_directional = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphVolumetricLight.pixel_shader, { USE_DIRECTIONAL:\\\"\\\" } );\\r\\n\\t\\t//LGraphVolumetricLight._shader_omni = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphVolumetricLight.pixel_shader, { USE_OMNI:\\\"\\\" } );\\r\\n\\t}\\r\\n\\r\\n\\tvar shader = null;\\r\\n\\r\\n\\tswitch( light.type )\\r\\n\\t{\\r\\n\\t\\tcase LS.Light.SPOT: shader = LGraphVolumetricLight._shader_spot; break;\\r\\n\\t\\tcase LS.Light.DIRECTIONAL: shader = LGraphVolumetricLight._shader_directional; break;\\r\\n\\t\\tcase LS.Light.OMNI: //shader = LGraphVolumetricLight._shader_omni;\\r\\n\\t\\t\\t//not supported yet\\r\\n\\t\\t\\tconsole.warn(\\\"volumetric light not supported for omni lights\\\");\\r\\n\\t\\t\\tthis.properties.enabled = false;\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar vp = camera._viewprojection_matrix;\\r\\n\\tvar intensity = this.properties.intensity;\\r\\n\\tvar inv = this._inv_matrix;\\r\\n\\tmat4.invert( inv, camera._viewprojection_matrix );\\r\\n\\r\\n\\tvar uniforms = this._uniforms;\\r\\n\\tuniforms.u_intensity = intensity;\\r\\n\\tuniforms.u_camera_planes = camera._uniforms.u_camera_planes;\\r\\n\\tuniforms.u_shadow_params = light._uniforms.u_shadow_params;\\r\\n\\tuniforms.u_attenuation = this.properties.attenuation;\\r\\n\\tuniforms.u_rand[1] = uniforms.u_rand[0] = 0;\\r\\n\\r\\n\\tvar light_uniforms = light._uniforms;\\r\\n\\r\\n\\tif(!tex)\\r\\n\\t\\ttex = LS.Renderer._black_texture;\\r\\n\\tvar noise_texture = LGraphTexture.getNoiseTexture();\\r\\n\\r\\n\\tthis._tex.drawTo(function() {\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.disable( gl.CULL_FACE );\\r\\n\\t\\tgl.disable( gl.BLEND );\\r\\n\\r\\n\\t\\tif(!light.enabled)\\r\\n\\t\\t\\ttex.toViewport();\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\ttex.bind(0);\\r\\n\\t\\t\\tdepth.bind(1);\\r\\n\\t\\t\\tshadowmap.bind(2);\\r\\n\\t\\t\\tnoise_texture.bind(3);\\r\\n\\t\\t\\tvar mesh = GL.Mesh.getScreenQuad();\\r\\n\\t\\t\\tshader.uniforms( light_uniforms );\\r\\n\\t\\t\\tshader.uniforms( uniforms ).draw( mesh );\\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\tthis.setOutputData( 0, this._tex );\\r\\n}\\r\\n\\r\\nLGraphVolumetricLight.prototype.onGetInputs = function()\\r\\n{\\r\\n\\treturn [[\\\"enabled\\\",\\\"boolean\\\"]];\\r\\n}\\r\\n\\r\\n/* from http://www.alexandre-pestana.com/volumetric-lights/\\r\\n// Mie scaterring approximated with Henyey-Greenstein phase function.\\r\\n\\r\\naccumFog += ComputeScattering(dot(rayDirection, sunDirection)).xxx * g_SunColor;\\r\\n*/\\r\\n\\r\\nLGraphVolumetricLight.pixel_shader = \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_color_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_depth_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_shadow_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_noise_texture;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_inv_vp;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_light_viewprojection_matrix;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_camera_planes;\\\\n\\\\\\r\\n\\t\\tuniform vec4 u_shadow_params;\\\\n\\\\\\r\\n\\t\\tuniform vec4 u_light_info;\\\\n\\\\\\r\\n\\t\\tuniform vec3 u_light_front;\\\\n\\\\\\r\\n\\t\\tuniform vec3 u_light_color;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_light_matrix;\\\\n\\\\\\r\\n\\t\\tuniform float u_intensity;\\\\n\\\\\\r\\n\\t\\tuniform float u_attenuation;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_rand;\\\\n\\\\\\r\\n\\t\\tconst float G_SCATTERING = 0.5;\\\\n\\\\\\r\\n\\t\\tconst float PI = 3.14159265358979323846;\\\\n\\\\\\r\\n\\t\\tfloat ComputeScattering(float lightDotView)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tfloat result = 1.0 - G_SCATTERING * G_SCATTERING;\\\\n\\\\\\r\\n\\t\\t\\tresult /= (4.0 * PI * pow(1.0 + G_SCATTERING * G_SCATTERING - (2.0 * G_SCATTERING) * lightDotView, 1.5));\\\\n\\\\\\r\\n\\t\\t\\treturn result;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t#define SAMPLES 64\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv = v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = texture2D(u_color_texture, uv);\\\\n\\\\\\r\\n\\t\\t\\tfloat depth = texture2D(u_depth_texture, uv).x;\\\\n\\\\\\r\\n\\t\\t\\tfloat zNear = u_camera_planes.x;\\\\n\\\\\\r\\n\\t\\t\\tfloat zFar = u_camera_planes.y;\\\\n\\\\\\r\\n\\t\\t\\tdepth = depth * 2.0 - 1.0;\\\\n\\\\\\r\\n\\t\\t\\tfloat z = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\\\\n\\\\\\r\\n\\t\\t\\tvec4 screenpos = vec4( uv * 2.0 - vec2(1.0), depth, 1.0 );\\\\n\\\\\\r\\n\\t\\t\\tvec4 farpos = u_inv_vp * screenpos;\\\\n\\\\\\r\\n\\t\\t\\tfarpos /= farpos.w;\\\\n\\\\\\r\\n\\t\\t\\tscreenpos.z = 0.0;\\\\n\\\\\\r\\n\\t\\t\\tvec4 nearpos = u_inv_vp * screenpos;\\\\n\\\\\\r\\n\\t\\t\\tnearpos.xyz /= nearpos.w;\\\\n\\\\\\r\\n\\t\\t\\tvec3 rayDir = (farpos.xyz - nearpos.xyz) / float(SAMPLES);\\\\n\\\\\\r\\n\\t\\t\\tfloat weight = length(rayDir);\\\\n\\\\\\r\\n\\t\\t\\tvec4 current_pos = vec4( nearpos.xyz, 1.0 );\\\\n\\\\\\r\\n\\t\\t\\tcurrent_pos.xyz += rayDir * texture2D(u_noise_texture, uv * 2.0 + u_rand ).x;\\\\n\\\\\\r\\n\\t\\t\\tfloat brightness = 0.0;\\\\n\\\\\\r\\n\\t\\t\\tfloat bias = u_shadow_params.y;\\\\n\\\\\\r\\n\\t\\t\\tfloat accum = ComputeScattering( dot( normalize(rayDir), -u_light_front));\\\\n\\\\\\r\\n\\t\\t\\tfor(int i = 0; i < SAMPLES; ++i)\\\\n\\\\\\r\\n\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 light_uv = u_light_matrix * current_pos;\\\\n\\\\\\r\\n\\t\\t\\t\\tlight_uv.xy /= light_uv.w;\\\\n\\\\\\r\\n\\t\\t\\t\\tlight_uv.xy = light_uv.xy * 0.5 + vec2(0.5);\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat shadow_depth = texture2D( u_shadow_texture, light_uv.xy ).x;\\\\n\\\\\\r\\n\\t\\t\\t\\tif (((light_uv.z - bias) / light_uv.w * 0.5 + 0.5) < shadow_depth )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tbrightness += accum * weight;\\\\n\\\\\\r\\n\\t\\t\\t\\tcurrent_pos.xyz += rayDir;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t//color.xyz += u_intensity * u_light_color * brightness / float(SAMPLES);\\\\n\\\\\\r\\n\\t\\t\\tcolor.xyz = mix(color.xyz, u_light_color, clamp( pow(u_intensity * brightness / float(SAMPLES), u_attenuation),0.0,1.0));\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\nLiteGraph.registerNodeType(\\\"texture/volumetric_light\\\", LGraphVolumetricLight );\\r\\n\\r\\nif( LiteGraph.Nodes.LGraphTextureCanvas2D )\\r\\n{\\r\\n\\r\\n\\tfunction LGraphTextureCanvas2DFromScript() {\\r\\n this.addInput(\\\"v\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"Texture\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tfilename: \\\"\\\",\\r\\n\\t\\t\\twidth: 512,\\r\\n\\t\\t\\theight: 512,\\r\\n\\t\\t\\tclear: true,\\r\\n\\t\\t\\tprecision: LGraphTexture.DEFAULT,\\r\\n\\t\\t\\tuse_html_canvas: false\\r\\n\\t\\t};\\r\\n\\t\\tthis._func = null;\\r\\n\\t\\tthis._temp_texture = null;\\r\\n\\t\\tthis.size = [180,30];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureCanvas2DFromScript.title = \\\"Canvas2DFromScript\\\";\\r\\n\\tLGraphTextureCanvas2DFromScript.desc = \\\"Executes Canvas2D script file inside a texture or the viewport.\\\";\\r\\n\\tLGraphTextureCanvas2DFromScript.help = \\\"Set width and height to 0 to match viewport size.\\\";\\r\\n\\r\\n\\tLGraphTextureCanvas2DFromScript.widgets_info = {\\r\\n\\t\\tprecision: { widget: \\\"combo\\\", values: LGraphTexture.MODE_VALUES },\\r\\n\\t\\tfilename: { type: \\\"script\\\" },\\r\\n\\t\\twidth: { type: \\\"Number\\\", precision: 0, step: 1 },\\r\\n\\t\\theight: { type: \\\"Number\\\", precision: 0, step: 1 }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphTextureCanvas2DFromScript.prototype.onPropertyChanged = function(\\tname, value ) {\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tif (name == \\\"filename\\\" && LiteGraph.allow_scripts) {\\r\\n\\t\\t\\tthis._func = null;\\r\\n\\t\\t\\tif(!value)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tLS.ResourcesManager.load( value, function(script_resource){\\r\\n\\t\\t\\t\\tthat.compileCode(script_resource.data);\\r\\n\\t\\t\\t\\tthat._code_version = script_resource._version || 0;\\r\\n\\t\\t\\t});\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureCanvas2DFromScript.prototype.onExecute = function() {\\r\\n\\r\\n\\t\\tif (!this.isOutputConnected(0))\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar script_resource = LS.ResourcesManager.getResource( this.properties.filename );\\r\\n\\t\\tif( script_resource && script_resource._version != this._code_version )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.compileCode( script_resource.data );\\r\\n\\t\\t\\tthis._code_version = script_resource._version || 0;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar func = this._func;\\r\\n\\t\\tif (!func)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis.executeDraw( func );\\r\\n\\t}\\r\\n\\r\\n\\tLGraphTextureCanvas2DFromScript.prototype.getResources = function(res)\\r\\n\\t{\\r\\n\\t\\tif(this.properties.filename)\\r\\n\\t\\t\\tres[this.properties.filename] = true;\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\tLGraphTextureCanvas2DFromScript.prototype.compileCode = LiteGraph.Nodes.LGraphTextureCanvas2D.prototype.compileCode;\\r\\n\\r\\n\\tLGraphTextureCanvas2DFromScript.prototype.compileCode = LiteGraph.Nodes.LGraphTextureCanvas2D.prototype.compileCode;\\r\\n\\tLGraphTextureCanvas2DFromScript.prototype.executeDraw = LiteGraph.Nodes.LGraphTextureCanvas2D.prototype.executeDraw;\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"texture/canvas2DfromScript\\\", LGraphTextureCanvas2DFromScript);\\r\\n}\\r\\n/*\\r\\nfunction LGraphDepthAwareUpscale()\\r\\n{\\r\\n\\tthis.addInput(\\\"tex\\\",\\\"Texture\\\");\\r\\n\\tthis.addInput(\\\"lowdepth\\\",\\\"Texture\\\");\\r\\n\\tthis.addInput(\\\"highdepth\\\",\\\"Texture\\\");\\r\\n\\tthis.addInput(\\\"camera\\\",\\\"Camera\\\");\\r\\n\\tthis.addOutput(\\\"out\\\",\\\"Texture\\\");\\r\\n\\tthis.properties = { enabled: true, precision: LGraphTexture.DEFAULT };\\r\\n\\r\\n\\tthis._uniforms = { \\r\\n\\t\\tu_color_texture:0,\\r\\n\\t\\tu_lowdepth_texture:1,\\r\\n\\t\\tu_highdepth_texture:2,\\r\\n\\t\\tu_lowsize: vec4.create(),\\r\\n\\t\\tu_highsize: vec4.create(),\\r\\n\\t\\tu_viewprojection: null\\r\\n\\t};\\r\\n}\\r\\n\\r\\nLGraphDepthAwareUpscale.widgets_info = {\\r\\n\\t\\\"precision\\\": { widget:\\\"combo\\\", values: LGraphTexture.MODE_VALUES }\\r\\n};\\r\\n\\r\\nLGraphDepthAwareUpscale.title = \\\"DepthAware Upscale\\\";\\r\\nLGraphDepthAwareUpscale.desc = \\\"Upscales a texture\\\";\\r\\n\\r\\nLGraphDepthAwareUpscale.prototype.onExecute = function()\\r\\n{\\r\\n\\tvar tex = this.getInputData(0);\\r\\n\\tvar lowdepth = this.getInputData(1);\\r\\n\\tvar highdepth = this.getInputData(2);\\r\\n\\tvar camera = this.getInputData(3);\\r\\n\\r\\n\\tif( !this.isOutputConnected(0) || !tex )\\r\\n\\t\\treturn; //saves work\\r\\n\\r\\n\\tvar enabled = this.getInputData(4);\\r\\n\\tif(enabled != null)\\r\\n\\t\\tthis.properties.enabled = Boolean( enabled );\\r\\n\\r\\n\\tif(this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false || !lowdepth || !highdepth || !camera )\\r\\n\\t{\\r\\n\\t\\tthis.setOutputData(0, tex);\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar width = tex.width;\\r\\n\\tvar height = tex.height;\\r\\n\\tvar type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT;\\r\\n\\tif (this.precision === LGraphTexture.DEFAULT)\\r\\n\\t\\ttype = tex ? tex.type : gl.UNSIGNED_BYTE;\\r\\n\\tif(!this._tex || this._tex.width != width || this._tex.height != height || this._tex.type != type || this._tex.format != tex.format )\\r\\n\\t\\tthis._tex = new GL.Texture( width, height, { type: type, format: tex.format, filter: gl.LINEAR });\\r\\n\\r\\n\\tif(!LGraphDepthAwareUpscale._shader)\\r\\n\\t\\tLGraphDepthAwareUpscale._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphDepthAwareUpscale.pixel_shader );\\r\\n\\r\\n\\tvar shader = null;\\r\\n\\r\\n\\tvar uniforms = this._uniforms;\\r\\n\\tuniforms.u_lowsize[0] = lowdepth.width; uniforms.u_lowsize[1] = lowdepth.height; \\tuniforms.u_lowsize[2] = 1/lowdepth.width; uniforms.u_lowsize[3] = 1/lowdepth.height;\\r\\n\\tuniforms.u_highsize[0] = highdepth.width; uniforms.u_highsize[1] = highdepth.height; uniforms.u_highsize[2] = 1/highdepth.width; uniforms.u_highsize[3] = 1/highdepth.height;\\r\\n\\tuniforms.u_viewprojection = camera._viewprojection_matrix;\\r\\n\\r\\n\\tthis._tex.drawTo(function() {\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.disable( gl.CULL_FACE );\\r\\n\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\ttex.bind(0);\\r\\n\\t\\tlowdepth.bind(1);\\r\\n\\t\\thighdepth.bind(2);\\r\\n\\t\\tvar mesh = GL.Mesh.getScreenQuad();\\r\\n\\t\\tshader.uniforms( uniforms ).draw( mesh );\\r\\n\\t});\\r\\n\\r\\n\\tthis.setOutputData( 0, this._tex );\\r\\n}\\r\\n\\r\\nLGraphDepthAwareUpscale.pixel_shader = \\\"precision highp float;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_color_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_lowdepth_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_highdepth_texture;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform vec4 u_lowsize;\\\\n\\\\\\r\\n\\t\\tuniform vec4 u_highsize;\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv = v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvec2 low_ltuv = floor(v_coord * u_lowsize.xy) * u_lowsize.zw;\\\\n\\\\\\r\\n\\t\\t\\tvec2 low_rbuv = ceil(v_coord * u_lowsize.xy) * u_lowsize.zw;\\\\n\\\\\\r\\n\\t\\t\\tvec2 high_ltuv = floor(v_coord * u_highsize.xy) * u_highsize.zw;\\\\n\\\\\\r\\n\\t\\t\\tvec2 high_rbuv = ceil(v_coord * u_highsize.xy) * u_highsize.zw;\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = texture2D(u_color_texture, uv);\\\\n\\\\\\r\\n\\t\\t\\tfloat lowdepth = texture2D(u_lowdepth_texture, uv).x;\\\\n\\\\\\r\\n\\t\\t\\tfloat highdepth = texture2D(u_highdepth_texture, uv).x;\\\\n\\\\\\r\\n\\t\\t\\tcolor.xyz = mix(color.xyz, u_light_color, clamp( pow(u_intensity * brightness / float(SAMPLES), u_attenuation),0.0,1.0));\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\nLiteGraph.registerNodeType(\\\"texture/depthaware_upscale\\\", LGraphDepthAwareUpscale );\\r\\n*/\\r\\n\\r\\n\\r\\n}\\r\\n///@FILE:../src/graph/actions.js\\r\\n//not used yet\\r\\n//meant for triggers\\r\\n///@FILE:../src/graph/helpers.js\\r\\n///@INFO: GRAPHS\\r\\nif(typeof(LiteGraph) != \\\"undefined\\\")\\r\\n{\\r\\n\\tLiteGraph.CORNER_TOP_LEFT = 0;\\r\\n\\tLiteGraph.CORNER_TOP_RIGHT = 1;\\r\\n\\tLiteGraph.CORNER_BOTTOM_LEFT = 2;\\r\\n\\tLiteGraph.CORNER_BOTTOM_RIGHT = 3;\\r\\n\\tLiteGraph.CORNER_TOP_CENTER = 4;\\r\\n\\tLiteGraph.CORNER_BOTTOM_CENTER = 5;\\r\\n\\r\\n\\tvar corner_options = { type:\\\"enum\\\", values:{ \\r\\n\\t\\t\\\"top-left\\\": LiteGraph.CORNER_TOP_LEFT, \\r\\n\\t\\t\\\"top-right\\\": LiteGraph.CORNER_TOP_RIGHT,\\r\\n\\t\\t\\\"bottom-left\\\": LiteGraph.CORNER_BOTTOM_LEFT,\\r\\n\\t\\t\\\"bottom-right\\\": LiteGraph.CORNER_BOTTOM_RIGHT,\\r\\n\\t\\t\\\"top-center\\\": LiteGraph.CORNER_TOP_CENTER,\\r\\n\\t\\t\\\"bottom-center\\\": LiteGraph.CORNER_BOTTOM_CENTER\\r\\n\\t}};\\r\\n\\r\\n\\tfunction positionToArea( position, corner, area )\\r\\n\\t{\\r\\n\\t\\tvar x = position[0];\\r\\n\\t\\tvar y = position[1];\\r\\n\\r\\n\\t\\tswitch( corner )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase LiteGraph.CORNER_TOP_RIGHT: x = gl.canvas.width - x; break;\\r\\n\\t\\t\\tcase LiteGraph.CORNER_BOTTOM_LEFT: y = gl.canvas.height - y; break;\\r\\n\\t\\t\\tcase LiteGraph.CORNER_BOTTOM_RIGHT: x = gl.canvas.width - x; y = gl.canvas.height - y; break;\\r\\n\\t\\t\\tcase LiteGraph.CORNER_TOP_CENTER: x = gl.canvas.width * 0.5; break;\\r\\n\\t\\t\\tcase LiteGraph.CORNER_BOTTOM_CENTER: x = gl.canvas.width * 0.5; y = gl.canvas.height - y; break;\\r\\n\\t\\t\\tcase LiteGraph.CORNER_TOP_LEFT:\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tarea[0] = x;\\r\\n\\t\\tarea[1] = y;\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\tfunction LGraphGetMesh() {\\r\\n\\t\\tthis.addOutput(\\\"out\\\", \\\"mesh\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tname: \\\"\\\"\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGetMesh.title = \\\"mesh\\\";\\r\\n\\tLGraphGetMesh.desc = \\\"gets mesh\\\";\\r\\n\\r\\n\\tLGraphGetMesh.widgets_info = {\\r\\n\\t\\tname: { widget: \\\"resource\\\" }\\r\\n\\t};\\r\\n\\r\\n\\tLGraphGetMesh.prototype.onExecute = function() {\\r\\n\\t\\tvar mesh = null;\\r\\n\\t\\tif(this.properties.name)\\r\\n\\t\\t\\tmesh = LS.ResourcesManager.meshes[this.properties.name];\\r\\n\\t\\tif(mesh && mesh.constructor !== GL.Mesh)\\r\\n\\t\\t\\tmesh = null;\\r\\n\\t\\tthis.setOutputData(0,mesh);\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGetMesh.prototype.getResources = function(o)\\r\\n\\t{\\r\\n\\t\\tif(this.properties.name)\\r\\n\\t\\t\\to[this.properties.name] = true;\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType( \\\"geometry/getMesh\\\", LGraphGetMesh );\\t\\r\\n\\r\\n\\t//special kind of node\\r\\n\\tfunction LGraphGUIPanel()\\r\\n\\t{\\r\\n\\t\\tthis.properties = { enabled: true, title: \\\"\\\", color: [0.1,0.1,0.1], opacity: 0.7, titlecolor: [0,0,0], position: [10,10], size: [300,200], rounding: 8, corner: LiteGraph.CORNER_TOP_LEFT };\\r\\n\\t\\tthis._pos = vec4.create();\\r\\n\\t\\tthis._color = vec4.create();\\r\\n\\t\\tthis._titlecolor = vec4.create();\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIPanel.title = \\\"GUIPanel\\\";\\r\\n\\tLGraphGUIPanel.desc = \\\"renders a rectangle on webgl canvas\\\";\\r\\n\\tLGraphGUIPanel.priority = -1; //render first\\r\\n\\r\\n\\tLGraphGUIPanel[\\\"@corner\\\"] = corner_options;\\r\\n\\tLGraphGUIPanel[\\\"@color\\\"] = { type:\\\"color\\\" };\\r\\n\\tLGraphGUIPanel[\\\"@titlecolor\\\"] = { type:\\\"color\\\" };\\r\\n\\tLGraphGUIPanel[\\\"@opacity\\\"] = { widget:\\\"slider\\\", min:0,max:1 };\\r\\n\\r\\n\\tLGraphGUIPanel.prototype.onRenderGUI = function()\\r\\n\\t{ \\r\\n\\t\\tvar ctx = window.gl;\\r\\n\\t\\tif(!ctx || !this.properties.enabled)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tthis._color.set( this.properties.color || [0.1,0.1,0.1] );\\r\\n\\t\\tthis._color[3] = this.properties.opacity;\\r\\n\\t\\tctx.fillColor = this._color;\\r\\n\\t\\tpositionToArea( this.properties.position, this.properties.corner, this._pos );\\r\\n\\t\\tthis._pos[2] = this.properties.size[0];\\r\\n\\t\\tthis._pos[3] = this.properties.size[1];\\r\\n\\r\\n\\t\\t//var mouse = LS.Input.current_click;\\r\\n\\t\\t//var clicked = LS.Input.isEventInRect( mouse, this._pos, LS.GUI._offset );\\r\\n\\t\\t//if(clicked)\\r\\n\\t\\t//\\tLS.Input.current_click = false; //consume event\\r\\n\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.enable( gl.BLEND );\\r\\n\\t\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );\\r\\n\\t\\tvar rounding = Math.min(15,this.properties.rounding);\\r\\n\\t\\tif(rounding > 0)\\r\\n\\t\\t{\\r\\n\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\tctx.roundRect( this._pos[0], this._pos[1], this.properties.size[0], this.properties.size[1], rounding, rounding );\\r\\n\\t\\t\\tctx.fill();\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tctx.fillRect( this._pos[0], this._pos[1], this.properties.size[0], this.properties.size[1] );\\r\\n\\r\\n\\t\\tif(this.properties.title)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._titlecolor.set( this.properties.titlecolor || [0.3,0.3,0.3] );\\r\\n\\t\\t\\tthis._titlecolor[3] = this.properties.opacity;\\r\\n\\t\\t\\tctx.fillColor = this._titlecolor;\\r\\n\\t\\t\\tif(rounding > 0)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\t\\tctx.roundRect( this._pos[0], this._pos[1], this.properties.size[0], 30, rounding, 0 );\\r\\n\\t\\t\\t\\tctx.fill();\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tctx.fillRect( this._pos[0], this._pos[1], this.properties.size[0], 30 );\\r\\n\\t\\t\\tctx.fillColor = [0.8,0.8,0.8,this.properties.opacity];\\r\\n\\t\\t\\tctx.font = \\\"20px Arial\\\";\\r\\n\\t\\t\\tctx.fillText( this.properties.title, 10 + this._pos[0],24 + this._pos[1]);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"gui/panel\\\", LGraphGUIPanel );\\r\\n\\r\\n\\tfunction LGraphGUIText()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"text\\\");\\r\\n\\t\\tthis.properties = { enabled: true, text: \\\"\\\", font: \\\"\\\", color: [1,1,1,1], precision: 3, position: [20,20], corner: LiteGraph.CORNER_TOP_LEFT };\\r\\n\\t\\tthis._pos = vec2.create();\\r\\n\\t\\tthis._text = \\\"\\\";\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIText.title = \\\"GUIText\\\";\\r\\n\\tLGraphGUIText.desc = \\\"renders text on webgl canvas\\\";\\r\\n\\r\\n\\tLGraphGUIText[\\\"@corner\\\"] = corner_options;\\r\\n\\tLGraphGUIText[\\\"@color\\\"] = { type:\\\"color\\\" };\\r\\n\\r\\n\\tLGraphGUIText.prototype.onGetInputs = function(){\\r\\n\\t\\treturn [[\\\"enabled\\\",\\\"boolean\\\"]];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIText.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tvar v = this.getInputData(0);\\r\\n\\t\\tif(v != null)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( v.constructor === Number )\\r\\n\\t\\t\\t\\tthis._text = v.toFixed( this.properties.precision );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis._text = String(v);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIText.prototype.onRenderGUI = function()\\r\\n\\t{ \\r\\n\\t\\tvar ctx = window.gl;\\r\\n\\t\\tif(!ctx)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar enabled = this.getInputOrProperty(\\\"enabled\\\");\\r\\n\\t\\tif(enabled === false)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar text = (this.properties.text || \\\"\\\") + this._text;\\r\\n\\t\\tif(text == \\\"\\\")\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tctx.font = this.properties.font || \\\"20px Arial\\\";\\r\\n\\t\\tctx.textAlign = \\\"left\\\";\\r\\n\\t\\tctx.fillColor = this.properties.color || [1,1,1,1];\\r\\n\\r\\n\\t\\tpositionToArea( this.properties.position, this.properties.corner, this._pos );\\r\\n\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.enable( gl.BLEND );\\r\\n\\t\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );\\r\\n\\t\\tctx.fillText( text, this._pos[0], this._pos[1] );\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"gui/text\\\", LGraphGUIText );\\r\\n\\r\\n\\tfunction LGraphGUIImage()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"\\\",\\\"image,canvas,texture\\\");\\r\\n\\t\\tthis.properties = { enabled: true, opacity: 1, keep_aspect: true, flipX: false, flipY: false, force_update: false, position: [20,20], size: [300,200], corner: LiteGraph.CORNER_TOP_LEFT };\\r\\n\\t\\tthis._pos = vec2.create();\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIImage.title = \\\"GUIImage\\\";\\r\\n\\tLGraphGUIImage.desc = \\\"renders an image on webgl canvas\\\";\\r\\n\\r\\n\\tLGraphGUIImage[\\\"@corner\\\"] = corner_options;\\r\\n\\tLGraphGUIImage[\\\"@opacity\\\"] = { widget:\\\"slider\\\", min:0,max:1 };\\r\\n\\r\\n\\tLGraphGUIImage.prototype.onGetInputs = function(){\\r\\n\\t\\treturn [[\\\"enabled\\\",\\\"boolean\\\"]];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIImage.prototype.onRenderGUI = function()\\r\\n\\t{ \\r\\n\\t\\tvar ctx = window.gl;\\r\\n\\t\\tif(!ctx)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar img = this.getInputData(0);\\r\\n\\t\\tvar enabled = this.getInputOrProperty(\\\"enabled\\\");\\r\\n\\t\\tif(enabled === false || !img)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(this.properties.force_update)\\r\\n\\t\\t\\timg.mustUpdate = true;\\r\\n\\r\\n\\t\\tpositionToArea( this.properties.position, this.properties.corner, this._pos );\\r\\n\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.enable( gl.BLEND );\\r\\n\\t\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );\\r\\n\\t\\tvar tmp = ctx.globalAlpha;\\r\\n\\t\\tctx.globalAlpha *= this.properties.opacity;\\r\\n\\t\\tvar x = this._pos[0];\\r\\n\\t\\tvar y = this._pos[1];\\r\\n\\t\\tvar w = this.properties.size[0];\\r\\n\\t\\tvar h = this.properties.size[1];\\r\\n\\t\\tif(this.properties.keep_aspect)\\r\\n\\t\\t\\th = (this.properties.size[0] / img.width) * img.height;\\r\\n\\t\\tif(this.properties.flipX)\\r\\n\\t\\t{\\r\\n\\t\\t\\tx += w;\\r\\n\\t\\t\\tw *= -1;\\r\\n\\t\\t}\\r\\n\\t\\tif(this.properties.flipY)\\r\\n\\t\\t{\\r\\n\\t\\t\\ty += h;\\r\\n\\t\\t\\th *= -1;\\r\\n\\t\\t}\\r\\n\\t\\tctx.drawImage( img, x, y, w, h );\\r\\n\\t\\tctx.globalAlpha = tmp;\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"gui/image\\\", LGraphGUIImage );\\r\\n\\r\\n\\r\\n\\t//special kind of node\\r\\n\\tfunction LGraphGUISlider()\\r\\n\\t{\\r\\n\\t\\tthis.addOutput(\\\"v\\\");\\r\\n\\t\\tthis.properties = { enabled: true, text: \\\"\\\", min: 0, max: 1, value: 0, position: [20,20], size: [200,40], corner: LiteGraph.CORNER_TOP_LEFT };\\r\\n\\t\\tthis._area = vec4.create();\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUISlider.title = \\\"GUISlider\\\";\\r\\n\\tLGraphGUISlider.desc = \\\"Renders a slider on the main canvas\\\";\\r\\n\\tLGraphGUISlider[\\\"@corner\\\"] = corner_options;\\r\\n\\r\\n\\tLGraphGUISlider.prototype.onRenderGUI = function()\\r\\n\\t{\\r\\n\\t\\tif(!this.properties.enabled)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tpositionToArea( this.properties.position, this.properties.corner, this._area );\\r\\n\\t\\tthis._area[2] = this.properties.size[0];\\r\\n\\t\\tthis._area[3] = this.properties.size[1];\\r\\n\\t\\tthis.properties.value = LS.GUI.HorizontalSlider( this._area, Number(this.properties.value), Number(this.properties.min), Number(this.properties.max), true );\\r\\n\\t\\tif(this.properties.text)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.textAlign = \\\"right\\\";\\r\\n\\t\\t\\tgl.fillStyle = \\\"#AAA\\\";\\r\\n\\t\\t\\tgl.fillText( this.properties.text, this._area[0] - 20, this._area[1] + this._area[3] * 0.75);\\r\\n\\t\\t\\tgl.textAlign = \\\"left\\\";\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUISlider.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tif(this.inputs && this.inputs.length)\\r\\n\\t\\t\\tthis.properties.enabled = this.getInputOrProperty(\\\"enabled\\\");\\r\\n\\t\\tthis.setOutputData(0, this.properties.value );\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUISlider.prototype.onGetInputs = function(){\\r\\n\\t\\treturn [[\\\"enabled\\\",\\\"boolean\\\"]];\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"gui/slider\\\", LGraphGUISlider );\\r\\n\\r\\n\\r\\n\\tfunction LGraphGUIToggle()\\r\\n\\t{\\r\\n\\t\\tthis.addOutput(\\\"v\\\");\\r\\n\\t\\tthis.properties = { enabled: true, value: true, text:\\\"toggle\\\", position: [20,20], size: [140,40], corner: LiteGraph.CORNER_TOP_LEFT };\\r\\n\\t\\tthis._area = vec4.create();\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIToggle.title = \\\"GUIToggle\\\";\\r\\n\\tLGraphGUIToggle.desc = \\\"Renders a toggle widget on the main canvas\\\";\\r\\n\\tLGraphGUIToggle[\\\"@corner\\\"] = corner_options;\\r\\n\\r\\n\\tLGraphGUIToggle.prototype.onRenderGUI = function()\\r\\n\\t{\\r\\n\\t\\tif(!this.properties.enabled)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tpositionToArea( this.properties.position, this.properties.corner, this._area );\\r\\n\\t\\tthis._area[2] = this.properties.size[0];\\r\\n\\t\\tthis._area[3] = this.properties.size[1];\\r\\n\\t\\tthis.properties.value = LS.GUI.Toggle( this._area, this.properties.value, this.properties.text );\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIToggle.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tthis.setOutputData(0, this.properties.value );\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"gui/toggle\\\", LGraphGUIToggle );\\r\\n\\r\\n\\tfunction LGraphGUIButton()\\r\\n\\t{\\r\\n\\t\\tthis.addOutput(\\\"on\\\",LiteGraph.EVENT);\\r\\n\\t\\tthis.addOutput(\\\"was_pressed\\\");\\r\\n\\t\\tthis.properties = { enabled: true, text:\\\"clickme\\\", position: [20,20], size: [140,40], corner: LiteGraph.CORNER_TOP_LEFT };\\r\\n\\t\\tthis._area = vec4.create();\\r\\n\\t\\tthis._was_pressed = false;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIButton.title = \\\"GUIButton\\\";\\r\\n\\tLGraphGUIButton.desc = \\\"Renders a toggle widget on the main canvas\\\";\\r\\n\\tLGraphGUIButton[\\\"@corner\\\"] = corner_options;\\r\\n\\r\\n\\tLGraphGUIButton.prototype.onRenderGUI = function()\\r\\n\\t{\\r\\n\\t\\tif(!this.properties.enabled)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tpositionToArea( this.properties.position, this.properties.corner, this._area );\\r\\n\\t\\tthis._area[2] = this.properties.size[0];\\r\\n\\t\\tthis._area[3] = this.properties.size[1];\\r\\n\\t\\tthis._was_pressed = LS.GUI.Button( this._area, this.properties.text );\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIButton.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tvar enabled = this.getInputDataByName(\\\"enabled\\\");\\r\\n\\t\\tif(enabled === false || enabled === true)\\r\\n\\t\\t\\tthis.properties.enabled = enabled;\\r\\n\\t\\tif(this._was_pressed)\\r\\n\\t\\t\\tthis.trigger(\\\"on\\\");\\r\\n\\t\\tthis.setOutputData(1, this._was_pressed );\\r\\n\\t\\tthis._was_pressed = false;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIButton.prototype.onGetInputs = function(){\\r\\n\\t\\treturn [[\\\"enabled\\\",\\\"boolean\\\"]];\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"gui/button\\\", LGraphGUIButton );\\r\\n\\r\\n\\r\\n\\tfunction LGraphGUIMultipleChoice()\\r\\n\\t{\\r\\n\\t\\tthis.addOutput(\\\"v\\\");\\r\\n\\t\\tthis.addOutput(\\\"i\\\");\\r\\n\\t\\tthis.properties = { enabled: true, selected: 0, values:\\\"option1;option2;option3\\\", one_line: false, position: [20,20], size: [180,100], corner: LiteGraph.CORNER_TOP_LEFT };\\r\\n\\t\\tthis._area = vec4.create();\\r\\n\\t\\tthis._values = this.properties.values.split(\\\";\\\");\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tthis.widget = this.addWidget(\\\"text\\\",\\\"Options\\\",this.properties.values,function(v){\\r\\n\\t\\t\\tthat.properties.values = v;\\r\\n\\t\\t\\tthat.onPropertyChanged(\\\"values\\\",v);\\r\\n\\t\\t});\\r\\n\\t\\tthis.size = [240,70];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIMultipleChoice.title = \\\"GUIMultipleChoice\\\";\\r\\n\\tLGraphGUIMultipleChoice.desc = \\\"Renders a multiple choice widget on the main canvas\\\";\\r\\n\\tLGraphGUIMultipleChoice[\\\"@corner\\\"] = corner_options;\\r\\n\\r\\n\\tLGraphGUIMultipleChoice.prototype.onPropertyChanged = function(name,value)\\r\\n\\t{\\r\\n\\t\\tif(name == \\\"values\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._values = value.split(\\\";\\\");\\r\\n\\t\\t\\tthis.widget.value = value;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIMultipleChoice.prototype.onAction = function(name, param)\\r\\n\\t{\\r\\n\\t\\tif(name == \\\"prev\\\")\\r\\n\\t\\t\\tthis.properties.selected -= 1;\\r\\n\\t\\telse if(name == \\\"next\\\")\\r\\n\\t\\t\\tthis.properties.selected += 1;\\r\\n\\t\\tthis.properties.selected = this.properties.selected % this._values.length;\\r\\n\\t\\tif(this.properties.selected < 0)\\r\\n\\t\\t\\tthis.properties.selected += this._values.length;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIMultipleChoice.prototype.onRenderGUI = function()\\r\\n\\t{\\r\\n\\t\\tif(!this._values.length || !this.properties.enabled )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar selected = this.properties.selected = Math.floor( this.properties.selected );\\r\\n\\t\\tpositionToArea( this.properties.position, this.properties.corner, this._area );\\r\\n\\t\\tvar ctx = gl;\\r\\n\\r\\n\\t\\tif(this.properties.one_line)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pos = this.properties.position;\\r\\n\\t\\t\\tvar size = this.properties.size;\\r\\n\\t\\t\\tvar w = size[1]; //use height as width\\r\\n\\t\\t\\tthis._area[2] = w * 2;\\r\\n\\t\\t\\tthis._area[3] = size[1];\\r\\n\\t\\t\\tif( LS.GUI.ClickArea( this._area ) )\\r\\n\\t\\t\\t\\tselected -= 1;\\r\\n\\t\\t\\tthis._area[0] += size[0] - w*2;\\r\\n\\t\\t\\tif( LS.GUI.ClickArea( this._area ) )\\r\\n\\t\\t\\t\\tselected += 1;\\r\\n\\t\\t\\tselected = selected % this._values.length;\\r\\n\\t\\t\\tif(selected < 0)\\r\\n\\t\\t\\t\\tselected += this._values.length;\\r\\n\\t\\t\\tctx.fillStyle = \\\"black\\\";\\r\\n\\t\\t\\tctx.strokeStyle = \\\"#AAA\\\";\\r\\n\\t\\t\\tctx.lineWidth = 2;\\r\\n\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\tctx.roundRect( pos[0], pos[1], size[0], size[1], w * 0.5 );\\r\\n\\t\\t\\tctx.fill();\\r\\n\\t\\t\\tctx.stroke();\\r\\n\\t\\t\\tctx.fillStyle = \\\"white\\\";\\r\\n\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\tvar m = w * 0.25;\\r\\n\\t\\t\\tctx.moveTo( pos[0] + m, pos[1] + w * 0.5 );\\r\\n\\t\\t\\tctx.lineTo( pos[0] + w*0.5 + m*2, pos[1] + m );\\r\\n\\t\\t\\tctx.lineTo( pos[0] + w*0.5 + m*2, pos[1] + w - m);\\r\\n\\t\\t\\tctx.fill();\\r\\n\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\tctx.moveTo( pos[0] + size[0] - m, pos[1] + w * 0.5 );\\r\\n\\t\\t\\tctx.lineTo( pos[0] + size[0] - w*0.5 - m*2, pos[1] + m);\\r\\n\\t\\t\\tctx.lineTo( pos[0] + size[0] - w*0.5 - m*2, pos[1] + w - m);\\r\\n\\t\\t\\tctx.fill();\\r\\n\\t\\t\\tctx.fillStyle = \\\"#AAA\\\";\\r\\n\\t\\t\\tctx.textAlign = \\\"center\\\";\\r\\n\\t\\t\\tctx.font = (w*0.75).toFixed(0) + \\\"px \\\" + LS.GUI.GUIStyle.font;\\r\\n\\t\\t\\tctx.fillText( String(this._values[selected]), pos[0] + size[0] * 0.5, pos[1] + size[1] * 0.75 );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._area[2] = this.properties.size[0];\\r\\n\\t\\t\\tthis._area[3] = this.properties.size[1] / this._values.length;\\r\\n\\t\\t\\tvar y = this._area[1];\\r\\n\\t\\t\\tfor(var i = 0; i < this._values.length; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._area[1] = y + i * this._area[3];\\r\\n\\t\\t\\t\\tif( LS.GUI.Toggle( this._area, i == selected, this._values[i], null, true ) )\\r\\n\\t\\t\\t\\t\\tselected = i;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.properties.selected = selected;\\r\\n\\r\\n\\t\\tvar mouse = LS.Input.current_click;\\r\\n\\t\\tif(mouse)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar clicked = LS.Input.isEventInRect( mouse, this._area, LS.GUI._offset );\\r\\n\\t\\t\\tif(clicked)\\r\\n\\t\\t\\t\\tLS.Input.current_click = false; //consume event\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIMultipleChoice.prototype.onGetInputs = function(){\\r\\n\\t\\treturn [[\\\"enabled\\\",\\\"boolean\\\"],[\\\"options\\\",\\\"array\\\"],[\\\"next\\\",LiteGraph.ACTION],[\\\"prev\\\",LiteGraph.ACTION]];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphGUIMultipleChoice.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tif(this.inputs)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i = 0; i < this.inputs.length; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar input_info = this.inputs[i];\\r\\n\\t\\t\\t\\tvar v = this.getInputData(i);\\r\\n\\t\\t\\t\\tif( input_info.name == \\\"enabled\\\" )\\r\\n\\t\\t\\t\\t\\tthis.properties.enabled = Boolean(v);\\r\\n\\t\\t\\t\\telse if( input_info.name == \\\"options\\\" && v)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tthis._values = v;\\r\\n\\t\\t\\t\\t\\tthis.properties.values = v.join(\\\";\\\");\\r\\n\\t\\t\\t\\t\\tthis.widget.value = this.properties.values;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tthis.setOutputData( 0, this._values[ this.properties.selected ] );\\r\\n\\t\\tthis.setOutputData( 1, this.properties.selected );\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"gui/multiple_choice\\\", LGraphGUIMultipleChoice );\\r\\n\\r\\n\\r\\n\\t//based in the NNI distance \\r\\n\\tfunction LGraphMap2D()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"x\\\",\\\"number\\\");\\r\\n\\t\\tthis.addInput(\\\"y\\\",\\\"number\\\");\\r\\n\\t\\tthis.addOutput(\\\"[]\\\",\\\"array\\\");\\r\\n\\t\\tthis.addOutput(\\\"obj\\\",\\\"object\\\");\\r\\n\\t\\tthis.addProperty(\\\"circular\\\",false);\\r\\n\\t\\tthis.points = [];\\r\\n\\t\\tthis.weights = [];\\r\\n\\t\\tthis.weights_obj = {};\\r\\n\\t\\tthis.current_pos = new Float32Array([0.5,0.5]);\\r\\n\\t\\tthis.size = [200,200];\\r\\n\\t\\tthis.dragging = false;\\r\\n\\t\\tthis.show_names = true;\\r\\n\\t\\tthis.circle_center = [0,0];\\r\\n\\t\\tthis.circle_radius = 1;\\r\\n\\t\\tthis.margin = 20;\\r\\n\\t\\tthis._values_changed = true;\\r\\n\\t\\tthis._visualize_weights = false;\\r\\n\\t\\tthis._version = 0;\\r\\n\\t\\tthis._selected_point = null;\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.LGraphMap2D = LGraphMap2D;\\r\\n\\tLGraphMap2D.title = \\\"Map2D\\\";\\r\\n\\tLGraphMap2D.colors = [[255,0,0],[0,255,0],[0,0,255],[0,128,128,0],[128,0,128],[128,128,0],[255,128,0],[255,0,128],[0,128,255],[128,0,255]];\\r\\n\\tLGraphMap2D.grid_size = 64;\\r\\n\\r\\n\\tLGraphMap2D.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tvar pos = this.current_pos;\\r\\n\\t\\tpos[0] = this.getInputData(0) || pos[0];\\r\\n\\t\\tpos[1] = this.getInputData(1) || pos[1];\\r\\n\\t\\tthis.computeWeights(pos);\\r\\n\\t\\tthis.setOutputData(0, this.weights );\\r\\n\\t\\tthis.setOutputData(1, this.weights_obj );\\r\\n\\t}\\r\\n\\t\\r\\n\\t//now to compute the final weight we iterate for every cell to see if our point is nearer to the cell than the nearest point of the cell,\\r\\n\\t//if that is the case we increase the weight of the nearest point. At the end we normalize the weights of the points by the number of near points\\r\\n\\t//and that give us the weight for every point\\r\\n\\tLGraphMap2D.prototype.computeWeights = function(pos)\\r\\n\\t{\\r\\n\\t\\tif(!this.points.length)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar values = this._precomputed_weights;\\r\\n\\t\\tif(!values || this._values_changed )\\r\\n\\t\\t\\tvalues = this.precomputeWeights();\\r\\n\\t\\tvar pos2 = vec2.create();\\r\\n\\t\\tvar circular = this.properties.circular;\\r\\n\\t\\tvar weights = this.weights;\\r\\n\\t\\tweights.length = this.points.length;\\r\\n\\t\\tvar gridsize = LGraphMap2D.grid_size;\\r\\n\\t\\tfor(var i = 0; i < weights.length; ++i)\\r\\n\\t\\t\\tweights[i] = 0;\\r\\n\\t\\tvar total_inside = 0;\\r\\n\\t\\tfor(var y = 0; y < gridsize; ++y)\\r\\n\\t\\t\\tfor(var x = 0; x < gridsize; ++x)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tpos2[0] = x / gridsize;\\r\\n\\t\\t\\t\\tpos2[1] = y / gridsize;\\r\\n\\t\\t\\t\\tif(circular)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tpos2[0] = pos2[0] * 2 - 1;\\r\\n\\t\\t\\t\\t\\tpos2[1] = pos2[1] * 2 - 1;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tvar data_pos = x*2 + y * gridsize*2;\\r\\n\\t\\t\\t\\tvar point_index = values[ data_pos ];\\r\\n\\t\\t\\t\\tvar is_inside = vec2.distance( pos2, pos ) < (values[ data_pos + 1] + 0.001); //epsilon\\r\\n\\t\\t\\t\\tif(is_inside)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tweights[ point_index ] += 1;\\r\\n\\t\\t\\t\\t\\ttotal_inside++;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\tfor(var i = 0; i < weights.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tweights[i] /= total_inside;\\r\\n\\t\\t\\tthis.weights_obj[ this.points[i].name ] = weights[i];\\r\\n\\t\\t}\\r\\n\\t\\treturn weights;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.onMouseDown = function(e,pos)\\r\\n\\t{\\r\\n\\t\\tif(this.flags.collapsed || pos[1] < 0 || pos[0] < this.margin || pos[0] > (this.size[0] - this.margin) || pos[1] < this.margin )\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\tif( pos[1] > (this.size[1] - this.margin))\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._visualize_weights = !this._visualize_weights;\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.dragging = true;\\r\\n\\t\\treturn true;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.onMouseUp = function(e,pos)\\r\\n\\t{\\r\\n\\t\\tthis.dragging = false;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.onMouseMove = function(e,pos)\\r\\n\\t{\\r\\n\\t\\tif( !this.dragging || this.flags.collapsed )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar margin = this.margin;\\r\\n\\t\\tvar center = [0,0];\\r\\n\\t\\tvar radius = this.size[1] * 0.5 - margin;\\r\\n\\t\\tvar cpos = this.current_pos;\\r\\n\\t\\tcpos[0] = Math.clamp( (pos[0] - margin) / (this.size[0] - margin*2), 0,1 );\\r\\n\\t\\tcpos[1] = Math.clamp( (pos[1] - margin) / (this.size[1] - margin*2), 0,1 );\\r\\n\\t\\tif( this.properties.circular )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcpos[0] = cpos[0] * 2 - 1;\\r\\n\\t\\t\\tcpos[1] = cpos[1] * 2 - 1;\\r\\n\\t\\t\\tvar dist = vec2.distance( cpos, center );\\r\\n\\t\\t\\tif(dist > 1)\\r\\n\\t\\t\\t\\tvec2.normalize(cpos,cpos);\\r\\n\\t\\t}\\r\\n\\t\\treturn true;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.onDrawBackground = function( ctx )\\r\\n\\t{\\r\\n\\t\\tif(this.flags.collapsed)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar pos = this.current_pos;\\r\\n\\t\\tvar margin = this.margin;\\r\\n\\t\\tvar circular = this.properties.circular;\\r\\n\\t\\tvar show_names = this.show_names;\\r\\n\\r\\n\\t\\tctx.fillStyle = \\\"black\\\";\\r\\n\\t\\tctx.strokeStyle = \\\"#BBB\\\";\\r\\n\\t\\tif(circular)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.circle_center[0] = this.size[0] * 0.5;\\r\\n\\t\\t\\tthis.circle_center[1] = this.size[1] * 0.5;\\r\\n\\t\\t\\tthis.circle_radius = this.size[1] * 0.5 - margin;\\r\\n\\t\\t\\tctx.lineWidth = 2;\\r\\n\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\tctx.arc( this.circle_center[0], this.circle_center[1], this.circle_radius, 0, Math.PI * 2 );\\r\\n\\t\\t\\tctx.fill();\\r\\n\\t\\t\\tctx.stroke();\\r\\n\\t\\t\\tctx.lineWidth = 1;\\r\\n\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\tctx.moveTo( this.circle_center[0] + 0.5, this.circle_center[1] - this.circle_radius );\\r\\n\\t\\t\\tctx.lineTo( this.circle_center[0] + 0.5, this.circle_center[1] + this.circle_radius );\\r\\n\\t\\t\\tctx.moveTo( this.circle_center[0] - this.circle_radius, this.circle_center[1]);\\r\\n\\t\\t\\tctx.lineTo( this.circle_center[0] + this.circle_radius, this.circle_center[1]);\\r\\n\\t\\t\\tctx.stroke();\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tctx.fillRect(margin,margin,this.size[0]-margin*2, this.size[1]-margin*2);\\r\\n\\t\\t\\tctx.strokeRect(margin,margin,this.size[0]-margin*2, this.size[1]-margin*2);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar image = this.precomputeWeightsToImage( pos );\\r\\n\\t\\tif(image)\\r\\n\\t\\t{\\r\\n\\t\\t\\tctx.globalAlpha = 0.5;\\r\\n\\t\\t\\tctx.imageSmoothingEnabled = false;\\r\\n\\t\\t\\tif(circular)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tctx.save();\\r\\n\\t\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\t\\tctx.arc( this.circle_center[0], this.circle_center[1], this.circle_radius, 0, Math.PI * 2 );\\r\\n\\t\\t\\t\\tctx.clip();\\r\\n\\t\\t\\t\\tctx.drawImage( image, this.circle_center[0] - this.circle_radius, this.circle_center[1] - this.circle_radius, this.circle_radius*2, this.circle_radius*2 );\\r\\n\\t\\t\\t\\tctx.restore();\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tctx.drawImage( image, margin, margin,this.size[0]-margin*2, this.size[1]-margin*2 );\\r\\n\\t\\t\\tctx.imageSmoothingEnabled = true;\\r\\n\\t\\t\\tctx.globalAlpha = 1;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var i = 0; i < this.points.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar point = this.points[i];\\r\\n\\t\\t\\tvar x = point.pos[0];\\r\\n\\t\\t\\tvar y = point.pos[1];\\r\\n\\t\\t\\tif(circular)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tx = x*0.5 + 0.5;\\r\\n\\t\\t\\t\\ty = y*0.5 + 0.5;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tx = x * (this.size[0]-margin*2) + margin;\\r\\n\\t\\t\\ty = y * (this.size[1]-margin*2) + margin;\\r\\n\\t\\t\\tx = Math.clamp( x, margin, this.size[0]-margin);\\r\\n\\t\\t\\ty = Math.clamp( y, margin, this.size[1]-margin);\\r\\n\\t\\t\\tctx.fillStyle = point == this._selected_point ? \\\"#9DF\\\" : \\\"#789\\\";\\r\\n\\t\\t\\tctx.beginPath();\\r\\n\\t\\t\\tctx.arc(x,y,3,0,Math.PI*2);\\r\\n\\t\\t\\tctx.fill();\\r\\n\\t\\t\\tif( show_names )\\r\\n\\t\\t\\t\\tctx.fillText( point.name, x + 5, y + 5);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tctx.fillStyle = \\\"white\\\";\\r\\n\\t\\tctx.beginPath();\\r\\n\\t\\tvar x = pos[0];\\r\\n\\t\\tvar y = pos[1];\\r\\n\\t\\tif(circular)\\r\\n\\t\\t{\\r\\n\\t\\t\\tx = x*0.5 + 0.5;\\r\\n\\t\\t\\ty = y*0.5 + 0.5;\\r\\n\\t\\t}\\r\\n\\t\\tx = x * (this.size[0]-margin*2) + margin;\\r\\n\\t\\ty = y * (this.size[1]-margin*2) + margin;\\r\\n\\t\\tx = Math.clamp( x, margin, this.size[0]-margin);\\r\\n\\t\\ty = Math.clamp( y, margin, this.size[1]-margin);\\r\\n\\t\\tctx.arc(x,y,5,0,Math.PI*2);\\r\\n\\t\\tctx.fill();\\r\\n\\r\\n\\t\\t//weights\\r\\n\\t\\tctx.save();\\r\\n\\t\\tctx.fillStyle = \\\"black\\\";\\r\\n\\t\\tctx.fillRect( margin, this.size[1] - margin + 2, this.size[0] - margin * 2, margin - 4);\\r\\n\\t\\tctx.strokeStyle = \\\"white\\\";\\r\\n\\t\\tctx.strokeRect( margin, this.size[1] - margin + 2, this.size[0] - margin * 2, margin - 4);\\r\\n\\t\\tctx.textAlign = \\\"center\\\";\\r\\n\\t\\tctx.fillStyle = \\\"white\\\";\\r\\n\\t\\tctx.fillText( \\\"Visualize Weights\\\", this.size[0] * 0.5, this.size[1] - margin * 0.3 );\\r\\n\\t\\tctx.textAlign = \\\"left\\\";\\r\\n\\r\\n\\t\\tif(this.weights.length && this._visualize_weights)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar x = this.size[0] + 5;\\r\\n\\t\\t\\tvar y = 16; //this.size[1] - this.weights.length * 5 - 10;\\r\\n\\t\\t\\tfor(var i = 0; i < this.weights.length; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar c = LGraphMap2D.colors[i % LGraphMap2D.colors.length];\\r\\n\\t\\t\\t\\tctx.fillStyle = \\\"black\\\";\\r\\n\\t\\t\\t\\tctx.fillRect(x, y + i*5, 100,4 );\\r\\n\\t\\t\\t\\tctx.fillStyle = \\\"rgb(\\\" + ((c[0]*255)|0) + \\\",\\\" + ((c[1]*255)|0) + \\\",\\\" + ((c[2]*255)|0) + \\\")\\\";\\r\\n\\t\\t\\t\\tctx.fillRect(x, y + i*5, this.weights[i]*100,4 );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tctx.restore();\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.addPoint = function( name, pos )\\r\\n\\t{\\r\\n\\t\\tif( this.findPoint(name) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"there is already a point with that name\\\" );\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tif(!pos)\\r\\n\\t\\t\\tpos = [this.current_pos[0], this.current_pos[1]];\\r\\n\\t\\tpos[0] = Math.clamp( pos[0], -1,1 );\\r\\n\\t\\tpos[1] = Math.clamp( pos[1], -1,1 );\\r\\n\\t\\tthis.points.push({ name: name, pos: pos });\\r\\n\\t\\tthis._values_changed = true;\\r\\n\\t\\tthis.setDirtyCanvas(true);\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.removePoint = function(name)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < this.points.length; ++i)\\r\\n\\t\\t\\tif( this.points[i].name == name )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.points.splice(i,1);\\r\\n\\t\\t\\t\\tthis._values_changed = true;\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.findPoint = function( name )\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < this.points.length; ++i)\\r\\n\\t\\t\\tif( this.points[i].name == name )\\r\\n\\t\\t\\t\\treturn this.points[i];\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\t//here we precompute for every cell, which is the closest point of the points set and how far it is from the center of the cell\\r\\n\\t//we store point index and distance in this._precomputed_weights\\r\\n\\t//this is done only when the points set change\\r\\n\\tLGraphMap2D.prototype.precomputeWeights = function()\\r\\n\\t{\\r\\n\\t\\tvar points = this.points;\\r\\n\\t\\tvar num_points = points.length;\\r\\n\\t\\tvar pos = vec2.create();\\r\\n\\t\\tvar circular = this.properties.circular;\\r\\n\\t\\tthis._values_changed = false;\\r\\n\\t\\tthis._version++;\\r\\n\\t\\tvar gridsize = LGraphMap2D.grid_size;\\r\\n\\t\\tvar total_nums = 2 * gridsize * gridsize;\\r\\n\\t\\tif(!this._precomputed_weights || this._precomputed_weights.length != total_nums )\\r\\n\\t\\t\\tthis._precomputed_weights = new Float32Array( total_nums );\\r\\n\\t\\tvar values = this._precomputed_weights;\\r\\n\\t\\tthis._precomputed_weights_gridsize = gridsize;\\r\\n\\r\\n\\t\\tfor(var y = 0; y < gridsize; ++y)\\r\\n\\t\\t\\tfor(var x = 0; x < gridsize; ++x)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar nearest = -1;\\r\\n\\t\\t\\t\\tvar min_dist = 100000;\\r\\n\\t\\t\\t\\tfor(var i = 0; i < num_points; ++i)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tpos[0] = x / gridsize;\\r\\n\\t\\t\\t\\t\\tpos[1] = y / gridsize;\\r\\n\\t\\t\\t\\t\\tif(circular)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tpos[0] = pos[0] * 2 - 1;\\r\\n\\t\\t\\t\\t\\t\\tpos[1] = pos[1] * 2 - 1;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tvar dist = vec2.distance( pos, points[i].pos );\\r\\n\\t\\t\\t\\t\\tif( dist > min_dist )\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\tnearest = i;\\r\\n\\t\\t\\t\\t\\tmin_dist = dist;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tvalues[ x*2 + y*2*gridsize ] = nearest;\\r\\n\\t\\t\\t\\tvalues[ x*2 + y*2*gridsize + 1] = min_dist;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\treturn values;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.precomputeWeightsToImage = function(pos)\\r\\n\\t{\\r\\n\\t\\tif(!this.points.length)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tvar values = this._precomputed_weights;\\r\\n\\t\\tif(!values || this._values_changed || this._precomputed_weights_gridsize != LGraphMap2D.grid_size)\\r\\n\\t\\t\\tvalues = this.precomputeWeights();\\r\\n\\t\\tvar canvas = this._canvas;\\r\\n\\t\\tvar gridsize = LGraphMap2D.grid_size;\\r\\n\\t\\tif(!canvas)\\r\\n\\t\\t\\tcanvas = this._canvas = document.createElement(\\\"canvas\\\");\\r\\n\\t\\tcanvas.width = canvas.height = gridsize;\\r\\n\\t\\tvar ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tvar white = [255,255,255];\\r\\n\\t\\tvar pos2 = vec2.create();\\r\\n\\t\\tvar circular = this.properties.circular;\\r\\n\\t\\tvar weights = this.weights;\\r\\n\\t\\tweights.length = this.points.length;\\r\\n\\t\\tfor(var i = 0; i < weights.length; ++i)\\r\\n\\t\\t\\tweights[i] = 0;\\r\\n\\t\\tvar total_inside = 0;\\r\\n\\t\\tvar pixels = ctx.getImageData(0,0,gridsize,gridsize);\\r\\n\\t\\tfor(var y = 0; y < gridsize; ++y)\\r\\n\\t\\t\\tfor(var x = 0; x < gridsize; ++x)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tpos2[0] = x / gridsize;\\r\\n\\t\\t\\t\\tpos2[1] = y / gridsize;\\r\\n\\t\\t\\t\\tif(circular)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tpos2[0] = pos2[0] * 2 - 1;\\r\\n\\t\\t\\t\\t\\tpos2[1] = pos2[1] * 2 - 1;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tvar pixel_pos = x*4 + y*gridsize*4;\\r\\n\\t\\t\\t\\tvar data_pos = x*2 + y * gridsize*2;\\r\\n\\t\\t\\t\\tvar point_index = values[ data_pos ];\\r\\n\\t\\t\\t\\tvar c = LGraphMap2D.colors[ point_index % LGraphMap2D.colors.length ];\\r\\n\\t\\t\\t\\tvar is_inside = vec2.distance( pos2, pos ) < (values[ data_pos + 1] + 0.001);\\r\\n\\t\\t\\t\\tif(is_inside)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tweights[ point_index ] += 1;\\r\\n\\t\\t\\t\\t\\ttotal_inside++;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tpixels.data[pixel_pos] = c[0] + (is_inside ? 128 : 0);\\r\\n\\t\\t\\t\\tpixels.data[pixel_pos+1] = c[1] + (is_inside ? 128 : 0);\\r\\n\\t\\t\\t\\tpixels.data[pixel_pos+2] = c[2] + (is_inside ? 128 : 0);\\r\\n\\t\\t\\t\\tpixels.data[pixel_pos+3] = 255;\\r\\n\\t\\t\\t}\\r\\n\\t\\tfor(var i = 0; i < weights.length; ++i)\\r\\n\\t\\t\\tweights[i] /= total_inside;\\r\\n\\t\\tctx.putImageData(pixels,0,0);\\r\\n\\t\\treturn canvas;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.clear = function()\\r\\n\\t{\\r\\n\\t\\tthis.points.length = 0;\\r\\n\\t\\tthis._precomputed_weights = null;\\r\\n\\t\\tthis._canvas = null;\\r\\n\\t\\tthis._selected_point = null;\\r\\n\\t\\tthis.setDirtyCanvas(true);\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.getExtraMenuOptions = function()\\r\\n\\t{\\r\\n\\t\\treturn[{content:\\\"Clear Points\\\", callback: this.clear.bind(this) }];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.onInspect = function( inspector )\\r\\n\\t{\\r\\n\\t\\tvar node = this;\\r\\n\\t\\tif(!this._selected_point && this.points.length)\\r\\n\\t\\t\\tthis._selected_point = this.points[0];\\r\\n\\t\\tinspector.addTitle(\\\"Points\\\");\\r\\n\\r\\n\\t\\tinspector.widgets_per_row = 4;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < this.points.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar point = this.points[i];\\r\\n\\t\\t\\tinspector.addString( null, point.name, { point: point, width: \\\"40%\\\", callback: function(v){\\r\\n\\t\\t\\t\\tthis.options.point.name = v;\\r\\n\\t\\t\\t\\tnode.setDirtyCanvas(true);\\r\\n\\t\\t\\t}});\\r\\n\\t\\t\\tvar posX_widget = inspector.addNumber(null, point.pos[0], { point: point, width: \\\"20%\\\", min:-1, max:1, step: 0.01, callback: function(v){\\r\\n\\t\\t\\t\\tthis.options.point.pos[0] = Math.clamp( v, -1, 1 );\\r\\n\\t\\t\\t\\tnode._values_changed = true;\\r\\n\\t\\t\\t}});\\r\\n\\t\\t\\tvar posY_widget = inspector.addNumber(null,point.pos[1], { point: point, width: \\\"20%\\\", min:-1, max:1, step: 0.01, callback: function(v){\\r\\n\\t\\t\\t\\tthis.options.point.pos[1] = Math.clamp( v, -1, 1 );\\r\\n\\t\\t\\t\\tnode._values_changed = true;\\r\\n\\t\\t\\t}});\\r\\n\\t\\t\\tinspector.addButton(null,\\\"o\\\", { point: point, width: \\\"10%\\\", callback: function(){\\r\\n\\t\\t\\t\\tthis.options.point.pos[0] = node.current_pos[0];\\r\\n\\t\\t\\t\\tthis.options.point.pos[1] = node.current_pos[1];\\r\\n\\t\\t\\t\\tnode._values_changed = true;\\r\\n\\t\\t\\t}});\\r\\n\\t\\t\\tinspector.addButton(null,\\\"X\\\", { point: point, width: \\\"10%\\\", callback: function(){\\r\\n\\t\\t\\t\\tLiteGUI.confirm(\\\"Are you sure? Removing one point could mess up the whole weights order\\\", (function(v){\\r\\n\\t\\t\\t\\t\\tif(!v)\\r\\n\\t\\t\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t\\t\\tnode.removePoint( this.point.name );\\t\\r\\n\\t\\t\\t\\t\\tinspector.refresh();\\r\\n\\t\\t\\t\\t}).bind(this.options));\\r\\n\\t\\t\\t}});\\r\\n\\t\\t}\\r\\n\\t\\tinspector.widgets_per_row = 1;\\r\\n\\r\\n\\t\\tvar new_point_name = \\\"\\\";\\r\\n\\t\\tinspector.widgets_per_row = 2;\\r\\n\\t\\tinspector.addString(\\\"New Point\\\",\\\"\\\",{ width:\\\"75%\\\", callback: function(v){\\r\\n\\t\\t\\tnew_point_name = v;\\r\\n\\t\\t}});\\r\\n\\t\\tinspector.addButton(null,\\\"Create\\\",{ width:\\\"25%\\\",callback: function(v){\\r\\n\\t\\t\\tif(new_point_name)\\r\\n\\t\\t\\t\\tnode.addPoint( new_point_name );\\r\\n\\t\\t\\tinspector.refresh();\\r\\n\\t\\t}});\\r\\n\\t\\tinspector.widgets_per_row = 1;\\r\\n\\r\\n\\t\\tinspector.addSeparator();\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.onSerialize = function(o)\\r\\n\\t{\\r\\n\\t\\to.current_pos = this.current_pos;\\r\\n\\t\\tfor(var i = 0; i < this.points.length; ++i)\\r\\n\\t\\t\\tdelete this.points[i]._dist;\\r\\n\\t\\to.points = this.points;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphMap2D.prototype.onConfigure = function(o)\\r\\n\\t{\\r\\n\\t\\tif(o.current_pos)\\r\\n\\t\\t\\tthis.current_pos = o.current_pos;\\r\\n\\t\\tif(o.points)\\r\\n\\t\\t\\tthis.points = o.points;\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"math/map2D\\\", LGraphMap2D );\\r\\n\\r\\n\\r\\n\\tfunction LGraphRemapWeights()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"in\\\",\\\"array\\\"); //array because they are already in order\\r\\n\\t\\tthis.addOutput(\\\"out\\\",\\\"object\\\");\\r\\n\\t\\tthis.points = [];\\t//2D points, name of point (\\\"happy\\\",\\\"sad\\\") and weights (\\\"mouth_left\\\":0.4, \\\"mouth_right\\\":0.3)\\r\\n\\t\\tthis.current_weights = {}; //object that tells the current state of weights, like \\\"mouth_left\\\":0.3, ...\\r\\n\\t\\tthis.properties = { enabled: true };\\r\\n\\t\\tvar node = this;\\r\\n\\t\\tthis.combo = this.addWidget(\\\"combo\\\",\\\"Point\\\", \\\"\\\", function(v){\\r\\n\\t\\t\\tnode._selected_point = node.findPoint(v);\\r\\n\\t\\t}, { values:[] } );\\r\\n\\t\\tthis.import_button = this.addWidget(\\\"button\\\", \\\"import weights\\\", \\\"\\\", function(){\\r\\n\\t\\t\\tnode.importWeights(true,true);\\r\\n\\t\\t});\\r\\n\\t\\tthis.size = [170,80];\\r\\n\\t\\tthis._selected_point = null;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRemapWeights.title = \\\"Remap Weights\\\";\\r\\n\\r\\n\\tLGraphRemapWeights.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tvar enabled = this.getInputOrProperty(\\\"enabled\\\");\\r\\n\\t\\tif(!enabled)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar point_weights = this.getInputData(0); //array\\r\\n\\r\\n\\t\\tif(this.inputs)\\r\\n\\t\\tfor(var i = 1; i < this.inputs.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar input_info = this.inputs[i];\\r\\n\\t\\t\\tif(input_info.name == \\\"selected\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar selected = this.getInputData(i); \\r\\n\\t\\t\\t\\tif(selected)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tthis._selected_point = this.findPoint(selected);\\r\\n\\t\\t\\t\\t\\tthis.combo.value = selected;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var i in this.current_weights)\\r\\n\\t\\t\\tthis.current_weights[i] = 0;\\r\\n\\r\\n\\t\\tvar points_has_changed = false;\\r\\n\\t\\tif( point_weights )\\r\\n\\t\\tfor(var i = 0; i < point_weights.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar point = this.points[i];\\r\\n\\t\\t\\tif(!point)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tpoints_has_changed = true;\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tvar w = point_weights[i]; //input\\r\\n\\t\\t\\t//for(var j = 0, l = point.weights.length; j < lw && j < l; ++j)\\r\\n\\t\\t\\tfor(var j in point.weights)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar v = (point.weights[j] || 0) * w;\\r\\n\\t\\t\\t\\tthis.current_weights[j] += v;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//output weights\\r\\n\\t\\tthis.setOutputData(0, this.current_weights );\\r\\n\\r\\n\\t\\tif(this.outputs)\\r\\n\\t\\tfor(var i = 1; i < this.outputs.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar output_info = this.outputs[i];\\r\\n\\t\\t\\tif(!output_info)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif(output_info.name == \\\"selected\\\")\\r\\n\\t\\t\\t\\tthis.setOutputData(i, this._selected_point ? this._selected_point.name : \\\"\\\" );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis.setOutputData(i, this.current_weights[output_info.name] );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(points_has_changed)\\r\\n\\t\\t\\tthis.importPoints();\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRemapWeights.prototype.onAction = function(name, params)\\r\\n\\t{\\r\\n\\t\\tif(name == \\\"import\\\")\\r\\n\\t\\t\\tthis.importWeights(true); //do not force or recursion ahead\\r\\n\\t}\\r\\n\\r\\n\\t//adds a 2D point with the weights associated to it (not used?)\\r\\n\\tLGraphRemapWeights.prototype.addPoint = function( name, weights )\\r\\n\\t{\\r\\n\\t\\tif(!weights)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"no weights passed to add point\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tvar w = {};\\r\\n\\t\\tfor(var i in weights)\\r\\n\\t\\t\\tw[i] = weights[i];\\r\\n\\t\\tthis.points.push({name: name, weights: w});\\t\\r\\n\\t}\\r\\n\\r\\n\\t//import 2D points from input node (usually LGraphMap2D), just the names\\r\\n\\tLGraphRemapWeights.prototype.importPoints = function()\\r\\n\\t{\\r\\n\\t\\tvar input_node = this.getInputNode(0);\\r\\n\\t\\tif(!input_node || !input_node.points || !input_node.points.length )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis.points.length = input_node.points.length;\\r\\n\\t\\tfor(var i = 0; i < this.points.length; ++i)\\r\\n\\t\\t\\tthis.points[i] = { name: input_node.points[i].name, weights: {} };\\r\\n\\t\\tthis._selected_point = this.points[0];\\r\\n\\t\\tif( this._selected_point )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.combo.value = this._selected_point.name;\\r\\n\\t\\t\\tthis.combo.options.values = this.points.map(function(a){return a.name;});\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.combo.value = \\\"\\\";\\r\\n\\t\\t\\tthis.combo.options.values = [\\\"\\\"];\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//when called it reads the output nodes to get which morph targets it is using and read their weights\\r\\n\\t//then sets the current 2D point to this weights\\r\\n\\tLGraphRemapWeights.prototype.importWeights = function( assign, run_graph )\\r\\n\\t{\\r\\n\\t\\t//force data to flow from inputs to here\\r\\n\\t\\tif(this.graph && run_graph)\\r\\n\\t\\t\\tthis.graph.runStep(1,false, this.order );\\r\\n\\r\\n\\t\\tvar name_weights = this.getInputDataByName(\\\"name_weights\\\");\\r\\n\\t\\t\\r\\n\\t\\tif(name_weights)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var j in name_weights)\\r\\n\\t\\t\\t\\tthis.current_weights[j] = name_weights[j];\\r\\n\\t\\t}\\r\\n\\t\\telse //get from output\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar output_nodes = this.getOutputNodes(0);\\r\\n\\t\\t\\tif(!output_nodes || output_nodes.length == 0)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t\\tfor(var i = 0; i < output_nodes.length; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar output_node = output_nodes[i];\\r\\n\\t\\t\\t\\tif( !output_node.getComponent )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tvar component = output_node.getComponent();\\r\\n\\t\\t\\t\\tif(!component)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tvar compo_weights = component.name_weights;\\r\\n\\t\\t\\t\\tvar compo_weights = component.name_weights;\\r\\n\\t\\t\\t\\tfor(var j in compo_weights)\\r\\n\\t\\t\\t\\t\\tthis.current_weights[j] = compo_weights[j];\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.setDirtyCanvas(true);\\r\\n\\r\\n\\t\\tif( !assign || !this._selected_point)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._selected_point.weights = {};\\r\\n\\t\\tfor(var i in this.current_weights)\\r\\n\\t\\t\\tthis._selected_point.weights[i] = this.current_weights[i];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRemapWeights.prototype.findPoint = function( name )\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < this.points.length; ++i)\\r\\n\\t\\t\\tif( this.points[i].name == name )\\r\\n\\t\\t\\t\\treturn this.points[i];\\r\\n\\t\\treturn null;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRemapWeights.prototype.assignCurrentWeightsToPoint = function( point )\\r\\n\\t{\\r\\n\\t\\tfor(var i in this.current_weights)\\r\\n\\t\\t\\tpoint.weights[i] = this.current_weights[i];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRemapWeights.prototype.onSerialize = function(o)\\r\\n\\t{\\r\\n\\t\\to.current_weights = this.current_weights;\\r\\n\\t\\to.points = this.points;\\r\\n\\t\\to.enabled = this.enabled;\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRemapWeights.prototype.onConfigure = function(o)\\r\\n\\t{\\r\\n\\t\\tif(o.enabled !== undefined)\\r\\n\\t\\t\\tthis.properties.enabled = o.enabled;\\r\\n\\t\\tif( o.current_weights )\\r\\n\\t\\t\\tthis.current_weights = o.current_weights;\\r\\n\\t\\tif(o.points)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.points = o.points;\\r\\n\\r\\n\\t\\t\\t//legacy\\r\\n\\t\\t\\tfor(var i = 0;i < this.points.length; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar p = this.points[i];\\r\\n\\t\\t\\t\\tif(p.weights && p.weights.constructor !== Object)\\r\\n\\t\\t\\t\\t\\tp.weights = {};\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//widget\\r\\n\\t\\t\\tthis.combo.options.values = this.points.map(function(a){ return a.name; });\\r\\n\\t\\t\\tthis.combo.value = this.combo.options.values[0] || \\\"\\\";\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLGraphRemapWeights.prototype.onInspect = function( inspector )\\r\\n\\t{\\r\\n\\t\\tvar node = this;\\r\\n\\r\\n\\t\\tinspector.addButton(null,\\\"Import points from input\\\", { callback: function(){\\r\\n\\t\\t\\tnode.importPoints();\\r\\n\\t\\t\\tinspector.refresh();\\r\\n\\t\\t}});\\r\\n\\t\\tinspector.addButton(null,\\\"Import weights from output\\\", { callback: function(){\\r\\n\\t\\t\\tnode.importWeights(null,true);\\r\\n\\t\\t\\tinspector.refresh();\\r\\n\\t\\t}});\\r\\n\\r\\n\\t\\tinspector.addSeparator();\\r\\n\\r\\n\\t\\tinspector.addTitle(\\\"Points\\\");\\r\\n\\r\\n\\t\\tvar point_names = [];\\r\\n\\t\\tfor(var i = 0; i < this.points.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar point = this.points[i];\\r\\n\\t\\t\\tpoint_names.push( point.name );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!this._selected_point && this.points.length)\\r\\n\\t\\t\\tthis._selected_point = this.points[0];\\r\\n\\r\\n\\t\\tinspector.addCombo(\\\"Points\\\",this._selected_point ? this._selected_point.name : \\\"\\\", { values: point_names, callback: function(v){\\r\\n\\t\\t\\tnode._selected_point = node.findPoint(v);\\r\\n\\t\\t\\tnode.combo.value = v;\\r\\n\\t\\t\\tif(node._selected_point)\\r\\n\\t\\t\\t\\tfor(var i in node._selected_point.weights)\\r\\n\\t\\t\\t\\t\\tnode.current_weights[i] = node._selected_point.weights[i];\\r\\n\\t\\t\\tnode.setDirtyCanvas(true);\\r\\n\\t\\t\\tinspector.refresh();\\r\\n\\t\\t}});\\r\\n\\r\\n\\t\\tinspector.addButton(null,\\\"current weights to point\\\", { callback: function(){\\r\\n\\t\\t\\tif(!node._selected_point)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tnode.assignCurrentWeightsToPoint(node._selected_point);\\r\\n\\t\\t\\tinspector.refresh();\\r\\n\\t\\t}});\\r\\n\\r\\n\\t\\tinspector.addSeparator();\\r\\n\\t\\tinspector.addTitle(\\\"Weights\\\");\\r\\n\\r\\n\\t\\tfor(var i in this.current_weights)\\r\\n\\t\\t{\\r\\n\\t\\t\\tinspector.addNumber( i, this.current_weights[i], { name_width: \\\"80%\\\", index: i, callback: function(v){\\r\\n\\t\\t\\t\\tnode.current_weights[ this.options.index ] = v;\\r\\n\\t\\t\\t}});\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tinspector.addStringButton(\\\"Add Weight\\\",\\\"\\\", { button: \\\"+\\\", callback_button: function(v){\\r\\n\\t\\t\\tnode.current_weights[v] = 0;\\r\\n\\t\\t\\tinspector.refresh();\\r\\n\\t\\t}});\\r\\n\\r\\n\\t\\tinspector.addSeparator();\\r\\n\\t}\\r\\n\\r\\n LGraphRemapWeights.prototype.onGetOutputs = function() {\\r\\n var r = [[\\\"selected\\\",\\\"string\\\"]];\\r\\n\\t\\tfor(var i in this.current_weights)\\r\\n\\t\\t\\tr.push([i,\\\"number\\\"]);\\r\\n\\t\\treturn r;\\r\\n };\\r\\n\\r\\n\\tLGraphRemapWeights.prototype.onGetInputs = function()\\r\\n\\t{\\r\\n\\t\\treturn [[\\\"enabled\\\",\\\"boolean\\\"],[\\\"import\\\",LiteGraph.ACTION],[\\\"selected\\\",\\\"string\\\"],[\\\"name_weights\\\",\\\"object\\\"]];\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"math/remap_weights\\\", LGraphRemapWeights );\\r\\n\\r\\n\\t//******************************************\\r\\n\\r\\n\\tfunction LGraphCameraProject()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"camera\\\",\\\"component,camera\\\");\\r\\n\\t\\tthis.addInput(\\\"pos3D\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addOutput(\\\"screen_pos\\\",\\\"vec4\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tclamp_to_viewport: false,\\r\\n\\t\\t\\treverse_y: true\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis._screen_pos = vec4.create();\\r\\n\\t\\tthis.size = [160,50];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphCameraProject.title = \\\"Camera Project\\\";\\r\\n\\r\\n\\tLGraphCameraProject.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tvar camera = this.getInputData(0);\\r\\n\\t\\tvar pos = this.getInputData(1);\\r\\n\\t\\tif(!camera || camera.constructor != LS.Camera || !pos)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tcamera.project( pos, null, this._screen_pos, this.properties.reverse_y );\\r\\n\\t\\tvar dist = vec3.distance( camera.eye, pos );\\r\\n\\t\\tthis._screen_pos[3] = (Math.sin(camera.fov * DEG2RAD) / dist) * 100.0;\\r\\n\\r\\n\\t\\tif( this.properties.clamp_to_viewport )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._screen_pos[0] = Math.clamp( this._screen_pos[0], 0, gl.canvas.width);\\r\\n\\t\\t\\tthis._screen_pos[1] = Math.clamp( this._screen_pos[1], 0, gl.canvas.height);\\r\\n\\t\\t}\\r\\n\\t\\tthis.setOutputData(0, this._screen_pos);\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"math3d/camera_project\\\", LGraphCameraProject );\\r\\n\\r\\n\\t//*********************************************\\r\\n\\r\\n\\tfunction LGraphInputKey()\\r\\n\\t{\\r\\n\\t\\tthis.addOutput(\\\"\\\",\\\"boolean\\\");\\r\\n\\t\\tthis.addOutput(\\\"\\\",LiteGraph.EVENT);\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tkey: \\\"SPACE\\\"\\r\\n\\t\\t};\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tthis.widgets_up = true;\\r\\n\\t\\tthis.addWidget(\\\"text\\\",\\\"Key\\\",this.properties.key,function(v){\\r\\n\\t\\t\\tif(v)\\r\\n\\t\\t\\t\\tthat.properties.key = v;\\r\\n\\t\\t});\\r\\n\\t}\\r\\n\\r\\n\\tLGraphInputKey.title = \\\"Key\\\";\\r\\n\\r\\n LGraphInputKey.prototype.getTitle = function() {\\r\\n if (this.flags.collapsed) {\\r\\n return \\\"Key: \\\" + this.properties.key;\\r\\n }\\r\\n return this.title;\\r\\n };\\r\\n\\r\\n\\tLGraphInputKey.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t\\tvar v = LS.Input.wasKeyPressed(this.properties.key);\\r\\n\\t\\tthis.boxcolor = v ? \\\"#fff\\\" : \\\"#000\\\";\\r\\n\\t\\tthis.setOutputData(0,v);\\r\\n\\t\\tif(v)\\r\\n\\t\\t\\tthis.triggerSlot(1,this.properties.key);\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"input/key\\\", LGraphInputKey );\\r\\n}\\r\\n\\r\\n///@FILE:../src/graph/shader.js\\r\\n///@INFO: GRAPHS\\r\\nif(typeof(LiteGraph) != \\\"undefined\\\")\\r\\n{\\r\\n\\tvar SHADER_COLOR = \\\"#333\\\";\\r\\n\\tvar SHADER_BGCOLOR = \\\"#333\\\";\\r\\n\\tvar SHADER_TITLE_TEXT_COLOR = \\\"#AAA\\\";\\r\\n\\r\\n\\tvar getInputLinkID = LiteGraph.getInputLinkID = function getInputLinkID( node, num )\\r\\n\\t{\\r\\n\\t\\tvar info = node.getInputInfo( num );\\t\\r\\n\\t\\tif(!info)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tif(info.link == -1)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tvar link = node.graph.links[ info.link ];\\r\\n\\t\\tif(!link)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn \\\"LINK_\\\" + link.origin_id + \\\"_\\\" + link.origin_slot;\\r\\n\\t}\\r\\n\\r\\n\\tvar getOutputLinkID = LiteGraph.getOutputLinkID = function getOutputLinkID( node, num, force )\\r\\n\\t{\\r\\n\\t\\tvar info = node.getOutputInfo( num );\\t\\r\\n\\t\\tif((!info || !info.links || !info.links.length) && !force )\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn \\\"LINK_\\\" + node.id + \\\"_\\\" + num;\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerShaderNode = function registerShaderNode( name, ctor )\\r\\n\\t{\\r\\n\\t\\tctor.filter = \\\"shader\\\";\\r\\n\\t\\tctor.color = SHADER_COLOR;\\r\\n\\t\\tctor.bgcolor = SHADER_BGCOLOR;\\r\\n\\t\\tctor.title_text_color = SHADER_TITLE_TEXT_COLOR;\\r\\n\\t\\tLiteGraph.registerNodeType(\\\"shader/\\\" + name, ctor );\\r\\n\\t}\\r\\n\\r\\n\\tvar typeToGLSL = {\\r\\n\\t\\tnumber: \\\"float\\\",\\r\\n\\t\\tcolor: \\\"vec3\\\",\\r\\n\\t\\tcolor4: \\\"vec4\\\"\\r\\n\\t};\\r\\n\\r\\n\\tLiteGraph.generatePreviewShader = function(node)\\r\\n\\t{\\r\\n\\t\\t//get ancestors\\r\\n\\t\\tvar nodes = node.graph.getAncestors(node);\\r\\n\\t\\tvar context = { uniforms: [] };\\r\\n\\t\\tvar graph_code = \\\"\\\";\\r\\n\\t\\tvar nodes = this._graph._nodes_in_order;\\r\\n\\t\\tfor(var i = 0; i < nodes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar node = nodes[i];\\r\\n\\t\\t\\tif( node.onGetCode )\\r\\n\\t\\t\\t\\tgraph_code += node.onGetCode( \\\"glsl\\\", context );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar uniforms_code = \\\"\\\";\\r\\n\\t\\tfor(var i = 0; i < context.uniforms.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar uniform = context.uniforms[i];\\r\\n\\t\\t\\tuniforms_code += \\\"uniform \\\" + uniform.type + \\\" \\\" + uniform.link_name + \\\";\\\\n\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t}\\r\\n\\r\\n\\t//base function\\r\\n\\t/*\\r\\n\\tfunction ShaderCodeFragment( code )\\r\\n\\t{\\r\\n\\t\\tthis.id = -1;\\r\\n\\t\\tthis.code = \\\"\\\";\\r\\n\\t\\tthis.inputs = [];\\r\\n\\t\\tthis.outputs = [];\\r\\n\\t\\tthis.properties = [];\\r\\n\\t}\\r\\n\\r\\n\\tShaderCodeFragment.prototype.toString = function()\\r\\n\\t{\\r\\n\\t\\t\\r\\n\\t}\\r\\n\\r\\n\\t//use\\r\\n\\tfunction LGraphShaderFloat()\\r\\n\\t{\\r\\n\\t\\tthis.addOutput(\\\"v\\\",\\\"number\\\");\\r\\n\\t\\tthis.properties = { value: 0, uniform: \\\"\\\" };\\r\\n\\t}\\r\\n\\r\\n\\tLGraphShaderFloat.prototype.onGenerateCode = function( context )\\r\\n\\t{\\r\\n\\t\\tif(!this._fragment)\\r\\n\\t\\t\\tthis._fragment = new ShaderCodeFragment(\\\"float @OUT_0 = @PROP_0;\\\\n\\\");\\r\\n\\t\\tvar output = getOutputLinkID( this, 0 );\\r\\n\\t\\tthis._fragment.outputs[0] = output;\\r\\n\\t\\tthis.setOutputData(0, this._fragment );\\r\\n\\t}\\r\\n\\r\\n\\tfunction LGraphShaderScale()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"in\\\",\\\"number\\\");\\r\\n\\t\\tthis.addInput(\\\"f\\\",\\\"number\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\",\\\"number\\\");\\r\\n\\t}\\r\\n\\r\\n\\tLGraphShaderScale.prototype.onGenerateCode = function( context )\\r\\n\\t{\\r\\n\\t\\tif(!this._fragment)\\r\\n\\t\\t\\tthis._fragment = new ShaderCodeFragment(\\\"float @OUT_0 = @PROP_0;\\\\n\\\");\\r\\n\\t\\tvar output = getOutputLinkID( this, 0 );\\r\\n\\t\\tthis._fragment.outputs[0] = output;\\r\\n\\t\\tthis.setOutputData(0, this._fragment );\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\tfunction createShaderConstantNode( type, value )\\r\\n\\t{\\r\\n\\t\\tvar original_type = type;\\r\\n\\t\\tvar ctor_code = \\\"this.addOutput(\\\\\\\"v\\\\\\\",\\\\\\\"\\\"+type+\\\"\\\\\\\");\\\\n\\\";\\r\\n\\t\\tctor_code += \\\"this.properties = { value: \\\"+JSON.stringify(value)+\\\", uniform:\\\\\\\"\\\\\\\"};\\\\n\\\";\\r\\n\\t\\tvar ctor = new Function( ctor_code );\\r\\n\\r\\n\\t\\ttype = typeToGLSL[type] || type;\\r\\n\\r\\n\\t\\tvar getcode_code = \\\"if( lang != \\\\\\\"glsl\\\\\\\" ) return \\\\\\\"\\\\\\\";\\\\n\\\";\\r\\n\\t\\tgetcode_code += \\\"var output = LiteGraph.getOutputLinkID( this, 0, true );\\\\n\\\";\\r\\n\\t\\tgetcode_code += \\\"if(this.properties.uniform)\\\\n \\t{ context.uniforms.push({ name: this.properties.uniform, link_name: output, type: \\\\\\\"\\\"+type+\\\"\\\\\\\", value: this.properties.value });\\\\n return \\\\\\\"\\\\\\\";\\t}\\\";\\r\\n\\t\\tgetcode_code += \\\"return \\\\\\\"\\\\\\\\t \\\"+type+\\\" \\\\\\\"+output+\\\\\\\" = \\\"+type+\\\"(\\\\\\\"+String(this.properties.value)+\\\\\\\");\\\\\\\\n\\\\\\\";\\\\n\\\";\\r\\n\\t\\tctor.prototype.onGetCode = new Function( \\\"lang\\\",\\\"context\\\", getcode_code );\\r\\n\\r\\n\\t\\tvar exec_code = \\\"this.setOutputDataType( 0, \\\\\\\"\\\"+type+\\\"\\\\\\\" );\\\\n\\\";\\r\\n\\t\\tctor.prototype.onExecute = new Function( exec_code );\\r\\n\\t\\t\\t\\t\\r\\n\\t\\tLiteGraph.registerShaderNode( original_type.toLowerCase(), ctor );\\r\\n\\t\\tctor.title = original_type;\\r\\n\\t\\tctor.desc = \\\"Shader constant \\\" + original_type;\\r\\n\\r\\n\\t\\tctor.prototype.getTitle = function()\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(this.flags.collapsed)\\r\\n\\t\\t\\t\\treturn String( this.properties.value );\\r\\n\\t\\t\\treturn this.title;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tctor.prototype.onDrawBackground = function(){ \\r\\n\\t\\t\\tif( this.properties.value.length < 5 )\\r\\n\\t\\t\\t\\tthis.outputs[0].label = String( this.properties.value );\\r\\n\\t\\t}\\r\\n\\t\\treturn ctor;\\r\\n\\t}\\r\\n\\r\\n\\tcreateShaderConstantNode(\\\"float\\\", 1 );\\r\\n\\tcreateShaderConstantNode(\\\"vec2\\\", [0,0] );\\r\\n\\tcreateShaderConstantNode(\\\"vec3\\\", [0,0,0] );\\r\\n\\tcreateShaderConstantNode(\\\"color\\\", [1,1,1] );\\r\\n\\tcreateShaderConstantNode(\\\"vec4\\\", [0,0,0,0] );\\r\\n\\tcreateShaderConstantNode(\\\"color4\\\", [1,1,1,1] );\\r\\n\\tcreateShaderConstantNode(\\\"mat3\\\", [1,0,0,0,1,0,0,0,1] );\\r\\n\\tcreateShaderConstantNode(\\\"mat4\\\", [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1] );\\r\\n\\r\\n\\tfunction createShaderOperationNode( name, inputs, output, op_code, title )\\r\\n\\t{\\r\\n\\t\\tif(inputs.length >= 10)\\r\\n\\t\\t\\tthrow(\\\"cannot be used with more than 10 vars, regexp not supporting it\\\");\\r\\n\\r\\n\\t\\tvar ctor_code = \\\"this.addOutput(\\\\\\\"v\\\\\\\",\\\\\\\"\\\"+output+\\\"\\\\\\\");\\\\n\\\";\\r\\n\\t\\tfor(var i = 0; i < inputs.length; ++i)\\r\\n\\t\\t\\tctor_code += \\\"this.addInput(\\\\\\\"\\\" + String.fromCharCode(65+i)\\t+ \\\"\\\\\\\",\\\\\\\"\\\" + inputs[i] + \\\"\\\\\\\");\\\\n\\\";\\r\\n\\t\\tvar ctor = new Function( ctor_code );\\r\\n\\r\\n\\t\\tvar exec_code = \\\"\\tif( lang != \\\\\\\"glsl\\\\\\\" ) return \\\\\\\"\\\\\\\";\\\\n\\\";\\r\\n\\t\\texec_code += \\\"\\tvar code = \\\\\\\"\\\\\\\";\\\\n\\\";\\r\\n\\t\\tfor(var i = 0; i < inputs.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar type = typeToGLSL[inputs[i]] || inputs[i];\\r\\n\\t\\t\\texec_code += \\\"var input_\\\" + i + \\\" = LiteGraph.getInputLinkID( this, \\\"+i+\\\" );\\\\n\\\";\\r\\n\\t\\t\\texec_code += \\\"if(input_\\\" + i + \\\" == null) input_\\\" + i + \\\" = \\\\\\\"\\\" + type + \\\"(1.0)\\\\\\\";\\\\n\\\";\\r\\n\\t\\t\\top_code = op_code.replace( new RegExp(\\\"@\\\" + i,\\\"gi\\\"), \\\"\\\\\\\" + input_\\\" + i + \\\" + \\\\\\\"\\\");\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\texec_code += \\\"\\tvar output = LiteGraph.getOutputLinkID(this, 0, true);\\\\n\\\";\\r\\n\\t\\texec_code += \\\"\\treturn \\\\\\\"\\\\\\\\t \\\"+(typeToGLSL[output] || output)+\\\" \\\\\\\"+output+\\\\\\\" = \\\"+op_code+\\\";\\\\\\\\n\\\\\\\";\\\\n\\\";\\r\\n\\t\\tctor.prototype.onGetCode = new Function( \\\"lang\\\", exec_code );\\r\\n\\r\\n\\t\\tif(title)\\r\\n\\t\\t\\tctor.prototype.getTitle = new Function( \\\" return \\\\\\\"\\\" + title + \\\"\\\\\\\";\\\" );\\r\\n\\t\\t\\t\\t\\r\\n\\t\\tctor.title = name;\\r\\n\\t\\tctor.filter = \\\"shader\\\";\\r\\n\\t\\tLiteGraph.registerShaderNode( name.toLowerCase(), ctor );\\r\\n\\r\\n\\t\\treturn ctor;\\r\\n\\t}\\r\\n\\r\\n\\tcreateShaderOperationNode(\\\"Float->Vec2\\\", [\\\"float\\\"], \\\"vec2\\\", \\\"vec2(@0)\\\", \\\"vec2\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Float->Vec3\\\", [\\\"float\\\"], \\\"vec3\\\", \\\"vec3(@0)\\\", \\\"vec3\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Add Float\\\", [\\\"float\\\",\\\"float\\\"], \\\"float\\\", \\\"@0 + @1\\\", \\\"A+B\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Add Vec3\\\", [\\\"vec3\\\",\\\"vec3\\\"], \\\"vec3\\\", \\\"@0 + @1\\\", \\\"A+B\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Sub Vec3\\\", [\\\"vec3\\\",\\\"vec3\\\"], \\\"vec3\\\", \\\"@0 - @1\\\", \\\"A-B\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Scale Vec3\\\", [\\\"vec3\\\",\\\"float\\\"], \\\"vec3\\\", \\\"@0 * @1\\\", \\\"A*B\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Sub Float\\\", [\\\"float\\\",\\\"float\\\"], \\\"float\\\", \\\"@0 - @1\\\", \\\"A-B\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Normalize Vec2\\\", [\\\"vec2\\\"], \\\"vec2\\\", \\\"normalize(@0)\\\", \\\"normalize\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Normalize Vec3\\\", [\\\"vec3\\\"], \\\"vec3\\\", \\\"normalize(@0)\\\", \\\"normalize\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Exp Float\\\", [\\\"float\\\"], \\\"float\\\", \\\"exp(@0)\\\", \\\"exp\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Pow Float\\\", [\\\"float\\\",\\\"float\\\"], \\\"float\\\", \\\"pow(@0,@1)\\\", \\\"pow\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Pow Vec3\\\", [\\\"vec3\\\",\\\"float\\\"], \\\"vec3\\\", \\\"pow(@0,@1)\\\", \\\"pow\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Float->Vec3\\\", [\\\"float\\\"], \\\"vec3\\\", \\\"vec3(@0)\\\", \\\"vec3\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Dot\\\", [\\\"vec3\\\",\\\"vec3\\\"], \\\"float\\\", \\\"dot(@0,@1)\\\", \\\"dot\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Cross\\\", [\\\"vec3\\\",\\\"vec3\\\"], \\\"vec3\\\", \\\"cross(@0,@1)\\\", \\\"cross\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Abs Vec3\\\", [\\\"vec3\\\"], \\\"vec3\\\", \\\"abs(@0)\\\", \\\"abs\\\" );\\r\\n\\tcreateShaderOperationNode(\\\"Abs Float\\\", [\\\"float\\\"], \\\"float\\\", \\\"abs(@0)\\\", \\\"abs\\\" );\\r\\n\\r\\n\\tfunction LGraphShaderSurface()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"Albedo\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addInput(\\\"Emission\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addInput(\\\"Normal\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addInput(\\\"Specular\\\",\\\"float\\\");\\r\\n\\t\\tthis.addInput(\\\"Gloss\\\",\\\"float\\\");\\r\\n\\t\\tthis.addInput(\\\"Reflectivity\\\",\\\"float\\\");\\r\\n\\t\\tthis.addInput(\\\"Alpha\\\",\\\"float\\\");\\r\\n\\t\\tthis.size = [90,110];\\r\\n\\r\\n\\t\\tthis.properties = {};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphShaderSurface.title = \\\"Surface\\\";\\r\\n\\tLGraphShaderSurface.desc = \\\"Surface properties\\\";\\r\\n\\r\\n\\tLGraphShaderSurface.prototype.onExecute = function()\\r\\n\\t{\\r\\n\\t}\\r\\n\\r\\n\\tLGraphShaderSurface.prototype.onGetCode = function( lang )\\r\\n\\t{\\r\\n\\t\\tif( lang != \\\"glsl\\\" )\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\t/* created from GraphCode so it can add the graph code in between\\r\\n\\t\\tvar surface_code = \\\"void surf( in Input IN, inout SurfaceOutput o ) {\\\\n\\\\\\r\\n\\t\\to.Albedo = vec3(1.0) * IN.color.xyz;\\\\n\\\\\\r\\n\\t\\to.Normal = IN.worldNormal;\\\\n\\\\\\r\\n\\t\\to.Emission = vec3(0.0);\\\\n\\\\\\r\\n\\t\\to.Specular = 1.0;\\\\n\\\\\\r\\n\\t\\to.Gloss = 40.0;\\\\n\\\\\\r\\n\\t\\to.Reflectivity = 0.0;\\\\n\\\\\\r\\n\\t\\to.Alpha = IN.color.a;\\\\n\\\";\\r\\n\\t\\t*/\\r\\n\\r\\n\\t\\tvar code = \\\"\\\\n\\\";\\r\\n\\t\\tvar input = getInputLinkID( this, 0 );\\r\\n\\t\\tif( input )\\r\\n\\t\\t\\tcode += \\\"\\\\t o.Albedo = \\\"+input+\\\";\\\\n\\\";\\r\\n\\t\\tinput = getInputLinkID( this, 1 );\\r\\n\\t\\tif( input )\\r\\n\\t\\t\\tcode += \\\"\\\\t o.Emission = \\\"+input+\\\";\\\\n\\\";\\r\\n\\t\\tinput = getInputLinkID( this, 2 );\\r\\n\\t\\tif( input )\\r\\n\\t\\t\\tcode += \\\"\\\\t o.Normal = \\\"+input+\\\";\\\\n\\\";\\r\\n\\t\\tinput = getInputLinkID( this, 3 );\\r\\n\\t\\tif( input )\\r\\n\\t\\t\\tcode += \\\"\\\\t o.Specular = \\\"+input+\\\";\\\\n\\\";\\r\\n\\t\\tinput = getInputLinkID( this, 4 );\\r\\n\\t\\tif( input )\\r\\n\\t\\t\\tcode += \\\"\\\\t o.Gloss = \\\"+input+\\\";\\\\n\\\";\\r\\n\\t\\tinput = getInputLinkID( this, 5 );\\r\\n\\t\\tif( input )\\r\\n\\t\\t\\tcode += \\\"\\\\t o.Reflectivity = \\\"+input+\\\";\\\\n\\\";\\r\\n\\t\\tinput = getInputLinkID( this, 6 );\\r\\n\\t\\tif( input )\\r\\n\\t\\t\\tcode += \\\"\\\\t o.Alpha = \\\"+input+\\\";\\\\n\\\";\\r\\n\\r\\n\\t\\treturn code;\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerShaderNode( \\\"surface\\\", LGraphShaderSurface );\\r\\n\\r\\n\\tfunction LGraphShaderColor()\\r\\n\\t{\\r\\n\\t\\tthis.addOutput(\\\"c\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.properties = {\\r\\n\\t\\t\\tvalue: [1,1,1],\\r\\n\\t\\t\\tuniform: \\\"\\\"\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\t//mult vec3\\r\\n\\tfunction LGraphShaderVec3ToXYZ()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"in\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addOutput(\\\"x\\\",\\\"float\\\");\\r\\n\\t\\tthis.addOutput(\\\"y\\\",\\\"float\\\");\\r\\n\\t\\tthis.addOutput(\\\"z\\\",\\\"float\\\");\\r\\n\\t}\\r\\n\\r\\n\\tLGraphShaderVec3ToXYZ.title = \\\"Vec3->XYZ\\\";\\r\\n\\tLGraphShaderVec3ToXYZ.desc = \\\"Split vec3\\\";\\r\\n\\tLGraphShaderVec3ToXYZ.filter = \\\"shader\\\";\\r\\n\\r\\n\\tLGraphShaderVec3ToXYZ.prototype.onGetCode = function(type)\\r\\n\\t{\\r\\n\\t\\tif(type != \\\"glsl\\\")\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\tvar input = getInputLinkID( this, 0 );\\r\\n\\t\\tvar output_0 = getOutputLinkID( this, 0 );\\r\\n\\t\\tvar output_1 = getOutputLinkID( this, 1 );\\r\\n\\t\\tvar output_2 = getOutputLinkID( this, 2 );\\r\\n\\t\\tif(!input)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\tvar str = \\\"\\\";\\r\\n\\t\\tif(input && output_0)\\r\\n\\t\\t\\tstr +=\\\"\\tfloat \\\"+output_0+\\\" = \\\"+ input +\\\".x;\\\\n\\\";\\r\\n\\t\\tif(input && output_0)\\r\\n\\t\\t\\tstr +=\\\"\\tfloat \\\"+output_1+\\\" = \\\"+ input +\\\".y;\\\\n\\\";\\r\\n\\t\\tif(input && output_0)\\r\\n\\t\\t\\tstr +=\\\"\\tfloat \\\"+output_2+\\\" = \\\"+ input +\\\".z;\\\\n\\\";\\r\\n\\t\\treturn str;\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerShaderNode( \\\"Vec3toXYZ\\\", LGraphShaderVec3ToXYZ );\\r\\n\\r\\n\\r\\n\\t/*\\r\\n\\tfunction LGraphShaderAbs()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"in\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\");\\r\\n\\t}\\r\\n\\r\\n\\tLGraphShaderAbs.title = \\\"Scale\\\";\\r\\n\\tLGraphShaderAbs.desc = \\\"Multiply by number\\\";\\r\\n\\tLGraphShaderAbs.filter = \\\"shader\\\";\\r\\n\\r\\n\\tLGraphShaderAbs.prototype.onGetCode = function(type)\\r\\n\\t{\\r\\n\\t\\tif(type != \\\"glsl\\\")\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\tvar input = getInputLinkID( this, 0 );\\r\\n\\t\\tvar output = getOutputLinkID( this, 0 );\\r\\n\\t\\tif(input && output)\\r\\n\\t\\t\\treturn \\\"vec3 \\\"+output+\\\" = \\\"+ input_0 +\\\" * \\\"+ input_1 +\\\";\\\\n\\\";\\r\\n\\t\\treturn \\\"\\\";\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"shader/scale\\\", LGraphShaderAbs );\\r\\n\\t*/\\r\\n\\r\\n\\t/*\\r\\n\\tfunction LGraphShaderFloat()\\r\\n\\t{\\r\\n\\t\\tthis.addOutput(\\\"out\\\",\\\"number\\\");\\r\\n\\t\\tthis.properties = { value: 1, uniform: \\\"\\\" };\\r\\n\\t}\\r\\n\\r\\n\\tLGraphShaderFloat.title = \\\"Const\\\";\\r\\n\\tLGraphShaderFloat.desc = \\\"Multiply by number\\\";\\r\\n\\tLGraphShaderFloat.filter = \\\"shader\\\";\\r\\n\\r\\n\\tLGraphShaderFloat.prototype.onGetCode = function( type, context )\\r\\n\\t{\\r\\n\\t\\tif(type != \\\"glsl\\\")\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\tvar output = getOutputLinkID( this, 0 );\\r\\n\\t\\tif(!output)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\tif(this.properties.uniform)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcontext.uniforms.push({ name: this.properties.uniform, link_name: output, type: \\\"float\\\", value: this.properties.value });\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn \\\"float \\\"+output+\\\" = \\\"+ this.properties.value.toFixed(3) +\\\";\\\\n\\\";\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"shader/float\\\", LGraphShaderFloat );\\r\\n\\t*/\\r\\n\\r\\n\\tfunction LGraphShaderSampler2D()\\r\\n\\t{\\r\\n\\t\\tthis.addOutput(\\\"out\\\",\\\"sampler2d\\\");\\r\\n\\t\\tthis.properties = { name:\\\"texture\\\", value: \\\"\\\" };\\r\\n\\t}\\r\\n\\r\\n\\tLGraphShaderSampler2D.title = \\\"Texture\\\";\\r\\n\\tLGraphShaderSampler2D.desc = \\\"To pass a texture\\\";\\r\\n\\r\\n\\tLGraphShaderSampler2D.prototype.onGetCode = function( type, context )\\r\\n\\t{\\r\\n\\t\\tif(type != \\\"glsl\\\")\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\tvar output = getOutputLinkID( this, 0 );\\r\\n\\t\\tif(!output)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\t\\tcontext.uniforms.push({ name: this.properties.name, link_name: output, type: \\\"sampler2D\\\", value: this.properties.value });\\r\\n\\t}\\r\\n\\r\\n\\t//LiteGraph.registerNodeType(\\\"shader/sampler2D\\\", LGraphShaderSampler2D );\\r\\n\\t//LiteGraph.registerShaderNode( \\\"scale\\\", LGraphShaderScale );\\r\\n\\r\\n\\r\\n\\t/*\\r\\n\\tfunction LGraphShaderDot()\\r\\n\\t{\\r\\n\\t\\tthis.addInput(\\\"A\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addInput(\\\"B\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addOutput(\\\"out\\\",\\\"number\\\");\\r\\n\\t\\tthis.properties = {};\\r\\n\\t}\\r\\n\\r\\n\\tLGraphShaderDot.title = \\\"Dot\\\";\\r\\n\\tLGraphShaderDot.desc = \\\"Dot product of two vec3\\\";\\r\\n\\tLGraphShaderDot.filter = \\\"shader\\\";\\r\\n\\r\\n\\tLGraphShaderDot.prototype.onGetCode = function( type, context )\\r\\n\\t{\\r\\n\\t\\tif(type != \\\"glsl\\\")\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\tvar input_A = getInputLinkID( this, 0 );\\r\\n\\t\\tvar input_B = getInputLinkID( this, 1 );\\r\\n\\t\\tvar output = getOutputLinkID( this, 0 );\\r\\n\\t\\tif(!output || !input_A || !input_B)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\treturn \\\"\\\\t float \\\"+output+\\\" = dot(\\\"+ input_A +\\\",\\\"+ input_B+\\\");\\\\n\\\";\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerNodeType(\\\"shader/dot\\\", LGraphShaderDot );\\r\\n\\t*/\\r\\n\\r\\n\\r\\n\\r\\n\\t/*\\r\\n\\t\\tvec4 color;\\\\n\\\\\\r\\n\\t\\tvec3 vertex;\\\\n\\\\\\r\\n\\t\\tvec3 normal;\\\\n\\\\\\r\\n\\t\\tvec2 uv;\\\\n\\\\\\r\\n\\t\\tvec2 uv1;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvec3 camPos;\\\\n\\\\\\r\\n\\t\\tvec3 viewDir;\\\\n\\\\\\r\\n\\t\\tvec3 worldPos;\\\\n\\\\\\r\\n\\t\\tvec3 worldNormal;\\\\n\\\\\\r\\n\\t\\tvec4 screenPos;\\\\n\\\\\\r\\n\\t*/\\r\\n\\r\\n\\tfunction LGraphShaderVertex()\\r\\n\\t{\\r\\n\\t\\tthis.addOutput(\\\"position\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addOutput(\\\"local_position\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addOutput(\\\"normal\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addOutput(\\\"local_normal\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addOutput(\\\"uv\\\",\\\"vec2\\\");\\r\\n\\t\\tthis.addOutput(\\\"screen\\\",\\\"vec4\\\");\\r\\n\\t\\tthis.addOutput(\\\"viewDir\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addOutput(\\\"camPos\\\",\\\"vec3\\\");\\r\\n\\t\\tthis.addOutput(\\\"color\\\",\\\"vec4\\\");\\r\\n\\t\\tthis.size = [97,156];\\r\\n\\t}\\r\\n\\r\\n\\tLGraphShaderVertex.title = \\\"Vertex\\\";\\r\\n\\tLGraphShaderVertex.desc = \\\"Reads info from vertex shader\\\";\\r\\n\\r\\n\\tLGraphShaderVertex.prototype.onGetCode = function( type, context )\\r\\n\\t{\\r\\n\\t\\tif(type != \\\"glsl\\\")\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\tvar code = \\\"\\\";\\r\\n\\t\\tvar output = getOutputLinkID( this, 0 );\\r\\n\\t\\tif(output)\\r\\n\\t\\t\\tcode += \\\"\\\\t vec3 \\\"+output+\\\" = IN.worldPos;\\\\n\\\";\\r\\n\\t\\toutput = getOutputLinkID( this, 1 );\\r\\n\\t\\tif(output)\\r\\n\\t\\t\\tcode += \\\"\\\\t vec3 \\\"+output+\\\" = IN.vertex;\\\\n\\\";\\r\\n\\t\\toutput = getOutputLinkID( this, 2 );\\r\\n\\t\\tif(output)\\r\\n\\t\\t\\tcode += \\\"\\\\t vec3 \\\"+output+\\\" = IN.worldNormal;\\\\n\\\";\\r\\n\\t\\toutput = getOutputLinkID( this, 3 );\\r\\n\\t\\tif(output)\\r\\n\\t\\t\\tcode += \\\"\\\\t vec3 \\\"+output+\\\" = IN.normal;\\\\n\\\";\\r\\n\\t\\toutput = getOutputLinkID( this, 4 );\\r\\n\\t\\tif(output)\\r\\n\\t\\t\\tcode += \\\"\\\\t vec2 \\\"+output+\\\" = IN.uv;\\\\n\\\";\\r\\n\\t\\toutput = getOutputLinkID( this, 5 );\\r\\n\\t\\tif(output)\\r\\n\\t\\t\\tcode += \\\"\\\\t vec4 \\\"+output+\\\" = IN.screenPos;\\\\n\\\";\\r\\n\\t\\toutput = getOutputLinkID( this, 6 );\\r\\n\\t\\tif(output)\\r\\n\\t\\t\\tcode += \\\"\\\\t vec3 \\\"+output+\\\" = IN.viewDir;\\\\n\\\";\\r\\n\\t\\toutput = getOutputLinkID( this, 7 );\\r\\n\\t\\tif(output)\\r\\n\\t\\t\\tcode += \\\"\\\\t vec3 \\\"+output+\\\" = IN.camPos;\\\\n\\\";\\r\\n\\t\\toutput = getOutputLinkID( this, 8 );\\r\\n\\t\\tif(output)\\r\\n\\t\\t\\tcode += \\\"\\\\t vec4 \\\"+output+\\\" = IN.color;\\\\n\\\";\\r\\n\\t\\treturn code;\\r\\n\\t}\\r\\n\\r\\n\\tLiteGraph.registerShaderNode( \\\"vertex\\\", LGraphShaderVertex );\\r\\n}\\r\\n///@FILE:../src/helpers/path.js\\r\\n///@INFO: UNCOMMON\\r\\n/** Path\\r\\n* Used to store splines\\r\\n* types defined in defines.js: LINEAR, HERMITE, BEZIER\\r\\n* @class Path\\r\\n*/\\r\\nfunction Path()\\r\\n{\\r\\n\\tthis.points = [];\\r\\n\\tthis.closed = false;\\r\\n\\tthis.type = LS.LINEAR;\\r\\n}\\r\\n\\r\\nPath.prototype.clear = function()\\r\\n{\\r\\n\\tthis.points.length = 0;\\r\\n}\\r\\n\\r\\n//points stored are cloned\\r\\nPath.prototype.addPoint = function(p)\\r\\n{\\r\\n\\tvar pos = vec3.create();\\r\\n\\tpos[0] = p[0];\\r\\n\\tpos[1] = p[1];\\r\\n\\tif(p.length > 2)\\r\\n\\t\\tpos[2] = p[2];\\r\\n\\tthis.points.push( pos );\\r\\n}\\r\\n\\r\\nPath.prototype.getSegments = function()\\r\\n{\\r\\n\\tvar l = this.points.length;\\r\\n\\r\\n\\tswitch(this.type)\\r\\n\\t{\\r\\n\\t\\tcase LS.LINEAR: \\r\\n\\t\\t\\tif(l < 2) \\r\\n\\t\\t\\t\\treturn 0;\\r\\n\\t\\t\\treturn l - 1 + (this.closed ? 1 : 0); \\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase LS.HERMITE:\\r\\n\\t\\t\\tif(l < 2) \\r\\n\\t\\t\\t\\treturn 0;\\r\\n\\t\\t\\treturn l - 1 + (this.closed ? 1 : 0); \\r\\n\\t\\tcase LS.BEZIER:\\r\\n\\t\\t\\tif(l < 3) \\r\\n\\t\\t\\t\\treturn 0;\\r\\n\\t\\t\\treturn (((l-1)/3)|0) + (this.closed ? 1 : 0);\\r\\n\\t\\t\\tbreak;\\r\\n\\t}\\r\\n\\treturn 0;\\r\\n}\\r\\n\\r\\nPath.prototype.movePoint = function( index, pos, preserve_tangents )\\r\\n{\\r\\n\\tif(index < 0 && index >= this.points.length)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar p = this.points[ index ];\\r\\n\\tvar total_diff = vec3.sub( vec3.create(), pos, p );\\r\\n\\tvec3.copy(p, pos);\\r\\n\\r\\n\\tif( !preserve_tangents || this.type != LS.BEZIER )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(index % 3 == 2 && this.points.length > index + 2 )\\r\\n\\t{\\r\\n\\t\\tvar middle_pos = this.points[index + 1];\\r\\n\\t\\tvar next_pos = this.points[index + 2];\\r\\n\\t\\tvar diff = vec3.sub( vec3.create(), middle_pos, p );\\r\\n\\t\\tvec3.add( next_pos, middle_pos, diff );\\r\\n\\t}\\r\\n\\telse if(index % 3 == 1 && index > 3 )\\r\\n\\t{\\r\\n\\t\\tvar middle_pos = this.points[index - 1];\\r\\n\\t\\tvar prev_pos = this.points[index - 2];\\r\\n\\t\\tvar diff = vec3.sub( vec3.create(), middle_pos, p );\\r\\n\\t\\tvec3.add( prev_pos, middle_pos, diff );\\r\\n\\t}\\r\\n\\telse if( index % 3 == 0 )\\r\\n\\t{\\r\\n\\t\\tif( index > 1 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar prev_pos = this.points[index - 1];\\r\\n\\t\\t\\tvec3.add( prev_pos, prev_pos, total_diff );\\r\\n\\t\\t}\\r\\n\\t\\tif( index < this.points.length - 1 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar next_pos = this.points[index + 1];\\r\\n\\t\\t\\tvec3.add( next_pos, next_pos, total_diff );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPath.prototype.computePoint = function(f, out)\\r\\n{\\r\\n\\tswitch(this.type)\\r\\n\\t{\\r\\n\\t\\tcase LS.HERMITE: return this.getHermitePoint(f,out); break;\\r\\n\\t\\tcase LS.BEZIER: return this.getBezierPoint(f,out); break;\\r\\n\\t\\tcase LS.LINEAR: \\r\\n\\t\\tdefault:\\r\\n\\t\\t\\treturn this.getLinearPoint(f,out);\\r\\n\\t\\t\\tbreak;\\r\\n\\t}\\r\\n\\t//throw(\\\"Impossible path type\\\");\\r\\n}\\r\\n\\r\\n\\r\\nPath.prototype.getLinearPoint = function(f, out)\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tvar num = this.points.length;\\r\\n\\tvar l = num;\\r\\n\\tif(l < 2)\\r\\n\\t\\treturn out;\\r\\n\\r\\n\\tif(f <= 0)\\r\\n\\t\\treturn vec3.copy(out, this.points[0]);\\r\\n\\tif(f >= 1)\\r\\n\\t{\\r\\n\\t\\tif(this.closed)\\r\\n\\t\\t\\treturn vec3.copy(out, this.points[0]);\\r\\n\\t\\treturn vec3.copy(out, this.points[l-1]);\\r\\n\\t}\\r\\n\\r\\n\\tif( this.closed )\\r\\n\\t\\tl += 1;\\r\\n\\r\\n\\tvar v = ((l-1) * f);\\r\\n\\tvar i = v|0;\\r\\n\\tvar fract = v-i;\\r\\n\\tvar p = this.points[ i % num ];\\r\\n\\tvar p2 = this.points[ (i+1) % num ];\\r\\n\\treturn vec3.lerp(out, p, p2, fract);\\r\\n}\\r\\n\\r\\nPath.temp_vec3a = vec3.create();\\r\\nPath.temp_vec3b = vec3.create();\\r\\nPath.temp_vec3c = vec3.create();\\r\\n\\r\\nPath.prototype.getBezierPoint = function(f, out)\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tvar l = this.points.length;\\r\\n\\tif(l < 4)\\r\\n\\t\\treturn out;\\r\\n\\tl = (((l-1)/3)|0) * 3 + 1; //take only useful points\\r\\n\\r\\n\\tif(f <= 0)\\r\\n\\t\\treturn vec3.copy(out, this.points[0]);\\r\\n\\tif(f >= 1)\\r\\n\\t\\treturn vec3.copy(out, this.points[ this.closed ? 0 : l-1 ]);\\r\\n\\r\\n\\tvar num = (l-1)/3 + (this.closed ? 1 : 0); //num segment\\r\\n\\tvar v = num*f; //id.weight\\r\\n\\tvar i = (v|0); //id\\r\\n\\tvar t = v-i;//weight\\r\\n\\r\\n\\tvar i1 = (i*3);\\r\\n\\tvar i2 = (i*3+1);\\r\\n\\tvar i3 = (i*3+2);\\r\\n\\tvar i4 = (i*3+3);\\r\\n\\r\\n\\tvar p,p1,p2,p3;\\r\\n\\r\\n\\tif( this.closed && i == num-1 )\\r\\n\\t{\\r\\n\\t\\tp = this.points[l-1];\\r\\n\\t\\tp3 = this.points[0];\\r\\n\\t\\tvar diff = vec3.sub( Path.temp_vec3c, p, this.points[l-2] );\\r\\n\\t\\tp1 = vec3.add( Path.temp_vec3a, p, diff );\\r\\n\\t\\tdiff = vec3.sub( Path.temp_vec3c, p3, this.points[1] );\\r\\n\\t\\tp2 = vec3.add( Path.temp_vec3b, p3, diff );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tp = this.points[ i1 ];\\r\\n\\t\\tp1 = this.points[ i2 ];\\r\\n\\t\\tp2 = this.points[ i3 ];\\r\\n\\t\\tp3 = this.points[ i4 ];\\r\\n\\t}\\r\\n\\r\\n\\tvar b1 = (1-t)*(1-t)*(1-t);\\r\\n\\tvar b2 = 3*t*(1-t)*(1-t);\\r\\n\\tvar b3 = 3*t*t*(1-t);\\r\\n\\tvar b4 = t*t*t;\\r\\n\\r\\n\\tout[0] = p[0] * b1 + p1[0] * b2 + p2[0] * b3 + p3[0] * b4;\\r\\n\\tout[1] = p[1] * b1 + p1[1] * b2 + p2[1] * b3 + p3[1] * b4;\\r\\n\\tout[2] = p[2] * b1 + p1[2] * b2 + p2[2] * b3 + p3[2] * b4;\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nPath.prototype.getHermitePoint = function(f, out)\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tvar l = this.points.length;\\r\\n\\tif(l < 2)\\r\\n\\t\\treturn out;\\r\\n\\tif(f <= 0)\\r\\n\\t\\treturn vec3.copy(out, this.points[0]);\\r\\n\\tif(f >= 1)\\r\\n\\t\\treturn vec3.copy(out, this.points[ this.closed ? 0 : l-1]);\\r\\n\\r\\n\\tvar num = (l-1) + (this.closed ? 1 : 0); //num segments\\r\\n\\tvar v = num*f; //id.weight\\r\\n\\tvar i = (v|0); //id\\r\\n\\tvar t = v-i;//weight\\r\\n\\r\\n\\tvar pre_p0 = this.points[i - 1];\\r\\n\\tvar p0 = this.points[ i ];\\r\\n\\tvar p1 = this.points[ i+1 ];\\r\\n\\tvar post_p1 = this.points[ i+2 ];\\r\\n\\r\\n\\tif(!pre_p0)\\r\\n\\t\\tpre_p0 = this.closed ? this.points[l - 1] : p0;\\r\\n\\tif(!p1)\\r\\n\\t\\tp1 = this.points[ (i+1) % l ];\\r\\n\\tif(!post_p1)\\r\\n\\t\\tpost_p1 = this.closed ? this.points[ (i+2) % l ] : p1;\\r\\n\\r\\n\\tAnimation.EvaluateHermiteSplineVector( p0, p1, pre_p0, post_p1, t, out );\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n\\r\\n/*\\r\\nPath.prototype.getCatmullPoint = function(f, out)\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tvar l = this.points.length;\\r\\n\\tif(l < 4)\\r\\n\\t\\treturn out;\\r\\n\\tl = (((l-1)/3)|0) * 3 + 1; //take only useful points\\r\\n\\tif(f <= 0)\\r\\n\\t\\treturn vec3.copy(out, this.points[0]);\\r\\n\\tif(f >= 1)\\r\\n\\t\\treturn vec3.copy(out, this.points[l-1]);\\r\\n\\r\\n\\tvar v = ((l-1)/3*f); \\r\\n\\tvar i = v|0;//spline number\\r\\n\\tvar fract = v-i;//weight\\r\\n\\tvar p = this.points[ i ];\\r\\n\\tvar p1 = this.points[ i+1 ];\\r\\n\\tvar p2 = this.points[ i+2 ];\\r\\n\\tvar p3 = this.points[ i+3 ];\\r\\n\\tvar w = fract;\\r\\n\\tvar w2 = w*w;\\r\\n\\tvar w3 = w2*w;\\r\\n\\tout[0] = Path.interpolate( p[0], p1[0], p2[0], p3[0], w,w2,w3 );\\r\\n\\tout[1] = Path.interpolate( p[1], p1[1], p2[1], p3[1], w,w2,w3 );\\r\\n\\tout[2] = Path.interpolate( p[2], p1[2], p2[2], p3[2], w,w2,w3 );\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n//catmull-rom\\r\\nPath.interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {\\r\\n\\tvar v0 = ( p2 - p0 ) * 0.5;\\r\\n\\tvar v1 = ( p3 - p1 ) * 0.5;\\r\\n\\treturn ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;\\r\\n};\\r\\n*/\\r\\n\\r\\nPath.prototype.samplePoints = function( n, out )\\r\\n{\\r\\n\\tif(n <= 0)\\r\\n\\t{\\r\\n\\t\\tvar segments = this.getSegments();\\r\\n\\t\\tif(this.type == LS.LINEAR)\\r\\n\\t\\t\\tn = segments + 1;\\r\\n\\t\\telse\\r\\n\\t\\t\\tn = segments * 20;\\r\\n\\t}\\r\\n\\r\\n\\tout = out || Array(n);\\r\\n\\tout.length = n;\\r\\n\\r\\n\\tfor(var i = 0; i < n; i++)\\r\\n\\t\\tout[i] = this.computePoint(i/(n-1));\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nPath.prototype.samplePointsTyped = function( n, out )\\r\\n{\\r\\n\\tif(out && out.length < (n * 3))\\r\\n\\t\\tn = Math.floor(out.length / 3);\\r\\n\\r\\n\\tif(n <= 0)\\r\\n\\t{\\r\\n\\t\\tvar segments = this.getSegments();\\r\\n\\t\\tif(this.type == LS.LINEAR)\\r\\n\\t\\t\\tn = segments + 1;\\r\\n\\t\\telse\\r\\n\\t\\t\\tn = segments * 20;\\r\\n\\t}\\r\\n\\r\\n\\tout = out || new Float32Array( n * 3 );\\r\\n\\tfor(var i = 0; i < n; i++)\\r\\n\\t\\tthis.computePoint(i/(n-1),out.subarray(i*3,i*3+3));\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n\\r\\nPath.prototype.serialize = function()\\r\\n{\\r\\n\\tvar o = {};\\r\\n\\tvar points = Array( this.points.length * 3 );\\r\\n\\tfor(var i = 0; i < this.points.length; i++)\\r\\n\\t{\\r\\n\\t\\tvar p = this.points[i];\\r\\n\\t\\tpoints[i*3] = p[0];\\r\\n\\t\\tpoints[i*3+1] = p[1];\\r\\n\\t\\tpoints[i*3+2] = p[2];\\r\\n\\t}\\r\\n\\r\\n\\to.points = points;\\r\\n\\to.type = this.type;\\r\\n\\to.closed = this.closed;\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\nPath.prototype.configure = function(o)\\r\\n{\\r\\n\\tthis.type = o.type;\\r\\n\\tthis.closed = o.closed;\\r\\n\\r\\n\\tif(o.points)\\r\\n\\t{\\r\\n\\t\\tthis.points.length = o.points.length / 3;\\r\\n\\t\\tvar points = o.points;\\r\\n\\t\\tfor(var i = 0; i < this.points.length; i++)\\r\\n\\t\\t\\tthis.points[i] = vec3.fromValues( points[i*3], points[i*3+1], points[i*3+2] );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\nLS.Path = Path;\\r\\n///@FILE:../src/helpers/FXstack.js\\r\\n///@INFO: UNCOMMON\\r\\n/** FXStack\\r\\n* Helps apply a stack of FXs to a texture with as fewer render calls as possible with low memory footprint\\r\\n* Used by CameraFX and FrameFX but also available for any other use\\r\\n* You can add new FX to the FX pool if you want.\\r\\n* @class FXStack\\r\\n*/\\r\\nfunction FXStack( o )\\r\\n{\\r\\n\\tthis.apply_fxaa = false;\\r\\n\\tthis.filter = true;\\r\\n\\tthis.fx = [];\\r\\n\\r\\n\\tthis._uniforms = { u_aspect: 1, u_viewport: vec2.create(), u_iviewport: vec2.create(), u_texture: 0, u_depth_texture: 1, u_random: vec2.create() };\\r\\n\\r\\n\\tthis._passes = null;\\r\\n\\tthis._must_update_passes = true;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nFXStack.available_fx = {\\r\\n\\t\\\"brightness_contrast\\\": {\\r\\n\\t\\tname: \\\"Brightness & Contrast\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\tbrightness: { name: \\\"u_brightness\\\", type: \\\"float\\\", value: 1, step: 0.01 },\\r\\n\\t\\t\\tcontrast: { name: \\\"u_contrast\\\", type: \\\"float\\\", value: 1, step: 0.01 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = (color.xyz * u_brightness@ - vec3(0.5)) * u_contrast@ + vec3(0.5);\\\"\\r\\n\\t},\\r\\n\\t\\\"hue_saturation\\\": {\\r\\n\\t\\tname: \\\"Hue & Saturation\\\",\\r\\n\\t\\tfunctions: [\\\"HSV\\\"],\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\thue: { name: \\\"u_hue\\\", type: \\\"float\\\", value: 0, step: 0.01 },\\r\\n\\t\\t\\tsaturation: { name: \\\"u_saturation\\\", type: \\\"float\\\", value: 1, step: 0.01 },\\r\\n\\t\\t\\tbrightness: { name: \\\"u_brightness\\\", type: \\\"float\\\", value: 0, step: 0.01 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = rgb2hsv(color.xyz); color.xz += vec2(u_hue@,u_brightness@); color.y *= u_saturation@; color.xyz = hsv2rgb(color.xyz);\\\"\\r\\n\\t},\\r\\n\\t\\\"invert\\\": {\\r\\n\\t\\tname: \\\"Invert color\\\",\\r\\n\\t\\tcode:\\\"color.xyz = vec3(1.0) - color.xyz;\\\"\\r\\n\\t},\\r\\n\\t\\\"threshold\\\": {\\r\\n\\t\\tname: \\\"Threshold\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\tthreshold: { name: \\\"u_threshold\\\", type: \\\"float\\\", value: 0.5, min: 0, max: 2, step: 0.01 },\\r\\n\\t\\t\\tthreshold_width: { name: \\\"u_threshold_width\\\", type: \\\"float\\\", value: 0.01, min: 0, max: 1, step: 0.001 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = vec3( smoothstep( u_threshold@ - u_threshold_width@ * 0.5, u_threshold@ + u_threshold_width@ * 0.5, length(color.xyz) ));\\\"\\r\\n\\t},\\r\\n\\t\\\"colorize\\\": {\\r\\n\\t\\tname: \\\"Colorize\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\tcolorize: { name: \\\"u_colorize\\\", type: \\\"color3\\\", value: [1,1,1] },\\r\\n\\t\\t\\tvibrance: { name: \\\"u_vibrance\\\", type: \\\"float\\\", value: 0.0, min: 0, max: 2, step: 0.01 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = color.xyz * (u_colorize@ + vec3(u_vibrance@ * 0.1)) * (1.0 + u_vibrance@);\\\"\\r\\n\\t},\\r\\n\\t\\\"color_add\\\": {\\r\\n\\t\\tname: \\\"Color add\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\tcolor_add: { name: \\\"u_coloradd\\\", type: \\\"color3\\\", value: [0.1,0.1,0.1] }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = color.xyz + u_coloradd@;\\\"\\r\\n\\t},\\r\\n\\t\\\"fog\\\":{\\r\\n\\t\\tname:\\\"fog\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\tfog_color: { name: \\\"u_fog_color\\\", type: \\\"color3\\\", value: [0.1,0.1,0.1] },\\r\\n\\t\\t\\tfog_start: { name: \\\"u_fog_start\\\", type: \\\"float\\\", value: 10 },\\r\\n\\t\\t\\tfog_density: { name: \\\"u_fog_density\\\", type: \\\"float\\\", precision: 0.00001, value: 0.001, step: 0.00001 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"float z_n@ = 2.0 * texture2D( u_depth_texture, v_coord).x - 1.0;\\\" +\\r\\n\\t\\t\\t\\\"float cam_dist@ = 2.0 * u_depth_range.x * u_depth_range.y / (u_depth_range.y + u_depth_range.x - z_n@ * (u_depth_range.y - u_depth_range.x));\\\" +\\r\\n\\t\\t\\t\\\"float fog_factor@ = 1. - 1.0 / exp(max(0.0,cam_dist@ - u_fog_start@) * u_fog_density@);\\\" +\\r\\n\\t\\t\\t\\\"color.xyz = mix( color.xyz, u_fog_color@, fog_factor@ );\\\"\\r\\n\\t},\\r\\n\\t\\\"vigneting\\\": {\\r\\n\\t\\tname: \\\"Vigneting\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\tradius: { name: \\\"u_radius\\\", type: \\\"float\\\", value: 1 },\\r\\n\\t\\t\\tintensity: { name: \\\"u_vigneting\\\", type: \\\"float\\\", value: 1, min: 0, max: 2, step: 0.01 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = mix( color.xyz * max( 1.0 - (dist_to_center * u_radius@ / 0.7071), 0.0), color.xyz, u_vigneting@);\\\"\\r\\n\\t},\\r\\n\\t\\\"aberration\\\": {\\r\\n\\t\\tname: \\\"Chromatic Aberration\\\",\\r\\n\\t\\tbreak_pass: true,\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\tdifraction: { name: \\\"u_difraction\\\", type: \\\"float\\\", value: 1 }\\r\\n\\t\\t},\\r\\n\\t\\tcode: \\\"color.x = texture2D(u_texture, uv - to_center * 0.001 * u_difraction@ ).x;\\\" + \\r\\n\\t\\t\\t\\\"color.z = texture2D(u_texture, uv + to_center * 0.001 * u_difraction@ ).z;\\\"\\r\\n\\t},\\r\\n\\t\\\"halftone\\\": {\\r\\n\\t\\tname: \\\"Halftone\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\t\\\"Halftone angle\\\": { name: \\\"u_halftone_angle\\\", type: \\\"float\\\", value: 0, step: 0.01 },\\r\\n\\t\\t\\t\\\"Halftone size\\\": { name: \\\"u_halftone_size\\\", type: \\\"float\\\", value: 1, step: 0.01 }\\r\\n\\t\\t},\\r\\n\\t\\tfunctions: [\\\"pattern\\\"],\\r\\n\\t\\tcode:\\\"color.x = ( (color.x * 10.0 - 5.0) + pattern( u_halftone_angle@, u_halftone_size@ ) );\\\" + \\r\\n\\t\\t\\t\\\"color.y = ( (color.y * 10.0 - 5.0) + pattern( u_halftone_angle@ + 0.167, u_halftone_size@ ) );\\\" + \\r\\n\\t\\t\\t\\\"color.z = ( (color.z * 10.0 - 5.0) + pattern( u_halftone_angle@ + 0.333, u_halftone_size@ ) );\\\"\\r\\n\\t},\\r\\n\\t\\\"halftoneBN\\\": {\\r\\n\\t\\tname: \\\"Halftone B/N\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\t\\\"Halftone angle\\\": { name: \\\"u_halftone_angle\\\", type: \\\"float\\\", value: 0, step: 0.01 },\\r\\n\\t\\t\\t\\\"Halftone size\\\": { name: \\\"u_halftone_size\\\", type: \\\"float\\\", value: 1, step: 0.01 }\\r\\n\\t\\t},\\r\\n\\t\\tfunctions: [\\\"pattern\\\"],\\r\\n\\t\\tcode:\\\"color.xyz = vec3( (length(color.xyz) * 10.0 - 5.0) + pattern( u_halftone_angle@, u_halftone_size@ ) );\\\"\\r\\n\\t},\\r\\n\\t\\\"lens\\\": {\\r\\n\\t\\tname: \\\"Lens Distortion\\\",\\r\\n\\t\\tbreak_pass: true,\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\tlens_k: { name: \\\"u_lens_k\\\", type: \\\"float\\\", value: -0.15 },\\r\\n\\t\\t\\tlens_kcube: { name: \\\"u_lens_kcube\\\", type: \\\"float\\\", value: 0.8 },\\r\\n\\t\\t\\tlens_scale: { name: \\\"u_lens_scale\\\", type: \\\"float\\\", value: 1 }\\r\\n\\t\\t},\\r\\n\\t\\tuv_code:\\\"float r2 = u_aspect * u_aspect * (uv.x-0.5) * (uv.x-0.5) + (uv.y-0.5) * (uv.y-0.5); float distort@ = 1. + r2 * (u_lens_k@ + u_lens_kcube@ * sqrt(r2)); uv = vec2( u_lens_scale@ * distort@ * (uv.x-0.5) + 0.5, u_lens_scale@ * distort@ * (uv.y-0.5) + 0.5 );\\\"\\r\\n\\t},\\r\\n\\t\\\"image\\\": {\\r\\n\\t\\tname: \\\"Image\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\timage_texture: { name: \\\"u_image_texture\\\", type: \\\"sampler2D\\\", widget: \\\"Texture\\\", value: \\\"\\\" },\\r\\n\\t\\t\\timage_alpha: { name: \\\"u_image_alpha\\\", type: \\\"float\\\", value: 1, step: 0.001 },\\r\\n\\t\\t\\timage_scale: { name: \\\"u_image_scale\\\", type: \\\"vec2\\\", value: [1,1], step: 0.001 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"vec4 image@ = texture2D( u_image_texture@, (uv - vec2(0.5)) * u_image_scale@ + vec2(0.5)); color.xyz = mix(color.xyz, image@.xyz, image@.a * u_image_alpha@ );\\\"\\r\\n\\t},\\r\\n\\t\\\"warp\\\": {\\r\\n\\t\\tname: \\\"Warp\\\",\\r\\n\\t\\tbreak_pass: true,\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\twarp_amp: { name: \\\"u_warp_amp\\\", type: \\\"float\\\", value: 0.01, step: 0.001 },\\r\\n\\t\\t\\twarp_offset: { name: \\\"u_warp_offset\\\", type: \\\"vec2\\\", value: [0,0], step: 0.001 },\\r\\n\\t\\t\\twarp_scale: { name: \\\"u_warp_scale\\\", type: \\\"vec2\\\", value: [1,1], step: 0.001 },\\r\\n\\t\\t\\twarp_texture: { name: \\\"u_warp_texture\\\", type: \\\"sampler2D\\\", widget: \\\"Texture\\\", value: \\\"\\\" }\\r\\n\\t\\t},\\r\\n\\t\\tuv_code:\\\"uv = uv + u_warp_amp@ * (texture2D( u_warp_texture@, uv * u_warp_scale@ + u_warp_offset@ ).xy - vec2(0.5));\\\"\\r\\n\\t},\\r\\n\\t\\\"LUT\\\": {\\r\\n\\t\\tname: \\\"LUT\\\",\\r\\n\\t\\tfunctions: [\\\"LUT\\\"],\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\tlut_intensity: { name: \\\"u_lut_intensity\\\", type: \\\"float\\\", value: 1, step: 0.01 },\\r\\n\\t\\t\\tlut_texture: { name: \\\"u_lut_texture\\\", type: \\\"sampler2D\\\", filter: \\\"nearest\\\", wrap: \\\"clamp\\\", widget: \\\"Texture\\\", value: \\\"\\\" }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = mix(color.xyz, LUT( color.xyz, u_lut_texture@ ), u_lut_intensity@);\\\"\\r\\n\\t},\\r\\n\\t\\\"pixelate\\\": {\\r\\n\\t\\tname: \\\"Pixelate\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\twidth: { name: \\\"u_width\\\", type: \\\"float\\\", value: 256, step: 1, min: 1 },\\r\\n\\t\\t\\theight: { name: \\\"u_height\\\", type: \\\"float\\\", value: 256, step: 1, min: 1 }\\r\\n\\t\\t},\\r\\n\\t\\tuv_code:\\\"uv = vec2( floor(uv.x * u_width@) / u_width@, floor(uv.y * u_height@) / u_height@ );\\\"\\r\\n\\t},\\r\\n\\t\\\"quantize\\\": {\\r\\n\\t\\tname: \\\"Quantize\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\tlevels: { name: \\\"u_levels\\\", type: \\\"float\\\", value: 8, step: 1, min: 1 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = floor(color.xyz * u_levels@) / u_levels@;\\\"\\r\\n\\t},\\r\\n\\t\\\"edges\\\": {\\r\\n\\t\\tname: \\\"Edges\\\",\\r\\n\\t\\tbreak_pass: true,\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\t\\\"Edges factor\\\": { name: \\\"u_edges_factor\\\", type: \\\"float\\\", value: 1 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"vec4 color@ = texture2D(u_texture, uv );\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color_up@ = texture2D(u_texture, uv + vec2(0., u_iviewport.y));\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color_right@ = texture2D(u_texture, uv + vec2(u_iviewport.x,0.));\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color_down@ = texture2D(u_texture, uv + vec2(0., -u_iviewport.y));\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color_left@ = texture2D(u_texture, uv + vec2(-u_iviewport.x,0.));\\\\n\\\\\\r\\n\\t\\t\\t\\tcolor = u_edges_factor@ * (abs(color@ - color_up@) + abs(color@ - color_down@) + abs(color@ - color_left@) + abs(color@ - color_right@));\\\"\\r\\n\\t},\\r\\n\\t\\\"depth\\\": {\\r\\n\\t\\tname: \\\"Depth\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\t\\\"near\\\": { name: \\\"u_near\\\", type: \\\"float\\\", value: 0.01, step: 0.1 },\\r\\n\\t\\t\\t\\\"far\\\": { name: \\\"u_far\\\", type: \\\"float\\\", value: 1000, step: 1 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = vec3( (2.0 * u_near@) / (u_far@ + u_near@ - texture2D( u_depth_texture, uv ).x * (u_far@ - u_near@)) );\\\"\\r\\n\\t},\\r\\n\\t\\\"logarithmic\\\": {\\r\\n\\t\\tname: \\\"Logarithmic\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\t\\\"Log. A Factor\\\": { name: \\\"u_logfactor_a\\\", type: \\\"float\\\", value: 2, step: 0.01 },\\r\\n\\t\\t\\t\\\"Log. B Factor\\\": { name: \\\"u_logfactor_b\\\", type: \\\"float\\\", value: 2, step: 0.01 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = log( color.xyz * u_logfactor_a@ ) * u_logfactor_b@;\\\"\\r\\n\\t},\\r\\n\\t\\\"ditherBN\\\": {\\r\\n\\t\\tname: \\\"dither B/N\\\",\\r\\n\\t\\tfunctions: [\\\"dither\\\"],\\r\\n\\t\\tcode:\\\"color.xyz = vec3( dither( color.x ) );\\\"\\r\\n\\t},\\r\\n\\t\\\"dither\\\": {\\r\\n\\t\\tname: \\\"Dither\\\",\\r\\n\\t\\tfunctions: [\\\"dither\\\"],\\r\\n\\t\\tcode:\\\"color.xyz = vec3( dither( color.x ), dither( color.y ), dither( color.z ) );\\\"\\r\\n\\t},\\r\\n\\t\\\"gamma\\\": {\\r\\n\\t\\tname: \\\"Gamma\\\",\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\t\\\"Gamma\\\": { name: \\\"u_gamma\\\", type: \\\"float\\\", value: 2.2, step: 0.01 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz = pow( color.xyz, vec3( 1.0 / u_gamma@) );\\\"\\r\\n\\t},\\r\\n\\t\\\"noiseBN\\\": {\\r\\n\\t\\tname: \\\"Noise B&N\\\",\\r\\n\\t\\tfunctions: [\\\"noise\\\"],\\r\\n\\t\\tuniforms: {\\r\\n\\t\\t\\t\\\"noise\\\": { name: \\\"u_noise\\\", type: \\\"float\\\", value: 0.1, step: 0.01 }\\r\\n\\t\\t},\\r\\n\\t\\tcode:\\\"color.xyz += u_noise@ * vec3( noise( (u_random + v_coord) * u_viewport) );\\\"\\r\\n\\t}\\r\\n\\t/*\\r\\n\\t\\\"blur\\\": {\\r\\n\\t\\t\\tname: \\\"Blur\\\",\\r\\n\\t\\t\\tbreak_pass: true,\\r\\n\\t\\t\\tuniforms: {\\r\\n\\t\\t\\t\\t\\\"blur_intensity\\\": { name: \\\"u_blur_intensity\\\", type: \\\"float\\\", value: 0.1, step: 0.01 }\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\tlocal_callback: FXStack.applyBlur\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\t*/\\r\\n\\t//median: https://github.com/patriciogonzalezvivo/flatLand/blob/master/bin/data/median.frag\\r\\n};\\r\\n\\r\\n//functions that could be used\\r\\nFXStack.available_functions = {\\r\\n\\tpattern: \\\"float pattern(float angle, float size) {\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat s = sin(angle * 3.1415), c = cos(angle * 3.1415);\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 tex = v_coord * u_viewport.xy;\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 point = vec2( c * tex.x - s * tex.y , s * tex.x + c * tex.y ) * size;\\\\n\\\\\\r\\n\\t\\t\\t\\treturn (sin(point.x) * sin(point.y)) * 4.0;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\",\\r\\n\\tdither: \\\"float dither(float v) {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 pixel = v_coord * u_viewport;\\\\n\\\\\\r\\n\\t\\t\\t\\tint i = int(floor(clamp(v,0.0,1.0) * 16.0 + 0.5));\\\\n\\\\\\r\\n\\t\\t\\t\\tif(i < 1)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tif(i >= 15)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat x = floor(pixel.x);\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat y = floor(pixel.y);\\\\n\\\\\\r\\n\\t\\t\\t\\tbool xmod4 = mod(x, 4.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool ymod4 = mod(y, 4.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool xmod2 = mod(x, 2.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool ymod2 = mod(y, 2.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool xmod4_2 = mod(x + 2.0, 4.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool ymod4_2 = mod(y + 2.0, 4.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool xmod2_1 = mod(x + 1.0, 2.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool ymod2_1 = mod(y + 1.0, 2.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool xmod4_1 = mod(x + 1.0, 4.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool ymod4_1 = mod(y + 1.0, 4.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool xmod4_3 = mod(x + 3.0, 4.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\tbool ymod4_3 = mod(y + 3.0, 4.0) == 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\t\\tif(i < 9)\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i >= 1 && xmod4 && ymod4 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i >= 2 && xmod4_2 && ymod4_2)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i >= 3 && xmod4_2 && ymod2 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i >= 4 && xmod2 && ymod2 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i >= 5 && xmod4_1 && ymod4_1 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i >= 6 && xmod4_3 && ymod4_3 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i >= 7 && xmod4_1 && ymod4_3 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i >= 8 && xmod4_3 && ymod4_1 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\telse\\\\n\\\\\\r\\n\\t\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i < 15 && xmod4_1 && ymod4 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i < 14 && xmod4_3 && ymod4_2)\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i < 13 && xmod4_3 && ymod2 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i < 12 && xmod2_1 && ymod2 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i < 11 && xmod4_2 && ymod4_1 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\tif(i < 10 && xmod4 && ymod4_3 )\\\\n\\\\\\r\\n\\t\\t\\t\\t\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\",\\r\\n\\tLUT: \\\"vec3 LUT(in vec3 color, in sampler2D textureB) {\\\\n\\\\\\r\\n\\t\\t lowp vec3 textureColor = clamp( color, vec3(0.0), vec3(1.0) );\\\\n\\\\\\r\\n\\t\\t mediump float blueColor = textureColor.b * 63.0;\\\\n\\\\\\r\\n\\t\\t mediump vec2 quad1;\\\\n\\\\\\r\\n\\t\\t quad1.y = floor(floor(blueColor) / 8.0);\\\\n\\\\\\r\\n\\t\\t quad1.x = floor(blueColor) - (quad1.y * 8.0);\\\\n\\\\\\r\\n\\t\\t mediump vec2 quad2;\\\\n\\\\\\r\\n\\t\\t quad2.y = floor(ceil(blueColor) / 8.0);\\\\n\\\\\\r\\n\\t\\t quad2.x = ceil(blueColor) - (quad2.y * 8.0);\\\\n\\\\\\r\\n\\t\\t highp vec2 texPos1;\\\\n\\\\\\r\\n\\t\\t texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\\\\n\\\\\\r\\n\\t\\t texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\\\\n\\\\\\r\\n\\t\\t highp vec2 texPos2;\\\\n\\\\\\r\\n\\t\\t texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\\\\n\\\\\\r\\n\\t\\t texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\\\\n\\\\\\r\\n\\t\\t lowp vec3 newColor1 = texture2D(textureB, texPos1).xyz;\\\\n\\\\\\r\\n\\t\\t lowp vec3 newColor2 = texture2D(textureB, texPos2).xyz;\\\\n\\\\\\r\\n\\t\\t lowp vec3 newColor = mix(newColor1, newColor2, fract(blueColor));\\\\n\\\\\\r\\n\\t\\t return newColor.rgb;\\\\n\\\\\\r\\n\\t }\\\",\\r\\n\\tnoise: \\\"\\\\n\\\\\\r\\n\\t\\tfloat hash(float n) { return fract(sin(n) * 1e4); }\\\\n\\\\\\r\\n\\t\\tfloat hash(vec2 p) { return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x)))); }\\\\n\\\\\\r\\n\\t\\tfloat noise(float x) {\\\\n\\\\\\r\\n\\t\\t\\tfloat i = floor(x);\\\\n\\\\\\r\\n\\t\\t\\tfloat f = fract(x);\\\\n\\\\\\r\\n\\t\\t\\tfloat u = f * f * (3.0 - 2.0 * f);\\\\n\\\\\\r\\n\\t\\t\\treturn mix(hash(i), hash(i + 1.0), u);\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\tfloat noise(vec2 x) {\\\\n\\\\\\r\\n\\t\\t\\tvec2 i = floor(x);\\\\n\\\\\\r\\n\\t\\t\\tvec2 f = fract(x);\\\\n\\\\\\r\\n\\t\\t\\tfloat a = hash(i);\\\\n\\\\\\r\\n\\t\\t\\tfloat b = hash(i + vec2(1.0, 0.0));\\\\n\\\\\\r\\n\\t\\t\\tfloat c = hash(i + vec2(0.0, 1.0));\\\\n\\\\\\r\\n\\t\\t\\tfloat d = hash(i + vec2(1.0, 1.0));\\\\n\\\\\\r\\n\\t\\t\\tvec2 u = f * f * (3.0 - 2.0 * f);\\\\n\\\\\\r\\n\\t\\t\\treturn mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\\",\\r\\n\\tHSV: \\\"vec3 rgb2hsv(vec3 c)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tvec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\\\\n\\\\\\r\\n\\t\\t\\tvec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\\\\n\\\\\\r\\n\\t\\t\\tvec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tfloat d = q.x - min(q.w, q.y);\\\\n\\\\\\r\\n\\t\\t\\tfloat e = 1.0e-10;\\\\n\\\\\\r\\n\\t\\t\\treturn vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvec3 hsv2rgb(vec3 c)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tvec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\\\\n\\\\\\r\\n\\t\\t\\tvec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\\\\n\\\\\\r\\n\\t\\t\\treturn c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\\\\n\\\\\\r\\n\\t\\t}\\\"\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the first component of this container that is of the same class\\r\\n* @method configure\\r\\n* @param {Object} o object with the configuration info from a previous serialization\\r\\n*/\\r\\nFXStack.prototype.configure = function(o)\\r\\n{\\r\\n\\tthis.apply_fxaa = !!o.apply_fxaa;\\r\\n\\tif(o.fx)\\r\\n\\t\\tthis.fx = o.fx.concat();\\r\\n\\tthis._must_update_passes = true;\\r\\n}\\r\\n\\r\\nFXStack.prototype.serialize = FXStack.prototype.toJSON = function()\\r\\n{\\r\\n\\treturn { \\r\\n\\t\\tapply_fxaa: this.apply_fxaa,\\r\\n\\t\\tfx: this.fx.concat()\\r\\n\\t};\\r\\n}\\r\\n\\r\\nFXStack.prototype.getResources = function(res)\\r\\n{\\r\\n\\tvar fxs = this.fx;\\r\\n\\tfor(var i = 0; i < fxs.length; i++)\\r\\n\\t{\\r\\n\\t\\tvar fx = fxs[i];\\r\\n\\t\\tvar fx_info = FXStack.available_fx[ fx.name ];\\r\\n\\t\\tif(!fx_info)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(!fx_info.uniforms)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tfor(var j in fx_info.uniforms)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar uniform = fx_info.uniforms[j];\\r\\n\\t\\t\\tif(uniform.type == \\\"sampler2D\\\" && fx[j])\\r\\n\\t\\t\\t\\tres[ fx[j] ] = GL.Texture;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nFXStack.prototype.onResourceRenamed = function(old_name, new_name, resource)\\r\\n{\\r\\n\\tvar fxs = this.fx;\\r\\n\\tfor(var i = 0; i < fxs.length; i++)\\r\\n\\t{\\r\\n\\t\\tvar fx = fxs[i];\\r\\n\\t\\tvar fx_info = FXStack.available_fx[ fx.name ];\\r\\n\\t\\tif(!fx_info)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(!fx_info.uniforms)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tfor(var j in fx_info.uniforms)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar uniform = fx_info.uniforms[j];\\r\\n\\t\\t\\tif(uniform.type == \\\"sampler2D\\\" && fx[j] == old_name )\\r\\n\\t\\t\\t\\tfx[j] = new_name;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n//attach a new FX to the FX Stack\\r\\nFXStack.prototype.addFX = function( name )\\r\\n{\\r\\n\\tif(!name)\\r\\n\\t\\treturn;\\r\\n\\tif( !FXStack.available_fx[ name ] )\\r\\n\\t{\\r\\n\\t\\tconsole.warn( \\\"FXStack not found: \\\" + name );\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\tthis.fx.push({ name: name });\\r\\n\\tthis._must_update_passes = true;\\r\\n}\\r\\n\\r\\n//returns the Nth FX in the FX Stack\\r\\nFXStack.prototype.getFX = function(index)\\r\\n{\\r\\n\\treturn this.fx[ index ];\\r\\n}\\r\\n\\r\\n//rearranges an FX\\r\\nFXStack.prototype.moveFX = function( fx, offset )\\r\\n{\\r\\n\\toffset = offset || -1;\\r\\n\\r\\n\\tvar index = this.fx.indexOf(fx);\\r\\n\\tif( index == -1 )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis.fx.splice(index,1);\\r\\n\\tindex += offset;\\r\\n\\r\\n\\r\\n\\tif(index >= 0 && index < this.fx.length)\\r\\n\\t\\tthis.fx.splice(index,0,fx);\\r\\n\\telse\\r\\n\\t\\tthis.fx.push(fx);\\r\\n\\tthis._must_update_passes = true;\\r\\n}\\r\\n\\r\\n//removes an FX from the FX stack\\r\\nFXStack.prototype.removeFX = function( fx )\\r\\n{\\r\\n\\tfor(var i = 0; i < this.fx.length; i++)\\r\\n\\t{\\r\\n\\t\\tif(this.fx[i] !== fx)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tthis.fx.splice(i,1);\\r\\n\\t\\tthis._must_update_passes = true;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//extract the number of passes to do according to the fx enabled\\r\\nFXStack.prototype.buildPasses = function()\\r\\n{\\r\\n\\tvar fxs = this.fx;\\r\\n\\r\\n\\tvar passes = [];\\r\\n\\tvar current_pass = {\\r\\n\\t\\tfxs:[],\\r\\n\\t\\tuniforms:{},\\r\\n\\t\\tshader:null,\\r\\n\\t\\tfirst_fx_id: 0\\r\\n\\t};\\r\\n\\r\\n\\tvar uv_code = \\\"\\\";\\r\\n\\tvar color_code = \\\"\\\";\\r\\n\\tvar uniforms_code = \\\"\\\";\\r\\n\\tvar included_functions = {};\\r\\n\\r\\n\\tvar is_first = true;\\r\\n\\r\\n\\tvar fx_id = 0;\\r\\n\\tfor(var i = 0; i < fxs.length; i++)\\r\\n\\t{\\r\\n\\t\\t//the FX settings\\r\\n\\t\\tvar fx = fxs[i];\\r\\n\\t\\tfx_id = i;\\r\\n\\r\\n\\t\\t//the FX definition\\r\\n\\t\\tvar fx_info = FXStack.available_fx[ fx.name ];\\r\\n\\t\\tif(!fx_info)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t//break this pass\\r\\n\\t\\tif( fx_info.break_pass && !is_first)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcurrent_pass.uv_code = uv_code;\\r\\n\\t\\t\\tcurrent_pass.color_code = color_code;\\r\\n\\t\\t\\tcurrent_pass.uniforms_code = uniforms_code;\\r\\n\\t\\t\\tcurrent_pass.included_functions = included_functions;\\r\\n\\t\\t\\tpasses.push(current_pass);\\r\\n\\t\\t\\tthis.buildPassShader( current_pass );\\r\\n\\r\\n\\t\\t\\tuv_code = \\\"\\\";\\r\\n\\t\\t\\tcolor_code = \\\"\\\";\\r\\n\\t\\t\\tuniforms_code = \\\"\\\";\\r\\n\\t\\t\\tincluded_functions = {};\\r\\n\\r\\n\\t\\t\\tcurrent_pass = {\\r\\n\\t\\t\\t\\tfxs:[],\\r\\n\\t\\t\\t\\tuniforms:{},\\r\\n\\t\\t\\t\\tfirst_fx_id: fx_id\\r\\n\\t\\t\\t};\\r\\n\\t\\t\\tis_first = true;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tis_first = false;\\r\\n\\r\\n\\t\\tif(fx_info.functions)\\r\\n\\t\\t\\tfor(var z in fx_info.functions)\\r\\n\\t\\t\\t\\tincluded_functions[ fx_info.functions[z] ] = true;\\r\\n\\t\\tif( fx_info.code )\\r\\n\\t\\t\\tcolor_code += fx_info.code.split(\\\"@\\\").join( fx_id ) + \\\";\\\\n\\\";\\r\\n\\t\\tif( fx_info.uv_code )\\r\\n\\t\\t\\tuv_code += fx_info.uv_code.split(\\\"@\\\").join( fx_id ) + \\\";\\\\n\\\";\\r\\n\\r\\n\\t\\tif(fx_info.uniforms)\\r\\n\\t\\t\\tfor(var j in fx_info.uniforms)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar uniform = fx_info.uniforms[j];\\r\\n\\t\\t\\t\\tvar varname = uniform.name + fx_id;\\r\\n\\t\\t\\t\\tuniforms_code += \\\"uniform \\\" + uniform.type + \\\" \\\" + varname + \\\";\\\\n\\\";\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\tcurrent_pass.fxs.push( fx );\\r\\n\\t}\\r\\n\\r\\n\\tif(!is_first)\\r\\n\\t{\\r\\n\\t\\tcurrent_pass.uv_code = uv_code;\\r\\n\\t\\tcurrent_pass.color_code = color_code;\\r\\n\\t\\tcurrent_pass.included_functions = included_functions;\\r\\n\\t\\tpasses.push( current_pass );\\r\\n\\t\\tthis.buildPassShader( current_pass );\\r\\n\\t}\\r\\n\\r\\n\\tthis._passes = passes;\\r\\n}\\r\\n\\r\\nFXStack.prototype.buildPassShader = function( pass )\\r\\n{\\r\\n\\tvar functions_code = \\\"\\\";\\r\\n\\tfor(var i in pass.included_functions)\\r\\n\\t{\\r\\n\\t\\tvar func = FXStack.available_functions[ i ];\\r\\n\\t\\tif(!func)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"FXStack: Function not found: \\\" + i);\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\t\\tfunctions_code += func + \\\"\\\\n\\\";\\r\\n\\t}\\r\\n\\r\\n\\tvar fullcode = \\\"\\\\n\\\\\\r\\n\\t\\t#extension GL_OES_standard_derivatives : enable\\\\n\\\\\\r\\n\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t#define color3 vec3\\\\n\\\\\\r\\n\\t\\t#define color4 vec4\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_depth_texture;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_iviewport;\\\\n\\\\\\r\\n\\t\\tuniform float u_aspect;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_depth_range;\\\\n\\\\\\r\\n\\t\\tuniform vec2 u_random;\\\\n\\\\\\r\\n\\t\\tvec2 uv;\\\\n\\\\\\r\\n\\t\\t\\\" + pass.uniforms_code + \\\"\\\\n\\\\\\r\\n\\t\\t\\\" + functions_code + \\\"\\\\n\\\\\\r\\n\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\tuv = v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvec2 to_center = vec2(0.5) - uv;\\\\n\\\\\\r\\n\\t\\t\\tfloat dist_to_center = length(to_center);\\\\n\\\\\\r\\n\\t\\t\\t\\\" + pass.uv_code + \\\"\\\\n\\\\\\r\\n\\t\\t\\tvec4 color = texture2D(u_texture, uv);\\\\n\\\\\\r\\n\\t\\t\\tfloat temp = 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\\" + pass.color_code + \\\"\\\\n\\\\\\r\\n\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\";\\r\\n\\r\\n\\tthis._must_update_passes = false;\\r\\n\\tpass.shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, fullcode );\\r\\n\\treturn pass.shader;\\r\\n}\\r\\n\\r\\n\\r\\nFXStack.prototype.applyFX = function( input_texture, output_texture, options )\\r\\n{\\r\\n\\tvar color_texture = input_texture;\\r\\n\\tvar depth_texture = options.depth_texture;\\r\\n\\r\\n\\tvar global_uniforms = this._uniforms;\\r\\n\\tglobal_uniforms.u_viewport[0] = color_texture.width;\\r\\n\\tglobal_uniforms.u_viewport[1] = color_texture.height;\\r\\n\\tglobal_uniforms.u_iviewport[0] = 1 / color_texture.width;\\r\\n\\tglobal_uniforms.u_iviewport[1] = 1 / color_texture.height;\\r\\n\\tglobal_uniforms.u_aspect = color_texture.width / color_texture.height;\\r\\n\\tglobal_uniforms.u_random[0] = Math.random();\\r\\n\\tglobal_uniforms.u_random[1] = Math.random();\\r\\n\\r\\n\\tif(!this._passes || this._must_update_passes )\\r\\n\\t\\tthis.buildPasses();\\r\\n\\r\\n\\tif(!this._passes.length)\\r\\n\\t{\\r\\n\\t\\tif(output_texture)\\r\\n\\t\\t\\tinput_texture.copyTo( output_texture );\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar fxaa_shader = GL.Shader.getFXAAShader();\\r\\n\\t\\t\\tfxaa_shader.setup();\\r\\n\\t\\t\\tinput_texture.toViewport( this.apply_fxaa ? fxaa_shader : null );\\r\\n\\t\\t}\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar w = output_texture ? output_texture.width : input_texture.width;\\r\\n\\tvar h = output_texture ? output_texture.height : input_texture.height;\\r\\n\\r\\n\\tvar origin_texture = GL.Texture.getTemporary( w, h, { type: input_texture.type, format: input_texture.format } );\\r\\n\\tvar target_texture = GL.Texture.getTemporary( w, h, { type: input_texture.type, format: input_texture.format } );\\r\\n\\r\\n\\tinput_texture.copyTo( origin_texture );\\r\\n\\r\\n\\tvar fx_id = 0;\\r\\n\\tfor(var i = 0; i < this._passes.length; i++)\\r\\n\\t{\\r\\n\\t\\tvar pass = this._passes[i];\\r\\n\\t\\tvar texture_slot = 2;\\r\\n\\t\\tvar uniforms = pass.uniforms;\\r\\n\\r\\n\\t\\t//gather uniform values\\r\\n\\t\\tfor(var j = 0; j < pass.fxs.length; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar fx = pass.fxs[j];\\r\\n\\t\\t\\tfx_id = pass.first_fx_id + j;\\r\\n\\r\\n\\t\\t\\t//the FX definition\\r\\n\\t\\t\\tvar fx_info = FXStack.available_fx[ fx.name ];\\r\\n\\t\\t\\tif(!fx_info)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(!fx_info.uniforms)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tfor(var k in fx_info.uniforms)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar uniform = fx_info.uniforms[k];\\r\\n\\t\\t\\t\\tvar varname = uniform.name + fx_id;\\r\\n\\t\\t\\t\\tif(uniform.type == \\\"sampler2D\\\")\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tuniforms[ varname ] = texture_slot;\\r\\n\\t\\t\\t\\t\\tvar tex = this.getTexture( fx[k] );\\r\\n\\t\\t\\t\\t\\tif(tex)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\ttex.bind( texture_slot );\\r\\n\\t\\t\\t\\t\\t\\tif(uniform.filter == \\\"nearest\\\")\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tgl.texParameteri( tex.texture_type, gl.TEXTURE_MAG_FILTER, gl.NEAREST );\\r\\n\\t\\t\\t\\t\\t\\t\\tgl.texParameteri( tex.texture_type, gl.TEXTURE_MIN_FILTER, gl.NEAREST );\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tif(uniform.wrap == \\\"clamp\\\")\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tgl.texParameteri( tex.texture_type, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );\\r\\n\\t\\t\\t\\t\\t\\t\\tgl.texParameteri( tex.texture_type, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t//bind something to avoid problems\\r\\n\\t\\t\\t\\t\\t\\ttex = LS.Renderer._missing_texture;\\r\\n\\t\\t\\t\\t\\t\\tif(tex)\\r\\n\\t\\t\\t\\t\\t\\t\\ttex.bind( texture_slot );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\ttexture_slot++;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tuniforms[ varname ] = fx[j] !== undefined ? fx[j] : uniform.value;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//apply pass\\r\\n\\t\\tvar shader = pass.shader;\\r\\n\\t\\t//error compiling shader\\r\\n\\t\\tif(!shader)\\r\\n\\t\\t{\\r\\n\\t\\t\\tinput_texture.toViewport(); //what about output_texture?\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//set the depth texture for some FXs like fog or depth\\r\\n\\t\\tif(depth_texture && shader.hasUniform(\\\"u_depth_texture\\\"))\\r\\n\\t\\t{\\r\\n\\t\\t\\tdepth_texture.bind(1);\\r\\n\\t\\t\\tif(depth_texture.near_far_planes)\\r\\n\\t\\t\\t\\tuniforms.u_depth_range = depth_texture.near_far_planes;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//apply FX and accumulate in secondary texture ***************\\r\\n\\t\\tshader.uniforms( global_uniforms );\\r\\n\\t\\torigin_texture.copyTo( target_texture, shader, uniforms );\\r\\n\\r\\n\\t\\t//swap\\r\\n\\t\\tvar tmp = origin_texture;\\r\\n\\t\\torigin_texture = target_texture;\\r\\n\\t\\ttarget_texture = tmp;\\r\\n\\t}\\r\\n\\r\\n\\t//to the screen or the output_texture\\r\\n\\tvar final_texture = target_texture;\\r\\n\\tfinal_texture.setParameter( gl.TEXTURE_MAG_FILTER, this.filter ? gl.LINEAR : gl.NEAREST );\\r\\n\\tfinal_texture.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR );\\r\\n\\r\\n\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\tgl.disable( gl.BLEND );\\r\\n\\tgl.disable( gl.CULL_FACE );\\r\\n\\r\\n\\t//to screen\\r\\n\\tif( this.apply_fxaa )\\r\\n\\t{\\r\\n\\t\\tvar fx_aa_shader = GL.Shader.getFXAAShader();\\r\\n\\t\\tfx_aa_shader.setup();\\r\\n\\t\\tif(!output_texture)\\r\\n\\t\\t\\tfinal_texture.toViewport( fx_aa_shader );\\r\\n\\t\\telse\\r\\n\\t\\t\\tfinal_texture.copyTo( output_texture, fx_aa_shader );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tif(!output_texture)\\r\\n\\t\\t\\tfinal_texture.toViewport();\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tshader.uniforms( uniforms );\\r\\n\\t\\t\\tfinal_texture.copyTo( output_texture, shader );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//release textures back to the pool\\r\\n\\tGL.Texture.releaseTemporary( origin_texture );\\r\\n\\tGL.Texture.releaseTemporary( target_texture );\\r\\n}\\r\\n\\r\\n\\r\\n//executes the FX stack in the input texture and outputs the result in the output texture (or the screen)\\r\\nFXStack.prototype.applyFX = function( input_texture, output_texture, options )\\r\\n{\\r\\n\\tvar color_texture = input_texture;\\r\\n\\tvar depth_texture = options.depth_texture;\\r\\n\\r\\n\\tvar fxs = this.fx;\\r\\n\\r\\n\\tvar update_shader = this._must_update_passes;\\r\\n\\tthis._must_update_passes = false;\\r\\n\\r\\n\\tvar uniforms = this._uniforms;\\r\\n\\tuniforms.u_viewport[0] = color_texture.width;\\r\\n\\tuniforms.u_viewport[1] = color_texture.height;\\r\\n\\tuniforms.u_iviewport[0] = 1 / color_texture.width;\\r\\n\\tuniforms.u_iviewport[1] = 1 / color_texture.height;\\r\\n\\tuniforms.u_aspect = color_texture.width / color_texture.height;\\r\\n\\tuniforms.u_random[0] = Math.random();\\r\\n\\tuniforms.u_random[1] = Math.random();\\r\\n\\r\\n\\tvar uv_code = \\\"\\\";\\r\\n\\tvar color_code = \\\"\\\";\\r\\n\\tvar included_functions = {};\\r\\n\\tvar uniforms_code = \\\"\\\";\\r\\n\\tvar texture_slot = 2;\\r\\n\\r\\n\\tvar fx_id = 0;\\r\\n\\tfor(var i = 0; i < fxs.length; i++)\\r\\n\\t{\\r\\n\\t\\t//the FX settings\\r\\n\\t\\tvar fx = fxs[i];\\r\\n\\t\\tfx_id = i;\\r\\n\\r\\n\\t\\t//the FX definition\\r\\n\\t\\tvar fx_info = FXStack.available_fx[ fx.name ];\\r\\n\\t\\tif(!fx_info)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif(update_shader)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(fx_info.functions)\\r\\n\\t\\t\\t\\tfor(var z in fx_info.functions)\\r\\n\\t\\t\\t\\t\\tincluded_functions[ fx_info.functions[z] ] = true;\\r\\n\\t\\t\\tif( fx_info.code )\\r\\n\\t\\t\\t\\tcolor_code += fx_info.code.split(\\\"@\\\").join( fx_id ) + \\\";\\\\n\\\";\\r\\n\\t\\t\\tif( fx_info.uv_code )\\r\\n\\t\\t\\t\\tuv_code += fx_info.uv_code.split(\\\"@\\\").join( fx_id ) + \\\";\\\\n\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(fx_info.uniforms)\\r\\n\\t\\t\\tfor(var j in fx_info.uniforms)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar uniform = fx_info.uniforms[j];\\r\\n\\t\\t\\t\\tvar varname = uniform.name + fx_id;\\r\\n\\t\\t\\t\\tif(update_shader)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tuniforms_code += \\\"uniform \\\" + uniform.type + \\\" \\\" + varname + \\\";\\\\n\\\";\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif(uniform.type == \\\"sampler2D\\\")\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tuniforms[ varname ] = texture_slot;\\r\\n\\t\\t\\t\\t\\tvar tex = this.getTexture( fx[j] );\\r\\n\\t\\t\\t\\t\\tif(tex)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\ttex.bind( texture_slot );\\r\\n\\t\\t\\t\\t\\t\\tif(uniform.filter == \\\"nearest\\\")\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tgl.texParameteri( tex.texture_type, gl.TEXTURE_MAG_FILTER, gl.NEAREST );\\r\\n\\t\\t\\t\\t\\t\\t\\tgl.texParameteri( tex.texture_type, gl.TEXTURE_MIN_FILTER, gl.NEAREST );\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tif(uniform.wrap == \\\"clamp\\\")\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tgl.texParameteri( tex.texture_type, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );\\r\\n\\t\\t\\t\\t\\t\\t\\tgl.texParameteri( tex.texture_type, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t//bind something to avoid problems\\r\\n\\t\\t\\t\\t\\t\\ttex = LS.Renderer._missing_texture;\\r\\n\\t\\t\\t\\t\\t\\tif(tex)\\r\\n\\t\\t\\t\\t\\t\\t\\ttex.bind( texture_slot );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\ttexture_slot++;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tuniforms[ varname ] = fx[j] !== undefined ? fx[j] : uniform.value;\\r\\n\\t\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\tvar shader = null;\\r\\n\\tif(update_shader)\\r\\n\\t{\\r\\n\\t\\tvar functions_code = \\\"\\\";\\r\\n\\t\\tfor(var i in included_functions)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar func = FXStack.available_functions[ i ];\\r\\n\\t\\t\\tif(!func)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.error(\\\"FXStack: Function not found: \\\" + i);\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tfunctions_code += func + \\\"\\\\n\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar fullcode = \\\"\\\\n\\\\\\r\\n\\t\\t\\t#extension GL_OES_standard_derivatives : enable\\\\n\\\\\\r\\n\\t\\t\\tprecision highp float;\\\\n\\\\\\r\\n\\t\\t\\t#define color3 vec3\\\\n\\\\\\r\\n\\t\\t\\t#define color4 vec4\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_depth_texture;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_viewport;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_iviewport;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_aspect;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_depth_range;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec2 u_random;\\\\n\\\\\\r\\n\\t\\t\\tvec2 uv;\\\\n\\\\\\r\\n\\t\\t\\t\\\" + uniforms_code + \\\"\\\\n\\\\\\r\\n\\t\\t\\t\\\" + functions_code + \\\"\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tuv = v_coord;\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 to_center = vec2(0.5) - uv;\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat dist_to_center = length(to_center);\\\\n\\\\\\r\\n\\t\\t\\t\\t\\\" + uv_code + \\\"\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 color = texture2D(u_texture, uv);\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat temp = 0.0;\\\\n\\\\\\r\\n\\t\\t\\t\\t\\\" + color_code + \\\"\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n\\t\\tthis._last_shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, fullcode );\\r\\n\\t}\\r\\n\\r\\n\\tshader = this._last_shader;\\r\\n\\r\\n\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\tgl.disable( gl.BLEND );\\r\\n\\tgl.disable( gl.CULL_FACE );\\r\\n\\r\\n\\t//error compiling shader\\r\\n\\tif(!shader)\\r\\n\\t{\\r\\n\\t\\tinput_texture.toViewport();\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//set the depth texture for some FXs like fog or depth\\r\\n\\tif(shader.hasUniform(\\\"u_depth_texture\\\") && depth_texture )\\r\\n\\t{\\r\\n\\t\\tdepth_texture.bind(1);\\r\\n\\t\\tif(depth_texture.near_far_planes)\\r\\n\\t\\t\\tuniforms.u_depth_range = depth_texture.near_far_planes;\\r\\n\\t}\\r\\n\\r\\n\\tcolor_texture.setParameter( gl.TEXTURE_MAG_FILTER, this.filter ? gl.LINEAR : gl.NEAREST );\\r\\n\\tcolor_texture.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR );\\r\\n\\r\\n\\tif( this.apply_fxaa )\\r\\n\\t{\\r\\n\\t\\tif(!this.temp_tex || this.temp_tex.width != gl.viewport_data[2] || this.temp_tex.height != gl.viewport_data[3])\\r\\n\\t\\t\\tthis.temp_tex = new GL.Texture(gl.viewport_data[2],gl.viewport_data[3]);\\r\\n\\t\\tthis.temp_tex.drawTo(function(){\\r\\n\\t\\t\\tcolor_texture.toViewport( shader, uniforms );\\r\\n\\t\\t});\\r\\n\\t\\tvar fx_aa_shader = GL.Shader.getFXAAShader();\\r\\n\\t\\tfx_aa_shader.setup();\\r\\n\\r\\n\\t\\tif(!output_texture)\\r\\n\\t\\t\\tthis.temp_tex.toViewport( fx_aa_shader );\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.temp_tex.copyTo( output_texture, fx_aa_shader );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tthis.temp_tex = null;\\r\\n\\t\\tif(!output_texture)\\r\\n\\t\\t\\tcolor_texture.toViewport( shader, uniforms );\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tshader.uniforms( uniforms );\\r\\n\\t\\t\\tcolor_texture.copyTo( output_texture, shader );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nFXStack.prototype.getTexture = function( name )\\r\\n{\\r\\n\\treturn LS.ResourcesManager.getTexture( name );\\r\\n}\\r\\n\\r\\nFXStack.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif(path.length < 2)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar fx_num = parseInt( path[0] );\\r\\n\\r\\n\\t//fx not active\\r\\n\\tif(fx_num >= this.fx.length)\\r\\n\\t\\treturn null;\\r\\n\\tvar fx = this.fx[ fx_num ];\\r\\n\\r\\n\\tvar fx_info = FXStack.available_fx[ fx.name ];\\r\\n\\tif(!fx_info)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar varname = path[1];\\r\\n\\tif(varname == \\\"name\\\")\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar uniform = fx_info.uniforms[ varname ];\\r\\n\\tif(!uniform)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar type = uniform.type;\\r\\n\\r\\n\\tif(type == \\\"float\\\")\\r\\n\\t\\ttype = \\\"number\\\";\\r\\n\\telse if(type == \\\"sampler2D\\\")\\r\\n\\t\\ttype = \\\"texture\\\";\\r\\n\\r\\n\\treturn {\\r\\n\\t\\ttarget: fx,\\r\\n\\t\\tname: varname,\\r\\n\\t\\tvalue: fx[ varname ],\\r\\n\\t\\ttype: uniform.type || \\\"number\\\"\\r\\n\\t};\\r\\n}\\r\\n\\r\\nFXStack.prototype.setPropertyValueFromPath = function( path, value, offset )\\r\\n{\\r\\n\\toffset = offset || 0;\\r\\n\\r\\n\\tif( path.length < (offset+1) )\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar fx_num = parseInt( path[offset] );\\r\\n\\tif(fx_num >= this.fx.length)\\r\\n\\t\\treturn null;\\r\\n\\tvar fx = this.fx[ fx_num ];\\r\\n\\tif(!fx)\\r\\n\\t\\treturn null;\\r\\n\\t\\r\\n\\tvar varname = path[offset+1];\\r\\n\\tif (fx[ varname ] === undefined )\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\t//to avoid incompatible types\\r\\n\\tif( fx[ varname ] !== undefined && value !== undefined && fx[ varname ].constructor === value.constructor )\\r\\n\\t\\tfx[ varname ] = value;\\r\\n}\\r\\n\\r\\n//static method to register new FX in the system\\r\\nFXStack.registerFX = function( name, fx_info )\\r\\n{\\r\\n\\tif( !fx_info.name )\\r\\n\\t\\tfx_info.name = name;\\r\\n\\tif( fx_info.code === undefined )\\r\\n\\t\\tthrow(\\\"FXStack must have a code\\\");\\r\\n\\tif( fx_info.uniforms && Object.keys( fx_info.uniforms ) && fx_info.code && fx_info.code.indexOf(\\\"@\\\") == -1 )\\r\\n\\t\\tconsole.warn(\\\"FXStack using uniforms must use the character '@' at the end of every use to avoid collisions with other variables with the same name.\\\");\\r\\n\\r\\n\\tFXStack.available_fx[ name ] = fx_info;\\r\\n}\\r\\n\\r\\n//for common functions shared among different FXs...\\r\\nFXStack.registerFunction = function( name, code )\\r\\n{\\r\\n\\tFXStack.available_functions[name] = code;\\r\\n}\\r\\n\\r\\nLS.FXStack = FXStack;\\r\\nLS.TextureFX = FXStack; //LEGACY\\r\\n///@FILE:../src/helpers/tween.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Allows to launch tweening \\r\\n*\\r\\n* @class Tween\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nLS.Tween = {\\r\\n\\tMAX_EASINGS: 256, //to avoid problems\\r\\n\\r\\n\\tEASE_IN_QUAD: 1,\\r\\n\\tEASE_OUT_QUAD: 2,\\r\\n\\tEASE_IN_OUT_QUAD: 3,\\r\\n\\tQUAD: 3,\\r\\n\\r\\n\\tEASE_IN_CUBIC: 4,\\r\\n\\tEASE_OUT_CUBIC: 5,\\r\\n\\tEASE_IN_OUT_CUBIC: 6,\\r\\n\\tCUBIC: 6,\\r\\n\\r\\n\\tEASE_IN_QUART: 7,\\r\\n\\tEASE_OUT_QUART: 8,\\r\\n\\tEASE_IN_OUT_QUART: 9,\\r\\n\\tQUART: 9,\\r\\n\\r\\n\\tEASE_IN_SINE: 10,\\r\\n\\tEASE_OUT_SINE: 11,\\r\\n\\tEASE_IN_OUT_SINE: 12,\\r\\n\\tSINE: 12,\\r\\n\\r\\n\\tEASE_IN_EXPO: 13,\\r\\n\\tEASE_OUT_EXPO: 14,\\r\\n\\tEASE_IN_OUT_EXPO: 15,\\r\\n\\tEXPO: 15,\\r\\n\\r\\n\\tEASE_IN_BACK: 16,\\r\\n\\tEASE_OUT_BACK: 17,\\r\\n\\tEASE_IN_OUT_BACK: 18,\\r\\n\\tBACK: 18,\\r\\n\\r\\n\\tcurrent_easings: [],\\r\\n\\t_alife: [], //temporal array\\r\\n\\r\\n\\treset: function()\\r\\n\\t{\\r\\n\\t\\tthis.current_easings = [];\\r\\n\\t\\tthis._alife = [];\\r\\n\\t},\\r\\n\\r\\n\\t/*\\r\\n\\tease: function()\\r\\n\\t{\\r\\n\\t\\tthis.easeProperty(\\r\\n\\t},\\r\\n\\t*/\\r\\n\\r\\n\\teaseProperty: function( object, property, target, time, easing_function, on_complete, on_progress )\\r\\n\\t{\\r\\n\\t\\tif( !object )\\r\\n\\t\\t\\tthrow(\\\"ease object cannot be null\\\");\\r\\n\\t\\tif( target === undefined )\\r\\n\\t\\t\\tthrow(\\\"target vaue must be defined\\\");\\r\\n\\t\\tif(object[property] === undefined)\\r\\n\\t\\t\\tthrow(\\\"property not found in object, must be initialized to a value\\\");\\r\\n\\r\\n\\t\\t//cancel previous in case we already have one for this property\\r\\n\\t\\tif(this.current_easings.length)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i = 0; i < this.current_easings.length; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar easing = this.current_easings[i];\\r\\n\\t\\t\\t\\tif( easing.object !== object || easing.property != property )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tthis.current_easings.splice(i,1); //remove old one\\r\\n\\t\\t\\t\\tbreak;\\t\\t\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\teasing_function = easing_function || this.EASE_IN_OUT_QUAD;\\r\\n\\r\\n\\t\\t//clone to avoid problems\\r\\n\\t\\tvar origin = null;\\r\\n\\t\\t\\r\\n\\t\\tif(property)\\r\\n\\t\\t\\torigin = LS.cloneObject( object[ property ] );\\r\\n\\t\\telse\\r\\n\\t\\t\\torigin = LS.cloneObject( object );\\r\\n\\t\\ttarget = LS.cloneObject( target );\\r\\n\\r\\n\\t\\t//precompute target value size\\r\\n\\t\\tvar size = 0;\\r\\n\\t\\tif(target.constructor === Number)\\r\\n\\t\\t\\tsize = -1;\\r\\n\\t\\telse if(target && target.length !== undefined)\\r\\n\\t\\t\\tsize = target.length;\\r\\n\\r\\n\\t\\tvar type = null;\\r\\n\\t\\tvar type_info = object.constructor[\\\"@\\\" + property];\\r\\n\\t\\tif( type_info )\\r\\n\\t\\t\\ttype = type_info.type;\\r\\n\\r\\n\\t\\tvar data = { \\r\\n\\t\\t\\tobject: object, \\r\\n\\t\\t\\tproperty: property, \\r\\n\\t\\t\\torigin: origin, \\r\\n\\t\\t\\ttarget: target, \\r\\n\\t\\t\\tcurrent: 0, \\r\\n\\t\\t\\ttime: time, \\r\\n\\t\\t\\teasing: easing_function, \\r\\n\\t\\t\\ton_complete: on_complete, \\r\\n\\t\\t\\ton_progress: on_progress, \\r\\n\\t\\t\\tsize: size, \\r\\n\\t\\t\\ttype: type,\\r\\n\\t\\t\\trunning: true\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tfor(var i = 0; i < this.current_easings.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.current_easings[i].object == object && this.current_easings[i].property == property )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.current_easings[i] = data; //replace old easing\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(this.current_easings.length >= this.MAX_EASINGS)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar easing = this.current_easings.shift();\\r\\n\\t\\t\\t//TODO: this could be improved applyting the target value right now\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.current_easings.push( data );\\r\\n\\t\\treturn data;\\r\\n\\t},\\r\\n\\r\\n\\teaseObject: function( object, target, time, easing_function, on_complete, on_progress )\\r\\n\\t{\\r\\n\\t\\tif( !object || !target )\\r\\n\\t\\t\\tthrow(\\\"ease object cannot be null\\\");\\r\\n\\r\\n\\t\\teasing_function = easing_function || this.EASE_IN_OUT_QUAD;\\r\\n\\r\\n\\t\\t//clone to avoid problems\\r\\n\\t\\tvar origin = LS.cloneObject( object );\\r\\n\\t\\ttarget = LS.cloneObject( target );\\r\\n\\r\\n\\t\\t//precompute size\\r\\n\\t\\tvar size = 0;\\r\\n\\t\\tif(target.length !== undefined)\\r\\n\\t\\t\\tsize = target.length;\\r\\n\\r\\n\\t\\tvar data = { object: object, origin: origin, target: target, current: 0, time: time, easing: easing_function, on_complete: on_complete, on_progress: on_progress, size: size };\\r\\n\\r\\n\\t\\tfor(var i = 0; i < this.current_easings.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this.current_easings[i].object == object )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.current_easings[i] = data; //replace old easing\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(this.current_easings.length >= this.MAX_EASINGS)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.current_easings.shift();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.current_easings.push( data );\\r\\n\\t\\treturn data;\\r\\n\\t},\\r\\n\\r\\n\\t//updates all the active tweens\\r\\n\\tupdate: function( dt )\\r\\n\\t{\\r\\n\\t\\tif( !this.current_easings.length )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar easings = this.current_easings;\\r\\n\\t\\tvar alive = this._alife;\\r\\n\\t\\talive.length = easings.length;\\r\\n\\t\\tvar pos = 0;\\r\\n\\r\\n\\t\\t//for every pending easing method\\r\\n\\t\\tfor(var i = 0, l = easings.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar item = easings[i];\\r\\n\\t\\t\\titem.current += dt;\\r\\n\\t\\t\\tvar t = 1;\\r\\n\\t\\t\\tif(item.current < item.time)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tt = item.current / item.time;\\r\\n\\t\\t\\t\\talive[ pos ] = item;\\r\\n\\t\\t\\t\\tpos += 1;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar f = this.getEaseFactor( t, item.easing );\\r\\n\\r\\n\\t\\t\\tvar result = null;\\r\\n\\r\\n\\t\\t\\tif(item.size)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(item.size == -1) //number\\r\\n\\t\\t\\t\\t\\titem.object[ item.property ] = item.target * f + item.origin * ( 1.0 - f );\\r\\n\\t\\t\\t\\telse //array\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar property = item.object[ item.property ];\\r\\n\\r\\n\\t\\t\\t\\t\\tif(item.type && item.type == \\\"quat\\\")\\r\\n\\t\\t\\t\\t\\t\\tquat.slerp( property, item.origin, item.target, f );\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t//regular linear interpolation\\r\\n\\t\\t\\t\\t\\t\\tfor(var j = 0; j < item.size; ++j)\\r\\n\\t\\t\\t\\t\\t\\t\\tproperty[j] = item.target[j] * f + item.origin[j] * ( 1.0 - f );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif(item.object.mustUpdate !== undefined)\\r\\n\\t\\t\\t\\t\\titem.object.mustUpdate = true;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(item.on_progress)\\r\\n\\t\\t\\t\\titem.on_progress( item );\\r\\n\\r\\n\\t\\t\\tif(t >= 1)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(item.on_complete)\\r\\n\\t\\t\\t\\t\\titem.on_complete( item );\\r\\n\\t\\t\\t\\titem.running = false;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\talive.length = pos; //trim\\r\\n\\r\\n\\t\\tthis.current_easings = alive;\\r\\n\\t\\tthis._alife = easings;\\r\\n\\t},\\r\\n\\r\\n\\tgetEaseFactor: function(t,type)\\r\\n\\t{\\r\\n\\t\\tif(t>1) \\r\\n\\t\\t\\tt = 1;\\r\\n\\t\\telse if(t < 0)\\r\\n\\t\\t\\tt = 0;\\r\\n\\t\\tvar s = 1.70158;\\r\\n\\t\\ttype = type || this.QUAD;\\r\\n\\t\\tswitch(type)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase this.EASE_IN_QUAD: return (t*t);\\r\\n\\t\\t\\tcase this.EASE_OUT_QUAD: return 1-(t*t);\\r\\n\\t\\t\\tcase this.EASE_IN_OUT_QUAD: { \\r\\n\\t\\t\\t\\tt *= 2;\\r\\n\\t\\t\\t\\tif( t < 1 ) return 0.5 * t * t;\\r\\n\\t\\t\\t\\tt -= 1;\\r\\n\\t\\t\\t\\treturn -0.5 * ((t)*(t-2) - 1);\\r\\n\\t\\t\\t};\\r\\n\\r\\n\\t\\t\\tcase this.EASE_IN_CUBIC: return t*t*t;\\r\\n\\t\\t\\tcase this.EASE_OUT_CUBIC: {\\r\\n\\t\\t\\t\\tt -= 1;\\r\\n\\t\\t\\t\\treturn t*t*t + 1;\\r\\n\\t\\t\\t};\\r\\n\\t\\t\\tcase this.EASE_IN_OUT_CUBIC: {\\r\\n\\t\\t\\t\\tt *= 2;\\r\\n\\t\\t\\t\\tif( t < 1 )\\r\\n\\t\\t\\t\\t\\treturn 0.5 * t*t*t;\\r\\n\\t\\t\\t\\tt -= 2;\\r\\n\\t\\t\\t\\treturn 0.5*(t*t*t + 2);\\r\\n\\t\\t\\t};\\r\\n\\r\\n\\t\\t\\tcase this.EASE_IN_QUART: return t*t*t*t;\\r\\n\\t\\t\\tcase this.EASE_OUT_QUART: {\\r\\n\\t\\t\\t\\tt -= 1;\\r\\n\\t\\t\\t\\treturn -(t*t*t*t - 1);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcase this.EASE_IN_OUT_QUART: {\\r\\n\\t\\t\\t\\tt *= 2;\\r\\n\\t\\t\\t\\tif( t < 1 ) return 0.5*t*t*t*t;\\r\\n\\t\\t\\t\\telse {\\r\\n\\t\\t\\t\\t\\tt -= 2;\\r\\n\\t\\t\\t\\t\\treturn -0.5 * (t*t*t*t - 2);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tcase this.EASE_IN_SINE:\\treturn 1-Math.cos( t * Math.PI / 2 );\\r\\n\\t\\t\\tcase this.EASE_OUT_SINE:\\treturn Math.sin( t * Math.PI / 2 );\\r\\n\\t\\t\\tcase this.EASE_IN_OUT_SINE: return -0.5 * ( Math.cos( Math.PI * t ) - 1 );\\r\\n\\r\\n\\t\\t\\tcase this.EASE_IN_EXPO: return t == 0 ? 0 : Math.pow( 2, 10 * (t - 1) );\\r\\n\\t\\t\\tcase this.EASE_OUT_EXPO: return t == 1 ? 1 : 1 - Math.pow( 2, -10 * t );\\r\\n\\t\\t\\tcase this.EASE_IN_OUT_EXPO: {\\r\\n\\t\\t\\t\\tif( t == 0 ) return 0;\\r\\n\\t\\t\\t\\tif( t == 1 ) return 1;\\r\\n\\t\\t\\t\\tt *= 2;\\r\\n\\t\\t\\t\\tif( t < 1 ) return 0.5 * Math.pow( 2, 10 * (t - 1) );\\r\\n\\t\\t\\t\\treturn 0.5 * ( -Math.pow( 2, -10 * (t - 1)) + 2);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tcase this.EASE_IN_BACK: return t * t * ((s+1)*t - s);\\r\\n\\t\\t\\tcase this.EASE_OUT_BACK: return (t*t*((s+1)*t + s) + 1);\\r\\n\\t\\t\\tcase this.EASE_IN_OUT_BACK: {\\r\\n\\t\\t\\t\\tt *= 2;\\r\\n\\t\\t\\t\\tif( t < 1 ) {\\r\\n\\t\\t\\t\\t\\ts *= 1.525;\\r\\n\\t\\t\\t\\t\\treturn 0.5*(t*t*((s+1)*t - s));\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse {\\r\\n\\t\\t\\t\\t\\tt -= 2;\\r\\n\\t\\t\\t\\t\\ts *= 1.525;\\r\\n\\t\\t\\t\\t\\treturn 0.5*(t*t*((s+1)*t+ s) + 2);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t};\\r\\n\\t\\t}\\r\\n\\t\\treturn t;\\r\\n\\t}\\r\\n};\\r\\n\\r\\n///@FILE:../src/helpers/animationBlender.js\\r\\n///@INFO: UNCOMMON,ANIMATION\\r\\n\\r\\nfunction AnimationBlender()\\r\\n{\\r\\n\\tthis.active_entries = [];\\r\\n}\\r\\n\\r\\nAnimationBlender.prototype.addEntry = function( animation, take, time )\\r\\n{\\r\\n\\tif(animation.constructor === LS.AnimationBlender.Entry)\\r\\n\\t{\\r\\n\\t\\tthis.active_entries.push(animation);\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar entry = new LS.AnimationBlender.Entry();\\r\\n\\tentry.animation_name = animation;\\r\\n\\tentry.take_name = take;\\r\\n\\tentry.time = time;\\r\\n\\tthis.active_entries.push(entry);\\r\\n\\treturn entry;\\r\\n}\\r\\n\\r\\nAnimationBlender.prototype.removeEntry = function( entry )\\r\\n{\\r\\n\\tvar index = this.active_entries.indexOf( entry );\\r\\n\\tif(index != -1)\\r\\n\\t\\tthis.active_entries.splice( index, 1 );\\r\\n}\\r\\n\\r\\nAnimationBlender.prototype.execute = function( root_node, scene )\\r\\n{\\r\\n\\tvar tracks = {};\\r\\n\\r\\n\\t//compute total weight (sum of all weights)\\r\\n\\tvar total_weight = 0;\\r\\n\\tfor(var i = 0; i < this.active_entries.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar entry = this.active_entries[i];\\r\\n\\t\\tif(!entry._animation_take)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\ttotal_weight += entry.weight;\\r\\n\\t\\tvar take = entry._animation_take;\\r\\n\\t\\tfor(var j = 0; j < take.tracks.length; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar track = take.tracks[j];\\r\\n\\t\\t\\tvar samples = tracks[ track.property ];\\r\\n\\t\\t\\tif(!samples)\\r\\n\\t\\t\\t\\tsamples = tracks[ track.property ] = [];\\r\\n\\t\\t\\tvar sample = track.getSample();\\r\\n\\t\\t\\tsamples.push( sample );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//reverse weight system (hard to explain here...)\\r\\n\\tfor(var i = 0; i < this.active_entries.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar entry = this.active_entries[i];\\r\\n\\t\\ttotal_weight += entry.weight;\\r\\n\\t}\\r\\n\\r\\n\\t//\\r\\n\\tfor(var i = 0; i < this.active_entries.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar entry = this.active_entries[i];\\r\\n\\t\\tentry.execute( remaining / total_weight, false, root_node, scene );\\r\\n\\t\\tremaining -= entry.weight;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nLS.AnimationBlender = AnimationBlender;\\r\\n\\r\\n\\r\\n\\r\\nfunction Entry()\\r\\n{\\r\\n\\tthis.time = 0;\\r\\n\\tthis.weight = 1;\\r\\n\\r\\n\\tthis._animation_take = null; //pointer to the take\\r\\n\\r\\n\\tthis._animation_name = null;\\r\\n\\tthis._take_name = null;\\r\\n\\r\\n\\tthis._last_time = 0;\\r\\n\\tthis._must_update = false;\\r\\n}\\r\\n\\r\\nObject.defineProperty( Entry.prototype, \\\"take_name\\\", {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tif( this._take_name == v )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._take_name = v;\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._take_name;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( Entry.prototype, \\\"animation_name\\\", {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tif( this._animation_name == v )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._animation_name = v;\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._animation_name;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( Entry.prototype, \\\"duration\\\", {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tthrow(\\\"duration cannot be set. It depends in the animation duration\\\");\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\tif(!this._animation_take)\\r\\n\\t\\t\\treturn -1;\\r\\n\\t\\treturn this._animation_take.duration;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( Entry.prototype, \\\"loaded\\\", {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tthrow(\\\"duration cannot be set. It depends in the animation duration\\\");\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn !!this._animation_take; //to bool\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n\\r\\nEntry.prototype.execute = function( final_weight, ignore_interpolation, root_node, scene )\\r\\n{\\r\\n\\tif( !this._animation_name || !this._take_name )\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\tif( this._must_update )\\r\\n\\t{\\r\\n\\t\\tvar animation = LS.ResourcesManager.get( this._animation_name );\\r\\n\\t\\tif( !animation )\\r\\n\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\tthis._animation_take = animation.takes[ this._take_name ];\\r\\n\\t}\\r\\n\\r\\n\\tif(!this._animation_take)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._animation_take.applyTracks( this.time, this._last_time, ignore_interpolation, root_node, scene, final_weight );\\r\\n\\r\\n\\tthis._last_time = this.time;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n\\r\\nAnimationBlender.Entry = Entry;\\r\\n\\r\\n\\r\\n///@FILE:../src/helpers/spatialContainer.js\\r\\n///@INFO: BASE\\r\\n//WORK IN PROGRESS\\r\\n\\r\\n/** SpatialContainer\\r\\n* This class allows to store data spatially so it can be retrieved by proximity or by camera frustum view.\\r\\n* It can store objects associated with a bounding box.\\r\\n* It is used by the renderer to store all the RenderInstances, Lights and Colliders\\r\\n* IT IS A WORK IN PROGRESS SO FOR NOW IT DO NOT STORES THE INFO SPATIALLY, IT JUST EXPOSES AN INTERFACE\\r\\n* @class SpatialContainer\\r\\n*/\\r\\n\\r\\nfunction SpatialContainer()\\r\\n{\\r\\n\\tthis.size = 100000;\\r\\n\\tthis.root = [];\\r\\n\\tthis.objects_cell_by_id = new WeakMap();\\r\\n}\\r\\n\\r\\n//adds a new object to the container\\r\\nSpatialContainer.prototype.add = function( object, bounding )\\r\\n{\\r\\n\\tvar cell = this.root;\\r\\n\\r\\n\\tcell.push( object );\\r\\n\\tthis.objects_cell_by_id.set( object, cell ); //in which container is\\r\\n\\treturn object.uid;\\r\\n}\\r\\n\\r\\nSpatialContainer.prototype.updateBounding = function( object, new_bounding )\\r\\n{\\r\\n\\t//...\\r\\n}\\r\\n\\r\\n\\r\\n//adds a new object to the container\\r\\nSpatialContainer.prototype.remove = function( object, bounding )\\r\\n{\\r\\n\\tvar cell = \\tthis.objects_cell_by_id.get( object );\\r\\n\\tif(!cell)\\r\\n\\t\\treturn;\\r\\n\\tvar index = cell.indexOf( object );\\r\\n\\tif(index !== -1)\\r\\n\\t\\tcell.splice( index, 1 );\\r\\n\\tthis.objects_cell_by_id[\\\"delete\\\"]( object );\\r\\n}\\r\\n\\r\\n//retrieves a list of objects overlaping this area\\r\\nSpatialContainer.prototype.retrieve = function( bounding )\\r\\n{\\r\\n\\t//TODO: search cells that lay inside the bounding\\r\\n\\treturn this.root;\\r\\n}\\r\\n\\r\\n//retrieves a list of objects overlaping this area\\r\\nSpatialContainer.prototype.retrieveFromCamera = function( camera )\\r\\n{\\r\\n\\t//TODO: search cells that lay inside the frustum\\r\\n\\treturn this.root;\\r\\n}\\r\\n\\r\\n\\r\\nSpatialContainer.prototype.clear = function()\\r\\n{\\r\\n\\tthis.root.length = 0;\\r\\n\\tthis.objects_cell_by_id = new WeakMap();\\r\\n}\\r\\n\\r\\nLS.SpatialContainer = SpatialContainer;\\r\\n///@FILE:../src/render/renderSettings.js\\r\\n///@INFO: BASE\\r\\n/** RenderSettings contains how the scene should be renderer \\r\\n* There could be different renderSettings for different scene quality.\\r\\n* @class RenderSettings\\r\\n* @constructor\\r\\n**/\\r\\n\\r\\nfunction RenderSettings( o )\\r\\n{\\r\\n\\tthis.renderer_name = null; //null means default\\r\\n\\r\\n\\t//global render settings\\r\\n\\tthis.default_shadowmap_resolution = LS.RenderSettings.default_shadowmap_resolution; //let the system decide best shadowmap resolution according to quality settings\\r\\n\\tthis.ignore_viewports = false;\\t//render to full viewport, ignoring the viewport in the cameras\\r\\n\\tthis.ignore_clear = false;\\t//skip global clear, used in case you want to mix LiteScene with another renderer\\r\\n\\tthis.keep_viewport = false; //do not force a full canvas viewport at render start (use the current one in WebGL as the full)\\r\\n\\t//this.linear_depth = false; //forces depth to be stored lineally\\r\\n\\r\\n\\tthis.shadows_enabled = true; //allow shadowmaps\\r\\n\\tthis.update_shadowmaps = true; //automatically update shadowmaps in every frame (enable if there are dynamic objects)\\r\\n\\tthis.update_all_shadowmaps = false; //update shadowmaps even if they are not visible\\r\\n\\r\\n\\tthis.force_wireframe = false; //render everything in wireframe\\r\\n\\tthis.lights_disabled = false; //flat lighting\\r\\n\\tthis.quality = RenderSettings.AUTO_QUALITY;\\r\\n\\r\\n\\tthis.render_all_cameras = true; //render secundary cameras too\\r\\n\\tthis.render_fx = true; //postprocessing fx\\r\\n\\tthis.render_gui = true; //render gui\\r\\n\\tthis.render_helpers = true; //render helpers (for the editor)\\r\\n\\r\\n\\tthis.layers = 0xFF; //this is masked with the camera layers when rendering\\r\\n\\r\\n\\tthis.z_pass = false; //enable when the shaders are too complex (normalmaps, etc) to reduce work of the GPU (still some features missing)\\r\\n\\tthis.frustum_culling = true; //test bounding box by frustum to determine visibility\\r\\n\\r\\n\\t//info\\r\\n\\tthis.in_player = true; //is in the player (not in the editor)\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nRenderSettings.AUTO_QUALITY = 0;\\r\\nRenderSettings.HIGH_QUALITY = 1;\\r\\nRenderSettings.MEDIUM_QUALITY = 2;\\r\\nRenderSettings.LOW_QUALITY = 3;\\r\\n\\r\\nRenderSettings.default_shadowmap_resolution = 1024;\\r\\n\\r\\nRenderSettings[\\\"@default_shadowmap_resolution\\\"] = { widget: \\\"combo\\\", values: [0,256,512,1024,2048,4096] };\\r\\nRenderSettings[\\\"@layers\\\"] = { type: \\\"layers\\\" };\\r\\n\\r\\nRenderSettings.prototype.serialize = function()\\r\\n{\\r\\n\\treturn LS.cloneObject(this);\\r\\n}\\r\\n\\r\\nRenderSettings.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(o)\\r\\n\\t\\tfor(var i in o)\\r\\n\\t\\t\\tthis[i] = o[i];\\r\\n\\r\\n\\t//legacy\\r\\n\\tif(this.layers === null)\\r\\n\\t\\tthis.layers = 0xFF;\\r\\n}\\r\\n\\r\\nRenderSettings.prototype.toJSON = RenderSettings.prototype.serialize;\\r\\n\\r\\nLS.RenderSettings = RenderSettings;\\r\\n///@FILE:../src/render/renderState.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* RenderState sets the flags for the GPU associated with a rendering action (blending, masking, depth test, etc)\\r\\n* It is stored in the material (although defined usually from ShaderCode) so the material can use it.\\r\\n*\\r\\n* @class RenderState\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\n/* gpu flags\\r\\n\\r\\n0: front_face: GL.CCW\\r\\n1: cull_face: 1\\r\\n2: cull_face_mode: GL.BACK\\r\\n\\r\\n//depth buffer\\r\\n4: depth_test: 1\\r\\n5: depth_mask: 1 //write in depth buffer\\r\\n6: depth_func: GL.LESS\\r\\n7: depth_range0: 0\\r\\n8: depth_range1: 1\\r\\n\\r\\n//blend function\\r\\n9: blend: 0;\\r\\n10: blendFunc0: GL.SRC_ALPHA\\r\\n11: blendFunc1: GL.ONE_MINUS_SRC_ALPHA\\r\\n\\r\\n//color mask\\r\\n12:\\tcolorMask0: 1\\r\\n13:\\tcolorMask1: 1\\r\\n14:\\tcolorMask2: 1\\r\\n15:\\tcolorMask3: 1\\r\\n\\r\\n//stencil buffer\\r\\n16: stencil_test: 0\\r\\n17:\\tstencil_mask: 0xFF,\\r\\n18:\\tstencil_func_func: GL.ALWAYS,\\r\\n19:\\tstencil_func_ref: 0,\\r\\n20:\\tstencil_func_mask: 0xFF,\\r\\n21:\\tstencil_op_sfail: GL.KEEP,\\r\\n22:\\tstencil_op_dpfail: GL.KEEP,\\r\\n23:\\tstencil_op_dppass: GL.KEEP\\r\\n\\r\\n24: flags\\r\\n*/\\r\\n\\r\\nfunction RenderState( o )\\r\\n{\\r\\n\\tthis._data = new Uint32Array(25);\\r\\n\\tthis.init();\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"front_face\\\", {\\r\\n\\tset: function(v) { this._data[0] = v; },\\r\\n\\tget: function() { return this._data[0];\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState.SKIP_BLEND = 1;\\r\\nRenderState.SKIP_DEPTH = 2;\\r\\nRenderState.SKIP_STENCIL = 4;\\r\\n\\r\\nRenderState[\\\"@front_face\\\"] = { widget: \\\"combo\\\", values: { CW: GL.CW, CCW: GL.CCW } };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"cull_face\\\", {\\r\\n\\tset: function(v) { this._data[1] = v ? 1 : 0; },\\r\\n\\tget: function() { return this._data[1] !== 0;\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"cull_face_mode\\\", {\\r\\n\\tset: function(v) { this._data[2] = v; },\\r\\n\\tget: function() { return this._data[2];\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState[\\\"@cull_face_mode\\\"] = { widget: \\\"combo\\\", values: { FRONT: GL.FRONT, BACK: GL.BACK, FRONT_AND_BACK: GL.FRONT_AND_BACK } };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"depth_test\\\", {\\r\\n\\tset: function(v) { this._data[4] = v ? 1 : 0; },\\r\\n\\tget: function() { return this._data[4] !== 0;\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"depth_mask\\\", {\\r\\n\\tset: function(v) { this._data[5] = v ? 1 : 0; },\\r\\n\\tget: function() { return this._data[5] !== 0;\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"depth_func\\\", {\\r\\n\\tset: function(v) { this._data[6] = v; },\\r\\n\\tget: function() { return this._data[6];\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState[\\\"@depth_func\\\"] = { widget: \\\"combo\\\", values: { LESS: GL.LESS, LEQUAL: GL.LEQUAL, EQUAL: GL.EQUAL, NOTEQUAL: GL.NOTEQUAL, GREATER: GL.GREATER, GEQUAL: GL.GEQUAL, ALWAYS: GL.ALWAYS, NEVER: GL.NEVER } };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"depth_range\\\", {\\r\\n\\tset: function(v) { \\r\\n\\t\\tif(!v || v.length != 2)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._data[7] = v[0];\\r\\n\\t\\tthis._data[8] = v[1];\\r\\n\\t},\\r\\n\\tget: function() { return this._data.subarray(7,9);\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"blend\\\", {\\r\\n\\tset: function(v) { this._data[9] = v ? 1 : 0; },\\r\\n\\tget: function() { return this._data[9] !== 0;\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"blendFunc0\\\", {\\r\\n\\tset: function(v) { this._data[10] = v; },\\r\\n\\tget: function() { return this._data[10];\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState[\\\"@blendFunc0\\\"] = { widget: \\\"combo\\\", values: { ZERO: GL.ZERO, ONE: GL.ONE, SRC_COLOR: GL.SRC_COLOR, ONE_MINUS_SRC_COLOR: GL.ONE_MINUS_SRC_COLOR, DST_COLOR: GL.DST_COLOR, ONE_MINUS_DST_COLOR: GL.ONE_MINUS_DST_COLOR, SRC_ALPHA: GL.SRC_ALPHA, ONE_MINUS_SRC_ALPHA: GL.ONE_MINUS_SRC_ALPHA, DST_ALPHA: GL.DST_ALPHA, ONE_MINUS_DST_ALPHA: GL.ONE_MINUS_DST_ALPHA, CONSTANT_COLOR: GL.CONSTANT_COLOR, ONE_MINUS_CONSTANT_COLOR: GL.ONE_MINUS_CONSTANT_COLOR, CONSTANT_ALPHA: GL.CONSTANT_ALPHA, ONE_MINUS_CONSTANT_ALPHA: GL.ONE_MINUS_CONSTANT_ALPHA, SRC_ALPHA_SATURATE: GL.SRC_ALPHA_SATURATE } };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"blendFunc1\\\", {\\r\\n\\tset: function(v) { this._data[11] = v; },\\r\\n\\tget: function() { return this._data[11];\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState[\\\"@blendFunc1\\\"] = { widget: \\\"combo\\\", values: { ZERO: GL.ZERO, ONE: GL.ONE, SRC_COLOR: GL.SRC_COLOR, ONE_MINUS_SRC_COLOR: GL.ONE_MINUS_SRC_COLOR, DST_COLOR: GL.DST_COLOR, ONE_MINUS_DST_COLOR: GL.ONE_MINUS_DST_COLOR, SRC_ALPHA: GL.SRC_ALPHA, ONE_MINUS_SRC_ALPHA: GL.ONE_MINUS_SRC_ALPHA, DST_ALPHA: GL.DST_ALPHA, ONE_MINUS_DST_ALPHA: GL.ONE_MINUS_DST_ALPHA, CONSTANT_COLOR: GL.CONSTANT_COLOR, ONE_MINUS_CONSTANT_COLOR: GL.ONE_MINUS_CONSTANT_COLOR, CONSTANT_ALPHA: GL.CONSTANT_ALPHA, ONE_MINUS_CONSTANT_ALPHA: GL.ONE_MINUS_CONSTANT_ALPHA, SRC_ALPHA_SATURATE: GL.SRC_ALPHA_SATURATE } };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"blendFunc\\\", {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tif(!v || v.length != 2)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._data[10] = v[0];\\r\\n\\t\\tthis._data[11] = v[1];\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._data.subarray(10,12);\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"colorMask0\\\", {\\r\\n\\tset: function(v) { this._data[12] = v ? 1 : 0; },\\r\\n\\tget: function() { return this._data[12] !== 0;\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"colorMask1\\\", {\\r\\n\\tset: function(v) { this._data[13] = v ? 1 : 0; },\\r\\n\\tget: function() { return this._data[13] !== 0;\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"colorMask2\\\", {\\r\\n\\tset: function(v) { this._data[14] = v ? 1 : 0; },\\r\\n\\tget: function() { return this._data[14] !== 0;\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"colorMask3\\\", {\\r\\n\\tset: function(v) { this._data[15] = v ? 1 : 0; },\\r\\n\\tget: function() { return this._data[15] !== 0;\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"colorMask\\\", {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tif(!v || v.length != 4)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._data[12] = v[0];\\r\\n\\t\\tthis._data[13] = v[1];\\r\\n\\t\\tthis._data[14] = v[2];\\r\\n\\t\\tthis._data[15] = v[3];\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._data.subarray(12,16);\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n/*\\r\\n16: stencil_test: 0\\r\\n17:\\tstencil_mask: 0xFF,\\r\\n18:\\tstencil_func_func: GL.ALWAYS,\\r\\n19:\\tstencil_func_ref: 0,\\r\\n20:\\tstencil_func_mask: 0xFF,\\r\\n21:\\tstencil_op_sfail: GL.KEEP,\\r\\n22:\\tstencil_op_dpfail: GL.KEEP,\\r\\n23:\\tstencil_op_dppass: GL.KEEP\\r\\n*/\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"stencil_test\\\", {\\r\\n\\tset: function(v) { this._data[16] = v ? 1 : 0; },\\r\\n\\tget: function() { return this._data[16] !== 0;\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"stencil_mask\\\", {\\r\\n\\tset: function(v) { this._data[17] = v; },\\r\\n\\tget: function() { return this._data[17]; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"skip_blend\\\", {\\r\\n\\tset: function(v) { this._data[25] = v ? (this._data[25] | RenderState.SKIP_BLEND) : (this._data[25] & ~(RenderState.SKIP_BLEND)); },\\r\\n\\tget: function() { return Boolean(this._data[25] & RenderState.SKIP_BLEND); },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"skip_depth\\\", {\\r\\n\\tset: function(v) { this._data[25] = v ? (this._data[25] | RenderState.SKIP_DEPTH) : (this._data[25] & ~(RenderState.SKIP_DEPTH)); },\\r\\n\\tget: function() { return Boolean(this._data[25] & RenderState.SKIP_DEPTH); },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"skip_stencil\\\", {\\r\\n\\tset: function(v) { this._data[25] = v ? (this._data[25] | RenderState.SKIP_STENCIL) : (this._data[25] & ~(RenderState.SKIP_STENCIL)); },\\r\\n\\tget: function() { return Boolean(this._data[25] & RenderState.SKIP_STENCIL); },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\nRenderState[\\\"@stencil_mask\\\"] = { widget: \\\"number\\\", min: 0, max: 256, step: 1, precision: 0 };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"stencil_func\\\", {\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(!v || v.length != 3)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._data[18] = v[0];\\r\\n\\t\\tthis._data[19] = v[1];\\r\\n\\t\\tthis._data[20] = v[2];\\r\\n\\t},\\r\\n\\tget: function() { return this._data.subarray(18,21); },\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"stencil_func_func\\\", {\\r\\n\\tset: function(v) { this._data[18] = v; },\\r\\n\\tget: function() { return this._data[18]; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState[\\\"@stencil_func_func\\\"] = { widget: \\\"combo\\\", values: { LESS: GL.LESS, LEQUAL: GL.LEQUAL, EQUAL: GL.EQUAL, NOTEQUAL: GL.NOTEQUAL, GREATER: GL.GREATER, GEQUAL: GL.GEQUAL, ALWAYS: GL.ALWAYS, NEVER: GL.NEVER } };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"stencil_func_ref\\\", {\\r\\n\\tset: function(v) { this._data[19] = v; },\\r\\n\\tget: function() { return this._data[19]; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState[\\\"@stencil_func_ref\\\"] = { widget: \\\"number\\\", min: 0, max: 256, step: 1, precision: 0 };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"stencil_func_mask\\\", {\\r\\n\\tset: function(v) { this._data[20] = v; },\\r\\n\\tget: function() { return this._data[20]; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState[\\\"@stencil_func_mask\\\"] = { widget: \\\"number\\\", min: 0, max: 256, step: 1, precision: 0 };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"stencil_op\\\", {\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(!v || v.length != 3)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._data[21] = v[0];\\r\\n\\t\\tthis._data[22] = v[1];\\r\\n\\t\\tthis._data[23] = v[2];\\r\\n\\t},\\r\\n\\tget: function() { return this._data.subarray(21,24); },\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"stencil_op_sfail\\\", {\\r\\n\\tset: function(v) { this._data[21] = v; },\\r\\n\\tget: function() { return this._data[21]; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState[\\\"@stencil_op_sfail\\\"] = { widget: \\\"combo\\\", values: { KEEP: GL.KEEP, ZERO: GL.ZERO, REPLACE: GL.REPLACE, INCR: GL.INCR, INCR_WRAP: GL.INCR_WRAP, DECR: GL.DECR_WRAP, INVERT: GL.INVERT } };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"stencil_op_dpfail\\\", {\\r\\n\\tset: function(v) { this._data[22] = v; },\\r\\n\\tget: function() { return this._data[22]; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState[\\\"@stencil_op_dpfail\\\"] = { widget: \\\"combo\\\", values: { KEEP: GL.KEEP, ZERO: GL.ZERO, REPLACE: GL.REPLACE, INCR: GL.INCR, INCR_WRAP: GL.INCR_WRAP, DECR: GL.DECR_WRAP, INVERT: GL.INVERT } };\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"stencil_op_dppass\\\", {\\r\\n\\tset: function(v) { this._data[23] = v; },\\r\\n\\tget: function() { return this._data[23]; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( RenderState.prototype, \\\"flags\\\", {\\r\\n\\tset: function(v) { this._data[24] = v; },\\r\\n\\tget: function() { return this._data[24]; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nRenderState[\\\"@stencil_op_dppass\\\"] = { widget: \\\"combo\\\", values: { KEEP: GL.KEEP, ZERO: GL.ZERO, REPLACE: GL.REPLACE, INCR: GL.INCR, INCR_WRAP: GL.INCR_WRAP, DECR: GL.DECR_WRAP, INVERT: GL.INVERT } };\\r\\n\\r\\nRenderState.default_state = {\\r\\n\\tfront_face: GL.CCW,\\r\\n\\tcull_face: true,\\r\\n\\tdepth_test: true,\\r\\n\\tdepth_func: GL.LESS,\\r\\n\\tdepth_mask: true,\\r\\n\\tblend: false,\\r\\n\\tblendFunc0: GL.SRC_ALPHA,\\r\\n\\tblendFunc1: GL.ONE_MINUS_SRC_ALPHA,\\r\\n\\tcolorMask0: true,\\r\\n\\tcolorMask1: true,\\r\\n\\tcolorMask2: true,\\r\\n\\tcolorMask3: true,\\r\\n\\tstencil_test: false,\\r\\n\\tstencil_mask: 0xFF,\\r\\n\\tstencil_func_func: GL.ALWAYS,\\r\\n\\tstencil_func_ref: 0,\\r\\n\\tstencil_func_mask: 0xFF,\\r\\n\\tstencil_op_sfail: GL.KEEP,\\r\\n\\tstencil_op_dpfail: GL.KEEP,\\r\\n\\tstencil_op_dppass: GL.KEEP\\r\\n};\\r\\n\\r\\nRenderState.last_state = null;\\r\\n\\r\\nRenderState.prototype.init = function()\\r\\n{\\r\\n\\t//gpu flags\\r\\n\\tthis.front_face = GL.CCW;\\r\\n\\tthis.cull_face = true;\\r\\n\\t//this.cull_face_mode = GL.BACK;\\r\\n\\r\\n\\t//depth buffer\\r\\n\\tthis.depth_test = true;\\r\\n\\tthis.depth_mask = true; //write in depth buffer\\r\\n\\tthis.depth_func = GL.LESS;\\r\\n\\t//depth range: never used\\r\\n\\r\\n\\t//blend function\\r\\n\\tthis.blend = false;\\r\\n\\tthis.blendFunc0 = GL.SRC_ALPHA;\\r\\n\\tthis.blendFunc1 = GL.ONE_MINUS_SRC_ALPHA;\\r\\n\\t//blend equation\\r\\n\\r\\n\\t//color mask\\r\\n\\tthis.colorMask0 = true;\\r\\n\\tthis.colorMask1 = true;\\r\\n\\tthis.colorMask2 = true;\\r\\n\\tthis.colorMask3 = true;\\r\\n\\r\\n\\t//stencil buffer\\r\\n\\tthis.stencil_test = false;\\r\\n\\tthis.stencil_mask = 0xFF;\\r\\n\\tthis.stencil_func_func = GL.ALWAYS;\\r\\n\\tthis.stencil_func_ref = 0;\\r\\n\\tthis.stencil_func_mask = 0xFF;\\r\\n\\tthis.stencil_op_sfail = GL.KEEP;\\r\\n\\tthis.stencil_op_dpfail = GL.KEEP;\\r\\n\\tthis.stencil_op_dppass = GL.KEEP;\\r\\n\\r\\n\\tthis.flags = 0;\\r\\n}\\r\\n\\r\\n//helper, allows to set the blend mode from a string\\r\\nRenderState.prototype.setBlendMode = function( mode )\\r\\n{\\r\\n\\tvar functions = LS.BlendFunctions[ mode ];\\r\\n\\tif(!mode || mode == LS.Blend.NORMAL )\\r\\n\\t{\\r\\n\\t\\tthis.blend = false;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tthis.blend = true;\\r\\n\\tthis.blendFunc0 = mode[0];\\r\\n\\tthis.blendFunc1 = mode[1];\\r\\n}\\r\\n\\r\\nRenderState.prototype.enable = function( render_settings )\\r\\n{\\r\\n\\tRenderState.enable( this, null, render_settings );\\r\\n}\\r\\n\\r\\nRenderState.enable = function( state, prev, render_settings )\\r\\n{\\r\\n\\tvar flags = state.flags;\\r\\n\\r\\n\\tvar force_two_sided = render_settings && render_settings.force_two_sided ? true : false;\\r\\n\\r\\n\\tif(!prev)\\r\\n\\t{\\r\\n\\t\\t//faces\\r\\n\\t\\tif(LS.Renderer._reverse_faces)\\r\\n\\t\\t\\tgl.frontFace( state.front_face == GL.CCW ? GL.CW : GL.CCW );\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.frontFace( state.front_face );\\r\\n\\t\\tif(state.cull_face && !force_two_sided )\\r\\n\\t\\t\\tgl.enable( gl.CULL_FACE );\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.disable( gl.CULL_FACE );\\r\\n\\r\\n\\t\\t//depth\\r\\n\\t\\tif( !(flags & RenderState.SKIP_DEPTH) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(state.depth_test)\\r\\n\\t\\t\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\t\\tgl.depthMask( state.depth_mask );\\r\\n\\t\\t\\tgl.depthFunc( state.depth_func );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//blend\\r\\n\\t\\tif( !(flags & RenderState.SKIP_BLEND) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(state.blend)\\r\\n\\t\\t\\t\\tgl.enable( gl.BLEND );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\t\\tgl.blendFunc( state.blendFunc0, state.blendFunc1 );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//color\\r\\n\\t\\tgl.colorMask( state.colorMask0, state.colorMask1, state.colorMask2, state.colorMask3 );\\r\\n\\r\\n\\t\\t//stencil\\r\\n\\t\\tif( !(flags & RenderState.SKIP_STENCIL) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(state.stencil_test)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tgl.enable( gl.STENCIL_TEST );\\r\\n\\t\\t\\t\\tgl.stencilFunc( state.stencil_func_func, state.stencil_func_ref, state.stencil_func_mask );\\r\\n\\t\\t\\t\\tgl.stencilOp( state.stencil_op_sfail, state.stencil_op_dpfail, state.stencil_op_dppass );\\r\\n\\t\\t\\t\\tgl.stencilMask( state.stencil_mask );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.disable( gl.STENCIL_TEST );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.last_state = state;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//***********************************************\\r\\n\\r\\n\\t//faces\\r\\n\\tif(LS.Renderer._reverse_faces)\\r\\n\\t\\tgl.frontFace( state.front_face == GL.CCW ? GL.CW : GL.CCW );\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tif( prev.front_face !== state.front_face )\\r\\n\\t\\t\\tgl.frontFace( state.front_face );\\r\\n\\t}\\r\\n\\tif( prev.cull_face !== state.cull_face || force_two_sided )\\r\\n\\t{\\r\\n\\t\\tif( state.cull_face && !force_two_sided )\\r\\n\\t\\t\\tgl.enable( gl.CULL_FACE );\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.disable( gl.CULL_FACE );\\r\\n\\t}\\r\\n\\r\\n\\t//depth\\r\\n\\tif( !(flags & RenderState.SKIP_DEPTH) )\\r\\n\\t{\\r\\n\\t\\tif(prev.depth_test !== state.depth_test)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(state.depth_test)\\r\\n\\t\\t\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\t}\\r\\n\\t\\tif(prev.depth_mask !== state.depth_mask)\\r\\n\\t\\t\\tgl.depthMask( state.depth_mask );\\r\\n\\t\\tif(prev.depth_func !== state.depth_func)\\r\\n\\t\\t\\tgl.depthFunc( state.depth_func );\\r\\n\\t}\\r\\n\\r\\n\\t//blend\\r\\n\\tif( !(flags & RenderState.SKIP_BLEND) )\\r\\n\\t{\\r\\n\\t\\tif(prev.blend !== state.blend)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(state.blend)\\r\\n\\t\\t\\t\\tgl.enable( gl.BLEND );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\t}\\r\\n\\t\\tif(prev.blendFunc0 !== state.blendFunc0 || prev.blendFunc1 !== state.blendFunc1)\\r\\n\\t\\t\\tgl.blendFunc( state.blendFunc0, state.blendFunc1 );\\r\\n\\t}\\r\\n\\r\\n\\t//color\\r\\n\\tif(prev.colorMask0 !== state.colorMask0 || prev.colorMask1 !== state.colorMask1 || prev.colorMask2 !== state.colorMask2 || prev.colorMask3 !== state.colorMask3 )\\r\\n\\t\\tgl.colorMask( state.colorMask0, state.colorMask1, state.colorMask2, state.colorMask3 );\\r\\n\\r\\n\\t//stencil\\r\\n\\tif( !(flags & RenderState.SKIP_STENCIL) )\\r\\n\\t{\\r\\n\\t\\tif(prev.stencil_test != state.stencil_test )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(state.stencil_test)\\r\\n\\t\\t\\t\\tgl.enable( gl.STENCIL_TEST);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.disable( gl.STENCIL_TEST );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(state.stencil_test)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( state.stencil_func_func !== prev.stencil_func_func || state.stencil_func_ref !== prev.stencil_func_ref || state.stencil_func_mask !== prev.stencil_func_mask )\\r\\n\\t\\t\\t\\tgl.stencilFunc( state.stencil_func_func, state.stencil_func_ref, state.stencil_func_mask );\\r\\n\\r\\n\\t\\t\\tif(state.stencil_op_sfail !== prev.stencil_op_sfail || state.stencil_op_dpfail !== stencil_op_dpfail || state.stencil_op_dppass !== stencil_op_dppass )\\r\\n\\t\\t\\t\\tgl.stencilOp( state.stencil_op_sfail, state.stencil_op_dpfail, state.stencil_op_dppass );\\r\\n\\r\\n\\t\\t\\tif(state.stencil_mask !== prev.stencil_mask)\\r\\n\\t\\t\\t\\tgl.stencilMask( prev.stencil_mask );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//save state\\r\\n\\tthis.last_state = state;\\r\\n}\\r\\n\\r\\nRenderState.reset = function()\\r\\n{\\r\\n\\tthis.enable( this.default_state );\\r\\n}\\r\\n\\r\\nRenderState.prototype.serialize = function()\\r\\n{\\r\\n\\treturn LS.cloneObject(this);\\r\\n}\\r\\n\\r\\nRenderState.prototype.toJSON = RenderState.prototype.serialize;\\r\\n\\r\\nRenderState.prototype.configure = function(o)\\r\\n{\\r\\n\\tLS.cloneObject(o,this);\\r\\n}\\r\\n\\r\\nRenderState.prototype.copyFrom = function( rs )\\r\\n{\\r\\n\\tthis._data.set( rs._data );\\r\\n}\\r\\n\\r\\n\\r\\nLS.RenderState = RenderState;\\r\\n///@FILE:../src/render/renderCall.js\\r\\n///@INFO: UNCOMMON\\r\\n//WIP: this is the lowest GPU rendering object, which encapsulates all about a render call\\r\\n//by encapsulating every render action into an object we can have materials that produce several render passes in different moments\\r\\n//of the rendering process\\r\\n//the only problem is that uniform containrs could change between render calls which will lead to errors \\r\\n\\r\\nfunction RenderCall()\\r\\n{\\r\\n\\tthis.shader = null;\\r\\n\\tthis.uniforms_containers = [];\\r\\n\\tthis.vertex_buffers = null;\\r\\n\\tthis.index_buffer = null;\\r\\n\\tthis.offset_start = -1;\\r\\n\\tthis.offset_range = -1;\\r\\n\\tthis.primitive = -1;\\r\\n\\r\\n\\tthis.renderState = null;\\r\\n}\\r\\n\\r\\nRenderCall.prototype.draw = function()\\r\\n{\\r\\n\\tthis.renderState.enable();\\r\\n\\r\\n\\tthis.shader.uniforms( this.uniforms ).drawBuffers( this.vertex_buffers,\\r\\n\\t this.index_buffer,\\r\\n\\t this.primitive, this.offset_start, this.offset_range );\\r\\n}\\r\\n\\r\\n//Pool\\r\\nRenderCall.pool = [];\\r\\n\\r\\nRenderCall.get = function()\\r\\n{\\r\\n\\tif( RenderCall.pool.length > 0 )\\r\\n\\t\\treturn RenderCall.pool.pop();\\r\\n\\treturn new RenderCall();\\r\\n}\\r\\n\\r\\nRenderCall.prototype.release = function()\\r\\n{\\r\\n\\tRenderCall.pool.push(this);\\r\\n}\\r\\n\\r\\n///@FILE:../src/render/renderInstance.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* RenderInstance contains info of one object to be rendered on the scene.\\r\\n* It shouldnt contain ids to resources (strings), instead if must contain the direct reference (to mesh, material)\\r\\n*\\r\\n* @class RenderInstance\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nfunction RenderInstance( node, component )\\r\\n{\\r\\n\\tthis.uid = LS.generateUId(\\\"RINS\\\"); //unique identifier for this RI\\r\\n\\tthis.layers = 3; //in layer 1 and 2 by default\\r\\n\\tthis.index = -1; //used to know the rendering order\\r\\n\\tthis.version = -1; //not used yet\\r\\n\\r\\n\\t//info about the mesh\\r\\n\\tthis.vertex_buffers = {};\\r\\n\\tthis.index_buffer = null;\\r\\n\\tthis.wireframe_index_buffer = null;\\r\\n\\tthis.range = new Int32Array([0,-1]); //start, offset\\r\\n\\tthis.primitive = GL.TRIANGLES;\\r\\n\\r\\n\\tthis.transform = null; //parented transform: not finished\\r\\n\\r\\n\\tthis.mesh = null; //shouldnt be used (buffers are added manually), but just in case\\r\\n\\tthis.collision_mesh = null; //in case of raycast\\r\\n\\r\\n\\t//where does it come from\\r\\n\\tthis.node = node;\\r\\n\\tthis.component = component;\\r\\n\\tthis.priority = 10; //only used if the RenderQueue is in PRIORITY MODE, instances are rendered from higher to lower priority\\r\\n\\tthis.sort_mode = RenderInstance.NO_SORT;\\r\\n\\r\\n\\t//transformation\\r\\n\\tthis.matrix = mat4.create();\\r\\n\\tthis.normal_matrix = mat4.create();\\r\\n\\tthis.position = vec3.create(); //the origin of the node\\r\\n\\r\\n\\t//for visibility computation\\r\\n\\tthis.oobb = BBox.create(); //object space bounding box\\r\\n\\tthis.aabb = BBox.create(); //axis aligned bounding box\\r\\n\\tthis.center = BBox.getCenter( this.aabb ); //the center of the AABB\\r\\n\\r\\n\\t//info about the material\\r\\n\\tthis.material = null; //the material, cannot be a string\\r\\n\\tthis.use_bounding = true; //in case it has vertex shader deformers the bounding box is not usable\\r\\n\\r\\n\\t//for extra data for the shader\\r\\n\\tthis.uniforms = {};\\r\\n\\tthis.samplers = [];\\r\\n\\r\\n\\tthis.instanced_models = [];\\r\\n\\r\\n\\tthis.shader_block_flags = 0;\\r\\n\\tthis.shader_blocks = [];\\r\\n\\r\\n\\tthis.picking_node = null; //for picking\\r\\n\\r\\n\\t//this.deformers = []; //TODO\\r\\n\\r\\n\\t//TO DO: instancing\\r\\n\\t//this.uniforms_instancing = {};\\r\\n\\r\\n\\t//for internal use\\r\\n\\tthis._camera_visibility = 0; //tells in which camera was visible this instance during the last rendering (using bit operations)\\r\\n\\tthis._is_visible = false; //used during the rendering to mark if it was seen\\r\\n\\tthis._dist = 0; //computed during rendering, tells the distance to the current camera\\r\\n\\tthis._nearest_reflection_probe = null;\\r\\n}\\r\\n\\r\\nRenderInstance.NO_SORT = 0;\\r\\nRenderInstance.SORT_NEAR_FIRST = 1;\\r\\nRenderInstance.SORT_FAR_FIRST = 2;\\r\\n\\r\\nRenderInstance.fast_normalmatrix = false; //avoid doint the inverse transpose for normal matrix, and just copies the model\\r\\n\\r\\nRenderInstance.prototype.fromNode = function(node)\\r\\n{\\r\\n\\tif(!node)\\r\\n\\t\\tthrow(\\\"no node\\\");\\r\\n\\tthis.node = node;\\r\\n\\tif(node.transform)\\r\\n\\t\\tthis.setMatrix( node.transform._global_matrix );\\r\\n\\telse\\r\\n\\t\\tthis.setMatrix( LS.IDENTITY );\\r\\n\\tmat4.multiplyVec3( this.position, this.matrix, LS.ZEROS );\\r\\n\\tthis.layers = node.layers;\\r\\n}\\r\\n\\r\\n//set the matrix \\r\\nRenderInstance.prototype.setMatrix = function(matrix, normal_matrix)\\r\\n{\\r\\n\\tthis.matrix.set( matrix );\\r\\n\\r\\n\\tif( normal_matrix )\\r\\n\\t\\tthis.normal_matrix.set( normal_matrix )\\r\\n\\telse\\r\\n\\t\\tthis.computeNormalMatrix();\\r\\n}\\r\\n\\r\\n/**\\r\\n* Updates the normal matrix using the matrix\\r\\n*\\r\\n* @method computeNormalMatrix\\r\\n*/\\r\\nRenderInstance.prototype.computeNormalMatrix = function()\\r\\n{\\r\\n\\tif(RenderInstance.fast_normalmatrix)\\r\\n\\t{\\r\\n\\t\\tthis.normal_matrix.set( this.matrix );\\r\\n\\t\\tmat4.setTranslation( this.normal_matrix, LS.ZEROS );\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar m = mat4.invert(this.normal_matrix, this.matrix);\\r\\n\\tif(m)\\r\\n\\t\\tmat4.transpose(this.normal_matrix, m);\\r\\n}\\r\\n\\r\\n/**\\r\\n* applies a transformation to the current matrix\\r\\n*\\r\\n* @method applyTransform\\r\\n* @param {mat4} matrix\\r\\n* @param {mat4} normal_matrix [optional]\\r\\n*/\\r\\nRenderInstance.prototype.applyTransform = function( matrix, normal_matrix )\\r\\n{\\r\\n\\tmat4.mul( this.matrix, this.matrix, matrix );\\r\\n\\tif( normal_matrix )\\r\\n\\t\\tmat4.mul( this.normal_matrix, this.normal_matrix, normal_matrix );\\r\\n\\telse\\r\\n\\t\\tthis.computeNormalMatrix();\\r\\n}\\r\\n\\r\\n//set the material and apply material flags to render instance\\r\\nRenderInstance.prototype.setMaterial = function(material)\\r\\n{\\r\\n\\tif(material && !material.constructor.is_material)\\r\\n\\t{\\r\\n\\t\\t//console.error(\\\"Material in RenderInstance is not a material class:\\\",material);\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\tthis.material = material;\\r\\n\\tif(material && material.applyToRenderInstance)\\r\\n\\t\\tmaterial.applyToRenderInstance(this);\\r\\n}\\r\\n\\r\\n//sets the buffers to render, the primitive, and the bounding\\r\\nRenderInstance.prototype.setMesh = function( mesh, primitive )\\r\\n{\\r\\n\\tif( primitive == -1 || primitive === undefined )\\r\\n\\t\\tprimitive = gl.TRIANGLES;\\r\\n\\tthis.primitive = primitive;\\r\\n\\r\\n\\tif(mesh != this.mesh)\\r\\n\\t{\\r\\n\\t\\tthis.mesh = mesh;\\r\\n\\t\\tthis.vertex_buffers = {};\\r\\n\\t}\\r\\n\\r\\n\\tif(!this.mesh)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//this.vertex_buffers = mesh.vertexBuffers;\\r\\n\\tfor(var i in mesh.vertexBuffers)\\r\\n\\t\\tthis.vertex_buffers[i] = mesh.vertexBuffers[i];\\r\\n\\r\\n\\tswitch(primitive)\\r\\n\\t{\\r\\n\\t\\tcase gl.TRIANGLES: \\r\\n\\t\\t\\tthis.index_buffer = mesh.indexBuffers[\\\"triangles\\\"]; //works for indexed and non-indexed\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase gl.LINES: \\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\tif(!mesh.indexBuffers[\\\"lines\\\"])\\r\\n\\t\\t\\t\\tmesh.computeWireframe();\\r\\n\\t\\t\\t*/\\r\\n\\t\\t\\tthis.index_buffer = mesh.indexBuffers[\\\"lines\\\"];\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase 10: //wireframe\\r\\n\\t\\t\\tthis.primitive = gl.LINES;\\r\\n\\t\\t\\tif(!mesh.indexBuffers[\\\"wireframe\\\"])\\r\\n\\t\\t\\t\\tmesh.computeWireframe();\\r\\n\\t\\t\\tthis.index_buffer = mesh.indexBuffers[\\\"wireframe\\\"];\\r\\n\\t\\t\\tbreak;\\r\\n\\r\\n\\t\\tcase gl.POINTS: \\r\\n\\t\\tdefault:\\r\\n\\t\\t\\tthis.index_buffer = null;\\r\\n\\t\\t\\tbreak;\\r\\n\\t}\\r\\n\\r\\n\\tif(mesh.bounding)\\r\\n\\t{\\r\\n\\t\\tthis.oobb.set( mesh.bounding ); //copy\\r\\n\\t\\tthis.use_bounding = true;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tthis.use_bounding = false;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Sets the object oriented bounding box using the BBox format (usually is the mesh bounding but in some cases could be different like with skinning or submeshes)\\r\\n*\\r\\n* @method setBoundinbBox\\r\\n* @param {BBox} bbox bounding in bbox format\\r\\n*/\\r\\nRenderInstance.prototype.setBoundingBox = function(bbox)\\r\\n{\\r\\n\\tthis.oobb.set( bbox );\\r\\n}\\r\\n\\r\\n/**\\r\\n* specifies the rendering range for the mesh (from where and how many primitives), if -1 then ignored\\r\\n*\\r\\n* @method setRange\\r\\n* @param {Number} start\\r\\n* @param {Number} length\\r\\n*/\\r\\nRenderInstance.prototype.setRange = function( start, length )\\r\\n{\\r\\n\\tthis.range[0] = start;\\r\\n\\tthis.range[1] = length;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Enable flag in the flag bit field\\r\\n*\\r\\n* @method enableFlag\\r\\n* @param {number} flag id\\r\\n*/\\r\\nRenderInstance.prototype.enableFlag = function(flag)\\r\\n{\\r\\n\\tthis.flags |= flag;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Disable flag in the flag bit field\\r\\n*\\r\\n* @method enableFlag\\r\\n* @param {number} flag id\\r\\n*/\\r\\nRenderInstance.prototype.disableFlag = function(flag)\\r\\n{\\r\\n\\tthis.flags &= ~flag;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Tells if a flag is enabled\\r\\n*\\r\\n* @method enableFlag\\r\\n* @param {number} flag id\\r\\n* @return {boolean} flag value\\r\\n*/\\r\\nRenderInstance.prototype.isFlag = function(flag)\\r\\n{\\r\\n\\treturn (this.flags & flag);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Computes the instance bounding box in world space from the one in local space\\r\\n*\\r\\n* @method updateAABB\\r\\n*/\\r\\nRenderInstance.prototype.updateAABB = function()\\r\\n{\\r\\n\\tBBox.transformMat4( this.aabb, this.oobb, this.matrix );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Used to update the RI info without having to go through the collectData process, it is faster but some changes may take a while\\r\\n*\\r\\n* @method update\\r\\n*/\\r\\nRenderInstance.prototype.update = function()\\r\\n{\\r\\n\\tif(!this.node || !this.node.transform)\\r\\n\\t\\treturn;\\r\\n\\tthis.setMatrix( this.node.transform._global_matrix );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Calls render taking into account primitive and range\\r\\n*\\r\\n* @method render\\r\\n* @param {Shader} shader\\r\\n*/\\r\\nRenderInstance.prototype.render = function(shader, primitive)\\r\\n{\\r\\n\\t//in case no normals found but they are required\\r\\n\\tif(shader.attributes[\\\"a_normal\\\"] && !this.vertex_buffers[\\\"normals\\\"])\\r\\n\\t{\\r\\n\\t\\tthis.mesh.computeNormals();\\t\\t\\r\\n\\t\\tthis.vertex_buffers[\\\"normals\\\"] = this.mesh.vertexBuffers[\\\"normals\\\"];\\r\\n\\t}\\r\\n\\r\\n\\t//in case no coords found but they are required\\r\\n\\t/*\\r\\n\\tif(shader.attributes[\\\"a_coord\\\"] && !this.vertex_buffers[\\\"coords\\\"])\\r\\n\\t{\\r\\n\\t\\t//this.mesh.computeTextureCoordinates();\\t\\t\\r\\n\\t\\t//this.vertex_buffers[\\\"coords\\\"] = this.mesh.vertexBuffers[\\\"coords\\\"];\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\t//in case no tangents found but they are required\\r\\n\\tif(shader.attributes[\\\"a_tangent\\\"] && !this.vertex_buffers[\\\"tangents\\\"])\\r\\n\\t{\\r\\n\\t\\tthis.mesh.computeTangents();\\t\\t\\r\\n\\t\\tthis.vertex_buffers[\\\"tangents\\\"] = this.mesh.vertexBuffers[\\\"tangents\\\"];\\r\\n\\t}\\r\\n\\r\\n\\t//in case no secondary coords found but they are required\\r\\n\\tif(shader.attributes[\\\"a_coord1\\\"] && !this.vertex_buffers[\\\"coords1\\\"])\\r\\n\\t{\\r\\n\\t\\tthis.mesh.createVertexBuffer(\\\"coords1\\\",2, vertex_buffers[\\\"coords\\\"].data );\\r\\n\\t\\tthis.vertex_buffers[\\\"coords1\\\"] = this.mesh.vertexBuffers[\\\"coords1\\\"];\\r\\n\\t}\\r\\n\\r\\n\\tif(shader.attributes[\\\"a_normal\\\"] && !this.vertex_buffers[\\\"normals\\\"])\\r\\n\\t{\\r\\n\\t\\tthis.mesh.computeNormals();\\t\\t\\r\\n\\t\\tthis.vertex_buffers[\\\"normals\\\"] = this.mesh.vertexBuffers[\\\"normals\\\"];\\r\\n\\t}\\r\\n\\r\\n\\t//in case no secondary coords found but they are required\\r\\n\\tif(shader.attributes[\\\"a_extra\\\"] && !this.vertex_buffers[\\\"extra\\\"])\\r\\n\\t{\\r\\n\\t\\tthis.mesh.createVertexBuffer(\\\"extra\\\", \\\"a_extra\\\", 1 );\\r\\n\\t\\tthis.vertex_buffers[\\\"extra\\\"] = this.mesh.vertexBuffers[\\\"extra\\\"];\\r\\n\\t}\\r\\n\\r\\n\\tif(shader.attributes[\\\"a_extra2\\\"] && !this.vertex_buffers[\\\"extra2\\\"])\\r\\n\\t{\\r\\n\\t\\tthis.mesh.createVertexBuffer(\\\"extra2\\\",\\\"a_extra2\\\", 2 );\\r\\n\\t\\tthis.vertex_buffers[\\\"extra2\\\"] = this.mesh.vertexBuffers[\\\"extra2\\\"];\\r\\n\\t}\\r\\n\\r\\n\\tif(shader.attributes[\\\"a_extra3\\\"] && !this.vertex_buffers[\\\"extra3\\\"])\\r\\n\\t{\\r\\n\\t\\tthis.mesh.createVertexBuffer(\\\"extra3\\\",\\\"a_extra3\\\", 3 );\\r\\n\\t\\tthis.vertex_buffers[\\\"extra3\\\"] = this.mesh.vertexBuffers[\\\"extra3\\\"];\\r\\n\\t}\\r\\n\\r\\n\\t//in case no secondary coords found but they are required\\r\\n\\tif(shader.attributes[\\\"a_color\\\"] && !this.vertex_buffers[\\\"colors\\\"])\\r\\n\\t{\\r\\n\\t\\tthis.mesh.createVertexBuffer( \\\"colors\\\", \\\"a_color\\\", 4 );\\r\\n\\t\\tthis.vertex_buffers[\\\"colors\\\"] = this.mesh.vertexBuffers[\\\"colors\\\"];\\r\\n\\t}\\r\\n\\r\\n\\tshader.drawBuffers( this.vertex_buffers,\\r\\n\\t this.index_buffer,\\r\\n\\t primitive !== undefined ? primitive : this.primitive,\\r\\n\\t this.range[0], this.range[1] );\\r\\n}\\r\\n\\r\\nRenderInstance.prototype.addShaderBlock = function( block, uniforms )\\r\\n{\\r\\n\\tif( block.flag_mask & this.shader_block_flags && uniforms === undefined )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < this.shader_blocks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tif(!this.shader_blocks[i])\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif( this.shader_blocks[i].block == block )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(uniforms !== undefined)\\r\\n\\t\\t\\t\\tthis.shader_blocks[i].uniforms = uniforms;\\r\\n\\t\\t\\treturn i;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\tthis.shader_blocks.push( { block: block, uniforms: uniforms } );\\r\\n\\tthis.shader_block_flags |= block.flag_mask;\\r\\n\\treturn this.shader_blocks.length - 1;\\r\\n}\\r\\n\\r\\nRenderInstance.prototype.disableShaderBlock = function( block )\\r\\n{\\r\\n\\tif( ! (block.flag_mask & this.shader_block_flags) )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < this.shader_blocks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tif(!this.shader_blocks[i])\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif( this.shader_blocks[i].block !== block )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tthis.shader_block_flags &= ~block.flag_mask;\\r\\n\\t\\tbreak;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\nRenderInstance.prototype.removeShaderBlock = function( block )\\r\\n{\\r\\n\\tif( ! (block.flag_mask & this.shader_block_flags) )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < this.shader_blocks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tif(!this.shader_blocks[i])\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif( this.shader_blocks[i].block !== block )\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tthis.shader_blocks.splice(i,1);\\r\\n\\t\\tthis.shader_block_flags &= ~block.flag_mask;\\r\\n\\t\\tbreak;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//checks the ShaderBlocks attached to this instance and resolves the flags\\r\\nRenderInstance.prototype.computeShaderBlockFlags = function()\\r\\n{\\r\\n\\treturn this.shader_block_flags;\\r\\n\\r\\n\\t/*\\r\\n\\tvar r = 0;\\r\\n\\tfor(var i = 0; i < this.shader_blocks.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar shader_block = this.shader_blocks[i];\\r\\n\\t\\tif(!shader_block)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar block = this.shader_blocks[i].block;\\r\\n\\t\\tr |= block.flag_mask;\\r\\n\\t}\\r\\n\\treturn r;\\r\\n\\t*/\\r\\n}\\r\\n\\r\\n/*\\r\\nRenderInstance.prototype.renderInstancing = function( shader )\\r\\n{\\r\\n\\tvar instances_info = this.instances_info;\\r\\n\\r\\n\\tvar matrices = new Float32Array( instances_info.length * 16 );\\r\\n\\tfor(var j = 0; j < instances_info.length; ++j)\\r\\n\\t{\\r\\n\\t\\tvar matrix = instances_info[j].matrix;\\r\\n\\t\\tmatrices.set( matrix, j*16 );\\r\\n\\t}\\r\\n\\r\\n\\tgl.bindBuffer(gl.ARRAY_BUFFER, matricesBuffer );\\r\\n\\tgl.bufferData(gl.ARRAY_BUFFER, matrices, gl.STREAM_DRAW);\\r\\n\\r\\n\\t// Bind the instance matrices data (mat4 behaves as 4 attributes)\\r\\n\\tfor(var k = 0; k < 4; ++k)\\r\\n\\t{\\r\\n\\t\\tgl.enableVertexAttribArray( location+k );\\r\\n\\t\\tgl.vertexAttribPointer( location+k, 4, gl.FLOAT , false, 16*4, k*4*4 );\\r\\n\\t\\text.vertexAttribDivisorANGLE( location+k, 1 ); // This makes it instanced!\\r\\n\\t}\\r\\n\\r\\n\\t//gl.drawElements( gl.TRIANGLES, length, indexBuffer.buffer.gl_type, 0 ); //gl.UNSIGNED_SHORT\\r\\n\\text.drawElementsInstancedANGLE( gl.TRIANGLES, length, indexBuffer.buffer.gl_type, 0, batch.length );\\r\\n\\tGFX.stats.calls += 1;\\r\\n\\tfor(var k = 0; k < 4; ++k)\\r\\n\\t{\\r\\n\\t\\text.vertexAttribDivisorANGLE( location+k, 0 );\\r\\n\\t\\tgl.disableVertexAttribArray( location+k );\\r\\n\\t}\\r\\n}\\r\\n*/\\r\\n\\r\\nRenderInstance.prototype.overlapsSphere = function( center, radius )\\r\\n{\\r\\n\\t//we dont know if the bbox of the instance is valid\\r\\n\\tif( !this.use_bounding )\\r\\n\\t\\treturn true;\\r\\n\\treturn geo.testSphereBBox( center, radius, this.aabb );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Checks if this object was visible by a camera during the last frame\\r\\n*\\r\\n* @method wasVisibleByCamera\\r\\n* @param {LS.Camera} camera [optional] if a camera is supplied it checks if it was visible by that camera, otherwise tells you if it was visible by any camera\\r\\n* @return {Boolean} true if it was visible by the camera (or any camera if no camera supplied), false otherwise\\r\\n*/\\r\\nRenderInstance.prototype.wasVisibleByCamera = function( camera )\\r\\n{\\r\\n\\tif(!camera)\\r\\n\\t\\treturn this._camera_visibility != 0;\\r\\n\\treturn (this._camera_visibility | (1<<(camera._rendering_index))) ? true : false;\\r\\n}\\r\\n\\r\\nLS.RenderInstance = RenderInstance;\\r\\n///@FILE:../src/render/renderFrameContext.js\\r\\n///@INFO: BASE\\r\\n/**\\tRenderFrameContext\\r\\n*\\tThis class is used when you want to render the scene not to the screen but to some texture for postprocessing\\r\\n*\\tIt helps to create the textures and bind them easily, add extra buffers or show it on the screen.\\r\\n*\\tCheck the FrameFX and CameraFX components to see it in action.\\r\\n* Dependencies: LS.Renderer (writes there only)\\r\\n*\\r\\n* @class RenderFrameContext\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\nfunction RenderFrameContext( o )\\r\\n{\\r\\n\\tthis.width = 0; //0 means the same size as the viewport, negative numbers mean reducing the texture in half N times\\r\\n\\tthis.height = 0; //0 means the same size as the viewport\\r\\n\\tthis.precision = RenderFrameContext.DEFAULT_PRECISION; //LOW_PRECISION uses a byte, MEDIUM uses a half_float, HIGH uses a float, or directly the texture type (p.e gl.UNSIGNED_SHORT_4_4_4_4 )\\r\\n\\tthis.filter_texture = true; //magFilter: in case the texture is shown, do you want to see it pixelated?\\r\\n\\tthis.format = GL.RGB; //how many color channels, or directly the texture internalformat \\r\\n\\tthis.use_depth_texture = true; //store the depth in a texture\\r\\n\\tthis.use_stencil_buffer = false; //add an stencil buffer (cannot be read as a texture in webgl)\\r\\n\\tthis.num_extra_textures = 0; //number of extra textures in case we want to render to several buffers\\r\\n\\tthis.name = null; //if a name is provided all the textures will be stored in the LS.ResourcesManager\\r\\n\\r\\n\\tthis.generate_mipmaps = false; //try to generate mipmaps if possible (only when the texture is power of two)\\r\\n\\tthis.adjust_aspect = false; //when the size doesnt match the canvas size it could look distorted, settings this to true will fix the problem\\r\\n\\tthis.clone_after_unbind = false; //clones the textures after unbinding it. Used when the texture will be in the 3D scene\\r\\n\\r\\n\\tthis._fbo = null;\\r\\n\\tthis._color_texture = null;\\r\\n\\tthis._depth_texture = null;\\r\\n\\tthis._textures = []; //all color textures (the first will be _color_texture)\\r\\n\\tthis._cloned_textures = null; //in case we set the clone_after_unbind to true\\r\\n\\r\\n\\tthis._version = 1; //to detect changes\\r\\n\\tthis._minFilter = gl.NEAREST;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\n\\r\\nRenderFrameContext.current = null;\\r\\nRenderFrameContext.stack = [];\\r\\n\\r\\nRenderFrameContext.DEFAULT_PRECISION = 0; //selected by the renderer\\r\\nRenderFrameContext.LOW_PRECISION = 1; //byte\\r\\nRenderFrameContext.MEDIUM_PRECISION = 2; //half_float or float\\r\\nRenderFrameContext.HIGH_PRECISION = 3; //float\\r\\n\\r\\nRenderFrameContext.DEFAULT_PRECISION_WEBGL_TYPE = GL.UNSIGNED_BYTE;\\r\\n\\r\\nRenderFrameContext[\\\"@width\\\"] = { type: \\\"number\\\", step: 1, precision: 0 };\\r\\nRenderFrameContext[\\\"@height\\\"] = { type: \\\"number\\\", step: 1, precision: 0 };\\r\\n\\r\\n//definitions for the GUI\\r\\nRenderFrameContext[\\\"@precision\\\"] = { widget: \\\"combo\\\", values: { \\r\\n\\t\\\"default\\\": RenderFrameContext.DEFAULT_PRECISION, \\r\\n\\t\\\"low\\\": RenderFrameContext.LOW_PRECISION,\\r\\n\\t\\\"medium\\\": RenderFrameContext.MEDIUM_PRECISION,\\r\\n\\t\\\"high\\\": RenderFrameContext.HIGH_PRECISION\\r\\n\\t}\\r\\n};\\r\\n\\r\\nRenderFrameContext[\\\"@format\\\"] = { widget: \\\"combo\\\", values: { \\r\\n\\t\\t\\\"RGB\\\": GL.RGB,\\r\\n\\t\\t\\\"RGBA\\\": GL.RGBA\\r\\n//\\t\\t\\\"R8\\\": GL.LUMINANCE,\\r\\n//\\t\\t\\\"LUMINANCE_ALPHA\\\": GL.LUMINANCE_ALPHA,\\r\\n//\\t\\t\\\"ALPHA\\\": GL.ALPHA\\r\\n\\t}\\r\\n};\\r\\n\\r\\nRenderFrameContext[\\\"@num_extra_textures\\\"] = { type: \\\"number\\\", step: 1, min: 0, max: 4, precision: 0 };\\r\\nRenderFrameContext[\\\"@name\\\"] = { type: \\\"string\\\" };\\r\\n\\r\\nRenderFrameContext.prototype.clear = function()\\r\\n{\\r\\n\\tif(this.name)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < this._textures.length; ++i)\\r\\n\\t\\t\\tdelete LS.ResourcesManager.textures[ this.name + (i > 1 ? i : \\\"\\\") ];\\r\\n\\t\\tif(this._depth_texture)\\r\\n\\t\\t\\tdelete LS.ResourcesManager.textures[ this.name + \\\"_depth\\\"];\\r\\n\\t}\\r\\n\\r\\n\\tthis._fbo = null;\\r\\n\\tthis._textures = [];\\r\\n\\tthis._color_texture = null;\\r\\n\\tthis._depth_textures = null;\\r\\n}\\r\\n\\r\\nRenderFrameContext.prototype.configure = function(o)\\r\\n{\\r\\n\\tthis.width = o.width || 0;\\r\\n\\tthis.height = o.height || 0;\\r\\n\\tthis.format = o.format || GL.RGBA;\\r\\n\\tthis.precision = o.precision || 0;\\r\\n\\tthis.filter_texture = !!o.filter_texture;\\r\\n\\tthis.adjust_aspect = !!o.adjust_aspect;\\r\\n\\tthis.use_depth_texture = !!o.use_depth_texture;\\r\\n\\tthis.use_stencil_buffer = !!o.use_stencil_buffer;\\r\\n\\tthis.num_extra_textures = o.num_extra_textures || 0;\\r\\n\\tthis.name = o.name;\\r\\n\\tthis.clone_after_unbind = !!o.clone_after_unbind;\\r\\n}\\r\\n\\r\\nRenderFrameContext.prototype.serialize = function()\\r\\n{\\r\\n\\treturn {\\r\\n\\t\\twidth: this.width,\\r\\n\\t\\theight: this.height,\\r\\n\\t\\tfilter_texture: this.filter_texture,\\r\\n\\t\\tprecision: this.precision,\\r\\n\\t\\tformat: this.format,\\r\\n\\t\\tadjust_aspect: this.adjust_aspect,\\r\\n\\t\\tuse_depth_texture: this.use_depth_texture,\\r\\n\\t\\tuse_stencil_buffer: this.use_stencil_buffer,\\r\\n\\t\\tnum_extra_textures: this.num_extra_textures,\\r\\n\\t\\tclone_after_unbind: this.clone_after_unbind,\\r\\n\\t\\tname: this.name\\r\\n\\t};\\r\\n}\\r\\n\\r\\nRenderFrameContext.prototype.prepare = function( viewport_width, viewport_height )\\r\\n{\\r\\n\\t//compute the right size for the textures\\r\\n\\tvar final_width = this.width;\\r\\n\\tvar final_height = this.height;\\r\\n\\tif(final_width == 0)\\r\\n\\t\\tfinal_width = viewport_width;\\r\\n\\telse if(final_width < 0)\\r\\n\\t\\tfinal_width = viewport_width >> Math.abs( this.width ); //subsampling\\r\\n\\tif(final_height == 0)\\r\\n\\t\\tfinal_height = viewport_height;\\r\\n\\telse if(final_height < 0)\\r\\n\\t\\tfinal_height = viewport_height >> Math.abs( this.height ); //subsampling\\r\\n\\r\\n\\tvar format = this.format;\\r\\n\\tvar magFilter = this.filter_texture ? gl.LINEAR : gl.NEAREST ;\\r\\n\\tvar type = 0;\\r\\n\\r\\n\\tvar minFilter = gl.LINEAR;\\r\\n\\tif(this.generate_mipmaps && GL.isPowerOfTwo(final_width) && GL.isPowerOfTwo(final_height) )\\r\\n\\t\\tminFilter = gl.LINEAR_MIPMAP_LINEAR;\\r\\n\\tthis._minFilter = minFilter;\\r\\n\\r\\n\\tswitch( this.precision )\\r\\n\\t{\\r\\n\\t\\tcase RenderFrameContext.LOW_PRECISION:\\r\\n\\t\\t\\ttype = gl.UNSIGNED_BYTE; break;\\r\\n\\t\\tcase RenderFrameContext.MEDIUM_PRECISION:\\r\\n\\t\\t\\ttype = gl.HIGH_PRECISION_FORMAT; break; //gl.HIGH_PRECISION_FORMAT is HALF_FLOAT_OES, if not supported then is FLOAT, otherwise is UNSIGNED_BYTE\\r\\n\\t\\tcase RenderFrameContext.HIGH_PRECISION:\\r\\n\\t\\t\\ttype = gl.FLOAT; break;\\r\\n\\t\\tcase RenderFrameContext.DEFAULT_PRECISION:\\r\\n\\t\\t\\ttype = RenderFrameContext.DEFAULT_PRECISION_WEBGL_TYPE; break;\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\ttype = this.precision; break; //used for custom formats\\r\\n\\t}\\r\\n\\r\\n\\t//check support due to weirdeness of webgl 1.0\\r\\n\\tif( type == GL.HALF_FLOAT_OES && !GL.FBO.testSupport( type, format ) )\\r\\n\\t\\tformat = gl.RGBA;\\r\\n\\tif( type == GL.HALF_FLOAT_OES && !GL.FBO.testSupport( type, format ) )\\r\\n\\t\\ttype = gl.FLOAT;\\r\\n\\r\\n\\tvar textures = this._textures;\\r\\n\\r\\n\\t//for the color: check that the texture size matches\\r\\n\\tif( !this._color_texture || \\r\\n\\t\\tthis._color_texture.width != final_width || this._color_texture.height != final_height || \\r\\n\\t\\tthis._color_texture.type != type || this._color_texture.format != format || this._color_texture.minFilter != minFilter )\\r\\n\\t\\tthis._color_texture = new GL.Texture( final_width, final_height, { minFilter: minFilter, magFilter: magFilter, format: format, type: type });\\r\\n\\telse\\r\\n\\t\\tthis._color_texture.setParameter( gl.TEXTURE_MAG_FILTER, magFilter );\\r\\n\\ttextures[0] = this._color_texture;\\r\\n\\r\\n\\t//extra color texture (multibuffer rendering)\\r\\n\\tvar total_extra = Math.min( this.num_extra_textures, 4 );\\r\\n\\t\\r\\n\\t//extra buffers not supported in this webgl context\\r\\n\\tif(gl.webgl_version == 1 && !gl.extensions[\\\"WEBGL_draw_buffers\\\"])\\r\\n\\t\\ttotal_extra = 0;\\r\\n\\r\\n\\tfor(var i = 0; i < total_extra; ++i) //MAX is 4\\r\\n\\t{\\r\\n\\t\\tvar extra_texture = textures[1 + i];\\r\\n\\t\\tif( (!extra_texture || extra_texture.width != final_width || extra_texture.height != final_height || extra_texture.type != type || extra_texture.format != format || extra_texture.minFilter != minFilter) )\\r\\n\\t\\t\\textra_texture = new GL.Texture( final_width, final_height, { minFilter: minFilter, magFilter: magFilter, format: format, type: type });\\r\\n\\t\\telse\\r\\n\\t\\t\\textra_texture.setParameter( gl.TEXTURE_MAG_FILTER, magFilter );\\r\\n\\t\\ttextures[1 + i] = extra_texture;\\r\\n\\t}\\r\\n\\r\\n\\t//for the depth\\r\\n\\tvar depth_format = gl.DEPTH_COMPONENT;\\r\\n\\tvar depth_type = gl.UNSIGNED_INT;\\r\\n\\r\\n\\tif(this.use_stencil_buffer && gl.extensions.WEBGL_depth_texture)\\r\\n\\t{\\r\\n\\t\\tdepth_format = gl.DEPTH_STENCIL;\\r\\n\\t\\tdepth_type = gl.extensions.WEBGL_depth_texture.UNSIGNED_INT_24_8_WEBGL;\\r\\n\\t}\\r\\n\\r\\n\\tif( this.use_depth_texture && \\r\\n\\t\\t(!this._depth_texture || this._depth_texture.width != final_width || this._depth_texture.height != final_height || this._depth_texture.format != depth_format || this._depth_texture.type != depth_type ) && \\r\\n\\t\\tgl.extensions[\\\"WEBGL_depth_texture\\\"] )\\r\\n\\t\\tthis._depth_texture = new GL.Texture( final_width, final_height, { filter: gl.NEAREST, format: depth_format, type: depth_type });\\r\\n\\telse if( !this.use_depth_texture )\\r\\n\\t\\tthis._depth_texture = null;\\r\\n\\r\\n\\t//we will store some extra info in the depth texture for the near and far plane distances\\r\\n\\tif(this._depth_texture)\\r\\n\\t{\\r\\n\\t\\tif(!this._depth_texture.near_far_planes)\\r\\n\\t\\t\\tthis._depth_texture.near_far_planes = vec2.create();\\r\\n\\t}\\r\\n\\r\\n\\t//create FBO\\r\\n\\tif( !this._fbo )\\r\\n\\t\\tthis._fbo = new GL.FBO();\\r\\n\\r\\n\\t//cut extra\\r\\n\\ttextures.length = 1 + total_extra;\\r\\n\\r\\n\\t//assign textures (this will enable the FBO but it will restore the old one after finishing)\\r\\n\\tthis._fbo.stencil = this.use_stencil_buffer;\\r\\n\\tthis._fbo.setTextures( textures, this._depth_texture );\\r\\n\\tthis._version += 1;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Called to bind the rendering to this context, from now on all the render will be stored in the textures inside\\r\\n*\\r\\n* @method enable\\r\\n*/\\r\\nRenderFrameContext.prototype.enable = function( render_settings, viewport, camera )\\r\\n{\\r\\n\\tviewport = viewport || gl.viewport_data;\\r\\n\\r\\n\\t//create FBO and textures (pass width and height of current viewport)\\r\\n\\tthis.prepare( viewport[2], viewport[3] );\\r\\n\\r\\n\\tif(!this._fbo)\\r\\n\\t\\tthrow(\\\"No FBO created in RenderFrameContext\\\");\\r\\n\\r\\n\\t//enable FBO\\r\\n\\tRenderFrameContext.enableFBO( this._fbo, this.adjust_aspect );\\r\\n\\r\\n\\tif(LS.RenderFrameContext.current)\\r\\n\\t\\tRenderFrameContext.stack.push( LS.RenderFrameContext.current );\\r\\n\\tLS.RenderFrameContext.current = this;\\r\\n\\r\\n\\t//set depth info inside the texture\\r\\n\\tcamera = camera || LS.Renderer._current_camera;\\r\\n\\tif(this._depth_texture && camera)\\r\\n\\t{\\r\\n\\t\\tthis._depth_texture.near_far_planes[0] = camera.near;\\r\\n\\t\\tthis._depth_texture.near_far_planes[1] = camera.far;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//we cannot read and write in the same buffer, so we need to clone the textures\\r\\n//done from... ?\\r\\nRenderFrameContext.prototype.cloneBuffers = function()\\r\\n{\\r\\n\\t//we do not call this._fbo.unbind because it will set the previous FBO\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, null );\\r\\n\\r\\n\\t///for every color texture\\r\\n\\tif( this._textures.length )\\r\\n\\t{\\r\\n\\t\\tif(!this._cloned_textures)\\r\\n\\t\\t\\tthis._cloned_textures = [];\\r\\n\\t\\tvar textures = this._textures;\\r\\n\\t\\tthis._cloned_textures.length = textures.length;\\r\\n\\t\\tfor(var i = 0; i < textures.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar texture = textures[i];\\r\\n\\t\\t\\tvar cloned_texture = this._cloned_textures[i];\\r\\n\\t\\t\\tif( !cloned_texture || !cloned_texture.hasSameSize( texture ) || !cloned_texture.hasSameProperties( texture ) )\\r\\n\\t\\t\\t\\tcloned_texture = this._cloned_textures[i] = new GL.Texture( texture.width, texture.height, texture.getProperties() );\\r\\n\\t\\t\\ttexture.copyTo( cloned_texture );\\r\\n\\t\\t\\tif(i == 0)\\r\\n\\t\\t\\t\\tLS.ResourcesManager.textures[\\\":color_buffer\\\" ] = cloned_texture;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//for depth\\r\\n\\tif( this._depth_texture )\\r\\n\\t{\\r\\n\\t\\tvar depth = this._depth_texture;\\r\\n\\t\\tif(!this._cloned_depth_texture || this._cloned_depth_texture.width != depth.width || this._cloned_depth_texture.height != depth.height || !this._cloned_depth_texture.hasSameProperties( depth ) )\\r\\n\\t\\t\\tthis._cloned_depth_texture = new GL.Texture( depth.width, depth.height, depth.getProperties() );\\r\\n\\r\\n\\t\\tdepth.copyTo( this._cloned_depth_texture );\\r\\n\\t\\tLS.ResourcesManager.textures[\\\":depth_buffer\\\" ] = this._cloned_depth_texture;\\r\\n\\t}\\r\\n\\r\\n\\t//rebind FBO\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, this._fbo.handler );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Called to stop rendering to this context\\r\\n*\\r\\n* @method disable\\r\\n*/\\r\\nRenderFrameContext.prototype.disable = function()\\r\\n{\\r\\n\\t//sets some global parameters for aspect and current RFC\\r\\n\\tRenderFrameContext.disableFBO( this._fbo );\\r\\n\\r\\n\\t//if we need to store the textures in the ResourcesManager\\r\\n\\tif(this.name)\\r\\n\\t{\\r\\n\\t\\tvar textures = this._textures;\\r\\n\\t\\tfor(var i = 0; i < textures.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar name = this.name + (i > 0 ? i : \\\"\\\");\\r\\n\\t\\t\\ttextures[i].filename = name;\\r\\n\\t\\t\\tvar final_texture = textures[i];\\r\\n\\r\\n\\t\\t\\t//only clone main color if requested\\r\\n\\t\\t\\tif( this.clone_after_unbind && i === 0 )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( !this._cloned_texture || \\r\\n\\t\\t\\t\\t\\tthis._cloned_texture.width !== final_texture.width || \\r\\n\\t\\t\\t\\t\\tthis._cloned_texture.height !== final_texture.height ||\\r\\n\\t\\t\\t\\t\\tthis._cloned_texture.type !== final_texture.type )\\r\\n\\t\\t\\t\\t\\tthis._cloned_texture = final_texture.clone();\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tfinal_texture.copyTo( this._cloned_texture );\\r\\n\\t\\t\\t\\tfinal_texture = this._cloned_texture;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif( this._minFilter == gl.LINEAR_MIPMAP_LINEAR )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfinal_texture.bind(0);\\r\\n\\t\\t\\t\\tgl.generateMipmap(gl.TEXTURE_2D);\\r\\n\\t\\t\\t\\tfinal_texture.has_mipmaps = true;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tLS.ResourcesManager.textures[ name ] = final_texture;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(this._depth_texture)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar name = this.name + \\\"_depth\\\";\\r\\n\\t\\t\\tthis._depth_texture.filename = name;\\r\\n\\t\\t\\tLS.ResourcesManager.textures[ name ] = this._depth_texture;\\r\\n\\t\\t\\t//LS.ResourcesManager.textures[ \\\":depth\\\" ] = this._depth_texture;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif( RenderFrameContext.stack.length )\\r\\n\\t\\tLS.RenderFrameContext.current = RenderFrameContext.stack.pop();\\r\\n\\telse\\r\\n\\t\\tLS.RenderFrameContext.current = null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the texture containing the data rendered in this context\\r\\n*\\r\\n* @method getColorTexture\\r\\n* @param {number} index the number of the texture (in case there is more than one)\\r\\n* @return {GL.Texture} the texture\\r\\n*/\\r\\nRenderFrameContext.prototype.getColorTexture = function(num)\\r\\n{\\r\\n\\treturn this._textures[ num || 0 ] || null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the depth texture containing the depth data rendered in this context (in case the use_depth_texture is set to true)\\r\\n*\\r\\n* @method getDepthTexture\\r\\n* @return {GL.Texture} the depth texture\\r\\n*/\\r\\nRenderFrameContext.prototype.getDepthTexture = function()\\r\\n{\\r\\n\\treturn this._depth_texture || null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Fills the textures with a flat color\\r\\n* @method clearTextures\\r\\n*/\\r\\nRenderFrameContext.prototype.clearTextures = function()\\r\\n{\\r\\n\\tfor(var i = 0; i < this._textures.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar texture = this._textures[i];\\r\\n\\t\\tif(!texture)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\ttexture.fill([0,0,0,0]);\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//enables the FBO and sets every texture with a flag so it cannot be used during the rendering process\\r\\nRenderFrameContext.enableFBO = function( fbo, adjust_aspect )\\r\\n{\\r\\n\\tfbo.bind( true ); //changes viewport to full FBO size (saves old)\\r\\n\\r\\n\\tLS.Renderer._full_viewport.set( gl.viewport_data );\\r\\n\\tif( adjust_aspect )\\r\\n\\t{\\r\\n\\t\\tfbo._old_aspect = LS.Renderer.global_aspect;\\r\\n\\t\\tLS.Renderer.global_aspect = (gl.canvas.width / gl.canvas.height) / (fbo.color_textures[0].width / fbo.color_textures[0].height);\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tdelete fbo._old_aspect;\\r\\n}\\r\\n\\r\\nRenderFrameContext.disableFBO = function( fbo )\\r\\n{\\r\\n\\tfbo.unbind(); //restores viewport to old saved one\\r\\n\\tLS.Renderer._full_viewport.set( fbo._old_viewport );\\r\\n\\tif( fbo._old_aspect )\\r\\n\\t\\tLS.Renderer.global_aspect = fbo._old_aspect;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Render the context of the context to the viewport (allows to apply FXAA)\\r\\n*\\r\\n* @method show\\r\\n* @param {boolean} use_antialiasing in case you want to render with FXAA antialiasing\\r\\n*/\\r\\nRenderFrameContext.prototype.show = function( use_antialiasing )\\r\\n{\\r\\n\\tvar texture = this._color_texture;\\r\\n\\tif(!use_antialiasing)\\r\\n\\t{\\r\\n\\t\\ttexture.toViewport();\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar viewport = gl.getViewport();\\r\\n\\tvar shader = GL.Shader.getFXAAShader();\\r\\n\\tvar mesh = GL.Mesh.getScreenQuad();\\r\\n\\ttexture.bind(0);\\r\\n\\tshader.uniforms( { u_texture:0, uViewportSize: viewport.subarray(2,4), u_iViewportSize: [1 / texture.width, 1 / texture.height]} ).draw( mesh );\\r\\n}\\r\\n\\r\\n//Resets the current WebGL fbo so it renders to the screen\\r\\nRenderFrameContext.reset = function()\\r\\n{\\r\\n\\tgl.bindFramebuffer( gl.FRAMEBUFFER, null );\\r\\n\\tLS.RenderFrameContext.current = null;\\r\\n\\tLS.RenderFrameContext.stack.length = 0;\\r\\n}\\r\\n\\r\\n\\r\\nLS.RenderFrameContext = RenderFrameContext;\\r\\n\\r\\n///@FILE:../src/render/renderQueue.js\\r\\n///@INFO: BASE\\r\\n//RenderQueue is in charge of storing the RenderInstances that must be rendered\\r\\n//There could be several RenderQueue (for opaque, transparent, overlays, etc)\\r\\n//It works similar to the one in Unity\\r\\nfunction RenderQueue( sort_mode )\\r\\n{\\r\\n\\tthis.sort_mode = sort_mode || LS.RenderQueue.NO_SORT;\\r\\n\\tthis.instances = [];\\r\\n}\\r\\n\\r\\nRenderQueue.prototype.sort = function()\\r\\n{\\r\\n\\tvar func = null;\\r\\n\\tswitch(this.sort_mode)\\r\\n\\t{\\r\\n\\t\\tcase 1: func = LS.RenderQueue.sort_near_to_far_func; break;\\r\\n\\t\\tcase 2: func = LS.RenderQueue.sort_far_to_near_func; break;\\r\\n\\t\\tcase 3: func = LS.RenderQueue.sort_by_priority_func; break;\\r\\n\\t}\\r\\n\\r\\n\\tif(func)\\r\\n\\t\\tthis.instances.sort( func );\\r\\n}\\r\\n\\r\\nRenderQueue.prototype.add = function( ri )\\r\\n{\\r\\n\\tthis.instances.push( ri );\\r\\n}\\r\\n\\r\\nRenderQueue.prototype.clear = function()\\r\\n{\\r\\n\\tthis.instances.length = 0;\\r\\n}\\r\\n\\r\\nRenderQueue.DEFAULT = 0;\\r\\nRenderQueue.BACKGROUND = 5;\\r\\nRenderQueue.GEOMETRY = 10;\\r\\nRenderQueue.TRANSPARENT = 15;\\r\\nRenderQueue.READBACK_COLOR = 20;\\r\\nRenderQueue.OVERLAY = 25;\\r\\n\\r\\nRenderQueue.NO_SORT = 0;\\r\\nRenderQueue.SORT_NEAR_TO_FAR = 1;\\r\\nRenderQueue.SORT_FAR_TO_NEAR = 2;\\r\\nRenderQueue.SORT_BY_PRIORITY = 3;\\r\\n\\r\\nRenderQueue.sort_far_to_near_func = function(a,b) { return b._dist - a._dist; },\\r\\nRenderQueue.sort_near_to_far_func = function(a,b) { return a._dist - b._dist; },\\r\\nRenderQueue.sort_by_priority_func = function(a,b) { return b.priority - a.priority; },\\r\\nRenderQueue.sort_by_priority_and_near_to_far_func = function(a,b) { var r = b.priority - a.priority; return r ? r : (a._dist - b._dist) },\\r\\nRenderQueue.sort_by_priority_and_far_to_near_func = function(a,b) { var r = b.priority - a.priority; return r ? r : (b._dist - a._dist) },\\r\\n\\r\\nLS.RenderQueue = RenderQueue;\\r\\n///@FILE:../src/render/renderer.js\\r\\n///@INFO: BASE\\r\\n\\r\\n//************************************\\r\\n/**\\r\\n* The Renderer is in charge of generating one frame of the scene. Contains all the passes and intermediate functions to create the frame.\\r\\n*\\r\\n* @class Renderer\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\n//passes\\r\\nvar COLOR_PASS = LS.COLOR_PASS = { name: \\\"color\\\", id: 1 };\\r\\nvar SHADOW_PASS = LS.SHADOW_PASS = { name: \\\"shadow\\\", id: 2 };\\r\\nvar PICKING_PASS = LS.PICKING_PASS = { name: \\\"picking\\\", id: 3 };\\r\\n\\r\\n//events\\r\\n\\r\\nEVENT.BEFORE_RENDER = \\\"beforeRender\\\";\\r\\nEVENT.READY_TO_RENDER = \\\"readyToRender\\\";\\r\\nEVENT.RENDER_SHADOWS = \\\"renderShadows\\\";\\r\\nEVENT.AFTER_VISIBILITY = \\\"afterVisibility\\\";\\r\\nEVENT.RENDER_REFLECTIONS = \\\"renderReflections\\\";\\r\\nEVENT.BEFORE_RENDER_MAIN_PASS = \\\"beforeRenderMainPass\\\";\\r\\nEVENT.ENABLE_FRAME_CONTEXT = \\\"enableFrameContext\\\";\\r\\nEVENT.SHOW_FRAME_CONTEXT = \\\"showFrameContext\\\";\\r\\nEVENT.AFTER_RENDER = \\\"afterRender\\\";\\r\\nEVENT.BEFORE_RENDER_FRAME = \\\"beforeRenderFrame\\\";\\r\\nEVENT.BEFORE_RENDER_SCENE = \\\"beforeRenderScene\\\";\\r\\nEVENT.COMPUTE_VISIBILITY = \\\"computeVisibility\\\";\\r\\nEVENT.AFTER_RENDER_SCENE = \\\"afterRenderScene\\\";\\r\\nEVENT.RENDER_HELPERS = \\\"renderHelpers\\\";\\r\\nEVENT.BEFORE_SHOW_FRAME_CONTEXT = \\\"beforeShowFrameContext\\\";\\r\\nEVENT.BEFORE_CAMERA_ENABLED = \\\"beforeCameraEnabled\\\";\\r\\nEVENT.AFTER_CAMERA_ENABLED = \\\"afterCameraEnabled\\\";\\r\\nEVENT.BEFORE_RENDER_INSTANCES = \\\"beforeRenderInstances\\\";\\r\\nEVENT.RENDER_INSTANCES = \\\"renderInstances\\\";\\r\\nEVENT.RENDER_SCREEN_SPACE = \\\"renderScreenSpace\\\";\\r\\nEVENT.AFTER_RENDER_INSTANCES = \\\"afterRenderInstances\\\";\\r\\nEVENT.RENDER_GUI = \\\"renderGUI\\\";\\r\\nEVENT.FILL_SCENE_UNIFORMS = \\\"fillSceneUniforms\\\";\\r\\nEVENT.AFTER_COLLECT_DATA = \\\"afterCollectData\\\";\\r\\nEVENT.PREPARE_MATERIALS = \\\"prepareMaterials\\\";\\r\\n\\r\\nvar Renderer = {\\r\\n\\r\\n\\tdefault_render_settings: new LS.RenderSettings(), //overwritten by the global info or the editor one\\r\\n\\tdefault_material: new LS.StandardMaterial(), //used for objects without material\\r\\n\\r\\n\\tglobal_aspect: 1, //used when rendering to a texture that doesnt have the same aspect as the screen\\r\\n\\tdefault_point_size: 1, //point size in pixels (could be overwritte by render instances)\\r\\n\\r\\n\\trender_profiler: false,\\r\\n\\r\\n\\t_global_viewport: vec4.create(), //the viewport we have available to render the full frame (including subviewports), usually is the 0,0,gl.canvas.width,gl.canvas.height\\r\\n\\t_full_viewport: vec4.create(), //contains info about the full viewport available to render (current texture size or canvas size)\\r\\n\\r\\n\\t//temporal info during rendering\\r\\n\\t_current_scene: null,\\r\\n\\t_current_render_settings: null,\\r\\n\\t_current_camera: null,\\r\\n\\t_current_target: null, //texture where the image is being rendered\\r\\n\\t_current_pass: COLOR_PASS, //object containing info about the pass\\r\\n\\t_global_textures: {}, //used to speed up fetching global textures\\r\\n\\t_global_shader_blocks: [], //used to add extra shaderblocks to all objects in the scene (it gets reseted every frame)\\r\\n\\t_global_shader_blocks_flags: 0, \\r\\n\\t_reverse_faces: false,\\r\\n\\t_in_player: true, //true if rendering in the player\\r\\n\\r\\n\\t_queues: [], //render queues in order\\r\\n\\r\\n\\t_main_camera: null,\\r\\n\\r\\n\\t_visible_cameras: null,\\r\\n\\t_visible_lights: null,\\r\\n\\t_visible_instances: null,\\r\\n\\t_visible_materials: [],\\r\\n\\t_near_lights: [],\\r\\n\\t_active_samples: [],\\r\\n\\r\\n\\t//stats\\r\\n\\t_frame_time: 0,\\r\\n\\t_frame_cpu_time: 0,\\r\\n\\t_rendercalls: 0, //calls to instance.render\\r\\n\\t_rendered_instances: 0, //instances processed\\r\\n\\t_rendered_passes: 0,\\r\\n\\t_frame: 0,\\r\\n\\t_last_time: 0,\\r\\n\\r\\n\\t//using timer queries\\r\\n\\tgpu_times: {\\r\\n\\t\\ttotal: 0,\\r\\n\\t\\tshadows: 0,\\r\\n\\t\\treflections: 0,\\r\\n\\t\\tmain: 0,\\r\\n\\t\\tpostpo: 0,\\r\\n\\t\\tgui: 0\\r\\n\\t},\\r\\n\\r\\n\\t_timer_queries: {},\\r\\n\\t_waiting_queries: false,\\r\\n\\r\\n\\t//settings\\r\\n\\t_collect_frequency: 1, //used to reuse info (WIP)\\r\\n\\r\\n\\t//reusable locals\\r\\n\\t_view_matrix: mat4.create(),\\r\\n\\t_projection_matrix: mat4.create(),\\r\\n\\t_viewprojection_matrix: mat4.create(),\\r\\n\\t_2Dviewprojection_matrix: mat4.create(),\\r\\n\\r\\n\\t_temp_matrix: mat4.create(),\\r\\n\\t_temp_cameye: vec3.create(),\\r\\n\\t_identity_matrix: mat4.create(),\\r\\n\\t_uniforms: {},\\r\\n\\t_samplers: [],\\r\\n\\t_instancing_data: [],\\r\\n\\r\\n\\t//safety\\r\\n\\t_is_rendering_frame: false,\\r\\n\\t_ignore_reflection_probes: false,\\r\\n\\r\\n\\t//debug\\r\\n\\tallow_textures: true,\\r\\n\\t_sphere_mesh: null,\\r\\n\\t_debug_instance: null,\\r\\n\\r\\n\\t//fixed texture slots for global textures\\r\\n\\tSHADOWMAP_TEXTURE_SLOT: 7,\\r\\n\\tENVIRONMENT_TEXTURE_SLOT: 6,\\r\\n\\tIRRADIANCE_TEXTURE_SLOT: 5,\\r\\n\\tLIGHTPROJECTOR_TEXTURE_SLOT: 4,\\r\\n\\tLIGHTEXTRA_TEXTURE_SLOT: 3,\\r\\n\\r\\n\\t//used in special cases\\r\\n\\tBONES_TEXTURE_SLOT: 3,\\r\\n\\tMORPHS_TEXTURE_SLOT: 2,\\r\\n\\tMORPHS_TEXTURE2_SLOT: 1,\\r\\n\\r\\n\\t//called from...\\r\\n\\tinit: function()\\r\\n\\t{\\r\\n\\t\\t//create some useful textures: this is used in case a texture is missing\\r\\n\\t\\tthis._black_texture = new GL.Texture(1,1, { pixel_data: [0,0,0,255] });\\r\\n\\t\\tthis._gray_texture = new GL.Texture(1,1, { pixel_data: [128,128,128,255] });\\r\\n\\t\\tthis._white_texture = new GL.Texture(1,1, { pixel_data: [255,255,255,255] });\\r\\n\\t\\tthis._normal_texture = new GL.Texture(1,1, { pixel_data: [128,128,255,255] });\\r\\n\\t\\tthis._missing_texture = this._gray_texture;\\r\\n\\t\\tvar internal_textures = [ this._black_texture, this._gray_texture, this._white_texture, this._normal_texture, this._missing_texture ];\\r\\n\\t\\tinternal_textures.forEach(function(t){ t._is_internal = true; });\\r\\n\\t\\tLS.ResourcesManager.textures[\\\":black\\\"] = this._black_texture;\\r\\n\\t\\tLS.ResourcesManager.textures[\\\":gray\\\"] = this._gray_texture;\\r\\n\\t\\tLS.ResourcesManager.textures[\\\":white\\\"] = this._white_texture;\\r\\n\\t\\tLS.ResourcesManager.textures[\\\":flatnormal\\\"] = this._normal_texture;\\r\\n\\r\\n\\t\\t//some global meshes could be helpful: used for irradiance probes\\r\\n\\t\\tthis._sphere_mesh = GL.Mesh.sphere({ size:1, detail:32 });\\r\\n\\r\\n\\t\\t//draw helps rendering debug stuff\\r\\n\\t\\tif(LS.Draw)\\r\\n\\t\\t{\\r\\n\\t\\t\\tLS.Draw.init();\\r\\n\\t\\t\\tLS.Draw.onRequestFrame = function() { LS.GlobalScene.requestFrame(); }\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//enable webglCanvas lib so it is easy to render in 2D\\r\\n\\t\\tif(global.enableWebGLCanvas && !gl.canvas.canvas2DtoWebGL_enabled)\\r\\n\\t\\t\\tglobal.enableWebGLCanvas( gl.canvas );\\r\\n\\r\\n\\t\\t// we use fixed slots to avoid changing texture slots all the time\\r\\n\\t\\t// from more common to less (to avoid overlappings with material textures)\\r\\n\\t\\t// the last slot is reserved for litegl binding stuff\\r\\n\\t\\tvar max_texture_units = this._max_texture_units = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );\\r\\n\\t\\tthis.SHADOWMAP_TEXTURE_SLOT = max_texture_units - 2;\\r\\n\\t\\tthis.ENVIRONMENT_TEXTURE_SLOT = max_texture_units - 3;\\r\\n\\t\\tthis.IRRADIANCE_TEXTURE_SLOT = max_texture_units - 4;\\r\\n\\r\\n\\t\\tthis.LIGHTPROJECTOR_TEXTURE_SLOT = max_texture_units - 5;\\r\\n\\t\\tthis.LIGHTEXTRA_TEXTURE_SLOT = max_texture_units - 6;\\r\\n\\r\\n\\t\\tthis.BONES_TEXTURE_SLOT = max_texture_units - 7;\\r\\n\\t\\tthis.MORPHS_TEXTURE_SLOT = max_texture_units - 8;\\r\\n\\t\\tthis.MORPHS_TEXTURE2_SLOT = max_texture_units - 9;\\r\\n\\r\\n\\t\\tthis._active_samples.length = max_texture_units;\\r\\n\\r\\n\\t\\t//set render queues\\r\\n\\t\\tthis.createRenderQueue( LS.RenderQueue.BACKGROUND, LS.RenderQueue.NO_SORT );\\r\\n\\t\\tthis.createRenderQueue( LS.RenderQueue.GEOMETRY, LS.RenderQueue.SORT_NEAR_TO_FAR );\\r\\n\\t\\tthis.createRenderQueue( LS.RenderQueue.TRANSPARENT, LS.RenderQueue.SORT_FAR_TO_NEAR );\\r\\n\\r\\n\\t\\t//very special queue that must change the renderframecontext before start rendering anything\\r\\n\\t\\tthis.createRenderQueue( LS.RenderQueue.READBACK_COLOR, LS.RenderQueue.SORT_FAR_TO_NEAR, {\\r\\n\\t\\t\\tonStart: function( render_settings, pass ){\\r\\n\\t\\t\\t\\tif(pass.name === \\\"color\\\")\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif( LS.RenderFrameContext.current )\\r\\n\\t\\t\\t\\t\\t\\tLS.RenderFrameContext.current.cloneBuffers();\\r\\n\\t\\t\\t\\t\\t//if it is a cubemap where we are rendering, we cannot clone that face easily, too much work, so...\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.createRenderQueue( LS.RenderQueue.OVERLAY, LS.RenderQueue.NO_SORT );\\r\\n\\t\\tthis._full_viewport.set([0,0,gl.drawingBufferWidth,gl.drawingBufferHeight]);\\r\\n\\r\\n\\t\\tthis._uniforms.u_viewport = gl.viewport_data;\\r\\n\\t\\tthis._uniforms.environment_texture = this.ENVIRONMENT_TEXTURE_SLOT;\\r\\n\\t\\tthis._uniforms.u_clipping_plane = vec4.create();\\r\\n\\t},\\r\\n\\r\\n\\treset: function()\\r\\n\\t{\\r\\n\\t},\\r\\n\\r\\n\\t//used to clear the state\\r\\n\\tresetState: function()\\r\\n\\t{\\r\\n\\t\\tthis._is_rendering_frame = false;\\r\\n\\t\\tthis._reverse_faces = false;\\r\\n\\t},\\r\\n\\r\\n\\t//used to store which is the current full viewport available (could be different from the canvas in case is a FBO or the camera has a partial viewport)\\r\\n\\tsetFullViewport: function(x,y,w,h)\\r\\n\\t{\\r\\n\\t\\tif(arguments.length == 0) //restore\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._full_viewport[0] = this._full_viewport[1] = 0;\\r\\n\\t\\t\\tthis._full_viewport[2] = gl.drawingBufferWidth;\\r\\n\\t\\t\\tthis._full_viewport[3] = gl.drawingBufferHeight;\\r\\n\\t\\t}\\r\\n\\t\\telse if(x.constructor === Number)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._full_viewport[0] = x; this._full_viewport[1] = y; this._full_viewport[2] = w; this._full_viewport[3] = h;\\r\\n\\t\\t}\\r\\n\\t\\telse if(x.length)\\r\\n\\t\\t\\tthis._full_viewport.set(x);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders the current scene to the screen\\r\\n\\t* Many steps are involved, from gathering info from the scene tree, generating shadowmaps, setup FBOs, render every camera\\r\\n\\t* If you want to change the rendering pipeline, do not overwrite this function, try to understand it first, otherwise you will miss lots of features\\r\\n\\t*\\r\\n\\t* @method render\\r\\n\\t* @param {Scene} scene\\r\\n\\t* @param {RenderSettings} render_settings\\r\\n\\t* @param {Array} [cameras=null] if no cameras are specified the cameras are taken from the scene\\r\\n\\t*/\\r\\n\\trender: function( scene, render_settings, cameras )\\r\\n\\t{\\r\\n\\t\\tscene = scene || LS.GlobalScene;\\r\\n\\r\\n\\t\\tif( this._is_rendering_frame )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"Last frame didn't finish and a new one was issued. Remember that you cannot call LS.Renderer.render from an event dispatched during the render, this would cause a recursive loop. Call LS.Renderer.reset() to clear from an error.\\\");\\r\\n\\t\\t\\t//this._is_rendering_frame = false; //for safety, we setting to false \\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//init frame\\r\\n\\t\\tthis._is_rendering_frame = true;\\r\\n\\t\\trender_settings = render_settings || this.default_render_settings;\\r\\n\\t\\tthis._current_render_settings = render_settings;\\r\\n\\t\\tthis._current_scene = scene;\\r\\n\\t\\tthis._main_camera = cameras ? cameras[0] : null;\\r\\n\\t\\tscene._frame += 1; //done at the beginning just in case it crashes\\r\\n\\t\\tthis._frame += 1;\\r\\n\\t\\tscene._must_redraw = false;\\r\\n\\r\\n\\t\\tvar start_time = getTime();\\r\\n\\t\\tthis._frame_time = start_time - this._last_time;\\r\\n\\t\\tthis._last_time = start_time;\\r\\n\\t\\tthis._rendercalls = 0;\\r\\n\\t\\tthis._rendered_instances = 0;\\r\\n\\t\\tthis._rendered_passes = 0;\\r\\n\\t\\tthis._global_shader_blocks.length = 0;\\r\\n\\t\\tthis._global_shader_blocks_flags = 0;\\r\\n\\t\\tfor(var i in this._global_textures)\\r\\n\\t\\t\\tthis._global_textures[i] = null;\\r\\n\\t\\tif(!this._current_pass)\\r\\n\\t\\t\\tthis._current_pass = COLOR_PASS;\\r\\n\\t\\tthis._reverse_faces = false;\\r\\n\\r\\n\\t\\t//extract info about previous frame\\r\\n\\t\\tthis.resolveQueries();\\r\\n\\r\\n\\t\\t//to restore from a possible exception (not fully tested, remove if problem)\\r\\n\\t\\tif(!render_settings.ignore_reset)\\r\\n\\t\\t\\tLS.RenderFrameContext.reset();\\r\\n\\r\\n\\t\\tif(gl.canvas.canvas2DtoWebGL_enabled)\\r\\n\\t\\t\\tgl.resetTransform(); //reset \\r\\n\\r\\n\\t\\tLS.GUI.ResetImmediateGUI(true);//just to let the GUI ready\\r\\n\\r\\n\\t\\t//force fullscreen viewport\\r\\n\\t\\tif( !render_settings.keep_viewport )\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight );\\r\\n\\t\\t\\tthis.setFullViewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); //assign this as the full available viewport\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.setFullViewport( gl.viewport_data );\\r\\n\\t\\tthis._global_viewport.set( gl.viewport_data );\\r\\n\\r\\n\\t\\t//Event: beforeRender used in actions that could affect which info is collected for the rendering\\r\\n\\t\\tthis.startGPUQuery( \\\"beforeRender\\\" );\\r\\n\\t\\tLEvent.trigger( scene, EVENT.BEFORE_RENDER, render_settings );\\r\\n\\t\\tthis.endGPUQuery();\\r\\n\\r\\n\\t\\t//get render instances, cameras, lights, materials and all rendering info ready (computeVisibility)\\r\\n\\t\\tthis.processVisibleData( scene, render_settings, cameras );\\r\\n\\r\\n\\t\\t//Define the main camera, the camera that should be the most important (used for LOD info, or shadowmaps)\\r\\n\\t\\tcameras = cameras && cameras.length ? cameras : this._visible_cameras;\\r\\n\\t\\tif(cameras.length == 0)\\r\\n\\t\\t\\tthrow(\\\"no cameras\\\");\\r\\n\\t\\tthis._visible_cameras = cameras; //the cameras being rendered\\r\\n\\t\\tthis._main_camera = cameras[0];\\r\\n\\r\\n\\t\\t//Event: readyToRender when we have all the info to render\\r\\n\\t\\tLEvent.trigger( scene, EVENT.READY_TO_RENDER, render_settings );\\r\\n\\r\\n\\t\\t//remove the lights that do not lay in front of any camera (this way we avoid creating shadowmaps)\\r\\n\\t\\t//TODO\\r\\n\\r\\n\\t\\t//Event: renderShadowmaps helps to generate shadowMaps that need some camera info (which could be not accessible during processVisibleData)\\r\\n\\t\\tthis.startGPUQuery(\\\"shadows\\\");\\r\\n\\t\\tLEvent.trigger(scene, EVENT.RENDER_SHADOWS, render_settings );\\r\\n\\t\\tthis.endGPUQuery();\\r\\n\\r\\n\\t\\t//Event: afterVisibility allows to cull objects according to the main camera\\r\\n\\t\\tLEvent.trigger(scene, EVENT.AFTER_VISIBILITY, render_settings );\\r\\n\\r\\n\\t\\t//Event: renderReflections in case some realtime reflections are needed, this is the moment to render them inside textures\\r\\n\\t\\tthis.startGPUQuery(\\\"reflections\\\");\\r\\n\\t\\tLEvent.trigger(scene, EVENT.RENDER_REFLECTIONS, render_settings );\\r\\n\\t\\tthis.endGPUQuery();\\r\\n\\r\\n\\t\\t//Event: beforeRenderMainPass in case a last step is missing\\r\\n\\t\\tLEvent.trigger(scene, EVENT.BEFORE_RENDER_MAIN_PASS, render_settings );\\r\\n\\r\\n\\t\\t//enable global FX context\\r\\n\\t\\tif(render_settings.render_fx)\\r\\n\\t\\t\\tLEvent.trigger( scene, EVENT.ENABLE_FRAME_CONTEXT, render_settings );\\r\\n\\r\\n\\t\\t//render all cameras\\r\\n\\t\\tthis.renderFrameCameras( cameras, render_settings );\\r\\n\\r\\n\\t\\t//keep original viewport\\r\\n\\t\\tif( render_settings.keep_viewport )\\r\\n\\t\\t\\tgl.setViewport( this._global_viewport );\\r\\n\\r\\n\\t\\t//disable and show final FX context\\r\\n\\t\\tif(render_settings.render_fx)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.startGPUQuery(\\\"postpo\\\");\\r\\n\\t\\t\\tLEvent.trigger( scene, EVENT.SHOW_FRAME_CONTEXT, render_settings );\\r\\n\\t\\t\\tthis.endGPUQuery();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//renderGUI\\r\\n\\t\\tthis.startGPUQuery(\\\"gui\\\");\\r\\n\\t\\tthis.renderGUI( render_settings );\\r\\n\\t\\tthis.endGPUQuery();\\r\\n\\r\\n\\t\\t//profiling must go here\\r\\n\\t\\tthis._frame_cpu_time = getTime() - start_time;\\r\\n\\t\\tif( LS.Draw ) //developers may decide not to include LS.Draw\\r\\n\\t\\t\\tthis._rendercalls += LS.Draw._rendercalls; LS.Draw._rendercalls = 0; //stats are not centralized\\r\\n\\r\\n\\t\\t//Event: afterRender to give closure to some actions\\r\\n\\t\\tLEvent.trigger( scene, EVENT.AFTER_RENDER, render_settings ); \\r\\n\\t\\tthis._is_rendering_frame = false;\\r\\n\\r\\n\\t\\t//coroutines\\r\\n\\t\\tLS.triggerCoroutines(\\\"render\\\");\\r\\n\\r\\n\\t\\tif(this.render_profiler)\\r\\n\\t\\t\\tthis.renderProfiler();\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Calls renderFrame of every camera in the cameras list (triggering the appropiate events)\\r\\n\\t*\\r\\n\\t* @method renderFrameCameras\\r\\n\\t* @param {Array} cameras\\r\\n\\t* @param {RenderSettings} render_settings\\r\\n\\t*/\\r\\n\\trenderFrameCameras: function( cameras, render_settings )\\r\\n\\t{\\r\\n\\t\\tvar scene = this._current_scene;\\r\\n\\r\\n\\t\\t//for each camera\\r\\n\\t\\tfor(var i = 0; i < cameras.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar current_camera = cameras[i];\\r\\n\\r\\n\\t\\t\\tLEvent.trigger(scene, EVENT.BEFORE_RENDER_FRAME, render_settings );\\r\\n\\t\\t\\tLEvent.trigger(current_camera, EVENT.BEFORE_RENDER_FRAME, render_settings );\\r\\n\\t\\t\\tLEvent.trigger(current_camera, EVENT.ENABLE_FRAME_CONTEXT, render_settings );\\r\\n\\r\\n\\t\\t\\t//main render\\r\\n\\t\\t\\tthis.startGPUQuery(\\\"main\\\");\\r\\n\\t\\t\\tthis.renderFrame( current_camera, render_settings ); \\r\\n\\t\\t\\tthis.endGPUQuery();\\r\\n\\r\\n\\t\\t\\t//show buffer on the screen\\r\\n\\t\\t\\tthis.startGPUQuery(\\\"postpo\\\");\\r\\n\\t\\t\\tLEvent.trigger(current_camera, EVENT.SHOW_FRAME_CONTEXT, render_settings );\\r\\n\\t\\t\\tLEvent.trigger(current_camera, EVENT.AFTER_RENDER_FRAME, render_settings );\\r\\n\\t\\t\\tLEvent.trigger(scene, EVENT.AFTER_RENDER_FRAME, render_settings );\\r\\n\\t\\t\\tthis.endGPUQuery();\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* renders the view from one camera to the current viewport (could be the screen or a texture)\\r\\n\\t*\\r\\n\\t* @method renderFrame\\r\\n\\t* @param {Camera} camera \\r\\n\\t* @param {Object} render_settings [optional]\\r\\n\\t* @param {Scene} scene [optional] this can be passed when we are rendering a different scene from LS.GlobalScene (used in renderMaterialPreview)\\r\\n\\t*/\\r\\n\\trenderFrame: function ( camera, render_settings, scene )\\r\\n\\t{\\r\\n\\t\\trender_settings = render_settings || this.default_render_settings;\\r\\n\\r\\n\\t\\t//get all the data\\r\\n\\t\\tif(scene) //in case we use another scene than the default one\\r\\n\\t\\t{\\r\\n\\t\\t\\tscene._frame++;\\r\\n\\t\\t\\tthis.processVisibleData( scene, render_settings );\\r\\n\\t\\t}\\r\\n\\t\\tthis._current_scene = scene = scene || this._current_scene; //ugly, I know\\r\\n\\r\\n\\t\\t//set as active camera and set viewport\\r\\n\\t\\tthis.enableCamera( camera, render_settings, render_settings.skip_viewport, scene ); \\r\\n\\r\\n\\t\\t//compute the rendering order\\r\\n\\t\\tthis.sortRenderQueues( camera, render_settings );\\r\\n\\r\\n\\t\\t//clear buffer\\r\\n\\t\\tthis.clearBuffer( camera, render_settings );\\r\\n\\r\\n\\t\\t//send before events\\r\\n\\t\\tLEvent.trigger(scene, EVENT.BEFORE_RENDER_SCENE, camera );\\r\\n\\t\\tLEvent.trigger(this, EVENT.BEFORE_RENDER_SCENE, camera );\\r\\n\\r\\n\\t\\t//in case the user wants to filter instances\\r\\n\\t\\tLEvent.trigger(this, EVENT.COMPUTE_VISIBILITY, this._visible_instances );\\r\\n\\r\\n\\t\\t//here we render all the instances\\r\\n\\t\\tthis.renderInstances( render_settings, this._visible_instances );\\r\\n\\r\\n\\t\\t//send after events\\r\\n\\t\\tLEvent.trigger( scene, EVENT.AFTER_RENDER_SCENE, camera );\\r\\n\\t\\tLEvent.trigger( this, EVENT.AFTER_RENDER_SCENE, camera );\\r\\n\\t\\tif(this.onRenderScene)\\r\\n\\t\\t\\tthis.onRenderScene( camera, render_settings, scene);\\r\\n\\r\\n\\t\\t//render helpers (guizmos)\\r\\n\\t\\tif(render_settings.render_helpers)\\r\\n\\t\\t\\tLEvent.trigger(this, EVENT.RENDER_HELPERS, camera );\\r\\n\\t},\\r\\n\\r\\n\\t//shows a RenderFrameContext to the viewport (warning, some components may do it bypassing this function)\\r\\n\\tshowRenderFrameContext: function( render_frame_context, camera )\\r\\n\\t{\\r\\n\\t\\t//if( !this._current_render_settings.onPlayer)\\r\\n\\t\\t//\\treturn;\\r\\n\\t\\tLEvent.trigger(this, EVENT.BEFORE_SHOW_FRAME_CONTEXT, render_frame_context );\\r\\n\\t\\trender_frame_context.show();\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Sets camera as the current camera, sets the viewport according to camera info, updates matrices, and prepares LS.Draw\\r\\n\\t*\\r\\n\\t* @method enableCamera\\r\\n\\t* @param {Camera} camera\\r\\n\\t* @param {RenderSettings} render_settings\\r\\n\\t*/\\r\\n\\tenableCamera: function(camera, render_settings, skip_viewport, scene )\\r\\n\\t{\\r\\n\\t\\tscene = scene || this._current_scene || LS.GlobalScene;\\r\\n\\r\\n\\t\\tLEvent.trigger( camera, EVENT.BEFORE_CAMERA_ENABLED, render_settings );\\r\\n\\t\\tLEvent.trigger( scene, EVENT.BEFORE_CAMERA_ENABLED, camera );\\r\\n\\r\\n\\t\\t//assign viewport manually (shouldnt use camera.getLocalViewport to unify?)\\r\\n\\t\\tvar startx = this._full_viewport[0];\\r\\n\\t\\tvar starty = this._full_viewport[1];\\r\\n\\t\\tvar width = this._full_viewport[2];\\r\\n\\t\\tvar height = this._full_viewport[3];\\r\\n\\t\\tif(width == 0 && height == 0)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"enableCamera: full viewport was 0, assigning to full viewport\\\");\\r\\n\\t\\t\\twidth = gl.viewport_data[2];\\r\\n\\t\\t\\theight = gl.viewport_data[3];\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar final_x = Math.floor(width * camera._viewport[0] + startx);\\r\\n\\t\\tvar final_y = Math.floor(height * camera._viewport[1] + starty);\\r\\n\\t\\tvar final_width = Math.ceil(width * camera._viewport[2]);\\r\\n\\t\\tvar final_height = Math.ceil(height * camera._viewport[3]);\\r\\n\\r\\n\\t\\tif(!skip_viewport)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//force fullscreen viewport?\\r\\n\\t\\t\\tif(render_settings && render_settings.ignore_viewports )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcamera.final_aspect = this.global_aspect * camera._aspect * (width / height);\\r\\n\\t\\t\\t\\tgl.viewport( this._full_viewport[0], this._full_viewport[1], this._full_viewport[2], this._full_viewport[3] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcamera.final_aspect = this.global_aspect * camera._aspect * (final_width / final_height); //what if we want to change the aspect?\\r\\n\\t\\t\\t\\tgl.viewport( final_x, final_y, final_width, final_height );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tcamera._last_viewport_in_pixels.set( gl.viewport_data );\\r\\n\\r\\n\\t\\t//recompute the matrices (view,proj and viewproj)\\r\\n\\t\\tcamera.updateMatrices();\\r\\n\\r\\n\\t\\t//store matrices locally\\r\\n\\t\\tmat4.copy( this._view_matrix, camera._view_matrix );\\r\\n\\t\\tmat4.copy( this._projection_matrix, camera._projection_matrix );\\r\\n\\t\\tmat4.copy( this._viewprojection_matrix, camera._viewprojection_matrix );\\r\\n\\r\\n\\t\\t//safety in case something went wrong in the camera\\r\\n\\t\\tfor(var i = 0; i < 16; ++i)\\r\\n\\t\\t\\tif( isNaN( this._viewprojection_matrix[i] ) )\\r\\n\\t\\t\\t\\tconsole.warn(\\\"warning: viewprojection matrix contains NaN when enableCamera is used\\\");\\r\\n\\r\\n\\t\\t//2D Camera: TODO: MOVE THIS SOMEWHERE ELSE\\r\\n\\t\\tmat4.ortho( this._2Dviewprojection_matrix, -1, 1, -1, 1, 1, -1 );\\r\\n\\r\\n\\t\\t//set as the current camera\\r\\n\\t\\tthis._current_camera = camera;\\r\\n\\r\\n\\t\\t//Draw allows to render debug info easily\\r\\n\\t\\tif(LS.Draw)\\r\\n\\t\\t{\\r\\n\\t\\t\\tLS.Draw.reset(); //clear \\r\\n\\t\\t\\tLS.Draw.setCamera( camera );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLEvent.trigger( camera, EVENT.AFTER_CAMERA_ENABLED, render_settings );\\r\\n\\t\\tLEvent.trigger( scene, EVENT.AFTER_CAMERA_ENABLED, camera ); //used to change stuff according to the current camera (reflection textures)\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the camera active\\r\\n\\t*\\r\\n\\t* @method getCurrentCamera\\r\\n\\t* @return {Camera} camera\\r\\n\\t*/\\r\\n\\tgetCurrentCamera: function()\\r\\n\\t{\\r\\n\\t\\treturn this._current_camera;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* clear color using camera info ( background color, viewport scissors, clear depth, etc )\\r\\n\\t*\\r\\n\\t* @method clearBuffer\\r\\n\\t* @param {Camera} camera\\r\\n\\t* @param {LS.RenderSettings} render_settings\\r\\n\\t*/\\r\\n\\tclearBuffer: function( camera, render_settings )\\r\\n\\t{\\r\\n\\t\\tif( render_settings.ignore_clear || (!camera.clear_color && !camera.clear_depth) )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//scissors test for the gl.clear, otherwise the clear affects the full viewport\\r\\n\\t\\tgl.scissor( gl.viewport_data[0], gl.viewport_data[1], gl.viewport_data[2], gl.viewport_data[3] );\\r\\n\\t\\tgl.enable(gl.SCISSOR_TEST);\\r\\n\\r\\n\\t\\t//clear color buffer \\r\\n\\t\\tgl.colorMask( true, true, true, true );\\r\\n\\t\\tgl.clearColor( camera.background_color[0], camera.background_color[1], camera.background_color[2], camera.background_color[3] );\\r\\n\\r\\n\\t\\t//clear depth buffer\\r\\n\\t\\tgl.depthMask( true );\\r\\n\\r\\n\\t\\t//to clear the stencil\\r\\n\\t\\tgl.enable( gl.STENCIL_TEST );\\r\\n\\t\\tgl.clearStencil( 0x0 );\\r\\n\\r\\n\\t\\t//do the clearing\\r\\n\\t\\tgl.clear( ( camera.clear_color ? gl.COLOR_BUFFER_BIT : 0) | (camera.clear_depth ? gl.DEPTH_BUFFER_BIT : 0) | gl.STENCIL_BUFFER_BIT );\\r\\n\\r\\n\\t\\tgl.disable( gl.SCISSOR_TEST );\\r\\n\\t\\tgl.disable( gl.STENCIL_TEST );\\r\\n\\t},\\r\\n\\r\\n\\tsortRenderQueues: function( camera, render_settings )\\r\\n\\t{\\r\\n\\t\\tvar instances = this._visible_instances;\\r\\n\\t\\tif(!instances)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//compute distance to camera\\r\\n\\t\\tvar camera_eye = camera.getEye( this._temp_cameye );\\r\\n\\t\\tfor(var i = 0, l = instances.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar instance = instances[i];\\r\\n\\t\\t\\tif(instance)\\r\\n\\t\\t\\t\\tinstance._dist = vec3.dist( instance.center, camera_eye );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//sort queues\\r\\n\\t\\tfor(var i = 0, l = this._queues.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar queue = this._queues[i];\\r\\n\\t\\t\\tif(!queue || !queue.sort_mode)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tqueue.sort();\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* To set gl state to a known and constant state in every render pass\\r\\n\\t*\\r\\n\\t* @method resetGLState\\r\\n\\t* @param {RenderSettings} render_settings\\r\\n\\t*/\\r\\n\\tresetGLState: function( render_settings )\\r\\n\\t{\\r\\n\\t\\trender_settings = render_settings || this._current_render_settings;\\r\\n\\r\\n\\t\\t//maybe we should use this function instead\\r\\n\\t\\t//LS.RenderState.reset(); \\r\\n\\r\\n\\t\\tgl.enable( gl.CULL_FACE );\\r\\n\\t\\tgl.frontFace(gl.CCW);\\r\\n\\r\\n\\t\\tgl.colorMask(true,true,true,true);\\r\\n\\r\\n\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.depthFunc( gl.LESS );\\r\\n\\t\\tgl.depthMask(true);\\r\\n\\r\\n\\t\\tgl.disable( gl.BLEND );\\r\\n\\t\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );\\r\\n\\r\\n\\t\\tgl.disable( gl.STENCIL_TEST );\\r\\n\\t\\tgl.stencilMask( 0xFF );\\r\\n\\t\\tgl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP );\\r\\n\\t\\tgl.stencilFunc( gl.ALWAYS, 1, 0xFF );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Calls the render method for every RenderInstance (it also takes into account events and frustrum culling)\\r\\n\\t*\\r\\n\\t* @method renderInstances\\r\\n\\t* @param {RenderSettings} render_settings\\r\\n\\t* @param {Array} instances array of RIs, if not specified the last visible_instances are rendered\\r\\n\\t*/\\r\\n\\trenderInstances: function( render_settings, instances, scene )\\r\\n\\t{\\r\\n\\t\\tscene = scene || this._current_scene;\\r\\n\\t\\tif(!scene)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"LS.Renderer.renderInstances: no scene found in LS.Renderer._current_scene\\\");\\r\\n\\t\\t\\treturn 0;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._rendered_passes += 1;\\r\\n\\r\\n\\t\\tvar pass = this._current_pass;\\r\\n\\t\\tvar camera = this._current_camera;\\r\\n\\t\\tvar camera_index_flag = camera._rendering_index != -1 ? (1<<(camera._rendering_index)) : 0;\\r\\n\\t\\tvar apply_frustum_culling = render_settings.frustum_culling;\\r\\n\\t\\tvar frustum_planes = camera.updateFrustumPlanes();\\r\\n\\t\\tvar layers_filter = camera.layers & render_settings.layers;\\r\\n\\t\\tvar instancing_supported = gl.webgl_version > 1 || gl.extensions[\\\"ANGLE_instanced_arrays\\\"];\\r\\n\\r\\n\\t\\tLEvent.trigger( scene, EVENT.BEFORE_RENDER_INSTANCES, render_settings );\\r\\n\\t\\t//scene.triggerInNodes( EVENT.BEFORE_RENDER_INSTANCES, render_settings );\\r\\n\\r\\n\\t\\t//reset state of everything!\\r\\n\\t\\tthis.resetGLState( render_settings );\\r\\n\\r\\n\\t\\tLEvent.trigger( scene, EVENT.RENDER_INSTANCES, render_settings );\\r\\n\\t\\tLEvent.trigger( this, EVENT.RENDER_INSTANCES, render_settings );\\r\\n\\r\\n\\t\\t//reset again!\\r\\n\\t\\tthis.resetGLState( render_settings );\\r\\n\\r\\n\\t\\t/*\\r\\n\\t\\tvar render_instance_func = pass.render_instance;\\r\\n\\t\\tif(!render_instance_func)\\r\\n\\t\\t\\treturn 0;\\r\\n\\t\\t*/\\r\\n\\r\\n\\t\\tvar render_instances = instances || this._visible_instances;\\r\\n\\r\\n\\t\\t//global samplers\\r\\n\\t\\tthis.bindSamplers( this._samplers );\\r\\n\\r\\n\\t\\tvar instancing_data = this._instancing_data;\\r\\n\\r\\n\\r\\n\\t\\t//compute visibility pass: checks which RIs are visible from this camera\\r\\n\\t\\tfor(var i = 0, l = render_instances.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//render instance\\r\\n\\t\\t\\tvar instance = render_instances[i];\\r\\n\\t\\t\\tvar node_flags = instance.node.flags;\\r\\n\\t\\t\\tinstance._is_visible = false;\\r\\n\\r\\n\\t\\t\\t//hidden nodes\\r\\n\\t\\t\\tif( pass == SHADOW_PASS && !(instance.material.flags.cast_shadows) )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif( pass == PICKING_PASS && node_flags.selectable === false )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif( (layers_filter & instance.layers) === 0 )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//done here because sometimes some nodes are moved in this action\\r\\n\\t\\t\\tif(instance.onPreRender)\\r\\n\\t\\t\\t\\tif( instance.onPreRender( render_settings ) === false)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(!instance.material) //in case something went wrong...\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar material = camera._overwrite_material || instance.material;\\r\\n\\r\\n\\t\\t\\tif(material.opacity <= 0) //TODO: remove this, do it somewhere else\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//test visibility against camera frustum\\r\\n\\t\\t\\tif( apply_frustum_culling && instance.use_bounding && !material.flags.ignore_frustum )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(geo.frustumTestBox( frustum_planes, instance.aabb ) == CLIP_OUTSIDE )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//save visibility info\\r\\n\\t\\t\\tinstance._is_visible = true;\\r\\n\\t\\t\\tif(camera_index_flag) //shadowmap cameras dont have an index\\r\\n\\t\\t\\t\\tinstance._camera_visibility |= camera_index_flag;\\r\\n\\r\\n\\r\\n\\t\\t\\t//TODO: if material supports instancing WIP\\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\tif( instancing_supported && material._allows_instancing && !instance._shader_blocks.length )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar instancing_ri_info = null;\\r\\n\\t\\t\\t\\tif(!instancing_data[ material._index ] )\\r\\n\\t\\t\\t\\t\\tinstancing_data[ material._index ] = instancing_ri_info = [];\\r\\n\\t\\t\\t\\tinstancing_ri_info.push( instance );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t*/\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar start = this._rendered_instances;\\r\\n\\t\\tvar debug_instance = this._debug_instance;\\r\\n\\r\\n\\t\\t//process render queues\\r\\n\\t\\tfor(var j = 0; j < this._queues.length; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar queue = this._queues[j];\\r\\n\\t\\t\\tif(!queue || !queue.instances.length) //empty\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//used to change RenderFrameContext stuff (cloning textures for refraction, etc)\\r\\n\\t\\t\\tif(queue.onStart)\\r\\n\\t\\t\\t\\tif( queue.onStart( render_settings, pass ) === false )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar render_instances = queue.instances;\\r\\n\\r\\n\\t\\t\\t//for each render instance\\r\\n\\t\\t\\tfor(var i = 0, l = render_instances.length; i < l; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//render instance\\r\\n\\t\\t\\t\\tvar instance = render_instances[i];\\r\\n\\r\\n\\t\\t\\t\\t//used to debug\\r\\n\\t\\t\\t\\tif(instance == debug_instance)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.log(debug_instance);\\r\\n\\t\\t\\t\\t\\tdebugger; \\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif( !instance._is_visible || !instance.mesh )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tthis._rendered_instances += 1;\\r\\n\\r\\n\\t\\t\\t\\tvar material = camera._overwrite_material || instance.material;\\r\\n\\r\\n\\t\\t\\t\\tif( pass == PICKING_PASS && material.renderPickingInstance )\\r\\n\\t\\t\\t\\t\\tmaterial.renderPickingInstance( instance, render_settings, pass );\\r\\n\\t\\t\\t\\telse if( material.renderInstance )\\r\\n\\t\\t\\t\\t\\tmaterial.renderInstance( instance, render_settings, pass );\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\t//some instances do a post render action\\r\\n\\t\\t\\t\\tif(instance.onPostRender)\\r\\n\\t\\t\\t\\t\\tinstance.onPostRender( render_settings );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(queue.onFinish)\\r\\n\\t\\t\\t\\tqueue.onFinish( render_settings, pass );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.resetGLState( render_settings );\\r\\n\\r\\n\\t\\tLEvent.trigger( scene, EVENT.RENDER_SCREEN_SPACE, render_settings);\\r\\n\\r\\n\\t\\t//restore state\\r\\n\\t\\tthis.resetGLState( render_settings );\\r\\n\\r\\n\\t\\tLEvent.trigger( scene, EVENT.AFTER_RENDER_INSTANCES, render_settings );\\r\\n\\t\\tLEvent.trigger( this, EVENT.AFTER_RENDER_INSTANCES, render_settings );\\r\\n\\r\\n\\t\\t//and finally again\\r\\n\\t\\tthis.resetGLState( render_settings );\\r\\n\\r\\n\\t\\treturn this._rendered_instances - start;\\r\\n\\t},\\r\\n\\r\\n\\trenderGUI: function( render_settings )\\r\\n\\t{\\r\\n\\t\\t//renders GUI items using mostly the Canvas2DtoWebGL library\\r\\n\\t\\tgl.viewport( this._full_viewport[0], this._full_viewport[1], this._full_viewport[2], this._full_viewport[3] ); //assign full viewport always?\\r\\n\\t\\tif(gl.start2D) //in case we have Canvas2DtoWebGL installed (it is optional)\\r\\n\\t\\t\\tgl.start2D();\\r\\n\\t\\tif( render_settings.render_gui )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( LEvent.hasBind( this._current_scene, EVENT.RENDER_GUI ) ) //to avoid forcing a redraw if no gui is set\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(LS.GUI)\\r\\n\\t\\t\\t\\t\\tLS.GUI.ResetImmediateGUI(); //mostly to change the cursor (warning, true to avoid forcing redraw)\\r\\n\\t\\t\\t\\tLEvent.trigger( this._current_scene, EVENT.RENDER_GUI, gl );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tif( this.on_render_gui ) //used by the editor (here to ignore render_gui flag)\\r\\n\\t\\t\\tthis.on_render_gui( render_settings );\\r\\n\\t\\tif( gl.finish2D )\\r\\n\\t\\t\\tgl.finish2D();\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* returns a list of all the lights overlapping this instance (it uses sperical bounding so it could returns lights that are not really overlapping)\\r\\n\\t* It is used by the multipass lighting to iterate \\r\\n\\t*\\r\\n\\t* @method getNearLights\\r\\n\\t* @param {RenderInstance} instance the render instance\\r\\n\\t* @param {Array} result [optional] the output array\\r\\n\\t* @return {Array} array containing a list of LS.Light affecting this RenderInstance\\r\\n\\t*/\\r\\n\\tgetNearLights: function( instance, result )\\r\\n\\t{\\r\\n\\t\\tresult = result || [];\\r\\n\\r\\n\\t\\tresult.length = 0; //clear old lights\\r\\n\\r\\n\\t\\t//it uses the lights gathered by prepareVisibleData\\r\\n\\t\\tvar lights = this._visible_lights;\\r\\n\\t\\tif(!lights || !lights.length)\\r\\n\\t\\t\\treturn result;\\r\\n\\r\\n\\t\\t//Compute lights affecting this RI (by proximity, only takes into account spherical bounding)\\r\\n\\t\\tresult.length = 0;\\r\\n\\t\\tvar numLights = lights.length;\\r\\n\\t\\tfor(var j = 0; j < numLights; j++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar light = lights[j];\\r\\n\\t\\t\\t//same layer?\\r\\n\\t\\t\\tif( (light.illuminated_layers & instance.layers) == 0 || (light.illuminated_layers & this._current_camera.layers) == 0)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar light_intensity = light.computeLightIntensity();\\r\\n\\t\\t\\t//light intensity too low?\\r\\n\\t\\t\\tif(light_intensity < 0.0001)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar light_radius = light.computeLightRadius();\\r\\n\\t\\t\\tvar light_pos = light.position;\\r\\n\\t\\t\\t//overlapping?\\r\\n\\t\\t\\tif( light_radius == -1 || instance.overlapsSphere( light_pos, light_radius ) )\\r\\n\\t\\t\\t\\tresult.push( light );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\r\\n\\tregenerateShadowmaps: function( scene, render_settings )\\r\\n\\t{\\r\\n\\t\\tscene = scene || this._current_scene;\\r\\n\\t\\trender_settings = render_settings || this.default_render_settings;\\r\\n\\t\\tLEvent.trigger( scene, EVENT.RENDER_SHADOWS, render_settings );\\r\\n\\t\\tfor(var i = 0; i < this._visible_lights.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar light = this._visible_lights[i];\\r\\n\\t\\t\\tlight.prepare( render_settings );\\r\\n\\t\\t\\tlight.onGenerateShadowmap();\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\tmergeSamplers: function( samplers, result )\\r\\n\\t{\\r\\n\\t\\tresult = result || [];\\r\\n\\t\\tresult.length = this._max_texture_units;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < result.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var j = samplers.length - 1; j >= 0; --j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(\\tsamplers[j][i] )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tresult[i] = samplers[j][i];\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\r\\n\\t//to be sure we dont have anything binded\\r\\n\\tclearSamplers: function()\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < this._max_texture_units; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.activeTexture(gl.TEXTURE0 + i);\\r\\n\\t\\t\\tgl.bindTexture( gl.TEXTURE_2D, null );\\r\\n\\t\\t\\tgl.bindTexture( gl.TEXTURE_CUBE_MAP, null );\\r\\n\\t\\t\\tthis._active_samples[i] = null;\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\tbindSamplers: function( samplers )\\r\\n\\t{\\r\\n\\t\\tif(!samplers)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar allow_textures = this.allow_textures; //used for debug\\r\\n\\r\\n\\t\\tfor(var i = 0; i < samplers.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar sampler = samplers[i];\\r\\n\\t\\t\\tif(!sampler) \\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//REFACTOR THIS\\r\\n\\t\\t\\tvar tex = null;\\r\\n\\t\\t\\tif(sampler.constructor === String || sampler.constructor === GL.Texture) //old way\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttex = sampler;\\r\\n\\t\\t\\t\\tsampler = null;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(sampler.texture)\\r\\n\\t\\t\\t\\ttex = sampler.texture;\\r\\n\\t\\t\\telse //dont know what this var type is?\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//continue; //if we continue the sampler slot will remain empty which could lead to problems\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif( tex && tex.constructor === String)\\r\\n\\t\\t\\t\\ttex = LS.ResourcesManager.textures[ tex ];\\r\\n\\t\\t\\tif(!allow_textures)\\r\\n\\t\\t\\t\\ttex = null;\\r\\n\\r\\n\\t\\t\\tif(!tex)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(sampler)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tswitch( sampler.missing )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"black\\\": tex = this._black_texture; break;\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"white\\\": tex = this._white_texture; break;\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"gray\\\": tex = this._gray_texture; break;\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"normal\\\": tex = this._normal_texture; break;\\r\\n\\t\\t\\t\\t\\t\\tdefault: tex = this._missing_texture;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\ttex = this._missing_texture;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//avoid to read from the same texture we are rendering to (generates warnings)\\r\\n\\t\\t\\tif(tex._in_current_fbo) \\r\\n\\t\\t\\t\\ttex = this._missing_texture;\\r\\n\\r\\n\\t\\t\\ttex.bind( i );\\r\\n\\t\\t\\tthis._active_samples[i] = tex;\\r\\n\\r\\n\\t\\t\\t//texture properties\\r\\n\\t\\t\\tif(sampler)// && sampler._must_update ) //disabled because samplers ALWAYS must set to the value, in case the same texture is used in several places in the scene\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(sampler.minFilter)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif( sampler.minFilter !== gl.LINEAR_MIPMAP_LINEAR || (GL.isPowerOfTwo( tex.width ) && GL.isPowerOfTwo( tex.height )) )\\r\\n\\t\\t\\t\\t\\t\\tgl.texParameteri(tex.texture_type, gl.TEXTURE_MIN_FILTER, sampler.minFilter);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif(sampler.magFilter)\\r\\n\\t\\t\\t\\t\\tgl.texParameteri(tex.texture_type, gl.TEXTURE_MAG_FILTER, sampler.magFilter);\\r\\n\\t\\t\\t\\tif(sampler.wrap)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tgl.texParameteri(tex.texture_type, gl.TEXTURE_WRAP_S, sampler.wrap);\\r\\n\\t\\t\\t\\t\\tgl.texParameteri(tex.texture_type, gl.TEXTURE_WRAP_T, sampler.wrap);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif(sampler.anisotropic != null && gl.extensions.EXT_texture_filter_anisotropic )\\r\\n\\t\\t\\t\\t\\tgl.texParameteri(tex.texture_type, gl.extensions.EXT_texture_filter_anisotropic.TEXTURE_MAX_ANISOTROPY_EXT, sampler.anisotropic );\\r\\n\\r\\n\\t\\t\\t\\t//sRGB textures must specified ON CREATION, so no\\r\\n\\t\\t\\t\\t//if(sampler.anisotropic != null && gl.extensions.EXT_sRGB )\\r\\n\\t\\t\\t\\t//sampler._must_update = false;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t//Called at the beginning of processVisibleData \\r\\n\\tfillSceneUniforms: function( scene, render_settings )\\r\\n\\t{\\r\\n\\t\\t//global uniforms\\r\\n\\t\\tvar uniforms = scene._uniforms;\\r\\n\\t\\tuniforms.u_time = scene._time || getTime() * 0.001;\\r\\n\\t\\tuniforms.u_ambient_light = scene.info ? scene.info.ambient_color : vec3.create();\\r\\n\\r\\n\\t\\tthis._samplers.length = 0;\\r\\n\\r\\n\\t\\t//clear globals\\r\\n\\t\\tthis._global_textures.environment = null;\\r\\n\\r\\n\\t\\t//fetch global textures\\r\\n\\t\\tif(scene.info)\\r\\n\\t\\tfor(var i in scene.info.textures)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar texture = LS.getTexture( scene.info.textures[i] );\\r\\n\\t\\t\\tif(!texture)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar slot = 0;\\r\\n\\t\\t\\tif( i == \\\"environment\\\" )\\r\\n\\t\\t\\t\\tslot = LS.Renderer.ENVIRONMENT_TEXTURE_SLOT;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tcontinue; \\r\\n\\r\\n\\t\\t\\tvar type = (texture.texture_type == gl.TEXTURE_2D ? \\\"_texture\\\" : \\\"_cubemap\\\");\\r\\n\\t\\t\\tif(texture.texture_type == gl.TEXTURE_2D)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttexture.bind(0);\\r\\n\\t\\t\\t\\ttexture.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR ); //avoid artifact\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis._samplers[ slot ] = texture;\\r\\n\\t\\t\\tscene._uniforms[ i + \\\"_texture\\\" ] = slot; \\r\\n\\t\\t\\tscene._uniforms[ i + type ] = slot; //LEGACY\\r\\n\\r\\n\\t\\t\\tif( i == \\\"environment\\\" )\\r\\n\\t\\t\\t\\tthis._global_textures.environment = texture;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLEvent.trigger( scene, EVENT.FILL_SCENE_UNIFORMS, scene._uniforms );\\r\\n\\t},\\t\\r\\n\\r\\n\\t/**\\r\\n\\t* Collects and process the rendering instances, cameras and lights that are visible\\r\\n\\t* Its a prepass shared among all rendering passes\\r\\n\\t* Warning: rendering order is computed here, so it is shared among all the cameras (TO DO, move somewhere else)\\r\\n\\t*\\r\\n\\t* @method processVisibleData\\r\\n\\t* @param {Scene} scene\\r\\n\\t* @param {RenderSettings} render_settings\\r\\n\\t* @param {Array} cameras in case you dont want to use the scene cameras\\r\\n\\t*/\\r\\n\\tprocessVisibleData: function( scene, render_settings, cameras, instances, skip_collect_data )\\r\\n\\t{\\r\\n\\t\\t//options = options || {};\\r\\n\\t\\t//options.scene = scene;\\r\\n\\t\\tvar frame = scene._frame;\\r\\n\\r\\n\\t\\tthis._current_scene = scene;\\r\\n\\t\\t//compute global scene info\\r\\n\\t\\tthis.fillSceneUniforms( scene, render_settings );\\r\\n\\r\\n\\t\\t//update info about scene (collecting it all or reusing the one collected in the frame before)\\r\\n\\t\\tif(!skip_collect_data)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this._frame % this._collect_frequency == 0)\\r\\n\\t\\t\\t\\tscene.collectData( cameras );\\r\\n\\t\\t\\tLEvent.trigger( scene, EVENT.AFTER_COLLECT_DATA, scene );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//set cameras: use the parameters ones or the ones found in the scene\\r\\n\\t\\tcameras = (cameras && cameras.length) ? cameras : scene._cameras;\\r\\n\\t\\tif( cameras.length == 0 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"no cameras found\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\t\\t\\t\\r\\n\\t\\t//find which materials are going to be seen\\r\\n\\t\\tvar materials = this._visible_materials; \\r\\n\\t\\tmaterials.length = 0;\\r\\n\\r\\n\\t\\t//prepare cameras: TODO: sort by priority\\r\\n\\t\\tfor(var i = 0, l = cameras.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar camera = cameras[i];\\r\\n\\t\\t\\tcamera._rendering_index = i;\\r\\n\\t\\t\\tcamera.prepare();\\r\\n\\t\\t\\tif(camera.overwrite_material)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar material = camera.overwrite_material.constructor === String ? LS.ResourcesManager.resources[ camera.overwrite_material ] : camera.overwrite_material;\\r\\n\\t\\t\\t\\tif(material)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tcamera._overwrite_material = material;\\r\\n\\t\\t\\t\\t\\tmaterials.push( material );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tcamera._overwrite_material = null;\\r\\n\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//define the main camera (the camera used for some algorithms)\\r\\n\\t\\tif(!this._main_camera)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( cameras.length )\\r\\n\\t\\t\\t\\tthis._main_camera = cameras[0];\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis._main_camera = new LS.Camera(); // ??\\r\\n\\t\\t}\\r\\n\\r\\n\\r\\n\\t\\tvar nearest_reflection_probe = scene.findNearestReflectionProbe( this._main_camera.getEye() );\\r\\n\\r\\n\\t\\tinstances = instances || scene._instances;\\r\\n\\t\\tvar camera = this._main_camera; // || scene.getCamera();\\r\\n\\t\\tvar camera_eye = camera.getEye( this._temp_cameye );\\r\\n\\r\\n\\t\\t//clear render queues\\r\\n\\t\\tfor(var i = 0; i < this._queues.length; ++i)\\r\\n\\t\\t\\tif(this._queues[i])\\r\\n\\t\\t\\t\\tthis._queues[i].clear();\\r\\n\\r\\n\\t\\t//process render instances (add stuff if needed, gather materials)\\r\\n\\t\\tfor(var i = 0, l = instances.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar instance = instances[i];\\r\\n\\t\\t\\tif(!instance)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar node_flags = instance.node.flags;\\r\\n\\r\\n\\t\\t\\tif(!instance.mesh)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"RenderInstance must always have mesh\\\");\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//materials\\r\\n\\t\\t\\tif(!instance.material)\\r\\n\\t\\t\\t\\tinstance.material = this.default_material;\\r\\n\\r\\n\\t\\t\\tif( instance.material._last_frame_update != frame )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tinstance.material._last_frame_update = frame;\\r\\n\\t\\t\\t\\tmaterials.push( instance.material );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//add extra info: distance to main camera (used for sorting)\\r\\n\\t\\t\\tinstance._dist = vec3.dist( instance.center, camera_eye );\\r\\n\\r\\n\\t\\t\\t//find nearest reflection probe\\r\\n\\t\\t\\tif( scene._reflection_probes.length && !this._ignore_reflection_probes )\\r\\n\\t\\t\\t\\tinstance._nearest_reflection_probe = nearest_reflection_probe;//scene.findNearestReflectionProbe( instance.center );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tinstance._nearest_reflection_probe = null;\\r\\n\\r\\n\\t\\t\\t//change conditionaly\\r\\n\\t\\t\\tif(render_settings.force_wireframe && instance.primitive != gl.LINES ) \\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tinstance.primitive = gl.LINES;\\r\\n\\t\\t\\t\\tif(instance.mesh)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(!instance.mesh.indexBuffers[\\\"wireframe\\\"])\\r\\n\\t\\t\\t\\t\\t\\tinstance.mesh.computeWireframe();\\r\\n\\t\\t\\t\\t\\tinstance.index_buffer = instance.mesh.indexBuffers[\\\"wireframe\\\"];\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//add to queues\\r\\n\\t\\t\\tvar queue_index = instance.material.queue;\\r\\n\\t\\t\\tvar queue = null;\\r\\n\\t\\t\\tif( queue_index === undefined || queue_index === LS.RenderQueue.DEFAULT )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//TODO: maybe this case should be treated directly in StandardMaterial\\r\\n\\t\\t\\t\\tif( instance.material._render_state.blend )\\r\\n\\t\\t\\t\\t\\tqueue = this._queues[ LS.RenderQueue.TRANSPARENT ];\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tqueue = this._queues[ LS.RenderQueue.GEOMETRY ];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tqueue = this._queues[ queue_index ];\\r\\n\\t\\t\\t\\tif(!queue)\\r\\n\\t\\t\\t\\t\\tLS.Renderer.createRenderQueue( queue_index );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif(!queue)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tqueue.add( instance );\\r\\n\\r\\n\\t\\t\\tinstance._camera_visibility = 0|0;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//prepare materials \\r\\n\\t\\tfor(var i = 0; i < materials.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar material = materials[i];\\r\\n\\t\\t\\tmaterial._index = i;\\r\\n\\t\\t\\tif( material.prepare )\\r\\n\\t\\t\\t\\tmaterial.prepare( scene );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLEvent.trigger( scene, EVENT.PREPARE_MATERIALS );\\r\\n\\r\\n\\t\\t//pack all macros, uniforms, and samplers relative to this instance in single containers\\r\\n\\t\\tfor(var i = 0, l = instances.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar instance = instances[i];\\r\\n\\t\\t\\tvar node = instance.node;\\r\\n\\t\\t\\tvar material = instance.material;\\r\\n\\t\\t\\tinstance.index = i;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//store all the info\\r\\n\\t\\tthis._visible_instances = scene._instances;\\r\\n\\t\\tthis._visible_lights = scene._lights;\\r\\n\\t\\tthis._visible_cameras = cameras; \\r\\n\\t\\t//this._visible_materials = materials;\\r\\n\\r\\n\\t\\t//prepare lights (collect data and generate shadowmaps)\\r\\n\\t\\tfor(var i = 0, l = this._visible_lights.length; i < l; ++i)\\r\\n\\t\\t\\tthis._visible_lights[i].prepare( render_settings );\\r\\n\\r\\n\\t\\tLEvent.trigger( scene, EVENT.AFTER_COLLECT_DATA, scene );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a frame into a texture (could be a cubemap, in which case does the six passes)\\r\\n\\t*\\r\\n\\t* @method renderInstancesToRT\\r\\n\\t* @param {Camera} cam\\r\\n\\t* @param {Texture} texture\\r\\n\\t* @param {RenderSettings} render_settings\\r\\n\\t*/\\r\\n\\trenderInstancesToRT: function( cam, texture, render_settings, instances )\\r\\n\\t{\\r\\n\\t\\trender_settings = render_settings || this.default_render_settings;\\r\\n\\t\\tthis._current_target = texture;\\r\\n\\t\\tvar scene = LS.Renderer._current_scene;\\r\\n\\t\\ttexture._in_current_fbo = true;\\r\\n\\r\\n\\t\\tif(texture.texture_type == gl.TEXTURE_2D)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.enableCamera(cam, render_settings);\\r\\n\\t\\t\\ttexture.drawTo( inner_draw_2d );\\r\\n\\t\\t}\\r\\n\\t\\telse if( texture.texture_type == gl.TEXTURE_CUBE_MAP)\\r\\n\\t\\t\\tthis.renderToCubemap( cam.getEye(), texture.width, texture, render_settings, cam.near, cam.far );\\r\\n\\t\\tthis._current_target = null;\\r\\n\\t\\ttexture._in_current_fbo = false;\\r\\n\\r\\n\\t\\tfunction inner_draw_2d()\\r\\n\\t\\t{\\r\\n\\t\\t\\tLS.Renderer.clearBuffer( cam, render_settings );\\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\tgl.clearColor(cam.background_color[0], cam.background_color[1], cam.background_color[2], cam.background_color[3] );\\r\\n\\t\\t\\tif(render_settings.ignore_clear != true)\\r\\n\\t\\t\\t\\tgl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\\r\\n\\t\\t\\t*/\\r\\n\\t\\t\\t//render scene\\r\\n\\t\\t\\tLS.Renderer.renderInstances( render_settings, instances );\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders the current scene to a cubemap centered in the given position\\r\\n\\t*\\r\\n\\t* @method renderToCubemap\\r\\n\\t* @param {vec3} position center of the camera where to render the cubemap\\r\\n\\t* @param {number} size texture size\\r\\n\\t* @param {Texture} texture to reuse the same texture\\r\\n\\t* @param {RenderSettings} render_settings\\r\\n\\t* @param {number} near\\r\\n\\t* @param {number} far\\r\\n\\t* @return {Texture} the resulting texture\\r\\n\\t*/\\r\\n\\trenderToCubemap: function( position, size, texture, render_settings, near, far, background_color, instances )\\r\\n\\t{\\r\\n\\t\\tsize = size || 256;\\r\\n\\t\\tnear = near || 1;\\r\\n\\t\\tfar = far || 1000;\\r\\n\\r\\n\\t\\tif(render_settings && render_settings.constructor !== LS.RenderSettings)\\r\\n\\t\\t\\tthrow(\\\"render_settings parameter must be LS.RenderSettings.\\\");\\r\\n\\r\\n\\t\\tvar eye = position;\\r\\n\\t\\tif( !texture || texture.constructor != GL.Texture)\\r\\n\\t\\t\\ttexture = null;\\r\\n\\r\\n\\t\\tvar scene = this._current_scene;\\r\\n\\t\\tif(!scene)\\r\\n\\t\\t\\tscene = this._current_scene = LS.GlobalScene;\\r\\n\\r\\n\\t\\tvar camera = this._cubemap_camera;\\r\\n\\t\\tif(!camera)\\r\\n\\t\\t\\tcamera = this._cubemap_camera = new LS.Camera();\\r\\n\\t\\tcamera.configure({ fov: 90, aspect: 1.0, near: near, far: far });\\r\\n\\r\\n\\t\\ttexture = texture || new GL.Texture(size,size,{texture_type: gl.TEXTURE_CUBE_MAP, minFilter: gl.NEAREST});\\r\\n\\t\\tthis._current_target = texture;\\r\\n\\t\\ttexture._in_current_fbo = true; //block binding this texture during rendering of the reflection\\r\\n\\r\\n\\t\\ttexture.drawTo( function(texture, side) {\\r\\n\\r\\n\\t\\t\\tvar info = LS.Camera.cubemap_camera_parameters[side];\\r\\n\\t\\t\\tif(texture._is_shadowmap || !background_color )\\r\\n\\t\\t\\t\\tgl.clearColor(0,0,0,0);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tgl.clearColor( background_color[0], background_color[1], background_color[2], background_color[3] );\\r\\n\\t\\t\\tgl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\\r\\n\\t\\t\\tcamera.configure({ eye: eye, center: [ eye[0] + info.dir[0], eye[1] + info.dir[1], eye[2] + info.dir[2]], up: info.up });\\r\\n\\r\\n\\t\\t\\tLS.Renderer.enableCamera( camera, render_settings, true );\\r\\n\\t\\t\\tLS.Renderer.renderInstances( render_settings, instances, scene );\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis._current_target = null;\\r\\n\\t\\ttexture._in_current_fbo = false;\\r\\n\\t\\treturn texture;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the last camera that falls into a given screen position\\r\\n\\t*\\r\\n\\t* @method getCameraAtPosition\\r\\n\\t* @param {number} x in canvas coordinates (0,0 is bottom-left)\\r\\n\\t* @param {number} y in canvas coordinates (0,0 is bottom-left)\\r\\n\\t* @param {Scene} scene if not specified last rendered scene will be used\\r\\n\\t* @return {Camera} the camera\\r\\n\\t*/\\r\\n\\tgetCameraAtPosition: function(x,y, cameras)\\r\\n\\t{\\r\\n\\t\\tcameras = cameras || this._visible_cameras;\\r\\n\\t\\tif(!cameras || !cameras.length)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tfor(var i = cameras.length - 1; i >= 0; --i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar camera = cameras[i];\\r\\n\\t\\t\\tif(!camera.enabled || camera.render_to_texture)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif( camera.isPoint2DInCameraViewport(x,y) )\\r\\n\\t\\t\\t\\treturn camera;\\r\\n\\t\\t}\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Adds a new RenderQueue to the Renderer.\\r\\n\\t*\\r\\n\\t* @method addRenderQueue\\r\\n\\t* @param {RenderQueue} name name of the render pass as in render_passes\\r\\n\\t* @param {Number} sorting which algorithm use to sort ( LS.RenderQueue.NO_SORT, LS.RenderQueue.SORT_NEAR_TO_FAR, LS.RenderQueue.SORT_FAR_TO_NEAR )\\r\\n\\t* @param {Object} options extra stuff to add to the queue ( like callbacks onStart, onFinish )\\r\\n\\t* @return {Number} index of the render queue\\r\\n\\t*/\\r\\n\\tcreateRenderQueue: function( index, sorting, options )\\r\\n\\t{\\r\\n\\t\\tif(index === undefined)\\r\\n\\t\\t\\tthrow(\\\"RenderQueue must have index\\\");\\r\\n\\t\\tvar queue = new LS.RenderQueue( sorting );\\r\\n\\t\\tif( this._queues[ index ] )\\r\\n\\t\\t\\tconsole.warn(\\\"There is already a RenderQueue in slot \\\",index );\\r\\n\\t\\tthis._queues[ index ] = queue;\\r\\n\\r\\n\\t\\tif(options)\\r\\n\\t\\t\\tfor(var i in options)\\r\\n\\t\\t\\t\\tqueue[i] = options[i];\\r\\n\\t},\\r\\n\\r\\n\\tsetRenderPass: function( pass )\\r\\n\\t{\\r\\n\\t\\tif(!pass)\\r\\n\\t\\t\\tpass = COLOR_PASS;\\r\\n\\t\\tthis._current_pass = pass;\\r\\n\\t},\\r\\n\\t\\r\\n\\t/**\\r\\n\\t* Enables a ShaderBlock ONLY DURING THIS FRAME\\r\\n\\t* must be called during frame rendering (event like fillSceneUniforms)\\r\\n\\t*\\r\\n\\t* @method enableFrameShaderBlock\\r\\n\\t* @param {String} shader_block_name\\r\\n\\t*/\\r\\n\\tenableFrameShaderBlock: function( shader_block_name, uniforms, samplers )\\r\\n\\t{\\r\\n\\t\\tvar shader_block = shader_block_name.constructor === LS.ShaderBlock ? shader_block_name : LS.Shaders.getShaderBlock( shader_block_name );\\r\\n\\r\\n\\t\\tif( !shader_block || this._global_shader_blocks_flags & shader_block.flag_mask )\\r\\n\\t\\t\\treturn; //already added\\r\\n\\r\\n\\t\\tthis._global_shader_blocks.push( shader_block );\\r\\n\\t\\tthis._global_shader_blocks_flags |= shader_block.flag_mask;\\r\\n\\r\\n\\t\\t//add uniforms to renderer uniforms?\\r\\n\\t\\tif(uniforms)\\r\\n\\t\\t\\tfor(var i in uniforms)\\r\\n\\t\\t\\t\\tthis._uniforms[i] = uniforms[i];\\r\\n\\r\\n\\t\\tif(samplers)\\r\\n\\t\\t\\tfor(var i = 0; i < samplers.length; ++i)\\r\\n\\t\\t\\t\\tif( samplers[i] )\\r\\n\\t\\t\\t\\t\\tthis._samplers[i] = samplers[i];\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Disables a ShaderBlock ONLY DURING THIS FRAME\\r\\n\\t* must be called during frame rendering (event like fillSceneUniforms)\\r\\n\\t*\\r\\n\\t* @method disableFrameShaderBlock\\r\\n\\t* @param {String} shader_block_name\\r\\n\\t*/\\r\\n\\tdisableFrameShaderBlock: function( shader_block_name, uniforms, samplers )\\r\\n\\t{\\r\\n\\t\\tvar shader_block = shader_block_name.constructor === LS.ShaderBlock ? shader_block_name : LS.Shaders.getShaderBlock( shader_block_name );\\r\\n\\t\\tif( !shader_block || !(this._global_shader_blocks_flags & shader_block.flag_mask) )\\r\\n\\t\\t\\treturn; //not active\\r\\n\\r\\n\\t\\tvar index = this._global_shader_blocks.indexOf( shader_block );\\r\\n\\t\\tif(index != -1)\\r\\n\\t\\t\\tthis._global_shader_blocks.splice( index, 1 );\\r\\n\\t\\tthis._global_shader_blocks_flags &= ~( shader_block.flag_mask ); //disable bit\\r\\n\\t},\\r\\n\\r\\n\\t//time queries for profiling\\r\\n\\t_current_query: null,\\r\\n\\r\\n\\tstartGPUQuery: function( name )\\r\\n\\t{\\r\\n\\t\\tif(!gl.extensions[\\\"disjoint_timer_query\\\"]) //if not supported\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(this._waiting_queries)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar ext = gl.extensions[\\\"disjoint_timer_query\\\"];\\r\\n\\t\\tvar query = this._timer_queries[ name ];\\r\\n\\t\\tif(!query)\\r\\n\\t\\t\\tquery = this._timer_queries[ name ] = ext.createQueryEXT();\\r\\n\\t\\text.beginQueryEXT( ext.TIME_ELAPSED_EXT, query );\\r\\n\\t\\tthis._current_query = query;\\r\\n\\t},\\r\\n\\r\\n\\tendGPUQuery: function()\\r\\n\\t{\\r\\n\\t\\tif(!gl.extensions[\\\"disjoint_timer_query\\\"]) //if not supported\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(this._waiting_queries)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar ext = gl.extensions[\\\"disjoint_timer_query\\\"];\\r\\n\\t\\text.endQueryEXT( ext.TIME_ELAPSED_EXT );\\r\\n\\t\\tthis._current_query = null;\\r\\n\\t},\\r\\n\\r\\n\\tresolveQueries: function()\\r\\n\\t{\\r\\n\\t\\tif(!gl.extensions[\\\"disjoint_timer_query\\\"]) //if not supported\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//var err = gl.getError();\\r\\n\\t\\t//if(err != gl.NO_ERROR)\\r\\n\\t\\t//\\tconsole.log(\\\"GL_ERROR: \\\" + err );\\r\\n\\r\\n\\t\\tvar ext = gl.extensions[\\\"disjoint_timer_query\\\"];\\r\\n\\r\\n\\t\\tvar last_query = this._timer_queries[\\\"gui\\\"];\\r\\n\\t\\tif(!last_query)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar available = ext.getQueryObjectEXT( last_query, ext.QUERY_RESULT_AVAILABLE_EXT );\\r\\n\\t\\tif(!available)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._waiting_queries = true;\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\r\\n\\t\\tvar disjoint = gl.getParameter( ext.GPU_DISJOINT_EXT );\\r\\n\\t\\tif(!disjoint)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar total = 0;\\r\\n\\t\\t\\tfor(var i in this._timer_queries)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar query = this._timer_queries[i];\\r\\n\\t\\t\\t\\t// See how much time the rendering of the object took in nanoseconds.\\r\\n\\t\\t\\t\\tvar timeElapsed = ext.getQueryObjectEXT( query, ext.QUERY_RESULT_EXT ) * 10e-6; //to milliseconds;\\r\\n\\t\\t\\t\\ttotal += timeElapsed;\\r\\n\\t\\t\\t\\tthis.gpu_times[ i ] = timeElapsed;\\r\\n\\t\\t\\t\\t//ext.deleteQueryEXT(query);\\r\\n\\t\\t\\t\\t//this._timer_queries[i] = null;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.gpu_times.total = total;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._waiting_queries = false;\\r\\n\\t},\\r\\n\\r\\n\\tprofiler_text: [],\\r\\n\\r\\n\\trenderProfiler: function()\\r\\n\\t{\\r\\n\\t\\tif(!gl.canvas.canvas2DtoWebGL_enabled)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar text = this.profiler_text;\\r\\n\\t\\tvar ext = gl.extensions[\\\"disjoint_timer_query\\\"];\\r\\n\\r\\n\\t\\tif(this._frame % 5 == 0)\\r\\n\\t\\t{\\r\\n\\t\\t\\ttext.length = 0;\\r\\n\\t\\t\\tvar fps = 1000 / this._frame_time;\\r\\n\\t\\t\\ttext.push( fps.toFixed(2) + \\\" FPS\\\" );\\r\\n\\t\\t\\ttext.push( \\\"CPU: \\\" + this._frame_cpu_time.toFixed(2) + \\\" ms\\\" );\\r\\n\\t\\t\\ttext.push( \\\" - Passes: \\\" + this._rendered_passes );\\r\\n\\t\\t\\ttext.push( \\\" - RIs: \\\" + this._rendered_instances );\\r\\n\\t\\t\\ttext.push( \\\" - Draws: \\\" + this._rendercalls );\\r\\n\\r\\n\\t\\t\\tif( ext )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttext.push( \\\"GPU: \\\" + this.gpu_times.total.toFixed(2) );\\r\\n\\t\\t\\t\\ttext.push( \\\" - PreRender: \\\" + this.gpu_times.beforeRender.toFixed(2) );\\r\\n\\t\\t\\t\\ttext.push( \\\" - Shadows: \\\" + this.gpu_times.shadows.toFixed(2) );\\r\\n\\t\\t\\t\\ttext.push( \\\" - Reflections: \\\" + this.gpu_times.reflections.toFixed(2) );\\r\\n\\t\\t\\t\\ttext.push( \\\" - Scene: \\\" + this.gpu_times.main.toFixed(2) );\\r\\n\\t\\t\\t\\ttext.push( \\\" - Postpo: \\\" + this.gpu_times.postpo.toFixed(2) );\\r\\n\\t\\t\\t\\ttext.push( \\\" - GUI: \\\" + this.gpu_times.gui.toFixed(2) );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\ttext.push( \\\"GPU: ???\\\");\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar ctx = gl;\\r\\n\\t\\tctx.save();\\r\\n\\t\\tctx.translate( gl.canvas.width - 200, gl.canvas.height - 280 );\\r\\n\\t\\tctx.globalAlpha = 0.7;\\r\\n\\t\\tctx.font = \\\"14px Tahoma\\\";\\r\\n\\t\\tctx.fillStyle = \\\"black\\\";\\r\\n\\t\\tctx.fillRect(0,0,200,280);\\r\\n\\t\\tctx.fillStyle = \\\"white\\\";\\r\\n\\t\\tctx.fillText( \\\"Profiler\\\", 20, 20 );\\r\\n\\t\\tctx.fillStyle = \\\"#AFA\\\";\\r\\n\\t\\tfor(var i = 0; i < text.length; ++i)\\r\\n\\t\\t\\tctx.fillText( text[i], 20,50 + 20 * i );\\r\\n\\t\\tctx.restore();\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders one texture into another texture, it allows to apply a shader\\r\\n\\t*\\r\\n\\t* @method blit\\r\\n\\t* @param {GL.Texture} source\\r\\n\\t* @param {GL.Texture} destination\\r\\n\\t* @param {GL.Shader} shader [optional] shader to apply, it must use the GL.Shader.QUAD_VERTEX_SHADER as vertex shader\\r\\n\\t* @param {Object} uniforms [optional] uniforms for the shader\\r\\n\\t*/\\r\\n\\tblit: function( source, destination, shader, uniforms )\\r\\n\\t{\\r\\n\\t\\tif(!source || !destination)\\r\\n\\t\\t\\tthrow(\\\"data missing in blit\\\");\\r\\n\\r\\n\\t\\tif(source != destination)\\r\\n\\t\\t{\\r\\n\\t\\t\\tdestination.drawTo( function(){\\r\\n\\t\\t\\t\\tsource.toViewport( shader, uniforms );\\r\\n\\t\\t\\t});\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!shader)\\r\\n\\t\\t\\tthrow(\\\"blitting texture to the same texture doesnt makes sense unless a shader is specified\\\");\\r\\n\\r\\n\\t\\tvar temp = GL.Texture.getTemporary( source.width, source.height, source );\\r\\n\\t\\tsource.copyTo( temp );\\r\\n\\t\\ttemp.copyTo( source, shader, uniforms );\\r\\n\\t\\tGL.Texture.releaseTemporary( temp );\\r\\n\\t}\\r\\n};\\r\\n\\r\\n//Add to global Scope\\r\\nLS.Renderer = Renderer;\\r\\n\\r\\n\\r\\n\\r\\n///@FILE:../src/render/debug.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\tDebugRender\\r\\n* Used to render debug information like skeletons, a grid, etc\\r\\n* I moved it from WebGLStudio to LS so it could help when working with scenes coded without the editor\\r\\n*\\r\\n* @class DebugRender\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\nfunction DebugRender()\\r\\n{\\r\\n\\tthis.debug_points = []; //used for debugging, allows to draw points easily\\r\\n\\r\\n\\t//current frame data to render (we store it so we can render with less drawcalls)\\r\\n\\tthis._points = []; //linear array with x,y,z, x,y,z, ...\\r\\n\\tthis._points_color = [];\\r\\n\\tthis._points_nodepth = []; //linear array with x,y,z, x,y,z, ...\\r\\n\\tthis._points_color_nodepth = [];\\r\\n\\tthis._lines = []; //vec3,vec3 array\\r\\n\\tthis._lines_color = []; //\\r\\n\\tthis._names = []; //array of [vec3, string]\\r\\n\\r\\n\\tthis.grid_texture_url = \\\"imgs/grid.png\\\";\\r\\n\\r\\n\\t//this camera is used to render names\\r\\n\\tthis.camera2D = new LS.Camera({eye:[0,0,0],center:[0,0,-1]});\\r\\n\\tthis.createMeshes();\\r\\n\\r\\n\\tthis.colors = {\\r\\n\\t\\tselected: vec4.fromValues(1,1,1,1),\\r\\n\\t\\tnode: vec4.fromValues(1,0.5,0,1),\\r\\n\\t\\tbone: vec4.fromValues(1,0,0.5,1)\\r\\n\\t};\\r\\n\\r\\n\\tthis.settings = {\\r\\n\\t\\trender_grid: true,\\r\\n\\t\\tgrid_scale: 1.0,\\r\\n\\t\\tgrid_alpha: 0.5,\\r\\n\\t\\tgrid_plane: \\\"xz\\\",\\r\\n\\t\\trender_names: false,\\r\\n\\t\\trender_skeletons: true,\\r\\n\\t\\trender_tree: false,\\r\\n\\t\\trender_components: true,\\r\\n\\t\\trender_null_nodes: true,\\r\\n\\t\\trender_axis: false,\\r\\n\\t\\trender_colliders: true,\\r\\n\\t\\trender_paths: true,\\r\\n\\t\\trender_origin: true,\\r\\n\\t\\trender_colliders_aabb: false\\r\\n\\t};\\r\\n\\r\\n\\tthis._in_scene = false;\\r\\n}\\r\\n\\r\\nDebugRender.prototype.enable = function( scene )\\r\\n{\\r\\n\\tif(this._in_scene)\\r\\n\\t\\treturn;\\r\\n\\tscene = scene || LS.GlobalScene;\\r\\n\\tLEvent.bind( scene, \\\"afterRenderInstances\\\", this.onRender, this );\\r\\n\\tthis._in_scene = scene;\\r\\n}\\r\\n\\r\\nDebugRender.prototype.disable = function( scene )\\r\\n{\\r\\n\\tif(!this._in_scene)\\r\\n\\t\\treturn;\\r\\n\\tLEvent.unbind( this._in_scene, \\\"afterRenderInstances\\\", this.onRender, this );\\r\\n\\tthis._in_scene = null;\\r\\n}\\r\\n\\r\\nDebugRender.prototype.onRender = function( e, render_settings )\\r\\n{\\r\\n\\tthis.render( LS.Renderer._current_camera );\\r\\n}\\r\\n\\r\\n//we pass a callback to check if something is selected\\r\\nDebugRender.prototype.render = function( camera, is_selected_callback, scene )\\r\\n{\\r\\n\\tvar settings = this.settings;\\r\\n\\r\\n\\tscene = scene || LS.GlobalScene;\\r\\n\\r\\n\\tgl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);\\r\\n\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\tgl.disable(gl.BLEND);\\r\\n\\tgl.disable( gl.CULL_FACE );\\r\\n\\tgl.depthFunc( gl.LEQUAL );\\r\\n\\t//gl.depthMask( false );\\r\\n\\tvar selected_node = null;\\r\\n\\r\\n\\tif( settings.render_grid && settings.grid_alpha > 0 )\\r\\n\\t\\tthis.renderGrid();\\r\\n\\r\\n\\tif( settings.render_origin )\\r\\n\\t{\\r\\n\\t\\tLS.Draw.setColor([0.3,0.3,0.3,1.0]);\\r\\n\\t\\tLS.Draw.push();\\r\\n\\t\\tLS.Draw.scale(0.01,0.01,0.01);\\r\\n\\t\\tLS.Draw.rotate(-90,[1,0,0]);\\r\\n\\t\\tgl.blendFunc(gl.SRC_ALPHA,gl.ONE);\\r\\n\\t\\tLS.Draw.renderText(\\\"Origin\\\");\\r\\n\\t\\tgl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);\\r\\n\\t\\tLS.Draw.pop();\\r\\n\\t}\\r\\n\\r\\n\\tif( settings.render_components )\\r\\n\\t{\\r\\n\\t\\t//Node components\\r\\n\\t\\tfor(var i = 0, l = scene._nodes.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar node = scene._nodes[i];\\r\\n\\t\\t\\tvar is_node_selected = node._is_selected;\\r\\n\\t\\t\\tselected_node = node;\\r\\n\\t\\t\\tif(node.renderEditor)\\r\\n\\t\\t\\t\\tnode.renderEditor( is_node_selected );\\r\\n\\t\\t\\tfor(var j = 0, l2 = node._components.length; j < l2; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar component = node._components[j];\\r\\n\\t\\t\\t\\tvar is_component_selected = false;\\r\\n\\t\\t\\t\\tif(is_selected_callback)\\r\\n\\t\\t\\t\\t\\tis_component_selected = is_selected_callback( component );\\r\\n\\t\\t\\t\\tif(component.renderEditor)\\r\\n\\t\\t\\t\\t\\tcomponent.renderEditor( is_node_selected, is_component_selected );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//render local things\\t\\t\\r\\n\\tvar zero = vec3.create();\\r\\n\\tfor(var i = 0, l = scene._nodes.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar node = scene._nodes[i];\\r\\n\\t\\tif(node._is_root || !node.flags.visible ) \\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tvar global = node.transform ? node.transform.getGlobalMatrixRef() : mat4.create();\\r\\n\\t\\tvar pos = mat4.multiplyVec3( vec3.create(), global, zero ); //create a new one to store them\\r\\n\\r\\n\\t\\tif( settings.render_null_nodes)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( node._is_selected )\\r\\n\\t\\t\\t\\tthis.renderPoint( pos, true, this.colors.selected );\\r\\n\\t\\t\\telse if( node._is_bone )\\r\\n\\t\\t\\t\\tthis.renderPoint( pos, true, this.colors.bone );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis.renderPoint( pos, false, this.colors.node );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(settings.render_names)\\r\\n\\t\\t\\tthis.renderText(pos, node.name, node._is_selected ? [0.94, 0.8, 0.4,1] : [0.8,0.8,0.8,0.9] );\\r\\n\\r\\n\\t\\tif (node._parentNode && node._parentNode.transform && (settings.render_tree || (settings.render_skeletons && node._is_bone && node._parentNode._is_bone)) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.renderLine( pos , node._parentNode.transform.getGlobalPosition(), this.colors.bone );\\r\\n\\t\\t\\t//this.renderPoint( pos, true, this.colors.bone );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( settings.render_axis )\\r\\n\\t\\t{\\r\\n\\t\\t\\tLS.Draw.push();\\r\\n\\t\\t\\tLS.Draw.multMatrix(global);\\r\\n\\t\\t\\tLS.Draw.setColor([1,1,1,1]);\\r\\n\\t\\t\\tLS.Draw.renderMesh( this.axis_mesh, gl.LINES );\\r\\n\\t\\t\\tLS.Draw.pop();\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif( settings.render_colliders )\\r\\n\\t\\tthis.renderColliders( scene );\\r\\n\\tif( settings.render_paths )\\r\\n\\t\\tthis.renderPaths( scene );\\r\\n\\r\\n\\t//Render primitives (points, lines, text) ***********************\\r\\n\\r\\n\\tif( this._points.length )\\r\\n\\t{\\r\\n\\t\\tLS.Draw.setPointSize(4);\\r\\n\\t\\tLS.Draw.setColor([1,1,1,1]);\\r\\n\\t\\tLS.Draw.renderPoints( this._points, this._points_color );\\r\\n\\t\\tthis._points.length = 0;\\r\\n\\t\\tthis._points_color.length = 0;\\r\\n\\t}\\r\\n\\r\\n\\tif( this._points_nodepth.length )\\r\\n\\t{\\r\\n\\t\\tLS.Draw.setPointSize(4);\\r\\n\\t\\tLS.Draw.setColor([1,1,1,1]);\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tLS.Draw.renderPoints( this._points_nodepth, this._points_color_nodepth );\\r\\n\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\tthis._points_nodepth.length = 0;\\r\\n\\t\\tthis._points_color_nodepth.length = 0;\\r\\n\\t}\\r\\n\\r\\n\\tif( this._lines.length )\\r\\n\\t{\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tLS.Draw.setColor([1,1,1,1]);\\r\\n\\t\\tLS.Draw.renderLines( this._lines, this._lines_color );\\r\\n\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\tthis._lines.length = 0;\\r\\n\\t\\tthis._lines_color.length = 0;\\r\\n\\t}\\r\\n\\r\\n\\tif( this.debug_points.length )\\r\\n\\t{\\r\\n\\t\\tLS.Draw.setPointSize(5);\\r\\n\\t\\tLS.Draw.setColor([1,0,1,1]);\\r\\n\\t\\tLS.Draw.renderPoints( this.debug_points );\\r\\n\\t}\\r\\n\\r\\n\\t//this require Canvas2DtoWebGL library\\r\\n\\tif( settings.render_names && gl.start2D )\\r\\n\\t{\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\t\\tvar camera2D = this.camera2D;\\r\\n\\t\\tvar viewport = gl.getViewport();\\r\\n\\t\\tcamera2D.setOrthographic(0,viewport[2], 0,viewport[3], -1,1);\\r\\n\\t\\tcamera2D.updateMatrices();\\r\\n\\t\\tgl.start2D();\\r\\n\\t\\t//gl.disable( gl.BLEND );\\r\\n\\t\\tgl.font = \\\"14px Arial\\\";\\r\\n\\t\\tvar black_color = vec4.fromValues(0,0,0,0.5);\\r\\n\\r\\n\\t\\tfor(var i = 0; i < this._names.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pos2D = camera.project( this._names[i][1] );\\r\\n\\t\\t\\tif(pos2D[2] < 0)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tpos2D[2] = 0;\\r\\n\\r\\n\\t\\t\\tvar text_size = gl.measureText( this._names[i][0] );\\r\\n\\t\\t\\tgl.fillColor = black_color;\\r\\n\\t\\t\\tgl.fillRect( Math.floor(pos2D[0] + 10), viewport[3] - (Math.floor(pos2D[1] + 8)), text_size.width, text_size.height );\\r\\n\\t\\t\\tgl.fillColor = this._names[i][2];\\r\\n\\t\\t\\tgl.fillText( this._names[i][0], Math.floor(pos2D[0] + 10), viewport[3] - (Math.floor(pos2D[1] - 4) ) );\\r\\n\\t\\t}\\r\\n\\t\\tgl.finish2D();\\r\\n\\t\\tthis._names.length = 0;\\r\\n\\t}\\r\\n\\r\\n\\t//DEBUG\\r\\n\\tif(settings.render_axis && selected_node && selected_node.transform ) //render axis for all nodes\\r\\n\\t{\\r\\n\\t\\tLS.Draw.push();\\r\\n\\t\\tvar Q = selected_node.transform.getGlobalRotation();\\r\\n\\t\\tvar R = mat4.fromQuat( mat4.create(), Q );\\r\\n\\t\\tLS.Draw.setMatrix( R );\\r\\n\\t\\tLS.Draw.setColor([1,1,1,1]);\\r\\n\\t\\tLS.Draw.scale(10,10,10);\\r\\n\\t\\tLS.Draw.renderMesh( this.axis_mesh, gl.LINES );\\r\\n\\t\\tLS.Draw.pop();\\r\\n\\t}\\r\\n\\r\\n\\tgl.depthFunc( gl.LESS );\\r\\n}\\r\\n\\r\\n//this primitives are rendered after all the components editors are rendered\\r\\nDebugRender.prototype.renderPoint = function( p, ignore_depth, c )\\r\\n{\\r\\n\\tc = c || [1,1,1,1];\\r\\n\\tif(ignore_depth)\\r\\n\\t{\\r\\n\\t\\tthis._points_nodepth.push( p[0], p[1], p[2] );\\r\\n\\t\\tthis._points_color_nodepth.push( c[0], c[1], c[2], c[3] );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tthis._points.push( p[0], p[1], p[2] );\\r\\n\\t\\tthis._points_color.push( c[0], c[1], c[2], c[3] );\\r\\n\\t}\\r\\n}\\r\\n\\r\\nDebugRender.prototype.renderLine = function( start, end, color )\\r\\n{\\r\\n\\tcolor = color || [1,1,1,1];\\r\\n\\tthis._lines.push( start, end );\\r\\n\\tthis._lines_color.push( color, color );\\r\\n}\\r\\n\\r\\nDebugRender.prototype.renderText = function( position, text, color )\\r\\n{\\r\\n\\tcolor = color || [1,1,1,1];\\r\\n\\tthis._names.push([text,position, color]);\\r\\n}\\r\\n\\r\\nDebugRender.prototype.renderGrid = function()\\r\\n{\\r\\n\\tvar settings = this.settings;\\r\\n\\r\\n\\t//textured grid\\r\\n\\tif(!this.grid_shader)\\r\\n\\t{\\r\\n\\t\\t//this.grid_shader = LS.Draw.createSurfaceShader(\\\"float PI2 = 6.283185307179586; return vec4( vec3( max(0.0, cos(pos.x * PI2 * 0.1) - 0.95) * 10.0 + max(0.0, cos(pos.z * PI2 * 0.1) - 0.95) * 10.0 ),1.0);\\\");\\r\\n\\t\\tthis.grid_shader = LS.Draw.createSurfaceShader(\\\"vec2 f = vec2(1.0/64.0,-1.0/64.0); float brightness = texture2D(u_texture, pos.xz + f).x * 0.6 + texture2D(u_texture, pos.xz * 0.1 + f ).x * 0.3 + texture2D(u_texture, pos.xz * 0.01 + f ).x * 0.2; brightness /= max(1.0,0.001 * length(u_camera_position.xz - pos.xz));vec4 color = u_color * vec4(vec3(1.0),brightness); if( abs(pos.x) < 0.1 ) color = mix(vec4(0.4,0.4,1.0,0.5),color,abs(pos.x/0.1)); if( abs(pos.z) < 0.1 ) color = mix(vec4(1.0,0.4,0.4,0.5),color,abs(pos.z/0.1)); return color;\\\");\\r\\n\\t\\t//this.grid_shader = LS.Draw.createSurfaceShader(\\\"vec2 f = vec2(1.0/64.0,-1.0/64.0); float brightness = texture2D(u_texture, pos.xz + f).x * 0.6 + texture2D(u_texture, pos.xz * 0.1 + f ).x * 0.3 + texture2D(u_texture, pos.xz * 0.01 + f ).x * 0.2; brightness /= max(1.0,0.001 * length(u_camera_position.xz - pos.xz));vec4 color = u_color * vec4(vec3(1.0),brightness); return color;\\\");\\r\\n\\t\\tthis.grid_shader_xy = LS.Draw.createSurfaceShader(\\\"vec2 f = vec2(1.0/64.0,-1.0/64.0); float brightness = texture2D(u_texture, pos.xy + f).x * 0.6 + texture2D(u_texture, pos.xy * 0.1 + f ).x * 0.3 + texture2D(u_texture, pos.xy * 0.01 + f ).x * 0.2; brightness /= max(1.0,0.001 * length(u_camera_position.xy - pos.xy));vec4 color = u_color * vec4(vec3(1.0),brightness); if( abs(pos.x) < 0.025 ) color *= vec4(0.4,1.0,0.4,1.0); if( abs(pos.y) < 0.025 ) color *= vec4(1.0,0.4,0.4,1.0); return color;\\\");\\r\\n\\t\\t//this.grid_shader_xy = LS.Draw.createSurfaceShader(\\\"vec2 f = vec2(1.0/64.0,-1.0/64.0); float brightness = texture2D(u_texture, pos.xy + f).x * 0.6 + texture2D(u_texture, pos.xy * 0.1 + f ).x * 0.3 + texture2D(u_texture, pos.xy * 0.01 + f ).x * 0.2; brightness /= max(1.0,0.001 * length(u_camera_position.xy - pos.xy));return u_color * vec4(vec3(1.0),brightness);\\\");\\r\\n\\t\\tthis.grid_shader_yz = LS.Draw.createSurfaceShader(\\\"vec2 f = vec2(1.0/64.0,-1.0/64.0); float brightness = texture2D(u_texture, pos.yz + f).x * 0.6 + texture2D(u_texture, pos.yz * 0.1 + f ).x * 0.3 + texture2D(u_texture, pos.yz * 0.01 + f ).x * 0.2; brightness /= max(1.0,0.001 * length(u_camera_position.yz - pos.yz)); vec4 color = u_color * vec4(vec3(1.0),brightness); if( abs(pos.y) < 0.025 ) color *= vec4(0.4, 0.4, 1.0, 1.0); if( abs(pos.z) < 0.025 ) color *= vec4(0.4,1.0,0.4,1.0); return color;\\\");\\r\\n\\t\\t//this.grid_shader_yz = LS.Draw.createSurfaceShader(\\\"vec2 f = vec2(1.0/64.0,-1.0/64.0); float brightness = texture2D(u_texture, pos.yz + f).x * 0.6 + texture2D(u_texture, pos.yz * 0.1 + f ).x * 0.3 + texture2D(u_texture, pos.yz * 0.01 + f ).x * 0.2; brightness /= max(1.0,0.001 * length(u_camera_position.yz - pos.yz));return u_color * vec4(vec3(1.0),brightness);\\\");\\r\\n\\t\\tthis.grid_shader.uniforms({u_texture:0});\\r\\n\\r\\n\\t\\tif( this.grid_img && this.grid_img.loaded )\\r\\n\\t\\t\\tthis.grid_texture = GL.Texture.fromImage( this.grid_img, {format: gl.RGB, wrap: gl.REPEAT, anisotropic: 4, minFilter: gl.LINEAR_MIPMAP_LINEAR } );\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.grid_texture = GL.Texture.fromURL( this.grid_texture_url, {format: gl.RGB, wrap: gl.REPEAT, anisotropic: 4, minFilter: gl.LINEAR_MIPMAP_LINEAR } );\\r\\n\\t}\\r\\n\\r\\n\\tLS.Draw.push();\\r\\n\\r\\n\\tif(settings.grid_plane == \\\"xy\\\")\\r\\n\\t\\tLS.Draw.rotate(90,1,0,0);\\r\\n\\telse if(settings.grid_plane == \\\"yz\\\")\\r\\n\\t\\tLS.Draw.rotate(90,0,0,1);\\r\\n\\r\\n\\r\\n\\tif(!this.grid_texture || this.grid_texture.ready === false)\\r\\n\\t{\\r\\n\\t\\tvar grid_scale = 1;\\t\\t\\t\\r\\n\\t\\tvar grid_alpha = 1;\\r\\n\\t\\t//lines grid\\r\\n\\t\\tLS.Draw.setColor([0.2,0.2,0.2, grid_alpha * 0.75]);\\r\\n\\t\\tLS.Draw.scale( grid_scale , grid_scale , grid_scale );\\r\\n\\t\\tLS.Draw.renderMesh( this.grid_mesh, gl.LINES );\\r\\n\\t\\tLS.Draw.scale(10,10,10);\\r\\n\\t\\tLS.Draw.renderMesh( this.grid_mesh, gl.LINES );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\t//texture grid\\r\\n\\t\\tgl.enable(gl.BLEND);\\r\\n\\t\\tthis.grid_texture.bind(0);\\r\\n\\t\\tgl.depthMask( false );\\r\\n\\t\\tLS.Draw.setColor([1,1,1, this.settings.grid_alpha ]);\\r\\n\\t\\tLS.Draw.translate( LS.Draw.camera_position[0], 0, LS.Draw.camera_position[2] ); //follow camera\\r\\n\\t\\tLS.Draw.scale( 10000, 10000, 10000 );\\r\\n\\t\\tLS.Draw.renderMesh( this.plane_mesh, gl.TRIANGLES, settings.grid_plane == \\\"xy\\\" ? this.grid_shader_xy : (settings.grid_plane == \\\"yz\\\" ? this.grid_shader_yz : this.grid_shader) );\\r\\n\\t\\tgl.depthMask( true );\\r\\n\\t}\\r\\n\\r\\n\\tLS.Draw.pop();\\r\\n}\\r\\n\\r\\nDebugRender.prototype.renderColliders = function( scene )\\r\\n{\\r\\n\\tscene = scene || LS.GlobalScene;\\r\\n\\tif(!scene._colliders)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tLS.Draw.setColor([0.33,0.71,0.71,0.5]);\\r\\n\\r\\n\\tfor(var i = 0; i < scene._colliders.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar instance = scene._colliders[i];\\r\\n\\t\\tvar oobb = instance.oobb;\\r\\n\\r\\n\\t\\tif(this.settings.render_colliders_aabb) //render AABB\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar aabb = instance.aabb;\\r\\n\\t\\t\\tLS.Draw.push();\\r\\n\\t\\t\\tvar center = BBox.getCenter(aabb);\\r\\n\\t\\t\\tvar halfsize = BBox.getHalfsize(aabb);\\r\\n\\t\\t\\tLS.Draw.translate(center);\\r\\n\\t\\t\\t//LS.Draw.setColor([0.33,0.71,0.71,0.5]);\\r\\n\\t\\t\\tLS.Draw.renderWireBox(halfsize[0]*2,halfsize[1]*2,halfsize[2]*2);\\r\\n\\t\\t\\tLS.Draw.pop();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLS.Draw.push();\\r\\n\\t\\tLS.Draw.multMatrix( instance.matrix );\\r\\n\\t\\tvar halfsize = BBox.getHalfsize(oobb);\\r\\n\\r\\n\\t\\tif(instance.type == LS.PhysicsInstance.BOX)\\r\\n\\t\\t{\\r\\n\\t\\t\\tLS.Draw.translate( BBox.getCenter(oobb) );\\r\\n\\t\\t\\tLS.Draw.renderWireBox( halfsize[0]*2, halfsize[1]*2, halfsize[2]*2 );\\r\\n\\t\\t}\\r\\n\\t\\telse if(instance.type == LS.PhysicsInstance.SPHERE)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//Draw.scale(,halfsize[0],halfsize[0]);\\r\\n\\t\\t\\tLS.Draw.translate( BBox.getCenter(oobb) );\\r\\n\\t\\t\\tLS.Draw.renderWireSphere( halfsize[0], 20 );\\r\\n\\t\\t}\\r\\n\\t\\telse if(instance.type == LS.PhysicsInstance.MESH)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mesh = instance.mesh;\\r\\n\\t\\t\\tif(mesh)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(!mesh.indexBuffers[\\\"wireframe\\\"])\\r\\n\\t\\t\\t\\t\\tmesh.computeWireframe();\\r\\n\\t\\t\\t\\tLS.Draw.renderMesh(mesh, gl.LINES, null, \\\"wireframe\\\" );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLS.Draw.pop();\\r\\n\\t}\\r\\n}\\r\\n\\r\\nDebugRender.prototype.renderPaths = function( scene )\\r\\n{\\r\\n\\tscene = scene || LS.GlobalScene;\\r\\n\\r\\n\\tif(!scene._paths)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tLS.Draw.setColor([0.7,0.6,0.3,0.5]);\\r\\n\\r\\n\\tfor(var i = 0; i < scene._paths.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar path = scene._paths[i];\\r\\n\\t\\tvar points = path.samplePoints(0);\\r\\n\\t\\tLS.Draw.renderLines( points, null, true );\\r\\n\\t}\\r\\n}\\r\\n\\r\\nDebugRender.prototype.createMeshes = function()\\r\\n{\\r\\n\\t//plane\\r\\n\\tthis.plane_mesh = GL.Mesh.plane({xz:true});\\r\\n\\r\\n\\t//grid\\r\\n\\tvar dist = 10;\\r\\n\\tvar num = 10;\\r\\n\\tvar vertices = [];\\r\\n\\tfor(var i = -num; i <= num; i++)\\r\\n\\t{\\r\\n\\t\\tvertices.push([i*dist,0,dist*num]);\\r\\n\\t\\tvertices.push([i*dist,0,-dist*num]);\\r\\n\\t\\tvertices.push([dist*num,0,i*dist]);\\r\\n\\t\\tvertices.push([-dist*num,0,i*dist]);\\r\\n\\t}\\r\\n\\tthis.grid_mesh = GL.Mesh.load({vertices:vertices});\\r\\n\\r\\n\\t//box\\r\\n\\tvertices = new Float32Array([-1,1,1 , -1,1,-1, 1,1,-1, 1,1,1, -1,-1,1, -1,-1,-1, 1,-1,-1, 1,-1,1]);\\r\\n\\tvar triangles = new Uint16Array([0,1, 0,4, 0,3, 1,2, 1,5, 2,3, 2,6, 3,7, 4,5, 4,7, 6,7, 5,6 ]);\\r\\n\\tthis.box_mesh = GL.Mesh.load({vertices: vertices, lines:triangles });\\r\\n\\r\\n\\t//circle\\r\\n\\tthis.circle_mesh = GL.Mesh.circle({size:1,slices:50});\\r\\n\\tthis.circle_empty_mesh = GL.Mesh.circle({size:1,slices:50,empty:1});\\r\\n\\tthis.sphere_mesh = GL.Mesh.icosahedron({size:1, subdivisions: 3});\\r\\n\\r\\n\\t//dummy\\r\\n\\tvertices = [];\\r\\n\\tvertices.push([-dist*0.5,0,0],[+dist*0.5,0,0]);\\r\\n\\tvertices.push([0,-dist*0.5,0],[0,+dist*0.5,0]);\\r\\n\\tvertices.push([0,0,-dist*0.5],[0,0,+dist*0.5]);\\r\\n\\tthis.dummy_mesh = GL.Mesh.load({vertices:vertices});\\r\\n\\r\\n\\t//box\\r\\n\\tvertices = [];\\r\\n\\tvertices.push([-1.0,1.0,1.0],[1.0,1.0,1.0],[-1.0,1.0,-1.0], [1.0,1.0,-1.0],[-1.0,-1.0,1.0], [1.0,-1.0,1.0],[-1.0,-1.0,-1.0], [1.0,-1.0,-1.0]);\\r\\n\\tvertices.push([1.0,-1.0,1.0],[1.0,1.0,1.0],[1.0,-1.0,-1.0],[1.0,1.0,-1.0],[-1.0,-1.0,1.0],[-1.0,1.0,1.0],[-1.0,-1.0,-1.0],[-1.0,1.0,-1.0]);\\r\\n\\tvertices.push([1.0,1.0,1.0],[1.0,1.0,-1.0],[1.0,-1.0,1.0],[1.0,-1.0,-1.0],[-1.0,1.0,1.0],[-1.0,1.0,-1.0],[-1.0,-1.0,1.0],[-1.0,-1.0,-1.0]);\\r\\n\\tthis.cube_mesh = GL.Mesh.load({vertices:vertices});\\r\\n\\r\\n\\tfor(var i = 1; i >= 0.0; i -= 0.02)\\r\\n\\t{\\r\\n\\t\\tvar f = ( 1 - 0.001/(i) )*2-1;\\r\\n\\t\\tvertices.push([-1.0,1.0,f],[1.0,1.0,f],[-1.0,-1.0,f], [1.0,-1.0,f]);\\r\\n\\t\\tvertices.push([1.0,-1.0,f],[1.0,1.0,f],[-1.0,-1.0,f],[-1.0,1.0,f]);\\r\\n\\t}\\r\\n\\r\\n\\tthis.frustum_mesh = GL.Mesh.load({vertices:vertices});\\r\\n\\r\\n\\t//cylinder\\r\\n\\tthis.cylinder_mesh = GL.Mesh.cylinder({radius:10,height:2});\\r\\n\\r\\n\\t//axis\\r\\n\\tvertices = [];\\r\\n\\tvar colors = [];\\r\\n\\tdist = 2;\\r\\n\\tvertices.push([0,0,0],[+dist*0.5,0,0]);\\r\\n\\tcolors.push([1,0,0,1],[1,0,0,1]);\\r\\n\\tvertices.push([0,0,0],[0,+dist*0.5,0]);\\r\\n\\tcolors.push([0,1,0,1],[0,1,0,1]);\\r\\n\\tvertices.push([0,0,0],[0,0,+dist*0.5]);\\r\\n\\tcolors.push([0,0,1,1],[0,0,1,1]);\\r\\n\\tthis.axis_mesh = GL.Mesh.load({vertices:vertices, colors: colors});\\r\\n\\r\\n\\t//top\\r\\n\\tvertices = [];\\r\\n\\tvertices.push([0,0,0],[0,+dist*0.5,0]);\\r\\n\\tvertices.push([0,+dist*0.5,0],[0.1*dist,+dist*0.4,0]);\\r\\n\\tvertices.push([0,+dist*0.5,0],[-0.1*dist,+dist*0.4,0]);\\r\\n\\tthis.top_line_mesh = GL.Mesh.load({vertices:vertices});\\r\\n\\r\\n\\t//front\\r\\n\\tvertices = [];\\r\\n\\tvertices.push([0,0,0],[0,0,+dist*0.5]);\\r\\n\\tvertices.push([0,0,+dist*0.5],[0,0.1*dist,+dist*0.4]);\\r\\n\\tvertices.push([0,0,+dist*0.5],[0,-0.1*dist,+dist*0.4]);\\r\\n\\tthis.front_line_mesh = GL.Mesh.load({vertices:vertices});\\r\\n}\\r\\n\\r\\nLS.DebugRender = DebugRender;\\r\\n///@FILE:../src/render/draw.js\\r\\n//this module is in charge of rendering basic objects like lines, points, and primitives\\r\\n//it works over litegl (no need of scene)\\r\\n//carefull, it is very slow\\r\\n\\r\\n/**\\r\\n* LS.Draw allows to render basic primitives, similar to the OpenGL Fixed pipeline.\\r\\n* It reuses local meshes when possible to avoid fragmenting the VRAM.\\r\\n* @class Draw\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nvar Draw = {\\r\\n\\tready: false,\\r\\n\\timages: {},\\r\\n\\timage_last_id: 1,\\r\\n\\r\\n\\tonRequestFrame: null,\\r\\n\\treset_stack_on_reset: true,\\r\\n\\t_rendercalls: 0,\\r\\n\\r\\n\\t/**\\r\\n\\t* Sets up everything (prepare meshes, shaders, and so)\\r\\n\\t* @method init\\r\\n\\t*/\\r\\n\\tinit: function()\\r\\n\\t{\\r\\n\\t\\tif(this.ready)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(!gl)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tthis.color = new Float32Array(4);\\r\\n\\t\\tthis.color[3] = 1;\\r\\n\\t\\tthis.mvp_matrix = mat4.create();\\r\\n\\t\\tthis.temp_matrix = mat4.create();\\r\\n\\t\\tthis.point_size = 2;\\r\\n\\t\\tthis.line_width = 1;\\r\\n\\r\\n\\t\\tthis.stack = new Float32Array(16 * 32); //stack max size\\r\\n\\t\\tthis.model_matrix = new Float32Array(this.stack.buffer,0,16);\\r\\n\\t\\tmat4.identity( this.model_matrix );\\r\\n\\r\\n\\t\\t//matrices\\r\\n\\t\\tthis.camera = null;\\r\\n\\t\\tthis.camera_position = vec3.create();\\r\\n\\t\\tthis.view_matrix = mat4.create();\\r\\n\\t\\tthis.projection_matrix = mat4.create();\\r\\n\\t\\tthis.viewprojection_matrix = mat4.create();\\r\\n\\r\\n\\t\\tthis.camera_stack = []; //not used yet\\r\\n\\r\\n\\t\\tthis.uniforms = {\\r\\n\\t\\t\\t\\tu_model: this.model_matrix,\\r\\n\\t\\t\\t\\tu_viewprojection: this.viewprojection_matrix,\\r\\n\\t\\t\\t\\tu_mvp: this.mvp_matrix,\\r\\n\\t\\t\\t\\tu_color: this.color,\\r\\n\\t\\t\\t\\tu_camera_position: this.camera_position,\\r\\n\\t\\t\\t\\tu_point_size: this.point_size,\\r\\n\\t\\t\\t\\tu_point_perspective: 0,\\r\\n\\t\\t\\t\\tu_perspective: 1, //viewport.w * this._projection_matrix[5]\\r\\n\\t\\t\\t\\tu_texture: 0\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\t//temp containers\\r\\n\\t\\tthis._temp = vec3.create();\\r\\n\\r\\n\\t\\t//Meshes\\r\\n\\t\\tvar vertices = [[-1,1,0],[1,1,0],[1,-1,0],[-1,-1,0]];\\r\\n\\t\\tvar coords = [[0,1],[1,1],[1,0],[0,0]];\\r\\n\\t\\tthis.quad_mesh = GL.Mesh.load({vertices:vertices, coords: coords});\\r\\n\\r\\n\\t\\tvar vertex_shader = Draw.vertex_shader_code;\\r\\n\\t\\tvar pixel_shader = Draw.fragment_shader_code;\\r\\n\\r\\n\\t\\t//create shaders\\r\\n\\t\\tthis.shader = new Shader( vertex_shader, pixel_shader );\\r\\n\\t\\tthis.shader_instancing = new Shader(vertex_shader,pixel_shader,{\\\"USE_INSTANCING\\\":\\\"\\\"});\\r\\n\\t\\tthis.shader_color = new Shader(vertex_shader,pixel_shader,{\\\"USE_COLOR\\\":\\\"\\\"});\\r\\n\\t\\tthis.shader_color_instancing = new Shader(vertex_shader,pixel_shader,{\\\"USE_COLOR\\\":\\\"\\\",\\\"USE_INSTANCING\\\":\\\"\\\"});\\r\\n\\t\\tthis.shader_texture = new Shader(vertex_shader,pixel_shader,{\\\"USE_TEXTURE\\\":\\\"\\\"});\\r\\n\\t\\tthis.shader_texture_instancing = new Shader(vertex_shader,pixel_shader,{\\\"USE_TEXTURE\\\":\\\"\\\",\\\"USE_INSTANCING\\\":\\\"\\\"});\\r\\n\\t\\tthis.shader_points = new Shader(vertex_shader,pixel_shader,{\\\"USE_POINTS\\\":\\\"\\\"});\\r\\n\\t\\tthis.shader_points_color = new Shader(vertex_shader,pixel_shader,{\\\"USE_COLOR\\\":\\\"\\\",\\\"USE_POINTS\\\":\\\"\\\"});\\r\\n\\t\\tthis.shader_points_color_size = new Shader(vertex_shader,pixel_shader,{\\\"USE_COLOR\\\":\\\"\\\",\\\"USE_SIZE\\\":\\\"\\\",\\\"USE_POINTS\\\":\\\"\\\"});\\r\\n\\r\\n\\r\\n\\t\\tthis.shader_image = new Shader('\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_mvp;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_point_size;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_PointSize = u_point_size;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = u_mvp * vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\\\r\\n\\t\\t\\t','\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t vec4 tex = texture2D(u_texture, vec2(gl_PointCoord.x,1.0 - gl_PointCoord.y) );\\\\n\\\\\\r\\n\\t\\t\\t if(tex.a < 0.01)\\\\n\\\\\\r\\n\\t\\t\\t\\tdiscard;\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = u_color * tex;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\\\r\\n\\t\\t');\\r\\n\\r\\n\\t\\tthis.shader_points_color_texture_size = new Shader('\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec4 a_color;\\\\n\\\\\\r\\n\\t\\t\\tattribute float a_extra;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_mvp;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_point_size;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec4 v_color;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tv_color = a_color;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_PointSize = u_point_size * a_extra;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = u_mvp * vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\\\r\\n\\t\\t\\t','\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec4 v_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t vec4 tex = texture2D(u_texture, vec2(gl_PointCoord.x,1.0 - gl_PointCoord.y) );\\\\n\\\\\\r\\n\\t\\t\\t if(tex.a < 0.1)\\\\n\\\\\\r\\n\\t\\t\\t\\tdiscard;\\\\n\\\\\\r\\n\\t\\t\\t vec4 color = u_color * v_color * tex;\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\\\r\\n\\t\\t');\\r\\n\\r\\n\\t\\tthis.shader_text2D = new Shader('\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec4 a_extra4;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_mvp;\\\\n\\\\\\r\\n\\t\\t\\tuniform float u_point_size;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_PointSize = u_point_size;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = u_mvp * vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\\\r\\n\\t\\t\\t','\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t vec4 tex = texture2D(u_texture, vec2(gl_PointCoord.x,1.0 - gl_PointCoord.y) );\\\\n\\\\\\r\\n\\t\\t\\t if(tex.a < 0.1)\\\\n\\\\\\r\\n\\t\\t\\t\\tdiscard;\\\\n\\\\\\r\\n\\t\\t\\t vec4 color = u_color * tex;\\\\n\\\\\\r\\n\\t\\t\\t gl_FragColor = color;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\\\r\\n\\t\\t');\\r\\n\\r\\n\\t\\t//create shaders\\r\\n\\t\\tvar phong_vertex_code = \\\"\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_normal;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_pos;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_normal;\\\\n\\\\\\r\\n\\t\\t\\t#ifdef USE_INSTANCING\\\\n\\\\\\r\\n\\t\\t\\t\\tattribute mat4 u_model;\\\\n\\\\\\r\\n\\t\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\t\\tuniform mat4 u_model;\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tv_pos = ( u_model * vec4( a_vertex, 1.0 )).xyz;\\\\n\\\\\\r\\n\\t\\t\\t\\tv_normal = (u_model * vec4(a_normal,0.0)).xyz;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = u_viewprojection * vec4( v_pos, 1.0 );\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\";\\r\\n\\t\\t\\r\\n\\t\\tvar phong_pixel_shader = \\\"\\\\n\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec3 u_ambient_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec3 u_light_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec3 u_light_dir;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_pos;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_normal;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tvec3 N = normalize(v_normal);\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat NdotL = max(0.0, dot(N,u_light_dir));\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = u_color * vec4(u_ambient_color + u_light_color * NdotL, 1.0);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\";\\r\\n\\r\\n\\t\\tthis.shader_phong = new Shader( phong_vertex_code, phong_pixel_shader);\\r\\n\\t\\tthis.shader_phong_instanced = new Shader( phong_vertex_code, phong_pixel_shader, { \\\"USE_INSTANCING\\\":\\\"\\\" } );\\r\\n\\t\\tvar phong_uniforms = {u_ambient_color:[0.1,0.1,0.1], u_light_color:[0.8,0.8,0.8], u_light_dir: [0,1,0] };\\r\\n\\t\\tthis.shader_phong.uniforms( phong_uniforms );\\r\\n\\t\\tthis.shader_phong_instanced.uniforms( phong_uniforms );\\r\\n\\r\\n\\t\\t//create shaders\\r\\n\\t\\tthis.shader_depth = new Shader('\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec4 v_pos;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_model;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_mvp;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tv_pos = u_model * vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = u_mvp * vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\\\r\\n\\t\\t\\t','\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec4 v_pos;\\\\n\\\\\\r\\n\\t\\t\\t\\\\n\\\\\\r\\n\\t\\t\\tvec4 PackDepth32(float depth)\\\\n\\\\\\r\\n\\t\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\t\\tconst vec4 bitSh = vec4( 256*256*256, 256*256, 256, 1);\\\\n\\\\\\r\\n\\t\\t\\t\\tconst vec4 bitMsk = vec4( 0, 1.0/256.0, 1.0/256.0, 1.0/256.0);\\\\n\\\\\\r\\n\\t\\t\\t\\tvec4 comp;\\\\n\\\\\\r\\n\\t\\t\\t\\tcomp\\t= depth * bitSh;\\\\n\\\\\\r\\n\\t\\t\\t\\tcomp\\t= fract(comp);\\\\n\\\\\\r\\n\\t\\t\\t\\tcomp\\t-= comp.xxyz * bitMsk;\\\\n\\\\\\r\\n\\t\\t\\t\\treturn comp;\\\\n\\\\\\r\\n\\t\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tfloat depth = (v_pos.z / v_pos.w) * 0.5 + 0.5;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = PackDepth32(depth);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\\\r\\n\\t\\t');\\r\\n\\r\\n\\t\\tthis.ready = true;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* A helper to create shaders when you only want to specify some basic shading\\r\\n\\t* @method createSurfaceShader\\r\\n\\t* @param {string} surface_function GLSL code like: \\\"vec4 surface_function( vec3 pos, vec3 normal, vec2 coord ) { return vec4(1.0); } \\\";\\r\\n\\t* @param {object} macros [optional] object containing the macros and value\\r\\n\\t* @param {object} uniforms [optional] object with name and type\\r\\n\\t* @return {GL.Shader} the resulting shader\\r\\n\\t*/\\r\\n\\tcreateSurfaceShader: function( surface_function, uniforms, macros )\\r\\n\\t{\\r\\n\\t\\t//\\\"vec4 surface_function( vec3 pos, vec3 normal, vec2 coord ) { return vec4(1.0); } \\\";\\r\\n\\r\\n\\t\\tif( surface_function.indexOf(\\\"surface_function\\\") == -1 )\\r\\n\\t\\t\\tsurface_function = \\\"vec4 surface_function( vec3 pos, vec3 normal, vec2 coord ) { \\\" + surface_function + \\\"\\\\n } \\\";\\r\\n\\r\\n\\t\\tif(uniforms)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif (uniforms.constructor === String)\\r\\n\\t\\t\\t\\tsurface_function = uniforms + \\\";\\\\n\\\" + surface_function;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tfor(var i in uniforms)\\r\\n\\t\\t\\t\\t\\tsurface_function += \\\"uniform \\\" + uniforms[i] + \\\" \\\" + i + \\\";\\\\n\\\";\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar vertex_shader = \\\"\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec3 a_normal;\\\\n\\\\\\r\\n\\t\\t\\tattribute vec2 a_coord;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_pos;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_normal;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_mvp;\\\\n\\\\\\r\\n\\t\\t\\tuniform mat4 u_model;\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tv_coord = a_coord;\\\\n\\\\\\r\\n\\t\\t\\t\\tv_pos = (u_model * vec4(a_vertex,1.0)).xyz;\\\\n\\\\\\r\\n\\t\\t\\t\\tv_normal = (u_model * vec4(a_normal,0.0)).xyz;\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_Position = u_mvp * vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\\\r\\n\\t\\t\\t\\\";\\r\\n\\r\\n\\t\\tvar pixel_shader = \\\"\\\\\\r\\n\\t\\t\\tprecision mediump float;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_pos;\\\\n\\\\\\r\\n\\t\\t\\tvarying vec3 v_normal;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t\\t\\tuniform vec3 u_camera_position;\\\\n\\\\\\r\\n\\t\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t\\t\\t\\\"+ surface_function +\\\"\\\\n\\\\\\r\\n\\t\\t\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t\\t\\tgl_FragColor = surface_function(v_pos,v_normal,v_coord);\\\\n\\\\\\r\\n\\t\\t\\t}\\\\\\r\\n\\t\\t\\\";\\t\\r\\n\\r\\n\\t\\treturn new GL.Shader( vertex_shader, pixel_shader, macros );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* clears the stack\\r\\n\\t* @method reset\\r\\n\\t*/\\r\\n\\treset: function( reset_memory )\\r\\n\\t{\\r\\n\\t\\tif(!this.ready)\\r\\n\\t\\t\\tthis.init();\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.color.set([1,1,1,1]);\\r\\n\\t\\t\\tthis.point_size = 2;\\r\\n\\t\\t\\tthis.line_width = 1;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( reset_memory )\\r\\n\\t\\t\\tthis.images = {}; //clear images\\r\\n\\r\\n\\t\\tif(this.reset_stack_on_reset)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.model_matrix = new Float32Array(this.stack.buffer,0,16);\\r\\n\\t\\t\\tthis.uniforms.u_model = this.model_matrix;\\r\\n\\t\\t\\tmat4.identity( this.model_matrix );\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Sets the color used to paint primitives\\r\\n\\t* @method setColor\\r\\n\\t* @param {vec3|vec4} color\\r\\n\\t*/\\r\\n\\tsetColor: function(color)\\r\\n\\t{\\r\\n\\t\\tif( arguments.length >= 3 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.color[0] = arguments[0];\\r\\n\\t\\t\\tthis.color[1] = arguments[1];\\r\\n\\t\\t\\tthis.color[2] = arguments[2];\\r\\n\\t\\t\\tif( arguments.length == 4 )\\r\\n\\t\\t\\t\\tthis.color[3] = arguments[3];\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\tfor(var i = 0; i < color.length; i++)\\r\\n\\t\\t\\tthis.color[i] = color[i];\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Sets the alpha used to paint primitives\\r\\n\\t* @method setAlpha\\r\\n\\t* @param {number} alpha\\r\\n\\t*/\\r\\n\\tsetAlpha: function(alpha)\\r\\n\\t{\\r\\n\\t\\tthis.color[3] = alpha;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Sets the point size\\r\\n\\t* @method setPointSize\\r\\n\\t* @param {number} v size of points\\r\\n\\t* @param {number} perspective [optional] if set to true, the points will be affected by perspective\\r\\n\\t*/\\r\\n\\tsetPointSize: function(v, perspective)\\r\\n\\t{\\r\\n\\t\\tthis.point_size = v;\\r\\n\\t\\tthis.uniforms.u_point_size = v;\\r\\n\\t\\tthis.uniforms.u_point_perspective = perspective ? 1 : 0;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Sets the line width\\r\\n\\t* @method setLineWidth\\r\\n\\t* @param {number} v width in pixels\\r\\n\\t*/\\r\\n\\tsetLineWidth: function(v)\\r\\n\\t{\\r\\n\\t\\tif(gl.setLineWidth)\\r\\n\\t\\t\\tgl.setLineWidth(v);\\r\\n\\t\\telse\\r\\n\\t\\t\\tgl.lineWidth(v);\\r\\n\\t\\tthis.line_width = v;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Sets the camera to use during the rendering, this is already done by LS.Renderer\\r\\n\\t* @method setCamera\\r\\n\\t* @param {LS.Camera} camera\\r\\n\\t*/\\r\\n\\tsetCamera: function( camera )\\r\\n\\t{\\r\\n\\t\\tthis.camera = camera;\\r\\n\\t\\tcamera.updateMatrices();\\r\\n\\t\\tvec3.copy( this.camera_position, camera.getEye() );\\t\\r\\n\\t\\tthis.view_matrix.set( camera._view_matrix );\\r\\n\\t\\tthis.projection_matrix.set( camera._projection_matrix );\\r\\n\\t\\tthis.viewprojection_matrix.set( camera._viewprojection_matrix );\\r\\n\\t\\tthis.uniforms.u_perspective = gl.viewport_data[3] * this.projection_matrix[5];\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Specifies the camera position (used to compute point size)\\r\\n\\t* @method setCameraPosition\\r\\n\\t* @param {vec3} center\\r\\n\\t*/\\r\\n\\tsetCameraPosition: function(center)\\r\\n\\t{\\r\\n\\t\\tvec3.copy( this.camera_position, center);\\r\\n\\t},\\r\\n\\r\\n\\tpushCamera: function()\\r\\n\\t{\\r\\n\\t\\tthis.camera_stack.push( mat4.create( this.viewprojection_matrix ) );\\r\\n\\t},\\r\\n\\r\\n\\tpopCamera: function()\\r\\n\\t{\\r\\n\\t\\tif(this.camera_stack.length == 0)\\r\\n\\t\\t\\tthrow(\\\"too many pops\\\");\\r\\n\\t\\tthis.viewprojection_matrix.set( this.camera_stack.pop() );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Specifies the camera view and projection matrices\\r\\n\\t* @method setViewProjectionMatrix\\r\\n\\t* @param {mat4} view\\r\\n\\t* @param {mat4} projection\\r\\n\\t* @param {mat4} vp viewprojection matrix [optional]\\r\\n\\t*/\\r\\n\\tsetViewProjectionMatrix: function(view, projection, vp)\\r\\n\\t{\\r\\n\\t\\tmat4.copy( this.view_matrix, view);\\r\\n\\t\\tmat4.copy( this.projection_matrix, projection);\\r\\n\\t\\tif(vp)\\r\\n\\t\\t\\tmat4.copy( this.viewprojection_matrix, vp);\\r\\n\\t\\telse\\r\\n\\t\\t\\tmat4.multiply( this.viewprojection_matrix, view, vp);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Specifies the transformation matrix to apply to the mesh\\r\\n\\t* @method setMatrix\\r\\n\\t* @param {mat4} matrix\\r\\n\\t*/\\r\\n\\tsetMatrix: function(matrix)\\r\\n\\t{\\r\\n\\t\\tmat4.copy(this.model_matrix, matrix);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Multiplies the current matrix by a given one\\r\\n\\t* @method multMatrix\\r\\n\\t* @param {mat4} matrix\\r\\n\\t*/\\r\\n\\tmultMatrix: function(matrix)\\r\\n\\t{\\r\\n\\t\\tmat4.multiply(this.model_matrix, matrix, this.model_matrix);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Render lines given a set of points\\r\\n\\t* @method renderLines\\r\\n\\t* @param {Float32Array|Array} points\\r\\n\\t* @param {Float32Array|Array} colors [optional]\\r\\n\\t* @param {bool} strip [optional] if the lines are a line strip (one consecutive line)\\r\\n\\t* @param {bool} loop [optional] if strip, close loop\\r\\n\\t*/\\r\\n\\trenderLines: function(lines, colors, strip, loop)\\r\\n\\t{\\r\\n\\t\\tif(!lines || !lines.length) return;\\r\\n\\t\\tvar vertices = null;\\r\\n\\r\\n\\t\\tvertices = lines.constructor == Float32Array ? lines : this.linearize(lines);\\r\\n\\t\\tif(colors)\\r\\n\\t\\t\\tcolors = colors.constructor == Float32Array ? colors : this.linearize(colors);\\r\\n\\t\\tif(colors && (colors.length/4) != (vertices.length/3))\\r\\n\\t\\t\\tcolors = null;\\r\\n\\r\\n\\t\\tvar type = gl.LINES;\\r\\n\\t\\tif(loop)\\r\\n\\t\\t\\ttype = gl.LINE_LOOP;\\r\\n\\t\\telse if(strip)\\r\\n\\t\\t\\ttype = gl.LINE_STRIP;\\r\\n\\r\\n\\t\\tvar mesh = this.toGlobalMesh({vertices: vertices, colors: colors});\\r\\n\\t\\treturn this.renderMesh( mesh, type, colors ? this.shader_color : this.shader, undefined, 0, vertices.length / 3 );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Render points given a set of positions (and colors)\\r\\n\\t* @method renderPoints\\r\\n\\t* @param {Float32Array|Array} points\\r\\n\\t* @param {Float32Array|Array} colors [optional]\\r\\n\\t* @param {GL.Shader} shader [optional]\\r\\n\\t*/\\r\\n\\trenderPoints: function(points, colors, shader)\\r\\n\\t{\\r\\n\\t\\tif(!points || !points.length)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar vertices = null;\\r\\n\\r\\n\\t\\tif(points.constructor == Float32Array)\\r\\n\\t\\t\\tvertices = points;\\r\\n\\t\\telse if(points[0].length) //array of arrays\\r\\n\\t\\t\\tvertices = this.linearize(points);\\r\\n\\t\\telse\\r\\n\\t\\t\\tvertices = new Float32Array(points);\\r\\n\\r\\n\\t\\tif(colors && colors.constructor != Float32Array)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(colors.constructor === Array && colors[0].constructor === Number)\\r\\n\\t\\t\\t\\tcolors = new Float32Array( colors );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tcolors = this.linearize(colors);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar mesh = this.toGlobalMesh({vertices: vertices, colors: colors});\\r\\n\\t\\tif(!shader)\\r\\n\\t\\t\\tshader = colors ? this.shader_color : this.shader;\\r\\n\\r\\n\\t\\treturn this.renderMesh(mesh, gl.POINTS, shader, undefined, 0, vertices.length / 3 );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Render round points given a set of positions (and colors)\\r\\n\\t* @method renderRoundPoints\\r\\n\\t* @param {Float32Array|Array} points\\r\\n\\t* @param {Float32Array|Array} colors [optional]\\r\\n\\t* @param {GL.Shader} shader [optional]\\r\\n\\t*/\\r\\n\\trenderRoundPoints: function(points, colors, shader)\\r\\n\\t{\\r\\n\\t\\tif(!points || !points.length)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar vertices = null;\\r\\n\\r\\n\\t\\tif(points.constructor == Float32Array)\\r\\n\\t\\t\\tvertices = points;\\r\\n\\t\\telse if(points[0].length) //array of arrays\\r\\n\\t\\t\\tvertices = this.linearize(points);\\r\\n\\t\\telse\\r\\n\\t\\t\\tvertices = new Float32Array(points);\\r\\n\\r\\n\\t\\tif(colors)\\r\\n\\t\\t\\tcolors = colors.constructor == Float32Array ? colors : this.linearize(colors);\\r\\n\\r\\n\\t\\tvar mesh = this.toGlobalMesh({vertices: vertices, colors: colors});\\r\\n\\t\\tif(!shader)\\r\\n\\t\\t\\tshader = colors ? this.shader_points_color : this.shader_points;\\r\\n\\t\\treturn this.renderMesh( mesh, gl.POINTS, shader, undefined, 0, vertices.length / 3 );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Render points with color, size, and texture binded in 0\\r\\n\\t* @method renderPointsWithSize\\r\\n\\t* @param {Float32Array|Array} points\\r\\n\\t* @param {Float32Array|Array} colors [optional]\\r\\n\\t* @param {Float32Array|Array} sizes [optional]\\r\\n\\t* @param {GL.Texture} texture [optional]\\r\\n\\t* @param {GL.Shader} shader [optional]\\r\\n\\t*/\\r\\n\\trenderPointsWithSize: function(points, colors, sizes, texture, shader)\\r\\n\\t{\\r\\n\\t\\tif(!points || !points.length) return;\\r\\n\\t\\tvar vertices = null;\\r\\n\\r\\n\\t\\tif(points.constructor == Float32Array)\\r\\n\\t\\t\\tvertices = points;\\r\\n\\t\\telse if(points[0].length) //array of arrays\\r\\n\\t\\t\\tvertices = this.linearize(points);\\r\\n\\t\\telse\\r\\n\\t\\t\\tvertices = new Float32Array(points);\\r\\n\\r\\n\\t\\tif(!colors)\\r\\n\\t\\t\\tthrow(\\\"colors required in Draw.renderPointsWithSize\\\");\\r\\n\\t\\tcolors = colors.constructor == Float32Array ? colors : this.linearize(colors);\\r\\n\\t\\tif(!sizes)\\r\\n\\t\\t\\tthrow(\\\"sizes required in Draw.renderPointsWithSize\\\");\\r\\n\\t\\tsizes = sizes.constructor == Float32Array ? sizes : this.linearize(sizes);\\r\\n\\r\\n\\t\\tvar mesh = this.toGlobalMesh({vertices: vertices, colors: colors, extra: sizes});\\r\\n\\t\\tshader = shader || (texture ? this.shader_points_color_texture_size : this.shader_points_color_size);\\r\\n\\t\\t\\r\\n\\t\\treturn this.renderMesh(mesh, gl.POINTS, shader, undefined, 0, vertices.length / 3 );\\r\\n\\t},\\r\\n\\r\\n\\tcreateRectangleMesh: function( width, height, in_z, use_global )\\r\\n\\t{\\r\\n\\t\\tvar vertices = new Float32Array(4 * 3);\\r\\n\\t\\tif(in_z)\\r\\n\\t\\t\\tvertices.set([-width*0.5,0,height*0.5, width*0.5,0,height*0.5, width*0.5,0,-height*0.5, -width*0.5,0,-height*0.5]);\\r\\n\\t\\telse\\r\\n\\t\\t\\tvertices.set([-width*0.5,height*0.5,0, width*0.5,height*0.5,0, width*0.5,-height*0.5,0, -width*0.5,-height*0.5,0]);\\r\\n\\r\\n\\t\\tif(use_global)\\r\\n\\t\\t\\treturn this.toGlobalMesh( {vertices: vertices} );\\r\\n\\r\\n\\t\\treturn GL.Mesh.load({vertices: vertices});\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Render a wireframe rectangle of width x height \\r\\n\\t* @method renderRectangle\\r\\n\\t* @param {number} width\\r\\n\\t* @param {number} height\\r\\n\\t* @param {boolean} in_z [optional] if the plane is aligned with the z plane\\r\\n\\t*/\\r\\n\\trenderRectangle: function(width, height, in_z, fill)\\r\\n\\t{\\r\\n\\t\\tvar mesh = this.createRectangleMesh( width, height, in_z, true );\\r\\n\\t\\treturn this.renderMesh( mesh, fill ? gl.TRIANGLE_FAN : gl.LINE_LOOP, undefined, undefined, 0, this._global_mesh_last_size );\\r\\n\\t},\\r\\n\\r\\n\\tcreateCircleMesh: function(radius, segments, in_z, use_global)\\r\\n\\t{\\r\\n\\t\\tsegments = segments || 32;\\r\\n\\t\\tvar axis = [0,1,0];\\r\\n\\t\\tvar num_segments = segments || 100;\\r\\n\\t\\tvar R = quat.create();\\r\\n\\t\\tvar temp = this._temp;\\r\\n\\t\\tvar vertices = new Float32Array(num_segments * 3);\\r\\n\\r\\n\\t\\tvar offset = 2 * Math.PI / num_segments;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < num_segments; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\ttemp[0] = Math.sin(offset * i) * radius;\\r\\n\\t\\t\\tif(in_z)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttemp[1] = 0;\\r\\n\\t\\t\\t\\ttemp[2] = Math.cos(offset * i) * radius;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttemp[2] = 0;\\r\\n\\t\\t\\t\\ttemp[1] = Math.cos(offset * i) * radius;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvertices.set(temp, i*3);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(use_global)\\r\\n\\t\\t\\treturn this.toGlobalMesh({vertices: vertices});\\r\\n\\r\\n\\t\\treturn GL.Mesh.load({vertices: vertices});\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a circle \\r\\n\\t* @method renderCircle\\r\\n\\t* @param {number} radius\\r\\n\\t* @param {number} segments\\r\\n\\t* @param {boolean} in_z [optional] if the circle is aligned with the z plane\\r\\n\\t* @param {boolean} filled [optional] renders the interior\\r\\n\\t*/\\r\\n\\trenderCircle: function(radius, segments, in_z, filled)\\r\\n\\t{\\r\\n\\t\\tvar mesh = this.createCircleMesh(radius, segments, in_z, true);\\r\\n\\t\\treturn this.renderMesh(mesh, filled ? gl.TRIANGLE_FAN : gl.LINE_LOOP, undefined, undefined, 0, this._global_mesh_last_size );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Render a filled circle\\r\\n\\t* @method renderSolidCircle\\r\\n\\t* @param {number} radius\\r\\n\\t* @param {number} segments\\r\\n\\t* @param {boolean} in_z [optional] if the circle is aligned with the z plane\\r\\n\\t*/\\r\\n\\trenderSolidCircle: function(radius, segments, in_z)\\r\\n\\t{\\r\\n\\t\\treturn this.renderCircle(radius, segments, in_z, true);\\r\\n\\t},\\r\\n\\r\\n\\tcreateWireSphereMesh: function(radius, segments, use_global )\\r\\n\\t{\\r\\n\\t\\tvar axis = [0,1,0];\\r\\n\\t\\tsegments = segments || 100;\\r\\n\\t\\tvar R = quat.create();\\r\\n\\t\\tvar temp = this._temp;\\r\\n\\t\\tvar vertices = new Float32Array( segments * 2 * 3 * 3); \\r\\n\\r\\n\\t\\tvar delta = 1.0 / segments * Math.PI * 2;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < segments; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\ttemp.set([ Math.sin( i * delta) * radius, Math.cos( i * delta) * radius, 0]);\\r\\n\\t\\t\\tvertices.set(temp, i*18);\\r\\n\\t\\t\\ttemp.set([Math.sin( (i+1) * delta) * radius, Math.cos( (i+1) * delta) * radius, 0]);\\r\\n\\t\\t\\tvertices.set(temp, i*18 + 3);\\r\\n\\r\\n\\t\\t\\ttemp.set([ Math.sin( i * delta) * radius, 0, Math.cos( i * delta) * radius ]);\\r\\n\\t\\t\\tvertices.set(temp, i*18 + 6);\\r\\n\\t\\t\\ttemp.set([Math.sin( (i+1) * delta) * radius, 0, Math.cos( (i+1) * delta) * radius ]);\\r\\n\\t\\t\\tvertices.set(temp, i*18 + 9);\\r\\n\\r\\n\\t\\t\\ttemp.set([ 0, Math.sin( i * delta) * radius, Math.cos( i * delta) * radius ]);\\r\\n\\t\\t\\tvertices.set(temp, i*18 + 12);\\r\\n\\t\\t\\ttemp.set([ 0, Math.sin( (i+1) * delta) * radius, Math.cos( (i+1) * delta) * radius ]);\\r\\n\\t\\t\\tvertices.set(temp, i*18 + 15);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(use_global)\\r\\n\\t\\t\\treturn this.toGlobalMesh({vertices: vertices});\\r\\n\\t\\t\\r\\n\\t\\treturn GL.Mesh.load({vertices: vertices});\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders three circles to form a simple spherical shape\\r\\n\\t* @method renderWireSphere\\r\\n\\t* @param {number} radius\\r\\n\\t* @param {number} segments\\r\\n\\t*/\\r\\n\\trenderWireSphere: function(radius, segments)\\r\\n\\t{\\r\\n\\t\\tvar mesh = this.createWireSphereMesh( radius, segments, true );\\r\\n\\t\\treturn this.renderMesh( mesh, gl.LINES, undefined, undefined, 0, this._global_mesh_last_size );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders an sphere\\r\\n\\t* @method renderSolidSphere\\r\\n\\t* @param {number} radius\\r\\n\\t*/\\r\\n\\trenderSolidSphere: function(radius)\\r\\n\\t{\\r\\n\\t\\tvar mesh = this._sphere_mesh;\\r\\n\\t\\tif(!this._sphere_mesh)\\r\\n\\t\\t\\tmesh = this._sphere_mesh = GL.Mesh.sphere({ size: 1 });\\r\\n\\t\\tthis.push();\\r\\n\\t\\tthis.scale( radius,radius,radius );\\r\\n\\t\\tthis.renderMesh( mesh, gl.TRIANGLES );\\r\\n\\t\\tthis.pop();\\r\\n\\t},\\r\\n\\r\\n\\r\\n\\tcreateWireBoxMesh: function( sizex, sizey, sizez, use_global )\\r\\n\\t{\\r\\n\\t\\tsizex = sizex*0.5;\\r\\n\\t\\tsizey = sizey*0.5;\\r\\n\\t\\tsizez = sizez*0.5;\\r\\n\\t\\tvar vertices = new Float32Array([-sizex,sizey,sizez , -sizex,sizey,-sizez, sizex,sizey,-sizez, sizex,sizey,sizez,\\r\\n\\t\\t\\t\\t\\t\\t-sizex,-sizey,sizez, -sizex,-sizey,-sizez, sizex,-sizey,-sizez, sizex,-sizey,sizez]);\\r\\n\\t\\tvar triangles = new Uint16Array([0,1, 0,4, 0,3, 1,2, 1,5, 2,3, 2,6, 3,7, 4,5, 4,7, 6,7, 5,6 ]);\\r\\n\\r\\n\\t\\tif(use_global)\\r\\n\\t\\t\\treturn this.toGlobalMesh( {vertices: vertices}, triangles );\\r\\n\\r\\n\\t\\treturn GL.Mesh.load({vertices: vertices, lines:triangles });\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a wire box (box made of lines, not filled)\\r\\n\\t* @method renderWireBox\\r\\n\\t* @param {number} sizex\\r\\n\\t* @param {number} sizey\\r\\n\\t* @param {number} sizez\\r\\n\\t*/\\r\\n\\trenderWireBox: function(sizex,sizey,sizez)\\r\\n\\t{\\r\\n\\t\\tvar mesh = this.createWireBoxMesh(sizex,sizey,sizez, true);\\r\\n\\t\\treturn this.renderMesh( mesh, gl.LINES, undefined, \\\"indices\\\", 0, this._global_mesh_last_size );\\r\\n\\t},\\r\\n\\r\\n\\tcreateSolidBoxMesh: function( sizex,sizey,sizez, use_global)\\r\\n\\t{\\r\\n\\t\\tsizex = sizex*0.5;\\r\\n\\t\\tsizey = sizey*0.5;\\r\\n\\t\\tsizez = sizez*0.5;\\r\\n\\t\\t//var vertices = [[-sizex,sizey,-sizez],[-sizex,-sizey,+sizez],[-sizex,sizey,sizez],[-sizex,sizey,-sizez],[-sizex,-sizey,-sizez],[-sizex,-sizey,+sizez],[sizex,sizey,-sizez],[sizex,sizey,sizez],[sizex,-sizey,+sizez],[sizex,sizey,-sizez],[sizex,-sizey,+sizez],[sizex,-sizey,-sizez],[-sizex,sizey,sizez],[sizex,-sizey,sizez],[sizex,sizey,sizez],[-sizex,sizey,sizez],[-sizex,-sizey,sizez],[sizex,-sizey,sizez],[-sizex,sizey,-sizez],[sizex,sizey,-sizez],[sizex,-sizey,-sizez],[-sizex,sizey,-sizez],[sizex,-sizey,-sizez],[-sizex,-sizey,-sizez],[-sizex,sizey,-sizez],[sizex,sizey,sizez],[sizex,sizey,-sizez],[-sizex,sizey,-sizez],[-sizex,sizey,sizez],[sizex,sizey,sizez],[-sizex,-sizey,-sizez],[sizex,-sizey,-sizez],[sizex,-sizey,sizez],[-sizex,-sizey,-sizez],[sizex,-sizey,sizez],[-sizex,-sizey,sizez]];\\r\\n\\t\\tvar vertices = [-sizex,sizey,-sizez,-sizex,-sizey,+sizez,-sizex,sizey,sizez,-sizex,sizey,-sizez,-sizex,-sizey,-sizez,-sizex,-sizey,+sizez,sizex,sizey,-sizez,sizex,sizey,sizez,sizex,-sizey,+sizez,sizex,sizey,-sizez,sizex,-sizey,+sizez,sizex,-sizey,-sizez,-sizex,sizey,sizez,sizex,-sizey,sizez,sizex,sizey,sizez,-sizex,sizey,sizez,-sizex,-sizey,sizez,sizex,-sizey,sizez,-sizex,sizey,-sizez,sizex,sizey,-sizez,sizex,-sizey,-sizez,-sizex,sizey,-sizez,sizex,-sizey,-sizez,-sizex,-sizey,-sizez,-sizex,sizey,-sizez,sizex,sizey,sizez,sizex,sizey,-sizez,-sizex,sizey,-sizez,-sizex,sizey,sizez,sizex,sizey,sizez,-sizex,-sizey,-sizez,sizex,-sizey,-sizez,sizex,-sizey,sizez,-sizex,-sizey,-sizez,sizex,-sizey,sizez,-sizex,-sizey,sizez];\\r\\n\\t\\tif(use_global)\\r\\n\\t\\t\\treturn this.toGlobalMesh( {vertices: vertices} );\\r\\n\\r\\n\\t\\treturn GL.Mesh.load({vertices: vertices });\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a solid box \\r\\n\\t* @method renderSolidBox\\r\\n\\t* @param {number} sizex\\r\\n\\t* @param {number} sizey\\r\\n\\t* @param {number} sizez\\r\\n\\t*/\\r\\n\\trenderSolidBox: function(sizex,sizey,sizez)\\r\\n\\t{\\r\\n\\t\\tvar mesh = this.createSolidBoxMesh(sizex,sizey,sizez, true);\\r\\n\\t\\treturn this.renderMesh( mesh, gl.TRIANGLES, undefined, undefined, 0, this._global_mesh_last_size );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a wire cube of size size\\r\\n\\t* @method renderWireCube\\r\\n\\t* @param {number} size\\r\\n\\t*/\\r\\n\\trenderWireCube: function(size)\\r\\n\\t{\\r\\n\\t\\treturn this.renderWireBox(size,size,size);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a solid cube of size size\\r\\n\\t* @method renderSolidCube\\r\\n\\t* @param {number} size\\r\\n\\t*/\\r\\n\\trenderSolidCube: function(size)\\r\\n\\t{\\r\\n\\t\\treturn this.renderSolidBox(size,size,size);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a solid plane (could be textured or even with an specific shader)\\r\\n\\t* @method renderPlane\\r\\n\\t* @param {vec3} position\\r\\n\\t* @param {vec2} size\\r\\n\\t* @param {GL.Texture} texture\\r\\n\\t* @param {GL.Shader} shader\\r\\n\\t*/\\r\\n\\trenderPlane: function( position, size, texture, shader)\\r\\n\\t{\\r\\n\\t\\tif(!position || !size)\\r\\n\\t\\t\\tthrow(\\\"LS.Draw.renderPlane param missing\\\");\\r\\n\\r\\n\\t\\tthis.push();\\r\\n\\t\\tthis.translate(position);\\r\\n\\t\\tthis.scale( size[0], size[1], 1 );\\r\\n\\t\\tif(texture)\\r\\n\\t\\t\\ttexture.bind(0);\\r\\n\\r\\n\\t\\tif(!shader && texture)\\r\\n\\t\\t\\tshader = this.shader_texture;\\r\\n\\r\\n\\t\\tthis.renderMesh(this.quad_mesh, gl.TRIANGLE_FAN, shader );\\r\\n\\r\\n\\t\\tif(texture)\\r\\n\\t\\t\\ttexture.unbind(0);\\r\\n\\t\\t\\r\\n\\t\\tthis.pop();\\r\\n\\t},\\t\\r\\n\\r\\n\\tcreateGridMesh: function(dist,num)\\r\\n\\t{\\r\\n\\t\\tdist = dist || 20;\\r\\n\\t\\tnum = num || 10;\\r\\n\\t\\tvar vertices = new Float32Array( (num*2+1) * 4 * 3);\\r\\n\\t\\tvar pos = 0;\\r\\n\\t\\tfor(var i = -num; i <= num; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvertices.set( [i*dist,0,dist*num], pos);\\r\\n\\t\\t\\tvertices.set( [i*dist,0,-dist*num],pos+3);\\r\\n\\t\\t\\tvertices.set( [dist*num,0,i*dist], pos+6);\\r\\n\\t\\t\\tvertices.set( [-dist*num,0,i*dist],pos+9);\\r\\n\\t\\t\\tpos += 3*4;\\r\\n\\t\\t}\\r\\n\\t\\treturn GL.Mesh.load({vertices: vertices});\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a grid of lines\\r\\n\\t* @method renderGrid\\r\\n\\t* @param {number} dist distance between lines\\r\\n\\t* @param {number} num number of lines\\r\\n\\t*/\\r\\n\\trenderGrid: function(dist,num)\\r\\n\\t{\\r\\n\\t\\tvar mesh = this.createGridMesh(dist,num);\\r\\n\\t\\treturn this.renderMesh(mesh, gl.LINES);\\r\\n\\t},\\r\\n\\r\\n\\tcreateConeMesh: function(radius, height, segments, in_z, use_global )\\r\\n\\t{\\r\\n\\t\\tvar axis = [0,1,0];\\r\\n\\t\\tsegments = segments || 100;\\r\\n\\t\\tvar R = quat.create();\\r\\n\\t\\tvar temp = this._temp;\\r\\n\\t\\tvar vertices = new Float32Array( (segments+2) * 3);\\r\\n\\t\\tvertices.set(in_z ? [0,0,height] : [0,height,0], 0);\\r\\n\\r\\n\\t\\tfor(var i = 0; i <= segments; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tquat.setAxisAngle(R,axis, 2 * Math.PI * (i/segments) );\\r\\n\\t\\t\\tvec3.transformQuat(temp, [0,0,radius], R );\\r\\n\\t\\t\\tif(in_z)\\r\\n\\t\\t\\t\\tvec3.set(temp, temp[0],temp[2],temp[1] );\\r\\n\\t\\t\\tvertices.set(temp, i*3+3);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(use_global)\\r\\n\\t\\t\\treturn this.toGlobalMesh( {vertices: vertices} );\\r\\n\\r\\n\\t\\treturn GL.Mesh.load({vertices: vertices});\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a cone \\r\\n\\t* @method renderCone\\r\\n\\t* @param {number} radius\\r\\n\\t* @param {number} height\\r\\n\\t* @param {number} segments\\r\\n\\t* @param {boolean} in_z aligned with z axis\\r\\n\\t*/\\r\\n\\trenderCone: function(radius, height, segments, in_z)\\r\\n\\t{\\r\\n\\t\\tvar mesh = this.createConeMesh(radius, height, segments, in_z, true);\\r\\n\\t\\treturn this.renderMesh(mesh, gl.TRIANGLE_FAN, undefined, undefined, 0, this._global_mesh_last_size );\\r\\n\\t},\\r\\n\\r\\n\\tcreateCylinderMesh: function( radius, height, segments, in_z, use_global )\\r\\n\\t{\\r\\n\\t\\tvar axis = [0,1,0];\\r\\n\\t\\tsegments = segments || 100;\\r\\n\\t\\tvar R = quat.create();\\r\\n\\t\\tvar temp = this._temp;\\r\\n\\t\\tvar vertices = new Float32Array( (segments+1) * 3 * 2);\\r\\n\\r\\n\\t\\tfor(var i = 0; i <= segments; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tquat.setAxisAngle(R, axis, 2 * Math.PI * (i/segments) );\\r\\n\\t\\t\\tvec3.transformQuat(temp, [0,0,radius], R );\\r\\n\\t\\t\\tvertices.set(temp, i*3*2+3);\\r\\n\\t\\t\\ttemp[1] = height;\\r\\n\\t\\t\\tvertices.set(temp, i*3*2);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(use_global)\\r\\n\\t\\t\\treturn this.toGlobalMesh( {vertices: vertices} );\\r\\n\\r\\n\\t\\treturn GL.Mesh.load({vertices: vertices});\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a cylinder\\r\\n\\t* @method renderCylinder\\r\\n\\t* @param {number} radius\\r\\n\\t* @param {number} height\\r\\n\\t* @param {number} segments\\r\\n\\t* @param {boolean} in_z aligned with z axis\\r\\n\\t*/\\r\\n\\trenderCylinder: function( radius, height, segments, in_z )\\r\\n\\t{\\r\\n\\t\\tvar mesh = this.createCylinderMesh(radius, height, segments, in_z, true);\\r\\n\\t\\treturn this.renderMesh( mesh, gl.TRIANGLE_STRIP, undefined, undefined, 0, this._global_mesh_last_size );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders an image\\r\\n\\t* @method renderImage\\r\\n\\t* @param {vec3} position\\r\\n\\t* @param {Image|Texture|String} image from an URL, or a texture\\r\\n\\t* @param {number} size [optional=10]\\r\\n\\t* @param {boolean} fixed_size [optional=false] (camera distance do not affect size)\\r\\n\\t*/\\r\\n\\trenderImage: function( position, image, size, fixed_size )\\r\\n\\t{\\r\\n\\t\\tif(!position || !image)\\r\\n\\t\\t\\tthrow(\\\"LS.Draw.renderImage param missing\\\");\\r\\n\\t\\tsize = size || 10;\\r\\n\\t\\tvar texture = null;\\r\\n\\r\\n\\t\\tif(typeof(image) == \\\"string\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\ttexture = this.images[image];\\r\\n\\t\\t\\tif(texture == null)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tDraw.images[image] = 1; //loading\\r\\n\\t\\t\\t\\tvar img = new Image();\\r\\n\\t\\t\\t\\timg.src = image;\\r\\n\\t\\t\\t\\timg.onload = function()\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar texture = GL.Texture.fromImage(this);\\r\\n\\t\\t\\t\\t\\tDraw.images[image] = texture;\\r\\n\\t\\t\\t\\t\\tif(Draw.onRequestFrame)\\r\\n\\t\\t\\t\\t\\t\\tDraw.onRequestFrame();\\r\\n\\t\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t\\t}\\t\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(texture == 1)\\r\\n\\t\\t\\t\\treturn; //loading\\r\\n\\t\\t}\\r\\n\\t\\telse if(image.constructor == Image)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!image.texture)\\r\\n\\t\\t\\t\\timage.texture = GL.Texture.fromImage( this );\\r\\n\\t\\t\\ttexture = image.texture;\\r\\n\\t\\t}\\r\\n\\t\\telse if(image.constructor == Texture)\\r\\n\\t\\t\\ttexture = image;\\r\\n\\r\\n\\t\\tif(!texture)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(fixed_size)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.setPointSize( size );\\r\\n\\t\\t\\ttexture.bind(0);\\r\\n\\t\\t\\tthis.renderPoints( position, null, this.shader_image );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.push();\\r\\n\\t\\t\\t//this.lookAt(position, this.camera_position,[0,1,0]);\\r\\n\\t\\t\\tthis.billboard(position);\\r\\n\\t\\t\\tthis.scale(size,size,size);\\r\\n\\t\\t\\ttexture.bind(0);\\r\\n\\t\\t\\tthis.renderMesh(this.quad_mesh, gl.TRIANGLE_FAN, this.shader_texture );\\r\\n\\t\\t\\tthis.pop();\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a given mesh applyting the stack transformations\\r\\n\\t* @method renderMesh\\r\\n\\t* @param {GL.Mesh} mesh\\r\\n\\t* @param {enum} primitive [optional=gl.TRIANGLES] GL.TRIANGLES, gl.LINES, gl.POINTS, ...\\r\\n\\t* @param {string} indices [optional=\\\"triangles\\\"] the name of the buffer in the mesh with the indices\\r\\n\\t* @param {number} range_start [optional] in case of rendering a range, the start primitive\\r\\n\\t* @param {number} range_length [optional] in case of rendering a range, the number of primitives\\r\\n\\t*/\\r\\n\\trenderMesh: function( mesh, primitive, shader, indices, range_start, range_length )\\r\\n\\t{\\r\\n\\t\\tif(!this.ready)\\r\\n\\t\\t\\tthrow (\\\"Draw.js not initialized, call Draw.init()\\\");\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t\\tthrow (\\\"LS.Draw.renderMesh mesh cannot be null\\\");\\r\\n\\r\\n\\t\\tif(!shader)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(mesh === this._global_mesh && this._global_mesh_ignore_colors )\\r\\n\\t\\t\\t\\tshader = this.shader;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tshader = mesh.vertexBuffers[\\\"colors\\\"] ? this.shader_color : this.shader;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tmat4.multiply(this.mvp_matrix, this.viewprojection_matrix, this.model_matrix );\\r\\n\\r\\n\\t\\tshader.uniforms( this.uniforms );\\r\\n\\t\\t\\t\\t\\r\\n\\t\\tif( range_start === undefined )\\r\\n\\t\\t\\tshader.draw(mesh, primitive === undefined ? gl.TRIANGLES : primitive, indices );\\r\\n\\t\\telse\\r\\n\\t\\t\\tshader.drawRange(mesh, primitive === undefined ? gl.TRIANGLES : primitive, range_start, range_length, indices );\\r\\n\\r\\n\\t\\t//used for repeating render \\r\\n\\t\\tthis._last_mesh = mesh;\\r\\n\\t\\tthis._last_primitive = primitive;\\r\\n\\t\\tthis._last_shader = shader;\\r\\n\\t\\tthis._last_indices = indices;\\r\\n\\t\\tthis._last_range_start = range_start;\\r\\n\\t\\tthis._last_range_length = range_length;\\r\\n\\t\\tthis._rendercalls += 1;\\r\\n\\r\\n\\t\\tthis.last_mesh = mesh;\\r\\n\\t\\treturn mesh;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders several meshes in one draw call, keep in mind the shader and the browser should support instancing\\r\\n\\t* @method renderMeshesInstanced\\r\\n\\t* @param {GL.Mesh} mesh\\r\\n\\t* @param {Array} matrices an array containing all the matrices\\r\\n\\t* @param {enum} primitive [optional=gl.TRIANGLES] GL.TRIANGLES, gl.LINES, gl.POINTS, ...\\r\\n\\t* @param {string} indices [optional=\\\"triangles\\\"] the name of the buffer in the mesh with the indices\\r\\n\\t*/\\r\\n\\trenderMeshesInstanced: (function(){ \\r\\n\\t\\t\\r\\n\\t\\tvar tmp = { u_model: null };\\r\\n\\t\\tvar tmp_matrix = mat4.create();\\r\\n\\r\\n\\t\\treturn function( mesh, matrices, primitive, shader, indices )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!this.ready)\\r\\n\\t\\t\\t\\tthrow (\\\"Draw.js not initialized, call Draw.init()\\\");\\r\\n\\t\\t\\tif(!mesh)\\r\\n\\t\\t\\t\\tthrow (\\\"LS.Draw.renderMeshesInstanced mesh cannot be null\\\");\\r\\n\\r\\n\\t\\t\\tif( gl.webgl_version == 1 && !gl.extensions.ANGLE_instanced_arrays )\\r\\n\\t\\t\\t\\treturn null; //instancing not supported\\r\\n\\r\\n\\t\\t\\tif(!shader)\\r\\n\\t\\t\\t\\tshader = mesh.vertexBuffers[\\\"colors\\\"] ? this.shader_color_instancing : this.shader_instancing;\\r\\n\\r\\n\\t\\t\\tif( !shader.attributes.u_model )\\r\\n\\t\\t\\t\\tthrow(\\\"Shader does not support instancing, it must have a attribute u_model\\\");\\r\\n\\r\\n\\t\\t\\ttmp.u_model = matrices;\\r\\n\\t\\t\\t//this hack is done so we dont have to multiply the global model for every matrix, the VP is in reality a MVP\\r\\n\\t\\t\\ttmp_matrix.set( this.viewprojection_matrix );\\r\\n\\t\\t\\tmat4.multiply( this.viewprojection_matrix, this.viewprojection_matrix, this.model_matrix );\\r\\n\\r\\n\\t\\t\\tshader.uniforms( this.uniforms );\\r\\n\\t\\t\\tshader.drawInstanced( mesh, primitive === undefined ? gl.TRIANGLES : primitive, indices, tmp );\\r\\n\\r\\n\\t\\t\\tthis.viewprojection_matrix.set( tmp_matrix );\\r\\n\\t\\t\\tthis._rendercalls += 1;\\r\\n\\t\\t\\treturn mesh;\\r\\n\\t\\t};\\r\\n\\t})(),\\r\\n\\r\\n\\t//used in some special cases\\r\\n\\trepeatLastRender: function()\\r\\n\\t{\\r\\n\\t\\tthis.renderMesh( this._last_mesh, this._last_primitive, this._last_shader, this._last_indices, this._last_range_start, this._last_range_length );\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders a text in 3D, in the XY plane, using the current matrix position\\r\\n\\t* @method renderText\\r\\n\\t* @param {string} text\\r\\n\\t* @param {vec3} position [optional] 3D coordinate in relation to matrix\\r\\n\\t*/\\r\\n\\trenderText: function( text, position )\\r\\n\\t{\\r\\n\\t\\tposition = position || LS.ZEROS;\\r\\n\\r\\n\\t\\tif(!Draw.font_atlas)\\r\\n\\t\\t\\tthis.createFontAtlas();\\r\\n\\t\\tvar atlas = this.font_atlas;\\r\\n\\t\\tvar l = text.length;\\r\\n\\t\\tvar char_size = atlas.atlas.char_size;\\r\\n\\t\\tvar i_char_size = 1 / atlas.atlas.char_size;\\r\\n\\t\\tvar spacing = atlas.atlas.spacing;\\r\\n\\r\\n\\t\\tvar num_valid_chars = 0;\\r\\n\\t\\tfor(var i = 0; i < l; ++i)\\r\\n\\t\\t\\tif(atlas.atlas[ text.charCodeAt(i) ] != null)\\r\\n\\t\\t\\t\\tnum_valid_chars++;\\r\\n\\r\\n\\t\\tvar vertices = new Float32Array( num_valid_chars * 6 * 3);\\r\\n\\t\\tvar coords = new Float32Array( num_valid_chars * 6 * 2);\\r\\n\\r\\n\\t\\tvar pos = 0;\\r\\n\\t\\tvar x = 0, y = 0;\\r\\n\\t\\tfor(var i = 0; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar c = atlas.atlas[ text.charCodeAt(i) ];\\r\\n\\t\\t\\tif(!c)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(text.charCodeAt(i) == 10)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tx = 0;\\r\\n\\t\\t\\t\\t\\ty -= char_size;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tx += char_size;\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvertices.set( [x + position[0], y + position[1], position[2]], pos*6*3);\\r\\n\\t\\t\\tvertices.set( [x + position[0], y + position[1] + char_size, position[2]], pos*6*3+3);\\r\\n\\t\\t\\tvertices.set( [x + position[0] + char_size, y + position[1] + char_size, position[2]], pos*6*3+6);\\r\\n\\t\\t\\tvertices.set( [x + position[0] + char_size, y + position[1], position[2]], pos*6*3+9);\\r\\n\\t\\t\\tvertices.set( [x + position[0], y + position[1], position[2]], pos*6*3+12);\\r\\n\\t\\t\\tvertices.set( [x + position[0] + char_size, y + position[1] + char_size, position[2]], pos*6*3+15);\\r\\n\\r\\n\\t\\t\\tcoords.set( [c[0], c[1]], pos*6*2);\\r\\n\\t\\t\\tcoords.set( [c[0], c[3]], pos*6*2+2);\\r\\n\\t\\t\\tcoords.set( [c[2], c[3]], pos*6*2+4);\\r\\n\\t\\t\\tcoords.set( [c[2], c[1]], pos*6*2+6);\\r\\n\\t\\t\\tcoords.set( [c[0], c[1]], pos*6*2+8);\\r\\n\\t\\t\\tcoords.set( [c[2], c[3]], pos*6*2+10);\\r\\n\\r\\n\\t\\t\\tx+= spacing;\\r\\n\\t\\t\\t++pos;\\r\\n\\t\\t}\\r\\n\\t\\tvar mesh = this.toGlobalMesh({vertices: vertices, coords: coords});\\r\\n\\t\\tatlas.bind(0);\\r\\n\\t\\treturn this.renderMesh( mesh, gl.TRIANGLES, this.shader_texture, undefined, 0, vertices.length / 3 );\\r\\n\\t},\\r\\n\\r\\n\\t/*\\r\\n\\trenderText2D: function( text, position )\\r\\n\\t{\\r\\n\\t\\tposition = position || LS.ZEROS;\\r\\n\\t\\tif(!Draw.font_atlas)\\r\\n\\t\\t\\tthis.createFontAtlas();\\r\\n\\t\\tvar atlas = this.font_atlas;\\r\\n\\t\\tvar l = text.length;\\r\\n\\t\\tvar char_size = atlas.atlas.char_size;\\r\\n\\t\\tvar i_char_size = 1 / atlas.atlas.char_size;\\r\\n\\t\\tvar spacing = atlas.atlas.spacing;\\r\\n\\r\\n\\t\\tvar num_valid_chars = 0;\\r\\n\\t\\tfor(var i = 0; i < l; ++i)\\r\\n\\t\\t\\tif(atlas.atlas[ text.charCodeAt(i) ] != null)\\r\\n\\t\\t\\t\\tnum_valid_chars++;\\r\\n\\r\\n\\t\\tvar vertices = new Float32Array( num_valid_chars * 3 );\\r\\n\\t\\tvar extra4 = new Float32Array( num_valid_chars * 4 );\\r\\n\\r\\n\\t\\tvar pos = 0;\\r\\n\\t\\tvar x = 0, y = 0;\\r\\n\\t\\tfor(var i = 0; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar c = atlas.atlas[ text.charCodeAt(i) ];\\r\\n\\t\\t\\tif(!c)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(text.charCodeAt(i) == 10) //breakline\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tx = 0;\\r\\n\\t\\t\\t\\t\\ty += char_size;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tx += char_size;\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvertices.set( position, i*3 );\\r\\n\\t\\t\\textra4.set([ c[0], c[1], x,y );\\r\\n\\r\\n\\t\\t\\tx+= spacing;\\r\\n\\t\\t\\t++pos;\\r\\n\\t\\t}\\r\\n\\t\\tvar mesh = this.toGlobalMesh({ vertices: vertices, extra4: extra4 });\\r\\n\\t\\tthis.setPointSize(20);\\r\\n\\t\\tatlas.bind(0);\\r\\n\\t\\tthis.shader_text2D.uniforms({ u_char_offset: atlas.offset });\\r\\n\\t\\treturn this.renderMesh( mesh, gl.POINTS, this.shader_text2D, undefined, 0, vertices.length / 3 );\\r\\n\\t},\\r\\n\\t*/\\r\\n\\r\\n\\tcreateFontAtlas: function()\\r\\n\\t{\\r\\n\\t\\tvar canvas = createCanvas(512,512);\\r\\n\\t\\tvar fontsize = (canvas.width * 0.09)|0;\\r\\n\\t\\tvar char_size = (canvas.width * 0.1)|0;\\r\\n\\r\\n\\t\\t//$(\\\"body\\\").append(canvas);\\r\\n\\t\\tvar ctx = canvas.getContext(\\\"2d\\\");\\r\\n\\t\\t//ctx.fillRect(0,0,canvas.width,canvas.height);\\r\\n\\t\\tctx.fillStyle = \\\"white\\\";\\r\\n\\t\\tctx.font = fontsize + \\\"px Courier New\\\";\\r\\n\\t\\tctx.textAlign = \\\"center\\\";\\r\\n\\t\\tvar x = 0;\\r\\n\\t\\tvar y = 0;\\r\\n\\t\\tvar xoffset = 0.5, yoffset = fontsize * -0.3;\\r\\n\\t\\tvar atlas = { char_size: char_size, offset: char_size / canvas.width , spacing: char_size * 0.6 };\\r\\n\\r\\n\\t\\tfor(var i = 6; i < 100; i++)//valid characters\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar character = String.fromCharCode(i+27);\\r\\n\\t\\t\\tatlas[i+27] = [x/canvas.width, 1-(y+char_size)/canvas.height, (x+char_size)/canvas.width, 1-(y)/canvas.height];\\r\\n\\t\\t\\tctx.fillText( character,Math.floor(x+char_size*xoffset),Math.floor(y+char_size+yoffset),char_size);\\r\\n\\t\\t\\tx += char_size;\\r\\n\\t\\t\\tif((x + char_size) > canvas.width)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tx = 0;\\r\\n\\t\\t\\t\\ty += char_size;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.font_atlas = GL.Texture.fromImage(canvas, {magFilter: gl.LINEAR, minFilter: gl.LINEAR_MIPMAP_LINEAR} );\\r\\n\\t\\tgl.colorMask(true,true,true,false);\\r\\n\\t\\tthis.font_atlas.fill([1,1,1,0]);\\r\\n\\t\\tgl.colorMask(true,true,true,true);\\r\\n\\t\\tthis.font_atlas.atlas = atlas;\\r\\n\\t},\\r\\n\\r\\n\\tlinearize: function(array) //fairly optimized\\r\\n\\t{\\r\\n\\t\\tif(!array.length)\\r\\n\\t\\t\\treturn [];\\r\\n\\t\\tif(array[0].constructor === Number) //array of numbers\\r\\n\\t\\t\\treturn array.constructor === Float32Array ? array : new Float32Array(array);\\r\\n\\t\\t//linearize\\r\\n\\t\\tvar n = array[0].length; //assuming all values have the same size!\\r\\n\\t\\tvar result = new Float32Array(array.length * n);\\r\\n\\t\\tvar l = array.length;\\r\\n\\t\\tfor(var i = 0; i < l; ++i)\\r\\n\\t\\t\\tresult.set(array[i], i*n);\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* pushes the transform matrix into the stack to save the state\\r\\n\\t* @method push\\r\\n\\t*/\\r\\n\\tpush: function()\\r\\n\\t{\\r\\n\\t\\tif(this.model_matrix.byteOffset >= (this.stack.byteLength - 16*4))\\r\\n\\t\\t\\tthrow(\\\"matrices stack overflow\\\");\\r\\n\\r\\n\\t\\tvar old = this.model_matrix;\\r\\n\\t\\tthis.model_matrix = new Float32Array(this.stack.buffer,this.model_matrix.byteOffset + 16*4,16);\\r\\n\\t\\tthis.uniforms.u_model = this.model_matrix;\\r\\n\\t\\tmat4.copy(this.model_matrix, old);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* takes the matrix from the top position of the stack to restore the last saved state\\r\\n\\t* @method push\\r\\n\\t*/\\r\\n\\tpop: function()\\r\\n\\t{\\r\\n\\t\\tif(this.model_matrix.byteOffset == 0)\\r\\n\\t\\t\\tthrow(\\\"too many pops\\\");\\r\\n\\t\\tthis.model_matrix = new Float32Array(this.stack.buffer,this.model_matrix.byteOffset - 16*4,16);\\r\\n\\t\\tthis.uniforms.u_model = this.model_matrix;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* clears the transform matrix setting it to an identity\\r\\n\\t* @method identity\\r\\n\\t*/\\r\\n\\tidentity: function()\\r\\n\\t{\\r\\n\\t\\tmat4.identity(this.model_matrix);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* changes the scale of the transform matrix. The parameters could be a vec3, a single number (then the scale is uniform in all axis) or three numbers\\r\\n\\t* @method scale\\r\\n\\t* @param {vec3|array|number} x could be an array of 3, one value (if no other values are specified then it is an uniform scaling)\\r\\n\\t* @param {number} y\\r\\n\\t* @param {number} z\\r\\n\\t*/\\r\\n\\tscale: function(x,y,z)\\r\\n\\t{\\r\\n\\t\\tif(arguments.length == 3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar temp = this._temp;\\r\\n\\t\\t\\ttemp[0] = x; temp[1] = y; temp[2] = z;\\r\\n\\t\\t\\tmat4.scale(this.model_matrix,this.model_matrix,temp);\\r\\n\\t\\t}\\r\\n\\t\\telse if(x.length)//one argument: x is vec3\\r\\n\\t\\t\\tmat4.scale(this.model_matrix,this.model_matrix,x);\\r\\n\\t\\telse //is number\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar temp = this._temp;\\r\\n\\t\\t\\ttemp[0] = temp[1] = temp[2] = x;\\r\\n\\t\\t\\tmat4.scale(this.model_matrix,this.model_matrix,temp);\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* applies a translation to the transform matrix\\r\\n\\t* @method translate\\r\\n\\t* @param {vec3|number} x could be an array of 3 or the x transform\\r\\n\\t* @param {number} y\\r\\n\\t* @param {number} z\\r\\n\\t*/\\r\\n\\ttranslate: function(x,y,z)\\r\\n\\t{\\r\\n\\t\\tif(arguments.length == 3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar temp = this._temp;\\r\\n\\t\\t\\ttemp[0] = x; temp[1] = y; temp[2] = z;\\r\\n\\t\\t\\tmat4.translate(this.model_matrix,this.model_matrix,temp);\\r\\n\\t\\t}\\r\\n\\t\\telse //one argument: x -> vec3\\r\\n\\t\\t\\tmat4.translate(this.model_matrix,this.model_matrix,x);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* applies a translation to the transform matrix\\r\\n\\t* @method rotate\\r\\n\\t* @param {number} angle in degrees\\r\\n\\t* @param {number|vec3} x could be the x component or the full axis\\r\\n\\t* @param {number} y\\r\\n\\t* @param {number} z\\r\\n\\t*/\\r\\n\\trotate: function(angle, x,y,z)\\r\\n\\t{\\r\\n\\t\\tif(arguments.length == 4)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar temp = this._temp;\\r\\n\\t\\t\\ttemp[0] = x; temp[1] = y; temp[2] = z;\\r\\n\\t\\t\\tmat4.rotate(this.model_matrix, this.model_matrix, angle * DEG2RAD, [x,y,z]);\\r\\n\\t\\t}\\r\\n\\t\\telse //two arguments: x -> vec3\\r\\n\\t\\t\\tmat4.rotate(this.model_matrix, this.model_matrix, angle * DEG2RAD, x);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* moves an object to a given position and forces it to look to another direction\\r\\n\\t* Warning: it doesnt changes the camera in any way, only the transform matrix\\r\\n\\t* @method lookAt\\r\\n\\t* @param {vec3} position\\r\\n\\t* @param {vec3} target\\r\\n\\t* @param {vec3} up\\r\\n\\t*/\\r\\n\\tlookAt: function(position, target, up)\\r\\n\\t{\\r\\n\\t\\tmat4.lookAt( this.model_matrix, position, target, up );\\r\\n\\t\\tmat4.invert( this.model_matrix, this.model_matrix );\\r\\n\\t},\\r\\n\\r\\n\\tbillboard: function(position)\\r\\n\\t{\\r\\n\\t\\tmat4.invert(this.model_matrix, this.view_matrix);\\r\\n\\t\\tmat4.setTranslation(this.model_matrix, position);\\r\\n\\t},\\r\\n\\r\\n\\tfromTranslationFrontTop: function(position, front, top)\\r\\n\\t{\\r\\n\\t\\tmat4.fromTranslationFrontTop(this.model_matrix, position, front, top);\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* projects a point from 3D space to 2D space (multiply by MVP)\\r\\n\\t* @method project\\r\\n\\t* @param {vec3} position\\r\\n\\t* @param {vec3} dest [optional]\\r\\n\\t* @return {vec3} the point in screen space (in normalized coordinates)\\r\\n\\t*/\\r\\n\\tproject: function( position, dest )\\r\\n\\t{\\r\\n\\t\\tdest = dest || vec3.create();\\r\\n\\t\\treturn mat4.multiplyVec3(dest, this.mvp_matrix, position);\\r\\n\\t},\\r\\n\\r\\n\\tgetPhongShader: function( ambient_color, light_color, light_dir, instanced )\\r\\n\\t{\\r\\n\\t\\tvar shader = instanced ? this.shader_phong_instanced : this.shader_phong;\\r\\n\\t\\tvec3.normalize( light_dir, light_dir );\\r\\n\\t\\tshader.uniforms({ u_ambient_color: ambient_color, u_light_color: light_color, u_light_dir: light_dir });\\r\\n\\t\\treturn shader;\\r\\n\\t},\\r\\n\\r\\n\\tgetDepthShader: function()\\r\\n\\t{\\r\\n\\t\\treturn this.shader_depth;\\r\\n\\t},\\r\\n\\r\\n\\t//reuses a global mesh to avoid fragmenting the VRAM \\r\\n\\ttoGlobalMesh: function( buffers, indices )\\r\\n\\t{\\r\\n\\t\\tif(!this._global_mesh)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//global mesh: to reuse memory and save fragmentation\\r\\n\\t\\t\\tthis._global_mesh_max_vertices = 1024;\\r\\n\\t\\t\\tthis._global_mesh = new GL.Mesh({\\r\\n\\t\\t\\t\\tvertices: new Float32Array(this._global_mesh_max_vertices * 3),\\r\\n\\t\\t\\t\\tnormals: new Float32Array(this._global_mesh_max_vertices * 3),\\r\\n\\t\\t\\t\\tcoords: new Float32Array(this._global_mesh_max_vertices * 2),\\r\\n\\t\\t\\t\\tcolors: new Float32Array(this._global_mesh_max_vertices * 4),\\r\\n\\t\\t\\t\\textra: new Float32Array(this._global_mesh_max_vertices * 1)\\r\\n\\t\\t\\t},{\\r\\n\\t\\t\\t\\tindices: new Uint16Array(this._global_mesh_max_vertices * 3)\\r\\n\\t\\t\\t}, { stream_type: gl.DYNAMIC_STREAM });\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//take every stream and store it inside the mesh buffers\\r\\n\\t\\tfor(var i in buffers)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mesh_buffer = this._global_mesh.getBuffer( i );\\r\\n\\t\\t\\tif(!mesh_buffer)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Draw: global mesh lacks one buffer: \\\" + i );\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar buffer_data = buffers[i];\\r\\n\\t\\t\\tif(!buffer_data)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif(!buffer_data.buffer)\\r\\n\\t\\t\\t\\tbuffer_data = new Float32Array( buffer_data ); //force typed arrays\\r\\n\\r\\n\\t\\t\\t//some data would be lost here\\r\\n\\t\\t\\tif(buffer_data.length > mesh_buffer.data.length)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Draw: data is too big, resizing\\\" );\\r\\n\\t\\t\\t\\tthis.resizeGlobalMesh();\\r\\n\\t\\t\\t\\tmesh_buffer = this._global_mesh.getBuffer( i );\\r\\n\\t\\t\\t\\tbuffer_data = buffer_data.subarray(0,mesh_buffer.data.length);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tmesh_buffer.setData( buffer_data ); //set and upload\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._global_mesh_ignore_colors = !(buffers.colors);\\r\\n\\r\\n\\t\\tif(indices)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mesh_buffer = this._global_mesh.getIndexBuffer(\\\"indices\\\");\\t\\t\\t\\r\\n\\t\\t\\tmesh_buffer.setData( indices );\\r\\n\\t\\t\\tthis._global_mesh_last_size = indices.length;\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis._global_mesh_last_size = buffers[\\\"vertices\\\"].length / 3;\\r\\n\\t\\treturn this._global_mesh;\\r\\n\\t},\\r\\n\\r\\n\\tresizeGlobalMesh: function()\\r\\n\\t{\\r\\n\\t\\tif(!this._global_mesh)\\r\\n\\t\\t\\tthrow(\\\"No global mesh to resize\\\");\\r\\n\\r\\n\\t\\t//global mesh: to reuse memory and save fragmentation\\r\\n\\t\\tthis._global_mesh_max_vertices = this._global_mesh_max_vertices * 2;\\r\\n\\t\\tthis._global_mesh.deleteBuffers();\\r\\n\\r\\n\\t\\tthis._global_mesh = new GL.Mesh({\\r\\n\\t\\t\\tvertices: new Float32Array(this._global_mesh_max_vertices * 3),\\r\\n\\t\\t\\tnormals: new Float32Array(this._global_mesh_max_vertices * 3),\\r\\n\\t\\t\\tcoords: new Float32Array(this._global_mesh_max_vertices * 2),\\r\\n\\t\\t\\tcolors: new Float32Array(this._global_mesh_max_vertices * 4),\\r\\n\\t\\t\\textra: new Float32Array(this._global_mesh_max_vertices * 1)\\r\\n\\t\\t},{\\r\\n\\t\\t\\tindices: new Uint16Array(this._global_mesh_max_vertices * 3)\\r\\n\\t\\t}, { stream_type: gl.DYNAMIC_STREAM });\\r\\n\\t}\\r\\n};\\r\\n\\r\\n\\r\\nDraw.vertex_shader_code = '\\\\\\r\\n\\tprecision mediump float;\\\\n\\\\\\r\\n\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\tvarying vec3 v_vertex;\\\\n\\\\\\r\\n\\tattribute vec3 a_normal;\\\\n\\\\\\r\\n\\tvarying vec3 v_normal;\\\\n\\\\\\r\\n\\t#ifdef USE_COLOR\\\\n\\\\\\r\\n\\t\\tattribute vec4 a_color;\\\\n\\\\\\r\\n\\t\\tvarying vec4 v_color;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef USE_TEXTURE\\\\n\\\\\\r\\n\\t\\tattribute vec2 a_coord;\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef USE_SIZE\\\\n\\\\\\r\\n\\t\\tattribute float a_extra;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef USE_INSTANCING\\\\n\\\\\\r\\n\\t\\tattribute mat4 u_model;\\\\n\\\\\\r\\n\\t#else\\\\n\\\\\\r\\n\\t\\tuniform mat4 u_model;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\tuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n\\tuniform float u_point_size;\\\\n\\\\\\r\\n\\tuniform float u_perspective;\\\\n\\\\\\r\\n\\tuniform float u_point_perspective;\\\\n\\\\\\r\\n\\tfloat computePointSize(float radius, float w)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tif(radius < 0.0)\\\\n\\\\\\r\\n\\t\\t\\treturn -radius;\\\\n\\\\\\r\\n\\t\\treturn u_perspective * radius / w;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\t#ifdef USE_TEXTURE\\\\n\\\\\\r\\n\\t\\t\\tv_coord = a_coord;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t#ifdef USE_COLOR\\\\n\\\\\\r\\n\\t\\t\\tv_color = a_color;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\tv_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\\\\n\\\\\\r\\n\\t\\tv_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\\\\n\\\\\\r\\n\\t\\tgl_Position = u_viewprojection * vec4(v_vertex,1.0);\\\\n\\\\\\r\\n\\t\\tgl_PointSize = u_point_size;\\\\n\\\\\\r\\n\\t\\t#ifdef USE_SIZE\\\\n\\\\\\r\\n\\t\\t\\tgl_PointSize = a_extra;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\tif(u_point_perspective != 0.0)\\\\n\\\\\\r\\n\\t\\t\\tgl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\\\\n\\\\\\r\\n\\t}\\\\\\r\\n';\\r\\n\\r\\nDraw.fragment_shader_code = '\\\\\\r\\n\\tprecision mediump float;\\\\n\\\\\\r\\n\\tuniform vec4 u_color;\\\\n\\\\\\r\\n\\t#ifdef USE_COLOR\\\\n\\\\\\r\\n\\t\\tvarying vec4 v_color;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef USE_TEXTURE\\\\n\\\\\\r\\n\\t\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_texture;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tvec4 color = u_color;\\\\n\\\\\\r\\n\\t\\t#ifdef USE_TEXTURE\\\\n\\\\\\r\\n\\t\\t color *= texture2D(u_texture, v_coord);\\\\n\\\\\\r\\n\\t\\t if(color.a < 0.1)\\\\n\\\\\\r\\n\\t\\t\\tdiscard;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t#ifdef USE_POINTS\\\\n\\\\\\r\\n\\t\\t\\tfloat dist = length( gl_PointCoord.xy - vec2(0.5) );\\\\n\\\\\\r\\n\\t\\t\\tif( dist > 0.45 )\\\\n\\\\\\r\\n\\t\\t\\t\\tdiscard;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t#ifdef USE_COLOR\\\\n\\\\\\r\\n\\t\\t\\tcolor *= v_color;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\tgl_FragColor = color;\\\\n\\\\\\r\\n\\t}\\\\\\r\\n';\\r\\n\\r\\n\\r\\nif(typeof(LS) != \\\"undefined\\\")\\r\\n\\tLS.Draw = Draw;\\r\\n///@FILE:../src/render/deformer.js\\r\\n///@INFO: UNCOMMON\\r\\n//WORK IN PROGRESS\\r\\n/*\\r\\n//Is the class in charge of applying deformations to meshes (skinning and morph targets)\\r\\nfunction Deformer()\\r\\n{\\r\\n\\tthis.shader_block = null;\\r\\n\\tthis.macros = {}\\r\\n\\tthis.uniforms = {}\\r\\n}\\r\\n\\r\\nDeformer.prototype.applyToRenderInstance = function( RI )\\r\\n{\\r\\n}\\r\\n\\r\\n\\r\\nDeformer.prototype.applyByCPU = function( vertex_data, normals_data )\\r\\n{\\r\\n\\t//overwrite\\r\\n}\\r\\n\\r\\nLS.Deformer = Deformer;\\r\\n*/\\r\\n///@FILE:../src/render/shadows.js\\r\\n///@INFO: COMMON\\r\\n// Shadows are complex because there are too many combinations: SPOT/DIRECT,OMNI or DEPTH_COMPONENT,RGBA or HARD,SOFT,VARIANCE\\r\\n// This class encapsulates the shadowmap generation, and also how how it is read from the shader (using a ShaderBlock)\\r\\n\\r\\nfunction Shadowmap( light )\\r\\n{\\r\\n\\tthis.light = light;\\r\\n\\r\\n\\tthis.resolution = 512;\\r\\n\\tthis.bias = 0;\\r\\n\\tthis.format = GL.DEPTH_COMPONENT;\\r\\n\\tthis.layers = 0xFF; //visible layers\\r\\n\\tthis.texture = null;\\r\\n\\tthis.fbo = null;\\r\\n\\tthis.shadow_params = vec4.create(); //1.0 / this.texture.width, this.shadow_bias, this.near, closest_far\\r\\n\\tthis.reverse_faces = false; //improves quality in some cases\\r\\n}\\r\\n\\r\\nShadowmap.use_shadowmap_depth_texture = true;\\r\\n\\r\\nShadowmap.prototype.getReadShaderBlock = function()\\r\\n{\\r\\n\\tif( this.texture.format != GL.DEPTH_COMPONENT )\\r\\n\\t\\treturn Shadowmap.shader_block.flag_mask | Shadowmap.depth_in_color_block.flag_mask;\\r\\n\\treturn Shadowmap.shader_block.flag_mask;\\r\\n}\\r\\n\\r\\nShadowmap.prototype.getWriteShaderBlock = function()\\r\\n{\\r\\n\\treturn 0;\\r\\n}\\r\\n\\r\\nShadowmap.prototype.precomputeStaticShadowmap = function()\\r\\n{\\r\\n\\r\\n}\\r\\n\\r\\nShadowmap.prototype.generate = function( instances, render_settings, precompute_static )\\r\\n{\\r\\n\\tvar light = this.light;\\r\\n\\r\\n\\tvar light_intensity = light.computeLightIntensity();\\r\\n\\tif( light_intensity < 0.0001 )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//create the texture\\r\\n\\tvar shadowmap_resolution = this.resolution;\\r\\n\\tif(shadowmap_resolution == 0)\\r\\n\\t\\tshadowmap_resolution = render_settings.default_shadowmap_resolution;\\r\\n\\r\\n\\t//shadowmap size\\r\\n\\tvar shadowmap_width = shadowmap_resolution;\\r\\n\\tvar shadowmap_height = shadowmap_resolution;\\r\\n\\tif( light.type == LS.Light.OMNI)\\r\\n\\t\\tshadowmap_height *= 6; //for every face\\r\\n\\r\\n\\t//var tex_type = this.type == Light.OMNI ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;\\r\\n\\tvar tex_type = gl.TEXTURE_2D;\\r\\n\\tif(this.texture == null || this.texture.width != shadowmap_width || this.texture.height != shadowmap_height || this.texture.texture_type != tex_type )\\r\\n\\t{\\r\\n\\t\\tvar type = gl.UNSIGNED_BYTE;\\r\\n\\t\\tvar format = gl.RGBA;\\r\\n\\r\\n\\t\\t//not all webgl implementations support depth textures\\r\\n\\t\\tif( Shadowmap.use_shadowmap_depth_texture && gl.extensions.WEBGL_depth_texture )\\r\\n\\t\\t{\\r\\n\\t\\t\\tformat = gl.DEPTH_COMPONENT;\\r\\n\\t\\t\\ttype = gl.UNSIGNED_INT;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//create texture to store the shadowmap\\r\\n\\t\\tthis.texture = new GL.Texture( shadowmap_width, shadowmap_height, { type: type, texture_type: tex_type, format: format, magFilter: gl.NEAREST, minFilter: gl.NEAREST });\\r\\n\\r\\n\\t\\t//if( this.precompute_static_shadowmap && (format != gl.DEPTH_COMPONENT || gl.extensions.EXT_frag_depth) )\\r\\n\\t\\t//\\tthis._static_shadowmap = new GL.Texture( shadowmap_width, shadowmap_height, { type: type, texture_type: tex_type, format: format, magFilter: gl.NEAREST, minFilter: gl.NEAREST });\\r\\n\\r\\n\\t\\t//index, for debug\\r\\n\\t\\tthis.texture.filename = \\\":shadowmap_\\\" + light.uid;\\r\\n\\t\\tLS.ResourcesManager.textures[ this.texture.filename ] = this.texture; \\r\\n\\r\\n\\t\\tif( this.texture.texture_type == gl.TEXTURE_2D )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(format == gl.RGBA)\\r\\n\\t\\t\\t\\tthis.fbo = new GL.FBO( [this.texture] );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis.fbo = new GL.FBO( null, this.texture );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tLS.Renderer.setRenderPass( SHADOW_PASS );\\r\\n\\tLS.Renderer._current_light = light;\\r\\n\\tvar tmp_layer = render_settings.layers;\\r\\n\\trender_settings.layers = this.layers;\\r\\n\\r\\n\\t//render the scene inside the texture\\r\\n\\t// Render the object viewed from the light using a shader that returns the fragment depth.\\r\\n\\tthis.texture.unbind(); \\r\\n\\r\\n\\tLS.Renderer._current_target = this.texture;\\r\\n\\tthis.fbo.bind();\\r\\n\\r\\n\\tvar sides = 1;\\r\\n\\tvar viewport_width = this.texture.width;\\r\\n\\tvar viewport_height = this.texture.height;\\r\\n\\tif( light.type == LS.Light.OMNI )\\r\\n\\t{\\r\\n\\t\\tsides = 6;\\r\\n\\t\\tviewport_height /= 6;\\r\\n\\t}\\r\\n\\r\\n\\tgl.clearColor(1, 1, 1, 1);\\r\\n\\tif( this.texture.type == gl.DEPTH_COMPONENT )\\r\\n\\t\\tgl.colorMask(false,false,false,false);\\r\\n\\tgl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\\r\\n\\r\\n\\tfor(var i = 0; i < sides; ++i) //in case of omni\\r\\n\\t{\\r\\n\\t\\tvar shadow_camera = light.getLightCamera(i);\\r\\n\\t\\tthis.shadow_params[2] = shadow_camera.near;\\r\\n\\t\\tthis.shadow_params[3] = shadow_camera.far;\\r\\n\\t\\tLS.Renderer.enableCamera( shadow_camera, render_settings, true );\\r\\n\\r\\n\\t\\tvar viewport_y = 0;\\r\\n\\t\\tif( light.type == LS.Light.OMNI )\\r\\n\\t\\t\\tviewport_y = i * viewport_height;\\r\\n\\t\\tgl.viewport(0,viewport_y,viewport_width,viewport_height);\\r\\n\\r\\n\\t\\tif(this.reverse_faces)\\r\\n\\t\\t\\tLS.Renderer._reverse_faces = true;\\r\\n\\r\\n\\t\\t//RENDER INSTANCES in the shadowmap\\r\\n\\t\\tLS.Renderer.renderInstances( render_settings, instances );\\r\\n\\r\\n\\t\\tLS.Renderer._reverse_faces = false;\\r\\n\\t}\\r\\n\\r\\n\\tthis.fbo.unbind();\\r\\n\\tLS.Renderer._current_target = null;\\r\\n\\tgl.colorMask(true,true,true,true);\\r\\n\\r\\n\\trender_settings.layers = tmp_layer;\\r\\n\\tLS.Renderer.setRenderPass( COLOR_PASS );\\r\\n\\tLS.Renderer._current_light = null;\\r\\n\\t\\r\\n\\tif(this.onPostProcessShadowMap)\\r\\n\\t\\tthis.onPostProcessShadowMap( this.texture );\\r\\n}\\r\\n\\r\\nShadowmap.prototype.prepare = function( uniforms, samplers )\\r\\n{\\r\\n\\tif(!this.texture)\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"shadowmap without texture?\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar light = this.light;\\r\\n\\tvar closest_far = light.computeFar();\\r\\n\\tuniforms.u_shadow_params = this.shadow_params;\\r\\n\\tthis.shadow_params[0] = 1.0 / this.texture.width;\\r\\n\\tthis.shadow_params[1] = this.bias;\\r\\n\\t//2 and 3 are set when rendering the shadowmap\\r\\n\\r\\n\\tuniforms.shadowmap = LS.Renderer.SHADOWMAP_TEXTURE_SLOT;\\r\\n\\tsamplers[ LS.Renderer.SHADOWMAP_TEXTURE_SLOT ] = this.texture;\\r\\n}\\r\\n\\r\\nShadowmap.prototype.toViewport = function()\\r\\n{\\r\\n\\tif(!this.texture)\\r\\n\\t\\treturn;\\r\\n\\tthis.texture.toViewport(); //TODO: create shader to visualize correctly\\r\\n}\\r\\n\\r\\n//*******************************\\r\\n\\r\\nShadowmap._enabled_vertex_code =\\\"\\\\n\\\\\\r\\n\\t#pragma snippet \\\\\\\"light_structs\\\\\\\"\\\\n\\\\\\r\\n\\tvarying vec4 v_light_coord;\\\\n\\\\\\r\\n\\tvoid applyLight( vec3 pos ) { v_light_coord = u_light_matrix * vec4(pos,1.0); }\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nShadowmap._disabled_vertex_code =\\\"\\\\n\\\\\\r\\n\\tvoid applyLight(vec3 pos) {}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nShadowmap._enabled_fragment_code = \\\"\\\\n\\\\\\r\\n\\t#ifndef TESTSHADOW\\\\n\\\\\\r\\n\\t\\t#define TESTSHADOW\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#pragma shaderblock \\\\\\\"depth_in_color\\\\\\\"\\\\n\\\\\\r\\n\\t#pragma snippet \\\\\\\"PackDepth32\\\\\\\"\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tuniform sampler2D shadowmap;\\\\n\\\\\\r\\n\\tvarying vec4 v_light_coord;\\\\n\\\\\\r\\n\\tuniform vec4 u_shadow_params; // (1.0/(texture_size), bias, near, far)\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat UnpackDepth(vec4 depth)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\t#ifdef BLOCK_DEPTH_IN_COLOR\\\\n\\\\\\r\\n\\t\\t\\tconst vec4 bitShift = vec4( 1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0 );\\\\n\\\\\\r\\n\\t\\t\\treturn dot(depth, bitShift);\\\\n\\\\\\r\\n\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\treturn depth.x;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tfloat texsize = 1.0 / u_shadow_params.x;\\\\n\\\\\\r\\n\\tfloat real_depth = 0.0;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat pixelShadow( vec2 uv )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tfloat sampleDepth = UnpackDepth( texture2D(shadowmap, uv) );\\\\n\\\\\\r\\n\\t\\tfloat depth = (sampleDepth == 1.0) ? 1.0e9 : sampleDepth; //on empty data send it to far away\\\\n\\\\\\r\\n\\t\\tif (depth > 0.0) \\\\n\\\\\\r\\n\\t\\t\\treturn real_depth > depth ? 0.0 : 1.0;\\\\n\\\\\\r\\n\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tfloat expFunc(float f)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\treturn f*f*f*(f*(f*6.0-15.0)+10.0);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat testShadow( Light LIGHT )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tvec3 offset = vec3(0.0);\\\\n\\\\\\r\\n\\t\\tfloat depth = 0.0;\\\\n\\\\\\r\\n\\t\\tfloat bias = u_shadow_params.y;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvec2 sample = (v_light_coord.xy / v_light_coord.w) * vec2(0.5) + vec2(0.5) + offset.xy;\\\\n\\\\\\r\\n\\t\\t//is inside light frustum\\\\n\\\\\\r\\n\\t\\tif (clamp(sample, 0.0, 1.0) != sample) \\\\n\\\\\\r\\n\\t\\t\\treturn LIGHT.Info.x == 3.0 ? 1.0 : 0.0; //outside of shadowmap, no shadow\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\treal_depth = (v_light_coord.z - bias) / v_light_coord.w * 0.5 + 0.5;\\\\n\\\\\\r\\n\\t\\t#ifdef BLOCK_DEPTH_IN_COLOR\\\\n\\\\\\r\\n\\t\\t\\t//real_depth = linearDepthNormalized( real_depth, u_shadow_params.z, u_shadow_params.w );\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\tvec2 topleft_uv = sample * texsize;\\\\n\\\\\\r\\n\\t\\tvec2 offset_uv = fract( topleft_uv );\\\\n\\\\\\r\\n\\t\\toffset_uv.x = expFunc(offset_uv.x);\\\\n\\\\\\r\\n\\t\\toffset_uv.y = expFunc(offset_uv.y);\\\\n\\\\\\r\\n\\t\\ttopleft_uv = floor(topleft_uv) * u_shadow_params.x;\\\\n\\\\\\r\\n\\t\\tfloat topleft = pixelShadow( topleft_uv );\\\\n\\\\\\r\\n\\t\\tfloat topright = pixelShadow( topleft_uv + vec2(u_shadow_params.x,0.0) );\\\\n\\\\\\r\\n\\t\\tfloat bottomleft = pixelShadow( topleft_uv + vec2(0.0, u_shadow_params.x) );\\\\n\\\\\\r\\n\\t\\tfloat bottomright = pixelShadow( topleft_uv + vec2(u_shadow_params.x, u_shadow_params.x) );\\\\n\\\\\\r\\n\\t\\tfloat top = mix( topleft, topright, offset_uv.x );\\\\n\\\\\\r\\n\\t\\tfloat bottom = mix( bottomleft, bottomright, offset_uv.x );\\\\n\\\\\\r\\n\\t\\treturn mix( top, bottom, offset_uv.y );\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nShadowmap._disabled_fragment_code = \\\"\\\\nfloat testShadow( Light LIGHT ) { return 1.0; }\\\\n\\\";\\r\\n\\r\\nvar shadowmapping_depth_in_color_block = new LS.ShaderBlock(\\\"depth_in_color\\\");\\r\\nshadowmapping_depth_in_color_block.register();\\r\\nShadowmap.depth_in_color_block = shadowmapping_depth_in_color_block;\\r\\n\\r\\nvar shadowmapping_block = new LS.ShaderBlock(\\\"testShadow\\\");\\r\\nshadowmapping_block.addCode( GL.VERTEX_SHADER, Shadowmap._enabled_vertex_code, Shadowmap._disabled_vertex_code);\\r\\nshadowmapping_block.addCode( GL.FRAGMENT_SHADER, Shadowmap._enabled_fragment_code, Shadowmap._disabled_fragment_code );\\r\\n//shadowmapping_block.defineContextMacros({\\\"SHADOWBLOCK\\\":\\\"testShadow\\\"});\\r\\nshadowmapping_block.register();\\r\\nShadowmap.shader_block = shadowmapping_block;\\r\\n\\r\\n\\r\\n///@FILE:../src/picking.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Picking is used to detect which element is below one pixel (using the GPU) or using raycast\\r\\n*\\r\\n* @class Picking\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\nvar Picking = {\\r\\n\\r\\n\\tpicking_color_offset: 10, //color difference between picking objects\\r\\n\\t_picking_points: [], //used during picking fetching\\r\\n\\t_picking_nodes: null, //created before picking\\r\\n\\r\\n\\t//picking\\r\\n\\t_pickingMap: null,\\r\\n\\t_picking_color: new Uint8Array(4),\\r\\n\\t_picking_depth: 0,\\r\\n\\t_picking_next_color_id: 0,\\r\\n\\t_picking_render_settings: new RenderSettings(),\\r\\n\\r\\n\\t_use_scissor_test: true,\\r\\n\\r\\n\\t/**\\r\\n\\t* Renders the pixel and retrieves the color to detect which object it was, slow but accurate\\r\\n\\t* @method getNodeAtCanvasPosition\\r\\n\\t* @param {number} x in canvas coordinates\\r\\n\\t* @param {number} y in canvas coordinates\\r\\n\\t* @param {Camera} camera default is all cameras\\r\\n\\t* @param {number} layers default is 0xFFFF which is all\\r\\n\\t* @param {Scene} scene default is GlobalScene\\r\\n\\t*/\\r\\n\\tgetNodeAtCanvasPosition: function( x, y, camera, layers, scene )\\r\\n\\t{\\r\\n\\t\\tvar instance = this.getInstanceAtCanvasPosition( x, y, camera, layers, scene );\\r\\n\\t\\tif(!instance)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tif(instance.constructor == LS.SceneNode)\\r\\n\\t\\t\\treturn instance;\\r\\n\\r\\n\\t\\tif(instance._root && instance._root.constructor == LS.SceneNode)\\r\\n\\t\\t\\treturn instance._root;\\r\\n\\r\\n\\t\\tif(instance.node)\\r\\n\\t\\t\\treturn instance.node;\\r\\n\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns the instance under a screen position\\r\\n\\t* @method getInstanceAtCanvasPosition\\r\\n\\t* @param {number} x in canvas coordinates (0,0 is bottom-left)\\r\\n\\t* @param {number} y in canvas coordinates\\r\\n\\t* @param {Camera} camera\\r\\n\\t* @param {number} layers default is 0xFFFF which is all\\r\\n\\t* @param {Scene} scene\\r\\n\\t* @return {Object} the info supplied by the picker (usually a SceneNode)\\r\\n\\t*/\\r\\n\\tgetInstanceAtCanvasPosition: function( x, y, camera, layers, scene )\\r\\n\\t{\\r\\n\\t\\tscene = scene || LS.GlobalScene;\\r\\n\\r\\n\\t\\tif(!camera)\\r\\n\\t\\t\\tcamera = LS.Renderer.getCameraAtPosition( x, y, scene._cameras );\\r\\n\\r\\n\\t\\tif(!camera)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tthis._picking_nodes = {};\\r\\n\\r\\n\\t\\t//render all Render Instances\\r\\n\\t\\tthis.getPickingColorFromBuffer( scene, camera, x, y, layers );\\r\\n\\r\\n\\t\\tthis._picking_color[3] = 0; //remove alpha, because alpha is always 255\\r\\n\\t\\tvar id = new Uint32Array(this._picking_color.buffer)[0]; //get only element\\r\\n\\r\\n\\t\\tvar instance_info = this._picking_nodes[id];\\r\\n\\t\\tthis._picking_nodes = {};\\r\\n\\t\\treturn instance_info;\\r\\n\\t},\\t\\r\\n\\r\\n\\t/**\\r\\n\\t* Returns a color you should use to paint this node during picking rendering\\r\\n\\t* you tell what info you want to retrieve associated with this object if it is clicked\\r\\n\\t* @method getNextPickingColor\\r\\n\\t* @param {*} info\\r\\n\\t* @return {vec3} array containing all the RenderInstances that collided with the ray\\r\\n\\t*/\\r\\n\\tgetNextPickingColor: function( info )\\r\\n\\t{\\r\\n\\t\\tthis._picking_next_color_id += this.picking_color_offset;\\r\\n\\t\\tvar pick_color = new Uint32Array(1); //store four bytes number\\r\\n\\t\\tpick_color[0] = this._picking_next_color_id; //with the picking color for this object\\r\\n\\t\\tvar byte_pick_color = new Uint8Array( pick_color.buffer ); //read is as bytes\\r\\n\\t\\t//byte_pick_color[3] = 255; //Set the alpha to 1\\r\\n\\r\\n\\t\\tif(!this._picking_nodes) //not necessary but used for debug\\r\\n\\t\\t\\tthis._picking_nodes = {};\\r\\n\\t\\tthis._picking_nodes[ this._picking_next_color_id ] = info;\\r\\n\\t\\treturn vec4.fromValues( byte_pick_color[0] / 255, byte_pick_color[1] / 255, byte_pick_color[2] / 255, 1 );\\r\\n\\t},\\r\\n\\r\\n\\t//x,y must be in canvas coordinates (0,0 is bottom-left)\\r\\n\\tgetPickingColorFromBuffer: function( scene, camera, x, y, layers )\\r\\n\\t{\\r\\n\\t\\t//create texture\\r\\n\\t\\tif(this._pickingMap == null || this._pickingMap.width != gl.canvas.width || this._pickingMap.height != gl.canvas.height )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._pickingMap = new GL.Texture( gl.canvas.width, gl.canvas.height, { format: gl.RGBA, filter: gl.NEAREST });\\r\\n\\t\\t\\tthis._pickingFBO = new GL.FBO([this._pickingMap]);\\r\\n\\t\\t\\t//LS.ResourcesManager.textures[\\\":picking\\\"] = this._pickingMap; //debug the texture\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar small_area = this._use_scissor_test;\\r\\n\\t\\tLS.Renderer._current_target = this._pickingMap;\\r\\n\\r\\n\\t\\tthis._pickingFBO.bind();\\r\\n\\r\\n\\t\\t\\t//var viewport = camera.getLocalViewport();\\r\\n\\t\\t\\t//camera._real_aspect = viewport[2] / viewport[3];\\r\\n\\t\\t\\t//gl.viewport( viewport[0], viewport[1], viewport[2], viewport[3] );\\r\\n\\r\\n\\t\\t\\tif(small_area)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tgl.scissor(x-1,y-1,2,2);\\r\\n\\t\\t\\t\\tgl.enable(gl.SCISSOR_TEST);\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tthis.renderPickingBuffer( scene, camera, layers, [x,y] );\\r\\n\\r\\n\\t\\t\\tgl.readPixels(x,y,1,1,gl.RGBA,gl.UNSIGNED_BYTE, this._picking_color );\\r\\n\\r\\n\\t\\t\\tif(small_area)\\r\\n\\t\\t\\t\\tgl.disable(gl.SCISSOR_TEST);\\r\\n\\r\\n\\t\\tthis._pickingFBO.unbind();\\r\\n\\r\\n\\t\\tLS.Renderer._current_target = null; //??? deprecated\\r\\n\\r\\n\\t\\t//if(!this._picking_color) this._picking_color = new Uint8Array(4); //debug\\r\\n\\t\\t//trace(\\\" END Rendering: \\\", this._picking_color );\\r\\n\\t\\treturn this._picking_color;\\r\\n\\t},\\r\\n\\r\\n\\t//pos must be in canvas coordinates (0,0 is bottom-left)\\r\\n\\trenderPickingBuffer: function( scene, camera, layers, pos )\\r\\n\\t{\\r\\n\\t\\tif(layers === undefined)\\r\\n\\t\\t\\tlayers = 0xFFFF;\\r\\n\\t\\tvar picking_render_settings = this._picking_render_settings;\\r\\n\\r\\n\\t\\tLS.Renderer.enableCamera( camera, this._picking_render_settings );\\r\\n\\r\\n\\t\\tgl.clearColor(0,0,0,0);\\r\\n\\t\\tgl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\\r\\n\\r\\n\\t\\tthis._picking_next_color_id = 0;\\r\\n\\t\\tLS.Renderer.setRenderPass( PICKING_PASS );\\r\\n\\t\\tpicking_render_settings.layers = layers;\\r\\n\\r\\n\\t\\t//check instances colliding with cursor using a ray against AABBs\\r\\n\\t\\tvar instances = null;\\r\\n\\t\\tif( pos ) //not tested yet\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar ray = camera.getRay( pos[0], pos[1] );\\r\\n\\t\\t\\tvar instances_collisions = LS.Physics.raycastRenderInstances( ray.origin, ray.direction );\\r\\n\\t\\t\\tif( instances_collisions )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tinstances = Array( instances_collisions.length );\\r\\n\\t\\t\\t\\tfor(var i = 0; i < instances_collisions.length; ++i)\\r\\n\\t\\t\\t\\t\\tinstances[i] = instances_collisions[i].instance;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t//console.log(\\\"Instances ray collided:\\\", instances_collisions.length);\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tinstances = scene._instances;\\r\\n\\r\\n\\t\\tLS.Renderer.renderInstances( picking_render_settings, instances );\\r\\n\\r\\n\\t\\t//Nodes\\r\\n\\t\\t/* done in EditorView\\r\\n\\t\\tvar ray = null;\\r\\n\\t\\tif(mouse_pos)\\r\\n\\t\\t{\\r\\n\\t\\t\\tray = camera.getRayInPixel( pos[0], pos[1] );\\r\\n\\t\\t\\tray.end = vec3.add( vec3.create(), ray.origin, vec3.scale(vec3.create(), ray.direction, 10000 ) );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var i = 0, l = scene._nodes.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar node = scene._nodes[i];\\r\\n\\t\\t\\tif(!node.visible)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//nodes with special pickings?\\r\\n\\t\\t\\tif(node.renderPicking)\\r\\n\\t\\t\\t\\tnode.renderPicking(ray);\\r\\n\\r\\n\\t\\t\\tif( node.transform )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar pos = vec3.create();\\r\\n\\t\\t\\t\\tmat4.multiplyVec3(pos, node.transform.getGlobalMatrixRef(), pos); //create a new one to store them\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tfor(var j in node._components)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar component = node._components[j];\\r\\n\\t\\t\\t\\tif(component.renderPicking)\\r\\n\\t\\t\\t\\t\\tcomponent.renderPicking(ray);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\t*/\\r\\n\\r\\n\\t\\tLEvent.trigger( scene, \\\"renderPicking\\\", pos );\\r\\n\\t\\tLEvent.trigger( LS.Renderer, \\\"renderPicking\\\", pos );\\r\\n\\r\\n\\t\\tLS.Renderer.setRenderPass( COLOR_PASS );\\r\\n\\t},\\r\\n\\r\\n\\taddPickingPoint: function( position, size, info )\\r\\n\\t{\\r\\n\\t\\tsize = size || 5.0;\\r\\n\\t\\tvar color = LS.Picking.getNextPickingColor( info );\\r\\n\\t\\tthis._picking_points.push([ position,color,size]);\\r\\n\\t},\\r\\n\\r\\n\\trenderPickingPoints: function()\\r\\n\\t{\\r\\n\\t\\t//render all the picking points \\r\\n\\t\\tif(this._picking_points.length)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar points = new Float32Array( this._picking_points.length * 3 );\\r\\n\\t\\t\\tvar colors = new Float32Array( this._picking_points.length * 4 );\\r\\n\\t\\t\\tvar sizes = new Float32Array( this._picking_points.length );\\r\\n\\t\\t\\tfor(var i = 0; i < this._picking_points.length; i++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tpoints.set( this._picking_points[i][0], i*3 );\\r\\n\\t\\t\\t\\tcolors.set( this._picking_points[i][1], i*4 );\\r\\n\\t\\t\\t\\tsizes[i] = this._picking_points[i][2];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tLS.Draw.setPointSize(1);\\r\\n\\t\\t\\tLS.Draw.setColor([1,1,1,1]);\\r\\n\\t\\t\\tgl.disable( gl.DEPTH_TEST ); //because nodes are show over meshes\\r\\n\\t\\t\\tLS.Draw.renderPointsWithSize( points, colors, sizes );\\r\\n\\t\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\t\\tthis._picking_points.length = 0;\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\tvisualize: function(v)\\r\\n\\t{\\r\\n\\t\\t//to visualize picking buffer\\r\\n\\t\\tLS.Renderer.setRenderPass( v ? LS.PICKING_PASS : LS.COLOR_PASS );\\r\\n\\t}\\r\\n};\\r\\n\\r\\n//Extra info\\r\\n// renderPicking is not called from LiteScene, only from WebGLStudio EditorView\\r\\n\\r\\nLS.Picking = Picking;\\r\\n\\r\\n\\r\\n\\r\\n///@FILE:../src/physics.js\\r\\n///@INFO: UNCOMMON\\r\\n/* This is in charge of basic physics actions like ray tracing against the colliders */\\r\\n\\r\\n/**\\r\\n* Contains information about the collision of a ray and the scene\\r\\n* - position: vec3\\r\\n* - node: SceneNode\\r\\n* - instance: could be a RenderInstance or a PhysicsInstance\\r\\n* - distance: number\\r\\n* @class Collision\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n* @param {SceneNode} node\\r\\n* @param {PhysicsInstance|RenderInstance} instance\\r\\n* @param {vec3} position collision position\\r\\n* @param {number} distance\\r\\n*/\\r\\nfunction Collision( node, instance, position, distance, normal, hit )\\r\\n{\\r\\n\\tthis.position = vec3.create();\\r\\n\\tif(position)\\r\\n\\t\\tthis.position.set(position);\\r\\n\\tthis.node = node || null; //the node belonging to this colliding object\\r\\n\\tthis.instance = instance || null; //could be a RenderInstance or a PhysicsInstance\\r\\n\\tthis.distance = distance || 0; //distance from the ray start\\r\\n\\tthis.normal = normal;\\r\\n\\tthis.hit = hit; //contains info about the collision in local space\\r\\n}\\r\\n\\r\\nCollision.isCloser = function(a,b) { return a.distance - b.distance; }\\r\\n\\r\\nLS.Collision = Collision;\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* PhysicsInstance contains info of a colliding object. Used to test collisions with the scene\\r\\n*\\r\\n* @class PhysicsInstance\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\nfunction PhysicsInstance( node, component )\\r\\n{\\r\\n\\tthis.uid = LS.generateUId(\\\"PHSX\\\"); //unique identifier for this RI\\r\\n\\tthis.layers = 3|0;\\r\\n\\r\\n\\tthis.type = PhysicsInstance.BOX; //SPHERE, MESH\\r\\n\\tthis.mesh = null; \\r\\n\\r\\n\\t//where does it come from\\r\\n\\tthis.node = node;\\r\\n\\tthis.component = component;\\r\\n\\r\\n\\t//transformation\\r\\n\\tthis.matrix = mat4.create();\\r\\n\\tthis.center = vec3.create(); //in world space\\r\\n\\r\\n\\t//for visibility computation\\r\\n\\tthis.oobb = BBox.create(); //local object oriented bounding box\\r\\n\\tthis.aabb = BBox.create(); //world axis aligned bounding box\\r\\n}\\r\\n\\r\\nPhysicsInstance.BOX = 1;\\r\\nPhysicsInstance.SPHERE = 2;\\r\\nPhysicsInstance.PLANE = 3;\\r\\nPhysicsInstance.CAPSULE = 4;\\r\\nPhysicsInstance.MESH = 5;\\r\\nPhysicsInstance.FUNCTION = 6; //used to test against a internal function\\r\\n\\r\\n/**\\r\\n* Computes the instance bounding box in world space from the one in local space\\r\\n*\\r\\n* @method updateAABB\\r\\n*/\\r\\nPhysicsInstance.prototype.updateAABB = function()\\r\\n{\\r\\n\\tBBox.transformMat4( this.aabb, this.oobb, this.matrix );\\r\\n}\\r\\n\\r\\nPhysicsInstance.prototype.setMesh = function(mesh)\\r\\n{\\r\\n\\tthis.mesh = mesh;\\r\\n\\tthis.type = PhysicsInstance.MESH;\\t\\r\\n\\tBBox.setCenterHalfsize( this.oobb, BBox.getCenter( mesh.bounding ), BBox.getHalfsize( mesh.bounding ) );\\r\\n}\\r\\n\\r\\nLS.PhysicsInstance = PhysicsInstance;\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Physics is in charge of all physics testing methods\\r\\n* Right now is mostly used for testing collisions with rays agains the colliders in the scene\\r\\n*\\r\\n* @class Physics\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n*/\\r\\nvar Physics = {\\r\\n\\r\\n\\t/**\\r\\n\\t* Cast a ray that traverses the scene checking for collisions with Colliders\\r\\n\\t* @method raycast\\r\\n\\t* @param {vec3} origin in world space\\r\\n\\t* @param {vec3} direction in world space\\r\\n\\t* @param {Object} options ( max_dist maxium distance, layers which layers to check, scene, first_collision )\\r\\n\\t* @return {Array} Array of Collision objects containing all the nodes that collided with the ray or null in the form [SceneNode, Collider, collision point, distance]\\r\\n\\t*/\\r\\n\\traycast: function( origin, direction, options )\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\r\\n\\t\\tif(!origin || !direction)\\r\\n\\t\\t\\tthrow(\\\"Physics.raycast: origin or direction missing.\\\");\\r\\n\\r\\n\\t\\tvar layers = options.layers;\\r\\n\\t\\tif(layers === undefined)\\r\\n\\t\\t\\tlayers = 0xFFFF;\\r\\n\\t\\tvar max_distance = options.max_distance || Number.MAX_VALUE;\\r\\n\\t\\tvar scene = options.scene || LS.GlobalScene;\\r\\n\\t\\tvar first_collision = options.first_collision;\\r\\n\\r\\n\\t\\tvar colliders = options.colliders || scene._colliders;\\r\\n\\t\\tvar collisions = [];\\r\\n\\r\\n\\t\\tvar compute_normal = !!options.normal;\\r\\n\\r\\n\\t\\tif(!colliders)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar local_origin = vec3.create();\\r\\n\\t\\tvar local_direction = vec3.create();\\r\\n\\r\\n\\t\\t//for every instance\\r\\n\\t\\tfor(var i = 0; i < colliders.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar instance = colliders[i];\\r\\n\\r\\n\\t\\t\\tif( (layers & instance.layers) === 0 )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//test against AABB\\r\\n\\t\\t\\tvar collision_point = vec3.create();\\r\\n\\t\\t\\tvar collision_normal = null;\\r\\n\\t\\t\\tif( !geo.testRayBBox(origin, direction, instance.aabb, null, collision_point, max_distance) )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar model = instance.matrix;\\r\\n\\t\\t\\tvar hit = null;\\r\\n\\r\\n\\t\\t\\t//spheres are tested in world space, is cheaper (if no non-uniform scales...)\\r\\n\\t\\t\\tif( instance.type == PhysicsInstance.SPHERE )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(!geo.testRaySphere( origin, direction, instance.center, instance.oobb[3], collision_point, max_distance))\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tif(compute_normal)\\r\\n\\t\\t\\t\\t\\tcollision_normal = vec3.sub( vec3.create(), collision_point, instance.center );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse //the rest test first with the local BBox\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//ray to local instance coordinates\\r\\n\\t\\t\\t\\tvar inv = mat4.invert( mat4.create(), model );\\r\\n\\t\\t\\t\\tmat4.multiplyVec3( local_origin, inv, origin);\\r\\n\\t\\t\\t\\tmat4.rotateVec3( local_direction, inv, direction);\\r\\n\\r\\n\\t\\t\\t\\t//test against OOBB (a little bit more expensive)\\r\\n\\t\\t\\t\\tif( !geo.testRayBBox( local_origin, local_direction, instance.oobb, null, collision_point, max_distance) )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\t//if mesh use Octree\\r\\n\\t\\t\\t\\tif( instance.type == PhysicsInstance.MESH )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar octree = instance.mesh.octree;\\r\\n\\t\\t\\t\\t\\tif(!octree)\\r\\n\\t\\t\\t\\t\\t\\toctree = instance.mesh.octree = new GL.Octree( instance.mesh );\\r\\n\\t\\t\\t\\t\\thit = octree.testRay( local_origin, local_direction, 0.0, max_distance );\\r\\n\\t\\t\\t\\t\\tif(!hit)\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\t\\tmat4.multiplyVec3( collision_point, model, hit.pos );\\r\\n\\t\\t\\t\\t\\tif(compute_normal)\\r\\n\\t\\t\\t\\t\\t\\tcollision_normal = mat4.rotateVec3( vec3.create(), model, hit.normal );\\r\\n\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse //if just a BBox collision\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvec3.transformMat4( collision_point, collision_point, model );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar distance = vec3.distance( origin, collision_point );\\r\\n\\t\\t\\tcollisions.push( new LS.Collision( instance.node, instance, collision_point, distance, collision_normal, hit ));\\r\\n\\r\\n\\t\\t\\tif(first_collision)\\r\\n\\t\\t\\t\\treturn collisions;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//sort collisions by distance\\r\\n\\t\\tcollisions.sort( Collision.isCloser );\\r\\n\\t\\treturn collisions;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Test if a sphere collides with any of the colliders in the scene\\r\\n\\t* @method testSphere\\r\\n\\t* @param {vec3} origin in world space\\r\\n\\t* @param {radius} radius\\r\\n\\t* @param {Object} options layers, colliders, scene\\r\\n\\t* @return {PhysicsInstance} the first PhysicsObject that collided with, otherwise null\\r\\n\\t*/\\r\\n\\ttestSphere: function( origin, radius, options )\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\t\\tvar layers = options.layers;\\r\\n\\t\\tif(layers === undefined)\\r\\n\\t\\t\\tlayers = 0xFFFF;\\r\\n\\t\\tvar scene = options.scene || LS.GlobalScene;\\r\\n\\r\\n\\t\\tvar colliders = options.colliders || scene._colliders;\\r\\n\\t\\tvar collisions = [];\\r\\n\\r\\n\\t\\tvar local_origin = vec3.create();\\r\\n\\r\\n\\t\\tif(!colliders)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\t//for every instance\\r\\n\\t\\tfor(var i = 0; i < colliders.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar instance = colliders[i];\\r\\n\\r\\n\\t\\t\\tif( (layers & instance.layers) === 0 )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//test against AABB\\r\\n\\t\\t\\tif( !geo.testSphereBBox( origin, radius, instance.aabb ) )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar model = instance.matrix;\\r\\n\\r\\n\\t\\t\\t//ray to local\\r\\n\\t\\t\\tvar inv = mat4.invert( mat4.create(), model );\\r\\n\\t\\t\\tmat4.multiplyVec3( local_origin, inv, origin);\\r\\n\\r\\n\\t\\t\\t//test in world space, is cheaper\\r\\n\\t\\t\\tif( instance.type == LS.PhysicsInstance.SPHERE)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( vec3.distance( origin, local_origin ) > (radius + BBox.getRadius(instance.oobb)) )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse //the rest test first with the local BBox\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//test against OOBB (a little bit more expensive)\\r\\n\\t\\t\\t\\tif( !geo.testSphereBBox( local_origin, radius, instance.oobb) )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tif( instance.type == LS.PhysicsInstance.MESH )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar octree = instance.mesh.octree;\\r\\n\\t\\t\\t\\t\\tif(!octree)\\r\\n\\t\\t\\t\\t\\t\\toctree = instance.mesh.octree = new GL.Octree( instance.mesh );\\r\\n\\t\\t\\t\\t\\tif( !octree.testSphere( local_origin, radius ) )\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\treturn instance;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\t//test collision between two PhysicsInstance \\r\\n\\ttestCollision: function( A, B )\\r\\n\\t{\\r\\n\\t\\t//test AABBs\\r\\n\\t\\tif( !geo.testBBoxBBox( A.aabb, B.aabb ) )\\r\\n\\t\\t\\treturn false;\\r\\n\\r\\n\\t\\treturn true; //TODO\\r\\n\\r\\n\\t\\t//conver A to B local Space\\r\\n\\r\\n\\t\\t//test box box\\r\\n\\r\\n\\t\\t//test box sphere\\r\\n\\r\\n\\t\\t//test box mesh\\r\\n\\r\\n\\t\\t//test sphere box\\r\\n\\r\\n\\t\\t//test sphere sphere\\r\\n\\r\\n\\t\\t//mesh mesh not supported\\r\\n\\r\\n\\t\\t//return true;\\r\\n\\t},\\r\\n\\r\\n\\ttestAllCollisions: function( on_collision, layers, scene )\\r\\n\\t{\\r\\n\\t\\tif(layers === undefined)\\r\\n\\t\\t\\tlayers = 0xFFFF;\\r\\n\\t\\tscene = scene || LS.GlobalScene;\\r\\n\\r\\n\\t\\tvar colliders = scene._colliders;\\r\\n\\t\\tvar l = colliders.length;\\r\\n\\r\\n\\t\\tvar collisions = false;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar instance_A = colliders[i];\\r\\n\\r\\n\\t\\t\\tif( (layers & instance_A.layers) === 0 )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tfor(var j = i+1; j < l; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar instance_B = colliders[j];\\r\\n\\r\\n\\t\\t\\t\\tif( (layers & instance_B.layers) === 0 )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tif( this.testCollision( instance_A, instance_B ) )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(on_collision)\\r\\n\\t\\t\\t\\t\\t\\ton_collision( instance_A, instance_B );\\r\\n\\t\\t\\t\\t\\tcollisions = true;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn collisions;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Cast a ray that traverses the scene checking for collisions with RenderInstances instead of colliders\\r\\n\\t* Similar to Physics.raycast but using the RenderInstances (if options.triangle_collision it builds Octrees for the RIs whose OOBB collides with the ray)\\r\\n\\t* @method raycastRenderInstances\\r\\n\\t* @param {vec3} origin in world space\\r\\n\\t* @param {vec3} direction in world space\\r\\n\\t* @param {Object} options { instances: array of instances, if not the scene will be used, triangle_collision: true if you want to test against triangles, max_distance: maxium ray distance, layers, scene, max_distance, first_collision : returns the first collision (which could be not the closest one) }\\r\\n\\t* @return {Array} array containing all the RenderInstances that collided with the ray in the form [SceneNode, RenderInstance, collision point, distance]\\r\\n\\t*/\\r\\n\\traycastRenderInstances: function( origin, direction, options )\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\t\\tvar layers = options.layers;\\r\\n\\t\\tif(layers === undefined)\\r\\n\\t\\t\\tlayers = 0xFFFF;\\r\\n\\t\\tvar max_distance = options.max_distance || Number.MAX_VALUE;\\r\\n\\t\\tvar scene = options.scene || LS.GlobalScene;\\r\\n\\r\\n\\t\\tvar triangle_collision = !!options.triangle_collision;\\r\\n\\t\\tvar first_collision = !!options.first_collision;\\r\\n\\t\\tvar compute_normal = !!options.normal;\\r\\n\\t\\tvar ignore_transparent = !!options.ignore_transparent;\\r\\n\\r\\n\\t\\tvar instances = options.instances || scene._instances;\\r\\n\\t\\tif(!instances || !instances.length)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar collisions = [];\\r\\n\\r\\n\\t\\tvar local_origin = vec3.create();\\r\\n\\t\\tvar local_direction = vec3.create();\\r\\n\\r\\n\\t\\t//for every instance\\r\\n\\t\\tfor(var i = 0, l = instances.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar instance = instances[i];\\r\\n\\r\\n\\t\\t\\tif((layers & instance.layers) === 0 ) //|| !(instance.flags & RI_RAYCAST_ENABLED) \\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(instance.material && instance.material.render_state.blend && ignore_transparent)\\r\\n\\t\\t\\t\\tcontinue; //avoid semitransparent\\r\\n\\r\\n\\t\\t\\t//test against AABB\\r\\n\\t\\t\\tvar collision_point = vec3.create();\\r\\n\\t\\t\\tif( !geo.testRayBBox( origin, direction, instance.aabb, null, collision_point, max_distance ) )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar model = instance.matrix;\\r\\n\\t\\t\\tvar hit = null;\\r\\n\\r\\n\\t\\t\\t//ray to local\\r\\n\\t\\t\\tvar inv = mat4.invert( mat4.create(), model );\\r\\n\\t\\t\\tmat4.multiplyVec3( local_origin, inv, origin );\\r\\n\\t\\t\\tmat4.rotateVec3( local_direction, inv, direction );\\r\\n\\r\\n\\t\\t\\t//test against OOBB (a little bit more expensive)\\r\\n\\t\\t\\tif( !geo.testRayBBox( local_origin, local_direction, instance.oobb, null, collision_point, max_distance) )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//check which mesh to use\\r\\n\\t\\t\\tvar collision_normal = null;\\r\\n\\t\\t\\t\\r\\n\\t\\t\\t//test against mesh\\r\\n\\t\\t\\tif( triangle_collision )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar collision_mesh = instance.lod_mesh || instance.mesh;\\r\\n\\t\\t\\t\\tvar octree = collision_mesh.octree;\\r\\n\\t\\t\\t\\tif(!octree)\\r\\n\\t\\t\\t\\t\\toctree = collision_mesh.octree = new GL.Octree( collision_mesh );\\r\\n\\t\\t\\t\\thit = octree.testRay( local_origin, local_direction, 0.0, max_distance );\\r\\n\\t\\t\\t\\tif(!hit)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tmat4.multiplyVec3( collision_point, model, hit.pos );\\r\\n\\t\\t\\t\\tif(compute_normal)\\r\\n\\t\\t\\t\\t\\tcollision_normal = mat4.rotateVec3( vec3.create(), model, hit.normal );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tvec3.transformMat4( collision_point, collision_point, model );\\r\\n\\r\\n\\t\\t\\t//compute distance\\r\\n\\t\\t\\tvar distance = vec3.distance( origin, collision_point );\\r\\n\\t\\t\\tif(distance < max_distance)\\r\\n\\t\\t\\t\\tcollisions.push( new LS.Collision( instance.node, instance, collision_point, distance, collision_normal, hit ) );\\r\\n\\r\\n\\t\\t\\tif(first_collision)\\r\\n\\t\\t\\t\\treturn collisions;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tcollisions.sort( LS.Collision.isCloser );\\r\\n\\t\\treturn collisions;\\r\\n\\t},\\r\\n\\r\\n\\t/**\\r\\n\\t* Cast a ray that traverses the scene checking for collisions with RenderInstances instead of colliders\\r\\n\\t* Similar to Physics.raycast but using the RenderInstances (if options.triangle_collision it builds Octrees for the RIs whose OOBB collides with the ray)\\r\\n\\t* @method raycastRenderInstances\\r\\n\\t* @param {vec3} origin in world space\\r\\n\\t* @param {vec3} direction in world space\\r\\n\\t* @param {LS.SceneNode} node \\r\\n\\t* @param {Object} options ( triangle_collision: true if you want to test against triangles, max_distance: maxium ray distance, layers, scene, max_distance, first_collision : returns the first collision (which could be not the closest one) )\\r\\n\\t* @return {Array} array containing all the RenderInstances that collided with the ray in the form [SceneNode, RenderInstance, collision point, distance]\\r\\n\\t*/\\r\\n\\traycastNode: function( origin, direction, node, options )\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\t\\toptions.instances = node._instances;\\r\\n\\t\\treturn this.raycastRenderInstances( origin, direction, options );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\nLS.Physics = Physics;\\r\\n///@FILE:../src/components/transform.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* Transform that contains the position (vec3), rotation (quat) and scale (vec3) \\r\\n* It uses lazy update to recompute the matrices.\\r\\n* @class Transform\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\n\\r\\nfunction Transform( o )\\r\\n{\\r\\n\\t//packed data (helpful for animation stuff)\\r\\n\\tthis._data = new Float32Array( 3 + 4 + 3 ); //pos, rot, scale, also known as trans10\\r\\n\\r\\n\\t//TSR\\r\\n\\tthis._position = this._data.subarray(0,3);\\r\\n\\tthis._rotation = this._data.subarray(3,7);\\r\\n\\tquat.identity(this._rotation);\\r\\n\\tthis._scaling = this._data.subarray(7,10);\\r\\n\\tthis._scaling[0] = this._scaling[1] = this._scaling[2] = 1;\\r\\n\\r\\n\\t//matrices\\r\\n\\tthis._local_matrix = mat4.create();\\r\\n\\tthis._global_matrix = mat4.create();\\r\\n\\r\\n\\tthis._uid = null;\\r\\n\\tthis._root = null;\\r\\n\\tthis._parent = null;\\r\\n\\r\\n\\tthis._must_update = false; //local matrix must be redone\\r\\n\\tthis._version = 0;\\r\\n\\r\\n\\t/* JS feature deprecated\\r\\n\\tif(Object.observe)\\r\\n\\t{\\r\\n\\t\\tvar inner_transform_change = (function(c) { \\r\\n\\t\\t\\tthis._must_update = true;\\r\\n\\t\\t}).bind(this);\\r\\n\\t\\tObject.observe( this._position, inner_transform_change );\\r\\n\\t\\tObject.observe( this._rotation, inner_transform_change );\\r\\n\\t\\tObject.observe( this._scaling, inner_transform_change );\\r\\n\\t\\tObject.observe( this._data, inner_transform_change );\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nTransform.temp_matrix = mat4.create();\\r\\nTransform.icon = \\\"mini-icon-gizmo.png\\\";\\r\\nTransform.ZERO = vec3.create();\\r\\nTransform.UP = vec3.fromValues(0,1,0);\\r\\nTransform.RIGHT = vec3.fromValues(1,0,0);\\r\\nTransform.FRONT = vec3.fromValues(0,0,-1);\\r\\n\\r\\nTransform.TRANS10_IDENTITY = new Float32Array([0,0,0,0,0,0,1,1,1,1]);\\r\\n\\r\\nTransform[\\\"@rotation\\\"] = { type: \\\"quat\\\"};\\r\\nTransform[\\\"@data\\\"] = { type: \\\"trans10\\\" };\\r\\n\\r\\n//what is this used for??\\r\\nTransform.properties = {\\r\\n\\tposition:\\\"vec3\\\",\\r\\n\\tscaling:\\\"vec3\\\",\\r\\n\\trotation:\\\"quat\\\"\\r\\n};\\r\\n\\r\\nTransform.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tif(!node.transform)\\r\\n\\t\\tnode.transform = this;\\r\\n}\\r\\n\\r\\nTransform.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tif(node.transform == this)\\r\\n\\t\\tdelete node[\\\"transform\\\"];\\r\\n}\\r\\n\\r\\n/**\\r\\n* The position relative to its parent in vec3 format\\r\\n* @property position {vec3}\\r\\n*/\\r\\nObject.defineProperty( Transform.prototype, 'position', {\\r\\n\\tget: function() { return this._position; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif(!v || !v.length)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._position.set(v); \\r\\n\\t\\tthis._must_update = true; \\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( Transform.prototype, 'x', {\\r\\n\\tget: function() { return this._position[0]; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._position[0] = v; \\r\\n\\t\\tthis._must_update = true; \\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( Transform.prototype, 'y', {\\r\\n\\tget: function() { return this._position[1]; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._position[1] = v; \\r\\n\\t\\tthis._must_update = true; \\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( Transform.prototype, 'z', {\\r\\n\\tget: function() { return this._position[2]; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._position[2] = v; \\r\\n\\t\\tthis._must_update = true; \\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n/*\\r\\nObject.defineProperty( Transform.prototype, 'pitch', {\\r\\n\\tget: function() { return 0; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis.rotateX(v);\\r\\n\\t\\tthis._must_update = true; \\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n*/\\r\\n\\r\\n/**\\r\\n* The orientation relative to its parent in quaternion format\\r\\n* @property rotation {quat}\\r\\n*/\\r\\nObject.defineProperty( Transform.prototype, 'rotation', {\\r\\n\\tget: function() { return this._rotation; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._rotation.set(v);\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t},\\r\\n\\tenumerable: true //avoid problems\\r\\n});\\r\\n\\r\\n/**\\r\\n* The scaling relative to its parent in vec3 format (default is [1,1,1])\\r\\n* @property scaling {vec3}\\r\\n*/\\r\\nObject.defineProperty( Transform.prototype, 'scaling', {\\r\\n\\tget: function() { return this._scaling; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif(v.constructor === Number)\\r\\n\\t\\t\\tthis._scaling[0] = this._scaling[1] = this._scaling[2] = v;\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis._scaling.set(v);\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The scaling relative to its parent in vec3 format (default is [1,1,1])\\r\\n* @property scaling {vec3}\\r\\n*/\\r\\nObject.defineProperty( Transform.prototype, 'uniform_scaling', {\\r\\n\\tget: function() { return this._scaling[0]; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._scaling[0] = this._scaling[1] = this._scaling[2] = v;\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The local matrix transform relative to its parent in mat4 format\\r\\n* @property matrix {mat4}\\r\\n*/\\r\\nObject.defineProperty( Transform.prototype, 'matrix', {\\r\\n\\tget: function() { \\r\\n\\t\\tif(this._must_update)\\r\\n\\t\\t\\tthis.updateMatrix();\\r\\n\\t\\treturn this._local_matrix;\\r\\n\\t},\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis.fromMatrix(v);\\t\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n//this is used to speed up copying between transforms and for animation (better to animate one track than several)\\r\\nObject.defineProperty( Transform.prototype, 'data', {\\r\\n\\tget: function() { \\r\\n\\t\\treturn this._data;\\r\\n\\t},\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._data.set(v);\\t\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n//in degrees\\r\\nObject.defineProperty( Transform.prototype, 'xrotation', {\\r\\n\\tget: function() { return 0; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis.rotateX(v * DEG2RAD);\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n//in degrees\\r\\nObject.defineProperty( Transform.prototype, 'yrotation', {\\r\\n\\tget: function() { return 0; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis.rotateY(v * DEG2RAD);\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n//in degrees\\r\\nObject.defineProperty( Transform.prototype, 'zrotation', {\\r\\n\\tget: function() { return 0; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis.rotateZ(v * DEG2RAD);\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* The position relative to its parent in vec3 format\\r\\n* @property position {vec3}\\r\\n*/\\r\\nObject.defineProperty( Transform.prototype, 'globalPosition', {\\r\\n\\tget: function() { return this.getGlobalPosition(); },\\r\\n\\tset: function(v) { \\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The matrix transform relative to world coordinates\\r\\n* @property globalMatrix {mat4}\\r\\n*/\\r\\nObject.defineProperty( Transform.prototype, 'globalMatrix', {\\r\\n\\tget: function() { \\r\\n\\t\\tthis.updateGlobalMatrix();\\r\\n\\t\\treturn this._global_matrix;\\r\\n\\t},\\r\\n\\tset: function(v) { \\r\\n\\t\\tthrow(\\\"globalMatrix cannot be set, use fromMatrix(m,true)\\\");\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The forward vector in global coordinates\\r\\n* @property forward {mat4}\\r\\n*/\\r\\nObject.defineProperty( Transform.prototype, 'forward', {\\r\\n\\tget: function() { \\r\\n\\t\\tthis.updateGlobalMatrix();\\r\\n\\t\\treturn mat4.rotateVec3( vec3.create(), this._global_matrix, LS.FRONT );\\r\\n\\t},\\r\\n\\tset: function(v) { \\r\\n\\t\\tthrow(\\\"forward cannot be set\\\");\\r\\n\\t},\\r\\n\\tenumerable: false //dont worry, it uses its own serialize\\r\\n});\\r\\n\\r\\n/**\\r\\n* Force object to update matrices in case they were modified\\r\\n* @property mustUpdate {boolean}\\r\\n*/\\r\\nObject.defineProperty( Transform.prototype, 'mustUpdate', {\\r\\n\\tget: function() { \\r\\n\\t\\treturn this._must_update;\\r\\n\\t},\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._must_update = true;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nTransform.prototype.getPropertiesInfo = function(v)\\r\\n{\\r\\n\\tif(v == \\\"output\\\")\\r\\n\\t{\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tposition:\\\"vec3\\\",\\r\\n\\t\\t\\tscaling:\\\"vec3\\\",\\r\\n\\t\\t\\trotation:\\\"quat\\\",\\r\\n\\t\\t\\tmatrix:\\\"mat4\\\",\\r\\n\\t\\t\\tglobalPosition:\\\"vec3\\\",\\r\\n\\t\\t\\tglobalMatrix:\\\"mat4\\\"\\r\\n\\t\\t};\\r\\n\\t} \\r\\n\\telse //if(v == \\\"input\\\")\\r\\n\\t{\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tposition:\\\"vec3\\\",\\r\\n\\t\\t\\tscaling:\\\"vec3\\\",\\r\\n\\t\\t\\trotation:\\\"quat\\\",\\r\\n\\t\\t\\tmatrix:\\\"mat4\\\"\\r\\n\\t\\t};\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Copy the transform from another Transform\\r\\n* @method copyFrom\\r\\n* @param {Transform} src\\r\\n*/\\r\\nTransform.prototype.copyFrom = function(src)\\r\\n{\\r\\n\\tthis.configure( src.serialize() );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Configure from a serialized object\\r\\n* @method configure\\r\\n* @param {Object} object with the serialized info\\r\\n*/\\r\\nTransform.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(o.uid) this.uid = o.uid;\\r\\n\\tif(o.position) this._position.set( o.position );\\r\\n\\tif(o.scaling) this._scaling.set( o.scaling );\\r\\n\\r\\n\\tif(o.rotation && o.rotation.length == 4)\\r\\n\\t\\tthis._rotation.set( o.rotation );\\r\\n\\tif(o.rotation && o.rotation.length == 3)\\r\\n\\t{\\r\\n\\t\\tquat.identity( this._rotation );\\r\\n\\t\\tvar R = quat.setAngleAxis( quat.create(), [1,0,0], o.rotation[0] * DEG2RAD);\\r\\n\\t\\tquat.multiply(this._rotation, this._rotation, R ); \\r\\n\\t\\tquat.setAngleAxis( R, [0,1,0], o.rotation[1] * DEG2RAD );\\r\\n\\t\\tquat.multiply(this._rotation, this._rotation, R ); \\r\\n\\t\\tquat.setAngleAxis( R, [0,0,1], o.rotation[2] * DEG2RAD );\\r\\n\\t\\tquat.multiply(this._rotation, this._rotation, R ); \\r\\n\\t}\\r\\n\\r\\n\\tthis._must_update = true;\\r\\n\\tthis.updateGlobalMatrix();\\r\\n\\tthis._on_change();\\r\\n}\\r\\n\\r\\n/**\\r\\n* Serialize the object \\r\\n* @method serialize\\r\\n* @return {Object} object with the serialized info\\r\\n*/\\r\\nTransform.prototype.serialize = function( simplified )\\r\\n{\\r\\n\\t\\r\\n\\tvar o = {\\r\\n\\t\\tobject_class: \\\"Transform\\\",\\r\\n\\t\\tuid: this.uid,\\r\\n\\t\\tposition: [ this._position[0],this._position[1],this._position[2] ],\\r\\n\\t\\trotation: [ this._rotation[0],this._rotation[1],this._rotation[2],this._rotation[3] ],\\r\\n\\t\\tscaling: [ this._scaling[0],this._scaling[1],this._scaling[2] ]\\r\\n\\t};\\r\\n\\r\\n\\tif( !this.isIdentity() && !simplified )\\r\\n\\t\\to.matrix = toArray( this._local_matrix );; //could be useful\\r\\n\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\nTransform.prototype.isIdentity = function()\\r\\n{\\r\\n\\tfor(var i = 0; i < this._local_matrix.length; ++i)\\r\\n\\t\\tif( Math.abs( this._local_matrix[i] - LS.IDENTITY[i] ) > 0.001 )\\r\\n\\t\\t\\treturn false;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Reset this transform\\r\\n* @method identity\\r\\n*/\\r\\nTransform.prototype.identity = function()\\r\\n{\\r\\n\\tvec3.copy(this._position, LS.ZEROS );\\r\\n\\tquat.identity( this._rotation );\\r\\n\\tvec3.copy(this._scaling, LS.ONES );\\r\\n\\tmat4.identity(this._local_matrix);\\r\\n\\tmat4.identity(this._global_matrix);\\r\\n\\tthis._version += 1;\\r\\n\\tthis._must_update = false;\\r\\n}\\r\\n\\r\\nTransform.prototype.reset = Transform.prototype.identity;\\r\\n\\r\\n/**\\r\\n* Sets the rotation to identity\\r\\n* @method resetRotation\\r\\n*/\\r\\nTransform.prototype.resetRotation = function()\\r\\n{\\r\\n\\tquat.identity( this._rotation );\\r\\n\\tthis._version += 1;\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change();\\r\\n}\\r\\n\\r\\n/**\\r\\n* Sets the position to 0,0,0\\r\\n* @method resetPosition\\r\\n*/\\r\\nTransform.prototype.resetPosition = function()\\r\\n{\\r\\n\\tvec3.copy( this._position, LS.ZEROS );\\r\\n\\tthis._version += 1;\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Sets the scale to 1,1,1\\r\\n* @method resetScale\\r\\n*/\\r\\nTransform.prototype.resetScale = function()\\r\\n{\\r\\n\\tvec3.copy( this._scaling, LS.ONES );\\r\\n\\tthis._version += 1;\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns a copy of the local position\\r\\n* @method getPosition\\r\\n* @param {vec3} out [optional] where to store the result, otherwise one vec3 is created and returned\\r\\n* @return {vec3} the position\\r\\n*/\\r\\nTransform.prototype.getPosition = function(out)\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tout.set( this._position );\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a copy of the global position\\r\\n* @method getGlobalPosition\\r\\n* @param {vec3} out [optional] where to store the result, otherwise one vec3 is created and returned\\r\\n* @return {vec3} the position\\r\\n*/\\r\\nTransform.prototype.getGlobalPosition = function(out)\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tif(this._parent) \\r\\n\\t\\treturn mat4.multiplyVec3( out, this.getGlobalMatrix(), Transform.ZERO ); //cannot reuse matrix in getGlobalMatrix, is recursive\\r\\n\\treturn vec3.copy(out, this._position );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the rotation in quaternion array (a copy)\\r\\n* @method getRotation\\r\\n* @param {quat} out [optional] where to store the result, otherwise one quat is created and returned\\r\\n* @return {quat} the rotation\\r\\n*/\\r\\nTransform.prototype.getRotation = function(out)\\r\\n{\\r\\n\\tout = out || quat.create();\\r\\n\\treturn vec3.copy(out,this._rotation);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the global rotation in quaternion array (a copy)\\r\\n* @method getRotation\\r\\n* @param {quat} out [optional] where to store the result, otherwise one quat is created and returned\\r\\n* @return {quat} the rotation\\r\\n*/\\r\\nTransform.prototype.getGlobalRotation = function(out)\\r\\n{\\r\\n\\tout = out || quat.create();\\r\\n\\tif( !this._parent )\\r\\n\\t{\\r\\n\\t\\tquat.copy(out, this._rotation);\\r\\n\\t\\treturn out;\\r\\n\\t}\\r\\n\\r\\n\\tvar aux = this._parent;\\r\\n\\tquat.copy(out,this._rotation);\\r\\n\\twhile(aux)\\r\\n\\t{\\r\\n\\t\\tquat.multiply(out, aux._rotation, out);\\r\\n\\t\\taux = aux._parent;\\r\\n\\t}\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns the scale (its a copy)\\r\\n* @method getScale\\r\\n* @param {vec3} out [optional] where to store the result, otherwise one vec3 is created and returned\\r\\n* @return {vec3} the scale\\r\\n*/\\r\\nTransform.prototype.getScale = function(out)\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\treturn vec3.copy(out,this._scaling);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a copy of the global scale (this is not correct, there is no global_scale factor, because due to rotations the axis could change)\\r\\n* @method getGlobalScale\\r\\n* @param {vec3} out [optional] where to store the result, otherwise one vec3 is created and returned\\r\\n* @return {vec3} the scale\\r\\n*/\\r\\nTransform.prototype.getGlobalScale = function(out)\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tif( this._parent )\\r\\n\\t{\\r\\n\\t\\tvar aux = this;\\r\\n\\t\\tvec3.copy(out,this._scaling);\\r\\n\\t\\twhile(aux._parent)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvec3.multiply(out, out, aux._scaling);\\r\\n\\t\\t\\taux = aux._parent;\\r\\n\\t\\t}\\r\\n\\t\\treturn out;\\r\\n\\t}\\r\\n\\treturn vec3.copy(out, this._scaling);\\r\\n}\\r\\n\\r\\n/**\\r\\n* update the local Matrix to match the position,scale and rotation\\r\\n* @method updateMatrix\\r\\n*/\\r\\nTransform.prototype.updateMatrix = function()\\r\\n{\\r\\n\\tmat4.fromRotationTranslation( this._local_matrix , this._rotation, this._position );\\r\\n\\tmat4.scale(this._local_matrix, this._local_matrix, this._scaling);\\r\\n\\tthis._must_update = false;\\r\\n\\tthis._version += 1;\\r\\n\\tthis.updateDescendants();\\r\\n}\\r\\nTransform.prototype.updateLocalMatrix = Transform.prototype.updateMatrix;\\r\\n\\r\\n/**\\r\\n* updates the global matrix using the parents transformation\\r\\n* @method updateGlobalMatrix\\r\\n* @param {bool} fast it doesnt recompute parent matrices, just uses the stored one, is faster but could create errors if the parent doesnt have its global matrix update\\r\\n*/\\r\\nTransform.prototype.updateGlobalMatrix = function (fast)\\r\\n{\\r\\n\\tif(this._must_update)\\r\\n\\t\\tthis.updateMatrix();\\r\\n\\tif (this._parent)\\r\\n\\t\\tmat4.multiply( this._global_matrix, fast ? this._parent._global_matrix : this._parent.getGlobalMatrix( this._parent._global_matrix ), this._local_matrix );\\r\\n\\telse\\r\\n\\t\\tthis._global_matrix.set( this._local_matrix ); \\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a copy of the local matrix of this transform (it updates the matrix automatically)\\r\\n* @method getMatrix\\r\\n* @param {mat4} out [optional] where to store the result, otherwise one mat4 is created and returned\\r\\n* @return {mat4} the matrix\\r\\n*/\\r\\nTransform.prototype.getMatrix = function (out)\\r\\n{\\r\\n\\tout = out || mat4.create();\\r\\n\\tif(this._must_update)\\r\\n\\t\\tthis.updateMatrix();\\r\\n\\treturn mat4.copy(out, this._local_matrix);\\r\\n}\\r\\nTransform.prototype.getLocalMatrix = Transform.prototype.getMatrix; //alias\\r\\n\\r\\n/**\\r\\n* Returns the original local matrix of this transform (it updates the matrix automatically)\\r\\n* @method getLocalMatrixRef\\r\\n* @return {mat4} the matrix in array format\\r\\n*/\\r\\nTransform.prototype.getLocalMatrixRef = function ()\\r\\n{\\r\\n\\tif(this._must_update)\\r\\n\\t\\tthis.updateMatrix();\\r\\n\\treturn this._local_matrix;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns a copy of the global matrix of this transform (it updates the matrix automatically)\\r\\n* @method getGlobalMatrix\\r\\n* @param {mat4} out optional\\r\\n* @param {boolean} fast this flags skips recomputing parents matrices\\r\\n* @return {mat4} the matrix in array format\\r\\n*/\\r\\nTransform.prototype.getGlobalMatrix = function (out, fast)\\r\\n{\\r\\n\\tif(this._must_update)\\r\\n\\t\\tthis.updateMatrix();\\r\\n\\tout = out || mat4.create();\\r\\n\\tif (this._parent)\\r\\n\\t\\tmat4.multiply( this._global_matrix, fast ? this._parent._global_matrix : this._parent.getGlobalMatrix( this._parent._global_matrix ), this._local_matrix );\\r\\n\\telse\\r\\n\\t\\tmat4.copy( this._global_matrix, this._local_matrix ); \\r\\n\\treturn mat4.copy(out, this._global_matrix);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a copy of the global matrix of this transform (it updates the matrix automatically)\\r\\n* @method getGlobalMatrix\\r\\n* @return {mat4} the matrix in array format\\r\\n*/\\r\\nTransform.prototype.getGlobalMatrixRef = function (fast)\\r\\n{\\r\\n\\tthis.updateGlobalMatrix(fast);\\r\\n\\treturn this._global_matrix;\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns an array with all the ancestors\\r\\n* @method getAncestors\\r\\n* @return {Array} \\r\\n*/\\r\\nTransform.prototype.getAncestors = function()\\r\\n{\\r\\n\\tvar r = [ this ];\\r\\n\\tvar aux = this;\\r\\n\\twhile(aux = aux._parent)\\r\\n\\t\\tr.unshift(aux);\\t\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns a quaternion with all parents rotations\\r\\n* @method getGlobalRotation\\r\\n* @return {quat} Quaternion\\r\\n*/\\r\\n/*\\r\\nTransform.prototype.getGlobalRotation = function (q)\\r\\n{\\r\\n\\tq = q || quat.create();\\r\\n\\tq.set(this._rotation);\\r\\n\\r\\n\\t//concatenate all parents rotations\\r\\n\\tvar aux = this._parent;\\r\\n\\twhile(aux)\\r\\n\\t{\\r\\n\\t\\tquat.multiply(q,q,aux._rotation);\\r\\n\\t\\taux = aux._parent;\\r\\n\\t}\\r\\n\\treturn q;\\r\\n}\\r\\n*/\\r\\n/**\\r\\n* Returns a Matrix with all parents rotations\\r\\n* @method getGlobalRotationMatrix\\r\\n* @return {mat4} Matrix rotation\\r\\n*/\\r\\n/*\\r\\nTransform.prototype.getGlobalRotationMatrix = function (m)\\r\\n{\\r\\n\\tvar q = quat.clone(this._rotation);\\r\\n\\r\\n\\tvar aux = this._parent;\\r\\n\\twhile(aux)\\r\\n\\t{\\r\\n\\t\\tquat.multiply(q, q, aux._rotation);\\r\\n\\t\\taux = aux._parent;\\r\\n\\t}\\r\\n\\r\\n\\tm = m || mat4.create();\\r\\n\\treturn mat4.fromQuat(m,q);\\r\\n}\\r\\n*/\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns the local matrix of this transform without the rotation or scale\\r\\n* @method getGlobalTranslationMatrix\\r\\n* @return {mat4} the matrix in array format\\r\\n*/\\r\\nTransform.prototype.getGlobalTranslationMatrix = function ()\\r\\n{\\r\\n\\tvar pos = this.getGlobalPosition();\\r\\n\\treturn mat4.fromValues(1,0,0,0, 0,1,0,0, 0,0,1,0, pos[0], pos[1], pos[2], 1);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the global rotation in quaternion array (a copy)\\r\\n* @method getGlobalRotationMatrix\\r\\n* @return {mat4} the rotation\\r\\n*/\\r\\nTransform.prototype.getGlobalRotationMatrix = function(out)\\r\\n{\\r\\n\\tvar out = out || mat4.create();\\r\\n\\tif( !this._parent )\\r\\n\\t\\treturn mat4.fromQuat( out, this._rotation );\\r\\n\\t\\t\\r\\n\\tvar r = mat4.create();\\r\\n\\tvar aux = this;\\r\\n\\twhile( aux )\\r\\n\\t{\\r\\n\\t\\tmat4.fromQuat(r, aux._rotation);\\r\\n\\t\\tmat4.multiply(out,out,r);\\r\\n\\t\\taux = aux._parent;\\r\\n\\t}\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns the local matrix of this transform without the scale\\r\\n* @method getGlobalTranslationRotationMatrix\\r\\n* @return {mat4} the matrix in array format\\r\\n*/\\r\\nTransform.prototype.getGlobalTranslationRotationMatrix = function ()\\r\\n{\\r\\n\\tvar pos = this.getGlobalPosition();\\r\\n\\treturn mat4.fromRotationTranslation(mat4.create(), this.getGlobalRotation(), pos);\\r\\n}\\r\\nTransform.prototype.getGlobalMatrixWithoutScale = Transform.prototype.getGlobalTranslationRotationMatrix;\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns the matrix for the normals in the shader\\r\\n* @method getNormalMatrix\\r\\n* @return {mat4} the matrix in array format\\r\\n*/\\r\\nTransform.prototype.getNormalMatrix = function (m)\\r\\n{\\r\\n\\tif(this._must_update)\\r\\n\\t\\tthis.updateMatrix();\\r\\n\\r\\n\\tm = m || mat4.create();\\r\\n\\tif (this._parent)\\r\\n\\t\\tmat4.multiply( this._global_matrix, this._parent.getGlobalMatrix(), this._local_matrix );\\r\\n\\telse\\r\\n\\t\\tm.set(this._local_matrix); //return local because it has no parent\\r\\n\\treturn mat4.transpose(m, mat4.invert(m,m) );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Configure the transform from a local Matrix (do not tested carefully)\\r\\n* @method fromMatrix\\r\\n* @param {mat4} matrix the matrix in array format\\r\\n* @param {bool} is_global tells if the matrix is in global space [optional]\\r\\n*/\\r\\nTransform.prototype.fromMatrix = (function() { \\r\\n\\r\\n\\tvar global_temp = mat4.create();\\r\\n\\tvar temp_mat4 = mat4.create();\\r\\n\\tvar temp_mat3 = mat3.create();\\r\\n\\tvar temp_vec3 = vec3.create();\\r\\n\\t//var scale_temp = mat4.create();\\r\\n\\t\\r\\n\\treturn function fromMatrix( m, is_global )\\r\\n\\t{\\r\\n\\t\\tif(is_global && this._parent)\\r\\n\\t\\t{\\r\\n\\t\\t\\tmat4.copy(this._global_matrix, m); //assign to global\\r\\n\\t\\t\\tvar M_parent = this._parent.getGlobalMatrix( global_temp ); //get parent transform\\r\\n\\t\\t\\tvar r = mat4.invert( M_parent, M_parent ); //invert\\r\\n\\t\\t\\tif(!r)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tm = mat4.multiply( this._local_matrix, M_parent, m ); //transform from global to local\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//pos\\r\\n\\t\\tvar M = temp_mat4;\\r\\n\\t\\tM.set(m);\\r\\n\\t\\tmat4.multiplyVec3( this._position, M, LS.ZEROS );\\r\\n\\r\\n\\t\\t//compute scale\\r\\n\\t\\tthis._scaling[0] = vec3.length( mat4.rotateVec3( temp_vec3, M, LS.RIGHT) );\\r\\n\\t\\tthis._scaling[1] = vec3.length( mat4.rotateVec3( temp_vec3, M, LS.TOP) );\\r\\n\\t\\tthis._scaling[2] = vec3.length( mat4.rotateVec3( temp_vec3, M, LS.BACK) );\\r\\n\\r\\n\\t\\t//apply scale, why the inverse? ??\\r\\n\\t\\t//mat4.scale( scale_temp, M, [1/this._scaling[0], 1/this._scaling[1], 1/this._scaling[2]] );\\r\\n\\r\\n\\t\\t//quat.fromMat4(this._rotation, M);\\r\\n\\t\\t//*\\r\\n\\t\\t//normalize system vectors\\r\\n\\t\\tvec3.normalize( M.subarray(0,3), M.subarray(0,3) );\\r\\n\\t\\tvec3.normalize( M.subarray(4,7), M.subarray(4,7) );\\r\\n\\t\\tvec3.normalize( M.subarray(8,11), M.subarray(8,11) );\\r\\n\\r\\n\\t\\tvar M3 = mat3.fromMat4( temp_mat3, M );\\r\\n\\t\\tquat.fromMat3AndQuat( this._rotation, M3 );\\r\\n\\r\\n\\t\\t/* works with default fromMat3, not with fromMat3AndQuat\\r\\n\\t\\tvar M3 = mat3.fromMat4( temp_mat3, M );\\r\\n\\t\\tmat3.transpose( M3, M3 ); //why transpose?!?!\\r\\n\\t\\tquat.fromMat3( this._rotation, M3 );\\r\\n\\t\\tquat.normalize( this._rotation, this._rotation );\\r\\n\\t\\t//*/\\r\\n\\r\\n\\t\\tif(m != this._local_matrix)\\r\\n\\t\\t\\tmat4.copy(this._local_matrix, m);\\r\\n\\t\\tthis._must_update = false;\\r\\n\\t\\tthis._version += 1;\\r\\n\\t\\tthis._on_change(true);\\r\\n\\t}\\r\\n})();\\r\\n\\r\\n/**\\r\\n* Configure the transform from a global Matrix (do not tested carefully)\\r\\n* @method fromGlobalMatrix\\r\\n* @param {mat4} matrix the matrix in array format\\r\\n*/\\r\\nTransform.prototype.fromGlobalMatrix = function(m)\\r\\n{\\r\\n\\tthis.fromMatrix(m,true);\\t\\r\\n}\\r\\n\\r\\nTransform.fromMatrix4ToTransformData = (function() { \\r\\n\\r\\n\\tvar global_temp = mat4.create();\\r\\n\\tvar temp_mat4 = mat4.create();\\r\\n\\tvar temp_mat3 = mat3.create();\\r\\n\\tvar temp_vec3 = vec3.create();\\r\\n\\t\\r\\n\\treturn function fromMatrix4ToTransformData( m, out )\\r\\n\\t{\\r\\n\\t\\tvar data = out || new Float32Array( 3 + 4 + 3 ); //pos, rot, scale\\r\\n\\t\\tvar position = data.subarray(0,3);\\r\\n\\t\\tvar rotation = data.subarray(3,7);\\r\\n\\t\\tquat.identity(rotation);\\r\\n\\t\\tvar scaling = data.subarray(7,10);\\r\\n\\r\\n\\t\\t//pos\\r\\n\\t\\tvar M = temp_mat4;\\r\\n\\t\\tM.set(m);\\r\\n\\t\\tmat4.multiplyVec3( position, M, LS.ZEROS );\\r\\n\\r\\n\\t\\t//extract scaling by \\r\\n\\t\\tscaling[0] = vec3.length( mat4.rotateVec3( temp_vec3, M, LS.RIGHT) );\\r\\n\\t\\tscaling[1] = vec3.length( mat4.rotateVec3( temp_vec3, M, LS.TOP) );\\r\\n\\t\\tscaling[2] = vec3.length( mat4.rotateVec3( temp_vec3, M, LS.BACK) );\\r\\n\\r\\n\\t\\t//quat.fromMat4( rotation, M ); //doesnt work\\r\\n\\r\\n\\t\\t//normalize axis vectors\\r\\n\\t\\tvec3.normalize( M.subarray(0,3), M.subarray(0,3) );\\r\\n\\t\\tvec3.normalize( M.subarray(4,7), M.subarray(4,7) );\\r\\n\\t\\tvec3.normalize( M.subarray(8,11), M.subarray(8,11) );\\r\\n\\r\\n\\t\\tvar M3 = mat3.fromMat4( temp_mat3, M );\\r\\n\\t\\tmat3.transpose( M3, M3 );\\r\\n\\t\\tquat.fromMat3( rotation, M3 );\\r\\n\\t\\tquat.normalize( rotation, rotation );\\r\\n\\r\\n\\t\\treturn data;\\r\\n\\t}\\r\\n})();\\r\\n\\r\\n\\r\\n/**\\r\\n* Configure the transform rotation from a vec3 Euler angles (heading,attitude,bank)\\r\\n* @method setRotationFromEuler\\r\\n* @param {mat4} src, the matrix in array format\\r\\n*/\\r\\nTransform.prototype.setRotationFromEuler = function(v)\\r\\n{\\r\\n\\tquat.fromEuler( this._rotation, v );\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change();\\r\\n}\\r\\n\\r\\n/**\\r\\n* sets the position\\r\\n* @method setPosition\\r\\n* @param {number} x \\r\\n* @param {number} y\\r\\n* @param {number} z \\r\\n*/\\r\\nTransform.prototype.setPosition = function(x,y,z)\\r\\n{\\r\\n\\tif(arguments.length == 3)\\r\\n\\t\\tvec3.set(this._position, x,y,z);\\r\\n\\telse\\r\\n\\t\\tvec3.copy(this._position, x);\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* sets the rotation from a quaternion or from an angle(rad) and axis\\r\\n* @method setRotation\\r\\n* @param {quat} rotation in quaterion format or angle\\r\\n*/\\r\\nTransform.prototype.setRotation = function(q_angle,axis)\\r\\n{\\r\\n\\tif(axis)\\r\\n\\t\\tquat.setAxisAngle( this._rotation, axis, q_angle );\\r\\n\\telse\\r\\n\\t\\tquat.copy(this._rotation, q_angle );\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* sets the scale\\r\\n* @method setScale\\r\\n* @param {number} x \\r\\n* @param {number} y\\r\\n* @param {number} z \\r\\n*/\\r\\nTransform.prototype.setScale = function(x,y,z)\\r\\n{\\r\\n\\tif(arguments.length == 3)\\r\\n\\t\\tvec3.set(this._scaling, x,y,z);\\r\\n\\telse\\r\\n\\t\\tvec3.set(this._scaling, x,x,x);\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* translates object in local coordinates (using the rotation and the scale)\\r\\n* @method translate\\r\\n* @param {number} x \\r\\n* @param {number} y\\r\\n* @param {number} z \\r\\n*/\\r\\nTransform.prototype.translate = (function(){\\r\\n\\tvar tmp = vec3.create();\\r\\n\\tvar tmp2 = vec3.create();\\r\\n\\t\\r\\n\\treturn function(x,y,z)\\r\\n\\t{\\r\\n\\t\\tif(arguments.length == 3)\\r\\n\\t\\t{\\r\\n\\t\\t\\ttmp2[0] = x; tmp2[1] = y; tmp2[2] = z;\\r\\n\\t\\t\\tvec3.add( this._position, this._position, this.transformVector(tmp2, tmp) );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tvec3.add( this._position, this._position, this.transformVector(x, tmp) );\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t\\tthis._on_change(true);\\r\\n\\t};\\r\\n})();\\r\\n\\r\\n/**\\r\\n* translates object in local coordinates (adds to the position)\\r\\n* @method translateGlobal\\r\\n* @param {number} x \\r\\n* @param {number} y\\r\\n* @param {number} z \\r\\n*/\\r\\nTransform.prototype.translateGlobal = function(x,y,z)\\r\\n{\\r\\n\\tif(arguments.length == 3)\\r\\n\\t\\tvec3.add( this._position, this._position, [x,y,z] );\\r\\n\\telse\\r\\n\\t\\tvec3.add( this._position, this._position, x );\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* rotate object in local space (axis is in local space)\\r\\n* @method rotate\\r\\n* @param {number} angle_in_deg \\r\\n* @param {vec3} axis\\r\\n* @param {boolean} is_global tells if the axis is in global coordinates or local coordinates\\r\\n*/\\r\\nTransform.prototype.rotate = (function(){\\r\\n\\r\\n\\tvar temp = quat.create();\\r\\n\\tvar temp_axis = quat.create();\\r\\n\\r\\n\\treturn function(angle_in_deg, axis, is_global )\\r\\n\\t{\\r\\n\\t\\tif( is_global ) //convert global vector to local\\r\\n\\t\\t\\taxis = this.globalVectorToLocal( axis, temp_axis );\\r\\n\\t\\tquat.setAxisAngle( temp, axis, angle_in_deg * 0.0174532925 );\\r\\n\\t\\tquat.multiply( this._rotation, this._rotation, temp );\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t\\tthis._on_change(true);\\r\\n\\t}\\r\\n})();\\r\\n\\r\\n/**\\r\\n* rotate object in local space in local X axis\\r\\n* @method rotateX\\r\\n* @param {number} angle_in_rad\\r\\n*/\\r\\nTransform.prototype.rotateX = function(angle_in_rad)\\r\\n{\\r\\n\\tquat.rotateX( this._rotation, this._rotation, angle_in_rad );\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* rotate object in local space in local Y axis\\r\\n* @method rotateY\\r\\n* @param {number} angle_in_rad \\r\\n*/\\r\\nTransform.prototype.rotateY = function(angle_in_rad)\\r\\n{\\r\\n\\tquat.rotateY( this._rotation, this._rotation, angle_in_rad );\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change();\\r\\n}\\r\\n\\r\\n/**\\r\\n* rotate object in local space in local Z axis\\r\\n* @method rotateZ\\r\\n* @param {number} angle_in_rad \\r\\n*/\\r\\nTransform.prototype.rotateZ = function(angle_in_rad)\\r\\n{\\r\\n\\tquat.rotateZ( this._rotation, this._rotation, angle_in_rad );\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* rotate object in global space (axis is in global space)\\r\\n* @method rotateGlobal\\r\\n* @param {number} angle_in_deg \\r\\n* @param {vec3} axis\\r\\n*/\\r\\nTransform.prototype.rotateGlobal = function(angle_in_deg, axis)\\r\\n{\\r\\n\\tvar R = quat.setAxisAngle(quat.create(), axis, angle_in_deg * 0.0174532925);\\r\\n\\tquat.multiply(this._rotation, R, this._rotation);\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* rotate object in local space using a quat\\r\\n* @method rotateQuat\\r\\n* @param {quat} quaternion\\r\\n*/\\r\\nTransform.prototype.rotateQuat = function(quaternion)\\r\\n{\\r\\n\\tquat.multiply(this._rotation, this._rotation, quaternion);\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* rotate object in global space using a quat\\r\\n* @method rotateQuatGlobal\\r\\n* @param {quat} quaternion\\r\\n*/\\r\\nTransform.prototype.rotateQuatGlobal = function(quaternion)\\r\\n{\\r\\n\\tquat.multiply(this._rotation, quaternion, this._rotation);\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* scale the object\\r\\n* @method scale\\r\\n* @param {number} x \\r\\n* @param {number} y\\r\\n* @param {number} z \\r\\n*/\\r\\nTransform.prototype.scale = function(x,y,z)\\r\\n{\\r\\n\\tif(arguments.length == 3)\\r\\n\\t\\tvec3.multiply(this._scaling, this._scaling, [x,y,z]);\\r\\n\\telse\\r\\n\\t\\tvec3.multiply(this._scaling, this._scaling,x);\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change(true);\\r\\n}\\r\\n\\r\\n/**\\r\\n* This method is static (call it from Transform.interpolate)\\r\\n* interpolate the transform between two transforms and stores the result in another Transform\\r\\n* @method interpolate\\r\\n* @param {Transform} a \\r\\n* @param {Transform} b\\r\\n* @param {number} factor from 0 to 1 \\r\\n* @param {Transform} the destination\\r\\n*/\\r\\nTransform.interpolate = function( a, b, factor, result )\\r\\n{\\r\\n\\tvec3.lerp( result._scaling, a._scaling, b._scaling, factor); //scale\\r\\n\\tvec3.lerp( result._position, a._position, b._position, factor); //position\\r\\n\\tquat.slerp( result._rotation, a._rotation, b._rotation, factor); //rotation\\r\\n\\tthis._must_update = true;\\r\\n\\tthis._on_change();\\r\\n}\\r\\n\\r\\n/**\\r\\n* Orbits around a point\\r\\n* @method orbit\\r\\n* @param {number} angle_in_deg\\r\\n* @param {vec3} axis\\r\\n* @param {vec3} center in local coordinates\\r\\n*/\\r\\nTransform.prototype.orbit = (function() { \\r\\n\\tvar tmp_quat = quat.create();\\r\\n\\tvar tmp_vec3 = vec3.create();\\r\\n\\r\\n\\treturn function( angle_in_deg, axis, center )\\r\\n\\t{\\r\\n\\t\\tif(!center)\\r\\n\\t\\t\\tthrow(\\\"Transform orbit requires a center\\\");\\r\\n\\r\\n\\t\\tvar R = quat.setAxisAngle( tmp_quat, axis, angle_in_deg * 0.0174532925 );\\r\\n\\t\\ttmp_vec3.set( this._position );\\r\\n\\t\\tvec3.sub(tmp_vec3, tmp_vec3, center );\\r\\n\\t\\tvec3.transformQuat( tmp_vec3, tmp_vec3, R );\\r\\n\\t\\tvec3.add(tmp_vec3, tmp_vec3, center );\\r\\n\\t\\tthis._position.set( tmp_vec3 );\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t};\\r\\n})();\\r\\n\\r\\n\\r\\n/**\\r\\n* Orients the transform to look from one position to another\\r\\n* @method lookAt\\r\\n* @param {vec3} position\\r\\n* @param {vec3} target\\r\\n* @param {vec3} up\\r\\n* @param {boolean} in_world tells if the values are in world coordinates (otherwise asume its in local coordinates)\\r\\n*/\\r\\nTransform.prototype.lookAt = (function() { \\r\\n\\r\\n\\t//avoid garbage\\r\\n\\tvar GM = mat4.create();\\r\\n\\tvar temp = mat4.create();\\r\\n\\tvar temp_pos = vec3.create();\\r\\n\\tvar temp_target = vec3.create();\\r\\n\\tvar temp_up = vec3.create();\\r\\n\\t\\r\\n\\treturn function( pos, target, up, in_world )\\r\\n\\t{\\r\\n\\t\\tup = up || LS.TOP;\\r\\n\\r\\n\\t\\t//convert to local space\\r\\n\\t\\tif(in_world && this._parent)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._parent.getGlobalMatrix( GM );\\r\\n\\t\\t\\tvar inv = mat4.invert(GM,GM);\\r\\n\\t\\t\\tif(!inv)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tmat4.multiplyVec3(temp_pos, inv, pos);\\r\\n\\t\\t\\tmat4.multiplyVec3(temp_target, inv, target);\\r\\n\\t\\t\\tmat4.rotateVec3(temp_up, inv, up );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\ttemp_pos.set( pos );\\r\\n\\t\\t\\ttemp_target.set( target );\\r\\n\\t\\t\\ttemp_up.set( up );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tmat4.lookAt(temp, temp_pos, temp_target, temp_up);\\r\\n\\t\\t//mat4.invert(temp, temp);\\r\\n\\r\\n\\t\\tquat.fromMat4( this._rotation, temp );\\r\\n\\t\\tthis._position.set( temp_pos );\\t\\r\\n\\t\\tthis._must_update = true;\\r\\n\\r\\n\\t\\t/*\\r\\n\\t\\tmat4.lookAt(temp, pos, target, up);\\r\\n\\t\\tmat4.invert(temp, temp);\\r\\n\\t\\tthis.fromMatrix(temp);\\r\\n\\t\\tthis.updateGlobalMatrix();\\r\\n\\t\\t*/\\r\\n\\t}\\r\\n})();\\r\\n\\r\\n\\r\\n/**\\r\\n* Orients the transform to look at a position\\r\\n* @method orientTo\\r\\n* @param {vec3} target the position where to look at\\r\\n* @param {boolean} in_world tells if the target is in world coordinates (otherwise asume its in local coordinates)\\r\\n* @param {vec3} top [optional] a helper top vector, otherwise [0,1,0] is assumed\\r\\n* @param {vec3} right [optional] a helper right vector, otherwise [1,0,0] is assumed\\r\\n*/\\r\\nTransform.prototype.orientTo = (function() { \\r\\n\\r\\n\\t//avoid garbage\\r\\n\\tvar GM = mat4.create();\\r\\n\\tvar temp = mat3.create();\\r\\n\\tvar temp_pos = vec3.create();\\r\\n\\t//function\\r\\n\\treturn function( pos, in_world, top, right )\\r\\n\\t{\\r\\n\\t\\tright = right || LS.RIGHT;\\r\\n\\t\\ttop = top || LS.TOP;\\r\\n\\t\\t//convert to local space\\r\\n\\t\\tif(in_world && this._parent)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._parent.getGlobalMatrix( GM );\\r\\n\\t\\t\\tvar inv = mat4.invert(GM,GM);\\r\\n\\t\\t\\tif(!inv)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tmat4.multiplyVec3(temp_pos, inv, pos);\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\ttemp_pos.set( pos );\\r\\n\\t\\tmat3.setColumn( temp, right, 0 );\\r\\n\\t\\tmat3.setColumn( temp, top, 1 );\\r\\n\\t\\tmat3.setColumn( temp, pos, 2 );\\r\\n\\t\\tquat.fromMat3( this._rotation, temp );\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t}\\r\\n})();\\r\\n\\r\\n/**\\r\\n* Orients the transform so the axis points in that direction\\r\\n* @method orientAxis\\r\\n* @param {vec3} vector the vector to use as axis\\r\\n* @param {number} axis a enum that could be LS.POSX, LS.POSY, LS.POSZ, LS.NEGX, LS.NEGY, LS.NEGZ\\r\\n*/\\r\\nTransform.prototype.orientAxis = (function() { \\r\\n\\t//avoid garbage\\r\\n\\tvar GM = mat4.create();\\r\\n\\tvar temp = mat3.create();\\r\\n\\t//function\\r\\n\\treturn function( vector, axis )\\r\\n\\t{\\r\\n\\t\\tswitch(axis)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase LS.POSX: \\r\\n\\t\\t\\t\\tmat3.setColumn( temp, vector, 0 ); //x\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.TOP, 1 ); //y\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.FRONT, 2 ); //z\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase LS.POSY:\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.RIGHT, 0 ); //x\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, vector, 1 ); //y\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.FRONT, 2 ); //z\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase LS.POSZ:\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.RIGHT, 0 ); //x\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.TOP, 1 ); //y\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, vector, 2 ); //z\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase LS.NEGX: \\r\\n\\t\\t\\t\\tmat3.setColumn( temp, vector, 0 ); //x\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.BOTTOM, 1 ); //y\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.BACK, 2 ); //z\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase LS.NEGY:\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.LEFT, 0 ); //x\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, vector, 1 ); //y\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.BACK, 2 ); //z\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase LS.NEGZ:\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.LEFT, 0 ); //x\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, LS.BOTTOM, 1 ); //y\\r\\n\\t\\t\\t\\tmat3.setColumn( temp, vector, 2 ); //z\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tquat.fromMat3( this._rotation, temp );\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t}\\r\\n})();\\r\\n\\r\\n//Events\\r\\nTransform.prototype._on_change = function(only_events)\\r\\n{\\r\\n\\tif(!only_events)\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t/**\\r\\n\\t * Fired when the node has changed its transform\\r\\n\\t *\\r\\n\\t * @event changed\\r\\n\\t */\\r\\n\\tLEvent.trigger(this, \\\"changed\\\", this);\\r\\n\\tif(this._root)\\r\\n\\t\\tLEvent.trigger(this._root, \\\"transformChanged\\\", this);\\r\\n}\\r\\n\\r\\n//Transform\\r\\n/**\\r\\n* returns the [0,0,-1] vector in global space\\r\\n* @method getFront\\r\\n* @return {vec3}\\r\\n*/\\r\\nTransform.prototype.getFront = function(out) {\\r\\n\\treturn vec3.transformQuat(out || vec3.create(), Transform.FRONT, this.getGlobalRotation() );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the [0,1,0] vector in global space\\r\\n* @method getTop\\r\\n* @return {vec3}\\r\\n*/\\r\\nTransform.prototype.getTop = function(out) {\\r\\n\\treturn vec3.transformQuat(out || vec3.create(), Transform.UP, this.getGlobalRotation() );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the [1,0,0] vector in global space\\r\\n* @method getRight\\r\\n* @return {vec3}\\r\\n*/\\r\\nTransform.prototype.getRight = function(out) {\\r\\n\\treturn vec3.transformQuat(out || vec3.create(), Transform.RIGHT, this.getGlobalRotation() );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Multiplies a point by the local matrix (not global)\\r\\n* If no destination is specified a new vector is created\\r\\n* @method transformPoint\\r\\n* @param {vec3} point\\r\\n* @param {vec3} destination (optional)\\r\\n*/\\r\\nTransform.prototype.transformPoint = function(vec, dest) {\\r\\n\\tdest = dest || vec3.create();\\r\\n\\tif( this._must_update )\\r\\n\\t\\tthis.updateMatrix();\\r\\n\\treturn mat4.multiplyVec3( dest, this._local_matrix, vec );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* convert from local coordinates to global coordinates\\r\\n* If no destination is specified a new vector is created\\r\\n* @method localToGlobal\\r\\n* @param {vec3} point\\r\\n* @param {vec3} destination (optional)\\r\\n*/\\r\\nTransform.prototype.localToGlobal = function(vec, dest) {\\r\\n\\tdest = dest || vec3.create();\\r\\n\\tif(this._must_update)\\r\\n\\t\\tthis.updateMatrix();\\r\\n\\treturn mat4.multiplyVec3( dest, this.getGlobalMatrixRef(), vec );\\r\\n}\\r\\n\\r\\n/**\\r\\n* same as localToGlobal\\r\\n* @method transformPointGlobal\\r\\n* @param {vec3} point\\r\\n* @param {vec3} destination (optional)\\r\\n*/\\r\\nTransform.prototype.transformPointGlobal = Transform.prototype.localToGlobal;\\r\\n\\r\\n/**\\r\\n* convert from global coordinates to local coordinates\\r\\n* @method globalToLocal\\r\\n* @param {vec3} point\\r\\n* @param {vec3} destination (optional)\\r\\n*/\\r\\nTransform.prototype.globalToLocal = (function(){ \\r\\n\\tvar inv = mat4.create();\\r\\n\\treturn function(vec, dest) {\\r\\n\\t\\tdest = dest || vec3.create();\\r\\n\\t\\tif(this._must_update)\\r\\n\\t\\t\\tthis.updateMatrix();\\r\\n\\t\\tif( !mat4.invert( inv, this.getGlobalMatrixRef() ) )\\r\\n\\t\\t\\treturn inv;\\r\\n\\t\\treturn mat4.multiplyVec3( dest, inv, vec );\\r\\n\\t};\\r\\n})();\\r\\n\\r\\n/**\\r\\n* Applies the transformation to a vector (rotate but not translate)\\r\\n* @method transformVector\\r\\n* @param {vec3} vector\\r\\n* @param {vec3} destination (optional)\\r\\n*/\\r\\nTransform.prototype.transformVector = function( vec, dest ) {\\r\\n\\treturn vec3.transformQuat( dest || vec3.create(), vec, this._rotation );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Applies the transformation to a vector (rotate but not translate)\\r\\n* @method localVectorToGlobal\\r\\n* @param {vec3} vector\\r\\n* @param {vec3} destination (optional)\\r\\n*/\\r\\nTransform.prototype.localVectorToGlobal = function(vec, dest) {\\r\\n\\treturn vec3.transformQuat( dest || vec3.create(), vec, this.getGlobalRotation() );\\r\\n}\\r\\n\\r\\nTransform.prototype.transformVectorGlobal = Transform.prototype.localVectorToGlobal;\\r\\n\\r\\nTransform.prototype.globalVectorToLocal = function(vec, dest) {\\r\\n\\tvar Q = this.getGlobalRotation();\\r\\n\\tquat.invert(Q,Q);\\r\\n\\treturn vec3.transformQuat(dest || vec3.create(), vec, Q );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Apply a transform to this transform\\r\\n* @method applyTransform\\r\\n*/\\r\\nTransform.prototype.applyTransform = function( transform, center, is_global )\\r\\n{\\r\\n\\t//is local\\r\\n\\r\\n\\t//apply translation\\r\\n\\tvec3.add( this._position, this._position, transform._position );\\r\\n\\r\\n\\t//apply rotation\\r\\n\\tquat.multiply( this._rotation, this._rotation, transform._rotation );\\r\\n\\r\\n\\t//apply scale\\r\\n\\tvec3.multiply( this._scaling, this._scaling, transform._scaling );\\r\\n\\r\\n\\tthis._must_update = true; //matrix must be redone?\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Applies the transformation using a matrix\\r\\n* @method applyTransformMatrix\\r\\n* @param {mat4} matrix with the transform\\r\\n* @param {vec3} center different pivot [optional] if omited 0,0,0 will be used\\r\\n* @param {bool} is_global (optional) tells if the transformation should be applied in global space or local space\\r\\n*/\\r\\nTransform.prototype.applyTransformMatrix = (function(){ \\r\\n\\tvar T = mat4.create();\\r\\n\\tvar inv_center = vec3.create();\\r\\n\\tvar iT = mat4.create();\\r\\n\\tvar M = mat4.create();\\r\\n\\tvar temp = mat4.create();\\r\\n\\t\\r\\n\\treturn function(matrix, center, is_global)\\r\\n\\t{\\r\\n\\t\\tvar M = matrix;\\r\\n\\r\\n\\t\\tif(center)\\r\\n\\t\\t{\\r\\n\\t\\t\\tmat4.setTranslation( T, center);\\r\\n\\t\\t\\tvec3.scale( inv_center, center, -1 );\\r\\n\\t\\t\\tmat4.setTranslation( iT, inv_center);\\r\\n\\r\\n\\t\\t\\tmat4.multiply( M, T, matrix );\\r\\n\\t\\t\\tmat4.multiply( M, M, iT );\\r\\n\\t\\t}\\r\\n\\r\\n\\r\\n\\t\\tif(!this._parent)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(is_global)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.applyLocalTransformMatrix( M );\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//is local\\r\\n\\t\\t\\tthis.applyLocalTransformMatrix( M );\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t/*\\r\\n\\t\\t//convert transform to local coordinates\\r\\n\\t\\tvar GM = this.getGlobalMatrix();\\r\\n\\t\\tvar temp_mat = mat4.multiply( mat4.create(), M, GM );\\r\\n\\r\\n\\t\\tvar PGM = this._parent._global_matrix;\\r\\n\\t\\tvar inv_pgm = mat4.invert( mat4.create(), PGM );\\r\\n\\r\\n\\t\\tmat4.multiply(temp_mat, inv_pgm, temp_mat );\\r\\n\\t\\tthis.applyLocalTransformMatrix( temp_mat );\\r\\n\\t\\t//*/\\r\\n\\r\\n\\t\\t//*\\r\\n\\t\\tvar GM = this.getGlobalMatrix();\\r\\n\\t\\tvar PGM = this._parent._global_matrix;\\r\\n\\t\\tmat4.multiply( this._global_matrix, M, GM );\\r\\n\\r\\n\\t\\tif(!mat4.invert( temp, PGM ))\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t\\r\\n\\t\\tmat4.multiply( this._local_matrix, temp, this._global_matrix );\\r\\n\\t\\tthis.fromMatrix( this._local_matrix );\\r\\n\\t\\t//*/\\r\\n\\t};\\r\\n})();\\r\\n\\r\\n//applies matrix to position, rotation and scale individually, doesnt take into account parents\\r\\nTransform.prototype.applyLocalTransformMatrix = (function() {\\r\\n\\tvar temp = vec3.create();\\r\\n\\tvar temp_mat3 = mat3.create();\\r\\n\\tvar temp_mat4 = mat4.create();\\r\\n\\tvar temp_quat = quat.create();\\r\\n\\r\\n\\treturn (function( M )\\r\\n\\t{\\r\\n\\t\\t//apply translation\\r\\n\\t\\tvec3.transformMat4( this._position, this._position, M );\\r\\n\\r\\n\\t\\t//apply scale\\r\\n\\t\\tmat4.rotateVec3( temp, M, [1,0,0] );\\r\\n\\t\\tthis._scaling[0] *= vec3.length( temp );\\r\\n\\t\\tmat4.rotateVec3( temp, M, [0,1,0] );\\r\\n\\t\\tthis._scaling[1] *= vec3.length( temp );\\r\\n\\t\\tmat4.rotateVec3( temp, M, [0,0,1] );\\r\\n\\t\\tthis._scaling[2] *= vec3.length( temp );\\r\\n\\r\\n\\t\\t//apply rotation\\r\\n\\t\\tvar m = mat4.invert( temp_mat4, M );\\r\\n\\t\\tif(!m)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tmat4.transpose(m, m);\\r\\n\\t\\tvar m3 = mat3.fromMat4( temp_mat3, m);\\r\\n\\t\\tvar q = quat.fromMat3( temp_quat, m3);\\r\\n\\t\\tquat.normalize(q, q);\\r\\n\\t\\tquat.multiply( this._rotation, q, this._rotation );\\r\\n\\r\\n\\t\\tthis._must_update = true; //matrix must be redone?\\r\\n\\t\\tthis._on_change();\\r\\n\\t});\\r\\n})();\\r\\n\\r\\n\\r\\n/*\\r\\nTransform.prototype.applyTransformMatrix = function(matrix, center, is_global)\\r\\n{\\r\\n\\tvar M = matrix;\\r\\n\\r\\n\\tif(center)\\r\\n\\t{\\r\\n\\t\\tvar T = mat4.setTranslation( mat4.create(), center);\\r\\n\\t\\tvar inv_center = vec3.scale( vec3.create(), center, -1 );\\r\\n\\t\\tvar iT = mat4.setTranslation( mat4.create(), inv_center);\\r\\n\\r\\n\\t\\tM = mat4.create();\\r\\n\\t\\tmat4.multiply( M, T, matrix );\\r\\n\\t\\tmat4.multiply( M, M, iT );\\r\\n\\t}\\r\\n\\r\\n\\tif(!this._parent)\\r\\n\\t{\\r\\n\\t\\tif(is_global)\\r\\n\\t\\t\\tmat4.multiply(this._local_matrix, M, this._local_matrix);\\r\\n\\t\\telse\\r\\n\\t\\t\\tmat4.multiply(this._local_matrix, this._local_matrix, M);\\r\\n\\t\\tthis.fromMatrix(this._local_matrix);\\r\\n\\t\\tmat4.copy(this._global_matrix, this._local_matrix); //no parent? then is the global too\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar GM = this.getGlobalMatrix();\\r\\n\\tvar PGM = this._parent._global_matrix;\\r\\n\\tvar temp = mat4.create();\\r\\n\\tmat4.multiply( this._global_matrix, M, GM );\\r\\n\\r\\n\\tmat4.invert(temp,PGM);\\r\\n\\tmat4.multiply(this._local_matrix, temp, this._global_matrix );\\r\\n\\tthis.fromMatrix(this._local_matrix);\\r\\n}\\r\\n*/\\r\\n\\r\\n//marks descendants to be updated\\r\\nTransform.prototype.updateDescendants = function()\\r\\n{\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn;\\r\\n\\tvar children = this._root._children;\\r\\n\\tif(!children)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < children.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar node = children[i];\\r\\n\\t\\tif(!node.transform) //bug: what if the children doesnt have a transform but the grandchilden does?! TODO FIX THIS\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tnode.transform._must_update = true;\\r\\n\\t\\tnode.transform._version += 1;\\r\\n\\t\\tif(node._children && node._children.length)\\r\\n\\t\\t\\tnode.transform.updateDescendants();\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\nLS.registerComponent( Transform );\\r\\nLS.Transform = Transform;\\r\\n\\r\\n///@FILE:../src/components/camera.js\\r\\n///@INFO: BASE\\r\\n// ******* CAMERA **************************\\r\\n\\r\\n/**\\r\\n* Camera contains the info about a camera (matrices, near far planes, clear color, etc)\\r\\n* @class Camera\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\n\\r\\nfunction Camera(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.layers = 3;\\r\\n\\r\\n\\tthis.clear_color = true;\\r\\n\\tthis.clear_depth = true;\\r\\n\\r\\n\\tthis._type = Camera.PERSPECTIVE;\\r\\n\\r\\n\\t//contain the eye, center, up if local space\\r\\n\\tthis._eye = vec3.clone( Camera.DEFAULT_EYE ); //TODO: change to position\\r\\n\\tthis._center = vec3.clone( Camera.DEFAULT_CENTER );\\t//TODO: change to target\\r\\n\\tthis._up = vec3.clone( Camera.DEFAULT_UP );\\r\\n\\r\\n\\t//in global coordinates\\r\\n\\tthis._global_eye = vec3.clone(this._eye);\\r\\n\\tthis._global_center = vec3.clone(this._center);\\r\\n\\tthis._global_up = vec3.clone(this._up);\\r\\n\\tthis._global_front = vec3.create();\\r\\n\\tvec3.sub( this._global_front, this._global_center, this._global_eye );\\r\\n\\tvec3.normalize( this._global_front, this._global_front );\\r\\n\\r\\n\\t//clipping planes\\r\\n\\tthis._near = 0.1;\\r\\n\\tthis._far = 1000;\\r\\n\\r\\n\\t//orthographics planes (near and far took from ._near and ._far)\\r\\n\\tthis._ortho = new Float32Array([-1,1,-1,1]);\\r\\n\\r\\n\\tthis._aspect = 1.0; //must be one, otherwise it gets deformed, the final one used is in final_aspect\\r\\n\\tthis._fov = 45; //persp\\r\\n\\tthis._frustum_size = 50; //ortho\\r\\n\\tthis._final_aspect = 1.0; //the one used when computing the projection matrix\\r\\n\\r\\n\\t//viewport in normalized coordinates: left, bottom, width, height\\r\\n\\tthis._viewport = new Float32Array([0,0,1,1]);\\r\\n\\tthis._viewport_in_pixels = vec4.create(); //viewport in screen coordinates\\r\\n\\tthis._last_viewport_in_pixels = vec4.create(); //updated when the camera is enabled from Renderer.enableCamera\\r\\n\\r\\n\\tthis._background_color = vec4.fromValues(0,0,0,1);\\r\\n\\r\\n\\t//in case we want to overwrite the view matrix manually\\r\\n\\tthis._use_custom_projection_matrix = false; \\r\\n\\r\\n\\t//in case we want to overwrite the shader of all visible objects\\r\\n\\tthis.overwrite_material = null;\\r\\n\\r\\n\\tthis._view_matrix = mat4.create();\\r\\n\\tthis._projection_matrix = mat4.create();\\r\\n\\tthis._viewprojection_matrix = mat4.create();\\r\\n\\tthis._model_matrix = mat4.create(); //inverse of viewmatrix (used for local vectors)\\r\\n\\tthis._previous_viewprojection_matrix = mat4.create(); //viewmatrix from previous frame, used in some algorithms\\r\\n\\r\\n\\t//lazy upload\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n\\tthis._must_update_projection_matrix = true;\\r\\n\\tthis._rendering_index = -1; //tells the number of this camera in the rendering process\\r\\n\\r\\n\\t//used for render to texture\\r\\n\\tthis._frame = null;\\r\\n\\tthis.show_frame = true;\\r\\n\\r\\n\\tif(o) \\r\\n\\t\\tthis.configure(o);\\r\\n\\t//this.updateMatrices(); //done by configure\\r\\n\\r\\n\\tthis._uniforms = {\\r\\n\\t\\tu_view: this._view_matrix,\\r\\n\\t\\tu_viewprojection: this._viewprojection_matrix,\\r\\n\\t\\tu_camera_eye: this._global_eye,\\r\\n\\t\\tu_camera_front: this._global_front,\\r\\n\\t\\tu_camera_planes: vec2.fromValues( this.near, this.far ),\\r\\n\\t\\tu_camera_perspective: vec3.create(),\\r\\n\\t\\tu_background_color: this._background_color,\\r\\n\\t\\tu_previous_viewprojection: this._previous_viewprojection_matrix\\r\\n\\t};\\r\\n\\r\\n\\tthis._frustum_planes = this.updateFrustumPlanes(); //to create\\r\\n\\tthis.updateMatrices();\\r\\n\\r\\n\\t//LEvent.bind(this,\\\"cameraEnabled\\\", this.onCameraEnabled.bind(this));\\r\\n}\\r\\n\\r\\nCamera.icon = \\\"mini-icon-camera.png\\\";\\r\\n\\r\\nCamera.PERSPECTIVE = 1;\\r\\nCamera.ORTHOGRAPHIC = 2; //orthographic adapted to aspect ratio of viewport\\r\\nCamera.ORTHO2D = 3; //orthographic with manually defined left,right,top,bottom\\r\\n\\r\\nCamera.DEFAULT_EYE = [0,0,0];\\r\\nCamera.DEFAULT_CENTER = [0,0,-100];\\r\\nCamera.DEFAULT_UP = [0,1,0];\\r\\n\\r\\nCamera[\\\"@type\\\"] = { type: \\\"enum\\\", values: { \\\"perspective\\\": Camera.PERSPECTIVE, \\\"orthographic\\\": Camera.ORTHOGRAPHIC, \\\"ortho2D\\\": Camera.ORTHO2D } };\\r\\nCamera[\\\"@eye\\\"] = { type: \\\"vec3\\\", widget: \\\"position\\\" };\\r\\nCamera[\\\"@center\\\"] = { type: \\\"vec3\\\", widget: \\\"position\\\" };\\r\\nCamera[\\\"@layers\\\"] = { type: \\\"layers\\\" };\\r\\n\\r\\n// used when rendering a cubemap to set the camera view direction (crossx and crossy are for when generating a CROSS cubemap image)\\r\\n\\r\\n//OLD VERSION, it doesnt make sense but is the one that works perfectly\\r\\nCamera.cubemap_camera_parameters = [\\r\\n\\t{ name: \\\"posx\\\", dir: vec3.fromValues(1,0,0), up: vec3.fromValues(0,-1,0), crossx:2, crossy:1 },\\r\\n\\t{ name: \\\"negx\\\", dir: vec3.fromValues(-1,0,0), up: vec3.fromValues(0,-1,0), crossx:0, crossy:1 },\\r\\n\\t{ name: \\\"posy\\\", dir: vec3.fromValues(0,1,0), up: vec3.fromValues(0,0,1), crossx:1, crossy:0 },\\r\\n\\t{ name: \\\"negy\\\", dir: vec3.fromValues(0,-1,0), up: vec3.fromValues(0,0,-1), crossx:1, crossy:2 },\\r\\n\\t{ name: \\\"posz\\\", dir: vec3.fromValues(0,0,1), up: vec3.fromValues(0,-1,0), crossx:1, crossy:1 },\\r\\n\\t{ name: \\\"negz\\\", dir: vec3.fromValues(0,0,-1), up: vec3.fromValues(0,-1,0), crossx:3, crossy:1 }\\r\\n];\\r\\n//*/\\r\\n/*\\r\\nCamera.cubemap_camera_parameters = [\\r\\n\\t{ name: \\\"posx\\\", dir: vec3.fromValues(-1,0,0), up: vec3.fromValues(0,1,0), right: vec3.fromValues(0,0,-1), crossx:0, crossy:1 },\\r\\n\\t{ name: \\\"negx\\\", dir: vec3.fromValues(1,0,0), up: vec3.fromValues(0,1,0), right: vec3.fromValues(0,0,1), crossx:2, crossy:1 },\\r\\n\\t{ name: \\\"posy\\\", dir: vec3.fromValues(0,-1,0), up: vec3.fromValues(0,0,-1), right: vec3.fromValues(1,0,0), crossx:1, crossy:2 },\\r\\n\\t{ name: \\\"negy\\\", dir: vec3.fromValues(0,1,0), up: vec3.fromValues(0,0,1), right: vec3.fromValues(-1,0,0), crossx:1, crossy:0 },\\r\\n\\t{ name: \\\"posz\\\", dir: vec3.fromValues(0,0,-1), up: vec3.fromValues(0,1,0), right: vec3.fromValues(1,0,0), crossx:1, crossy:1 },\\r\\n\\t{ name: \\\"negz\\\", dir: vec3.fromValues(0,0,1), up: vec3.fromValues(0,1,0), right: vec3.fromValues(-1,0,0), crossx:3, crossy:1 }\\r\\n];\\r\\n\\r\\n//*/\\r\\n\\r\\n/*\\r\\nTexture.cubemap_camera_parameters = [\\r\\n\\t{ type:\\\"posX\\\", dir: vec3.fromValues(-1,0,0), \\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(0,0,-1) },\\r\\n\\t{ type:\\\"negX\\\", dir: vec3.fromValues(1,0,0),\\t\\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(0,0,1) },\\r\\n\\t{ type:\\\"posY\\\", dir: vec3.fromValues(0,-1,0), \\tup: vec3.fromValues(0,0,-1), right: vec3.fromValues(1,0,0) },\\r\\n\\t{ type:\\\"negY\\\", dir: vec3.fromValues(0,1,0),\\t\\tup: vec3.fromValues(0,0,1),\\tright: vec3.fromValues(-1,0,0) },\\r\\n\\t{ type:\\\"posZ\\\", dir: vec3.fromValues(0,0,-1), \\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(1,0,0) },\\r\\n\\t{ type:\\\"negZ\\\", dir: vec3.fromValues(0,0,1),\\t\\tup: vec3.fromValues(0,1,0),\\tright: vec3.fromValues(-1,0,0) }\\r\\n];\\r\\n*/\\r\\n\\r\\nCamera.prototype.onResourceRenamed = function( old_name, new_name )\\r\\n{\\r\\n\\tif(old_name == this.overwrite_material)\\r\\n\\t\\tthis.overwrite_material = new_name;\\r\\n}\\r\\n\\r\\nCamera.prototype.getResources = function (res)\\r\\n{\\r\\n\\tif(this.overwrite_material && this.overwrite_material.constructor === String)\\r\\n\\t\\tres[ this.overwrite_material ] = true;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Camera type, could be Camera.PERSPECTIVE or Camera.ORTHOGRAPHIC\\r\\n* @property type {vec3}\\r\\n* @default Camera.PERSPECTIVE;\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"type\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._type;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(\\tthis._type != v)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t\\t\\tthis._must_update_projection_matrix = true;\\r\\n\\t\\t}\\r\\n\\t\\tthis._type = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The position of the camera (in local space, node space)\\r\\n* @property eye {vec3}\\r\\n* @default [0,100,100]\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"eye\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._eye;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._eye.set(v);\\r\\n\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The center where the camera points (in local space, node space)\\r\\n* @property center {vec3}\\r\\n* @default [0,0,0]\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"center\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._center;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._center.set(v);\\r\\n\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The distance between the center and the eye point\\r\\n* When focalLength is modified it will change the center so it matches the distance.\\r\\n* @property focalLength {Number}\\r\\n* @default (depends)\\r\\n*/\\r\\nvar tmp = vec3.create();\\r\\n\\r\\nObject.defineProperty( Camera.prototype, \\\"focalLength\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn vec3.distance( this._eye, this._center );\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tv = Math.max(0.001,v); //avoid 0\\r\\n\\t\\tvec3.sub( tmp, this._center, this._eye );\\r\\n\\t\\tvar length = vec3.length(tmp);\\r\\n\\t\\tif(length < 0.0001)\\r\\n\\t\\t\\ttmp.set([0,0,-1]);\\r\\n\\t\\telse\\r\\n\\t\\t\\tv /= length;\\r\\n\\t\\tvec3.scaleAndAdd( tmp, this._eye, tmp, v );\\r\\n\\t\\tthis._center.set( tmp );\\r\\n\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\n/**\\r\\n* The up vector of the camera (in local space, node space)\\r\\n* @property up {vec3}\\r\\n* @default [0,1,0]\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"up\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._up;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._up.set(v);\\r\\n\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The near plane\\r\\n* @property near {number}\\r\\n* @default 1\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"near\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._near;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(\\tthis._near != v)\\r\\n\\t\\t\\tthis._must_update_projection_matrix = true;\\r\\n\\t\\tthis._near = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The far plane\\r\\n* @property far {number}\\r\\n* @default 1000\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"far\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._far;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(\\tthis._far != v)\\r\\n\\t\\t\\tthis._must_update_projection_matrix = true;\\r\\n\\t\\tthis._far = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The camera aspect ratio\\r\\n* @property aspect {number}\\r\\n* @default 1\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"aspect\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._aspect;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(\\tthis._aspect != v)\\r\\n\\t\\t\\tthis._must_update_projection_matrix = true;\\r\\n\\t\\tthis._aspect = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n//this is set by the renderer, it is the final aspect that will be used (taking into account viewport size)\\r\\nObject.defineProperty( Camera.prototype, \\\"final_aspect\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._final_aspect;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(\\tthis._final_aspect != v)\\r\\n\\t\\t\\tthis._must_update_projection_matrix = true;\\r\\n\\t\\tthis._final_aspect = v;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n/**\\r\\n* The field of view in degrees\\r\\n* @property fov {number}\\r\\n* @default 45\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"fov\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._fov;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(\\tthis._fov != v)\\r\\n\\t\\t\\tthis._must_update_projection_matrix = true;\\r\\n\\t\\tthis._fov = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The frustum size when working in ORTHOGRAPHIC\\r\\n* @property frustum_size {number}\\r\\n* @default 50\\r\\n*/\\r\\n\\r\\nObject.defineProperty( Camera.prototype, \\\"frustum_size\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._frustum_size;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(\\tthis._frustum_size == v)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//this._must_update_view_matrix = true;\\r\\n\\t\\tthis._must_update_projection_matrix = true;\\r\\n\\t\\tthis._frustum_size = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The frustum size when working in pure ORTHOGRAPHIC \\r\\n* left,right,bottom,top (near and far are in the near,far properties)\\r\\n* @property orthographic {vec4} \\r\\n* @default 50\\r\\n*/\\r\\n\\r\\nObject.defineProperty( Camera.prototype, \\\"orthographic\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._ortho;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(\\t!v || v.length < 4)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._ortho.set(v);\\r\\n\\t\\tthis._must_update_projection_matrix = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The view matrix of the camera \\r\\n* @property view_matrix {vec4}\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"view_matrix\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._view_matrix;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis.fromViewMatrix(v);\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The projection matrix of the camera (cannot be set manually, use setCustomProjectionMatrix instead)\\r\\n* @property projection_matrix {mat4}\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"projection_matrix\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._projection_matrix;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"projection matrix cannot be set manually, use setCustomProjectionMatrix instead.\\\");\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\n/**\\r\\n* The viewport in normalized coordinates (left,bottom, width, height)\\r\\n* @property viewport {vec4}\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"viewport\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._viewport;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._viewport.set(v);\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* @property viewport_offset {vec2}\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"viewport_offset\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._viewport.subarray(0,2);\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._viewport.set(v);\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* @property viewport_size {vec2}\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"viewport_size\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._viewport.subarray(2,4);\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._viewport.set(v,2);\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* the clear color\\r\\n* @property background_color {vec4}\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"background_color\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._background_color;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._background_color.set(v);\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* the clear alpha value\\r\\n* @property background_alpha {Number}\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"background_alpha\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._background_color[3];\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._background_color[3] = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* returns the texture from the render frame context\\r\\n* @property render_to_texture {GL.Texture} \\r\\n*/\\r\\n\\r\\nObject.defineProperty( Camera.prototype, \\\"render_to_texture\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn !!this._frame;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(!v)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._frame = null;\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tif(!this._frame)\\r\\n\\t\\t\\tthis._frame = new LS.RenderFrameContext();\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* contains the RenderFrameContext where the scene was stored\\r\\n* @property frame {LS.RenderFrameContext} \\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"frame\\\", {\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"frame cannot be assigned manually, enable render_to_texture\\\");\\r\\n\\t},\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._frame;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n/**\\r\\n* contains the color texture used by the RenderFrameContext\\r\\n* @property frame_color_texture {GL.Texture} \\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"frame_color_texture\\\", {\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"frame_color_texture cannot be assigned manually, enable render_to_texture\\\");\\r\\n\\t},\\r\\n\\tget: function(v) {\\r\\n\\t\\tif(!this._frame)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn this._frame.getColorTexture();\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n/**\\r\\n* contains the depth texture used by the RenderFrameContext\\r\\n* @property frame_depth_texture {GL.Texture} \\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"frame_depth_texture\\\", {\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"frame_depth_texture cannot be assigned manually, enable render_to_texture\\\");\\r\\n\\t},\\r\\n\\tget: function() {\\r\\n\\t\\tif(!this._frame)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn this._frame.getDepthTexture();\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n\\r\\n/**\\r\\n* to force updating projection and view matrix\\r\\n* @property mustUpdate {Boolean}\\r\\n*/\\r\\nObject.defineProperty( Camera.prototype, \\\"mustUpdate\\\", {\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._must_update_projection_matrix || this._must_update_view_matrix;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthis._must_update_projection_matrix = this._must_update_view_matrix = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\nCamera.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tif(!node.camera)\\r\\n\\t\\tnode.camera = this;\\r\\n\\tLEvent.bind( node, \\\"transformChanged\\\", this.onNodeMoved, this );\\r\\n}\\r\\n\\r\\nCamera.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tif(node.camera == this)\\r\\n\\t\\tdelete node.camera;\\r\\n\\tLEvent.unbind( node, \\\"transformChanged\\\", this.onNodeMoved, this );\\r\\n}\\r\\n\\r\\nCamera.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"collectCameras\\\", this.onCollectCameras, this ); //here because we store them in node\\r\\n}\\r\\n\\r\\nCamera.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind( scene, \\\"collectCameras\\\", this.onCollectCameras, this );\\r\\n\\r\\n\\tif(this._frame) //free memory\\r\\n\\t\\tthis._frame.clear();\\r\\n\\r\\n\\tif( this._binded_render_frame )\\r\\n\\t{\\r\\n\\t\\tLEvent.unbind(this, \\\"enableFrameContext\\\", this.enableRenderFrameContext, this );\\r\\n\\t\\tLEvent.unbind(this, \\\"showFrameContext\\\", this.disableRenderFrameContext, this );\\r\\n\\t\\tthis._binded_render_frame = false;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nCamera.prototype.onNodeMoved = function()\\r\\n{\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n}\\r\\n\\r\\nCamera.prototype.isRenderedToTexture = function()\\r\\n{\\r\\n\\treturn this.enabled && this.render_to_texture;\\r\\n}\\r\\n\\r\\nCamera.prototype.onCollectCameras = function(e, cameras)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this.isRenderedToTexture())\\r\\n\\t\\tcameras.push(this);\\r\\n\\telse\\r\\n\\t\\tcameras.unshift(this); //put at the begining\\r\\n\\r\\n\\t//in case we need to render to a texture this camera\\r\\n\\t//not very fond of this part, but its more optimal\\r\\n\\tif(this._frame)\\r\\n\\t{\\r\\n\\t\\tif(!this._binded_render_frame)\\r\\n\\t\\t{\\r\\n\\t\\t\\tLEvent.bind(this, \\\"enableFrameContext\\\", this.enableRenderFrameContext, this );\\r\\n\\t\\t\\tLEvent.bind(this, \\\"showFrameContext\\\", this.disableRenderFrameContext, this );\\r\\n\\t\\t\\tthis._binded_render_frame = true;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if( this._binded_render_frame )\\r\\n\\t{\\r\\n\\t\\tLEvent.unbind(this, \\\"enableFrameContext\\\", this.enableRenderFrameContext, this );\\r\\n\\t\\tLEvent.unbind(this, \\\"showFrameContext\\\", this.disableRenderFrameContext, this );\\r\\n\\t\\tthis._binded_render_frame = false;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Positions the camera at eye, pointing at center, and facing up as vertical.\\r\\n* If the camera is a node camera, then the node transform is modified (plus the center to match the focalLength)\\r\\n* @method lookAt\\r\\n* @param {vec3} eye\\r\\n* @param {vec3} center\\r\\n* @param {vec3} up\\r\\n*/\\r\\nCamera.prototype.lookAt = function( eye, center, up )\\r\\n{\\r\\n\\tif( this._root && this._root.transform )\\r\\n\\t{\\r\\n\\t\\tthis._root.transform.lookAt(eye,center,up);\\r\\n\\t\\tthis._eye.set(LS.ZEROS);\\r\\n\\t\\tthis._up.set([0,1,0]);\\r\\n\\t\\tthis.focalLength = vec3.distance( eye, center ); //changes the center\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tvec3.copy(this._eye, eye);\\r\\n\\t\\tvec3.copy(this._center, center);\\r\\n\\t\\tvec3.copy(this._up,up);\\r\\n\\t}\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Positions the camera using a matrix that contains the position an orientation (NOT FULLY TESTED)\\r\\n* If the camera is a node camera, then the node transform is modified (plus the center to match the focalLength)\\r\\n* @method lookAtFromMatrix\\r\\n* @param {mat4} matrix\\r\\n* @param {boolean} is_model if false the matrix is assumed to be a view matrix, otherwise a model (inverse of view)\\r\\n*/\\r\\nCamera.prototype.lookAtFromMatrix = function( matrix, is_model )\\r\\n{\\r\\n\\tif( this._root && this._root.transform )\\r\\n\\t{\\r\\n\\t\\tif(!is_model) //convert view to model\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar m = mat4.create();\\r\\n\\t\\t\\tmatrix = mat4.invert(m, matrix);\\r\\n\\t\\t}\\r\\n\\t\\tthis._root.transform.matrix = matrix;\\r\\n\\t\\tthis._eye.set(LS.ZEROS);\\r\\n\\t\\tthis._up.set([0,1,0]);\\r\\n\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t\\tthis.focalLength = 1; //changes center\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tvar inv = mat4.create();\\r\\n\\t\\tmat4.invert( inv, matrix );\\r\\n\\t\\tvar view = is_model ? inv : matrix;\\r\\n\\t\\tvar model = is_model ? matrix : inv;\\r\\n\\r\\n\\t\\tthis._view_matrix.set( view );\\r\\n\\t\\tvec3.transformMat4( this._eye, LS.ZEROS, model );\\r\\n\\t\\tvec3.transformMat4( this._center, LS.FRONT, model );\\r\\n\\t\\tmat4.rotateVec3( this._up, model, LS.TOP );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* resets eye, center and up, so they are in [0,0,0],[0,0,-focalDist] and [0,1,0]\\r\\n* @method resetVectors\\r\\n* @param {number} focalDist [optional] it not set it will be 1\\r\\n*/\\r\\nCamera.prototype.resetVectors = function(focalDist)\\r\\n{\\r\\n\\tfocalDist = focalDist || 1;\\r\\n\\tthis._eye.set([0,0,0]);\\r\\n\\tthis._center.set([0,0,-focalDist]);\\r\\n\\tthis._up.set([0,1,0]);\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Update matrices according to the eye,center,up,fov,aspect,...\\r\\n* @method updateMatrices\\r\\n*/\\r\\nCamera.prototype.updateMatrices = function( force )\\r\\n{\\r\\n\\t//if is a camera in a node we cannot assure the node hasnt change its transform (TODO feature)\\r\\n\\tthis._must_update_view_matrix = this._must_update_view_matrix || (this._root && !this._root._is_root);\\r\\n\\r\\n\\t//nothing to update?\\r\\n\\tif(!this._must_update_projection_matrix && !this._must_update_view_matrix && !force)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//update projection\\r\\n\\tif( (this._must_update_projection_matrix || force) && !this._use_custom_projection_matrix )\\r\\n\\t{\\r\\n\\t\\tif(this.type == Camera.ORTHOGRAPHIC)\\r\\n\\t\\t\\tmat4.ortho(this._projection_matrix, -this._frustum_size*this._final_aspect*0.5, this._frustum_size*this._final_aspect*0.5, -this._frustum_size*0.5, this._frustum_size*0.5, this._near, this._far);\\r\\n\\t\\telse if (this.type == Camera.ORTHO2D)\\r\\n\\t\\t\\tmat4.ortho(this._projection_matrix, this._ortho[0], this._ortho[1], this._ortho[2], this._ortho[3], this._near, this._far);\\r\\n\\t\\telse\\r\\n\\t\\t\\tmat4.perspective(this._projection_matrix, this._fov * DEG2RAD, this._final_aspect, this._near, this._far);\\r\\n\\t}\\r\\n\\r\\n\\t//update view (if is a camera in a node we cannot assure it hasnt change its transform)\\r\\n\\tif( this._must_update_view_matrix || force )\\r\\n\\t{\\r\\n\\t\\tif(this._root && this._root._is_root) //in root node\\r\\n\\t\\t\\tmat4.lookAt( this._view_matrix, this._eye, this._center, this._up );\\r\\n\\t\\telse\\r\\n\\t\\t\\tmat4.lookAt( this._view_matrix, this.getEye(this._global_eye), this.getCenter(this._global_center), this.getUp(this._global_up) );\\r\\n\\t\\tmat4.invert(this._model_matrix, this._view_matrix );\\r\\n\\t}\\r\\n\\r\\n\\tmat4.multiply(this._viewprojection_matrix, this._projection_matrix, this._view_matrix );\\r\\n\\tthis.updateFrustumPlanes();\\r\\n\\r\\n\\tthis._must_update_view_matrix = false;\\r\\n\\tthis._must_update_projection_matrix = false;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Update the frustum planes according to viewprojection_matrix, used for visibility testing\\r\\n* @method updateFrustumPlanes\\r\\n* @return {Float32Array} planes\\r\\n*/\\r\\nCamera.prototype.updateFrustumPlanes = function()\\r\\n{\\r\\n\\tif(!this._frustum_planes)\\r\\n\\t\\tthis._frustum_planes = new Float32Array(24);\\r\\n\\tgeo.extractPlanes( this._viewprojection_matrix, this._frustum_planes );\\r\\n\\treturn this._frustum_planes;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the inverse of the viewmatrix\\r\\n* @method getModelMatrix\\r\\n* @param {mat4} m optional output container\\r\\n* @return {mat4} matrix\\r\\n*/\\r\\nCamera.prototype.getModelMatrix = function(m)\\r\\n{\\r\\n\\tm = m || mat4.create();\\r\\n\\tif(this._must_update_view_matrix)\\r\\n\\t\\tthis.updateMatrices();\\r\\n\\treturn mat4.copy( m, this._model_matrix );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the viewmatrix\\r\\n* @method getViewMatrix\\r\\n* @param {mat4} m optional output container\\r\\n* @return {mat4} matrix\\r\\n*/\\r\\nCamera.prototype.getViewMatrix = function(m)\\r\\n{\\r\\n\\tm = m || mat4.create();\\r\\n\\tif(this._must_update_view_matrix)\\r\\n\\t\\tthis.updateMatrices();\\r\\n\\treturn mat4.copy( m, this._view_matrix );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the projection matrix\\r\\n* @method getProjectionMatrix\\r\\n* @param {mat4} m optional output container\\r\\n* @return {mat4} matrix\\r\\n*/\\r\\nCamera.prototype.getProjectionMatrix = function(m)\\r\\n{\\r\\n\\tm = m || mat4.create();\\r\\n\\tif(this._must_update_projection_matrix)\\r\\n\\t\\tthis.updateMatrices();\\r\\n\\treturn mat4.copy( m, this._projection_matrix );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the view projection matrix\\r\\n* @method getViewProjectionMatrix\\r\\n* @param {mat4} m optional output container\\r\\n* @param {boolean} force optional force to update\\r\\n* @return {mat4} matrix\\r\\n*/\\r\\nCamera.prototype.getViewProjectionMatrix = function(m, force)\\r\\n{\\r\\n\\tm = m || mat4.create();\\r\\n\\tif(this._must_update_view_matrix || this._must_update_projection_matrix || force )\\r\\n\\t\\tthis.updateMatrices();\\r\\n\\treturn mat4.copy( m, this._viewprojection_matrix );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the model view projection matrix computed from a passed model\\r\\n* @method getModelViewProjectionMatrix\\r\\n* @param {mat4} model model matrix\\r\\n* @param {mat4} out optional output container\\r\\n* @return {mat4} matrix\\r\\n*/\\r\\nCamera.prototype.getModelViewProjectionMatrix = function(model, out)\\r\\n{\\r\\n\\tout = out || mat4.create();\\r\\n\\tif(this._must_update_view_matrix || this._must_update_projection_matrix)\\r\\n\\t\\tthis.updateMatrices();\\r\\n\\treturn mat4.multiply( out, this._viewprojection_matrix, model );\\r\\n}\\r\\n\\r\\n/**\\r\\n* apply a transform to all the vectors (eye,center,up) using a matrix\\r\\n* @method updateVectors\\r\\n* @param {mat4} model matrix\\r\\n*/\\r\\nCamera.prototype.updateVectors = function(model)\\r\\n{\\r\\n\\tvar front = vec3.subtract(vec3.create(), this._center, this._eye);\\r\\n\\tvar dist = vec3.length(front);\\r\\n\\tthis._eye = mat4.multiplyVec3(vec3.create(), model, vec3.create() );\\r\\n\\tthis._center = mat4.multiplyVec3(vec3.create(), model, vec3.fromValues(0,0,-dist));\\r\\n\\tthis._up = mat4.rotateVec3(vec3.create(), model, vec3.fromValues(0,1,0));\\r\\n\\tthis.updateMatrices();\\r\\n}\\r\\n\\r\\n/**\\r\\n* transform a local coordinate to global coordinates\\r\\n* @method getLocalPoint\\r\\n* @param {vec3} v vector\\r\\n* @param {vec3} dest where to store the output, if not provided a vec3 is created\\r\\n* @return {vec3} v in global coordinates\\r\\n*/\\r\\nCamera.prototype.getLocalPoint = function( v, dest )\\r\\n{\\r\\n\\tdest = dest || vec3.create();\\r\\n\\r\\n\\tif( this._root && this._root.transform )\\r\\n\\t\\treturn mat4.multiplyVec3( dest, this._root.transform.getGlobalMatrixRef(), v );\\r\\n\\r\\n\\tif(this._must_update_view_matrix)\\r\\n\\t\\tthis.updateMatrices();\\r\\n\\r\\n\\treturn mat4.multiplyVec3( dest, this._model_matrix, v );\\r\\n}\\r\\n\\r\\n/**\\r\\n* rotate a local coordinate to global coordinates (skipping translation)\\r\\n* @method getLocalVector\\r\\n* @param {vec3} v vector\\r\\n* @param {vec3} dest where to store the output, if not provided a vec3 is created\\r\\n* @return {vec3} v in global coordinates\\r\\n*/\\r\\n\\r\\nCamera.prototype.getLocalVector = function(v, dest)\\r\\n{\\r\\n\\tdest = dest || vec3.create();\\r\\n\\r\\n\\tif( this._root && this._root.transform )\\r\\n\\t\\treturn mat4.rotateVec3( dest, this._root.transform.getGlobalMatrixRef(), v );\\r\\n\\r\\n\\tif(this._must_update_view_matrix)\\r\\n\\t\\tthis.updateMatrices();\\r\\n\\r\\n\\treturn mat4.rotateVec3( dest, this._model_matrix, v );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the eye (position of the camera) in global coordinates\\r\\n* Takes into account if it is a camera attached to a node\\r\\n* The result of this function wont match the _eye property if the camera is a node camera\\r\\n* @method getEye\\r\\n* @param {vec3} out output vector [optional]\\r\\n* @return {vec3} position in global coordinates\\r\\n*/\\r\\nCamera.prototype.getEye = function( out )\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tout[0] = this._eye[0];\\r\\n\\tout[1] = this._eye[1];\\r\\n\\tout[2] = this._eye[2];\\r\\n\\tif( this._root && this._root.transform )\\r\\n\\t\\treturn this._root.transform.getGlobalPosition( out );\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* returns the center of the camera (position where the camera is pointing) in global coordinates\\r\\n* @method getCenter\\r\\n* @param {vec3} out output vector [optional]\\r\\n* @return {vec3} position in global coordinates\\r\\n*/\\r\\nCamera.prototype.getCenter = function( out )\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\r\\n\\tif( this._root && this._root.transform )\\r\\n\\t\\treturn mat4.multiplyVec3( out, this._root.transform.getGlobalMatrixRef(), this._center );\\r\\n\\tout[0] = this._center[0];\\r\\n\\tout[1] = this._center[1];\\r\\n\\tout[2] = this._center[2];\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the front vector of the camera\\r\\n* @method getFront\\r\\n* @param {vec3} out output vector [optional]\\r\\n* @return {vec3} position in global coordinates\\r\\n*/\\r\\nCamera.prototype.getFront = function( out )\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\r\\n\\tif(this._root && this._root.transform)\\r\\n\\t{\\r\\n\\t\\tout[0] = out[1] = 0; out[2] = -1;\\r\\n\\t\\treturn mat4.rotateVec3(out, this._root.transform.getGlobalMatrixRef(), out );\\r\\n\\t}\\r\\n\\r\\n\\tvec3.sub( out, this._center, this._eye ); \\r\\n\\treturn vec3.normalize(out, out);\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the up vector of the camera\\r\\n* @method getUp\\r\\n* @param {vec3} out output vector [optional]\\r\\n* @return {vec3} position in global coordinates\\r\\n*/\\r\\nCamera.prototype.getUp = function( out )\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tout[0] = this._up[0];\\r\\n\\tout[1] = this._up[1];\\r\\n\\tout[2] = this._up[2];\\r\\n\\r\\n\\tif(this._root && this._root.transform)\\r\\n\\t{\\r\\n\\t\\treturn mat4.rotateVec3( out, this._root.transform.getGlobalMatrixRef(), out );\\r\\n\\t}\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the top vector of the camera (different from up, this one is perpendicular to front and right)\\r\\n* @method getTop\\r\\n* @param {vec3} out output vector [optional]\\r\\n* @return {vec3} position in global coordinates\\r\\n*/\\r\\nCamera.prototype.getTop = function( out )\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tvar front = vec3.sub( vec3.create(), this._center, this._eye ); \\r\\n\\tvar right = vec3.cross( vec3.create(), this._up, front );\\r\\n\\tvar top = vec3.cross( out, front, right );\\r\\n\\tvec3.normalize(top,top);\\r\\n\\tif(this._root && this._root.transform && this._root._parent)\\r\\n\\t\\treturn mat4.rotateVec3( top, this._root.transform.getGlobalMatrixRef(), top );\\r\\n\\treturn top;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the right vector of the camera \\r\\n* @method getRight\\r\\n* @param {vec3} out output vector [optional]\\r\\n* @return {vec3} position in global coordinates\\r\\n*/\\r\\nCamera.prototype.getRight = function( out )\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tvar front = vec3.sub( vec3.create(), this._center, this._eye ); \\r\\n\\tvar right = vec3.cross( out, this._up, front );\\r\\n\\tvec3.normalize(right,right);\\r\\n\\tif(this._root && this._root.transform && this._root._parent)\\r\\n\\t\\treturn mat4.rotateVec3( right, this._root.transform.getGlobalMatrixRef(), right );\\r\\n\\treturn right;\\r\\n}\\r\\n\\r\\n//DEPRECATED: use property eye instead\\r\\n\\r\\nCamera.prototype.setEye = function(v)\\r\\n{\\r\\n\\tthis._eye.set( v );\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n}\\r\\n\\r\\nCamera.prototype.setCenter = function(v)\\r\\n{\\r\\n\\tthis._center.set( v );\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* set camera in perspective mode and sets the properties\\r\\n* @method setPerspective\\r\\n* @param {number} fov in degrees\\r\\n* @param {number} aspect the aspect modifier (not the real final aspect, leave it to one)\\r\\n* @param {number} near distance\\r\\n* @param {number} far distance\\r\\n*/\\r\\nCamera.prototype.setPerspective = function( fov, aspect, near, far )\\r\\n{\\r\\n\\tthis._fov = fov;\\r\\n\\tthis._aspect = aspect;\\r\\n\\tthis._near = near;\\r\\n\\tthis._far = far;\\r\\n\\tthis._type = Camera.PERSPECTIVE;\\r\\n\\tthis._must_update_projection_matrix = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* set camera in orthographic mode and sets the planes\\r\\n* @method setOrthographic\\r\\n* @param {number} left\\r\\n* @param {number} right\\r\\n* @param {number} bottom\\r\\n* @param {number} top\\r\\n* @param {number} near\\r\\n* @param {number} far\\r\\n*/\\r\\nCamera.prototype.setOrthographic = function( left, right, bottom,top, near, far )\\r\\n{\\r\\n\\tthis._near = near;\\r\\n\\tthis._far = far;\\r\\n\\tthis._ortho.set([left,right,bottom,top]);\\r\\n\\tthis._type = Camera.ORTHO2D;\\r\\n\\tthis._must_update_projection_matrix = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* moves the camera by adding the delta vector to center and eye\\r\\n* @method move\\r\\n* @param {vec3} delta\\r\\n*/\\r\\nCamera.prototype.move = function(v)\\r\\n{\\r\\n\\tif(this._root && this._root.transform)\\r\\n\\t{\\r\\n\\t\\tthis._root.transform.move(v);\\r\\n\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvec3.add(this._center, this._center, v);\\r\\n\\tvec3.add(this._eye, this._eye, v);\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* rotate the camera around its center\\r\\n* @method rotate\\r\\n* @param {number} angle_in_deg\\r\\n* @param {vec3} axis\\r\\n* @param {boolean} in_local_space allows to specify if the axis is in local space or global space\\r\\n*/\\r\\nCamera.prototype.rotate = (function() { \\r\\n\\tvar tmp_quat = quat.create();\\r\\n\\tvar tmp_vec3 = vec3.create();\\r\\n\\t\\r\\n\\treturn function( angle_in_deg, axis, in_local_space )\\r\\n\\t{\\r\\n\\t\\tif(angle_in_deg == 0)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(this._root && this._root.transform)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._root.transform.rotate( angle_in_deg, axis, !in_local_space );\\r\\n\\t\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( in_local_space )\\r\\n\\t\\t\\tthis.getLocalVector( axis, tmp_vec3 );\\r\\n\\t\\telse\\r\\n\\t\\t\\ttmp_vec3.set( axis );\\r\\n\\r\\n\\t\\tvar R = quat.setAxisAngle( tmp_quat, tmp_vec3, angle_in_deg * 0.0174532925 );\\r\\n\\t\\tvar front = vec3.subtract( tmp_vec3, this._center, this._eye );\\r\\n\\r\\n\\t\\tvec3.transformQuat( front, front, R );\\r\\n\\t\\tvec3.add(this._center, this._eye, front);\\r\\n\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t};\\r\\n})();\\r\\n\\r\\n/**\\r\\n* Rotates the camera eye around a center\\r\\n* @method orbit\\r\\n* @param {number} angle_in_deg\\r\\n* @param {vec3} axis\\r\\n* @param {vec3} center optional\\r\\n*/\\r\\nCamera.prototype.orbit = (function() { \\r\\n\\tvar tmp_quat = quat.create();\\r\\n\\tvar tmp_vec3 = vec3.create();\\r\\n\\r\\n\\treturn function( angle_in_deg, axis, center )\\r\\n\\t{\\r\\n\\t\\tangle_in_deg = angle_in_deg || 0;\\r\\n\\r\\n\\t\\tif(angle_in_deg == 0)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif(!axis)\\r\\n\\t\\t\\tthrow(\\\"axis missing\\\");\\r\\n\\r\\n\\t\\tif(this._root && this._root.transform)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._root.transform.orbit( angle_in_deg, axis, center || this.getCenter() );\\r\\n\\t\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tcenter = center || this._center;\\r\\n\\t\\tvar R = quat.setAxisAngle( tmp_quat, axis, angle_in_deg * 0.0174532925 );\\r\\n\\t\\tvar front = vec3.subtract( tmp_vec3, this._eye, center );\\r\\n\\t\\tvec3.transformQuat( front, front, R );\\r\\n\\t\\tvec3.add( this._eye, center, front );\\r\\n\\t\\tthis._must_update_view_matrix = true;\\r\\n\\t};\\r\\n})();\\r\\n\\r\\n//this is too similar to setDistanceToCenter, must be removed\\r\\nCamera.prototype.orbitDistanceFactor = function(f, center)\\r\\n{\\r\\n\\tcenter = center || this._center;\\r\\n\\tvar front = vec3.subtract( vec3.create(), this._eye, center );\\r\\n\\tvec3.scale(front, front, f);\\r\\n\\tvec3.add(this._eye, center, front);\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Pans the camera (move acording to view)\\r\\n* @method panning\\r\\n* @param {number} x\\r\\n* @param {number} y\\r\\n*/\\r\\nCamera.prototype.panning = (function(x,y) { \\r\\n\\tvar tmp_top = vec3.create();\\r\\n\\tvar tmp_right = vec3.create();\\r\\n\\tvar tmp = vec3.create();\\r\\n\\r\\n\\treturn function( x,y, factor )\\r\\n\\t{\\r\\n\\t\\tfactor = factor || 1;\\r\\n\\t\\tthis.getLocalVector( LS.TOP, tmp_top );\\r\\n\\t\\tthis.getLocalVector( LS.RIGHT, tmp_right );\\r\\n\\t\\tvec3.scaleAndAdd( tmp, LS.ZEROS, tmp_top, y * factor );\\r\\n\\t\\tvec3.scaleAndAdd( tmp, tmp, tmp_right, x * factor );\\r\\n\\t\\tthis.move( tmp );\\r\\n\\t};\\r\\n})();\\r\\n\\r\\n\\r\\n/**\\r\\n* changes the distance between eye and center ( it could move the center or the eye, depending on the parameters )\\r\\n* @method setDistanceToCenter\\r\\n* @param {number} new_distance\\r\\n* @param {boolean} move_eye if this is true it moves the eye closer, otherwise it moves the center closer to the eye\\r\\n*/\\r\\nCamera.prototype.setDistanceToCenter = function( new_distance, move_eye )\\r\\n{\\r\\n\\tif(this._root)\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"cannot use setDistanceToCenter in a camera attached to a node\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar front = vec3.sub( vec3.create(), this._center, this._eye );\\r\\n\\tvar dist = vec3.length( front );\\r\\n\\tif(move_eye)\\r\\n\\t\\tvec3.scaleAndAdd( this._eye, this._center, front, -new_distance / dist );\\r\\n\\telse\\r\\n\\t\\tvec3.scaleAndAdd( this._center, this._eye, front, new_distance / dist );\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* orients the camera (changes where is facing) according to the rotation supplied\\r\\n* @method setOrientation\\r\\n* @param {quat} q\\r\\n*/\\r\\nCamera.prototype.setOrientation = function(q, use_vr)\\r\\n{\\r\\n\\tvar center = this.getCenter();\\r\\n\\tvar eye = this.getEye();\\r\\n\\tvar up = [0,1,0];\\r\\n\\r\\n\\tvar to_target = vec3.sub( vec3.create(), center, eye );\\r\\n\\tvar dist = vec3.length( to_target );\\r\\n\\r\\n\\tvar front = null;\\r\\n\\tfront = vec3.fromValues(0,0,-dist);\\r\\n\\r\\n\\tif(use_vr)\\r\\n\\t{\\r\\n\\t\\tvec3.rotateY( front, front, Math.PI * -0.5 );\\r\\n\\t\\tvec3.rotateY( up, up, Math.PI * -0.5 );\\r\\n\\t}\\r\\n\\r\\n\\tvec3.transformQuat(front, front, q);\\r\\n\\tvec3.transformQuat(up, up, q);\\r\\n\\r\\n\\tif(use_vr)\\r\\n\\t{\\r\\n\\t\\tvec3.rotateY( front, front, Math.PI * 0.5 );\\r\\n\\t\\tvec3.rotateY( up, up, Math.PI * 0.5 );\\r\\n\\t}\\r\\n\\r\\n\\tthis.center = vec3.add( vec3.create(), eye, front );\\r\\n\\tthis.up = up;\\r\\n\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* orients the camera (changes where is facing) using euler angles (yaw,pitch,roll)\\r\\n* @method setEulerAngles\\r\\n* @param {Number} yaw\\r\\n* @param {Number} pitch\\r\\n* @param {Number} roll\\r\\n*/\\r\\nCamera.prototype.setEulerAngles = function(yaw,pitch,roll)\\r\\n{\\r\\n\\tvar q = quat.create();\\r\\n\\tquat.fromEuler(q, [yaw, pitch, roll] );\\r\\n\\tthis.setOrientation(q);\\r\\n}\\r\\n\\r\\n/**\\r\\n* uses a view matrix to compute the eye,center,up vectors\\r\\n* @method fromViewMatrix\\r\\n* @param {mat4} mat the given view matrix\\r\\n*/\\r\\nCamera.prototype.fromViewMatrix = function(mat)\\r\\n{\\r\\n\\tif( this._root && this._root.transform )\\r\\n\\t{\\r\\n\\t\\tvar model = mat4.invert( mat4.create(), mat );\\r\\n\\t\\tthis._root.transform.fromMatrix( model, true );\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar M = mat4.invert( mat4.create(), mat );\\r\\n\\tthis.eye = vec3.transformMat4( vec3.create(), LS.ZEROS, M );\\r\\n\\tthis.center = vec3.transformMat4( vec3.create(), LS.FRONT, M );\\r\\n\\tthis.up = mat4.rotateVec3( vec3.create(), M, LS.TOP );\\r\\n\\tthis._must_update_view_matrix = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* overwrites the current projection matrix with a given one (it also blocks the camera from modifying the projection matrix)\\r\\n* @method setCustomProjectionMatrix\\r\\n* @param {mat4} mat the given projection matrix (or null to disable it)\\r\\n*/\\r\\nCamera.prototype.setCustomProjectionMatrix = function( mat )\\r\\n{\\r\\n\\tif(!mat)\\r\\n\\t{\\r\\n\\t\\tthis._use_custom_projection_matrix = false;\\r\\n\\t\\tthis._must_update_projection_matrix = true;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tthis._use_custom_projection_matrix = true;\\r\\n\\t\\tthis._projection_matrix.set( mat );\\r\\n\\t\\tthis._must_update_projection_matrix = false;\\r\\n\\t\\tmat4.multiply( this._viewprojection_matrix, this._projection_matrix, this._view_matrix );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Sets the viewport in pixels (using the gl.canvas as reference)\\r\\n* @method setViewportInPixels\\r\\n* @param {number} left\\r\\n* @param {number} right\\r\\n* @param {number} width\\r\\n* @param {number} height\\r\\n*/\\r\\nCamera.prototype.setViewportInPixels = function(left,bottom,width,height)\\r\\n{\\r\\n\\tthis._viewport[0] = left / gl.canvas.width;\\r\\n\\tthis._viewport[1] = bottom / gl.canvas.height;\\r\\n\\tthis._viewport[2] = width / gl.canvas.width;\\r\\n\\tthis._viewport[3] = height / gl.canvas.height;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Converts a 3D point to its 2D position in canvas space\\r\\n* @method project\\r\\n* @param {vec3} vec 3D position we want to proyect to 2D\\r\\n* @param {vec4} [viewport=null] viewport info (if omited full canvas viewport is used)\\r\\n* @param {vec3} result where to store the result, if omited it is created\\r\\n* @return {vec3} the coordinates in 2D\\r\\n*/\\r\\n\\r\\nCamera.prototype.project = function( vec, viewport, result, skip_reverse )\\r\\n{\\r\\n\\tresult = result || vec3.create();\\r\\n\\r\\n\\tif(!vec)\\r\\n\\t\\tthrow(\\\"camera project parameter 'vec' cannot be null\\\");\\r\\n\\r\\n\\tviewport = this.getLocalViewport(viewport);\\r\\n\\r\\n\\tif( this._must_update_view_matrix || this._must_update_projection_matrix )\\r\\n\\t\\tthis.updateMatrices();\\r\\n\\r\\n\\t//from https://github.com/hughsk/from-3d-to-2d/blob/master/index.js\\r\\n\\tvar m = this._viewprojection_matrix;\\r\\n\\r\\n\\tvec3.project( result, vec, this._viewprojection_matrix, viewport );\\r\\n\\tif(!skip_reverse)\\r\\n\\t\\tresult[1] = viewport[3] - result[1] + viewport[1]*2; //why 2? no idea, but it works :(\\r\\n\\treturn result;\\r\\n}\\r\\n\\r\\n/**\\r\\n* It tells you the 2D position of a node center in the screen\\r\\n* @method projectNodeCenter\\r\\n* @param {vec3} vec 3D position we want to proyect to 2D\\r\\n* @param {vec4} [viewport=null] viewport info (if omited full canvas viewport is used)\\r\\n* @param {vec3} result where to store the result, if omited it is created\\r\\n* @return {vec3} the coordinates in 2D\\r\\n*/\\r\\nCamera.prototype.projectNodeCenter = function( node, viewport, result, skip_reverse )\\r\\n{\\r\\n\\tvar center = node.transform ? node.transform.getGlobalPosition() : LS.ZEROS;\\r\\n\\treturn this.project( center, viewport, result, skip_reverse );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Converts a screen space 2D vector (with a Z value) to its 3D equivalent position\\r\\n* @method unproject\\r\\n* @param {vec3} vec 2D position we want to proyect to 3D\\r\\n* @param {vec4} [viewport=null] viewport info (if omited full canvas viewport is used)\\r\\n* @param {vec3} result where to store the result, if omited it is created\\r\\n* @return {vec3} the coordinates in 2D\\r\\n*/\\r\\nCamera.prototype.unproject = function( vec, viewport, result )\\r\\n{\\r\\n\\tviewport = this.getLocalViewport(viewport);\\r\\n\\tif( this._must_update_view_matrix || this._must_update_projection_matrix )\\r\\n\\t\\tthis.updateMatrices();\\r\\n\\treturn vec3.unproject(result || vec3.create(), vec, this._viewprojection_matrix, viewport );\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the viewport in pixels applying the local camera viewport to the full viewport of the canvas\\r\\n* @method getLocalViewport\\r\\n* @param {vec4} [viewport=null] viewport info, otherwise the canvas dimensions will be used (not the current viewport)\\r\\n* @param {vec4} [result=vec4] where to store the result, if omited it is created\\r\\n* @return {vec4} the viewport info of the camera in pixels\\r\\n*/\\r\\nCamera.prototype.getLocalViewport = function( viewport, result )\\r\\n{\\r\\n\\tresult = result || vec4.create();\\r\\n\\r\\n\\t//if no viewport specified, use the full canvas viewport as reference\\r\\n\\tif(!viewport)\\r\\n\\t{\\r\\n\\t\\tresult[0] = gl.canvas.width * this._viewport[0]; //asume starts in 0\\r\\n\\t\\tresult[1] = gl.canvas.height * this._viewport[1]; //asume starts in 0\\r\\n\\t\\tresult[2] = gl.canvas.width * this._viewport[2];\\r\\n\\t\\tresult[3] = gl.canvas.height * this._viewport[3];\\r\\n\\t\\treturn result;\\r\\n\\t}\\r\\n\\r\\n\\t//apply viewport\\r\\n\\tresult[0] = Math.floor(viewport[2] * this._viewport[0] + viewport[0]);\\r\\n\\tresult[1] = Math.floor(viewport[3] * this._viewport[1] + viewport[1]);\\r\\n\\tresult[2] = Math.ceil(viewport[2] * this._viewport[2]);\\r\\n\\tresult[3] = Math.ceil(viewport[3] * this._viewport[3]);\\r\\n\\treturn result;\\r\\n}\\r\\n\\r\\n/**\\r\\n* transform from mouse coordinates (0,0 is top-left on the canvas) to local camera viewport coordinates (0,0 is bottom-left corner of the camera viewport)\\r\\n* @method mouseToViewport\\r\\n* @param {vec2} pos in mouse coordinates\\r\\n* @return {vec2} the pos in local camera viewport coordinates\\r\\n*/\\r\\nCamera.prototype.mouseToViewport = function(pos, out)\\r\\n{\\r\\n\\tout = out || vec2.create();\\r\\n\\tvar v = this._last_viewport_in_pixels;\\r\\n\\tout[0] = pos[0] - v[0];\\r\\n\\tout[1] = v[3] - (pos[1] - v[1]);\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n/**\\r\\n* given an x and y position, returns the ray {start, dir}\\r\\n* @method getRay\\r\\n* @param {number} x in canvas coordinates (bottom-left is 0,0)\\r\\n* @param {number} y in canvas coordinates (bottom-left is 0,0)\\r\\n* @param {vec4} viewport viewport coordinates (if omited full viewport is used using the camera viewport)\\r\\n* @param {boolean} skip_local_viewport ignore the local camera viewport configuration when computing the viewport\\r\\n* @param {LS.Ray} result [optional] to reuse ray\\r\\n* @return {LS.Ray} {origin:vec3, direction:vec3} or null is values are undefined or NaN\\r\\n*/\\r\\nCamera.prototype.getRay = (function(){\\r\\n\\tvar tmp_pos = vec3.create();\\r\\n\\tvar tmp_eye = vec3.create();\\r\\n\\treturn function getRay( x, y, viewport, skip_local_viewport, result )\\r\\n\\t{\\r\\n\\t\\t//apply camera viewport\\r\\n\\t\\tif(!skip_local_viewport)\\r\\n\\t\\t\\tviewport = this.getLocalViewport( viewport, this._viewport_in_pixels );\\r\\n\\t\\telse\\r\\n\\t\\t\\tviewport = gl.viewport_data;\\r\\n\\r\\n\\t\\t//flip Y\\r\\n\\t\\t//y = (viewport[3] - y) - viewport[1];\\r\\n\\r\\n\\t\\tif( this._must_update_view_matrix || this._must_update_projection_matrix )\\r\\n\\t\\t\\tthis.updateMatrices();\\r\\n\\t\\ttmp_pos[0] = x; tmp_pos[1] = y; tmp_pos[2] = 1;\\r\\n\\t\\tvar pos = vec3.unproject( tmp_pos, tmp_pos, this._viewprojection_matrix, viewport );\\r\\n\\t\\tif(!pos)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar eye = null;\\r\\n\\t\\tif(this.type == Camera.ORTHOGRAPHIC)\\r\\n\\t\\t{\\r\\n\\t\\t\\ttmp_eye[0] = x; tmp_eye[1] = y; tmp_eye[2] = 0;\\r\\n\\t\\t\\teye = vec3.unproject( tmp_eye, tmp_eye, this._viewprojection_matrix, viewport );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\teye = this.getEye( tmp_eye );\\r\\n\\r\\n\\t\\tvar dir = vec3.subtract( pos, pos, eye );\\r\\n\\t\\tvec3.normalize(dir, dir);\\r\\n\\r\\n\\t\\tresult = result || new LS.Ray();\\r\\n\\t\\tresult.origin.set(eye);\\r\\n\\t\\tresult.direction.set(dir);\\r\\n\\t\\treturn result;\\r\\n\\t}\\r\\n})();\\r\\n\\r\\nCamera.prototype.getRayInPixel = Camera.prototype.getRay; //LEGACY\\r\\n\\r\\n/**\\r\\n* Returns true if the 2D point (in screen space coordinates) is inside the camera viewport area\\r\\n* @method isPoint2DInCameraViewport\\r\\n* @param {number} x in canvas coordinates (0,0 is bottom-left)\\r\\n* @param {number} y in canvas coordinates (0,0 is bottom-left)\\r\\n* @param {vec4} viewport viewport coordinates (if omited full viewport is used)\\r\\n* @return {boolean} \\r\\n*/\\r\\nCamera.prototype.isPoint2DInCameraViewport = function( x, y, viewport )\\r\\n{\\r\\n\\tvar v = this.getLocalViewport( viewport, this._viewport_in_pixels );\\r\\n\\tif( x < v[0] || x > v[0] + v[2] ||\\r\\n\\t\\ty < v[1] || y > v[1] + v[3] )\\r\\n\\t\\treturn false;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns true if the 3D point is inside the camera frustum\\r\\n* @method testSphereInsideFrustum\\r\\n* @param {vec3} center\\r\\n* @param {vec3} radius\\r\\n* @return {boolean} \\r\\n*/\\r\\nCamera.prototype.testSphereInsideFrustum = function( center, radius )\\r\\n{\\r\\n\\treturn geo.frustumTestSphere( this._frustum_planes, center, radius || 0 ) != CLIP_OUTSIDE;\\r\\n}\\r\\n\\r\\n\\r\\nCamera.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(o.uid !== undefined) this.uid = o.uid;\\r\\n\\tif(o.layers !== undefined) this.layers = o.layers;\\r\\n\\r\\n\\tif(o.enabled !== undefined) this.enabled = o.enabled;\\r\\n\\tif(o.type !== undefined) this._type = o.type;\\r\\n\\r\\n\\tif(o.eye !== undefined) this._eye.set(o.eye);\\r\\n\\tif(o.center !== undefined) this._center.set(o.center);\\r\\n\\tif(o.up !== undefined) this._up.set(o.up);\\r\\n\\r\\n\\tif(o.near !== undefined) this._near = o.near;\\r\\n\\tif(o.far !== undefined) this._far = o.far;\\r\\n\\tif(o.fov !== undefined) this._fov = o.fov;\\r\\n\\tif(o.aspect !== undefined) this._aspect = o.aspect;\\r\\n\\tif(o.final_aspect !== undefined) this._final_aspect = o.final_aspect;\\r\\n\\tif(o.frustum_size !== undefined) this._frustum_size = o.frustum_size;\\r\\n\\tif(o.viewport !== undefined) this._viewport.set( o.viewport );\\r\\n\\tif(o.orthographic !== undefined) this._ortho.set( o.orthographic );\\r\\n\\r\\n\\tif(o.background_color !== undefined) this._background_color.set( o.background_color );\\r\\n\\r\\n\\tif(o.render_to_texture !== undefined) this.render_to_texture = o.render_to_texture;\\r\\n\\tif(o.frame && this._frame) this._frame.configure( o.frame );\\r\\n\\tif(o.show_frame !== undefined) this.show_frame = o.show_frame;\\r\\n\\r\\n\\tif(o.clear_color !== undefined) this.clear_color = !!o.clear_color;\\r\\n\\tif(o.clear_depth !== undefined) this.clear_depth = !!o.clear_depth;\\r\\n\\r\\n\\tthis.updateMatrices( true );\\r\\n}\\r\\n\\r\\nCamera.prototype.serialize = function()\\r\\n{\\r\\n\\tvar o = {\\r\\n\\t\\tobject_class: \\\"Camera\\\",\\r\\n\\t\\tuid: this.uid,\\r\\n\\t\\tlayers: this.layers,\\r\\n\\t\\tenabled: this.enabled,\\r\\n\\t\\ttype: this._type,\\r\\n\\t\\teye: vec3.toArray(this._eye),\\r\\n\\t\\tcenter: vec3.toArray(this._center),\\r\\n\\t\\tup: vec3.toArray(this._up),\\r\\n\\t\\tnear: this._near,\\r\\n\\t\\tfar: this._far,\\r\\n\\t\\tfov: this._fov,\\r\\n\\t\\taspect: this._aspect,\\r\\n\\t\\torthographic: vec4.toArray(this._ortho),\\r\\n\\t\\tbackground_color: vec4.toArray(this._background_color),\\r\\n\\t\\tfrustum_size: this._frustum_size,\\r\\n\\t\\tviewport: toArray( this._viewport ),\\r\\n\\t\\trender_to_texture: this.render_to_texture,\\r\\n\\t\\tframe: this._frame ? this._frame.serialize() : null,\\r\\n\\t\\tshow_frame: this.show_frame,\\r\\n\\t\\tclear_color: this.clear_color,\\r\\n\\t\\tclear_depth: this.clear_depth\\r\\n\\t};\\r\\n\\r\\n\\t//clone\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\n//Layer stuff\\r\\nCamera.prototype.checkLayersVisibility = function( layers )\\r\\n{\\r\\n\\treturn (this.layers & layers) !== 0;\\r\\n}\\r\\n\\r\\nCamera.prototype.getLayers = function()\\r\\n{\\r\\n\\tvar r = [];\\r\\n\\tfor(var i = 0; i < 32; ++i)\\r\\n\\t{\\r\\n\\t\\tif( this.layers & (1< 0.999 ) \\r\\n\\t\\t\\tvec3.set(up,0,0,1);\\r\\n\\t\\tcamera.up = up;\\r\\n\\t\\tcamera.fov = (this.angle_end || 45); //fov is in degrees\\r\\n\\t}\\r\\n\\r\\n\\tvar closest_far = this.computeFar();\\r\\n\\r\\n\\tcamera.frustum_size = this.frustum_size || Light.DEFAULT_DIRECTIONAL_FRUSTUM_SIZE;\\r\\n\\tcamera.near = this.near;\\r\\n\\tcamera.far = closest_far;\\r\\n\\tcamera.layers = this.illuminated_layers;\\r\\n\\tcamera.updateMatrices();\\r\\n\\r\\n\\tthis._light_matrix.set( camera._viewprojection_matrix );\\r\\n\\r\\n\\treturn camera;\\r\\n}\\r\\n\\r\\n\\r\\nLight.prototype.computeFar = function()\\r\\n{\\r\\n\\tvar closest_far = this.far;\\r\\n\\r\\n\\tif( this.type == Light.OMNI )\\r\\n\\t{\\r\\n\\t\\t//Math.SQRT2 because in a 45� triangle the hypotenuse is sqrt(1+1) * side\\r\\n\\t\\tif( this.attenuation_type == this.RANGE_ATTENUATION && (this.att_end * Math.SQRT2) < closest_far)\\r\\n\\t\\t\\tclosest_far = this.att_end / Math.SQRT2;\\r\\n\\t\\t//TODO, if no range_attenuation but linear_attenuation also check intensity to reduce the far\\r\\n\\t}\\r\\n\\telse \\r\\n\\t{\\r\\n\\t\\tif( this.attenuation_type == this.RANGE_ATTENUATION && this.att_end < closest_far)\\r\\n\\t\\t\\tclosest_far = this.att_end;\\r\\n\\t}\\r\\n\\r\\n\\treturn closest_far;\\r\\n}\\r\\n\\r\\n/**\\r\\n* updates all the important vectors (target, position, etc) according to the node parent of the light\\r\\n* @method updateVectors\\r\\n*/\\r\\nLight.prototype.updateVectors = (function(){\\r\\n\\tvar temp_v3 = vec3.create();\\r\\n\\r\\n\\treturn function()\\r\\n\\t{\\r\\n\\t\\t//if the light is inside the root node of the scene\\r\\n\\t\\tif(!this._root || !this._root.transform) \\r\\n\\t\\t{\\r\\n\\t\\t\\t//position, target and up are already valid\\r\\n\\t\\t\\t //front\\r\\n\\t\\t\\t //vec3.subtract(this._front, this.position, this.target ); //positive z front\\r\\n\\t\\t\\t vec3.subtract(this._front, this._target, this._position ); //positive z front\\r\\n\\t\\t\\t vec3.normalize(this._front,this._front);\\r\\n\\t\\t\\t //right\\r\\n\\t\\t\\t vec3.normalize( temp_v3, this._up );\\r\\n\\t\\t\\t vec3.cross( this._right, this._front, temp_v3 );\\r\\n\\t\\t\\t //top\\r\\n\\t\\t\\t vec3.cross( this._top, this._right, this._front );\\r\\n\\t\\t\\t return;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar mat = this._root.transform.getGlobalMatrixRef();\\r\\n\\r\\n\\t\\t//position\\r\\n\\t\\tmat4.getTranslation( this._position, mat);\\r\\n\\t\\t//target\\r\\n\\t\\tif (!this.use_target)\\r\\n\\t\\t\\tmat4.multiplyVec3( this._target, mat, LS.FRONT ); //right in front of the object\\r\\n\\t\\t//up\\r\\n\\t\\tmat4.multiplyVec3( this._up, mat, LS.TOP ); //right in front of the object\\r\\n\\r\\n\\t\\t//vectors\\r\\n\\t\\tmat4.rotateVec3( this._front, mat, LS.FRONT ); \\r\\n\\t\\tmat4.rotateVec3( this._right, mat, LS.RIGHT ); \\r\\n\\t\\tvec3.copy( this._top, this.up ); \\r\\n\\t}\\r\\n})();\\r\\n/**\\r\\n* returns a copy of the light position (in global coordinates), if you want local you can access the position property\\r\\n* @method getPosition\\r\\n* @param {vec3} output optional\\r\\n* @return {vec3} the position\\r\\n*/\\r\\nLight.prototype.getPosition = function( out )\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\t//if(this._root && this._root.transform) return this._root.transform.localToGlobal( this.position, p || vec3.create() );\\r\\n\\tif(this._root && this._root.transform) \\r\\n\\t\\treturn this._root.transform.getGlobalPosition( out );\\r\\n\\tout.set( this._position );\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns a copy of the light target (in global coordinates), if you want local you can access the target property\\r\\n* @method getTarget\\r\\n* @param {vec3} output optional\\r\\n* @return {vec3} the target\\r\\n*/\\r\\nLight.prototype.getTarget = function( out )\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\tif(this._root && this._root.transform && !this.use_target) \\r\\n\\t\\treturn this._root.transform.localToGlobal( LS.FRONT , out );\\r\\n\\tout.set( this._target );\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns a copy of the light up vector (in global coordinates), if you want local you can access the up property\\r\\n* @method getUp\\r\\n* @param {vec3} output optional\\r\\n* @return {vec3} the up vector\\r\\n*/\\r\\nLight.prototype.getUp = function( out )\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\r\\n\\tif(this._root && this._root.transform) \\r\\n\\t\\treturn this._root.transform.transformVector( LS.TOP , out );\\r\\n\\tout.set( this._up );\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns a copy of the front vector (in global coordinates)\\r\\n* @method getFront\\r\\n* @param {vec3} output optional\\r\\n* @return {vec3} the front vector\\r\\n*/\\r\\nLight.prototype.getFront = function( out ) \\r\\n{\\r\\n\\tvar front = out || vec3.create();\\r\\n\\tthis.getPosition( front );\\r\\n\\tvec3.subtract( front, front, this.getTarget( Light._temp_position ) ); //front is reversed?\\r\\n\\t//vec3.subtract(front, this.getTarget(), this.getPosition() ); //front is reversed?\\r\\n\\tvec3.normalize(front, front);\\r\\n\\treturn front;\\r\\n}\\r\\n\\r\\n/*\\r\\nLight.prototype.getLightRotationMatrix = function()\\r\\n{\\r\\n\\t//TODO\\r\\n}\\r\\n*/\\r\\n\\r\\nLight.prototype.getResources = function (res)\\r\\n{\\r\\n\\tif(this.projective_texture)\\r\\n\\t\\tres[ this.projective_texture ] = GL.Texture;\\r\\n\\tif(this.extra_texture)\\r\\n\\t\\tres[ this.extra_texture ] = GL.Texture;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nLight.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.projective_texture == old_name)\\r\\n\\t\\tthis.projective_texture = new_name;\\r\\n\\tif(this.extra_texture == old_name)\\r\\n\\t\\tthis.extra_texture = new_name;\\r\\n}\\r\\n\\r\\n//Layer stuff\\r\\nLight.prototype.checkLayersVisibility = function( layers )\\r\\n{\\r\\n\\treturn (this.illuminated_layers & layers) !== 0;\\r\\n}\\r\\n\\r\\nLight.prototype.isInLayer = function(num)\\r\\n{\\r\\n\\treturn (this.illuminated_layers & (1< 10 )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._primitive = v;\\r\\n\\t\\tthis.updateRIs();\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The material to apply to this render, if not provided the one in the node will be used\\r\\n* @property material {string}\\r\\n* @default -1;\\r\\n*/\\r\\nObject.defineProperty( MeshRenderer.prototype, 'material', {\\r\\n\\tget: function() { return this._material; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._material = v;\\r\\n\\t\\tthis.updateRIs();\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The name of the mesh to render\\r\\n* @property mesh {string}\\r\\n* @default null;\\r\\n*/\\r\\nObject.defineProperty( MeshRenderer.prototype, 'mesh', {\\r\\n\\tget: function() { return this._mesh; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._mesh = v;\\r\\n\\t\\tthis.updateRIs();\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The name of the mesh to render in case the mesh is far away, this mesh is also used for collision testing if using raycast to RenderInstances\\r\\n* @property lod_mesh {string}\\r\\n* @default null;\\r\\n*/\\r\\nObject.defineProperty( MeshRenderer.prototype, 'lod_mesh', {\\r\\n\\tget: function() { return this._lod_mesh; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._lod_mesh = v;\\r\\n\\t\\tthis.updateRIs();\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The id of the submesh group to render, if the id is -1 then all the mesh is rendered.\\r\\n* @property submesh_id {number}\\r\\n* @default -1;\\r\\n*/\\r\\nObject.defineProperty( MeshRenderer.prototype, 'submesh_id', {\\r\\n\\tget: function() { return this._submesh_id; },\\r\\n\\tset: function(v) { \\r\\n\\t\\t//what about if v is a string, search for the index?\\r\\n\\t\\tthis._submesh_id = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( MeshRenderer.prototype, 'render_instance', {\\r\\n\\tget: function() { return this._RI; },\\r\\n\\tset: function(v) { throw(\\\"cannot set a render_instance, must use the collectRenderInstances process.\\\"); },\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nMeshRenderer.icon = \\\"mini-icon-teapot.png\\\";\\r\\n\\r\\n//vars\\r\\nMeshRenderer[\\\"@mesh\\\"] = { type: \\\"mesh\\\" };\\r\\nMeshRenderer[\\\"@lod_mesh\\\"] = { type: \\\"mesh\\\" };\\r\\nMeshRenderer[\\\"@material\\\"] = { type: \\\"material\\\" };\\r\\nMeshRenderer[\\\"@primitive\\\"] = { type:\\\"enum\\\", values: {\\\"Default\\\":-1, \\\"Points\\\": 0, \\\"Lines\\\":1, \\\"LineLoop\\\":2, \\\"LineStrip\\\":3, \\\"Triangles\\\":4, \\\"TriangleStrip\\\":5, \\\"TriangleFan\\\":6, \\\"Wireframe\\\":10 }};\\r\\nMeshRenderer[\\\"@submesh_id\\\"] = { type:\\\"enum\\\", values: function() {\\r\\n\\tvar component = this.instance;\\r\\n\\tvar mesh = component.getMesh();\\r\\n\\tif(!mesh)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tif(!mesh || !mesh.info || !mesh.info.groups)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar t = {\\\"all\\\":null};\\r\\n\\tfor(var i = 0; i < mesh.info.groups.length; ++i)\\r\\n\\t\\tt[mesh.info.groups[i].name] = i;\\r\\n\\treturn t;\\r\\n}};\\r\\n\\r\\nMeshRenderer[\\\"@use_submaterials\\\"] = { type: LS.TYPES.BOOLEAN, widget: null }; //avoid widget\\r\\nMeshRenderer[\\\"@submaterials\\\"] = { widget: null }; //avoid \\r\\n\\r\\n//we bind to onAddedToNode because the event is triggered per node so we know which RIs belong to which node\\r\\nMeshRenderer.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\tthis.checkRenderInstances();\\r\\n}\\r\\n\\r\\nMeshRenderer.prototype.onRemovedFromScene = function( scene )\\r\\n{\\r\\n\\tthis.checkRenderInstances();\\r\\n}\\r\\n\\r\\nMeshRenderer.prototype.onAddedToNode = function( node )\\r\\n{\\r\\n\\tLEvent.bind( node, \\\"materialChanged\\\", this.updateRIs, this );\\r\\n\\tLEvent.bind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n\\tthis._RI.node = node;\\r\\n}\\r\\n\\r\\nMeshRenderer.prototype.onRemovedFromNode = function( node )\\r\\n{\\r\\n\\tLEvent.unbind( node, \\\"materialChanged\\\", this.updateRIs, this );\\r\\n\\tLEvent.unbind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Configure from a serialized object\\r\\n* @method configure\\r\\n* @param {Object} object with the serialized info\\r\\n*/\\r\\nMeshRenderer.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(o.uid)\\r\\n\\t\\tthis.uid = o.uid;\\r\\n\\tif(o.enabled !== undefined)\\r\\n\\t\\tthis.enabled = o.enabled;\\r\\n\\tthis.mesh = o.mesh;\\r\\n\\tthis.lod_mesh = o.lod_mesh;\\r\\n\\tif(o.submesh_id !== undefined)\\r\\n\\t\\tthis.submesh_id = o.submesh_id;\\r\\n\\tthis.primitive = o.primitive; //gl.TRIANGLES\\r\\n\\tthis.material = o.material;\\r\\n\\tthis.use_submaterials = !!o.use_submaterials;\\r\\n\\tif(o.submaterials)\\r\\n\\t\\tthis.submaterials = o.submaterials;\\r\\n\\tif(o.material && o.material.constructor === String)\\r\\n\\t\\tthis.material = o.material;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Serialize the object \\r\\n* @method serialize\\r\\n* @return {Object} object with the serialized info\\r\\n*/\\r\\nMeshRenderer.prototype.serialize = function()\\r\\n{\\r\\n\\tvar o = { \\r\\n\\t\\tobject_class: \\\"MeshRenderer\\\",\\r\\n\\t\\tenabled: this.enabled,\\r\\n\\t\\tuid: this.uid,\\r\\n\\t\\tmesh: this.mesh,\\r\\n\\t\\tlod_mesh: this.lod_mesh\\r\\n\\t};\\r\\n\\r\\n\\tif(this.material && this.material.constructor === String )\\r\\n\\t\\to.material = this.material;\\r\\n\\r\\n\\tif(this.primitive != -1)\\r\\n\\t\\to.primitive = this.primitive;\\r\\n\\tif(this.submesh_id != -1)\\r\\n\\t\\to.submesh_id = this.submesh_id;\\r\\n\\to.material = this.material;\\r\\n\\r\\n\\tif(this.use_submaterials)\\r\\n\\t\\to.use_submaterials = this.use_submaterials;\\r\\n\\to.submaterials = this.submaterials;\\r\\n\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\nMeshRenderer.prototype.getMesh = function() {\\r\\n\\tif(!this.mesh)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tif( this.mesh.constructor === String )\\r\\n\\t\\treturn LS.ResourcesManager.meshes[ this.mesh ];\\r\\n\\treturn this.mesh;\\r\\n}\\r\\n\\r\\nMeshRenderer.prototype.getLODMesh = function() {\\r\\n\\tif(!this.lod_mesh)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tif( this.lod_mesh.constructor === String )\\r\\n\\t\\treturn LS.ResourcesManager.meshes[ this.lod_mesh ];\\r\\n\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\nMeshRenderer.prototype.getAnyMesh = function() {\\r\\n\\treturn (this.getMesh() || this.getLODMesh());\\r\\n}\\r\\n\\r\\nMeshRenderer.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif( this.mesh && this.mesh.constructor === String )\\r\\n\\t\\tres[ this.mesh ] = GL.Mesh;\\r\\n\\tif( this.lod_mesh && this.lod_mesh.constructor === String )\\r\\n\\t\\tres[this.lod_mesh] = GL.Mesh;\\r\\n\\tif( this.material && this.material.constructor === String )\\r\\n\\t\\tres[this.material] = LS.Material;\\r\\n\\r\\n\\tif(this.use_submaterials)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < this.submaterials.length; ++i)\\r\\n\\t\\t\\tif(this.submaterials[i])\\r\\n\\t\\t\\t\\tres[this.submaterials[i]] = LS.Material;\\r\\n\\t}\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nMeshRenderer.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.mesh == old_name)\\r\\n\\t\\tthis.mesh = new_name;\\r\\n\\tif(this.lod_mesh == old_name)\\r\\n\\t\\tthis.lod_mesh = new_name;\\r\\n\\tif(this.material == old_name)\\r\\n\\t\\tthis.material = new_name;\\r\\n\\tif(this.morph_targets)\\r\\n\\t\\tfor(var i in this.morph_targets)\\r\\n\\t\\t\\tif( this.morph_targets[i].mesh == old_name )\\r\\n\\t\\t\\t\\tthis.morph_targets[i].mesh = new_name;\\r\\n}\\r\\n\\r\\nMeshRenderer.prototype.checkRenderInstances = function()\\r\\n{\\r\\n\\treturn;\\r\\n\\r\\n\\tvar should_be_attached = this._enabled && this._root.scene;\\r\\n\\r\\n\\tif( should_be_attached && !this._is_attached )\\r\\n\\t{\\r\\n\\t\\tthis._root.scene.attachSceneElement( this._RI );\\r\\n\\t\\tthis._is_attached = true;\\r\\n\\t}\\r\\n\\telse if( !should_be_attached && this._is_attached )\\r\\n\\t{\\r\\n\\t\\tthis._root.scene.detachSceneElement( this._RI );\\r\\n\\t\\tthis._is_attached = false;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//called everytime something affecting this RIs configuration changes\\r\\nMeshRenderer.prototype.updateRIs = function()\\r\\n{\\r\\n\\treturn;\\r\\n\\r\\n\\tvar node = this._root;\\r\\n\\tif(!node)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar RI = this._RI;\\r\\n\\tvar is_static = this._root.flags && this._root.flags.is_static;\\r\\n\\tvar transform = this._root.transform;\\r\\n\\r\\n\\t//optimize: TODO\\r\\n\\t//if( is_static && LS.allow_static && !this._must_update_static && (!transform || (transform && this._transform_version == transform._version)) )\\r\\n\\t//\\treturn instances.push( RI );\\r\\n\\r\\n\\t//assigns matrix, layers\\r\\n\\tRI.fromNode( this._root );\\r\\n\\r\\n\\t//material (after flags because it modifies the flags)\\r\\n\\tvar material = null;\\r\\n\\tif(this.material)\\r\\n\\t\\tmaterial = LS.ResourcesManager.getResource( this.material );\\r\\n\\telse\\r\\n\\t\\tmaterial = this._root.getMaterial();\\r\\n\\tRI.setMaterial( material );\\r\\n\\r\\n\\t//buffers from mesh and bounding\\r\\n\\tvar mesh = LS.ResourcesManager.getMesh( this._mesh );\\r\\n\\tif( mesh )\\r\\n\\t{\\r\\n\\t\\tRI.setMesh( mesh, this.primitive );\\r\\n\\t\\tif(this._submesh_id != -1 && this._submesh_id != null && mesh.info && mesh.info.groups)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar group = mesh.info.groups[this._submesh_id];\\r\\n\\t\\t\\tif(group)\\r\\n\\t\\t\\t\\tRI.setRange( group.start, group.length );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tRI.setRange(0,-1);\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tRI.setMesh( null );\\r\\n\\t\\tRI.setRange(0,-1);\\r\\n\\t\\tif(this._once_binding_index != null)\\r\\n\\t\\t\\tthis._once_binding_index = LS.ResourcesManager.onceLoaded( this._mesh, this.updateRIs.bind(this ) );\\r\\n\\t}\\r\\n\\r\\n\\t//used for raycasting\\r\\n\\t/*\\r\\n\\tif(this.lod_mesh)\\r\\n\\t{\\r\\n\\t\\tif( this.lod_mesh.constructor === String )\\r\\n\\t\\t\\tRI.collision_mesh = LS.ResourcesManager.resources[ this.lod_mesh ];\\r\\n\\t\\telse\\r\\n\\t\\t\\tRI.collision_mesh = this.lod_mesh;\\r\\n\\t\\t//RI.setLODMesh( RI.collision_mesh );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t*/\\r\\n\\t\\tRI.collision_mesh = mesh;\\r\\n\\r\\n\\t//mark it as ready once no more changes should be applied\\r\\n\\tif( is_static && LS.allow_static && !this.isLoading() )\\r\\n\\t{\\r\\n\\t\\tthis._must_update_static = false;\\r\\n\\t\\tthis._transform_version = transform ? transform._version : 0;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//*\\r\\n//MeshRenderer.prototype.getRenderInstance = function(options)\\r\\nMeshRenderer.prototype.onCollectInstances = function(e, instances)\\r\\n{\\r\\n\\tif(!this._enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(this.use_submaterials)\\r\\n\\t{\\r\\n\\t\\tthis.onCollectInstancesSubmaterials( instances );\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar mesh = this.getAnyMesh();\\r\\n\\tif(!mesh)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar node = this._root;\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar RI = this._RI;\\r\\n\\tvar is_static = this._root.flags && this._root.flags.is_static;\\r\\n\\tvar transform = this._root.transform;\\r\\n\\tRI.layers = this._root.layers;\\r\\n\\r\\n\\t//optimize\\r\\n\\t//if( is_static && LS.allow_static && !this._must_update_static && (!transform || (transform && this._transform_version == transform._version)) )\\r\\n\\t//\\treturn instances.push( RI );\\r\\n\\r\\n\\t//assigns matrix, layers\\r\\n\\tRI.fromNode( this._root );\\r\\n\\r\\n\\t//material (after flags because it modifies the flags)\\r\\n\\tvar material = null;\\r\\n\\tif(this.material)\\r\\n\\t\\tmaterial = LS.ResourcesManager.getResource( this.material );\\r\\n\\telse\\r\\n\\t\\tmaterial = this._root.getMaterial();\\r\\n\\tRI.setMaterial( material );\\r\\n\\r\\n\\t//buffers from mesh and bounding\\r\\n\\tRI.setMesh( mesh, this.primitive );\\r\\n\\r\\n\\tif(this.submesh_id != -1 && this.submesh_id != null && mesh.info && mesh.info.groups)\\r\\n\\t{\\r\\n\\t\\tvar group = mesh.info.groups[this.submesh_id];\\r\\n\\t\\tif(group)\\r\\n\\t\\t{\\r\\n\\t\\t\\tRI.setRange( group.start, group.length );\\r\\n\\t\\t\\tif( group.bounding )\\r\\n\\t\\t\\t\\tRI.setBoundingBox( group.bounding );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tRI.setRange(0,-1);\\r\\n\\r\\n\\t//used for raycasting\\r\\n\\t/*\\r\\n\\tif(this.lod_mesh)\\r\\n\\t{\\r\\n\\t\\tif( this.lod_mesh.constructor === String )\\r\\n\\t\\t\\tRI.collision_mesh = LS.ResourcesManager.resources[ this.lod_mesh ];\\r\\n\\t\\telse\\r\\n\\t\\t\\tRI.collision_mesh = this.lod_mesh;\\r\\n\\t\\t//RI.setLODMesh( RI.collision_mesh );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t*/\\r\\n\\t\\tRI.collision_mesh = mesh;\\r\\n\\r\\n\\t//mark it as ready once no more changes should be applied\\r\\n\\tif( is_static && LS.allow_static && !this.isLoading() )\\r\\n\\t{\\r\\n\\t\\tthis._must_update_static = false;\\r\\n\\t\\tthis._transform_version = transform ? transform._version : 0;\\r\\n\\t}\\r\\n\\r\\n\\tinstances.push( RI );\\r\\n}\\r\\n\\r\\n//not fully tested\\r\\nMeshRenderer.prototype.onCollectInstancesSubmaterials = function(instances)\\r\\n{\\r\\n\\tif(!this._RIs)\\r\\n\\t\\tthis._RIs = [];\\r\\n\\r\\n\\tvar mesh = this.getMesh();\\r\\n\\tif(!mesh)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar groups = mesh.info.groups;\\r\\n\\tif(!groups)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar global = this._root.transform._global_matrix;\\r\\n\\tvar center = vec3.create();\\r\\n\\tmat4.multiplyVec3( center, global, LS.ZEROS );\\r\\n\\tvar first_RI = null;\\r\\n\\r\\n\\tfor(var i = 0; i < this.submaterials.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar submaterial_name = this.submaterials[i];\\r\\n\\t\\tif(!submaterial_name)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar group = groups[i];\\r\\n\\t\\tif(!group)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar material = LS.ResourcesManager.getResource( submaterial_name );\\r\\n\\t\\tif(!material)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tvar RI = this._RIs[i];\\r\\n\\t\\tif(!RI)\\r\\n\\t\\t\\tRI = this._RIs[i] = new LS.RenderInstance(this._root,this);\\r\\n\\r\\n\\t\\tif(!first_RI)\\r\\n\\t\\t\\tRI.setMatrix( this._root.transform._global_matrix );\\r\\n\\t\\telse\\r\\n\\t\\t\\tRI.setMatrix( first_RI.matrix, first_RI.normal_matrix );\\r\\n\\t\\tRI.center.set(center);\\r\\n\\r\\n\\t\\t//flags\\r\\n\\t\\tRI.setMaterial( material );\\r\\n\\t\\tRI.setMesh( mesh, this.primitive );\\r\\n\\t\\tRI.setRange( group.start, group.length );\\r\\n\\t\\tinstances.push(RI);\\r\\n\\r\\n\\t\\tif(!first_RI)\\r\\n\\t\\t\\tfirst_RI = RI;\\r\\n\\t}\\r\\n}\\r\\n//*/\\r\\n\\r\\n//test if any of the assets is being loaded\\r\\nMeshRenderer.prototype.isLoading = function()\\r\\n{\\r\\n\\tif( this.mesh && LS.ResourcesManager.isLoading( this.mesh ))\\r\\n\\t\\treturn true;\\r\\n\\tif( this.lod_mesh && LS.ResourcesManager.isLoading( this.lod_mesh ))\\r\\n\\t\\treturn true;\\r\\n\\tif( this.material && LS.ResourcesManager.isLoading( this.material ))\\r\\n\\t\\treturn true;\\r\\n\\tif(this._root && this._root.material && this._root.material.constructor === String && LS.ResourcesManager.isLoading( this._root.material ))\\r\\n\\t\\treturn true;\\r\\n\\treturn false;\\r\\n}\\r\\n\\r\\n//used when a node has too many submeshes with materials\\r\\nMeshRenderer.prototype.explodeSubmeshesToChildNodes = function() { \\r\\n\\tvar node = this._root;\\r\\n\\tif(!node)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar mesh = this.getMesh();\\r\\n\\tif(!mesh || !mesh.info || !mesh.info.groups )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tnode.removeComponent( this );\\r\\n\\r\\n\\tfor(var i = 0; i < mesh.info.groups.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar group = mesh.info.groups[i];\\r\\n\\t\\tvar child_node = new LS.SceneNode();\\r\\n\\t\\tnode.addChild( child_node );\\r\\n\\t\\tvar comp = new LS.Components.MeshRenderer({ mesh: this.mesh, submesh_id: i, material: group.material });\\r\\n\\t\\tchild_node.addComponent( comp );\\t\\r\\n\\t}\\r\\n\\r\\n\\tLS.GlobalScene.refresh();\\r\\n}\\r\\n\\r\\nLS.registerComponent( MeshRenderer );\\r\\nLS.MeshRenderer = MeshRenderer;\\r\\n///@FILE:../src/components/morphDeformer.js\\r\\n///@INFO: UNCOMMON\\r\\n\\r\\n/**\\r\\n* It complements a MeshRenderer to add Morph Targets (Blend Shapes) to deform meshes.\\r\\n* Morph Targets of a mesh must have the same topology and number of vertex, otherwise it won't work.\\r\\n* @class MorphDeformer\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\nfunction MorphDeformer(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\r\\n\\t/**\\r\\n\\t* The mode used to apply the morph targets, could be using the CPU, the GPU using uniforms( limited by the browser/driver) or using Textures (more expensive). Leave it as automatic so the system knows the best case.\\r\\n\\t* @property mode {Number} MorphDeformer.AUTOMATIC, MorphDeformer.CPU, MorphDeformer.STREAMS, MorphDeformer.TEXTURES\\r\\n\\t* @default MorphDeformer.AUTOMATIC;\\r\\n\\t*/\\r\\n\\tthis.mode = MorphDeformer.AUTOMATIC;\\r\\n\\r\\n\\t/**\\r\\n\\t* if true the meshes will be treated as a increment over the base mesh, not as an absolute mesh\\r\\n\\t* @property delta_meshes {Boolean} \\r\\n\\t* @default MorphDeformer.AUTOMATIC;\\r\\n\\t*/\\r\\n\\tthis.delta_meshes = false;\\r\\n\\r\\n\\t/**\\r\\n\\t* An array with every morph targets info in the form of { mesh: mesh_name, weight: number }\\r\\n\\t* @property morph_targets {Array}\\r\\n\\t* @default [];\\r\\n\\t*/\\r\\n\\tthis.morph_targets = [];\\r\\n\\r\\n\\tif(global.gl)\\r\\n\\t{\\r\\n\\t\\tif(MorphDeformer.max_supported_vertex_attribs === undefined)\\r\\n\\t\\t\\tMorphDeformer.max_supported_vertex_attribs = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\\r\\n\\t\\tif(MorphDeformer.max_supported_morph_targets_using_streams === undefined)\\r\\n\\t\\t\\tMorphDeformer.max_supported_morph_targets_using_streams = (gl.getParameter( gl.MAX_VERTEX_ATTRIBS ) - 6) / 2; //6 reserved for vertex, normal, uvs, uvs2, weights, bones. \\r\\n\\t}\\r\\n\\r\\n\\t\\r\\n\\tthis._stream_weights = new Float32Array( 4 );\\r\\n\\tthis._uniforms = { u_morph_weights: this._stream_weights, u_morph_info: 0 };\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nMorphDeformer.AUTOMATIC = 0;\\r\\nMorphDeformer.CPU = 1;\\r\\nMorphDeformer.STREAMS = 2;\\r\\nMorphDeformer.TEXTURES = 3;\\r\\n\\r\\nMorphDeformer.icon = \\\"mini-icon-teapot.png\\\";\\r\\nMorphDeformer.force_GPU = true; //used to avoid to recompile the shader when all morphs are 0\\r\\nMorphDeformer[\\\"@mode\\\"] = { type:\\\"enum\\\", values: {\\\"automatic\\\": MorphDeformer.AUTOMATIC, \\\"CPU\\\": MorphDeformer.CPU, \\\"streams\\\": MorphDeformer.STREAMS, \\\"textures\\\": MorphDeformer.TEXTURES }};\\r\\n\\r\\nMorphDeformer.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tLEvent.bind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n}\\r\\n\\r\\n//object with name:weight\\r\\nObject.defineProperty( MorphDeformer.prototype, \\\"name_weights\\\", {\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(!v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tfor(var i = 0; i < this.morph_targets.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar m = this.morph_targets[i];\\r\\n\\t\\t\\tif(v[m.mesh] !== undefined)\\r\\n\\t\\t\\t\\tm.weight = Number(v[m.mesh]);\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\tvar result = {};\\r\\n\\t\\tfor(var i = 0; i < this.morph_targets.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar m = this.morph_targets[i];\\r\\n\\t\\t\\tresult[ m.mesh ] = m.weight;\\r\\n\\t\\t}\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\tenumeration: false\\r\\n});\\r\\n\\r\\n\\r\\nObject.defineProperty( MorphDeformer.prototype, \\\"weights\\\", {\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(!v || !v.length)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tfor(var i = 0; i < v.length; ++i)\\r\\n\\t\\t\\tif( this.morph_targets[i] )\\r\\n\\t\\t\\t\\tthis.morph_targets[i].weight = v[i] || 0;\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\tvar result = new Array( this.morph_targets.length );\\r\\n\\t\\tfor(var i = 0; i < this.morph_targets.length; ++i)\\r\\n\\t\\t\\tresult[i] = this.morph_targets[i].weight;\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\tenumeration: false\\r\\n});\\r\\n\\r\\nMorphDeformer.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tLEvent.unbind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n\\r\\n\\t//disable\\r\\n\\tif( this._last_RI )\\r\\n\\t\\tthis.disableMorphingGPU( this._last_RI );\\r\\n\\tthis._last_RI = null;\\r\\n}\\r\\n\\r\\nMorphDeformer.prototype.getResources = function(res)\\r\\n{\\r\\n\\tfor(var i = 0; i < this.morph_targets.length; ++i)\\r\\n\\t\\tif( this.morph_targets[i].mesh )\\r\\n\\t\\t\\tres[ this.morph_targets[i].mesh ] = GL.Mesh;\\r\\n}\\r\\n\\r\\nMorphDeformer.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tfor(var i = 0; i < this.morph_targets.length; ++i)\\r\\n\\t\\tif( this.morph_targets[i].mesh == old_name )\\r\\n\\t\\t\\tthis.morph_targets[i].mesh = new_name;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Sets the weight for all the \\r\\n* @method clearWeights\\r\\n* @param {Object} object with the serialized info\\r\\n*/\\r\\nMorphDeformer.prototype.clearWeights = function()\\r\\n{\\r\\n\\tfor(var i = 0; i < this.morph_targets.length; ++i)\\r\\n\\t\\tthis.morph_targets[i].weight = 0;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Adds a new morph target\\r\\n* @method addMorph\\r\\n* @param {String} mesh_name\\r\\n* @param {Number} weight\\r\\n*/\\r\\nMorphDeformer.prototype.addMorph = function( mesh_name, weight)\\r\\n{\\r\\n\\tweight = weight || 0;\\r\\n\\tvar index = this.getMorphIndex( mesh_name );\\r\\n\\tif(index == -1)\\r\\n\\t\\tthis.morph_targets.push({mesh: mesh_name, weight: weight});\\r\\n\\telse\\r\\n\\t\\tthis.morph_targets[index] = {mesh: mesh_name, weight: weight};\\r\\n}\\r\\n\\r\\nMorphDeformer.prototype.onCollectInstances = function( e, render_instances )\\r\\n{\\r\\n\\tif(!render_instances.length || MorphDeformer.max_supported_vertex_attribs < 16)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar morph_RI = this.enabled ? render_instances[ render_instances.length - 1] : null;\\r\\n\\t\\r\\n\\tif( morph_RI != this._last_RI && this._last_RI )\\r\\n\\t\\tthis.disableMorphingGPU( this._last_RI );\\r\\n\\tthis._last_RI = morph_RI;\\r\\n\\r\\n\\tif( !morph_RI || !morph_RI.mesh)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._last_base_mesh = morph_RI.mesh;\\r\\n\\tthis._valid_morphs = this.computeValidMorphs( this._valid_morphs, morph_RI.mesh );\\r\\n\\r\\n\\t//grab the RI created previously and modified\\r\\n\\t//this.applyMorphTargets( last_RI );\\r\\n\\r\\n\\tif(this.mode === MorphDeformer.AUTOMATIC )\\r\\n\\t{\\r\\n\\t\\tif( this._morph_texture_supported === undefined )\\r\\n\\t\\t\\tthis._morph_texture_supported = (gl.extensions[\\\"OES_texture_float\\\"] !== undefined && gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 1);\\r\\n\\r\\n\\t\\tif( this._valid_morphs.length == 0 && !MorphDeformer.force_GPU )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif( this._valid_morphs.length <= MorphDeformer.max_supported_morph_targets_using_streams ) //use GPU\\r\\n\\t\\t\\tthis.applyMorphTargetsByGPU( morph_RI, this._valid_morphs );\\r\\n\\t\\telse if( this._morph_texture_supported ) //use GPU with textures\\r\\n\\t\\t\\tthis.applyMorphUsingTextures( morph_RI, this._valid_morphs );\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis.applyMorphBySoftware( morph_RI, this._valid_morphs );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tswitch( this.mode )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase MorphDeformer.STREAMS: this.applyMorphTargetsByGPU( morph_RI, this._valid_morphs ); break;\\r\\n\\t\\t\\tcase MorphDeformer.TEXTURES: this.applyMorphUsingTextures( morph_RI, this._valid_morphs ); break;\\r\\n\\t\\t\\tdefault: this.applyMorphBySoftware( morph_RI, this._valid_morphs ); break;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//returns a list of the morph targets that have some weight and with a mesh that is loaded\\r\\nMorphDeformer.prototype.computeValidMorphs = function( valid_morphs, base_mesh )\\r\\n{\\r\\n\\tvalid_morphs = valid_morphs || [];\\r\\n\\tvalid_morphs.length = 0;\\r\\n\\r\\n\\tif(!base_mesh)\\r\\n\\t\\treturn valid_morphs;\\r\\n\\r\\n\\t//sort by weight\\r\\n\\tvar morph_targets = this.morph_targets.concat();\\r\\n\\tmorph_targets.sort( function(a,b) { return Math.abs(b.weight) - Math.abs(a.weight); } );\\r\\n\\r\\n\\t//collect\\r\\n\\tfor(var i = 0; i < morph_targets.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar morph = morph_targets[i];\\r\\n\\t\\tif(!morph.mesh || Math.abs(morph.weight) < 0.001)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar morph_mesh = LS.ResourcesManager.resources[ morph.mesh ];\\r\\n\\t\\tif(!morph_mesh || morph_mesh.constructor !== GL.Mesh)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(!morph_mesh.info)\\r\\n\\t\\t\\tmorph_mesh.info = {};\\r\\n\\t\\tmorph_mesh.info.morph_target_from = base_mesh.filename;\\r\\n\\t\\tvalid_morphs.push( { name: morph.mesh, weight: morph.weight, mesh: morph_mesh } );\\r\\n\\t}\\r\\n\\r\\n\\treturn valid_morphs;\\r\\n}\\r\\n\\r\\n//add to the RI the info to apply the morphs using streams in the GPU\\r\\nMorphDeformer.prototype.applyMorphTargetsByGPU = function( RI, valid_morphs )\\r\\n{\\r\\n\\tvar base_mesh = RI.mesh;\\r\\n\\r\\n\\tvar base_vertices_buffer = base_mesh.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tvar streams_code = \\\"\\\";\\r\\n\\tvar morphs_buffers = {};\\r\\n\\tvar morphs_weights = [];\\r\\n\\r\\n\\t//collect (max 4 if using streams)\\r\\n\\tfor(var i = 0; i < valid_morphs.length && i < 4; ++i)\\r\\n\\t{\\r\\n\\t\\tvar morph = valid_morphs[i];\\r\\n\\t\\tvar morph_mesh = morph.mesh;\\r\\n\\r\\n\\t\\tvar vertices_buffer = morph_mesh.vertexBuffers[\\\"vertices\\\"];\\r\\n\\t\\tif(!vertices_buffer || vertices_buffer.data.length != base_vertices_buffer.data.length)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tvar normals_buffer = morph_mesh.vertexBuffers[\\\"normals\\\"];\\r\\n\\t\\tif(!normals_buffer)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tvar vertices_cloned = vertices_buffer.clone(true);\\r\\n\\t\\tvar normals_cloned = normals_buffer.clone(true);\\r\\n\\t\\tvertices_cloned.attribute = null;\\r\\n\\t\\tnormals_cloned.attribute = null;\\r\\n\\r\\n\\t\\tmorphs_buffers[\\\"a_vertex_morph\\\" + i ] = vertices_cloned;\\r\\n\\t\\tmorphs_buffers[\\\"a_normal_morph\\\" + i ] = normals_cloned;\\r\\n\\r\\n\\t\\tmorphs_weights.push( morph.weight );\\r\\n\\t}\\r\\n\\r\\n\\t//add buffers\\r\\n\\tRI.vertex_buffers = {};\\r\\n\\tfor(var i in base_mesh.vertexBuffers)\\r\\n\\t\\tRI.vertex_buffers[i] = base_mesh.vertexBuffers[i];\\r\\n\\tfor(var i in morphs_buffers)\\r\\n\\t\\tRI.vertex_buffers[i] = morphs_buffers[i];\\r\\n\\r\\n\\tif(RI.samplers[ LS.Renderer.MORPHS_TEXTURE_SLOT ])\\r\\n\\t{\\r\\n\\t\\tdelete RI.uniforms[\\\"u_morph_vertices_texture\\\"];\\r\\n\\t\\tdelete RI.uniforms[\\\"u_morph_normals_texture\\\"];\\r\\n\\t\\tRI.samplers[ LS.Renderer.MORPHS_TEXTURE_SLOT ] = null;\\r\\n\\t\\tRI.samplers[ LS.Renderer.MORPHS_TEXTURE2_SLOT ] = null;\\r\\n\\t}\\r\\n\\r\\n\\tvar weights = this._stream_weights;\\r\\n\\tif( !weights.fill ) //is an Array?\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < weights.length; ++i)\\r\\n\\t\\t\\tweights[i] = 0;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tweights.fill(0); //fill first because morphs_weights could have zero length\\r\\n\\tweights.set( morphs_weights );\\r\\n\\tRI.uniforms[\\\"u_morph_weights\\\"] = weights;\\r\\n\\tRI.uniforms[\\\"u_morph_info\\\"] = this.delta_meshes ? 1 : 0;\\r\\n\\r\\n\\t//SHADER BLOCK\\r\\n\\tRI.addShaderBlock( MorphDeformer.shader_block ); //global\\r\\n\\tRI.addShaderBlock( LS.MorphDeformer.morphing_streams_block, this._uniforms );\\r\\n\\tRI.removeShaderBlock( LS.MorphDeformer.morphing_texture_block );\\r\\n}\\r\\n\\r\\nMorphDeformer.prototype.applyMorphUsingTextures = function( RI, valid_morphs )\\r\\n{\\r\\n\\tvar base_mesh = RI.mesh;\\r\\n\\tvar base_vertices_buffer = base_mesh.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tvar base_normals_buffer = base_mesh.vertexBuffers[\\\"normals\\\"];\\r\\n\\r\\n\\t//create textures for the base mesh\\r\\n\\tif(!base_vertices_buffer._texture)\\r\\n\\t\\tbase_vertices_buffer._texture = this.createGeometryTexture( base_vertices_buffer );\\r\\n\\tif(!base_normals_buffer._texture)\\r\\n\\t\\tbase_normals_buffer._texture = this.createGeometryTexture( base_normals_buffer );\\r\\n\\r\\n\\t//LS.RM.textures[\\\":debug_base_vertex\\\"] = base_vertices_buffer._texture;\\r\\n\\t//LS.RM.textures[\\\":debug_base_normal\\\"] = base_normals_buffer._texture;\\r\\n\\r\\n\\r\\n\\tvar morphs_textures = [];\\r\\n\\r\\n\\t//create the texture container where all will be merged\\r\\n\\tif(!this._morphtarget_vertices_texture || this._morphtarget_vertices_texture.height != base_vertices_buffer._texture.height )\\r\\n\\t{\\r\\n\\t\\tthis._morphtarget_vertices_texture = new GL.Texture( base_vertices_buffer._texture.width, base_vertices_buffer._texture.height, { format: gl.RGB, type: gl.FLOAT, filter: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE, no_flip: true });\\r\\n\\t\\tthis._morphtarget_normals_texture = new GL.Texture( base_normals_buffer._texture.width, base_normals_buffer._texture.height, { format: gl.RGB, type: gl.FLOAT, filter: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE, no_flip: true });\\r\\n\\r\\n\\t\\t//used in the shader\\r\\n\\t\\tthis._texture_size = vec4.fromValues( this._morphtarget_vertices_texture.width, this._morphtarget_vertices_texture.height, \\r\\n\\t\\t\\t1 / this._morphtarget_vertices_texture.width, 1 / this._morphtarget_vertices_texture.height );\\r\\n\\r\\n\\t\\t//LS.RM.textures[\\\":debug_morph_vertex\\\"] = this._morphtarget_vertices_texture;\\r\\n\\t\\t//LS.RM.textures[\\\":debug_morph_normal\\\"] = this._morphtarget_normals_texture;\\r\\n\\t}\\r\\n\\r\\n\\t//prepare morph targets\\r\\n\\tfor(var i = 0; i < valid_morphs.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar morph = valid_morphs[i];\\r\\n\\t\\tvar morph_mesh = morph.mesh;\\r\\n\\r\\n\\t\\tvar vertices_buffer = morph_mesh.vertexBuffers[\\\"vertices\\\"];\\r\\n\\t\\tif(!vertices_buffer || vertices_buffer.data.length != base_vertices_buffer.data.length)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tvar normals_buffer = morph_mesh.vertexBuffers[\\\"normals\\\"];\\r\\n\\t\\tif(!normals_buffer)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif(!vertices_buffer._texture)\\r\\n\\t\\t\\tvertices_buffer._texture = this.createGeometryTexture( vertices_buffer );\\r\\n\\t\\tif(!normals_buffer._texture)\\r\\n\\t\\t\\tnormals_buffer._texture = this.createGeometryTexture( normals_buffer );\\r\\n\\r\\n\\t\\t//LS.RM.textures[\\\":debug_morph_vertex_\\\" + i] = vertices_buffer._texture;\\r\\n\\t\\t//LS.RM.textures[\\\":debug_morph_normal_\\\" + i] = normals_buffer._texture;\\r\\n\\t\\tmorphs_textures.push( { weight: morph.weight, vertices: vertices_buffer._texture, normals: normals_buffer._texture } );\\r\\n\\t}\\r\\n\\r\\n\\t//accumulate all morphs targets in two textures that contains the final vertex and final normal\\r\\n\\r\\n\\tvar shader = this.getMorphTextureShader();\\r\\n\\tshader.uniforms({ u_base_texture: 0, u_morph_texture: 1 });\\r\\n\\r\\n\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\tgl.enable( gl.BLEND );\\r\\n\\tgl.blendFunc( gl.ONE, gl.ONE );\\r\\n\\r\\n\\tbase_vertices_buffer._texture.bind(0);\\r\\n\\tvar quad_mesh = GL.Mesh.getScreenQuad();\\r\\n\\r\\n\\tthis._morphtarget_vertices_texture.drawTo( function(){\\r\\n\\t\\tgl.clearColor( 0,0,0,0 );\\r\\n\\t\\tgl.clear( gl.COLOR_BUFFER_BIT );\\r\\n\\t\\tfor(var i = 0; i < morphs_textures.length; ++i )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar stream_texture = morphs_textures[i].vertices;\\r\\n\\t\\t\\tstream_texture.bind(1);\\r\\n\\t\\t\\tshader.uniforms({ u_weight: morphs_textures[i].weight });\\r\\n\\t\\t\\tshader.draw( quad_mesh, gl.TRIANGLES );\\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\tbase_normals_buffer._texture.bind(0);\\r\\n\\r\\n\\tthis._morphtarget_normals_texture.drawTo( function(){\\r\\n\\t\\tgl.clearColor( 0,0,0,0 );\\r\\n\\t\\tgl.clear( gl.COLOR_BUFFER_BIT );\\r\\n\\t\\tfor(var i = 0; i < morphs_textures.length; ++i )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar stream_texture = morphs_textures[i].normals;\\r\\n\\t\\t\\tstream_texture.bind(1);\\r\\n\\t\\t\\tshader.uniforms({ u_weight: morphs_textures[i].weight });\\r\\n\\t\\t\\tshader.draw( quad_mesh, gl.TRIANGLES );\\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\tgl.disable( gl.BLEND );\\r\\n\\r\\n\\t//create sequence numbers buffer of the same size\\r\\n\\tvar num_verts = base_vertices_buffer.data.length / 3;\\r\\n\\tif(!this._ids_buffer || this._ids_buffer.data.length != num_verts )\\r\\n\\t{\\r\\n\\t\\tvar ids_data = new Float32Array( num_verts );\\r\\n\\t\\tfor(var i = 0; i < num_verts; ++i)\\r\\n\\t\\t\\tids_data[i] = i;\\r\\n\\t\\tthis._ids_buffer = new GL.Buffer( gl.ARRAY_BUFFER, ids_data, 1, gl.STATIC_DRAW );\\r\\n\\t\\tthis._ids_buffer.attribute = \\\"a_morphing_ids\\\";\\r\\n\\t}\\r\\n\\r\\n\\t//modify the RI to have the displacement texture\\r\\n\\tRI.uniforms[\\\"u_morph_vertices_texture\\\"] = LS.Renderer.MORPHS_TEXTURE_SLOT;\\r\\n\\tRI.samplers[ LS.Renderer.MORPHS_TEXTURE_SLOT ] = this._morphtarget_vertices_texture;\\r\\n\\r\\n\\tRI.uniforms[\\\"u_morph_normals_texture\\\"] = LS.Renderer.MORPHS_TEXTURE2_SLOT;\\r\\n\\tRI.samplers[ LS.Renderer.MORPHS_TEXTURE2_SLOT ] = this._morphtarget_normals_texture;\\r\\n\\r\\n\\tRI.uniforms[\\\"u_morph_texture_size\\\"] = this._texture_size;\\r\\n\\r\\n\\t//add the ids (the texture with 0,1,2, 3,4,5, ...)\\r\\n\\tRI.vertex_buffers[\\\"a_morphing_ids\\\"] = this._ids_buffer;\\r\\n\\r\\n\\t//SHADER BLOCK\\r\\n\\tRI.addShaderBlock( MorphDeformer.shader_block );\\r\\n\\tRI.addShaderBlock( LS.MorphDeformer.morphing_texture_block, { \\r\\n\\t\\t\\t\\tu_morph_vertices_texture: LS.Renderer.MORPHS_TEXTURE_SLOT, \\r\\n\\t\\t\\t\\tu_morph_normals_texture: LS.Renderer.MORPHS_TEXTURE2_SLOT, \\r\\n\\t\\t\\t\\tu_morph_texture_size: this._texture_size \\r\\n\\t\\t\\t});\\r\\n\\tRI.removeShaderBlock( LS.MorphDeformer.morphing_streams_block );\\r\\n}\\r\\n\\r\\n\\r\\nMorphDeformer.prototype.disableMorphingGPU = function( RI )\\r\\n{\\r\\n\\tif( !RI )\\r\\n\\t\\treturn;\\r\\n\\t\\r\\n\\tif( RI.samplers[ LS.Renderer.MORPHS_TEXTURE_SLOT ] )\\r\\n\\t{\\r\\n\\t\\tRI.samplers[ LS.Renderer.MORPHS_TEXTURE_SLOT ] = null;\\r\\n\\t\\tRI.samplers[ LS.Renderer.MORPHS_TEXTURE2_SLOT ] = null;\\r\\n\\t\\tdelete RI.uniforms[\\\"u_morph_vertices_texture\\\"];\\r\\n\\t\\tdelete RI.uniforms[\\\"u_morph_normals_texture\\\"];\\r\\n\\t}\\r\\n\\r\\n\\tRI.removeShaderBlock( LS.MorphDeformer.shader_block );\\r\\n\\tRI.removeShaderBlock( LS.MorphDeformer.morphing_streams_block );\\r\\n\\tRI.removeShaderBlock( LS.MorphDeformer.morphing_texture_block );\\r\\n}\\r\\n\\r\\nMorphDeformer.prototype.applyMorphBySoftware = function( RI, valid_morphs )\\r\\n{\\r\\n\\tvar base_mesh = RI.mesh;\\r\\n\\tvar base_vertices_buffer = base_mesh.vertexBuffers[\\\"vertices\\\"];\\r\\n\\r\\n\\tthis.disableMorphingGPU( RI ); //disable GPU version\\r\\n\\r\\n\\tvar key = \\\"\\\"; //used to avoid computing the mesh every frame\\r\\n\\r\\n\\t//collect\\r\\n\\tfor(var i = 0; i < valid_morphs.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar morph = valid_morphs[i];\\r\\n\\t\\tkey += morph.name + \\\"|\\\" + morph.weight.toFixed(2) + \\\"|\\\";\\r\\n\\t}\\r\\n\\r\\n\\t//to avoid recomputing if nothing has changed\\r\\n\\tif(key == this._last_key)\\r\\n\\t{\\r\\n\\t\\t//change the RI\\r\\n\\t\\tif(this._final_vertices_buffer)\\r\\n\\t\\t\\tRI.vertex_buffers[\\\"vertices\\\"] = this._final_vertices_buffer;\\r\\n\\t\\tif(this._final_normals_buffer)\\r\\n\\t\\t\\tRI.vertex_buffers[\\\"normals\\\"] = this._final_normals_buffer;\\r\\n\\t\\treturn; \\r\\n\\t}\\r\\n\\tthis._last_key = key;\\r\\n\\r\\n\\tvar base_vertices_buffer = base_mesh.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tvar base_vertices = base_vertices_buffer.data;\\r\\n\\tvar base_normals_buffer = base_mesh.vertexBuffers[\\\"normals\\\"];\\r\\n\\tvar base_normals = base_normals_buffer.data;\\r\\n\\r\\n\\t//create final buffers\\r\\n\\tif(!this._final_vertices || this._final_vertices.length != base_vertices.length )\\r\\n\\t{\\r\\n\\t\\tthis._final_vertices = new Float32Array( base_vertices.length );\\r\\n\\t\\tthis._final_vertices_buffer = new GL.Buffer( gl.ARRAY_BUFFER, this._final_vertices, 3, gl.STREAM_DRAW );\\r\\n\\t\\tthis._final_vertices_buffer.attribute = \\\"a_vertex\\\";\\r\\n\\t}\\r\\n\\r\\n\\tif(!this._final_normals || this._final_normals.length != base_normals.length )\\r\\n\\t{\\r\\n\\t\\tthis._final_normals = new Float32Array( base_normals.length );\\r\\n\\t\\tthis._final_normals_buffer = new GL.Buffer( gl.ARRAY_BUFFER, this._final_normals, 3, gl.STREAM_DRAW );\\r\\n\\t\\tthis._final_normals_buffer.attribute = \\\"a_normal\\\";\\r\\n\\t}\\r\\n\\r\\n\\tvar vertices = this._final_vertices;\\r\\n\\tvar normals = this._final_normals;\\r\\n\\r\\n\\tvertices.set( base_vertices );\\r\\n\\tnormals.set( base_normals );\\r\\n\\r\\n\\tvar morphs_vertices = [];\\r\\n\\tvar morphs_normals = [];\\r\\n\\tvar morphs_weights = [];\\r\\n\\tvar num_morphs = valid_morphs.length;\\r\\n\\r\\n\\tfor(var i = 0; i < valid_morphs.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar morph = valid_morphs[i];\\r\\n\\t\\tmorphs_vertices.push( morph.mesh.vertexBuffers[\\\"vertices\\\"].data );\\r\\n\\t\\tmorphs_normals.push( morph.mesh.vertexBuffers[\\\"normals\\\"].data );\\r\\n\\t\\tmorphs_weights.push( morph.weight );\\r\\n\\t}\\r\\n\\r\\n\\t//fill them \\r\\n\\tif(this.delta_meshes)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0, l = vertices.length; i < l; i += 3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar v = vertices.subarray(i,i+3);\\r\\n\\t\\t\\tvar n = normals.subarray(i,i+3);\\r\\n\\r\\n\\t\\t\\tfor(var j = 0; j < num_morphs; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar m_v = morphs_vertices[j];\\r\\n\\t\\t\\t\\tvar m_n = morphs_normals[j];\\r\\n\\t\\t\\t\\tvar w = morphs_weights[j];\\r\\n\\t\\t\\t\\tv[0] += m_v[i]* w;\\r\\n\\t\\t\\t\\tv[1] += m_v[i+1] * w;\\r\\n\\t\\t\\t\\tv[2] += m_v[i+2] * w;\\r\\n\\t\\t\\t\\tn[0] += m_n[i] * w;\\r\\n\\t\\t\\t\\tn[1] += m_n[i+1] * w;\\r\\n\\t\\t\\t\\tn[2] += m_n[i+2] * w;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0, l = vertices.length; i < l; i += 3)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar v = vertices.subarray(i,i+3);\\r\\n\\t\\t\\tvar n = normals.subarray(i,i+3);\\r\\n\\r\\n\\t\\t\\tfor(var j = 0; j < num_morphs; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar m_v = morphs_vertices[j];\\r\\n\\t\\t\\t\\tvar m_n = morphs_normals[j];\\r\\n\\t\\t\\t\\tvar w = morphs_weights[j];\\r\\n\\t\\t\\t\\tv[0] += (m_v[i] - base_vertices[i]) * w;\\r\\n\\t\\t\\t\\tv[1] += (m_v[i+1] - base_vertices[i+1]) * w;\\r\\n\\t\\t\\t\\tv[2] += (m_v[i+2] - base_vertices[i+2]) * w;\\r\\n\\t\\t\\t\\tn[0] += (m_n[i] - base_normals[i]) * w;\\r\\n\\t\\t\\t\\tn[1] += (m_n[i+1] - base_normals[i+1]) * w;\\r\\n\\t\\t\\t\\tn[2] += (m_n[i+2] - base_normals[i+2]) * w;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tthis._final_vertices_buffer.upload( gl.STREAM_DRAW );\\r\\n\\tthis._final_normals_buffer.upload( gl.STREAM_DRAW );\\r\\n\\r\\n\\t//change the RI\\r\\n\\tRI.vertex_buffers[\\\"vertices\\\"] = this._final_vertices_buffer;\\r\\n\\tRI.vertex_buffers[\\\"normals\\\"] = this._final_normals_buffer;\\r\\n\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n\\r\\nMorphDeformer._blend_shader_fragment_code = \\\"\\\\n\\\\\\r\\n\\tprecision highp float;\\\\n\\\\\\r\\n\\tuniform sampler2D u_base_texture;\\\\n\\\\\\r\\n\\tuniform sampler2D u_morph_texture;\\\\n\\\\\\r\\n\\tuniform float u_weight;\\\\n\\\\\\r\\n\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tgl_FragColor = u_weight * ( texture2D(u_morph_texture, v_coord) - texture2D(u_base_texture, v_coord) );\\\\n\\\\\\r\\n\\t\\tgl_FragColor.w = 1.0;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nMorphDeformer._delta_blend_shader_fragment_code = \\\"\\\\n\\\\\\r\\n\\tprecision highp float;\\\\n\\\\\\r\\n\\tuniform sampler2D u_morph_texture;\\\\n\\\\\\r\\n\\tuniform float u_weight;\\\\n\\\\\\r\\n\\tvarying vec2 v_coord;\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tgl_FragColor = u_weight * texture2D(u_morph_texture, v_coord);\\\\n\\\\\\r\\n\\t\\tgl_FragColor.w = 1.0;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nMorphDeformer.prototype.getMorphTextureShader = function()\\r\\n{\\r\\n\\tif(this.delta_meshes)\\r\\n\\t{\\r\\n\\t\\tif(!this._delta_blend_shader)\\r\\n\\t\\t\\tthis._delta_blend_shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, MorphDeformer._delta_blend_shader_fragment_code );\\r\\n\\t\\treturn this._delta_blend_shader;\\r\\n\\t}\\r\\n\\r\\n\\tif(!this._blend_shader)\\r\\n\\t\\tthis._blend_shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, MorphDeformer._blend_shader_fragment_code );\\r\\n\\treturn this._blend_shader;\\r\\n}\\r\\n\\r\\n//transfers the geometry to a texture\\r\\nMorphDeformer.prototype.createGeometryTexture = function( data_buffer, texture )\\r\\n{\\r\\n\\tvar stream_data = data_buffer.data;\\r\\n\\tvar buffer = stream_data.buffer;\\r\\n\\r\\n\\tvar max_texture_size = gl.getParameter( gl.MAX_TEXTURE_SIZE );\\r\\n\\r\\n\\tvar num_floats = stream_data.length; \\r\\n\\tvar num_vertex = num_floats / 3;\\r\\n\\tvar width = Math.min( max_texture_size, num_vertex );\\r\\n\\tvar height = Math.ceil( num_vertex / width );\\r\\n\\r\\n\\tvar buffer_padded = new Float32Array( width * height * 3 );\\r\\n\\tbuffer_padded.set( stream_data );\\r\\n\\tif(!texture || texture.width != width || texture.height != height )\\r\\n\\t\\ttexture = new GL.Texture( width, height, { format: gl.RGB, type: gl.FLOAT, filter: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE, pixel_data: buffer_padded, no_flip: true });\\r\\n\\telse\\r\\n\\t\\ttexture.uploadData( buffer_padded );\\r\\n\\treturn texture;\\r\\n}\\r\\n\\r\\n//in case the textures has been modyfied\\r\\nMorphDeformer.prototype.recomputeGeometryTextures = function()\\r\\n{\\r\\n\\tvar RI = this._last_RI;\\r\\n\\tif(!RI)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar base_mesh = RI.mesh;\\r\\n\\tvar base_vertices_buffer = base_mesh.vertexBuffers[\\\"vertices\\\"];\\r\\n\\tvar base_normals_buffer = base_mesh.vertexBuffers[\\\"normals\\\"];\\r\\n\\r\\n\\t//create textures for the base mesh\\r\\n\\tbase_vertices_buffer._texture = this.createGeometryTexture( base_vertices_buffer, base_vertices_buffer._texture );\\r\\n\\tbase_normals_buffer._texture = this.createGeometryTexture( base_normals_buffer, base_normals_buffer._texture );\\r\\n\\r\\n\\tvar valid_morphs = this._valid_morphs;\\r\\n\\tif(!valid_morphs)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < valid_morphs.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar morph = valid_morphs[i];\\r\\n\\t\\tvar morph_mesh = morph.mesh;\\r\\n\\r\\n\\t\\tvar vertices_buffer = morph_mesh.vertexBuffers[\\\"vertices\\\"];\\r\\n\\t\\tif( vertices_buffer && vertices_buffer._texture )\\r\\n\\t\\t\\tthis.createGeometryTexture( vertices_buffer, vertices_buffer._texture );\\r\\n\\r\\n\\t\\tvar normals_buffer = morph_mesh.vertexBuffers[\\\"normals\\\"];\\r\\n\\t\\tif( normals_buffer && normals_buffer._texture )\\r\\n\\t\\t\\tthis.createGeometryTexture( normals_buffer, normals_buffer._texture );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the index of the morph target that uses this mesh\\r\\n* @method getMorphIndex\\r\\n* @param {String} mesh_name the name (filename) of the mesh in the morph target\\r\\n* @return {number} the index\\r\\n*/\\r\\nMorphDeformer.prototype.getMorphIndex = function(mesh_name)\\r\\n{\\r\\n\\tfor(var i = 0; i < this.morph_targets.length; ++i)\\r\\n\\t\\tif (this.morph_targets[i].mesh == mesh_name )\\r\\n\\t\\t\\treturn i;\\r\\n\\treturn -1;\\r\\n}\\r\\n\\r\\n/**\\r\\n* sets the mesh for a morph target\\r\\n* @method setMorphMesh\\r\\n* @param {number} index the index of the morph target\\r\\n* @param {String} mesh the mesh resource\\r\\n*/\\r\\nMorphDeformer.prototype.setMorphMesh = function(index, value)\\r\\n{\\r\\n\\tif(index >= this.morph_targets.length)\\r\\n\\t\\treturn;\\r\\n\\tthis.morph_targets[index].mesh = value;\\r\\n}\\r\\n\\r\\n/**\\r\\n* sets the weight for a morph target\\r\\n* @method setMorphWeight\\r\\n* @param {number} index the index of the morph target\\r\\n* @param {number} weight the weight\\r\\n*/\\r\\nMorphDeformer.prototype.setMorphWeight = function(index, value)\\r\\n{\\r\\n\\tif(index >= this.morph_targets.length)\\r\\n\\t\\treturn;\\r\\n\\tthis.morph_targets[index].weight = value;\\r\\n}\\r\\n\\r\\nMorphDeformer.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif(path[0] != \\\"morphs\\\")\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(path.length == 1)\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tnode: this._root,\\r\\n\\t\\t\\ttarget: this.morph_targets,\\r\\n\\t\\t\\ttype: \\\"object\\\"\\r\\n\\t\\t};\\r\\n\\r\\n\\tvar num = parseInt( path[1] );\\r\\n\\tif(num >= this.morph_targets.length)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar varname = path[2];\\r\\n\\tif(varname != \\\"mesh\\\" && varname != \\\"weight\\\")\\r\\n\\t\\treturn;\\r\\n\\r\\n\\treturn {\\r\\n\\t\\tnode: this._root,\\r\\n\\t\\ttarget: this.morph_targets[num],\\r\\n\\t\\tname: varname,\\r\\n\\t\\tvalue: this.morph_targets[num][ varname ] !== undefined ? this.morph_targets[num][ varname ] : null,\\r\\n\\t\\ttype: varname == \\\"mesh\\\" ? \\\"mesh\\\" : \\\"number\\\"\\r\\n\\t};\\r\\n}\\r\\n\\r\\nMorphDeformer.prototype.setPropertyValueFromPath = function( path, value, offset )\\r\\n{\\r\\n\\toffset = offset || 0;\\r\\n\\r\\n\\tif( path.length < (offset+1) )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( path[offset] != \\\"morphs\\\" )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar num = parseInt( path[offset+1] );\\r\\n\\tif(num >= this.morph_targets.length)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar varname = path[offset+2];\\r\\n\\tthis.morph_targets[num][ varname ] = value;\\r\\n}\\r\\n\\r\\n//used for graphs\\r\\nMorphDeformer.prototype.setProperty = function(name, value)\\r\\n{\\r\\n\\tif( name == \\\"enabled\\\" )\\r\\n\\t\\tthis.enabled = value;\\r\\n\\telse if( name.substr(0,5) == \\\"morph\\\" )\\r\\n\\t{\\r\\n\\t\\tname = name.substr(5);\\r\\n\\t\\tvar t = name.split(\\\"_\\\");\\r\\n\\t\\tvar num = parseInt( t[0] );\\r\\n\\t\\tif( num < this.morph_targets.length )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( t[1] == \\\"weight\\\" )\\r\\n\\t\\t\\t\\tthis.morph_targets[ num ].weight = value;\\r\\n\\t\\t\\telse if( t[1] == \\\"mesh\\\" )\\r\\n\\t\\t\\t\\tthis.morph_targets[ num ].mesh = value;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if( name == \\\"weights\\\" )\\r\\n\\t\\tthis.weights = value;\\r\\n\\telse if( name == \\\"name_weights\\\" )\\r\\n\\t\\tthis.name_weights = value;\\r\\n}\\r\\n\\r\\nMorphDeformer.prototype.getProperty = function(name)\\r\\n{\\r\\n\\tif(name.substr(0,5) == \\\"morph\\\" && name.length > 5)\\r\\n\\t{\\r\\n\\t\\tvar t = name.substr(5).split(\\\"_\\\");\\r\\n\\t\\tvar index = Number(t[0]);\\r\\n\\t\\tvar morph = this.morph_targets[ index ];\\r\\n\\t\\tif(morph)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(t[1] == \\\"mesh\\\")\\r\\n\\t\\t\\t\\treturn morph.mesh;\\r\\n\\t\\t\\telse if(t[1] == \\\"weight\\\")\\r\\n\\t\\t\\t\\treturn morph.weight;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\treturn morph;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\nMorphDeformer.prototype.getPropertiesInfo = function()\\r\\n{\\r\\n\\tvar properties = {\\r\\n\\t\\tenabled: \\\"boolean\\\",\\r\\n\\t\\tweights: \\\"array\\\",\\r\\n\\t\\tname_weights: \\\"object\\\"\\r\\n\\t};\\r\\n\\r\\n\\tfor(var i = 0; i < this.morph_targets.length; i++)\\r\\n\\t{\\r\\n\\t\\tproperties[ \\\"morph\\\" + i + \\\"_weight\\\" ] = \\\"number\\\";\\r\\n\\t\\t//properties[ \\\"morph\\\" + i + \\\"_mesh\\\" ] = \\\"Mesh\\\";\\r\\n\\t}\\r\\n\\r\\n\\treturn properties;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the base mesh on which the morph targets will be applied\\r\\n* @method getBaseMesh\\r\\n*/\\r\\nMorphDeformer.prototype.getBaseMesh = function()\\r\\n{\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn null;\\r\\n\\tif( this._last_base_mesh )\\r\\n\\t\\treturn this._last_base_mesh;\\r\\n\\tvar mesh_renderer = this._root.getComponent( LS.Components.MeshRenderer );\\r\\n\\tif( mesh_renderer )\\r\\n\\t\\treturn LS.ResourcesManager.resources[ mesh_renderer.mesh ];\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Removes innecesary morph targets and removes data from mesh that is already in the base mesh (uvs and indices)\\r\\n* @method optimizeMorphTargets\\r\\n*/\\r\\nMorphDeformer.prototype.optimizeMorphTargets = function()\\r\\n{\\r\\n\\t//base mesh\\r\\n\\tvar base_mesh = this.getBaseMesh();\\r\\n\\r\\n\\tvar morph_targets = this.morph_targets.concat();\\r\\n\\r\\n\\tfor(var i = 0; i < morph_targets.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar morph = morph_targets[i];\\r\\n\\t\\tvar mesh = LS.ResourcesManager.meshes[ morph.mesh ];\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t\\r\\n\\t\\t//remove data not used \\r\\n\\t\\tmesh.removeVertexBuffer(\\\"coords\\\", true);\\r\\n\\t\\tmesh.removeIndexBuffer(\\\"triangles\\\", true);\\r\\n\\t\\tmesh.removeIndexBuffer(\\\"wireframe\\\", true);\\r\\n\\r\\n\\t\\t//compute difference\\r\\n\\t\\tif( base_mesh )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar diff = MorphDeformer.computeMeshDifference( base_mesh, mesh );\\r\\n\\t\\t\\tif( diff < 0.1 ) //too similar\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar mesh_fullpath = mesh.fullpath || mesh.filename;\\r\\n\\t\\t\\t\\tconsole.log(\\\"morph target is too similar to base mesh, removing it: \\\" + mesh_fullpath );\\r\\n\\t\\t\\t\\tvar index = this.morph_targets.indexOf( morph );\\r\\n\\t\\t\\t\\tthis.morph_targets.splice( index,1 );\\r\\n\\t\\t\\t\\tLS.ResourcesManager.unregisterResource( mesh_fullpath );\\r\\n\\t\\t\\t\\tvar container_fullpath = mesh.from_pack || mesh.from_prefab;\\r\\n\\t\\t\\t\\tif( container_fullpath )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar container = LS.ResourcesManager.resources[ container_fullpath ];\\r\\n\\t\\t\\t\\t\\tif(container)\\r\\n\\t\\t\\t\\t\\t\\tcontainer.removeResource( mesh_fullpath );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLS.ResourcesManager.resourceModified( mesh );\\r\\n\\t}\\r\\n\\r\\n\\tconsole.log(\\\"Morph targets optimized\\\");\\r\\n}\\r\\n\\r\\n//computes the difference between to meshes, used to detect useless morph targets\\r\\nMorphDeformer.computeMeshDifference = function( mesh_a, mesh_b )\\r\\n{\\r\\n\\tif(!mesh_a || !mesh_b || !mesh_a.vertexBuffers[\\\"vertices\\\"] || !mesh_b.vertexBuffers[\\\"vertices\\\"])\\r\\n\\t\\treturn 0;\\r\\n\\r\\n\\tvar vertices_a = mesh_a.vertexBuffers[\\\"vertices\\\"].data;\\r\\n\\tvar vertices_b = mesh_b.vertexBuffers[\\\"vertices\\\"].data;\\r\\n\\r\\n\\tif( !vertices_a || !vertices_b || vertices_a.length != vertices_b.length )\\r\\n\\t\\treturn 0;\\r\\n\\r\\n\\tvar diff = 0;\\r\\n\\tfor( var i = 0; i < vertices_a.length; i+=3 )\\r\\n\\t\\tdiff += vec3.distance( vertices_a.subarray(i,i+3), vertices_b.subarray(i,i+3) );\\r\\n\\treturn diff;\\r\\n}\\r\\n\\r\\nMorphDeformer.prototype.onInspectNode = function( inspector, graphnode )\\r\\n{\\r\\n\\tvar that = this;\\r\\n\\tinspector.addButton(null,\\\"Add weights' inputs\\\",{ callback: function(){\\r\\n\\t\\tfor(var i = 0; i < that.morph_targets.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar morph = that.morph_targets[i];\\r\\n\\t\\t\\tif(graphnode.findInputSlot(\\\"morph_\\\" + i + \\\"_weight\\\") == -1)\\r\\n\\t\\t\\t\\tgraphnode.addInput(\\\"morph_\\\" + i + \\\"_weight\\\",\\\"number\\\");\\r\\n\\t\\t}\\r\\n\\t\\tgraphnode.setDirtyCanvas(true);\\r\\n\\t}});\\r\\n}\\r\\n\\r\\nLS.registerComponent( MorphDeformer );\\r\\nLS.MorphDeformer = MorphDeformer;\\r\\n\\r\\n//SHADER BLOCKS ******************************************\\r\\n\\r\\nMorphDeformer.morph_streams_enabled_shader_code = \\\"\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t//max vertex attribs are 16 usually, so 10 are available after using 6 for V,N,UV,UV2,BW,BI\\\\n\\\\\\r\\n\\tattribute vec3 a_vertex_morph0;\\\\n\\\\\\r\\n\\tattribute vec3 a_normal_morph0;\\\\n\\\\\\r\\n\\tattribute vec3 a_vertex_morph1;\\\\n\\\\\\r\\n\\tattribute vec3 a_normal_morph1;\\\\n\\\\\\r\\n\\tattribute vec3 a_vertex_morph2;\\\\n\\\\\\r\\n\\tattribute vec3 a_normal_morph2;\\\\n\\\\\\r\\n\\tattribute vec3 a_vertex_morph3;\\\\n\\\\\\r\\n\\tattribute vec3 a_normal_morph3;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tuniform vec4 u_morph_weights;\\\\n\\\\\\r\\n\\tuniform float u_morph_info;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvoid applyMorphing( inout vec4 position, inout vec3 normal )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tvec3 original_vertex = vec3(0.0);\\\\n\\\\\\r\\n\\t\\tvec3 original_normal = vec3(0.0);\\\\n\\\\\\r\\n\\t\\tif( u_morph_info == 0.0 )\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\toriginal_vertex = position.xyz;\\\\n\\\\\\r\\n\\t\\t\\toriginal_normal = normal.xyz;\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tif(u_morph_weights[0] != 0.0)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tposition.xyz += (a_vertex_morph0 - original_vertex) * u_morph_weights[0]; normal.xyz += (a_normal_morph0 - original_normal) * u_morph_weights[0];\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\tif(u_morph_weights[1] != 0.0)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tposition.xyz += (a_vertex_morph1 - original_vertex) * u_morph_weights[1]; normal.xyz += (a_normal_morph1 - original_normal) * u_morph_weights[1];\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\tif(u_morph_weights[2] != 0.0)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tposition.xyz += (a_vertex_morph2 - original_vertex) * u_morph_weights[2]; normal.xyz += (a_normal_morph2 - original_normal) * u_morph_weights[2];\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\tif(u_morph_weights[3] != 0.0)\\\\n\\\\\\r\\n\\t\\t{\\\\n\\\\\\r\\n\\t\\t\\tposition.xyz += (a_vertex_morph3 - original_vertex) * u_morph_weights[3]; normal.xyz += (a_normal_morph3 - original_normal) * u_morph_weights[3];\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nMorphDeformer.morph_texture_enabled_shader_code = \\\"\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tattribute float a_morphing_ids;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tuniform sampler2D u_morph_vertices_texture;\\\\n\\\\\\r\\n\\tuniform sampler2D u_morph_normals_texture;\\\\n\\\\\\r\\n\\tuniform vec4 u_morph_texture_size;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tuniform vec4 u_morph_weights;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvoid applyMorphing( inout vec4 position, inout vec3 normal )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tvec2 coord;\\\\n\\\\\\r\\n\\t\\tcoord.x = ( mod( a_morphing_ids, u_morph_texture_size.x ) + 0.5 ) / u_morph_texture_size.x;\\\\n\\\\\\r\\n\\t\\tcoord.y = 1.0 - ( floor( a_morphing_ids / u_morph_texture_size.x ) + 0.5 ) / u_morph_texture_size.y;\\\\n\\\\\\r\\n\\t\\tposition.xyz += texture2D( u_morph_vertices_texture, coord ).xyz;\\\\n\\\\\\r\\n\\t\\tnormal.xyz += texture2D( u_morph_normals_texture, coord ).xyz;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nMorphDeformer.morph_enabled_shader_code = \\\"\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t#pragma shaderblock morphing_mode\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\n\\r\\nMorphDeformer.morph_disabled_shader_code = \\\"\\\\nvoid applyMorphing( inout vec4 position, inout vec3 normal ) {}\\\\n\\\";\\r\\n\\r\\n// ShaderBlocks used to inject to shader in runtime\\r\\nvar morphing_block = new LS.ShaderBlock(\\\"morphing\\\");\\r\\nmorphing_block.addCode( GL.VERTEX_SHADER, MorphDeformer.morph_enabled_shader_code, MorphDeformer.morph_disabled_shader_code );\\r\\nmorphing_block.register();\\r\\nMorphDeformer.shader_block = morphing_block;\\r\\n\\r\\nvar morphing_streams_block = new LS.ShaderBlock(\\\"morphing_streams\\\");\\r\\nmorphing_streams_block.defineContextMacros( { \\\"morphing_mode\\\": \\\"morphing_streams\\\"} );\\r\\nmorphing_streams_block.addCode( GL.VERTEX_SHADER, MorphDeformer.morph_streams_enabled_shader_code, MorphDeformer.morph_disabled_shader_code );\\r\\nmorphing_streams_block.register();\\r\\nMorphDeformer.morphing_streams_block = morphing_streams_block;\\r\\n\\r\\nvar morphing_texture_block = new LS.ShaderBlock(\\\"morphing_texture\\\");\\r\\nmorphing_texture_block.defineContextMacros( { \\\"morphing_mode\\\": \\\"morphing_texture\\\"} );\\r\\nmorphing_texture_block.addCode( GL.VERTEX_SHADER, MorphDeformer.morph_texture_enabled_shader_code, MorphDeformer.morph_disabled_shader_code );\\r\\nmorphing_texture_block.register();\\r\\nMorphDeformer.morphing_texture_block = morphing_texture_block;\\r\\n///@FILE:../src/components/skinDeformer.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* It applyes skinning to a RenderInstance created by another component (usually MeshRenderer)\\r\\n* Is in charge of gathering the bone nodes and adding to the RenderInstance the information needed to perform the skinning\\r\\n* It can do it using shader uniforms (simple way), a matrices texture (complex way), or by directly applying skinning by software (slow but well supported way)\\r\\n* It also allow to limit the bone search to specific nodes.\\r\\n*\\r\\n* @class SkinDeformer\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n*/\\r\\nfunction SkinDeformer( o )\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.search_bones_in_parent = true;\\r\\n\\tthis.skeleton_root_node = null;\\r\\n\\tthis.cpu_skinning = false;\\r\\n\\tthis.ignore_transform = true;\\r\\n\\r\\n\\tthis._mesh = null;\\r\\n\\tthis._last_bones = null;\\r\\n\\tthis._ris_skinned = [];\\r\\n\\tthis._skeleton = null; //used to overwrite the bones fetching\\r\\n\\t//this._skinning_mode = 0;\\r\\n\\r\\n\\t//check how many floats can we put in a uniform\\r\\n\\tif(!SkinDeformer._initialized && global.gl )\\r\\n\\t{\\r\\n\\t\\tSkinDeformer.num_supported_uniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );\\r\\n\\t\\tSkinDeformer.num_supported_textures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );\\r\\n\\t\\t//check if GPU skinning is supported\\r\\n\\t\\tif( SkinDeformer.num_supported_uniforms < SkinDeformer.MAX_BONES*3 && SkinDeformer.num_supported_textures == 0)\\r\\n\\t\\t\\tSkinDeformer.gpu_skinning_supported = false;\\r\\n\\t\\tSkinDeformer._initialized = true;\\r\\n\\t}\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n\\r\\n\\t//this._deformer = new LS.Deformer();\\r\\n}\\r\\n\\r\\nSkinDeformer.icon = \\\"mini-icon-stickman.png\\\";\\r\\n\\r\\nSkinDeformer.MAX_BONES = 64;\\r\\nSkinDeformer.MAX_TEXTURE_BONES = 128; //do not change this, hardcoded in the shader\\r\\nSkinDeformer.gpu_skinning_supported = true;\\r\\nSkinDeformer.icon = \\\"mini-icon-stickman.png\\\";\\r\\nSkinDeformer.apply_to_normals_by_software = false;\\r\\n\\r\\nSkinDeformer[\\\"@skeleton_root_node\\\"] = { type: LS.TYPES.SCENENODE };\\r\\n\\r\\nSkinDeformer.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tLEvent.bind(node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nSkinDeformer.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tLEvent.unbind(node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\n//returns the bone node taking into account the scoping of the component\\r\\nSkinDeformer.prototype.getBoneNode = function( name )\\r\\n{\\r\\n\\tvar root_node = this._root;\\r\\n\\tvar scene = root_node.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar node = null;\\r\\n\\r\\n\\tif( this.skeleton_root_node )\\r\\n\\t{\\r\\n\\t\\troot_node = scene.getNode( this.skeleton_root_node );\\r\\n\\t\\tif(root_node)\\r\\n\\t\\t\\treturn root_node.findNodeByName( name );\\r\\n\\t}\\r\\n\\telse if(this.search_bones_in_parent)\\r\\n\\t{\\r\\n\\t\\treturn root_node.parentNode.findNode( name );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\treturn scene.getNode( name );\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n//returns a reference to the global matrix of the bone\\r\\nSkinDeformer.prototype.getBoneMatrix = function( name )\\r\\n{\\r\\n\\tif(this._skeleton)\\r\\n\\t\\treturn this._skeleton.getBoneMatrix(name); //global\\r\\n\\r\\n\\tvar node = this.getBoneNode( name );\\r\\n\\tif(!node)\\r\\n\\t\\treturn null;\\r\\n\\tnode._is_bone = true;\\r\\n\\treturn node.transform.getGlobalMatrixRef();\\r\\n}\\r\\n\\r\\n//checks the list of bones in mesh.bones and retrieves its matrices\\r\\n//returns an array with all the bones matrices\\r\\nSkinDeformer.prototype.getBoneMatrices = function( ref_mesh )\\r\\n{\\r\\n\\t//bone matrices\\r\\n\\tvar bones = this._last_bones;\\r\\n\\r\\n\\t//reuse bone matrices\\r\\n\\tif(!this._last_bones || this._last_bones.length != ref_mesh.bones.length )\\r\\n\\t{\\r\\n\\t\\tbones = this._last_bones = [];\\r\\n\\t\\tfor(var i = 0; i < ref_mesh.bones.length; ++i)\\r\\n\\t\\t\\tbones[i] = mat4.create();\\r\\n\\t}\\r\\n\\r\\n\\tif(this._skeleton)\\r\\n\\t{\\r\\n\\t\\tvar global_model = this._root.transform ? this._root.transform.getGlobalMatrixRef() : null;\\r\\n\\t\\tthis._skeleton.computeFinalBoneMatricesAsArray( bones, ref_mesh, global_model );\\r\\n\\t\\treturn bones;\\t\\t\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i = 0; i < ref_mesh.bones.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar m = bones[i]; //mat4.create();\\r\\n\\t\\tvar joint = ref_mesh.bones[i];\\r\\n\\t\\tvar mat = this.getBoneMatrix( joint[0] ); //get the current matrix from the bone Node transform\\r\\n\\t\\tif(!mat)\\r\\n\\t\\t{\\r\\n\\t\\t\\tmat4.identity( m );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar inv = joint[1]; //inv bind pose pf the joint\\r\\n\\t\\t\\tmat4.multiply( m, mat, inv );\\r\\n\\t\\t\\tif(ref_mesh.bind_matrix) //not sure why this\\r\\n\\t\\t\\t\\tmat4.multiply( m, m, ref_mesh.bind_matrix);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn bones;\\r\\n}\\r\\n\\r\\n//Adds the deforming data to the last RenderInstance\\r\\nSkinDeformer.prototype.onCollectInstances = function( e, render_instances )\\r\\n{\\r\\n\\tif(!render_instances.length)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this.root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar last_RI;\\r\\n\\r\\n\\t//TODO: fix this, allow multiple mesh renderers with one single deformer\\r\\n\\r\\n\\t//get index\\r\\n\\tvar index = this.root.getIndexOfComponent(this);\\r\\n\\tvar prev_comp = this.root.getComponentByIndex( index - 1);\\r\\n\\tif(prev_comp)\\r\\n\\t\\tlast_RI = prev_comp._RI;\\r\\n\\r\\n\\tif(!last_RI)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._ris_skinned.length = 0;\\r\\n\\r\\n\\t//take last one (although maybe using this._root.instances ...)\\r\\n\\t//last_RI = render_instances[ render_instances.length - 1];\\r\\n\\t\\r\\n\\tif(!this.enabled)\\r\\n\\t{\\r\\n\\t\\t//disable\\r\\n\\t\\tthis.disableSkinning( last_RI );\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//grab the RI created previously and modified\\r\\n\\tthis.applySkinning( last_RI );\\r\\n}\\r\\n\\r\\n/*\\r\\nSkinDeformer.prototype.setBonesToBindPose = function()\\r\\n{\\r\\n\\t//bone matrices\\r\\n\\tvar bones = this._last_bones;\\r\\n\\tif( !bones || !this._mesh || !this._mesh.bones )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar mesh = this._mesh;\\r\\n\\tvar m = mat4.create();\\r\\n\\r\\n\\tfor(var i = 0; i < mesh.bones.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar m = bones[i]; //mat4.create();\\r\\n\\t\\tvar joint = mesh.bones[i];\\r\\n\\t\\tvar bone_node = this.getBoneNode( joint[0] );\\r\\n\\t\\tif(!bone_node)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tmat4.invert( m, joint[1] ); //invert first\\r\\n\\t\\tif(mesh.bind_matrix)\\r\\n\\t\\t\\tmat4.multiply( m, m, mesh.bind_matrix);\\r\\n\\t\\tbone_node.transform.matrix = m;\\r\\n\\t}\\r\\n}\\r\\n*/\\r\\n\\r\\n//Applies skinning taking into account the options available (using uniforms, a texture or applying it by software)\\r\\nSkinDeformer.prototype.applySkinning = function(RI)\\r\\n{\\r\\n\\tvar mesh = RI.mesh;\\r\\n\\tthis._mesh = mesh;\\r\\n\\tthis._ris_skinned.push( RI );\\r\\n\\r\\n\\t//this mesh doesnt have skinning info\\r\\n\\tif(!mesh || !mesh.getBuffer(\\\"vertices\\\") || !mesh.getBuffer(\\\"bone_indices\\\"))\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar bones = null;\\r\\n\\tvar u_bones = this._u_bones;\\r\\n\\t\\r\\n\\tif( SkinDeformer.gpu_skinning_supported && !this.cpu_skinning ) \\r\\n\\t{\\r\\n\\t\\t//get the final bone array to upload to the shader\\r\\n\\t\\t//retrieve all the bones\\r\\n\\t\\tbones = this.getBoneMatrices( mesh );\\r\\n\\r\\n\\t\\tvar bones_size = bones.length * 12;\\r\\n\\t\\tif(!bones_size)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"SkinDeformer.prototype.applySkinning: Bones not found\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!u_bones || u_bones.length != bones_size)\\r\\n\\t\\t\\tthis._u_bones = u_bones = new Float32Array( bones_size );\\r\\n\\r\\n\\t\\t//pack the bones in one single array (also skip the last row, is always 0,0,0,1)\\r\\n\\t\\tfor(var i = 0; i < bones.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tmat4.transpose( bones[i], bones[i] );\\r\\n\\t\\t\\tu_bones.set( bones[i].subarray(0,12), i * 12, (i+1) * 12 );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//can we pass the bones as a uniform?\\r\\n\\t\\tif( SkinDeformer.num_supported_uniforms >= bones_size )\\r\\n\\t\\t{\\r\\n\\t\\t\\t//upload the bones as uniform (faster but doesnt work in all GPUs)\\r\\n\\t\\t\\tRI.uniforms[\\\"u_bones\\\"] = u_bones;\\r\\n\\t\\t\\tRI.samplers[ LS.Renderer.BONES_TEXTURE_SLOT ] = null;\\r\\n\\r\\n\\t\\t\\tRI.addShaderBlock( LS.SkinDeformer.skinning_uniforms_block, { u_bones: u_bones } );\\r\\n\\t\\t}\\r\\n\\t\\telse if( SkinDeformer.num_supported_textures > 0 ) //upload the bones as a float texture (slower)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar texture = this._bones_texture;\\r\\n\\t\\t\\tif(!texture)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//TODO: to support more bones you could use a Nx3 texture instead of a 1x(N*3) \\r\\n\\t\\t\\t\\ttexture = this._bones_texture = new GL.Texture( 1, SkinDeformer.MAX_TEXTURE_BONES * 3, { format: gl.RGBA, type: gl.FLOAT, filter: gl.NEAREST} ); //3 rows of 4 values per matrix\\r\\n\\t\\t\\t\\ttexture._data = new Float32Array( texture.width * texture.height * 4 );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\ttexture._data.set( u_bones );\\r\\n\\r\\n\\t\\t\\ttexture.uploadData( texture._data, { no_flip: true } );\\r\\n\\t\\t\\tLS.RM.textures[\\\":bones_\\\" + this.uid ] = texture; //debug\\r\\n\\t\\t\\tRI.uniforms[\\\"u_bones\\\"] = LS.Renderer.BONES_TEXTURE_SLOT;\\r\\n\\t\\t\\tRI.samplers[ LS.Renderer.BONES_TEXTURE_SLOT ] = texture; //{ texture: texture, magFilter: gl.NEAREST, minFilter: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE };\\r\\n\\r\\n\\t\\t\\tRI.addShaderBlock( LS.SkinDeformer.skinning_texture_block, { u_bones: LS.Renderer.BONES_TEXTURE_SLOT } );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tconsole.error(\\\"impossible to get here\\\");\\r\\n\\r\\n\\t\\tRI.addShaderBlock( LS.SkinDeformer.skinning_block );\\r\\n\\t}\\r\\n\\telse //cpu skinning (mega slow)\\r\\n\\t{\\r\\n\\t\\tif(!this._skinned_mesh || this._skinned_mesh._reference != mesh)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._skinned_mesh = new GL.Mesh();\\r\\n\\t\\t\\tthis._skinned_mesh._reference = mesh;\\r\\n\\t\\t\\tvar vertex_buffer = mesh.getBuffer(\\\"vertices\\\");\\r\\n\\t\\t\\tvar normal_buffer = mesh.getBuffer(\\\"normals\\\");\\r\\n\\r\\n\\t\\t\\t//clone \\r\\n\\t\\t\\tfor (var i in mesh.vertexBuffers)\\r\\n\\t\\t\\t\\tthis._skinned_mesh.vertexBuffers[i] = mesh.vertexBuffers[i];\\r\\n\\t\\t\\tfor (var i in mesh.indexBuffers)\\r\\n\\t\\t\\t\\tthis._skinned_mesh.indexBuffers[i] = mesh.indexBuffers[i];\\r\\n\\r\\n\\t\\t\\t//new ones clonning old ones\\r\\n\\t\\t\\tthis._skinned_mesh.createVertexBuffer(\\\"vertices\\\",\\\"a_vertex\\\", 3, new Float32Array( vertex_buffer.data ), gl.STREAM_DRAW );\\r\\n\\t\\t\\tif(normal_buffer)\\r\\n\\t\\t\\t\\tthis._skinned_mesh.createVertexBuffer(\\\"normals\\\",\\\"a_normal\\\", 3, new Float32Array( normal_buffer.data ), gl.STREAM_DRAW );\\r\\n\\t\\t}\\r\\n\\r\\n\\r\\n\\t\\t//apply cpu skinning\\r\\n\\t\\tthis.applySoftwareSkinning( mesh, this._skinned_mesh );\\r\\n\\r\\n\\t\\tRI.setMesh( this._skinned_mesh, this.primitive );\\r\\n\\t\\tthis.disableSkinning( RI );\\r\\n\\t}\\r\\n\\r\\n\\tif( this.ignore_transform )\\r\\n\\t{\\r\\n\\t\\tmat4.identity( RI.matrix );\\r\\n\\t\\tRI.normal_matrix.set( RI.matrix );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tthis._root.transform.getGlobalMatrix( RI.matrix );\\r\\n\\r\\n\\tif(bones && bones.length) //extract at least one position from the bones, we need for probes and sorting\\r\\n\\t{\\r\\n\\t\\tRI.center.set( LS.ZEROS );\\r\\n\\t\\tfor(var i = 0; i < bones.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tRI.center[0] += bones[i][3];\\r\\n\\t\\t\\tRI.center[1] += bones[i][7];\\r\\n\\t\\t\\tRI.center[2] += bones[i][11];\\r\\n\\t\\t}\\r\\n\\t\\tRI.center[0] /= bones.length;\\r\\n\\t\\tRI.center[1] /= bones.length;\\r\\n\\t\\tRI.center[2] /= bones.length;\\r\\n\\t}\\r\\n\\telse if(u_bones) //take first bone\\r\\n\\t{\\r\\n\\t\\tRI.center[0] = u_bones[9];\\r\\n\\t\\tRI.center[1] = u_bones[10];\\r\\n\\t\\tRI.center[2] = u_bones[11];\\r\\n\\t}\\r\\n\\r\\n\\tmat4.getTranslation( RI.position, RI.matrix );\\r\\n\\r\\n\\tRI.use_bounding = false;\\r\\n}\\r\\n\\r\\nSkinDeformer.prototype.disableSkinning = function( RI )\\r\\n{\\r\\n\\tthis._mesh = null;\\r\\n\\r\\n\\tif( RI.samplers[\\\"u_bones\\\"] )\\r\\n\\t\\tdelete RI.samplers[\\\"u_bones\\\"];\\r\\n\\r\\n\\tRI.removeShaderBlock( LS.SkinDeformer.skinning_block );\\r\\n\\tRI.removeShaderBlock( LS.SkinDeformer.skinning_uniforms_block );\\r\\n\\tRI.removeShaderBlock( LS.SkinDeformer.skinning_texture_block );\\r\\n}\\r\\n\\r\\nSkinDeformer.prototype.getMesh = function()\\r\\n{\\r\\n\\treturn this._mesh;\\r\\n}\\r\\n\\r\\n//Takes every vertex and multiplyes it by its bone matrices (slow but it works everywhere)\\r\\nSkinDeformer.prototype.applySoftwareSkinning = function(ref_mesh, skin_mesh)\\r\\n{\\r\\n\\tvar original_vertices = ref_mesh.getBuffer(\\\"vertices\\\").data;\\r\\n\\tvar original_normals = null;\\r\\n\\tif(ref_mesh.getBuffer(\\\"normals\\\"))\\r\\n\\t\\toriginal_normals = ref_mesh.getBuffer(\\\"normals\\\").data;\\r\\n\\r\\n\\tvar weights = ref_mesh.getBuffer(\\\"weights\\\").data;\\r\\n\\tvar bone_indices = ref_mesh.getBuffer(\\\"bone_indices\\\").data;\\r\\n\\r\\n\\tvar vertices_buffer = skin_mesh.getBuffer(\\\"vertices\\\");\\r\\n\\tvar vertices = vertices_buffer.data;\\r\\n\\r\\n\\tvar normals_buffer = null;\\r\\n\\tvar normals = null;\\r\\n\\r\\n\\tif(!SkinDeformer.zero_matrix)\\r\\n\\t\\tSkinDeformer.zero_matrix = new Float32Array(16);\\r\\n\\tvar zero_matrix = SkinDeformer.zero_matrix;\\r\\n\\r\\n\\tif(original_normals)\\r\\n\\t{\\r\\n\\t\\tnormals_buffer = skin_mesh.getBuffer(\\\"normals\\\");\\r\\n\\t\\tnormals = normals_buffer.data;\\r\\n\\t}\\r\\n\\r\\n\\t//bone matrices\\r\\n\\tvar bones = this.getBoneMatrices( ref_mesh );\\r\\n\\tif(bones.length == 0) //no bones found\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\t//var factor = this.factor; //for debug\\r\\n\\r\\n\\t//apply skinning per vertex\\r\\n\\tvar temp = vec3.create();\\r\\n\\tvar ov_temp = vec3.create();\\r\\n\\tvar temp_matrix = mat4.create();\\r\\n\\tfor(var i = 0, l = vertices.length / 3; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar ov = original_vertices.subarray(i*3, i*3+3);\\r\\n\\r\\n\\t\\tvar b = bone_indices.subarray(i*4, i*4+4);\\r\\n\\t\\tvar w = weights.subarray(i*4, i*4+4);\\r\\n\\t\\tvar v = vertices.subarray(i*3, i*3+3);\\r\\n\\r\\n\\t\\tvar bmat = [ bones[ b[0] ], bones[ b[1] ], bones[ b[2] ], bones[ b[3] ] ];\\r\\n\\r\\n\\t\\ttemp_matrix.set( zero_matrix );\\r\\n\\t\\tmat4.scaleAndAdd( temp_matrix, temp_matrix, bmat[0], w[0] );\\r\\n\\t\\tif(w[1] > 0.0) mat4.scaleAndAdd( temp_matrix, temp_matrix, bmat[1], w[1] );\\r\\n\\t\\tif(w[2] > 0.0) mat4.scaleAndAdd( temp_matrix, temp_matrix, bmat[2], w[2] );\\r\\n\\t\\tif(w[3] > 0.0) mat4.scaleAndAdd( temp_matrix, temp_matrix, bmat[3], w[3] );\\r\\n\\r\\n\\t\\tmat4.multiplyVec3(v, temp_matrix, original_vertices.subarray(i*3, i*3+3) );\\r\\n\\t\\tif(normals)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar n = normals.subarray(i*3, i*3+3);\\r\\n\\t\\t\\tmat4.rotateVec3(n, temp_matrix, original_normals.subarray(i*3, i*3+3) );\\r\\n\\t\\t}\\r\\n\\t\\t\\r\\n\\t\\t//we could also multiply the normal but this is already superslow...\\r\\n\\t\\tif(SkinDeformer.apply_to_normals_by_software)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//apply weights\\r\\n\\t\\t\\tv[0] = v[1] = v[2] = 0.0; //reset\\r\\n\\t\\t\\tmat4.multiplyVec3(v, bmat[0], ov_temp);\\r\\n\\t\\t\\tvec3.scale(v,v,w[0]);\\r\\n\\t\\t\\tfor(var j = 1; j < 4; ++j)\\r\\n\\t\\t\\t\\tif(w[j] > 0.0)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tmat4.multiplyVec3( temp, bmat[j], ov_temp );\\r\\n\\t\\t\\t\\t\\tvec3.scaleAndAdd( v, v, temp, w[j] );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//if(factor != 1) vec3.lerp( v, ov, v, factor);\\r\\n\\t}\\r\\n\\r\\n\\t//upload\\r\\n\\tvertices_buffer.upload(gl.STREAM_DRAW);\\r\\n\\tif(normals_buffer)\\r\\n\\t\\tnormals_buffer.upload(gl.STREAM_DRAW);\\r\\n}\\r\\n\\r\\nSkinDeformer.prototype.extractSkeleton = function()\\r\\n{\\r\\n\\t//TODO\\r\\n}\\r\\n\\r\\n\\r\\n//extracts the matrices from the bind pose and applies it to the bones\\r\\nSkinDeformer.prototype.applyBindPose = function()\\r\\n{\\r\\n\\tvar mesh = this._mesh;\\r\\n\\r\\n\\t//this mesh doesnt have skinning info\\r\\n\\tif( !mesh || !mesh.bones )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar imat = mat4.create();\\r\\n\\r\\n\\tvar bone_nodes = this.getBones();\\r\\n\\tfor(var i = 0; i < bone_nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar node = bone_nodes[i];\\r\\n\\t\\tnode._level = node.getHierarchyLevel();\\r\\n\\t}\\r\\n\\r\\n\\t/*\\r\\n\\tfor(var i = 0; i < bones.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar joint = bones[i];\\r\\n\\t\\tvar bone_name = joint[0];\\r\\n\\t\\tvar bind_matrix = joint[1];\\r\\n\\t\\tvar bone_node = this.getBoneNode( bone_name );\\r\\n\\t\\tif( !bone_node || !bone_node.transform )\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tmat4.invert( imat, bind_matrix, bind_matrix );\\r\\n\\t\\tbone_node.transform.fromGlobalMatrix( imat );\\r\\n\\t}\\r\\n\\t*/\\r\\n}\\r\\n\\r\\n//returns an array with all the bone nodes affecting this mesh\\r\\nSkinDeformer.prototype.getBones = function()\\r\\n{\\r\\n\\tvar mesh = this._mesh;\\r\\n\\tif(!mesh && !mesh.bones)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar bones = [];\\r\\n\\tfor(var i in mesh.bones)\\r\\n\\t{\\r\\n\\t\\tvar bone = this.getBoneNode( mesh.bones[i][0] );\\r\\n\\t\\tif(bone)\\r\\n\\t\\t\\tbones.push( bone );\\r\\n\\t}\\r\\n\\r\\n\\treturn bones;\\r\\n}\\r\\n\\r\\nLS.registerComponent( SkinDeformer );\\r\\nLS.SkinDeformer = SkinDeformer;\\r\\n\\r\\nSkinDeformer.skinning_shader_code = \\\"\\\\n\\\\\\r\\n\\t//Skinning ******************* \\\\n\\\\\\r\\n\\t#ifndef MAX_BONES\\\\n\\\\\\r\\n\\t\\t#define MAX_BONES 64\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef USE_SKINNING_TEXTURE\\\\n\\\\\\r\\n\\t\\tuniform sampler2D u_bones;\\\\n\\\\\\r\\n\\t#else\\\\n\\\\\\r\\n\\t\\tuniform vec4 u_bones[ MAX_BONES * 3];\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\tattribute vec4 a_weights;\\\\n\\\\\\r\\n\\tattribute vec4 a_bone_indices;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvoid getMat(int id, inout mat4 m) {\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t\\t#ifdef USE_SKINNING_TEXTURE\\\\n\\\\\\r\\n\\t\\t\\tfloat i_max_texture_bones_offset = 1.0 / (128.0 * 3.0);\\\\n\\\\\\r\\n\\t\\t\\tm[0] = texture2D( u_bones, vec2( 0.0, (float(id*3)+0.5) * i_max_texture_bones_offset ) ); \\\\n\\\\\\r\\n\\t\\t\\tm[1] = texture2D( u_bones, vec2( 0.0, (float(id*3+1)+0.5) * i_max_texture_bones_offset ) );\\\\n\\\\\\r\\n\\t\\t\\tm[2] = texture2D( u_bones, vec2( 0.0, (float(id*3+2)+0.5) * i_max_texture_bones_offset ) );\\\\n\\\\\\r\\n\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\tm[0] = u_bones[ id * 3];\\\\n\\\\\\r\\n\\t\\t\\tm[1] = u_bones[ id * 3 + 1];\\\\n\\\\\\r\\n\\t\\t\\tm[2] = u_bones[ id * 3 + 2];\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tmat3 mat3_emu(mat4 m4) {\\\\n\\\\\\r\\n\\t return mat3(\\\\n\\\\\\r\\n\\t\\t m4[0][0], m4[0][1], m4[0][2],\\\\n\\\\\\r\\n\\t\\t m4[1][0], m4[1][1], m4[1][2],\\\\n\\\\\\r\\n\\t\\t m4[2][0], m4[2][1], m4[2][2]);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvoid applySkinning(inout vec4 position, inout vec3 normal)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\t//toji version seems faster\\\\n\\\\\\r\\n\\t\\tmat4 bone_matrix = mat4(0.0,0.0,0.0,0.0, 0.0,0.0,0.0,0.0, 0.0,0.0,0.0,0.0, 0.0,0.0,0.0,1.0);\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tgetMat( int(a_bone_indices.x), bone_matrix );\\\\n\\\\\\r\\n\\t\\tmat4 result = a_weights.x * bone_matrix;\\\\n\\\\\\r\\n\\t\\tgetMat( int(a_bone_indices.y), bone_matrix);\\\\n\\\\\\r\\n\\t\\tresult = result + a_weights.y * bone_matrix;\\\\n\\\\\\r\\n\\t\\tgetMat( int(a_bone_indices.z), bone_matrix);\\\\n\\\\\\r\\n\\t\\tresult = result + a_weights.z * bone_matrix;\\\\n\\\\\\r\\n\\t\\tgetMat( int(a_bone_indices.w), bone_matrix);\\\\n\\\\\\r\\n\\t\\tresult = result + a_weights.w * bone_matrix;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tposition.xyz = (position * result).xyz;\\\\n\\\\\\r\\n\\t\\tnormal = normal * mat3_emu(result);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nSkinDeformer.skinning_enabled_shader_code = \\\"\\\\n#pragma shaderblock skinning_mode\\\\n#define USING_SKINNING\\\\n\\\";\\r\\nSkinDeformer.skinning_disabled_shader_code = \\\"\\\\nvoid applySkinning( inout vec4 position, inout vec3 normal) {}\\\\n\\\";\\r\\n\\r\\n// ShaderBlocks used to inject to shader in runtime\\r\\nvar skinning_block = new LS.ShaderBlock(\\\"skinning\\\");\\r\\nskinning_block.addCode( GL.VERTEX_SHADER, SkinDeformer.skinning_enabled_shader_code, SkinDeformer.skinning_disabled_shader_code );\\r\\nskinning_block.register();\\r\\nSkinDeformer.skinning_block = skinning_block;\\r\\n\\r\\nvar skinning_uniforms_block = new LS.ShaderBlock(\\\"skinning_uniforms\\\");\\r\\nskinning_uniforms_block.defineContextMacros( { \\\"skinning_mode\\\": \\\"skinning_uniforms\\\"} );\\r\\nskinning_uniforms_block.addCode( GL.VERTEX_SHADER, SkinDeformer.skinning_shader_code, SkinDeformer.skinning_disabled_shader_code );\\r\\nskinning_uniforms_block.register();\\r\\nSkinDeformer.skinning_uniforms_block = skinning_uniforms_block;\\r\\n\\r\\nvar skinning_texture_block = new LS.ShaderBlock(\\\"skinning_texture\\\");\\r\\nskinning_texture_block.defineContextMacros( { \\\"skinning_mode\\\": \\\"skinning_texture\\\"} );\\r\\nskinning_texture_block.addCode( GL.VERTEX_SHADER, \\\"\\\\n#define USE_SKINNING_TEXTURE\\\\n\\\" + SkinDeformer.skinning_shader_code, SkinDeformer.skinning_disabled_shader_code );\\r\\nskinning_texture_block.register();\\r\\nSkinDeformer.skinning_texture_block = skinning_texture_block;\\r\\n///@FILE:../src/components/svgRenderer.js\\r\\n///@INFO: UNCOMMON\\r\\n//WORK IN PROGRESS\\r\\n\\r\\n\\r\\n/**\\r\\n* Renders one mesh, it allows to configure the rendering primitive, the submesh (range of mesh) and a level of detail mesh\\r\\n* @class SVGRenderer\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\nfunction SVGRenderer(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.svg = null;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n\\r\\n\\tthis._RI = new LS.RenderInstance( null, this );\\r\\n\\r\\n\\tthis._mesh = null;\\r\\n\\tthis._svg_data = null;\\r\\n}\\r\\n\\r\\nSVGRenderer.icon = \\\"mini-icon-teapot.png\\\";\\r\\n\\r\\n//vars\\r\\nSVGRenderer[\\\"@svg\\\"] = { type: \\\"resource\\\" };\\r\\n\\r\\nSVGRenderer.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n}\\r\\n\\r\\nSVGRenderer.prototype.onRemovedFromScene = function( scene )\\r\\n{\\r\\n}\\r\\n\\r\\nSVGRenderer.prototype.onAddedToNode = function( node )\\r\\n{\\r\\n\\tLEvent.bind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n\\tthis._RI.node = node;\\r\\n}\\r\\n\\r\\nSVGRenderer.prototype.onRemovedFromNode = function( node )\\r\\n{\\r\\n\\tLEvent.unbind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n}\\r\\n\\r\\nSVGRenderer.prototype.getMesh = function()\\r\\n{\\r\\n\\tif(!this.svg)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar svg = LS.ResourcesManager.getResource( this.svg );\\r\\n\\tvar mesh = null;\\r\\n\\r\\n\\tif(this._svg_data === svg )\\r\\n\\t{\\r\\n\\t\\tmesh = this._mesh;\\r\\n\\t}\\r\\n\\r\\n\\tif(!mesh)\\r\\n\\t\\tmesh = this._mesh = GL.Mesh.cube();\\r\\n\\treturn mesh;\\r\\n}\\r\\n\\r\\nSVGRenderer.prototype.onCollectInstances = function(e, instances)\\r\\n{\\r\\n\\tif(!this._enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar mesh = this.getMesh();\\r\\n\\tif(!mesh)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar node = this._root;\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar RI = this._RI;\\r\\n\\tvar is_static = this._root.flags && this._root.flags.is_static;\\r\\n\\tvar transform = this._root.transform;\\r\\n\\r\\n\\tRI.setMatrix( this._root.transform._global_matrix );\\r\\n\\tmat4.multiplyVec3( RI.center, RI.matrix, LS.ZEROS );\\r\\n\\r\\n\\t//material (after flags because it modifies the flags)\\r\\n\\tvar material = this._root.getMaterial();\\r\\n\\tRI.setMaterial( material );\\r\\n\\r\\n\\t//buffers from mesh and bounding\\r\\n\\tRI.setMesh( mesh, GL.TRIANGLES );\\r\\n\\tRI.setRange(0,-1);\\r\\n\\tRI.collision_mesh = mesh;\\r\\n\\tinstances.push( RI );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Configure from a serialized object\\r\\n* @method configure\\r\\n* @param {Object} object with the serialized info\\r\\n*/\\r\\nSVGRenderer.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(o.enabled !== undefined)\\r\\n\\t\\tthis.enabled = o.enabled;\\r\\n\\tthis.svg = o.svg;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Serialize the object \\r\\n* @method serialize\\r\\n* @return {Object} object with the serialized info\\r\\n*/\\r\\nSVGRenderer.prototype.serialize = function()\\r\\n{\\r\\n\\tvar o = { \\r\\n\\t\\tenabled: this.enabled,\\r\\n\\t\\tsvg: this.svg\\r\\n\\t};\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\nSVGRenderer.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(typeof(this.svg) == \\\"string\\\")\\r\\n\\t\\tres[this.svg] = LS.Resource;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nSVGRenderer.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.svg == old_name)\\r\\n\\t\\tthis.svg = new_name;\\r\\n}\\r\\n\\r\\n\\r\\n//LS.registerComponent( SVGRenderer );\\r\\n\\r\\n///@FILE:../src/components/skybox.js\\r\\n/**\\r\\n* Skybox allows to render a cubemap or polar image as a background for the 3D scene\\r\\n* @class Skybox\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {Object} object [optional] to configure from\\r\\n*/\\r\\n\\r\\nfunction Skybox(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.texture = null;\\r\\n\\tthis.material = null;\\r\\n\\tthis._intensity = 1;\\r\\n\\tthis.use_environment = true;\\r\\n\\tthis.gamma = false;\\r\\n\\tthis._bake_to_cubemap = false;\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nSkybox.icon = \\\"mini-icon-dome.png\\\";\\r\\n\\r\\n//vars\\r\\nSkybox[\\\"@material\\\"] = { type: LS.TYPES.MATERIAL };\\r\\nSkybox[\\\"@texture\\\"] = { type: LS.TYPES.TEXTURE };\\r\\n\\r\\nSkybox.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tLEvent.bind(node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nSkybox.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tLEvent.unbind(node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nSkybox.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind(scene, \\\"start\\\", this.onStart, this);\\r\\n}\\r\\n\\r\\nSkybox.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind(scene, \\\"start\\\", this.onStart, this);\\r\\n}\\r\\n\\r\\n\\r\\nObject.defineProperty( Skybox.prototype, \\\"intensity\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tthis._intensity = v;\\r\\n\\t\\tif(this._material)\\r\\n\\t\\t\\tthis._material._color.set([v,v,v,1]);\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._intensity;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( Skybox.prototype, \\\"bake_to_cubemap\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tthis._bake_to_cubemap = v;\\r\\n\\t\\tif(v)\\r\\n\\t\\t\\tthis.bakeToCubemap();\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._bake_to_cubemap;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\nSkybox.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(this.texture && this.texture.constructor === String)\\r\\n\\t\\tres[this.texture] = GL.Texture;\\r\\n\\tif(this.material && this.material.constructor === String)\\r\\n\\t\\tres[this.material] = LS.Material;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nSkybox.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.texture == old_name)\\r\\n\\t\\tthis.texture = new_name;\\r\\n}\\r\\n\\r\\nSkybox.prototype.onCollectInstances = function(e, instances)\\r\\n{\\r\\n\\tif(!this._root || !this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar mesh = this._mesh;\\r\\n\\tif(!mesh)\\r\\n\\t\\tmesh = this._mesh = GL.Mesh.cube({size: 10});\\r\\n\\r\\n\\tvar node = this._root;\\r\\n\\r\\n\\tvar RI = this._render_instance;\\r\\n\\tif(!RI)\\r\\n\\t{\\r\\n\\t\\tthis._render_instance = RI = new LS.RenderInstance(this._root, this);\\r\\n\\t\\tRI.priority = 100;\\r\\n\\r\\n\\t\\t//to position the skybox on top of the camera\\r\\n\\t\\tRI.onPreRender = function(render_settings) { \\r\\n\\t\\t\\tvar camera = LS.Renderer._current_camera;\\r\\n\\t\\t\\tvar cam_pos = camera.getEye();\\r\\n\\t\\t\\tmat4.identity(this.matrix);\\r\\n\\t\\t\\tmat4.setTranslation( this.matrix, cam_pos );\\r\\n\\t\\t\\tvar size = (camera.near + camera.far) * 0.5;\\r\\n\\t\\t\\t//mat4.scale( this.matrix, this.matrix, [ size, size, size ]);\\r\\n\\t\\t\\tif(this.node.transform)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar R = this.node.transform.getGlobalRotationMatrix();\\r\\n\\t\\t\\t\\tmat4.multiply( this.matrix, this.matrix, R );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//this.updateAABB(); this node doesnt have AABB (its always visible)\\r\\n\\t\\t\\tvec3.copy( this.center, cam_pos );\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tvar mat = null;\\r\\n\\tif(this.material)\\r\\n\\t{\\r\\n\\t\\tmat = LS.ResourcesManager.getResource( this.material );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tvar texture_name = null;\\r\\n\\t\\tif (this.use_environment && LS.Renderer._current_scene.info )\\r\\n\\t\\t\\ttexture_name = LS.Renderer._current_scene.info.textures[\\\"environment\\\"];\\r\\n\\t\\telse\\r\\n\\t\\t\\ttexture_name = this.texture;\\r\\n\\r\\n\\t\\tif(!texture_name)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar texture = LS.ResourcesManager.textures[ texture_name ];\\r\\n\\t\\tif(!texture)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tmat = this._material;\\r\\n\\t\\tif(!mat)\\r\\n\\t\\t{\\r\\n\\t\\t\\tmat = this._material = new LS.ShaderMaterial({ \\r\\n\\t\\t\\t\\tflags: { \\r\\n\\t\\t\\t\\t\\ttwo_sided: true, \\r\\n\\t\\t\\t\\t\\tcast_shadows: false, \\r\\n\\t\\t\\t\\t\\treceive_shadows: false,\\r\\n\\t\\t\\t\\t\\tignore_frustum: true,\\r\\n\\t\\t\\t\\t\\tignore_lights: true,\\r\\n\\t\\t\\t\\t\\tdepth_test: false \\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\tuse_scene_ambient:false,\\r\\n\\t\\t\\t\\tcolor: [ this.intensity, this.intensity, this.intensity ]\\r\\n\\t\\t\\t});\\r\\n\\t\\t\\t\\tmat.shader_code = new LS.ShaderCode( Skybox.shader_code );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tmat.color.set([this.intensity, this.intensity, this.intensity]);\\r\\n\\r\\n\\t\\tmat.setProperty( \\\"Texture\\\", texture_name );\\r\\n\\r\\n\\t\\t/*\\r\\n\\t\\tvar sampler = mat.setTexture( LS.Material.COLOR, texture_name );\\r\\n\\t\\t//sampler.gamma = this.gamma;\\r\\n\\t\\tif(texture && texture.texture_type == gl.TEXTURE_2D)\\r\\n\\t\\t{\\r\\n\\t\\t\\tsampler.uvs = \\\"polar_vertex\\\";\\r\\n\\t\\t\\ttexture.bind(0);\\r\\n\\t\\t\\ttexture.setParameter( gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); //to avoid going up\\r\\n\\t\\t\\ttexture.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR ); //avoid ugly error in atan2 edges\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tsampler.uvs = \\\"0\\\";\\r\\n\\t\\t*/\\r\\n\\t}\\r\\n\\r\\n\\tRI.setMesh( mesh );\\r\\n\\tRI.setMaterial( mat );\\r\\n\\tinstances.push(RI);\\r\\n\\r\\n\\t//if we have a material we can bake and it has changed...\\r\\n\\tif( this.material && mat && this._bake_to_cubemap && (this._prev_mat != mat || this._mat_version != mat.version ) )\\r\\n\\t{\\r\\n\\t\\tthis._prev_mat = mat;\\r\\n\\t\\tthis._mat_version = mat.version;\\r\\n\\t\\tthis.bakeToCubemap();\\r\\n\\t}\\r\\n}\\r\\n\\r\\nSkybox.prototype.onStart = function(e)\\r\\n{\\r\\n\\tif( this._bake_to_cubemap )\\r\\n\\t\\tthis.bakeToCubemap();\\r\\n}\\r\\n\\r\\nSkybox.prototype.bakeToCubemap = function( size, render_settings )\\r\\n{\\r\\n\\tvar that = this;\\r\\n\\tsize = size || 512;\\r\\n\\trender_settings = render_settings || new LS.RenderSettings();\\r\\n\\r\\n\\tif( !this.root || !this.root.scene )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar scene = this.root.scene;\\r\\n\\r\\n\\tif( !this._render_instance || !this._render_instance.material ) //generate the skybox render instance\\r\\n\\t{\\r\\n\\t\\t//wait till we have the material loaded\\r\\n\\t\\tsetTimeout( function(){ that.bakeToCubemap.bind( that ); },500 );\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//this will ensure the materials and instances are in the queues\\r\\n\\tvar instances = [];\\r\\n\\tthis.onCollectInstances(null, instances);\\r\\n\\tLS.Renderer.processVisibleData( scene, render_settings, null, instances, true );\\r\\n\\r\\n\\trender_settings.render_helpers = false;\\r\\n\\r\\n\\tthis._baked_texture = LS.Renderer.renderToCubemap( vec3.create(), size, this._baked_texture, render_settings, 0.001, 100, vec4.create(), instances);\\r\\n\\tLS.ResourcesManager.registerResource( \\\":baked_skybox\\\", this._baked_texture );\\r\\n\\tif( this.root.scene.info )\\r\\n\\t\\tthis.root.scene.info.textures[ \\\"environment\\\" ] = \\\":baked_skybox\\\";\\r\\n}\\r\\n\\r\\nSkybox.shader_code = \\\"\\\\n\\\\\\r\\n\\\\\\\\js\\\\n\\\\\\r\\n\\tthis.createSampler(\\\\\\\"Texture\\\\\\\",\\\\\\\"u_color_texture\\\\\\\", { magFilter: GL.LINEAR, missing: \\\\\\\"white\\\\\\\"} );\\\\n\\\\\\r\\n\\tthis.queue = LS.RenderQueue.BACKGROUND;\\\\n\\\\\\r\\n\\tthis.render_state.cull_face = false;\\\\n\\\\\\r\\n\\tthis.render_state.front_face = GL.CW;\\\\n\\\\\\r\\n\\tthis.render_state.depth_test = false;\\\\n\\\\\\r\\n\\tthis.render_state.front_face = GL.CW;\\\\n\\\\\\r\\n\\tthis.flags.ignore_frustum = true;\\\\n\\\\\\r\\n\\tthis.flags.ignore_lights = true;\\\\n\\\\\\r\\n\\tthis.flags.cast_shadows = false;\\\\n\\\\\\r\\n\\tthis.flags.receive_shadows = false;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\n\\\\\\\\color.vs\\\\n\\\\\\r\\n\\tprecision mediump float;\\\\n\\\\\\r\\n\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\tvarying vec3 v_world_position;\\\\n\\\\\\r\\n\\tuniform mat4 u_model;\\\\n\\\\\\r\\n\\tuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tvec4 vertex4 = u_model * vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\t\\tv_world_position = vertex4.xyz;\\\\n\\\\\\r\\n\\t\\tgl_Position = u_viewprojection * vertex4;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\\\\\\color.fs\\\\n\\\\\\r\\n\\tprecision mediump float;\\\\n\\\\\\r\\n\\tvarying vec3 v_world_position;\\\\n\\\\\\r\\n\\tuniform vec4 u_material_color;\\\\n\\\\\\r\\n\\tuniform vec3 u_camera_eye;\\\\n\\\\\\r\\n\\tuniform samplerCube u_color_texture;\\\\n\\\\\\r\\n\\tvec2 polarToCartesian(in vec3 V)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\treturn vec2( 0.5 - (atan(V.z, V.x) / -6.28318531), asin(V.y) / 1.57079633 * 0.5 + 0.5);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tvec3 E = normalize( v_world_position - u_camera_eye);\\\\n\\\\\\r\\n\\t\\tvec4 color = textureCube( u_color_texture, E );\\\\n\\\\\\r\\n\\t\\tgl_FragColor = u_material_color * color;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\\\\\\picking.vs\\\\n\\\\\\r\\n\\tprecision mediump float;\\\\n\\\\\\r\\n\\tattribute vec3 a_vertex;\\\\n\\\\\\r\\n\\tuniform mat4 u_model;\\\\n\\\\\\r\\n\\tuniform mat4 u_viewprojection;\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tvec4 vertex4 = vec4(a_vertex,1.0);\\\\n\\\\\\r\\n\\t\\tgl_Position = (u_viewprojection * u_model) * vertex4;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\\\\\\picking.fs\\\\n\\\\\\r\\n\\tprecision mediump float;\\\\n\\\\\\r\\n\\tuniform vec4 u_material_color;\\\\n\\\\\\r\\n\\tvoid main() {\\\\n\\\\\\r\\n\\t\\tgl_FragColor = u_material_color;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\n\\r\\nLS.registerComponent(Skybox);\\r\\nLS.Skybox = Skybox;\\r\\n///@FILE:../src/components/background.js\\r\\n///@INFO: UNCOMMON\\r\\nfunction BackgroundRenderer(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.texture = null;\\r\\n\\r\\n\\tthis.createProperty( \\\"color\\\", vec3.fromValues(1,1,1), \\\"color\\\" );\\r\\n\\tthis.opacity = 1.0;\\r\\n\\tthis.blend_mode = Blend.NORMAL;\\r\\n\\r\\n\\t//this._color = vec3.fromValues(1,1,1);\\r\\n\\tthis.material_name = null;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nBackgroundRenderer.icon = \\\"mini-icon-bg.png\\\";\\r\\nBackgroundRenderer[\\\"@texture\\\"] = { type: \\\"texture\\\" };\\r\\nBackgroundRenderer[\\\"@material_name\\\"] = { type: \\\"material\\\" };\\r\\nBackgroundRenderer[\\\"@blend_mode\\\"] = { type: \\\"enum\\\", values: LS.Blend };\\r\\nBackgroundRenderer[\\\"@opacity\\\"] = { type: \\\"number\\\", step: 0.01 };\\r\\n\\r\\n/*\\r\\nObject.defineProperty( BackgroundRenderer.prototype, 'color', {\\r\\n\\tget: function() { return this._color; },\\r\\n\\tset: function(v) { this._color.set(v);},\\r\\n\\tenumerable: true\\r\\n});\\r\\n*/\\r\\n\\r\\nBackgroundRenderer.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tLEvent.bind(node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nBackgroundRenderer.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tLEvent.unbind(node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nBackgroundRenderer.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(typeof(this.texture) == \\\"string\\\")\\r\\n\\t\\tres[this.texture] = GL.Texture;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nBackgroundRenderer.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.texture == old_name)\\r\\n\\t\\tthis.texture = new_name;\\r\\n}\\r\\n\\r\\nBackgroundRenderer.prototype.onCollectInstances = function(e, instances)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar mat = null;\\r\\n\\r\\n\\tif( this.material_name )\\r\\n\\t\\tmat = LS.ResourcesManager.materials[ this.material_name ];\\r\\n\\r\\n\\tif(!mat)\\r\\n\\t{\\r\\n\\t\\tvar texture = this.texture;\\r\\n\\t\\tif(!texture) \\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(texture.constructor === String)\\r\\n\\t\\t\\ttexture = LS.ResourcesManager.textures[ texture ];\\r\\n\\r\\n\\t\\tif(!this._material)\\r\\n\\t\\t\\tmat = this._material = new LS.StandardMaterial({shader: \\\"lowglobal\\\", \\r\\n\\t\\t\\t\\tqueue: LS.RenderQueue.BACKGROUND, \\r\\n\\t\\t\\t\\tflags: {\\r\\n\\t\\t\\t\\t\\tcast_shadows: false,\\r\\n\\t\\t\\t\\t\\tignore_lights: true,\\r\\n\\t\\t\\t\\t\\ttwo_sided: true,\\r\\n\\t\\t\\t\\t\\tdepth_test: false,\\r\\n\\t\\t\\t\\t\\tignore_frustum: true\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\tuse_scene_ambient:false\\r\\n\\t\\t\\t});\\r\\n\\t\\telse\\r\\n\\t\\t\\tmat = this._material;\\r\\n\\r\\n\\t\\tmat.setTexture(\\\"color\\\", texture);\\r\\n\\t\\tmat.color.set( this.color );\\r\\n\\t\\tmat.opacity = this.opacity;\\r\\n\\t\\tmat.blend_mode = this.blend_mode;\\r\\n\\t}\\r\\n\\r\\n\\tvar mesh = this._mesh;\\r\\n\\tif(!mesh)\\r\\n\\t\\tmesh = this._mesh = GL.Mesh.plane({size:2});\\r\\n\\r\\n\\tvar RI = this._render_instance;\\r\\n\\tif(!RI)\\r\\n\\t\\tthis._render_instance = RI = new LS.RenderInstance( this._root, this );\\r\\n\\r\\n\\tRI.setMesh( mesh );\\r\\n\\tRI.setMaterial( mat );\\r\\n\\r\\n\\tinstances.push(RI);\\r\\n}\\r\\n\\r\\n//disabled till the viewprojection matrix issue is fixed\\r\\n//LS.registerComponent( BackgroundRenderer );\\r\\n///@FILE:../src/components/collider.js\\r\\n///@INFO: UNCOMMON\\r\\nfunction Collider(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.shape = 1;\\r\\n\\tthis.mesh = null;\\r\\n\\tthis.size = vec3.fromValues(0.5,0.5,0.5); //in local space?\\r\\n\\tthis.center = vec3.create(); //in local space?\\r\\n\\tthis.use_mesh_bounding = false;\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nCollider.icon = \\\"mini-icon-collider.png\\\";\\r\\n\\r\\nCollider.BOX = LS.PhysicsInstance.BOX;\\r\\nCollider.SPHERE = LS.PhysicsInstance.SPHERE;\\r\\nCollider.MESH = LS.PhysicsInstance.MESH;\\r\\n\\r\\n//vars\\r\\nCollider[\\\"@size\\\"] = { type: \\\"vec3\\\", step: 0.01 };\\r\\nCollider[\\\"@center\\\"] = { type: \\\"vec3\\\", step: 0.01 };\\r\\nCollider[\\\"@mesh\\\"] = { type: \\\"mesh\\\" };\\r\\nCollider[\\\"@shape\\\"] = { type:\\\"enum\\\", values: {\\\"Box\\\": Collider.BOX, \\\"Sphere\\\": Collider.SPHERE, \\\"Mesh\\\": Collider.MESH }};\\r\\n\\r\\n//Collider[\\\"@adjustToNodeBounding\\\"] = { type:\\\"action\\\" };\\r\\n\\r\\nCollider.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"collectPhysicInstances\\\", this.onGetColliders, this);\\r\\n}\\r\\n\\r\\nCollider.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind( scene, \\\"collectPhysicInstances\\\", this.onGetColliders, this);\\r\\n}\\r\\n\\r\\nCollider.prototype.getMesh = function() {\\r\\n\\tif(typeof(this.mesh) === \\\"string\\\")\\r\\n\\t\\treturn LS.ResourcesManager.meshes[this.mesh];\\r\\n\\treturn this.mesh;\\r\\n}\\r\\n\\r\\nCollider.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(!this.mesh) return;\\r\\n\\tif(typeof(this.mesh) == \\\"string\\\")\\r\\n\\t\\tres[this.mesh] = Mesh;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nCollider.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.mesh == old_name)\\r\\n\\t\\tthis.mesh = new_name;\\r\\n}\\r\\n\\r\\n/*\\r\\nCollider.prototype.adjustToNodeBounding = function()\\r\\n{\\r\\n\\tvar final_bounding = BBox.create();\\r\\n\\tvar components = this._root.getComponents();\\r\\n\\tfor(var i = 0: i < components.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar component = components[i];\\r\\n\\t\\tif(!component.getMesh)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar mesh = component.getMesh();\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar bounding = mesh.getBoundingBox();\\r\\n\\t\\tif(!bounding)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t//TODO: merge all the boundings\\r\\n\\t}\\r\\n}\\r\\n*/\\r\\n\\r\\nCollider.prototype.onGetColliders = function(e, colliders)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar PI = this._PI;\\r\\n\\tif(!PI)\\r\\n\\t\\tthis._PI = PI = new LS.PhysicsInstance(this._root, this);\\r\\n\\r\\n\\tif(this._root.transform)\\r\\n\\t\\tPI.matrix.set( this._root.transform._global_matrix );\\r\\n\\r\\n\\tPI.type = this.shape;\\r\\n\\tPI.layers = this._root.layers;\\r\\n\\r\\n\\t//get mesh\\r\\n\\tvar mesh = null;\\r\\n\\tif(PI.type === LS.PhysicsInstance.MESH || this.use_mesh_bounding)\\r\\n\\t\\tmesh = this.getMesh();\\r\\n\\r\\n\\t//spherical collider\\r\\n\\tif(PI.type === LS.PhysicsInstance.SPHERE)\\r\\n\\t{\\r\\n\\t\\tif(mesh)\\r\\n\\t\\t\\tBBox.copy( PI.oobb, mesh.bounding );\\r\\n\\t\\telse\\r\\n\\t\\t\\tBBox.setCenterHalfsize( PI.oobb, this.center, [this.size[0],this.size[0],this.size[0]]);\\r\\n\\t}\\r\\n\\telse if(PI.type === LS.PhysicsInstance.BOX)\\r\\n\\t{\\r\\n\\t\\tif(mesh)\\r\\n\\t\\t\\tBBox.copy( PI.oobb, mesh.bounding );\\r\\n\\t\\telse\\r\\n\\t\\t\\tBBox.setCenterHalfsize( PI.oobb, this.center, this.size);\\r\\n\\t}\\r\\n\\r\\n\\tif(mesh)\\r\\n\\t\\tvec3.copy( PI.center, BBox.getCenter( mesh.bounding ) );\\r\\n\\telse\\r\\n\\t\\tvec3.copy( PI.center, this.center );\\r\\n\\r\\n\\t//convert center from local to world space\\r\\n\\tvec3.transformMat4( PI.center, PI.center, PI.matrix );\\r\\n\\r\\n\\tif(PI.type === LS.PhysicsInstance.MESH)\\r\\n\\t{\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tPI.setMesh(mesh);\\r\\n\\t}\\r\\n\\r\\n\\tcolliders.push(PI);\\r\\n}\\r\\n\\r\\n\\r\\nLS.registerComponent( Collider );\\r\\n///@FILE:../src/components/customData.js\\r\\n///@INFO: UNCOMMON\\r\\n/** \\r\\n* This module allows to store custom data inside a node\\r\\n* properties have the form of:\\r\\n* - name:\\r\\n* - value:\\r\\n* - type:\\r\\n* @class CustomData\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\n\\r\\nfunction CustomData(o)\\r\\n{\\r\\n\\tthis._properties = [];\\r\\n\\tthis._properties_by_name = {};\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nObject.defineProperty( CustomData.prototype, \\\"properties\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tif(!v || v.constructor !== Array)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._properties.length = v.length;\\r\\n\\t\\tthis._properties_by_name = {};\\r\\n\\t\\tfor(var i = 0; i < v.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar p = v[i];\\r\\n\\t\\t\\tthis._properties[i] = p;\\r\\n\\t\\t\\tthis._properties_by_name[ p.name ] = p;\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._properties;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nCustomData.icon = \\\"mini-icon-bg.png\\\";\\r\\n\\r\\nCustomData.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tif(!node.custom)\\r\\n\\t\\tnode.custom = this;\\r\\n}\\r\\n\\r\\nCustomData.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tif(node.custom == this)\\r\\n\\t\\tdelete node.custom;\\r\\n}\\r\\n\\r\\nCustomData.prototype.getResources = function(res)\\r\\n{\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nCustomData.prototype.addProperty = function( property )\\r\\n{\\r\\n\\tthis._properties.push( property );\\r\\n\\tif( this._properties_by_name[ property.name ] )\\r\\n\\t\\tconsole.warn(\\\"CustomData: there is a property with the same name\\\");\\r\\n\\tthis._properties_by_name[ property.name ] = property;\\r\\n}\\r\\n\\r\\nCustomData.prototype.getProperty = function( name )\\r\\n{\\r\\n\\treturn this._properties_by_name[ name ];\\r\\n}\\r\\n\\r\\nCustomData.prototype.getPropertiesInfo = function()\\r\\n{\\r\\n\\treturn this._properties;\\r\\n}\\r\\n\\r\\nCustomData.prototype.updateProperty = function( property )\\r\\n{\\r\\n\\tthis._properties_by_name[ property.name ] = property;\\r\\n}\\r\\n\\r\\n//used for animation tracks\\r\\nCustomData.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tvar varname = path[0];\\r\\n\\tvar property = this._properties_by_name[ varname ];\\r\\n\\tif(!property)\\r\\n\\t\\treturn null;\\r\\n\\treturn {\\r\\n\\t\\tnode: this._root,\\r\\n\\t\\ttarget: property, //no this\\r\\n\\t\\tname: \\\"value\\\",\\r\\n\\t\\tvalue: property.value,\\r\\n\\t\\ttype: property.type\\r\\n\\t};\\r\\n}\\r\\n\\r\\nCustomData.prototype.setPropertyValueFromPath = function( path, value, offset )\\r\\n{\\r\\n\\toffset = offset || 0;\\r\\n\\r\\n\\tvar varname = path[offset];\\r\\n\\tvar property = this._properties_by_name[ varname ];\\r\\n\\tif(!property)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//assign\\r\\n\\tif(property.value && property.value.set)\\r\\n\\t\\tproperty.value.set( value ); //typed arrays\\r\\n\\telse\\r\\n\\t\\tproperty.value = value;\\r\\n}\\r\\n\\r\\nCustomData.prototype.get = function(name)\\r\\n{\\r\\n\\tvar p = this._properties_by_name[ name ];\\r\\n\\tif(p)\\r\\n\\t\\treturn p.value;\\r\\n}\\r\\n\\r\\n\\r\\nCustomData.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n}\\r\\n\\r\\nLS.registerComponent( CustomData );\\r\\nLS.CustomData = CustomData;\\r\\n///@FILE:../src/components/sprite.js\\r\\n///@INFO: UNCOMMON\\r\\nvar temp_vec3 = vec3.create();\\r\\n\\r\\nfunction Sprite(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.texture = null;\\r\\n\\tthis.blend_mode = LS.Blend.ALPHA;\\r\\n\\r\\n\\tthis._size = vec2.fromValues(100,100);\\r\\n\\tthis.frame = null; //string\\r\\n\\tthis.flip_x = false;\\r\\n\\tthis.filtering = true;\\r\\n\\r\\n\\tthis._area = vec4.fromValues(0,0,1,1);\\r\\n\\r\\n\\tthis._atlas = null; //uid\\r\\n\\tthis._atlas_component = null; //instance\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nSprite.icon = \\\"mini-icon-teapot.png\\\";\\r\\n\\r\\nSprite[\\\"@texture\\\"] = { type:\\\"texture\\\" };\\r\\nSprite[\\\"@blend_mode\\\"] = { type: \\\"enum\\\", values: LS.Blend };\\r\\nSprite[\\\"@atlas\\\"] = { type: \\\"component\\\", filter: \\\"SpriteAtlas\\\" };\\r\\nSprite[\\\"@area\\\"] = { type: \\\"vec4\\\", step: 0.001 };\\r\\n\\r\\nObject.defineProperty( Sprite.prototype, \\\"size\\\", {\\r\\n\\tset: function(v){ this._size.set(v); },\\r\\n\\tget: function(){ return this._size; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( Sprite.prototype, \\\"area\\\", {\\r\\n\\tset: function(v){ this._area.set(v); },\\r\\n\\tget: function(){ return this._area; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( Sprite.prototype, \\\"atlas\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tvar compo = null;\\r\\n\\t\\tif(v && v.constructor === String) //find it by uid\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._atlas = v;\\r\\n\\t\\t\\tvar scene = this._root.scene || LS.GlobalScene;\\r\\n\\t\\t\\tcompo = scene.findComponentByUId( v );\\r\\n\\t\\t\\tif(compo && compo.constructor != LS.Components.SpriteAtlas)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Atlas must be of type SpriteAtlas\\\");\\r\\n\\t\\t\\t\\tcompo = null;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse //instance\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._atlas = v ? v.uid : null;\\r\\n\\t\\t\\tcompo = v;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//attach to atlas\\r\\n\\t\\tif( this._atlas_component && this._atlas_component != compo )\\r\\n\\t\\t\\tthis._atlas_component.removeSprite( this );\\r\\n\\t\\tthis._atlas_component = compo;\\r\\n\\t\\tif(this._atlas_component)\\r\\n\\t\\t\\tthis._atlas_component.addSprite( this );\\r\\n\\t},\\r\\n\\tget: function(){ \\r\\n\\t\\treturn this._atlas;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n//we bind to onAddedToNode because the event is triggered per node so we know which RIs belong to which node\\r\\nSprite.prototype.onAddedToNode = function( node )\\r\\n{\\r\\n\\tLEvent.bind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n\\r\\n\\tif( this._atlas_component )\\r\\n\\t\\tthis._atlas_component.addSprite( this );\\r\\n}\\r\\n\\r\\nSprite.prototype.onRemovedFromNode = function( node )\\r\\n{\\r\\n\\tLEvent.unbind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n\\r\\n\\tif( this._atlas_component )\\r\\n\\t\\tthis._atlas_component.removeSprite( this );\\r\\n}\\r\\n\\r\\nSprite.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\tif( this._atlas_component )\\r\\n\\t\\tthis._atlas_component.addSprite( this );\\r\\n}\\r\\n\\r\\nSprite.prototype.onRemovedFromScene = function( scene )\\r\\n{\\r\\n\\tif( this._atlas_component )\\r\\n\\t\\tthis._atlas_component.removeSprite( this );\\r\\n}\\r\\n\\r\\n//MeshRenderer.prototype.getRenderInstance = function(options)\\r\\nSprite.prototype.onCollectInstances = function(e, instances)\\r\\n{\\r\\n\\tif( !this.enabled || this._atlas )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar node = this._root;\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//Mesh\\r\\n\\tvar mesh = LS.Components.Sprite._mesh;\\r\\n\\tif(!mesh)\\r\\n\\t\\tmesh = LS.Components.Sprite._mesh = GL.Mesh.plane();\\r\\n\\r\\n\\t//RI\\r\\n\\tvar RI = this._render_instance;\\r\\n\\tif(!RI)\\r\\n\\t{\\r\\n\\t\\tthis._render_instance = RI = new LS.RenderInstance(this._root, this);\\r\\n\\t\\tRI.setMesh( mesh, gl.TRIANGLES );\\r\\n\\t}\\r\\n\\r\\n\\t//material\\r\\n\\tif(!this._material)\\r\\n\\t\\tthis._material = new LS.StandardMaterial({ shader_name: \\\"lowglobal\\\", flags: { two_sided: true } });\\r\\n\\tthis._material.setTexture( \\\"color\\\", this.texture, { uvs: LS.Material.COORDS_UV_TRANSFORMED, magFilter: this.filtering ? gl.LINEAR : gl.NEAREST } );\\r\\n\\tthis._material.blend_mode = this.blend_mode;\\r\\n\\tRI.setMaterial( this._material ); //sets material and blend modes in render instance\\r\\n\\r\\n\\t//Matrix do not need to update\\r\\n\\tif( this._root.transform )\\r\\n\\t\\tRI.setMatrix( this._root.transform._global_matrix );\\r\\n\\tmat4.getTranslation( RI.center, this._root.transform._global_matrix );\\r\\n\\r\\n\\t//apply size and flip x \\r\\n\\ttemp_vec3[0] = this._size[0] * (this.flip_x ? -1 : 1);\\r\\n\\ttemp_vec3[1] = this._size[1];\\r\\n\\ttemp_vec3[2] = 1;\\r\\n\\tmat4.scale( RI.matrix, RI.matrix, temp_vec3 );\\r\\n\\r\\n\\t//area\\r\\n\\tmat3.identity( this._material.uvs_matrix );\\r\\n\\tmat3.translate( this._material.uvs_matrix, this._material.uvs_matrix, this._area.subarray(0,2) );\\r\\n\\tmat3.scale( this._material.uvs_matrix, this._material.uvs_matrix, this._area.subarray(2,4) );\\r\\n\\r\\n\\tinstances.push(RI);\\r\\n}\\r\\n\\r\\nSprite.prototype.getResources = function( res )\\r\\n{\\r\\n\\tif(typeof(this.texture) == \\\"string\\\")\\r\\n\\t\\tres[this.texture] = GL.Texture;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nSprite.prototype.onResourceRenamed = function( old_name, new_name, resource )\\r\\n{\\r\\n\\tif( this.texture == old_name )\\r\\n\\t\\tthis.texture = new_name;\\r\\n}\\r\\n\\r\\nLS.registerComponent( Sprite );\\r\\n///@FILE:../src/components/spriteAtlas.js\\r\\n///@INFO: UNCOMMON\\r\\nfunction SpriteAtlas( o )\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.texture = null;\\r\\n\\tthis.areas = [];\\r\\n\\r\\n\\tthis._sprites = [];\\r\\n\\tthis._max_sprites = 256;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nSpriteAtlas.icon = \\\"mini-icon-teapot.png\\\";\\r\\n\\r\\nSpriteAtlas[\\\"@texture\\\"] = { type:\\\"texture\\\" };\\r\\nSpriteAtlas[\\\"@areas\\\"] = { type: \\\"texture_areas\\\" };\\r\\n\\r\\nSpriteAtlas.plane_vertices = [vec3.fromValues(-0.5,0.5,0),vec3.fromValues(0.5,-0.5,0),vec3.fromValues(0.5,0.5,0),vec3.fromValues(-0.5,-0.5,0)];\\r\\nSpriteAtlas.plane_normal = vec3.fromValues(0,0,1);\\r\\n\\r\\nSpriteAtlas.prototype.onAddedToNode = function( node )\\r\\n{\\r\\n\\tLEvent.bind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nSpriteAtlas.prototype.onRemovedFromNode = function( node )\\r\\n{\\r\\n\\tLEvent.unbind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nSpriteAtlas.prototype.createMesh = function ()\\r\\n{\\r\\n\\tif( this._mesh_maxsprites == this._max_sprites )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//buffers\\r\\n\\tvar vertices = new Float32Array( this._max_sprites * 4 * 3); //6 vertex per particle x 3 floats per vertex\\r\\n\\tvar normals = new Float32Array( this._max_sprites * 4 * 3); //6 vertex per particle x 3 floats per vertex\\r\\n\\tvar coords = new Float32Array( this._max_sprites * 4 * 2);\\r\\n\\tvar colors = new Float32Array( this._max_sprites * 4 * 4);\\r\\n\\tvar indices = new Uint16Array( this._max_sprites * 6);\\r\\n\\r\\n\\t//this._default_vertices = new Float32Array([-0.5,0.5,0 , -0.5,-0.5,0, 0.5,0.5,0, 0.5,-0.5,0 ]);\\r\\n\\t//var default_indices = [0,1,2,0,3,1];\\r\\n\\tvar default_coords = new Float32Array([0,0, 1,1, 1,0, 0,1 ]);\\r\\n\\tvar default_color = new Float32Array([ 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1 ]);\\r\\n\\r\\n\\t//set to constant values\\r\\n\\tfor(var i = 0, l = this._max_sprites; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tcoords.set( default_coords , i*4*2);\\r\\n\\t\\tcolors.set( default_color , i*4*4);\\r\\n\\r\\n\\t\\tindices[i*6] = i*4;\\r\\n\\t\\tindices[i*6+1] = i*4+1;\\r\\n\\t\\tindices[i*6+2] = i*4+2;\\r\\n\\t\\tindices[i*6+3] = i*4;\\r\\n\\t\\tindices[i*6+4] = i*4+3;\\r\\n\\t\\tindices[i*6+5] = i*4+1;\\r\\n\\t}\\r\\n\\r\\n\\tthis._mesh = new GL.Mesh();\\r\\n\\tthis._mesh.addBuffers({ vertices: vertices, normals: normals, coords: coords, colors: colors }, {triangles: indices}, gl.STREAM_DRAW);\\r\\n\\tthis._mesh_maxsprites = this._max_sprites;\\r\\n}\\r\\n\\r\\nSpriteAtlas.prototype.updateMesh = function()\\r\\n{\\r\\n\\tif(!this._mesh)\\r\\n\\t\\tthis.createMesh();\\r\\n\\r\\n\\tvar mesh = this._mesh;\\r\\n\\tvar vertices_array = SpriteAtlas.plane_vertices;\\r\\n\\tvar N = SpriteAtlas.plane_normal;\\r\\n\\r\\n\\tvar vertices_buffer = mesh.getBuffer(\\\"vertices\\\");\\r\\n\\tvar normals_buffer = mesh.getBuffer(\\\"normals\\\");\\r\\n\\tvar coords_buffer = mesh.getBuffer(\\\"coords\\\");\\r\\n\\tvar vertices = vertices_buffer.data;\\r\\n\\tvar normals = normals_buffer.data;\\r\\n\\tvar coords = coords_buffer.data;\\r\\n\\r\\n\\tvar pos = 0;\\r\\n\\r\\n\\tfor(var i = 0, l = Math.min(this._sprites.length, this._max_sprites); i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar sprite = this._sprites[i];\\r\\n\\t\\tif(!sprite.enabled)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t//collect positions for every sprite\\r\\n\\t\\tvar matrix = null;\\r\\n\\t\\tif(sprite.matrix)\\r\\n\\t\\t\\tmatrix = sprite.matrix;\\r\\n\\t\\telse if( sprite._root && sprite._root.transform)\\r\\n\\t\\t\\tmatrix = sprite._root.transform._global_matrix;\\r\\n\\t\\telse\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tmat4.rotateVec3( temp_vec3, matrix, N );\\r\\n\\r\\n\\t\\tfor(var j = 0; j < 4; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar v_index = pos*4*3 + j*3;\\r\\n\\t\\t\\tvar vertex = vertices.subarray(v_index, v_index + 3);\\r\\n\\t\\t\\tvertex.set( vertices_array[j] );\\r\\n\\t\\t\\tvertex[0] *= sprite._size[0]; vertex[1] *= sprite._size[1];\\r\\n\\t\\t\\tvec3.transformMat4( vertex, vertex, matrix );\\r\\n\\t\\t\\tnormals.set( temp_vec3, v_index );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//var default_coords = new Float32Array([0,0, 1,1, 1,0, 0,1 ]);\\r\\n\\t\\tvar area = sprite._area;\\r\\n\\t\\tvar ax = area[0]; var ay = area[1]; var aw = area[2]; var ah = area[3];\\r\\n\\t\\tcoords.set( [ax,ay, ax + aw,ay + ah, ax + aw,ay, ax,ay + ah ], pos*4*2);\\r\\n\\r\\n\\t\\tpos += 1;\\r\\n\\t}\\r\\n\\r\\n\\tif(pos)\\r\\n\\t{\\r\\n\\t\\t//TODO: upload range only\\r\\n\\t\\tvertices_buffer.upload();\\r\\n\\t\\tnormals_buffer.upload();\\r\\n\\t\\tcoords_buffer.upload();\\r\\n\\t}\\r\\n\\r\\n\\tthis._last_index = pos * 6;\\r\\n}\\r\\n\\r\\n//MeshRenderer.prototype.getRenderInstance = function(options)\\r\\nSpriteAtlas.prototype.onCollectInstances = function(e, instances)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar node = this._root;\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this._sprites.length)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//Mesh\\r\\n\\tthis.updateMesh();\\r\\n\\tvar mesh = this._mesh;\\r\\n\\r\\n\\tif(!this._last_index)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//RI\\r\\n\\tvar RI = this._render_instance;\\r\\n\\tif(!RI)\\r\\n\\t{\\r\\n\\t\\tthis._render_instance = RI = new LS.RenderInstance(this._root, this);\\r\\n\\t\\tRI.setMesh( mesh, gl.TRIANGLES );\\r\\n\\t}\\r\\n\\r\\n\\t//material\\r\\n\\tif(!this._material)\\r\\n\\t\\tthis._material = new LS.StandardMaterial({ shader_name: \\\"lowglobal\\\", flags: { two_sided: true } });\\r\\n\\tthis._material.setTexture( \\\"COLOR\\\", this.texture );\\r\\n\\tRI.setMaterial( this._material ); //sets material and blend modes in render instance\\r\\n\\r\\n\\t//flags\\r\\n\\tRI.setRange(0, this._last_index);\\r\\n\\r\\n\\t//opaque RIs\\r\\n\\tinstances.push(RI);\\r\\n}\\r\\n\\r\\n//attach sprites to this atlas\\r\\nSpriteAtlas.prototype.addSprite = function( sprite )\\r\\n{\\r\\n\\tvar index = this._sprites.indexOf( sprite );\\r\\n\\tif(index == -1)\\r\\n\\t\\tthis._sprites.push( sprite );\\r\\n}\\r\\n\\r\\nSpriteAtlas.prototype.removeSprite = function( sprite )\\r\\n{\\r\\n\\tvar index = this._sprites.indexOf( sprite );\\r\\n\\tif(index != -1)\\r\\n\\t\\tthis._sprites.splice( index, 1 );\\r\\n}\\r\\n\\r\\n\\r\\nSpriteAtlas.prototype.getResources = function( res )\\r\\n{\\r\\n\\tif(typeof(this.texture) == \\\"string\\\")\\r\\n\\t\\tres[this.texture] = GL.Texture;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nSpriteAtlas.prototype.onResourceRenamed = function( old_name, new_name, resource )\\r\\n{\\r\\n\\tif( this.texture == old_name )\\r\\n\\t\\tthis.texture = new_name;\\r\\n}\\r\\n\\r\\nLS.registerComponent( SpriteAtlas );\\r\\n\\r\\nSpriteAtlas.Area = function SpriteAtlasArea()\\r\\n{\\r\\n\\tthis._start = vec2.create();\\r\\n\\tthis._size = vec2.create();\\r\\n}\\r\\n///@FILE:../src/components/sceneInclude.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Allows to include a secondary scene inside this scene (with some limitations)\\r\\n* @class SceneInclude\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\n\\r\\nfunction SceneInclude( o )\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.include_instances = true;\\r\\n\\tthis.include_cameras = true;\\r\\n\\tthis.include_lights = true;\\r\\n\\tthis._frame_fx = false;\\r\\n\\tthis._frame_fx_binded = false;\\r\\n\\r\\n\\tthis.send_events = true;\\r\\n\\r\\n\\tthis._scene_path = null;\\r\\n\\tthis._scene_is_ready = false;\\r\\n\\r\\n\\tif( LS.Scene ) //this is because in some cases (debug mode) this component will be registered before the Scene exists\\r\\n\\t{\\r\\n\\t\\tthis._scene = new LS.Scene();\\r\\n\\t\\tthis._scene.root.removeAllComponents();\\r\\n\\t\\tLEvent.bind( this._scene, \\\"requestFrame\\\", function(){ \\r\\n\\t\\t\\tif(this._root.scene)\\r\\n\\t\\t\\t\\tthis._root.scene.requestFrame();\\r\\n\\t\\t}, this );\\r\\n\\t}\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nSceneInclude.max_recursive_level = 32;\\r\\nSceneInclude.recursive_level = 0;\\r\\n\\r\\n//which events from the scene should be propagated to the included scene...\\r\\nSceneInclude.propagable_events = [\\\"finish\\\",\\\"beforeRenderMainPass\\\",\\\"beforeRenderInstances\\\",\\\"afterRenderInstances\\\",\\\"readyToRender\\\",\\\"renderGUI\\\",\\\"renderHelpers\\\"];\\r\\nSceneInclude.fx_propagable_events = [\\\"enableFrameContext\\\",\\\"showFrameContext\\\"];\\r\\n\\r\\nObject.defineProperty( SceneInclude.prototype, \\\"scene_path\\\", {\\r\\n\\tset: function(v){ \\r\\n\\t\\tif(this._scene_path == v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._scene_path = v;\\r\\n\\t\\tif(this._root.scene)\\r\\n\\t\\t\\tthis.reloadScene();\\r\\n\\t},\\r\\n\\tget: function(){ return this._scene_path; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( SceneInclude.prototype, \\\"frame_fx\\\", {\\r\\n\\tset: function(v){ \\r\\n\\t\\tif(this._frame_fx == v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._frame_fx = v;\\r\\n\\t\\tthis.updateBindings();\\r\\n\\t},\\r\\n\\tget: function(){ return this._frame_fx; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\nSceneInclude[\\\"@scene_path\\\"] = { type: LS.TYPES.SCENE, widget: \\\"resource\\\" };\\r\\n\\r\\nSceneInclude.icon = \\\"mini-icon-teapot.png\\\";\\r\\n\\r\\n\\r\\nSceneInclude.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\t//bind events\\r\\n\\tLEvent.bind( scene, \\\"collectData\\\", this.onCollectData, this );\\r\\n\\tLEvent.bind( scene, \\\"start\\\", this.onStart, this );\\r\\n\\tLEvent.bind( scene, \\\"update\\\", this.onUpdate, this );\\r\\n\\r\\n\\tfor(var i in SceneInclude.propagable_events)\\r\\n\\t\\tLEvent.bind( scene, SceneInclude.propagable_events[i], this.onEvent, this );\\r\\n\\tthis.updateBindings();\\r\\n\\r\\n\\tif(this._scene_path)\\r\\n\\t\\tthis.reloadScene();\\r\\n\\r\\n}\\r\\n\\r\\nSceneInclude.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind( scene, \\\"collectData\\\", this.onCollectData, this );\\r\\n\\tLEvent.unbind( scene, \\\"start\\\", this.onStart, this );\\r\\n\\tLEvent.unbind( scene, \\\"update\\\", this.onUpdate, this );\\r\\n\\r\\n\\t//unbind all\\r\\n\\tvar events = SceneInclude.propagable_events.concat( SceneInclude.fx_propagable_events );\\r\\n\\tfor(var i in events)\\r\\n\\t\\tLEvent.unbind( scene, events[i], this.onEvent, this );\\r\\n}\\r\\n\\r\\n//we need special functions for this events because they need function calls, not events\\r\\nSceneInclude.prototype.onStart = function()\\r\\n{\\r\\n\\tSceneInclude.recursive_level += 1;\\r\\n\\tif(\\tthis._scene_is_ready && SceneInclude.recursive_level < SceneInclude.max_recursive_level )\\r\\n\\t\\tthis._scene.start();\\r\\n\\tSceneInclude.recursive_level -= 1;\\r\\n}\\r\\n\\r\\nSceneInclude.prototype.onUpdate = function(e, dt)\\r\\n{\\r\\n\\tSceneInclude.recursive_level += 1;\\r\\n\\tif(this.send_events && SceneInclude.recursive_level < SceneInclude.max_recursive_level )\\r\\n\\t\\tthis._scene.update(dt);\\r\\n\\tSceneInclude.recursive_level -= 1;\\r\\n}\\r\\n\\r\\n//propagate events\\r\\nSceneInclude.prototype.onEvent = function(e,p)\\r\\n{\\r\\n\\tif(!this.enabled || !this.send_events || !this._scene_path)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tSceneInclude.recursive_level += 1;\\r\\n\\tif(SceneInclude.recursive_level < SceneInclude.max_recursive_level )\\r\\n\\t\\tLEvent.trigger( this._scene, e, p );\\r\\n\\tSceneInclude.recursive_level -= 1;\\r\\n}\\r\\n\\r\\nSceneInclude.prototype.updateBindings = function()\\r\\n{\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//update frameFX bindings\\r\\n\\tif(this._frame_fx && !this._frame_fx_binded)\\r\\n\\t{\\r\\n\\t\\tfor(var i in SceneInclude.fx_propagable_events)\\r\\n\\t\\t\\tLEvent.bind( scene, SceneInclude.fx_propagable_events[i], this.onEvent, this );\\r\\n\\t\\tthis._frame_fx_binded = true;\\r\\n\\t}\\r\\n\\r\\n\\tif(!this._frame_fx && this._frame_fx_binded)\\r\\n\\t{\\r\\n\\t\\tfor(var i in SceneInclude.fx_propagable_events)\\r\\n\\t\\t\\tLEvent.unbind( scene, SceneInclude.fx_propagable_events[i], this.onEvent, this );\\r\\n\\t\\tthis._frame_fx_binded = false;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//collect data\\r\\nSceneInclude.prototype.onCollectData = function()\\r\\n{\\r\\n\\tif(!this.enabled || !this._scene_path || !this._scene_is_ready)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tvar inner_scene = this._scene;\\r\\n\\r\\n\\tSceneInclude.recursive_level += 1;\\r\\n\\tif(SceneInclude.recursive_level < SceneInclude.max_recursive_level )\\r\\n\\t\\tinner_scene.collectData();\\r\\n\\tSceneInclude.recursive_level -= 1;\\r\\n\\r\\n\\tvar mat = null;\\r\\n\\t\\r\\n\\tif( this._root.transform )\\r\\n\\t{\\r\\n\\t\\tmat = this._root.transform.getGlobalMatrix();\\r\\n\\t}\\r\\n\\t\\r\\n\\r\\n\\t//merge all the data\\r\\n\\tif( this.include_instances )\\r\\n\\t{\\r\\n\\t\\tscene._instances.push.apply( scene._instances, inner_scene._instances);\\r\\n\\t\\tscene._colliders.push.apply( scene._colliders, inner_scene._colliders);\\r\\n\\r\\n\\t\\tif(mat)\\r\\n\\t\\t\\tfor(var i = 0; i < inner_scene._instances.length; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar ri = inner_scene._instances[i];\\r\\n\\t\\t\\t\\tri.applyTransform( mat );\\t\\r\\n\\t\\t\\t}\\r\\n\\t}\\r\\n\\tif( this.include_lights )\\r\\n\\t{\\r\\n\\t\\t//cannot apply transform here, we will be modifying the inner lights state\\r\\n\\t\\tscene._lights.push.apply( scene._lights, inner_scene._lights);\\r\\n\\t}\\r\\n\\tif( this.include_cameras )\\r\\n\\t{\\r\\n\\t\\tscene._cameras.push.apply( scene._cameras, inner_scene._cameras);\\r\\n\\t}\\r\\n}\\r\\n\\r\\nSceneInclude.prototype.load = function()\\r\\n{\\r\\n\\tthis.reloadScene();\\r\\n}\\r\\n\\r\\nSceneInclude.prototype.unload = function()\\r\\n{\\r\\n\\tthis._scene_is_ready = false;\\r\\n\\tthis._scene.clear();\\r\\n}\\r\\n\\r\\n\\r\\nSceneInclude.prototype.reloadScene = function()\\r\\n{\\r\\n\\tthis._scene_is_ready = false;\\r\\n\\r\\n\\tSceneInclude.recursive_level += 1;\\r\\n\\tif(SceneInclude.recursive_level < SceneInclude.max_recursive_level )\\r\\n\\t\\tthis._scene.loadFromResources( this._scene_path, inner.bind(this) );\\r\\n\\tSceneInclude.recursive_level -= 1;\\r\\n\\r\\n\\tfunction inner()\\r\\n\\t{\\r\\n\\t\\tconsole.log(\\\"SceneInclude: scene loaded\\\");\\r\\n\\t\\tthis._scene_is_ready = true;\\r\\n\\t\\tif(this._root.scene._state == LS.PLAYING )\\r\\n\\t\\t\\tthis._scene.start();\\r\\n\\t}\\r\\n}\\r\\n\\r\\nSceneInclude.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif( !path.length || path[0] != \\\"custom\\\" )\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar custom = this._scene.root.custom;\\r\\n\\tif(!custom)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar varname = path[1];\\r\\n\\tvar property = custom._properties_by_name[ varname ];\\r\\n\\tif(!property)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\treturn {\\r\\n\\t\\tnode: this._root,\\r\\n\\t\\ttarget: property,\\r\\n\\t\\tname: \\\"value\\\",\\r\\n\\t\\tvalue: property.value,\\r\\n\\t\\ttype: property.type\\r\\n\\t};\\r\\n}\\r\\n\\r\\nSceneInclude.prototype.setPropertyValueFromPath = function( path, value, offset )\\r\\n{\\r\\n\\toffset = offset || 0;\\r\\n\\r\\n\\tif( !path.length || path[offset] != \\\"custom\\\" )\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar custom = this._scene.root.custom;\\r\\n\\tif(!custom)\\r\\n\\t\\treturn null;\\r\\n\\tcustom.setPropertyValueFromPath( path, value, offset + 1 );\\r\\n}\\r\\n\\r\\n//returns which events can trigger this component\\r\\nSceneInclude.prototype.getEvents = function()\\r\\n{\\r\\n\\treturn { \\\"loaded\\\": \\\"event\\\", \\\"unloaded\\\": \\\"event\\\" };\\r\\n}\\r\\n\\r\\n//returns which actions can be triggered in this component\\r\\nSceneInclude.prototype.getActions = function()\\r\\n{\\r\\n\\treturn { \\\"load\\\": \\\"function\\\", \\\"unload\\\": \\\"function\\\" };\\r\\n}\\r\\n\\r\\nSceneInclude.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(this._scene_path)\\r\\n\\t\\tres[ this._scene_path ] = LS.Scene;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nSceneInclude.prototype.onResourceRenamed = function( old_name, new_name, resource )\\r\\n{\\r\\n\\tif(old_name == this._scene_path)\\r\\n\\t\\tthis._scene_path = new_name;\\r\\n}\\r\\n\\r\\nLS.registerComponent( SceneInclude );\\r\\n///@FILE:../src/components/helper.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Spline allows to define splines in 3D\\r\\n* @class Spline\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\n\\r\\nfunction Helper( o )\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.in_editor = true;\\r\\n\\tthis.in_player = false;\\r\\n\\tthis.type = Helper.CIRCLE;\\r\\n\\tthis.color = [1,1,1,1];\\r\\n\\tthis.size = 1;\\r\\n\\tthis.fill = false;\\r\\n\\tthis.always_on_top = true;\\r\\n\\tthis.vertical = true;\\r\\n\\r\\n\\tthis._is_visible = false;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nHelper[\\\"@color\\\"] = { type: \\\"vec4\\\", step:0.01 };\\r\\n\\r\\nHelper.CIRCLE = 1;\\r\\nHelper.SQUARE = 2;\\r\\n\\r\\nHelper[\\\"@subdivisions\\\"] = { type: \\\"number\\\", step:1, min:1, max:100, precision:0 };\\r\\nHelper[\\\"@type\\\"] = { type: \\\"enum\\\", values: { circle: Helper.CIRCLE, square: Helper.SQUARE } };\\r\\n\\r\\nHelper.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( LS.Renderer, \\\"renderHelpers\\\", this.onRender, this );\\r\\n}\\r\\n\\r\\nHelper.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind( LS.Renderer, \\\"renderHelpers\\\", this.onRender, this );\\r\\n}\\r\\n\\r\\nHelper.prototype.onRender = function( event, camera )\\r\\n{\\r\\n\\tif( !this.enabled || !((LS.Renderer._in_player && this.in_player) || (!LS.Renderer._in_player && this.in_editor)) )\\r\\n\\t{\\r\\n\\t\\tthis._is_visible = false;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tthis._is_visible = true;\\r\\n\\r\\n\\tvar node = this._root;\\r\\n\\tif(!node || !node.visible || !node.transform)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar m = node.transform.getGlobalMatrixRef();\\r\\n\\tgl.enable( gl.BLEND );\\r\\n\\tgl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );\\r\\n\\tthis.renderHelper( m, this.color, this.fill );\\r\\n\\tgl.disable( gl.BLEND );\\r\\n}\\r\\n\\r\\nHelper.prototype.renderHelper = function( model, color, fill )\\r\\n{\\r\\n\\tif(this.always_on_top)\\r\\n\\t\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\r\\n\\tgl.disable( gl.CULL_FACE );\\r\\n\\tLS.Draw.push();\\r\\n\\tLS.Draw.setMatrix( model );\\r\\n\\tLS.Draw.setColor( color );\\r\\n\\tif(this.type == Helper.CIRCLE )\\r\\n\\t\\tLS.Draw.renderCircle( this.size, 32, !this.vertical, fill );\\r\\n\\telse if(this.type == Helper.SQUARE )\\r\\n\\t{\\r\\n\\t\\tif(fill)\\r\\n\\t\\t\\tLS.Draw.renderRectangle( this.size, this.size, !this.vertical, true );\\r\\n\\t\\telse\\r\\n\\t\\t\\tLS.Draw.renderRectangle( this.size, this.size, !this.vertical );\\r\\n\\t\\t\\t//LS.Draw.renderSolidBox( this.size, this.vertical ? this.size : 0, this.vertical ? 0 : this.size );\\r\\n\\t}\\r\\n\\tLS.Draw.pop();\\r\\n\\tgl.enable( gl.CULL_FACE );\\r\\n\\tgl.enable( gl.DEPTH_TEST );\\r\\n}\\r\\n\\r\\nHelper.prototype.renderPicking = function( ray )\\r\\n{\\r\\n\\tif(!this._is_visible)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar model = this._root.transform.getGlobalMatrixRef(true);\\r\\n\\tvar color = LS.Picking.getNextPickingColor( { instance: this } );\\r\\n\\tthis.renderHelper( model, color, true );\\r\\n}\\r\\n\\r\\n\\r\\nLS.registerComponent( Helper );\\r\\n\\r\\n\\r\\n///@FILE:../src/components/annotations.js\\r\\n///@INFO: UNCOMMON\\r\\nfunction AnnotationComponent(o)\\r\\n{\\r\\n\\tthis.text = \\\"\\\";\\r\\n\\tthis.notes = [];\\r\\n\\tthis._screen_pos = vec3.create();\\r\\n\\tthis._selected = null;\\r\\n\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nAnnotationComponent.editor_color = [0.33,0.874,0.56,0.9];\\r\\n\\r\\n\\r\\nAnnotationComponent.onShowMainAnnotation = function (node)\\r\\n{\\r\\n\\tif(typeof(AnnotationModule) != \\\"undefined\\\")\\r\\n\\t\\tAnnotationModule.editAnnotation(node);\\r\\n}\\r\\n\\r\\nAnnotationComponent.onShowPointAnnotation = function (node, note)\\r\\n{\\r\\n\\tvar comp = node.getComponent( AnnotationComponent );\\r\\n\\tif(!comp) return;\\r\\n\\r\\n\\t//in editor...\\r\\n\\tif(typeof(AnnotationModule) != \\\"undefined\\\")\\r\\n\\t{\\r\\n\\t\\tAnnotationModule.showDialog( note.text, { \\r\\n\\t\\t\\titem: note, \\r\\n\\t\\t\\ton_close: inner_update_note.bind(note), \\r\\n\\t\\t\\ton_delete: function(info) { \\r\\n\\t\\t\\t\\tcomp.removeAnnotation(info.item);\\r\\n\\t\\t\\t\\tthis._root.scene.requestFrame();\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\ton_focus: function(info) { \\r\\n\\t\\t\\t\\tAnnotationModule.focusInAnnotation(info.item);\\r\\n\\t\\t\\t\\tcomp._selected = info.item;\\r\\n\\t\\t\\t}});\\r\\n\\t}\\r\\n\\r\\n\\r\\n\\tfunction inner_update_note(text)\\r\\n\\t{\\r\\n\\t\\tthis.text = text;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nAnnotationComponent.prototype.addAnnotation = function(item)\\r\\n{\\r\\n\\tthis._selected = null;\\r\\n\\tthis.notes.push(item);\\r\\n}\\r\\n\\r\\nAnnotationComponent.prototype.getAnnotation = function(index)\\r\\n{\\r\\n\\treturn this.nodes[ index ];\\r\\n}\\r\\n\\r\\nAnnotationComponent.prototype.removeAnnotation = function(item)\\r\\n{\\r\\n\\tthis._selected = null;\\r\\n\\tvar pos = this.notes.indexOf(item);\\r\\n\\tif(pos != -1)\\r\\n\\t\\tthis.notes.splice(pos,1);\\r\\n}\\r\\n\\r\\nAnnotationComponent.prototype.setStartTransform = function()\\r\\n{\\r\\n\\tthis.start_position = this.getObjectCenter();\\r\\n}\\r\\n\\r\\nAnnotationComponent.prototype.getObjectCenter = function()\\r\\n{\\r\\n\\tvar center = vec3.create();\\r\\n\\tvar mesh = this._root.getMesh();\\r\\n\\tif(mesh && mesh.bounding )\\r\\n\\t\\tvec3.copy( center, BBox.getCenter(mesh.bounding) );\\r\\n\\tvar pos = this._root.transform.localToGlobal( center );\\r\\n\\treturn pos;\\r\\n}\\r\\n\\r\\nAnnotationComponent.prototype.serialize = function()\\r\\n{\\r\\n\\tvar o = {\\r\\n\\t\\tobject_class: \\\"AnnotationComponent\\\",\\r\\n\\t\\ttext: this.text,\\r\\n\\t\\tnotes: [],\\r\\n\\t\\tstart_position: this.start_position\\r\\n\\t};\\r\\n\\t\\r\\n\\tfor(var i = 0; i < this.notes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar note = this.notes[i];\\r\\n\\t\\tfor(var j in note)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(note[j].constructor == Float32Array)\\r\\n\\t\\t\\t\\tArray.prototype.slice.call( note[j] );\\r\\n\\t\\t}\\r\\n\\t\\to.notes.push(note);\\r\\n\\t}\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\nAnnotationComponent.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene,\\\"mousedown\\\",this.onMouse,this);\\r\\n}\\r\\n\\r\\nAnnotationComponent.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene,\\\"mousedown\\\",this.onMouse,this);\\r\\n}\\r\\n\\r\\nAnnotationComponent.prototype.onMouse = function(type, e)\\r\\n{\\r\\n\\tif(e.eventType == \\\"mousedown\\\")\\r\\n\\t{\\r\\n\\t\\tvar node = this._root;\\r\\n\\t\\tthis._screen_pos[2] = 0;\\r\\n\\t\\tvar dist = vec3.dist( this._screen_pos, [e.canvasx, gl.canvas.height - e.canvasy, 0] );\\r\\n\\t\\tif(dist < 30)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar that = this;\\r\\n\\t\\t\\tAnnotationComponent.onShowMainAnnotation(this._root);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var i = 0; i < this.notes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar note = this.notes[i];\\r\\n\\t\\t\\tdist = vec2.dist( note._end_screen, [e.mousex, gl.canvas.height - e.mousey] );\\r\\n\\t\\t\\tif(dist < 30)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._selected = note;\\r\\n\\t\\t\\t\\tAnnotationComponent.onShowPointAnnotation(this._root, note);\\r\\n\\t\\t\\t\\treturn true;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nAnnotationComponent.prototype.renderEditor = function( selected )\\r\\n{\\r\\n\\tif(!this.text && !this.notes.length)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar center = vec3.create();\\r\\n\\tvar mesh = this._root.getMesh();\\r\\n\\tif(mesh)\\r\\n\\t\\tvec3.copy( center, BBox.getCenter(mesh.bounding) );\\r\\n\\r\\n\\tvar camera = LS.Renderer._current_camera;\\r\\n\\r\\n\\tvar pos = this._root.transform.getGlobalPosition();\\r\\n\\tvar object_center = this.getObjectCenter();\\r\\n\\tvar camera_eye = camera.getEye();\\r\\n\\tvar right = camera.getLocalVector([1,0,0]);\\r\\n\\tvar top = \\tcamera.getLocalVector([0,1,0]);\\r\\n\\tvar front = camera.getLocalVector([0,0,1]);\\r\\n\\r\\n\\tvar f = Math.tan(camera.fov*DEG2RAD) * vec3.dist( pos, camera_eye );\\r\\n\\r\\n\\t//why? to scale the icon?\\r\\n\\tvar icon_top = vec3.scale(vec3.create(), top, f * 0.2);\\r\\n\\tvar icon_right = vec3.scale(vec3.create(), right, f * 0.2);\\r\\n\\tvar icon_pos = vec3.add( vec3.create(), pos, icon_top );\\r\\n\\tvec3.add( icon_pos, icon_right, icon_pos);\\r\\n\\r\\n\\tcamera.project( icon_pos, null, this._screen_pos );\\r\\n\\t//var right = camera.getLocalVector([10,0,0]);\\r\\n\\t//trace(this._screen_pos);\\r\\n\\r\\n\\tgl.enable(gl.BLEND);\\r\\n\\tgl.enable(gl.DEPTH_TEST);\\r\\n\\tLS.Draw.setColor([1,1,1,1]);\\r\\n\\r\\n\\tvar lines = [];\\r\\n\\tvar lines_colors = [];\\r\\n\\tvar points = [];\\r\\n\\tvar points_colors = [];\\r\\n\\r\\n\\tif(this.text)\\r\\n\\t{\\r\\n\\t\\tlines.push(pos, icon_pos);\\r\\n\\t\\tlines_colors.push( [1,1,1,0],[1,1,1,1]);\\r\\n\\t\\t//Draw.setColor([0.33,0.874,0.56,1.0]);\\r\\n\\t\\tif( window.EditorModule )\\r\\n\\t\\t\\tLS.Draw.renderImage( icon_pos, EditorModule.icons_path + \\\"/mini-icon-script.png\\\",f * 0.03);\\r\\n\\t}\\r\\n\\r\\n\\tvar model = this._root.transform.getGlobalMatrix();\\r\\n\\r\\n\\t//notes\\r\\n\\tfor(var i = 0; i < this.notes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar note = this.notes[i];\\r\\n\\t\\tvar start = mat4.multiplyVec3( vec3.create(), model, note.start );\\r\\n\\t\\tvar end = mat4.multiplyVec3( vec3.create(), model, note.end );\\r\\n\\t\\tnote.end_world = end;\\r\\n\\r\\n\\t\\tpoints.push( end );\\r\\n\\t\\tlines.push( start, end );\\r\\n\\r\\n\\t\\tif(this._selected == note)\\r\\n\\t\\t{\\r\\n\\t\\t\\tpoints_colors.push( [1,1,1,1] );\\r\\n\\t\\t\\tlines_colors.push( [1,1,1,0.2],[1,1,0.8,1]);\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tpoints_colors.push( LS.Components.AnnotationComponent.editor_color );\\r\\n\\t\\t\\tlines_colors.push( [0,0,0,0.2], LS.Components.AnnotationComponent.editor_color );\\r\\n\\t\\t}\\r\\n\\t\\tnote._end_screen = camera.project( end );\\r\\n\\t}\\r\\n\\r\\n\\t//transform\\r\\n\\tvar start = this.start_position;\\r\\n\\tif(start && vec3.dist(start, object_center) > 1)\\r\\n\\t{\\r\\n\\t\\t//dashed line...\\r\\n\\t\\tvar dist = vec3.dist(start, object_center);\\r\\n\\t\\tvar line_dist = dist / 20.0;\\r\\n\\t\\tvar delta = vec3.subtract(vec3.create(), object_center, start );\\r\\n\\t\\tvec3.normalize(delta, delta);\\r\\n\\t\\tfor(var i = 0; i < 20; i += 2)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar temp = vec3.scale(vec3.create(), delta, i*line_dist );\\r\\n\\t\\t\\tvec3.add(temp, temp, start);\\r\\n\\t\\t\\tlines.push(temp);\\r\\n\\t\\t\\t\\r\\n\\t\\t\\ttemp = vec3.scale(vec3.create(), delta,(i+1)*line_dist );\\r\\n\\t\\t\\tvec3.add(temp, temp, start);\\r\\n\\t\\t\\tlines.push(temp);\\r\\n\\t\\t\\tlines_colors.push( [0,1,0,0.2],[0,1,0,1]);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//render in two passes to have the cool semitransparent effect \\r\\n\\tLS.Draw.setPointSize( 12 );\\r\\n\\tLS.Draw.renderPoints(points, points_colors);\\r\\n\\r\\n\\tLS.Draw.setColor( [0,0,0,0.5] );\\r\\n\\tLS.Draw.setPointSize( 10 );\\r\\n\\tLS.Draw.renderPoints(points, points_colors);\\r\\n\\r\\n\\tLS.Draw.setColor([1,1,1,1]);\\r\\n\\tLS.Draw.renderLines(lines, lines_colors);\\r\\n\\r\\n\\tgl.depthFunc( gl.GREATER );\\r\\n\\r\\n\\tLS.Draw.setAlpha(0.1);\\r\\n\\tLS.Draw.renderPoints(points, points_colors);\\r\\n\\tLS.Draw.renderLines(lines, lines_colors);\\r\\n\\r\\n\\tgl.depthFunc( gl.LESS );\\r\\n\\r\\n\\t//texts\\r\\n\\tif( gl.start2D )\\r\\n\\t{\\r\\n\\t\\tLS.Draw.setColor( LS.Components.AnnotationComponent.editor_color );\\r\\n\\t\\tvar pos2D = vec3.create();\\r\\n\\t\\tgl.start2D();\\r\\n\\t\\tgl.fillColor = LS.Components.AnnotationComponent.editor_color;\\r\\n\\t\\tgl.font = \\\"20px Arial\\\";\\r\\n\\r\\n\\t\\tfor(var i = 0; i < this.notes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar note = this.notes[i];\\r\\n\\t\\t\\tcamera.project( note.end_world, null, pos2D, true );\\r\\n\\t\\t\\tvar first_line = note.text.split(\\\"\\\\n\\\")[0];\\r\\n\\t\\t\\tgl.fillText( first_line, pos2D[0] + 10, pos2D[1] + 8);\\r\\n\\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\tLS.Draw.push();\\r\\n\\t\\t\\t//Draw.lookAt( note.end_world, camera_eye, [0,1,0] );\\r\\n\\t\\t\\tLS.Draw.fromTranslationFrontTop(note.end_world, front, top );\\r\\n\\r\\n\\t\\t\\tLS.Draw.translate( [-1,-1,0] );\\r\\n\\t\\t\\tLS.Draw.scale( [-0.0004 * f,0.0004 * f,0.0004 * f] );\\r\\n\\t\\t\\tvar first_line = note.text.split(\\\"\\\\n\\\")[0];\\r\\n\\t\\t\\tLS.Draw.renderText( first_line );\\r\\n\\t\\t\\t//Draw.renderWireBox(10,10,10);\\r\\n\\t\\t\\tLS.Draw.pop();\\r\\n\\t\\t\\t*/\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgl.disable(gl.BLEND);\\r\\n}\\r\\n\\r\\n\\r\\nLS.registerComponent( AnnotationComponent );\\r\\n///@FILE:../src/components/animator.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Rotator rotate a mesh over time\\r\\n* @class Rotator\\r\\n* @namespace Components\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\n\\r\\nfunction Rotator(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.speed = 10;\\r\\n\\tthis.axis = [0,1,0];\\r\\n\\tthis.local_space = true;\\r\\n\\tthis.swing = false;\\r\\n\\tthis.swing_amplitude = 45;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nRotator.icon = \\\"mini-icon-rotator.png\\\";\\r\\n\\r\\nRotator.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind(scene,\\\"update\\\",this.onUpdate,this);\\r\\n}\\r\\n\\r\\n\\r\\nRotator.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind(scene,\\\"update\\\",this.onUpdate,this);\\r\\n}\\r\\n\\r\\nRotator.prototype.onUpdate = function(e,dt)\\r\\n{\\r\\n\\tif(!this._root || !this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\r\\n\\tif(!this._default)\\r\\n\\t\\tthis._default = this._root.transform.getRotation();\\r\\n\\r\\n\\tvec3.normalize(this.axis,this.axis);\\r\\n\\r\\n\\tif(this.swing)\\r\\n\\t{\\r\\n\\t\\tvar R = quat.setAxisAngle(quat.create(), this.axis, Math.sin( this.speed * scene._global_time * 2 * Math.PI) * this.swing_amplitude * DEG2RAD );\\r\\n\\t\\tquat.multiply( this._root.transform._rotation, R, this._default);\\r\\n\\t\\tthis._root.transform._must_update = true;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tif(this.local_space)\\r\\n\\t\\t\\tthis._root.transform.rotate(this.speed * dt,this.axis);\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis._root.transform.rotateGlobal(this.speed * dt,this.axis);\\r\\n\\t}\\r\\n\\r\\n\\tif(scene)\\r\\n\\t\\tscene.requestFrame();\\r\\n}\\r\\n\\r\\nLS.registerComponent( Rotator );\\r\\n///@FILE:../src/components/cameraController.js\\r\\n/**\\r\\n* Camera controller\\r\\n* Allows to move a camera with the user input. It uses the first camera attached to the same node\\r\\n* @class CameraController\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\n\\r\\nfunction CameraController(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\r\\n\\tthis.no_button_action = CameraController.NONE;\\r\\n\\tthis.left_button_action = CameraController.ORBIT;\\r\\n\\tthis.right_button_action = CameraController.PAN;\\r\\n\\tthis.middle_button_action = CameraController.PAN;\\r\\n\\t//this.touch_button_action = CameraController.ORBIT;\\r\\n\\tthis.mouse_wheel_action = CameraController.CHANGE_DISTANCE;\\r\\n\\r\\n\\tthis.keyboard_walk = false;\\r\\n\\tthis.keyboard_walk_plane = false;\\r\\n\\tthis.lock_mouse = false;\\r\\n\\r\\n\\tthis.rot_speed = 1;\\r\\n\\tthis.walk_speed = 10;\\r\\n\\tthis.wheel_speed = 1;\\r\\n\\tthis.smooth = false;\\r\\n\\tthis.render_crosshair = false;\\r\\n\\r\\n\\tthis._moving = vec3.fromValues(0,0,0);\\r\\n\\r\\n\\tthis._collision_none = vec3.create();\\r\\n\\tthis._collision_left = vec3.create();\\r\\n\\tthis._collision_middle = vec3.create();\\r\\n\\tthis._collision_right = vec3.create();\\r\\n\\tthis._dragging = false; //true if the mousedown was caught so the drag belongs to this component\\r\\n\\tthis._camera = null;\\r\\n\\r\\n\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nCameraController.NONE = 0; //no action\\r\\n\\r\\nCameraController.ORBIT = 1; //orbits around the center\\r\\nCameraController.ORBIT_HORIZONTAL = 2; //orbits around the center only around Y axis\\r\\n\\r\\nCameraController.ROTATE = 5; //rotates relative to the camera\\r\\nCameraController.ROTATE_HORIZONTAL = 6; //moves relative to the camera\\r\\n\\r\\nCameraController.PAN = 10; //moves paralel to the near plane\\r\\nCameraController.PAN_XZ = 11; //pans only in the XZ plane\\r\\n\\r\\nCameraController.CHANGE_DISTANCE = 15; //scales the center from eye to center\\r\\nCameraController.WALK = 16; //moves forward or backward\\r\\nCameraController.ELEVATE = 17; //moves forward or backward\\r\\nCameraController.FOV = 18; //changes zoom (FOV)\\r\\n\\r\\n\\r\\nCameraController.icon = \\\"mini-icon-cameracontroller.png\\\";\\r\\n\\r\\nCameraController.mode_values = { \\r\\n\\t\\t\\\"None\\\": CameraController.NONE,\\r\\n\\t\\t\\\"Orbit\\\": CameraController.ORBIT,\\r\\n\\t\\t\\\"Orbit Horizontal\\\": CameraController.ORBIT_HORIZONTAL, \\r\\n\\t\\t\\\"Rotate\\\": CameraController.ROTATE,\\r\\n\\t\\t\\\"Rotate Horizontal\\\": CameraController.ROTATE_HORIZONTAL, \\r\\n\\t\\t\\\"Pan\\\": CameraController.PAN,\\r\\n\\t\\t\\\"Pan XZ\\\": CameraController.PAN_XZ,\\r\\n\\t\\t\\\"Change Distance\\\": CameraController.CHANGE_DISTANCE,\\r\\n\\t\\t\\\"Walk\\\": CameraController.WALK,\\r\\n\\t\\t\\\"Elevate\\\": CameraController.ELEVATE\\r\\n\\t};\\r\\n\\r\\nCameraController.wheel_values = { \\r\\n\\t\\t\\\"None\\\": CameraController.NONE,\\r\\n\\t\\t\\\"Change Distance\\\": CameraController.CHANGE_DISTANCE,\\r\\n\\t\\t\\\"FOV\\\": CameraController.FOV,\\r\\n\\t\\t\\\"Walk\\\": CameraController.WALK,\\r\\n\\t\\t\\\"Elevate\\\": CameraController.ELEVATE\\r\\n};\\r\\n\\r\\nCameraController[\\\"@no_button_action\\\"] = { type:\\\"enum\\\", values: CameraController.mode_values };\\r\\nCameraController[\\\"@left_button_action\\\"] = { type:\\\"enum\\\", values: CameraController.mode_values };\\r\\nCameraController[\\\"@middle_button_action\\\"] = { type:\\\"enum\\\", values: CameraController.mode_values };\\r\\nCameraController[\\\"@right_button_action\\\"] = { type:\\\"enum\\\", values: CameraController.mode_values };\\r\\nCameraController[\\\"@mouse_wheel_action\\\"] = { type:\\\"enum\\\", values: CameraController.wheel_values };\\r\\n\\r\\nCameraController.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"start\\\",this.onStart,this);\\r\\n\\tLEvent.bind( scene, \\\"finish\\\",this.onFinish,this);\\r\\n\\tLEvent.bind( scene, \\\"mousedown\\\",this.onMouse,this);\\r\\n\\tLEvent.bind( scene, \\\"mousemove\\\",this.onMouse,this);\\r\\n\\tLEvent.bind( scene, \\\"mousewheel\\\",this.onMouse,this);\\r\\n\\tLEvent.bind( scene, \\\"touchstart\\\",this.onTouch,this);\\r\\n\\tLEvent.bind( scene, \\\"touchmove\\\",this.onTouch,this);\\r\\n\\tLEvent.bind( scene, \\\"touchend\\\",this.onTouch,this);\\r\\n\\tLEvent.bind( scene, \\\"keydown\\\",this.onKey,this);\\r\\n\\tLEvent.bind( scene, \\\"keyup\\\",this.onKey,this);\\r\\n\\tLEvent.bind( scene, \\\"update\\\",this.onUpdate,this);\\r\\n\\tLEvent.bind( scene, \\\"renderGUI\\\",this.onRenderGUI,this);\\r\\n}\\r\\n\\r\\nCameraController.prototype.onRemovedFromScene = function( scene )\\r\\n{\\r\\n\\tLEvent.unbindAll( scene, this );\\r\\n}\\r\\n\\r\\nCameraController.prototype.onStart = function(e)\\r\\n{\\r\\n\\tif(this.lock_mouse)\\r\\n\\t{\\r\\n\\t\\tLS.Input.lockMouse(true);\\r\\n\\t}\\r\\n}\\r\\n\\r\\nCameraController.prototype.onFinish = function(e)\\r\\n{\\r\\n\\tif(this.lock_mouse)\\r\\n\\t{\\r\\n\\t\\tLS.Input.lockMouse(false);\\r\\n\\t}\\r\\n}\\r\\n\\r\\nCameraController.prototype.onUpdate = function(e)\\r\\n{\\r\\n\\tif(!this._root || !this.enabled) \\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//get first camera attached to this node\\r\\n\\tvar cam = this._root.camera;\\r\\n\\r\\n\\t//no camera or disabled, then nothing to do\\r\\n\\tif(!cam || !cam.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//move using the delta vector\\r\\n\\tif(this._moving[0] != 0 || this._moving[1] != 0 || this._moving[2] != 0)\\r\\n\\t{\\r\\n\\t\\tvar delta = cam.getLocalVector( this._moving );\\r\\n\\t\\tif( this.keyboard_walk_plane )\\r\\n\\t\\t\\tdelta[1] = 0;\\r\\n\\t\\tif(vec3.length(delta))\\r\\n\\t\\t{\\r\\n\\t\\t\\tvec3.normalize( delta, delta );\\r\\n\\t\\t\\tvec3.scale(delta, delta, this.walk_speed * (this._fast ? 10 : 1));\\r\\n\\r\\n\\t\\t\\tif(this._root.transform) //attached to node\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._root.transform.translateGlobal(delta);\\r\\n\\t\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcam.move(delta);\\r\\n\\t\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif(this.smooth)\\r\\n\\t{\\r\\n\\t\\tthis._root.scene.requestFrame();\\r\\n\\t}\\r\\n}\\r\\n\\r\\nCameraController.prototype.processMouseButtonDownEvent = function( mode, mouse_event, coll_point )\\r\\n{\\r\\n\\tvar node = this._root;\\r\\n\\tvar cam = this._camera = node.camera;\\r\\n\\tif(!cam || !cam.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar is_global_camera = node._is_root;\\r\\n\\tvar changed = false;\\r\\n\\r\\n\\tif(mode == CameraController.PAN)\\r\\n\\t\\tthis.testPerpendicularPlane( mouse_event.canvasx, gl.canvas.height - mouse_event.canvasy, cam.getCenter(), coll_point );\\r\\n\\telse if(mode == CameraController.PAN_XZ)\\r\\n\\t\\tthis.testOriginPlane( mouse_event.canvasx, gl.canvas.height - mouse_event.canvasy, coll_point );\\r\\n\\r\\n\\treturn changed;\\r\\n}\\r\\n\\r\\nCameraController.prototype.processMouseButtonMoveEvent = function( mode, mouse_event, coll_point )\\r\\n{\\r\\n\\tvar node = this._root;\\r\\n\\tvar cam = this._camera = node.camera;\\r\\n\\tif(!cam || !cam.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar is_global_camera = node._is_root;\\r\\n\\tvar changed = false;\\r\\n\\r\\n\\tif(mode == CameraController.NONE)\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\tif(mode == CameraController.ORBIT)\\r\\n\\t{\\r\\n\\t\\tvar yaw = mouse_event.deltax * this.rot_speed;\\r\\n\\t\\tvar pitch = -mouse_event.deltay * this.rot_speed;\\r\\n\\r\\n\\t\\t//yaw rotation\\r\\n\\t\\tif( Math.abs(yaw) > 0.0001 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(is_global_camera)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcam.orbit( -yaw, [0,1,0] );\\r\\n\\t\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar eye = cam.getEye();\\r\\n\\t\\t\\t\\tnode.transform.globalToLocal( eye, eye );\\r\\n\\t\\t\\t\\tnode.transform.orbit( -yaw, [0,1,0], eye );\\r\\n\\t\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tchanged = true;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//pitch rotation\\r\\n\\t\\tvar right = cam.getRight();\\r\\n\\t\\tvar front = cam.getFront();\\r\\n\\t\\tvar up = cam.getUp();\\r\\n\\t\\tvar problem_angle = vec3.dot( up, front );\\r\\n\\t\\tif( !(problem_angle > 0.99 && pitch > 0 || problem_angle < -0.99 && pitch < 0)) //avoid strange behaviours\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(is_global_camera)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcam.orbit( -pitch, right, this.orbit_center );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar eye = cam.getEye();\\r\\n\\t\\t\\t\\tnode.transform.globalToLocal( eye, eye );\\r\\n\\t\\t\\t\\tnode.transform.orbit( -pitch, right, eye );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tchanged = true;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(mode == CameraController.ORBIT_HORIZONTAL)\\r\\n\\t{\\r\\n\\t\\tvar yaw = mouse_event.deltax * this.rot_speed;\\r\\n\\r\\n\\t\\tif( Math.abs(yaw) > 0.0001 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(is_global_camera)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcam.orbit( -yaw, [0,1,0] );\\r\\n\\t\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar eye = cam.getEye();\\r\\n\\t\\t\\t\\tnode.transform.globalToLocal( eye, eye );\\r\\n\\t\\t\\t\\tnode.transform.orbit( -yaw, [0,1,0], eye );\\r\\n\\t\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tchanged = true;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(mode == CameraController.ROTATE || mode == CameraController.ROTATE_HORIZONTAL )\\r\\n\\t{\\r\\n\\t\\tvar top = LS.TOP; //cam.getLocalVector(LS.TOP);\\r\\n\\t\\tcam.rotate( -mouse_event.deltax * this.rot_speed * 0.2, top );\\r\\n\\t\\tcam.updateMatrices();\\r\\n\\r\\n\\t\\tif( mode == CameraController.ROTATE )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar right = cam.getLocalVector(LS.RIGHT);\\r\\n\\t\\t\\tif(is_global_camera)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcam.rotate(-mouse_event.deltay * this.rot_speed * 0.2,right);\\r\\n\\t\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnode.transform.rotate( -mouse_event.deltay * this.rot_speed * 0.2, LS.RIGHT );\\r\\n\\t\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tchanged = true;\\r\\n\\t}\\r\\n\\telse if(mode == CameraController.PAN)\\r\\n\\t{\\r\\n\\t\\tvar collision = vec3.create();\\r\\n\\t\\tvar center = vec3.create();\\r\\n\\t\\tvar delta = vec3.create();\\r\\n\\r\\n\\t\\tcam.getCenter( center );\\r\\n\\t\\tthis.testPerpendicularPlane( mouse_event.canvasx, gl.canvas.height - mouse_event.canvasy, center, collision );\\r\\n\\t\\tvec3.sub( delta, coll_point, collision );\\r\\n\\r\\n\\t\\tif(is_global_camera)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcam.move( delta );\\r\\n\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tnode.transform.translateGlobal( delta );\\r\\n\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tchanged = true;\\t\\r\\n\\t}\\r\\n\\telse if(mode == CameraController.PAN_XZ)\\r\\n\\t{\\r\\n\\t\\tvar collision = vec3.create();\\r\\n\\t\\tvar delta = vec3.create();\\r\\n\\t\\tthis.testOriginPlane( mouse_event.canvasx, gl.canvas.height - mouse_event.canvasy, collision );\\r\\n\\t\\tvec3.sub( delta, coll_point, collision );\\r\\n\\t\\tif(is_global_camera)\\r\\n\\t\\t\\tcam.move( delta );\\r\\n\\t\\telse\\r\\n\\t\\t\\tnode.transform.translateGlobal( delta );\\r\\n\\t\\tcam.updateMatrices();\\r\\n\\r\\n\\t\\tchanged = true;\\r\\n\\t}\\r\\n\\telse if(mode == CameraController.CHANGE_DISTANCE)\\r\\n\\t{\\r\\n\\t\\tvar factor = mouse_event.deltay * this.wheel_speed;\\r\\n\\t\\tcam.orbitDistanceFactor(1 + factor * -0.05 );\\r\\n\\t\\tcam.updateMatrices();\\r\\n\\t\\tchanged = true;\\r\\n\\t}\\r\\n\\telse if(mode == CameraController.WALK)\\r\\n\\t{\\r\\n\\t\\tvar delta = cam.getLocalVector( [0,0, mouse_event.deltay * this.walk_speed] );\\r\\n\\t\\tcam.move(delta);\\r\\n\\t\\tcam.updateMatrices();\\r\\n\\t\\tchanged = true;\\r\\n\\t}\\r\\n\\telse if(mode == CameraController.ELEVATE)\\r\\n\\t{\\r\\n\\t\\tcam.move([0,mouse_event.deltay * this.walk_speed,0]);\\r\\n\\t\\tcam.updateMatrices();\\r\\n\\t\\tchanged = true;\\r\\n\\t}\\r\\n\\r\\n\\treturn changed;\\r\\n}\\r\\n\\r\\n//triggered on mouse move, or button clicked\\r\\nCameraController.prototype.onMouse = function(e, mouse_event)\\r\\n{\\r\\n\\tif(!this._root || !this.enabled) \\r\\n\\t\\treturn;\\r\\n\\t\\r\\n\\tvar node = this._root;\\r\\n\\tvar scene = node.scene;\\r\\n\\tvar cam = node.camera;\\r\\n\\tif(!cam || !cam.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar is_global_camera = node._is_root;\\r\\n\\r\\n\\tif(!mouse_event)\\r\\n\\t\\tmouse_event = e;\\r\\n\\r\\n\\tif(mouse_event.eventType == \\\"mousewheel\\\")\\r\\n\\t{\\r\\n\\t\\tvar wheel = mouse_event.wheel > 0 ? 1 : -1;\\r\\n\\r\\n\\t\\tswitch( this.mouse_wheel_action )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase CameraController.CHANGE_DISTANCE: \\r\\n\\t\\t\\t\\tcam.orbitDistanceFactor(1 + wheel * -0.05 * this.wheel_speed );\\r\\n\\t\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase CameraController.FOV: \\r\\n\\t\\t\\t\\tcam.fov = cam.fov - wheel;\\r\\n\\t\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tnode.scene.requestFrame();\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar changed = false;\\r\\n\\r\\n\\tif(mouse_event.eventType == \\\"mousedown\\\")\\r\\n\\t{\\r\\n\\t\\tif(this.lock_mouse && !document.pointerLockElement && scene._state == LS.PLAYING)\\r\\n\\t\\t\\tLS.Input.lockMouse(true);\\r\\n\\r\\n\\t\\tif( LS.Input.Mouse.isButtonPressed( GL.LEFT_MOUSE_BUTTON ) )\\r\\n\\t\\t\\tchanged |= this.processMouseButtonDownEvent( this.left_button_action, mouse_event, this._collision_left );\\r\\n\\t\\tif( LS.Input.Mouse.isButtonPressed( GL.MIDDLE_MOUSE_BUTTON ) )\\r\\n\\t\\t\\tchanged |= this.processMouseButtonDownEvent( this.middle_button_action, mouse_event, this._collision_middle );\\r\\n\\t\\tif( LS.Input.Mouse.isButtonPressed( GL.RIGHT_MOUSE_BUTTON ) )\\r\\n\\t\\t\\tchanged |= this.processMouseButtonDownEvent( this.right_button_action, mouse_event, this._collision_right );\\r\\n\\t\\tthis._dragging = true;\\r\\n\\t}\\r\\n\\r\\n\\tif(!mouse_event.dragging)\\r\\n\\t\\tthis._dragging = false;\\r\\n\\r\\n\\t//mouse move\\r\\n\\tif( ( mouse_event.eventType == \\\"mousemove\\\" || mouse_event.eventType == \\\"touchmove\\\" ) && (this.lock_mouse && ( document.pointerLockElement || mouse_event.is_touch )) )\\r\\n\\t\\tchanged |= this.processMouseButtonMoveEvent( this.no_button_action, mouse_event, this._collision_none );\\r\\n\\r\\n\\t//regular mouse dragging\\r\\n\\tif( mouse_event.eventType == \\\"mousemove\\\" && mouse_event.dragging && this._dragging )\\r\\n\\t{\\r\\n\\t\\tif( LS.Input.Mouse.isButtonPressed( GL.LEFT_MOUSE_BUTTON ) )\\r\\n\\t\\t\\tchanged |= this.processMouseButtonMoveEvent( this.left_button_action, mouse_event, this._collision_left );\\r\\n\\t\\tif( LS.Input.Mouse.isButtonPressed( GL.MIDDLE_MOUSE_BUTTON ) )\\r\\n\\t\\t\\tchanged |= this.processMouseButtonMoveEvent( this.middle_button_action, mouse_event, this._collision_middle );\\r\\n\\t\\tif( LS.Input.Mouse.isButtonPressed( GL.RIGHT_MOUSE_BUTTON ) )\\r\\n\\t\\t\\tchanged |= this.processMouseButtonMoveEvent( this.right_button_action, mouse_event, this._collision_right );\\r\\n\\t}\\r\\n\\r\\n\\tif(changed)\\r\\n\\t\\tthis._root.scene.requestFrame();\\r\\n}\\r\\n\\r\\n//manage pinching and dragging two fingers in a touch pad\\r\\nCameraController.prototype.onTouch = function( e, touch_event)\\r\\n{\\r\\n\\tif(!this._root || !this.enabled) \\r\\n\\t\\treturn;\\r\\n\\t\\r\\n\\tvar node = this._root;\\r\\n\\tvar cam = node.camera;\\r\\n\\tif(!cam || !cam.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar is_global_camera = node._is_root;\\r\\n\\r\\n\\tif(!touch_event)\\r\\n\\t\\ttouch_event = e;\\r\\n\\r\\n\\t//console.log( e );\\r\\n\\t//touch!\\r\\n\\tif( touch_event.type == \\\"touchstart\\\" )\\r\\n\\t{\\r\\n\\t\\tif( touch_event.touches.length == 2)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar distx = touch_event.touches[0].clientX - touch_event.touches[1].clientX;\\r\\n\\t\\t\\tvar disty = touch_event.touches[0].clientY - touch_event.touches[1].clientY;\\r\\n\\t\\t\\tthis._touch_distance = Math.sqrt(distx*distx + disty*disty);\\r\\n\\t\\t\\tthis._touch_center = [ (touch_event.touches[0].clientX + touch_event.touches[1].clientX) * 0.5,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t(touch_event.touches[0].clientY + touch_event.touches[1].clientY) * 0.5 ];\\r\\n\\t\\t\\ttouch_event.preventDefault();\\r\\n\\t\\t\\treturn false; //block\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\tif( touch_event.type == \\\"touchmove\\\" )\\r\\n\\t{\\r\\n\\t\\tif(touch_event.touches.length == 2)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar distx = touch_event.touches[0].clientX - touch_event.touches[1].clientX;\\r\\n\\t\\t\\tvar disty = touch_event.touches[0].clientY - touch_event.touches[1].clientY;\\r\\n\\t\\t\\tvar distance = Math.sqrt(distx*distx + disty*disty);\\r\\n\\t\\t\\tif(distance < 0.1)\\r\\n\\t\\t\\t\\tdistance = 0.1;\\r\\n\\t\\t\\tvar delta_dist = this._touch_distance / distance;\\r\\n\\t\\t\\tthis._touch_distance = distance;\\r\\n\\t\\t\\t//console.log( delta_dist );\\r\\n\\t\\t\\tcam.orbitDistanceFactor( delta_dist );\\r\\n\\t\\t\\tcam.updateMatrices();\\r\\n\\r\\n\\t\\t\\tvar delta_x = (touch_event.touches[0].clientX + touch_event.touches[1].clientX) * 0.5 - this._touch_center[0];\\r\\n\\t\\t\\tvar delta_y = (touch_event.touches[0].clientY + touch_event.touches[1].clientY) * 0.5 - this._touch_center[1];\\r\\n\\t\\t\\tvar panning_factor = cam.focalLength / gl.canvas.width;\\r\\n\\t\\t\\tcam.panning( -delta_x, delta_y, panning_factor );\\r\\n\\t\\t\\tthis._touch_center[0] = (touch_event.touches[0].clientX + touch_event.touches[1].clientX) * 0.5;\\r\\n\\t\\t\\tthis._touch_center[1] = (touch_event.touches[0].clientY + touch_event.touches[1].clientY) * 0.5;\\r\\n\\r\\n\\t\\t\\tcam.updateMatrices();\\r\\n\\t\\t\\tthis._root.scene.requestFrame();\\r\\n\\t\\t\\ttouch_event.preventDefault();\\r\\n\\t\\t\\treturn false; //block\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nCameraController.prototype.testOriginPlane = function(x,y, result)\\r\\n{\\r\\n\\tvar cam = this._root.camera;\\r\\n\\tvar ray = cam.getRay( x, gl.canvas.height - y );\\r\\n\\tvar result = result || vec3.create();\\r\\n\\r\\n\\t//test against plane at 0,0,0\\r\\n\\tif( geo.testRayPlane( ray.origin, ray.direction, LS.ZEROS, LS.TOP, result ) )\\r\\n\\t\\treturn true;\\r\\n\\treturn false;\\r\\n}\\r\\n\\r\\nCameraController.prototype.testPerpendicularPlane = function(x,y, center, result)\\r\\n{\\r\\n\\tvar cam = this._root.camera;\\r\\n\\tvar ray = cam.getRay( x, gl.canvas.height - y );\\r\\n\\r\\n\\tvar front = cam.getFront();\\r\\n\\tvar center = center || cam.getCenter();\\r\\n\\tvar result = result || vec3.create();\\r\\n\\r\\n\\t//test against plane\\r\\n\\tif( geo.testRayPlane( ray.origin, ray.direction, center, front, result ) )\\r\\n\\t\\treturn true;\\r\\n\\treturn false;\\r\\n}\\r\\n\\r\\nCameraController.prototype.onKey = function(e, key_event)\\r\\n{\\r\\n\\tif(!this._root || !this.enabled) \\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//keyboard movement\\r\\n\\tif( this.keyboard_walk )\\r\\n\\t{\\r\\n\\t\\tif(key_event.keyCode == 87)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(key_event.type == \\\"keydown\\\")\\r\\n\\t\\t\\t\\tthis._moving[2] = -1;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis._moving[2] = 0;\\r\\n\\t\\t}\\r\\n\\t\\telse if(key_event.keyCode == 83)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(key_event.type == \\\"keydown\\\")\\r\\n\\t\\t\\t\\tthis._moving[2] = 1;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis._moving[2] = 0;\\r\\n\\t\\t}\\r\\n\\t\\telse if(key_event.keyCode == 65)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(key_event.type == \\\"keydown\\\")\\r\\n\\t\\t\\t\\tthis._moving[0] = -1;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis._moving[0] = 0;\\r\\n\\t\\t}\\r\\n\\t\\telse if(key_event.keyCode == 68)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(key_event.type == \\\"keydown\\\")\\r\\n\\t\\t\\t\\tthis._moving[0] = 1;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis._moving[0] = 0;\\r\\n\\t\\t}\\r\\n\\t\\telse if(key_event.keyCode == 16) //shift in windows chrome\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(key_event.type == \\\"keydown\\\")\\r\\n\\t\\t\\t\\tthis._fast = true;\\r\\n\\t\\t\\telse if(key_event.type == \\\"keyup\\\")\\r\\n\\t\\t\\t\\tthis._fast = false;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//LEvent.trigger(Scene,\\\"change\\\");\\r\\n}\\r\\n\\r\\nCameraController.prototype.onRenderGUI = function()\\r\\n{\\r\\n\\tif(!this.render_crosshair || !this.enabled || !this._camera || !this._camera.enabled || !LS.Input.isMouseLocked() )\\r\\n\\t\\treturn;\\r\\n\\tvar ctx = gl;\\r\\n\\tgl.start2D();\\r\\n\\tctx.fillStyle = \\\"rgba(0,0,0,0.5)\\\";\\r\\n\\tctx.fillRect( gl.viewport_data[2] * 0.5 - 1, gl.viewport_data[3] * 0.5 - 1, 4, 4 );\\r\\n\\tctx.fillStyle = \\\"rgba(255,255,255,1)\\\";\\r\\n\\tctx.fillRect( gl.viewport_data[2] * 0.5, gl.viewport_data[3] * 0.5, 2, 2 );\\r\\n\\tgl.finish2D();\\r\\n}\\r\\n\\r\\nLS.registerComponent( CameraController );\\r\\n\\r\\n///@FILE:../src/components/nodeManipulator.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Node manipulator, allows to rotate it\\r\\n* @class NodeManipulator\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\n\\r\\nfunction NodeManipulator(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.on_node_clicked = false;\\r\\n\\tthis.use_global_up_for_yaw = false;\\r\\n\\tthis.rot_speed = [1,1]; //degrees\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nNodeManipulator.icon = \\\"mini-icon-rotator.png\\\";\\r\\n\\r\\nNodeManipulator.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"mousemove\\\",this.onSceneMouse,this);\\r\\n}\\r\\n\\r\\nNodeManipulator.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind( scene, \\\"mousemove\\\",this.onSceneMouse, this);\\r\\n}\\r\\n\\r\\nNodeManipulator.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tLEvent.bind( node, \\\"mousemove\\\",this.onNodeMouse,this);\\r\\n}\\r\\n\\r\\nNodeManipulator.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tLEvent.unbind( node, \\\"mousemove\\\",this.onNodeMouse,this);\\r\\n}\\r\\n\\r\\nNodeManipulator.prototype.onNodeMouse = function( e, mouse_event )\\r\\n{\\r\\n\\tif(!this.on_node_clicked || !this.enabled)\\r\\n\\t\\treturn;\\r\\n\\treturn this.onMouse( e, mouse_event );\\r\\n}\\r\\n\\r\\nNodeManipulator.prototype.onSceneMouse = function( e, mouse_event )\\r\\n{\\r\\n\\tif(this.on_node_clicked || !this.enabled)\\r\\n\\t\\treturn;\\r\\n\\treturn this.onMouse( e, mouse_event );\\r\\n}\\r\\n\\r\\nNodeManipulator.prototype.onMouse = function( e, mouse_event )\\r\\n{\\r\\n\\tif(!this._root || !this._root.transform)\\r\\n\\t\\treturn;\\r\\n\\t\\r\\n\\t//regular mouse dragging\\r\\n\\tif(!mouse_event.dragging)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tvar camera = scene.getCamera();\\r\\n\\r\\n\\t//yaw\\r\\n\\tvar up = this.use_global_up_for_yaw ? LS.Components.Transform.UP : camera.getLocalVector( LS.Components.Transform.UP );\\r\\n\\tthis._root.transform.rotateGlobal( mouse_event.deltax * this.rot_speed[0], up );\\r\\n\\r\\n\\t//pitch\\r\\n\\tvar right = camera.getLocalVector( LS.Components.Transform.RIGHT );\\r\\n\\tthis._root.transform.rotateGlobal( mouse_event.deltay * this.rot_speed[1], right );\\r\\n\\r\\n\\tscene.requestFrame();\\r\\n}\\r\\n\\r\\nLS.registerComponent( NodeManipulator );\\r\\n///@FILE:../src/components/target.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Target rotate a mesh to look at the camera or another object\\r\\n* @class Target\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\n\\r\\nfunction Target(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.node_id = null;\\r\\n\\tthis.face_camera = false;\\r\\n\\tthis.cylindrical = false;\\r\\n\\tthis.front = Target.NEGZ;\\r\\n\\tthis.up = Target.POSY;\\r\\n\\t\\r\\n\\tthis._global_position = vec3.create();\\r\\n\\tthis._target_position = vec3.create();\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nTarget.icon = \\\"mini-icon-billboard.png\\\";\\r\\n\\r\\nTarget.POSX = 1;\\r\\nTarget.NEGX = 2;\\r\\nTarget.POSY = 3;\\r\\nTarget.NEGY = 4;\\r\\nTarget.POSZ = 5;\\r\\nTarget.NEGZ = 6;\\r\\n\\r\\nTarget[\\\"@node_id\\\"] = { type: 'node_id' };\\r\\nTarget[\\\"@front\\\"] = { type: 'enum', values: { \\\"-Z\\\": Target.NEGZ,\\\"+Z\\\": Target.POSZ, \\\"-Y\\\": Target.NEGY,\\\"+Y\\\": Target.POSY,\\\"-X\\\": Target.NEGX,\\\"+X\\\": Target.POSX }};\\r\\nTarget[\\\"@up\\\"] = { type: 'enum', values: { \\\"-Z\\\": Target.NEGZ,\\\"+Z\\\": Target.POSZ, \\\"-Y\\\": Target.NEGY,\\\"+Y\\\": Target.POSY,\\\"-X\\\": Target.NEGX,\\\"+X\\\": Target.POSX }};\\r\\n\\r\\nTarget.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\t//it must be done before collect, otherwise elements wont have the right orientation\\r\\n\\tLEvent.bind( scene, LS.EVENT.BEFORE_RENDER, this.onBeforeRender, this);\\r\\n}\\r\\n\\r\\nTarget.prototype.onRemovedFromScene = function( scene )\\r\\n{\\r\\n\\tLEvent.unbind( scene, LS.EVENT.BEFORE_RENDER, this.onBeforeRender, this);\\r\\n}\\r\\n\\r\\nTarget.prototype.onBeforeRender = function(e)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis.updateOrientation();\\r\\n}\\r\\n\\r\\nTarget.temp_mat3 = mat3.create();\\r\\n\\r\\n//*\\r\\nTarget.temp_mat = mat4.create();\\r\\n\\r\\nTarget.prototype.updateOrientation = function()\\r\\n{\\r\\n\\tif(!this._root || !this._root.transform ) \\r\\n\\t\\treturn;\\r\\n\\tvar scene = this._root.scene;\\r\\n\\r\\n\\tvar transform = this._root.transform;\\r\\n\\r\\n\\tvar eye = null;\\r\\n\\tvar target_position = null;\\r\\n\\tvar up = null;\\r\\n\\tvar position = transform.getGlobalPosition( this._global_position );\\r\\n\\r\\n\\tswitch( this.up )\\r\\n\\t{\\r\\n\\t\\tcase Target.NEGX: up = vec3.fromValues(-1,0,0); break;\\r\\n\\t\\tcase Target.POSX: up = vec3.fromValues(1,0,0); break;\\r\\n\\t\\tcase Target.NEGZ: up = vec3.fromValues(0,0,-1); break;\\r\\n\\t\\tcase Target.POSZ: up = vec3.fromValues(0,0,1); break;\\r\\n\\t\\tcase Target.NEGY: up = vec3.fromValues(0,-1,0); break;\\r\\n\\t\\tcase Target.POSY: \\r\\n\\t\\tdefault:\\r\\n\\t\\t\\tup = vec3.fromValues(0,1,0);\\r\\n\\t}\\r\\n\\r\\n\\tif( this.node_id )\\r\\n\\t{\\r\\n\\t\\tvar node = scene.getNode( this.node_id );\\r\\n\\t\\tif(!node || node == this._root || !node.transform ) //avoid same node\\r\\n\\t\\t\\treturn;\\r\\n\\t\\ttarget_position = node.transform.getGlobalPosition( this._target_position );\\r\\n\\t}\\r\\n\\telse if( this.face_camera )\\r\\n\\t{\\r\\n\\t\\tvar camera = LS.Renderer._current_camera || LS.Renderer._main_camera;\\r\\n\\t\\tif(!camera)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\ttarget_position = camera.getEye();\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( this.cylindrical )\\r\\n\\t{\\r\\n\\t\\ttarget_position[1] = position[1];\\r\\n\\t\\t//up.set([0,1,0]);\\r\\n\\t}\\r\\n\\r\\n\\tmat4.lookAt( Target.temp_mat, position, target_position, up );\\r\\n\\tquat.fromMat4( transform._rotation, Target.temp_mat );\\r\\n\\r\\n\\t//transform.lookAt( position, target_position, up, true );\\r\\n\\r\\n\\tswitch( this.front )\\r\\n\\t{\\r\\n\\t\\tcase Target.POSY: quat.rotateX( transform._rotation, transform._rotation, Math.PI * -0.5 );\\tbreak;\\r\\n\\t\\tcase Target.NEGY: quat.rotateX( transform._rotation, transform._rotation, Math.PI * 0.5 );\\tbreak;\\r\\n\\t\\tcase Target.POSX: quat.rotateY( transform._rotation, transform._rotation, Math.PI * 0.5 );\\tbreak;\\r\\n\\t\\tcase Target.NEGX: quat.rotateY( transform._rotation, transform._rotation, Math.PI * -0.5 );\\tbreak;\\r\\n\\t\\tcase Target.POSZ: quat.rotateY( transform._rotation, transform._rotation, Math.PI );\\tbreak;\\r\\n\\t\\tcase Target.NEGZ:\\r\\n\\t\\tdefault:\\r\\n\\t}\\r\\n\\r\\n\\ttransform._on_change();\\r\\n}\\r\\n//*/\\r\\n\\r\\nLS.registerComponent( Target );\\r\\n///@FILE:../src/components/fogFX.js\\r\\n///@INFO: UNCOMMON\\r\\nfunction FogFX(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.start = 100;\\r\\n\\tthis.end = 1000;\\r\\n\\tthis.density = 0.001;\\r\\n\\tthis.type = FogFX.LINEAR;\\r\\n\\tthis.color = vec3.fromValues(0.5,0.5,0.5);\\r\\n\\r\\n\\tthis._uniforms = {\\r\\n\\t\\tu_fog_info: vec3.create(),\\r\\n\\t\\tu_fog_color: this.color\\r\\n\\t}\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nFogFX.icon = \\\"mini-icon-fog.png\\\";\\r\\n\\r\\nFogFX.LINEAR = 1;\\r\\nFogFX.EXP = 2;\\r\\nFogFX.EXP2 = 3;\\r\\n\\r\\nFogFX[\\\"@color\\\"] = { type: \\\"color\\\" };\\r\\nFogFX[\\\"@density\\\"] = { type: \\\"number\\\", min: 0, max:1, step:0.0001, precision: 4 };\\r\\nFogFX[\\\"@type\\\"] = { type:\\\"enum\\\", values: {\\\"linear\\\": FogFX.LINEAR, \\\"exponential\\\": FogFX.EXP, \\\"exponential 2\\\": FogFX.EXP2 }};\\r\\n\\r\\n\\r\\nFogFX.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\t//LEvent.bind( scene,\\\"fillLightUniforms\\\",this.fillUniforms,this);\\r\\n\\tLEvent.bind( scene, \\\"fillSceneUniforms\\\",this.fillSceneUniforms,this);\\r\\n\\r\\n}\\r\\n\\r\\nFogFX.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\t//LEvent.unbind(Scene,\\\"fillLightUniforms\\\",this.fillUniforms,this);\\r\\n\\tLEvent.unbind( scene, \\\"fillSceneUniforms\\\",this.fillSceneUniforms, this);\\r\\n}\\r\\n\\r\\nFogFX.prototype.fillSceneUniforms = function( e, uniforms )\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._uniforms.u_fog_info[0] = this.start;\\r\\n\\tthis._uniforms.u_fog_info[1] = this.end;\\r\\n\\tthis._uniforms.u_fog_info[2] = this.density;\\r\\n\\tthis._uniforms.u_fog_color = this.color;\\r\\n\\r\\n\\tLS.Renderer.enableFrameShaderBlock( \\\"fog\\\", this._uniforms );\\r\\n}\\r\\n\\r\\nLS.registerComponent(FogFX);\\r\\n\\r\\n//shaderblock\\r\\nvar fog_block = new LS.ShaderBlock(\\\"fog\\\");\\r\\n//fog_block.addInclude(\\\"computeFog\\\");\\r\\nfog_block.bindEvent(\\\"fs_functions\\\", \\\"\\tuniform vec3 u_fog_info;\\\\n\\tuniform vec3 u_fog_color;\\\\n\\\");\\r\\nfog_block.bindEvent(\\\"fs_final_pass\\\", \\\"\\tif(u_light_info.z == 0.0) { float cam_dist = length(u_camera_eye - v_pos);\\\\n\\tfloat fog = 1. - 1.0 / exp(max(0.0,cam_dist - u_fog_info.x) * u_fog_info.z);\\\\n\\tfinal_color.xyz = mix(final_color.xyz, u_fog_color, fog);\\\\n}\\\\n\\\\n\\\");\\r\\nfog_block.register();\\r\\nFogFX.block = fog_block;\\r\\n\\r\\n/*\\r\\n//apply fog\\r\\nvec3 computeFog( vec3 color, float cam_dist, float height )\\r\\n{\\r\\n\\t#ifdef USE_FOG_EXP\\r\\n\\t\\tfloat fog = 1. - 1.0 / exp(max(0.0,cam_dist - u_fog_info.x) * u_fog_info.z);\\r\\n\\t#elif defined(USE_FOG_EXP2)\\r\\n\\t\\tfloat fog = 1. - 1.0 / exp(pow(max(0.0,cam_dist - u_fog_info.x) * u_fog_info.z,2.0));\\r\\n\\t#else\\r\\n\\t\\tfloat fog = 1. - clamp((u_fog_info.y - cam_dist) / (u_fog_info.y - u_fog_info.x),0.,1.);\\r\\n\\t#endif\\r\\n\\t#ifdef FIRST_PASS\\r\\n\\t\\treturn mix(color, u_fog_color, fog);\\r\\n\\t#else\\r\\n\\t\\treturn mix(color, vec3(0.0), fog);\\r\\n\\t#endif\\r\\n}\\r\\n*/\\r\\n\\r\\n\\r\\n///@FILE:../src/components/followNode.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* FollowNode \\r\\n* @class FollowNode\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\n\\r\\nfunction FollowNode(o)\\r\\n{\\r\\n\\tthis.node_uid = \\\"\\\";\\r\\n\\tthis.fixed_y = false;\\r\\n\\tthis.follow_camera = false;\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nFollowNode.icon = \\\"mini-icon-follow.png\\\";\\r\\n\\r\\nFollowNode.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene,\\\"beforeRender\\\", this.updatePosition, this);\\r\\n}\\r\\n\\r\\nFollowNode.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind(scene,\\\"beforeRender\\\", this.updatePosition, this);\\r\\n}\\r\\n\\r\\nFollowNode.prototype.updatePosition = function(e,info)\\r\\n{\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar pos = null;\\r\\n\\tvar scene = this._root.scene;\\r\\n\\r\\n\\tif( this.follow_camera )\\r\\n\\t{\\r\\n\\t\\tvar camera = LS.Renderer._main_camera; //main camera\\r\\n\\t\\tif(!camera)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tpos = camera.getEye();\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tvar target_node = scene.getNode( this.node_uid );\\r\\n\\t\\tif(!target_node || !target_node.transform)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tpos = target_node.transform.getGlobalPosition();\\r\\n\\t}\\r\\n\\r\\n\\tif(this.fixed_y)\\r\\n\\t\\tpos[1] = this._root.transform._position[1];\\r\\n\\tif(!this._root.transform)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._root.transform.position = pos;\\r\\n}\\r\\n\\r\\nLS.registerComponent( FollowNode );\\r\\n///@FILE:../src/components/geometricPrimitive.js\\r\\n/**\\r\\n* GeometricPrimitive renders a primitive like a Cube, Sphere, Plane, etc\\r\\n* @class GeometricPrimitive\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\n\\r\\nfunction GeometricPrimitive( o )\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis._size = 10;\\r\\n\\tthis._subdivisions = 10;\\r\\n\\tthis._geometry = GeometricPrimitive.CUBE;\\r\\n\\tthis._custom_mesh = null; //used for meshes that must be stored with the JSON\\r\\n\\tthis._primitive = -1; //GL.POINTS, GL.LINES, GL.TRIANGLES, etc...\\r\\n\\tthis._point_size = 0.1;\\r\\n\\r\\n\\tthis._version = 1;\\r\\n\\tthis._mesh = null;\\r\\n\\tthis._mesh_version = 0;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* The shape to render, valid values are: LS.Components.GeometricPrimitive.CUBE,PLANE,CYLINDER,SPHERE,CIRCLE,HEMISPHERE,ICOSAHEDRON,CONE,QUAD\\r\\n* @property geometry {enum}\\r\\n* @default LS.Components.GeometricPrimitive.CUBE\\r\\n*/\\r\\nObject.defineProperty( GeometricPrimitive.prototype, 'geometry', {\\r\\n\\tget: function() { return this._geometry; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif( this._geometry == v )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tv = (v === undefined || v === null ? -1 : v|0);\\r\\n\\t\\tif( !GeometricPrimitive.VALID[v] )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._geometry = v;\\r\\n\\t\\tthis._version++;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The size of the primitive (the global scale)\\r\\n* @property size {Number}\\r\\n* @default 10\\r\\n*/\\r\\nObject.defineProperty( GeometricPrimitive.prototype, 'size', {\\r\\n\\tget: function() { return this._size; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif( this._size == v )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._size = v;\\r\\n\\t\\tthis._version++;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( GeometricPrimitive.prototype, 'subdivisions', {\\r\\n\\tget: function() { return this._subdivisions; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif( this._subdivisions == v )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._subdivisions = v;\\r\\n\\t\\tthis._version++;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* The GL primitive to use (LINES,LINE_STRIP,TRIANGLES,TRIANGLE_FAN\\r\\n* @property primitive {enum}\\r\\n* @default 10\\r\\n*/\\r\\nObject.defineProperty( GeometricPrimitive.prototype, 'primitive', {\\r\\n\\tget: function() { return this._primitive; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tv = (v === undefined || v === null ? -1 : v|0);\\r\\n\\t\\tif(v != -1 && v != 0 && v!= 1 && v!= 4 && v!= 10)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._primitive = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( GeometricPrimitive.prototype, 'point_size', {\\r\\n\\tget: function() { return this._point_size; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif( this._point_size == v )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._point_size = v;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n//assign a custom mesh\\r\\nObject.defineProperty( GeometricPrimitive.prototype, 'mesh', {\\r\\n\\tget: function() { return this._custom_mesh || this._mesh; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif(v && v.constructor !== GL.Mesh)\\r\\n\\t\\t\\tthrow(\\\"mesh must be a GL.Mesh\\\");\\r\\n\\t\\tthis._custom_mesh = v;\\r\\n\\t\\tif(v)\\r\\n\\t\\t\\tthis._geometry = GeometricPrimitive.CUSTOM;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n\\r\\nGeometricPrimitive.CUBE = 1;\\r\\nGeometricPrimitive.PLANE = 2;\\r\\nGeometricPrimitive.CYLINDER = 3;\\r\\nGeometricPrimitive.SPHERE = 4;\\r\\nGeometricPrimitive.CIRCLE = 5;\\r\\nGeometricPrimitive.HEMISPHERE = 6;\\r\\nGeometricPrimitive.ICOSAHEDRON = 7;\\r\\nGeometricPrimitive.CONE = 8;\\r\\nGeometricPrimitive.QUAD = 9;\\r\\nGeometricPrimitive.CUSTOM = 100;\\r\\n\\r\\nGeometricPrimitive.VALID = { 1:\\\"CUBE\\\", 2:\\\"PLANE\\\", 3:\\\"CYLINDER\\\", 4:\\\"SPHERE\\\", 5:\\\"CIRCLE\\\", 6:\\\"HEMISPHERE\\\", 7:\\\"ICOSAHEDRON\\\", 8: \\\"CONE\\\", 9:\\\"QUAD\\\", 100:\\\"CUSTOM\\\" };\\r\\n\\r\\n//Warning : if you add more primitives, be careful with the setter, it doesnt allow values bigger than 7\\r\\n\\r\\nGeometricPrimitive.icon = \\\"mini-icon-cube.png\\\";\\r\\nGeometricPrimitive[\\\"@geometry\\\"] = { type:\\\"enum\\\", values: {\\\"Cube\\\":GeometricPrimitive.CUBE, \\\"Plane\\\": GeometricPrimitive.PLANE, \\\"Cylinder\\\":GeometricPrimitive.CYLINDER, \\\"Sphere\\\":GeometricPrimitive.SPHERE, \\\"Cone\\\":GeometricPrimitive.CONE, \\\"Icosahedron\\\":GeometricPrimitive.ICOSAHEDRON, \\\"Circle\\\":GeometricPrimitive.CIRCLE, \\\"Hemisphere\\\":GeometricPrimitive.HEMISPHERE, \\\"Quad\\\": GeometricPrimitive.QUAD, \\\"Custom\\\": GeometricPrimitive.CUSTOM }};\\r\\nGeometricPrimitive[\\\"@primitive\\\"] = {widget:\\\"enum\\\", values: {\\\"Default\\\":-1, \\\"Points\\\": 0, \\\"Lines\\\":1, \\\"Triangles\\\":4, \\\"Wireframe\\\":10 }};\\r\\nGeometricPrimitive[\\\"@subdivisions\\\"] = { type:\\\"number\\\", step:1, min:1, precision: 0 };\\r\\nGeometricPrimitive[\\\"@point_size\\\"] = { type:\\\"number\\\", step:0.001 };\\r\\n\\r\\n//we bind to onAddedToNode because the event is triggered per node so we know which RIs belong to which node\\r\\nGeometricPrimitive.prototype.onAddedToNode = function( node )\\r\\n{\\r\\n\\tLEvent.bind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nGeometricPrimitive.prototype.onRemovedFromNode = function( node )\\r\\n{\\r\\n\\tLEvent.unbind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nGeometricPrimitive.prototype.serialize = function()\\r\\n{\\r\\n\\tvar r = LS.BaseComponent.prototype.serialize.call(this);\\r\\n\\tif(this._geometry == GeometricPrimitive.CUSTOM && this._custom_mesh)\\r\\n\\t\\tr.custom_mesh = this._custom_mesh.toJSON();\\r\\n\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\nGeometricPrimitive.prototype.configure = function(o)\\r\\n{\\r\\n\\tLS.BaseComponent.prototype.configure.call(this,o);\\r\\n\\r\\n\\t//legacy\\r\\n\\tif(this._geometry == GeometricPrimitive.PLANE && o.align_z === false )\\r\\n\\t\\tthis._geometry = GeometricPrimitive.QUAD;\\r\\n\\r\\n\\tif(o.geometry == GeometricPrimitive.CUSTOM && o.custom_mesh)\\r\\n\\t{\\r\\n\\t\\tif(!this._custom_mesh)\\r\\n\\t\\t\\tthis._custom_mesh = new GL.Mesh();\\r\\n\\t\\tthis._custom_mesh.fromJSON( o.custom_mesh );\\r\\n\\t}\\r\\n\\r\\n\\tthis._version++;\\r\\n}\\r\\n\\r\\nGeometricPrimitive.prototype.updateMesh = function()\\r\\n{\\r\\n\\tvar subdivisions = Math.max(0,this.subdivisions|0);\\r\\n\\r\\n\\tswitch (this._geometry)\\r\\n\\t{\\r\\n\\t\\tcase GeometricPrimitive.CUBE: \\r\\n\\t\\t\\tthis._mesh = GL.Mesh.cube({size: this.size, normals:true,coords:true});\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GeometricPrimitive.PLANE:\\r\\n\\t\\t\\tthis._mesh = GL.Mesh.plane({size: this.size, xz: true, detail: subdivisions, normals:true,coords:true});\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GeometricPrimitive.CYLINDER:\\r\\n\\t\\t\\tthis._mesh = GL.Mesh.cylinder({size: this.size, subdivisions: subdivisions, normals:true,coords:true});\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GeometricPrimitive.SPHERE:\\r\\n\\t\\t\\tthis._mesh = GL.Mesh.sphere({size: this.size, \\\"long\\\": subdivisions, lat: subdivisions, normals:true,coords:true});\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GeometricPrimitive.CIRCLE:\\r\\n\\t\\t\\tthis._mesh = GL.Mesh.circle({size: this.size, slices: subdivisions, normals:true, coords:true});\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GeometricPrimitive.HEMISPHERE:\\r\\n\\t\\t\\tthis._mesh = GL.Mesh.sphere({size: this.size, \\\"long\\\": subdivisions, lat: subdivisions, normals:true, coords:true, hemi: true});\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GeometricPrimitive.ICOSAHEDRON:\\r\\n\\t\\t\\tthis._mesh = GL.Mesh.icosahedron({size: this.size, subdivisions:subdivisions });\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GeometricPrimitive.CONE:\\r\\n\\t\\t\\tthis._mesh = GL.Mesh.cone({radius: this.size, height: this.size, subdivisions:subdivisions });\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GeometricPrimitive.QUAD:\\r\\n\\t\\t\\tthis._mesh = GL.Mesh.plane({size: this.size, xz: false, detail: subdivisions, normals:true, coords:true });\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GeometricPrimitive.CUSTOM:\\r\\n\\t\\t\\tthis._mesh = this._custom_mesh;\\r\\n\\t\\t\\tbreak;\\r\\n\\t}\\r\\n\\r\\n\\tthis._mesh_version = this._version;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Assigns a mesh as custom mesh and sets the geometry to CUSTOM\\r\\n* @method setCustomMesh\\r\\n* @param {GL.Mesh} mesh the mesh to use as custom mesh\\r\\n*/\\r\\nGeometricPrimitive.prototype.setCustomMesh = function( mesh )\\r\\n{\\r\\n\\tthis._geometry = GeometricPrimitive.CUSTOM;\\r\\n\\tthis._custom_mesh = mesh;\\r\\n\\tthis._mesh = this._custom_mesh;\\r\\n}\\r\\n\\r\\n//GeometricPrimitive.prototype.getRenderInstance = function()\\r\\nGeometricPrimitive.prototype.onCollectInstances = function(e, instances)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar mesh = null;\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar RI = this._render_instance;\\r\\n\\tif(!RI)\\r\\n\\t\\tthis._render_instance = RI = new LS.RenderInstance(this._root, this);\\r\\n\\r\\n\\tif(!this._mesh || this._version != this._mesh_version )\\r\\n\\t\\tthis.updateMesh();\\r\\n\\r\\n\\tif(!this._mesh) //could happend if custom mesh is null\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//assigns matrix, layers\\r\\n\\tRI.fromNode( this._root );\\r\\n\\tRI.setMesh( this._mesh, this._primitive );\\r\\n\\tthis._root.mesh = this._mesh;\\r\\n\\t\\r\\n\\tRI.setMaterial( this.material || this._root.getMaterial() );\\r\\n\\r\\n\\t//remove one day...\\r\\n\\tif(this.primitive == gl.POINTS)\\r\\n\\t{\\r\\n\\t\\tRI.uniforms.u_point_size = this.point_size;\\r\\n\\t\\t//RI.query.macros[\\\"USE_POINTS\\\"] = \\\"\\\";\\r\\n\\t}\\r\\n\\r\\n\\tinstances.push(RI);\\r\\n}\\r\\n\\r\\nLS.registerComponent( GeometricPrimitive );\\r\\n\\r\\n///@FILE:../src/components/globalInfo.js\\r\\n///@INFO: BASE\\r\\nfunction GlobalInfo(o)\\r\\n{\\r\\n\\tthis.createProperty( \\\"ambient_color\\\", GlobalInfo.DEFAULT_AMBIENT_COLOR, \\\"color\\\" );\\r\\n\\tthis._render_settings = null;\\r\\n\\tthis._textures = {};\\r\\n\\tthis._irradiance = null; //in SH form of float32(3*9)\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nObject.defineProperty( GlobalInfo.prototype, 'textures', {\\r\\n\\tset: function( v )\\r\\n\\t{\\r\\n\\t\\tif(typeof(v) != \\\"object\\\")\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tfor(var i in v)\\r\\n\\t\\t\\tif( v[i] === null || v[i].constructor === String || v[i] === GL.Texture )\\r\\n\\t\\t\\t\\tthis._textures[i] = v[i];\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this._textures;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( GlobalInfo.prototype, 'render_settings', {\\r\\n\\tset: function( v )\\r\\n\\t{\\r\\n\\t\\tif( !v )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._render_settings = null;\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tif(typeof(v) != \\\"object\\\")\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(!this._render_settings)\\r\\n\\t\\t\\tthis._render_settings = new LS.RenderSettings();\\r\\n\\t\\tif(v.constructor === Array && v[3] == \\\"RenderSettings\\\") //encoded object [\\\"@ENC\\\",\\\"object\\\",data,\\\"RenderSettings\\\"]\\r\\n\\t\\t\\tthis._render_settings.configure( v[2] );\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis._render_settings.configure(v);\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this._render_settings;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\nGlobalInfo.prototype.computeIrradiance = function( position, near, far, background_color )\\r\\n{\\r\\n\\tif(!LS.Components.IrradianceCache)\\r\\n\\t\\tthrow(\\\"cannot compute, no LS.Components.IrradianceCache component found\\\");\\r\\n\\r\\n\\tposition = position || vec3.create();\\r\\n\\tvar texture_size = LS.Components.IrradianceCache.capture_cubemap_size; //default is 64\\r\\n\\tvar texture_settings = { type: gl.FLOAT, texture_type: gl.TEXTURE_CUBE_MAP, format: gl.RGB };\\r\\n\\tvar cubemap = new GL.Texture( LS.Components.IrradianceCache.final_cubemap_size, LS.Components.IrradianceCache.final_cubemap_size, texture_settings );\\r\\n\\tvar temp_cubemap = new GL.Texture( texture_size, texture_size, texture_settings );\\r\\n\\t//renders scene to cubemap\\r\\n\\tLS.Components.IrradianceCache.captureIrradiance( position, cubemap, render_settings, near || 0.1, far || 1000, background_color || [0,0,0,1], true, temp_cubemap );\\r\\n\\tthis._irradiance = LS.Components.IrradianceCache.computeSH( cubemap );\\r\\n\\tconsole.log( \\\"IR factor\\\", this._irradiance );\\r\\n}\\r\\n\\r\\nGlobalInfo.prototype.clearIrradiance = function()\\r\\n{\\r\\n\\tthis._irradiance = null;\\r\\n}\\r\\n\\r\\nGlobalInfo.icon = \\\"mini-icon-bg.png\\\";\\r\\nGlobalInfo.DEFAULT_AMBIENT_COLOR = vec3.fromValues(0.2, 0.2, 0.2);\\r\\n\\r\\nGlobalInfo.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tscene.info = this;\\r\\n}\\r\\n\\r\\nGlobalInfo.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\t//scene.info = null;\\r\\n}\\r\\n\\r\\nGlobalInfo.prototype.getResources = function(res)\\r\\n{\\r\\n\\tfor(var i in this._textures)\\r\\n\\t{\\r\\n\\t\\tif(typeof(this._textures[i]) == \\\"string\\\")\\r\\n\\t\\t\\tres[ this._textures[i] ] = GL.Texture;\\r\\n\\t}\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nGlobalInfo.prototype.getPropertiesInfo = function()\\r\\n{\\r\\n\\treturn {\\r\\n\\t\\t\\\"ambient_color\\\":\\\"color\\\",\\r\\n\\t\\t\\\"textures/environment\\\": \\\"texture\\\",\\r\\n\\t\\t\\\"render_settings\\\":\\\"RenderSettings\\\"\\r\\n\\t};\\r\\n}\\r\\n\\r\\nGlobalInfo.prototype.setProperty = function( name, value )\\r\\n{\\r\\n\\tif(name.substr(0,9) == \\\"textures/\\\" && (!value || value.constructor === String || value.constructor === GL.Texture) )\\r\\n\\t{\\r\\n\\t\\tthis._textures[ name.substr(9) ] = value;\\r\\n\\t\\treturn true;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//used for animation tracks\\r\\nGlobalInfo.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif(path[0] != \\\"textures\\\")\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(path.length == 1)\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tnode: this._root,\\r\\n\\t\\t\\ttarget: this._textures,\\r\\n\\t\\t\\ttype: \\\"object\\\"\\r\\n\\t\\t};\\r\\n\\r\\n\\tvar varname = path[1];\\r\\n\\r\\n\\treturn {\\r\\n\\t\\tnode: this._root,\\r\\n\\t\\ttarget: this._textures,\\r\\n\\t\\tname: varname,\\r\\n\\t\\tvalue: this._textures[ varname ] || null,\\r\\n\\t\\ttype: \\\"texture\\\"\\r\\n\\t};\\r\\n}\\r\\n\\r\\nGlobalInfo.prototype.setPropertyValueFromPath = function( path, value, offset )\\r\\n{\\r\\n\\toffset = offset || 0;\\r\\n\\tif( path.length < (offset+1) )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( path[offset] != \\\"textures\\\" )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar varname = path[offset+1];\\r\\n\\tthis._textures[ varname ] = value;\\r\\n}\\r\\n\\r\\n\\r\\nGlobalInfo.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tfor(var i in this._textures)\\r\\n\\t{\\r\\n\\t\\tif(this._textures[i] == old_name)\\r\\n\\t\\t\\tthis._textures[i] = new_name;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nLS.registerComponent( GlobalInfo );\\r\\nLS.GlobalInfo = GlobalInfo;\\r\\n///@FILE:../src/components/graphComponents.js\\r\\n///@INFO: GRAPHS\\r\\n/* Requires LiteGraph.js ******************************/\\r\\n\\r\\n//on include, link to resources manager\\r\\nif(typeof(LGraphTexture) != \\\"undefined\\\")\\r\\n{\\r\\n\\t//link LGraph textures system with LiteScene\\r\\n\\tLGraphTexture.getTexturesContainer = function() { return LS.ResourcesManager.textures };\\r\\n\\tLGraphTexture.storeTexture = function(name, texture) { return LS.ResourcesManager.registerResource(name, texture); };\\r\\n\\tLGraphTexture.loadTexture = LS.ResourcesManager.load.bind( LS.ResourcesManager );\\r\\n\\r\\n\\tLiteGraph.allow_scripts = LS.allow_scripts; //let graphs that contain code execute it\\r\\n}\\r\\n\\r\\nif(typeof(LiteGraph.LGraphRender) != \\\"undefined\\\")\\r\\n{\\r\\n\\tLiteGraph.LGraphRender.onRequestCameraMatrices = function(view,proj,viewproj)\\r\\n\\t{\\r\\n\\t\\tvar camera = LS.Renderer.getCurrentCamera();\\r\\n\\t\\tif(!camera)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tview.set( camera._view_matrix );\\r\\n\\t\\tproj.set( camera._projection_matrix );\\r\\n\\t\\tviewproj.set( camera._viewprojection_matrix );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\nif( typeof(LGAudio) != \\\"undefined\\\" )\\r\\n{\\r\\n\\tLGAudio.onProcessAudioURL = function(url)\\r\\n\\t{\\r\\n\\t\\treturn LS.RM.getFullURL(url);\\r\\n\\t}\\r\\n}\\r\\n\\r\\nif(typeof(LiteGraph) != \\\"undefined\\\")\\r\\n{\\r\\n\\tLiteGraph.onNodeTypeReplaced = function(name,ctor,old)\\r\\n\\t{\\r\\n\\t\\tvar comps = LS.GlobalScene.findNodeComponents( LS.Components.GraphComponent );\\r\\n\\t\\tcomps = comps.concat( LS.GlobalScene.findNodeComponents( LS.Components.FXGraphComponent ) );\\r\\n\\t\\tfor(var i = 0; i < comps.length; ++i)\\r\\n\\t\\t\\tcomps[i].graph.checkNodeTypes();\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* This component allow to integrate a behaviour graph on any object\\r\\n* @class GraphComponent\\r\\n* @namespace LS.Components\\r\\n* @param {Object} o object with the serialized info\\r\\n*/\\r\\nfunction GraphComponent(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.from_file = false;\\r\\n\\tthis.force_redraw = false;\\r\\n\\tthis.title = null;\\r\\n\\tthis._filename = null;\\r\\n\\tthis._graphcode = null;\\r\\n\\tthis._graph_properties = null;\\r\\n\\t//this._properties_by_id = {};\\r\\n\\r\\n\\tthis.on_event = \\\"update\\\";\\r\\n\\r\\n\\tif(typeof(LiteGraph) == \\\"undefined\\\")\\r\\n\\t\\treturn console.error(\\\"Cannot use GraphComponent if LiteGraph is not installed\\\");\\r\\n\\r\\n\\tthis._graph_version = -1;\\r\\n\\tthis._graph = new LGraph();\\r\\n\\tthis._graph.getScene = function() { return this._scene || LS.GlobalScene; } //this OR is ugly\\r\\n\\tthis._graph._scenenode = null;\\r\\n\\tthis._loading = false;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n\\telse if(!this.from_file)//default\\r\\n\\t{\\r\\n\\t\\tvar graphnode = this._default_node = LiteGraph.createNode(\\\"scene/node\\\");\\r\\n\\t\\tthis._graph.add( graphnode );\\r\\n\\t}\\r\\n\\t\\r\\n\\tLEvent.bind( this,\\\"trigger\\\", this.trigger, this );\\t\\r\\n}\\r\\n\\r\\nGraphComponent[\\\"@on_event\\\"] = { type:\\\"enum\\\", values: [\\\"start\\\",\\\"render\\\",\\\"beforeRenderScene\\\",\\\"afterRenderScene\\\",\\\"update\\\",\\\"trigger\\\"] };\\r\\nGraphComponent[\\\"@filename\\\"] = { type:\\\"resource\\\", data_type: \\\"graph\\\" };\\r\\n\\r\\n\\r\\nObject.defineProperty( GraphComponent.prototype, \\\"graph\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._graph;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tconsole.error(\\\"graph cannot be set manually\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\n\\r\\nObject.defineProperty( GraphComponent.prototype, \\\"filename\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._filename;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(this._filename == v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(v) //to avoid double slashes\\r\\n\\t\\t\\tv = LS.ResourcesManager.cleanFullpath( v );\\r\\n\\t\\tthis.from_file = true;\\r\\n\\t\\tthis._filename = v;\\r\\n\\t\\tthis._loading = false;\\r\\n\\t\\tthis._graphcode = null;\\r\\n\\t\\tthis.processGraph();\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( GraphComponent.prototype, \\\"graphcode\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._graphcode;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\t//if(this._graphcode == v) return; //disabled because sometimes we want to force reload\\r\\n\\t\\tthis._loading = false;\\r\\n\\t\\tthis._graphcode = v;\\r\\n\\t\\tthis.from_file = true; //if assigning a graphcode, then its a from_file, even if it is null\\r\\n\\t\\tif( this._graphcode )\\r\\n\\t\\t\\tthis._filename = this._graphcode.fullpath || this._graphcode.filename;\\r\\n\\t\\telse \\r\\n\\t\\t\\tthis._filename = null;\\r\\n\\t\\tthis._graph_properties = this.serializeProperties();\\r\\n\\t\\tthis.processGraph();\\r\\n\\t}\\r\\n});\\r\\n\\r\\n/*\\r\\nGraphComponent.events_translator = {\\r\\n\\tbeforeRender: \\\"beforeRenderMainPass\\\",\\r\\n\\trender: \\\"beforeRenderScene\\\"\\r\\n};\\r\\n*/\\r\\n\\r\\nGraphComponent.icon = \\\"mini-icon-graph.png\\\";\\r\\n\\r\\n/**\\r\\n* Returns the first component of this container that is of the same class\\r\\n* @method configure\\r\\n* @param {Object} o object with the configuration info from a previous serialization\\r\\n*/\\r\\nGraphComponent.prototype.configure = function(o)\\r\\n{\\r\\n\\tthis._graph_version = -1;\\r\\n\\r\\n\\tif(o.uid)\\r\\n\\t\\tthis.uid = o.uid;\\r\\n\\tif(o.enabled != null)\\r\\n\\t\\tthis.enabled = !!o.enabled;\\r\\n\\tif(o.title)\\r\\n\\t\\tthis.title = String(o.title);\\r\\n\\tif(o.from_file)\\r\\n\\t{\\r\\n\\t\\tthis.from_file = true;\\r\\n\\t\\tthis.filename = o.filename;\\r\\n\\t\\tif( o.graph_properties )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(this._loading)\\r\\n\\t\\t\\t\\tthis._graph_properties = o.graph_properties; //should be cloned?\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis.configureProperties( o.graph_properties );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(o.graph_data)\\r\\n\\t{\\r\\n\\t\\tthis.from_file = false;\\r\\n\\t\\tif(LS.catch_exceptions)\\r\\n\\t\\t{\\r\\n\\t\\t\\ttry\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar obj = JSON.parse( o.graph_data );\\r\\n\\t\\t\\t\\tif( this._graph.configure( obj ) == true ) //has errors\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tLS.GlobalScene.has_errors = true;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.error(\\\"Error configuring Graph data: \\\" + err);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar obj = JSON.parse( o.graph_data );\\r\\n\\t\\t\\tif( this._graph.configure( obj ) == true ) //has errors\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tLS.GlobalScene.has_errors = true;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif(o.on_event)\\r\\n\\t\\tthis.on_event = o.on_event;\\r\\n\\tif(o.force_redraw != null)\\r\\n\\t\\tthis.force_redraw = o.force_redraw;\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.serialize = function()\\r\\n{\\r\\n\\treturn { \\r\\n\\t\\tobject_class: \\\"GraphComponent\\\",\\r\\n\\t\\tuid: this.uid,\\r\\n\\t\\ttitle: this.title,\\r\\n\\t\\tenabled: this.enabled, \\r\\n\\t\\tfrom_file: this.from_file,\\r\\n\\t\\tforce_redraw: this.force_redraw , \\r\\n\\t\\tfilename: this._filename,\\r\\n\\t\\tgraph_properties: this.from_file ? this.serializeProperties() : null,\\r\\n\\t\\tgraph_data: this.from_file ? null : JSON.stringify( this._graph.serialize() ),\\r\\n\\t\\ton_event: this.on_event\\r\\n\\t};\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(this._filename)\\r\\n\\t\\tres[this._filename] = true;\\r\\n\\tthis._graph.sendEventToAllNodes(\\\"getResources\\\",res);\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tthis._graph._scenenode = node;\\r\\n\\tif( this._default_node )\\r\\n\\t\\tthis._default_node.properties.node_id = node.uid;\\r\\n\\t//catch the global rendering\\r\\n\\t//LEvent.bind( LS.GlobalScene, \\\"beforeRenderMainPass\\\", this.onBeforeRender, this );\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tthis._graph._scenenode = null;\\r\\n\\t//LEvent.unbind( LS.GlobalScene, \\\"beforeRenderMainPass\\\", this.onBeforeRender, this );\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\tthis._graph._scene = scene;\\r\\n\\tLEvent.bind( scene , \\\"init\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.bind( scene , \\\"start\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.bind( scene , \\\"pause\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.bind( scene , \\\"unpause\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.bind( scene , \\\"finish\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.bind( scene , \\\"beforeRenderMainPass\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.bind( scene , \\\"beforeRenderScene\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.bind( scene , \\\"afterRenderScene\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.bind( scene , \\\"update\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.bind( scene , \\\"renderGUI\\\", this.onRenderGUI, this );\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.onRemovedFromScene = function( scene )\\r\\n{\\r\\n\\tthis._graph._scene = null;\\r\\n\\tLEvent.unbind( scene, \\\"init\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.unbind( scene, \\\"start\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.unbind( scene, \\\"pause\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.unbind( scene, \\\"unpause\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.unbind( scene, \\\"finish\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.unbind( scene, \\\"beforeRenderMainPass\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.unbind( scene, \\\"beforeRenderScene\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.unbind( scene, \\\"afterRenderScene\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.unbind( scene, \\\"update\\\", this.onSceneEvent, this );\\r\\n\\tLEvent.unbind( scene, \\\"renderGUI\\\", this.onRenderGUI, this );\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.onResourceRenamed = function( old_name, new_name, resource )\\r\\n{\\r\\n\\tif( old_name == this._filename)\\r\\n\\t\\tthis._filename = new_name;\\r\\n\\tthis._graph.sendEventToAllNodes(\\\"onResourceRenamed\\\",[ old_name, new_name, resource ]);\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.onRenderGUI = function( e, canvas )\\r\\n{\\r\\n\\tif( !this.enabled || !this._root.visible )\\r\\n\\t\\treturn;\\r\\n\\tthis._graph.sendEventToAllNodes(\\\"onRenderGUI\\\", canvas );\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.onSceneEvent = function( event_type, event_data )\\r\\n{\\r\\n\\tif(event_type == \\\"beforeRenderMainPass\\\")\\r\\n\\t\\tevent_type = \\\"render\\\";\\r\\n\\t//if( GraphComponent.events_translator[ event_type ] )\\r\\n\\t//\\tevent_type = GraphComponent.events_translator[ event_type ];\\r\\n\\r\\n\\tif(event_type == \\\"init\\\")\\r\\n\\t\\tthis._graph.sendEventToAllNodes(\\\"onInit\\\");\\r\\n\\telse if(event_type == \\\"start\\\")\\r\\n\\t{\\r\\n\\t\\tthis._graph.sendEventToAllNodes(\\\"onStart\\\");\\r\\n\\t\\tthis._graph.status = LGraph.STATUS_RUNNING;\\r\\n\\t}\\r\\n\\telse if(event_type == \\\"pause\\\")\\r\\n\\t{\\r\\n\\t\\tthis._graph.sendEventToAllNodes(\\\"onPause\\\");\\r\\n\\t\\tthis._graph.status = LGraph.STATUS_RUNNING;\\r\\n\\t}\\r\\n\\telse if(event_type == \\\"unpause\\\")\\r\\n\\t{\\r\\n\\t\\tthis._graph.sendEventToAllNodes(\\\"onUnpause\\\");\\r\\n\\t\\tthis._graph.status = LGraph.STATUS_RUNNING;\\r\\n\\t}\\r\\n\\telse if(event_type == \\\"finish\\\")\\r\\n\\t{\\r\\n\\t\\tthis._graph.sendEventToAllNodes(\\\"onStop\\\");\\r\\n\\t\\tthis._graph.status = LGraph.STATUS_STOPPED;\\r\\n\\t}\\r\\n\\r\\n\\tif(this.on_event == event_type)\\r\\n\\t\\tthis.runGraph();\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.trigger = function(e)\\r\\n{\\r\\n\\tif(this.on_event == \\\"trigger\\\")\\r\\n\\t\\tthis.runGraph();\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.runGraph = function()\\r\\n{\\r\\n\\tif(!this._root._in_tree || !this.enabled || !this._root.visible)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//if(!this._graphcode || this._graphcode._version != this._graph_version )\\r\\n\\t//\\tthis.processGraph();\\r\\n\\r\\n\\tif(this.from_file && !this._graphcode)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._graph.runStep( 1, LS.catch_exceptions );\\r\\n\\tif(this.force_redraw)\\r\\n\\t\\tthis._root.scene.requestFrame();\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.processGraph = function( skip_events, on_complete )\\r\\n{\\r\\n\\t//use inner graph\\r\\n\\tif(!this.from_file)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar that = this;\\r\\n\\tthis._graphcode = LS.ResourcesManager.getResource( this._filename );\\r\\n\\tif(!this._graphcode && !this._loading) //must be loaded\\r\\n\\t{\\r\\n\\t\\tthis._loading = true;\\r\\n\\t\\tLS.ResourcesManager.load( this._filename, null, function( res, url ){\\r\\n\\t\\t\\tthis._loading = false;\\r\\n\\t\\t\\tif( url != that.filename )\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tthat.processGraph( skip_events );\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\ton_complete(that);\\r\\n\\t\\t});\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tthis._graph.configure( this._graphcode.data );\\r\\n\\tif( this._graph_properties )\\r\\n\\t{\\r\\n\\t\\tthis.configureProperties( this._graph_properties );\\r\\n\\t\\tthis._graph_properties = null;\\r\\n\\t}\\r\\n\\tthis._graph_version = this._graphcode._version;\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.getResources = function(res)\\r\\n{\\r\\n\\tres[ this._filename ] = true;\\r\\n\\tthis._graph.sendEventToAllNodes(\\\"getResources\\\",res);\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.serializeProperties = function()\\r\\n{\\r\\n\\tvar properties = {};\\r\\n\\r\\n\\tvar nodes = this._graph.findNodesByType(\\\"scene/global\\\");\\r\\n\\tif(!nodes.length)\\r\\n\\t\\treturn properties;\\r\\n\\r\\n\\tfor(var i = 0; i < nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar n = nodes[i];\\r\\n\\t\\tproperties[ n.id ] = n.properties.value;\\r\\n\\t}\\r\\n\\r\\n\\treturn properties;\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.configureProperties = function( properties )\\r\\n{\\r\\n\\tvar nodes = this._graph.findNodesByType(\\\"scene/global\\\");\\r\\n\\tif(!nodes.length)\\r\\n\\t\\treturn properties;\\r\\n\\r\\n\\tfor(var i = 0; i < nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar n = nodes[i];\\r\\n\\t\\tif( properties[ n.id ] === undefined )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tn.properties.value = properties[ n.id ];\\r\\n\\t}\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.getPropertyValue = function( property )\\r\\n{\\r\\n\\tvar nodes = this._graph.findNodesByType(\\\"scene/global\\\");\\r\\n\\tif(!nodes.length)\\r\\n\\t\\treturn null;\\r\\n\\tfor(var i = 0; i < nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar n = nodes[i];\\r\\n\\t\\tvar type = n.properties.type;\\r\\n\\t\\tif(n.properties.name != property)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\treturn n.properties.value;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//TODO: optimize this precaching nodes of type scene/global\\r\\nGraphComponent.prototype.setPropertyValue = function( property, value )\\r\\n{\\r\\n\\tvar nodes = this._graph.findNodesByType(\\\"scene/global\\\");\\r\\n\\tif(!nodes.length)\\r\\n\\t\\treturn;\\r\\n\\tfor(var i = 0; i < nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar n = nodes[i];\\r\\n\\t\\tvar type = n.properties.type;\\r\\n\\t\\tif(n.properties.name != property)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tif(n.properties.value && n.properties.value.set)\\r\\n\\t\\t\\tn.properties.value.set(value);\\r\\n\\t\\telse\\r\\n\\t\\t\\tn.properties.value = value;\\r\\n\\t\\treturn true;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nGraphComponent.prototype.getComponentTitle = function()\\r\\n{\\r\\n\\treturn this.title;\\r\\n}\\r\\n\\r\\n\\r\\nLS.registerComponent( GraphComponent );\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* This component allow to integrate a rendering post FX using a graph\\r\\n* @class FXGraphComponent\\r\\n* @param {Object} o object with the serialized info\\r\\n*/\\r\\nfunction FXGraphComponent(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.frame = new LS.RenderFrameContext();\\r\\n\\tthis.use_antialiasing = false;\\r\\n\\tthis.use_node_camera = false;\\r\\n\\tthis.title = null;\\r\\n\\r\\n\\tif(typeof(LGraphTexture) == \\\"undefined\\\")\\r\\n\\t\\treturn console.error(\\\"Cannot use FXGraphComponent if LiteGraph is not installed\\\");\\r\\n\\r\\n\\tthis._graph = new LGraph();\\r\\n\\tthis._graph.getScene = function() { return this._scene; }\\r\\n\\tthis._graph.component = this;\\r\\n\\r\\n\\tif(o)\\r\\n\\t{\\r\\n\\t\\tthis.configure(o);\\r\\n\\t}\\r\\n\\telse //default\\r\\n\\t{\\r\\n\\t\\tthis._graph_frame_node = LiteGraph.createNode(\\\"scene/frame\\\",\\\"Rendered Frame\\\");\\r\\n\\t\\tthis._graph_frame_node.ignore_remove = true;\\r\\n\\t\\tthis._graph_frame_node.ignore_rename = true;\\r\\n\\t\\tthis._graph.add( this._graph_frame_node );\\r\\n\\r\\n\\t\\tthis._graph_viewport_node = LiteGraph.createNode(\\\"texture/toviewport\\\",\\\"Viewport\\\");\\r\\n\\t\\tthis._graph_viewport_node.pos[0] = 500;\\r\\n\\t\\tthis._graph_viewport_node.properties.disable_alpha = true;\\r\\n\\t\\tthis._graph.add( this._graph_viewport_node );\\r\\n\\r\\n\\t\\tthis._graph_frame_node.connect(0, this._graph_viewport_node );\\r\\n\\t}\\r\\n\\r\\n\\tif(FXGraphComponent.high_precision_format == null && global.gl)\\r\\n\\t{\\r\\n\\t\\tif(gl.half_float_ext)\\r\\n\\t\\t\\tFXGraphComponent.high_precision_format = gl.HALF_FLOAT_OES;\\r\\n\\t\\telse if(gl.float_ext)\\r\\n\\t\\t\\tFXGraphComponent.high_precision_format = gl.FLOAT;\\r\\n\\t\\telse\\r\\n\\t\\t\\tFXGraphComponent.high_precision_format = gl.UNSIGNED_BYTE;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nFXGraphComponent.icon = \\\"mini-icon-graph.png\\\";\\r\\nFXGraphComponent.buffer_size = [1024,512];\\r\\n\\r\\n\\r\\nObject.defineProperty( FXGraphComponent.prototype, \\\"graph\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._graph;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tconsole.error(\\\"graph cannot be set manually\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( FXGraphComponent.prototype, \\\"render_node\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._graph_frame_node;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tconsole.error(\\\"render_node cannot be set manually\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( FXGraphComponent.prototype, \\\"viewport_node\\\", {\\r\\n\\tenumerable: false,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._graph_viewport_node;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tconsole.error(\\\"viewport_node cannot be set manually\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns the first component of this container that is of the same class\\r\\n* @method configure\\r\\n* @param {Object} o object with the configuration info from a previous serialization\\r\\n*/\\r\\nFXGraphComponent.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(!this._graph || !o.graph_data)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis.uid = o.uid;\\r\\n\\tthis.enabled = !!o.enabled;\\r\\n\\tif(o.title)\\r\\n\\t\\tthis.title = o.title;\\r\\n\\tthis.use_antialiasing = !!o.use_antialiasing;\\r\\n\\tthis.use_node_camera = !!o.use_node_camera;\\r\\n\\tif(o.frame)\\r\\n\\t\\tthis.frame.configure(o.frame);\\r\\n\\r\\n\\tvar graph_data = JSON.parse( o.graph_data )\\r\\n\\tif( this._graph.configure( graph_data ) == true ) //has errors\\r\\n\\t{\\r\\n\\t\\tLS.GlobalScene.has_error = true;\\r\\n\\t}\\r\\n\\r\\n\\tthis._graph_frame_node = this._graph.findNodesByTitle(\\\"Rendered Frame\\\")[0];\\r\\n\\tthis._graph_viewport_node = this._graph.findNodesByType(\\\"texture/toviewport\\\")[0];\\r\\n\\r\\n\\tif(!this._graph_frame_node) //LEGACY CODE, DELETE AT SOME POINT\\r\\n\\t{\\r\\n\\t\\tconsole.log(\\\"CONVERTING LEGACY DATA TO NEW FORMAT\\\");\\r\\n\\t\\t\\r\\n\\t\\tthis._graph_frame_node = LiteGraph.createNode(\\\"scene/frame\\\",\\\"Rendered Frame\\\");\\r\\n\\t\\tthis._graph_frame_node.ignore_remove = true;\\r\\n\\t\\tthis._graph_frame_node.ignore_rename = true;\\r\\n\\t\\tthis._graph.add( this._graph_frame_node );\\r\\n\\r\\n\\t\\tvar old_nodes = [\\\"Color Buffer\\\",\\\"Depth Buffer\\\",\\\"Extra Buffer\\\"];\\r\\n\\t\\tfor(var j = 0; j < old_nodes.length; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar old_node = this._graph.findNodesByTitle(old_nodes[j])[0];\\r\\n\\t\\t\\tif(!old_node)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar connection_info = old_node.getOutputInfo(0);\\r\\n\\t\\t\\tif(!connection_info.links)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar links = connection_info.links.concat();\\r\\n\\t\\t\\tfor(var i in links)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar link = this._graph.links[ links[i] ];\\r\\n\\t\\t\\t\\tif(!link)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tthis._graph_frame_node.connect( j, link.target_id, link.target_slot ); \\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis._graph.remove( old_node );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.serialize = function()\\r\\n{\\r\\n\\treturn {\\r\\n\\t\\tobject_class: \\\"FXGraphComponent\\\",\\r\\n\\t\\tuid: this.uid,\\r\\n\\t\\tenabled: this.enabled,\\r\\n\\t\\ttitle: this.title,\\r\\n\\t\\tuse_antialiasing: this.use_antialiasing,\\r\\n\\t\\tframe: this.frame.serialize(),\\r\\n\\t\\tuse_node_camera: this.use_node_camera,\\r\\n\\r\\n\\t\\tgraph_data: this._graph ? JSON.stringify( this._graph.serialize() ) : null\\r\\n\\t};\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(!this._graph) //in case it wasnt connected\\r\\n\\t\\treturn;\\r\\n\\tthis._graph.sendEventToAllNodes(\\\"getResources\\\",res);\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.getPropertyValue = function( property )\\r\\n{\\r\\n\\tvar nodes = this._graph.findNodesByType(\\\"scene/global\\\");\\r\\n\\tif(nodes.length)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < nodes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar n = nodes[i];\\r\\n\\t\\t\\tvar type = n.properties.type;\\r\\n\\t\\t\\tif(n.properties.name != property)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\treturn n.properties.value;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\nFXGraphComponent.prototype.setPropertyValue = function( property, value )\\r\\n{\\r\\n\\tvar nodes = this._graph.findNodesByType(\\\"scene/global\\\");\\r\\n\\tif(nodes.length)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < nodes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar n = nodes[i];\\r\\n\\t\\t\\tvar type = n.properties.type;\\r\\n\\t\\t\\tif(n.properties.name != property)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(n.properties.value && n.properties.value.set)\\r\\n\\t\\t\\t\\tn.properties.value.set(value);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tn.properties.value = value;\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.onRenderGUI = GraphComponent.prototype.onRenderGUI;\\r\\n\\r\\nFXGraphComponent.prototype.onResourceRenamed = function(old_name, new_name, res)\\r\\n{\\r\\n\\tif(!this._graph) //in case it wasnt connected\\r\\n\\t\\treturn;\\r\\n\\tthis._graph.sendEventToAllNodes(\\\"onResourceRenamed\\\",[old_name, new_name, res]);\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tif(!this._graph) //in case litegraph is not installed\\r\\n\\t\\treturn;\\r\\n\\tthis._graph._scenenode = node;\\r\\n\\t//catch the global rendering\\r\\n\\t//LEvent.bind( LS.GlobalScene, \\\"beforeRenderMainPass\\\", this.onBeforeRender, this );\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tif(!this._graph) //in case it wasnt connected\\r\\n\\t\\treturn;\\r\\n\\tthis._graph._scenenode = null;\\r\\n\\t//LEvent.unbind( LS.GlobalScene, \\\"beforeRenderMainPass\\\", this.onBeforeRender, this );\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene , \\\"renderGUI\\\", this.onRenderGUI, this );\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind( scene , \\\"renderGUI\\\", this.onRenderGUI, this );\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\tif(!this._graph) //in case it wasnt connected\\r\\n\\t\\treturn;\\r\\n\\tthis._graph._scene = scene;\\r\\n\\tLEvent.bind( scene, \\\"beforeRender\\\", this.onBeforeRender, this );\\r\\n\\tLEvent.bind( scene, \\\"enableFrameContext\\\", this.onEnableContext, this );\\r\\n\\tLEvent.bind( scene, \\\"showFrameContext\\\", this.onAfterRender, this );\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.onRemovedFromScene = function( scene )\\r\\n{\\r\\n\\tif(!this._graph) //in case it wasnt connected\\r\\n\\t\\treturn;\\r\\n\\tthis._graph._scene = null;\\r\\n\\tLEvent.unbind( scene, \\\"beforeRender\\\", this.onBeforeRender, this );\\r\\n\\tLEvent.unbind( scene, \\\"enableFrameContext\\\", this.onEnableContext, this );\\r\\n\\tLEvent.unbind( scene, \\\"showFrameContext\\\", this.onAfterRender, this );\\r\\n\\r\\n\\tLS.ResourcesManager.unregisterResource( \\\":color_\\\" + this.uid );\\r\\n\\tLS.ResourcesManager.unregisterResource( \\\":depth_\\\" + this.uid );\\r\\n\\tLS.ResourcesManager.unregisterResource( \\\":extra_\\\" + this.uid );\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.onBeforeRender = function(e, render_settings)\\r\\n{\\r\\n\\tif(this.enabled && this._graph && this._root.visible) //used to read back from textures to avoid stalling\\r\\n\\t\\tthis._graph.sendEventToAllNodes(\\\"onPreRenderExecute\\\");\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.onEnableContext = function(e, render_settings)\\r\\n{\\r\\n\\tthis._last_camera = LS.Renderer._main_camera; //LS.Renderer._current_camera;\\r\\n\\r\\n\\tif(!this.enabled || !this._root.visible)\\r\\n\\t{\\r\\n\\t\\tif( this._binded_camera )\\r\\n\\t\\t{\\r\\n\\t\\t\\tLEvent.unbindAll( this._binded_camera, this );\\r\\n\\t\\t\\tthis._binded_camera = null;\\r\\n\\t\\t}\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//FBO for one camera\\r\\n\\tif(this.use_node_camera)\\r\\n\\t{\\r\\n\\t\\tvar camera = this._root.camera;\\r\\n\\t\\tif(camera && camera != this._binded_camera)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(this._binded_camera)\\r\\n\\t\\t\\t\\tLEvent.unbindAll( this._binded_camera, this );\\r\\n\\t\\t\\tLEvent.bind( camera, \\\"enableFrameContext\\\", this.enableCameraFBO, this );\\r\\n\\t\\t\\tLEvent.bind( camera, \\\"showFrameContext\\\", this.showCameraFBO, this );\\r\\n\\t\\t}\\r\\n\\t\\tthis._binded_camera = camera;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\telse if( this._binded_camera )\\r\\n\\t{\\r\\n\\t\\tLEvent.unbindAll( this._binded_camera, this );\\r\\n\\t\\tthis._binded_camera = null;\\r\\n\\t}\\r\\n\\r\\n\\tthis.enableGlobalFBO( render_settings );\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.onAfterRender = function(e, render_settings )\\r\\n{\\r\\n\\tif(!this.enabled || !this._root.visible)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(this.use_node_camera)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis.showFBO();\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.enableCameraFBO = function(e, render_settings )\\r\\n{\\r\\n\\tif(!this.enabled || !this._root.visible)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar camera = this._binded_camera;\\r\\n\\t\\r\\n\\tvar viewport = this._viewport = camera.getLocalViewport( null, this._viewport );\\r\\n\\tthis.frame.enable( render_settings, viewport );\\r\\n\\trender_settings.ignore_viewports = true;\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.showCameraFBO = function(e, render_settings )\\r\\n{\\r\\n\\tif(!this.enabled || !this._root.visible)\\r\\n\\t\\treturn;\\r\\n\\trender_settings.ignore_viewports = false;\\r\\n\\r\\n\\tthis.showFBO();\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.enableGlobalFBO = function( render_settings )\\r\\n{\\r\\n\\tif(!this.enabled || !this._root.visible)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//configure\\r\\n\\tthis.frame.enable( render_settings, null, LS.Renderer._main_camera );\\r\\n\\r\\n\\tif(this._graph)\\r\\n\\t\\tthis._graph.sendEventToAllNodes(\\\"onPreRenderExecute\\\");\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.showFBO = function()\\r\\n{\\r\\n\\tif(!this.enabled || !this._root.visible)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis.frame.disable();\\r\\n\\r\\n\\tLS.ResourcesManager.textures[\\\":color_\\\" + this.uid] = this.frame._color_texture;\\r\\n\\tLS.ResourcesManager.textures[\\\":depth_\\\" + this.uid] = this.frame._depth_texture;\\r\\n\\tif(this.frame.num_extra_textures)\\r\\n\\t{\\r\\n\\t\\tfor(var i = 0; i < this.frame.num_extra_textures; ++i)\\r\\n\\t\\t\\tLS.ResourcesManager.textures[\\\":extra\\\"+ i +\\\"_\\\" + this.uid] = this.frame._textures[i+1];\\r\\n\\t}\\r\\n\\r\\n\\tif(this.use_node_camera && this._viewport)\\r\\n\\t{\\r\\n\\t\\tgl.setViewport( this._viewport );\\r\\n\\t\\tthis.applyGraph();\\r\\n\\t\\tgl.setViewport( this.frame._fbo._old_viewport );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tthis.applyGraph();\\r\\n}\\r\\n\\r\\n\\r\\n//take the resulting textures and pass them through the graph\\r\\nFXGraphComponent.prototype.applyGraph = function()\\r\\n{\\r\\n\\tif(!this._graph)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this._graph_frame_node)\\r\\n\\t\\tthis._graph_frame_node = this._graph.findNodesByTitle(\\\"Rendered Frame\\\")[0];\\r\\n\\tthis._graph_frame_node._color_texture = \\\":color_\\\" + this.uid;\\r\\n\\tthis._graph_frame_node._depth_texture = \\\":depth_\\\" + this.uid;\\r\\n\\tthis._graph_frame_node._extra_texture = \\\":extra0_\\\" + this.uid;\\r\\n\\tthis._graph_frame_node._camera = this._last_camera;\\r\\n\\tthis._graph_frame_node._extra_texture = \\\":extra0_\\\" + this.uid;\\r\\n\\r\\n\\tif(this._graph_viewport_node) //force antialiasing\\r\\n\\t{\\r\\n\\t\\tthis._graph_viewport_node.properties.filter = this.frame.filter_texture;\\r\\n\\t\\tthis._graph_viewport_node.properties.antialiasing = this.use_antialiasing;\\r\\n\\t}\\r\\n\\r\\n\\t//execute graph\\r\\n\\tthis._graph.runStep(1, LS.catch_exceptions );\\r\\n}\\r\\n\\r\\nFXGraphComponent.prototype.getComponentTitle = GraphComponent.prototype.getComponentTitle;\\r\\n\\r\\nLS.registerComponent( FXGraphComponent );\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n///@FILE:../src/components/knob.js\\r\\n///@INFO: UNCOMMON\\r\\n(function(){\\r\\n\\r\\n/**\\r\\n* Knob allows to rotate a mesh like a knob (rotate when dragging)\\r\\n* @class Knob\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\n\\r\\nfunction Knob(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\r\\n\\tthis.value = 0;\\r\\n\\tthis.delta = 0.01;\\r\\n\\r\\n\\tthis.steps = 0; //0 = continuous\\r\\n\\tthis.min_value = 0;\\r\\n\\tthis.max_value = 1;\\r\\n\\tthis.min_angle = -120;\\r\\n\\tthis.max_angle = 120;\\r\\n\\tthis.axis = vec3.fromValues(0,0,1);\\r\\n\\r\\n\\tthis._dragging = false;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nKnob.icon = \\\"mini-icon-knob.png\\\";\\r\\n\\r\\nKnob.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"mousedown\\\", this.onMouse, this );\\r\\n\\tLEvent.bind( scene, \\\"mouseup\\\", this.onMouse, this );\\r\\n\\tLEvent.bind( scene, \\\"mousemove\\\", this.onMouse, this );\\r\\n\\tthis.updateKnob();\\r\\n}\\r\\n\\r\\nKnob.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbindAll( scene, this );\\r\\n}\\r\\n\\r\\n\\r\\nKnob.prototype.updateKnob = function()\\r\\n{\\r\\n\\tif(!this._root || !this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar f = this.value / (this.max_value - this.min_value);\\r\\n\\tvar angle = (this.min_angle + (this.max_angle - this.min_angle) * f );\\r\\n\\tquat.setAxisAngle(this._root.transform._rotation,this.axis, angle * DEG2RAD);\\r\\n\\tthis._root.transform.mustUpdate = true;\\r\\n}\\r\\n\\r\\nKnob.prototype.onMouse = function(e, mouse_event)\\r\\n{ \\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( e == \\\"mousedown\\\")\\r\\n\\t{\\r\\n\\t\\tif(!this._root || !this._root._instances || !this._root._instances.length)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar instance = this._root._instances[0];\\r\\n\\r\\n\\t\\tvar cam = LS.Renderer.getCameraAtPosition( mouse_event.canvasx, mouse_event.canvasy );\\r\\n\\t\\tif(!cam)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar ray = cam.getRay( mouse_event.canvasx, mouse_event.canvasy );\\r\\n\\t\\tif(!ray)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tthis._dragging = geo.testRayBBox( ray.origin, ray.direction, instance.aabb);\\r\\n\\t}\\r\\n\\telse if( e == \\\"mouseup\\\")\\r\\n\\t{\\r\\n\\t\\tthis._dragging = false;\\r\\n\\t}\\r\\n\\telse //mouse move\\r\\n\\t{\\r\\n\\t\\tif(!mouse_event.dragging || !this._dragging)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tthis.value -= mouse_event.deltay * this.delta;\\r\\n\\r\\n\\t\\tif(this.value > this.max_value)\\r\\n\\t\\t\\tthis.value = this.max_value;\\r\\n\\t\\telse if(this.value < this.min_value)\\r\\n\\t\\t\\tthis.value = this.min_value;\\r\\n\\r\\n\\t\\tthis.updateKnob();\\r\\n\\r\\n\\t\\tLEvent.trigger( this, \\\"change\\\", this.value );\\r\\n\\t\\tif(this._root)\\r\\n\\t\\t\\tLEvent.trigger( this._root, \\\"knobChange\\\", this.value );\\r\\n\\r\\n\\t\\treturn false;\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.registerComponent( Knob );\\r\\n\\r\\n})();\\r\\n///@FILE:../src/components/particles.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* The base class used by the ParticlesEmissor\\r\\n* @class Particle\\r\\n* @namespace LS\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\nfunction Particle()\\r\\n{\\r\\n\\tthis.id = 0;\\r\\n\\tthis._pos = vec3.fromValues(0,0,0);\\r\\n\\tthis._vel = vec3.fromValues(0,0,0);\\r\\n\\tthis.life = 1;\\r\\n\\tthis.angle = 0;\\r\\n\\tthis.size = 1;\\r\\n\\tthis.rot = 0;\\r\\n}\\r\\n\\r\\nObject.defineProperty( Particle.prototype, 'pos', {\\r\\n\\tget: function() { return this._pos; },\\r\\n\\tset: function(v) { this._pos.set(v); },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( Particle.prototype, 'vel', {\\r\\n\\tget: function() { return this._vel; },\\r\\n\\tset: function(v) { this._vel.set(v); },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* ParticlesEmissor allow to render a particle system, meant to render things like smoke or fire\\r\\n* @class ParticlesEmissor\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\nfunction ParticleEmissor(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\r\\n\\tthis.max_particles = 1024;\\r\\n\\tthis.warm_up_time = 0;\\r\\n\\tthis.point_particles = false;\\r\\n\\r\\n\\tthis.emissor_type = ParticleEmissor.BOX_EMISSOR;\\r\\n\\tthis.emissor_rate = 5; //particles per second\\r\\n\\tthis.emissor_size = vec3.fromValues(10,10,10);\\r\\n\\tthis.emissor_mesh = null;\\r\\n\\r\\n\\tthis.particle_life = 5;\\r\\n\\tthis.particle_speed = 10;\\r\\n\\tthis.particle_size = 5;\\r\\n\\tthis.particle_rotation = 0;\\r\\n\\tthis.particle_size_curve = [[1,1]];\\r\\n\\r\\n\\tthis._particle_start_color = vec3.fromValues(1,1,1);\\r\\n\\tthis._particle_end_color = vec3.fromValues(1,1,1);\\r\\n\\r\\n\\tthis.particle_opacity_curve = [[0.5,1]];\\r\\n\\r\\n\\tthis.texture_grid_size = 1;\\r\\n\\r\\n\\t//physics\\r\\n\\tthis.physics_gravity = [0,0,0];\\r\\n\\tthis.physics_friction = 0;\\r\\n\\r\\n\\t//material\\r\\n\\tthis.opacity = 1;\\r\\n\\tthis.additive_blending = false;\\r\\n\\tthis.texture = null;\\r\\n\\tthis.animation_fps = 1;\\r\\n\\tthis.soft_particles = false;\\r\\n\\r\\n\\tthis.use_node_material = false; \\r\\n\\tthis.animated_texture = false; //change frames\\r\\n\\tthis.loop_animation = false;\\r\\n\\tthis.independent_color = false;\\r\\n\\tthis.premultiplied_alpha = false;\\r\\n\\tthis.align_with_camera = true;\\r\\n\\tthis.align_always = false; //align with all cameras\\r\\n\\tthis.follow_emitter = false;\\r\\n\\tthis.sort_in_z = true; //slower\\r\\n\\tthis.stop_update = false; //do not move particles\\r\\n\\tthis.ignore_lights = false; \\r\\n\\r\\n\\tthis.onCreateParticle = null;\\r\\n\\tthis.onUpdateParticle = null;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n\\r\\n\\t//LEGACY!!! sizes where just a number before\\r\\n\\tif(typeof(this.emissor_size) == \\\"number\\\")\\r\\n\\t\\tthis.emissor_size = [this.emissor_size,this.emissor_size,this.emissor_size];\\r\\n\\r\\n\\tthis._emissor_pos = vec3.create();\\r\\n\\tthis._particles = [];\\r\\n\\tthis._remining_dt = 0;\\r\\n\\tthis._visible_particles = 0;\\r\\n\\tthis._min_particle_size = 0.001;\\r\\n\\tthis._last_id = 0;\\r\\n\\r\\n\\tif(global.gl)\\r\\n\\t\\tthis.createMesh();\\r\\n\\r\\n\\t\\r\\n\\t/* demo particles\\r\\n\\tfor(var i = 0; i < this.max_particles; i++)\\r\\n\\t{\\r\\n\\t\\tvar p = this.createParticle();\\r\\n\\t\\tthis._particles.push(p);\\r\\n\\t}\\r\\n\\t*/\\r\\n}\\r\\n\\r\\nParticleEmissor.BOX_EMISSOR = 1;\\r\\nParticleEmissor.SPHERE_EMISSOR = 2;\\r\\nParticleEmissor.MESH_EMISSOR = 3;\\r\\nParticleEmissor.CUSTOM_EMISSOR = 10;\\r\\n\\r\\nParticleEmissor[\\\"@emissor_type\\\"] = { type:\\\"enum\\\", values:{ \\\"Box\\\":ParticleEmissor.BOX_EMISSOR, \\\"Sphere\\\":ParticleEmissor.SPHERE_EMISSOR, \\\"Mesh\\\":ParticleEmissor.MESH_EMISSOR, \\\"Custom\\\": ParticleEmissor.CUSTOM_EMISSOR }};\\r\\nParticleEmissor.icon = \\\"mini-icon-particles.png\\\";\\r\\n\\r\\nObject.defineProperty( ParticleEmissor.prototype, 'particle_start_color', {\\r\\n\\tget: function() { return this._particle_start_color; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif(v)\\r\\n\\t\\t\\tthis._particle_start_color.set(v); \\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( ParticleEmissor.prototype, 'particle_end_color', {\\r\\n\\tget: function() { return this._particle_end_color; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif(v)\\r\\n\\t\\t\\tthis._particle_end_color.set(v); \\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\nParticleEmissor.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"update\\\",this.onUpdate,this);\\r\\n\\tLEvent.bind( scene, \\\"start\\\",this.onStart,this);\\r\\n\\tLEvent.bind( scene, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n\\tLEvent.bind( scene, \\\"afterCameraEnabled\\\",this.onAfterCamera, this);\\r\\n}\\r\\n\\r\\nParticleEmissor.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbindAll( scene, this );\\r\\n}\\r\\n\\r\\nParticleEmissor.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(this.emissor_mesh)\\r\\n\\t\\tres[ this.emissor_mesh ] = Mesh;\\r\\n\\tif(this.texture)\\r\\n\\t\\tres[ this.texture ] = Texture;\\r\\n}\\r\\n\\r\\nParticleEmissor.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.emissor_mesh == old_name)\\r\\n\\t\\tthis.emissor_mesh = new_name;\\r\\n\\tif(this.texture == old_name)\\r\\n\\t\\tthis.texture = new_name;\\r\\n}\\r\\n\\r\\nParticleEmissor.prototype.onAfterCamera = function(e,camera)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(this.align_always)\\r\\n\\t\\tthis.updateMesh( camera );\\r\\n}\\r\\n\\r\\nParticleEmissor.prototype.createParticle = function(p)\\r\\n{\\r\\n\\tp = p || new Particle();\\r\\n\\t\\r\\n\\tswitch(this.emissor_type)\\r\\n\\t{\\r\\n\\t\\tcase ParticleEmissor.BOX_EMISSOR: p._pos.set( [this.emissor_size[0] * ( Math.random() - 0.5), this.emissor_size[1] * ( Math.random() - 0.5 ), this.emissor_size[2] * (Math.random() - 0.5) ]); break;\\r\\n\\t\\tcase ParticleEmissor.SPHERE_EMISSOR: \\r\\n\\t\\t\\tvar gamma = 2 * Math.PI * Math.random();\\r\\n\\t\\t\\tvar theta = Math.acos(2 * Math.random() - 1);\\r\\n\\t\\t\\tp._pos.set( [Math.sin(theta) * Math.cos(gamma), Math.sin(theta) * Math.sin(gamma), Math.cos(theta) ]);\\r\\n\\t\\t\\tvec3.multiply( p.pos, p.pos, this.emissor_size); \\r\\n\\t\\t\\tbreak;\\r\\n\\t\\t\\t//p.pos = vec3.multiply( vec3.normalize( vec3.create( [(Math.random() - 0.5), ( Math.random() - 0.5 ), (Math.random() - 0.5)])), this.emissor_size); break;\\r\\n\\t\\tcase ParticleEmissor.MESH_EMISSOR: \\r\\n\\t\\t\\tvar mesh = this.emissor_mesh;\\r\\n\\t\\t\\tif(mesh && mesh.constructor === String)\\r\\n\\t\\t\\t\\tmesh = LS.ResourcesManager.getMesh(this.emissor_mesh);\\r\\n\\t\\t\\tif(mesh && mesh.getBuffer(\\\"vertices\\\") )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar vertices = mesh.getBuffer(\\\"vertices\\\").data;\\t\\t\\t\\t\\r\\n\\t\\t\\t\\tvar v = Math.floor(Math.random() * vertices.length / 3)*3;\\r\\n\\t\\t\\t\\tp._pos.set( [vertices[v] + Math.random() * 0.001, vertices[v+1] + Math.random() * 0.001, vertices[v+2] + Math.random() * 0.001] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tp._pos.set([0,0,0]);\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase ParticleEmissor.CUSTOM_EMISSOR: //done after the rest\\r\\n\\t\\tdefault: \\r\\n\\t}\\r\\n\\r\\n\\tp._vel.set( [Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 ] );\\r\\n\\tp.life = this.particle_life;\\r\\n\\tp.id = this._last_id;\\r\\n\\tp.angle = 0;\\r\\n\\tp.size = 1;\\r\\n\\tp.rot = this.particle_rotation + 0.25 * this.particle_rotation * Math.random();\\r\\n\\r\\n\\tthis._last_id += 1;\\r\\n\\tif(this.independent_color)\\r\\n\\t\\tp.c = vec3.clone( this.particle_start_color );\\r\\n\\r\\n\\tvec3.scale(p._vel, p._vel, this.particle_speed);\\r\\n\\r\\n\\t//after everything so the user can edit whatever he wants\\r\\n\\tif(this.emissor_type == ParticleEmissor.CUSTOM_EMISSOR && this.onCreateParticle)\\r\\n\\t\\tthis.onCreateParticle( p, this );\\r\\n\\r\\n\\t//this._root.transform.transformPoint(p.pos, p.pos);\\r\\n\\tif(!this.follow_emitter) //the transform will be applyed in the matrix\\r\\n\\t\\tvec3.add(p._pos, p._pos, this._emissor_pos);\\r\\n\\r\\n\\treturn p;\\r\\n}\\r\\n\\r\\nParticleEmissor.prototype.onStart = function(e)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(this.warm_up_time <= 0)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar delta = 1/30;\\r\\n\\tfor(var i = 0; i < this.warm_up_time; i+= delta)\\r\\n\\t\\tthis.onUpdate( null, delta, true);\\r\\n}\\r\\n\\r\\nParticleEmissor.prototype.onUpdate = function(e, dt, do_not_updatemesh )\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this._root.scene)\\r\\n\\t\\tthrow(\\\"update without scene? impossible\\\");\\r\\n\\r\\n\\tif(this._root.transform)\\r\\n\\t\\tthis._root.transform.getGlobalPosition(this._emissor_pos);\\r\\n\\r\\n\\tif(this.emissor_rate < 0) this.emissor_rate = 0;\\r\\n\\r\\n\\tif(!this.stop_update)\\r\\n\\t{\\r\\n\\t\\t//update particles\\r\\n\\t\\tvar gravity = vec3.clone(this.physics_gravity);\\r\\n\\t\\tvar friction = this.physics_friction;\\r\\n\\t\\tvar particles = [];\\r\\n\\t\\tvar vel = vec3.create();\\r\\n\\t\\tvar rot = this.particle_rotation * dt;\\r\\n\\r\\n\\t\\tfor(var i = 0, l = this._particles.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar p = this._particles[i];\\r\\n\\r\\n\\t\\t\\tvec3.copy(vel, p._vel);\\r\\n\\t\\t\\tvec3.add(vel, gravity, vel);\\r\\n\\t\\t\\tvec3.scale(vel, vel, dt);\\r\\n\\r\\n\\t\\t\\tif(friction)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvel[0] -= vel[0] * friction;\\r\\n\\t\\t\\t\\tvel[1] -= vel[1] * friction;\\r\\n\\t\\t\\t\\tvel[2] -= vel[2] * friction;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvec3.add( p._pos, vel, p._pos);\\r\\n\\r\\n\\t\\t\\tp.angle += p.rot * dt;\\r\\n\\t\\t\\tp.life -= dt;\\r\\n\\r\\n\\t\\t\\tif(this.onUpdateParticle)\\r\\n\\t\\t\\t\\tthis.onUpdateParticle(p,dt,this);\\r\\n\\r\\n\\t\\t\\tif(p.life > 0) //keep alive\\r\\n\\t\\t\\t\\tparticles.push(p);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//emit new\\r\\n\\t\\tif(this.emissor_rate != 0)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar new_particles = (dt + this._remining_dt) * this.emissor_rate;\\r\\n\\t\\t\\tthis._remining_dt = (new_particles % 1) / this.emissor_rate;\\r\\n\\t\\t\\tnew_particles = new_particles<<0;\\r\\n\\r\\n\\t\\t\\tif(new_particles > this.max_particles)\\r\\n\\t\\t\\t\\tnew_particles = this.max_particles;\\r\\n\\r\\n\\t\\t\\tfor(var i = 0; i < new_particles; i++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar p = this.createParticle();\\r\\n\\t\\t\\t\\tif(particles.length < this.max_particles)\\r\\n\\t\\t\\t\\t\\tparticles.push(p);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//replace old container with new one\\r\\n\\t\\tthis._particles = particles;\\r\\n\\t}\\r\\n\\r\\n\\t//compute mesh\\r\\n\\tif(!this.align_always && !do_not_updatemesh)\\r\\n\\t{\\r\\n\\t\\tthis.updateMesh( LS.Renderer._current_camera );\\r\\n\\t\\tthis._root.scene.requestFrame();\\r\\n\\t}\\r\\n\\r\\n\\t//send change\\r\\n\\tLEvent.trigger( this._root.scene , \\\"change\\\"); //??\\r\\n}\\r\\n\\r\\nParticleEmissor.prototype.createMesh = function ()\\r\\n{\\r\\n\\tif( this._mesh_maxparticles == this.max_particles)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._vertices = new Float32Array(this.max_particles * 6 * 3); //6 vertex per particle x 3 floats per vertex\\r\\n\\tthis._coords = new Float32Array(this.max_particles * 6 * 2);\\r\\n\\tthis._colors = new Float32Array(this.max_particles * 6 * 4);\\r\\n\\tthis._extra2 = new Float32Array(this.max_particles * 2);\\r\\n\\r\\n\\tvar default_coords = [1,1, 0,1, 1,0, 0,1, 0,0, 1,0];\\r\\n\\tvar default_color = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1];\\r\\n\\r\\n\\tfor(var i = 0; i < this.max_particles; i++)\\r\\n\\t{\\r\\n\\t\\tthis._coords.set( default_coords , i*6*2);\\r\\n\\t\\tthis._colors.set( default_color , i*6*4);\\r\\n\\t\\tthis._extra2[i*2] = 1;\\r\\n\\t\\tthis._extra2[i*2+1] = i;\\r\\n\\t}\\r\\n\\r\\n\\tthis._computed_grid_size = 1;\\r\\n\\t//this._mesh = Mesh.load({ vertices:this._vertices, coords: this._coords, colors: this._colors, stream_type: gl.STREAM_DRAW });\\r\\n\\tthis._mesh = new GL.Mesh();\\r\\n\\tthis._mesh.addBuffers({ vertices:this._vertices, coords: this._coords, colors: this._colors, extra2: this._extra2 }, null, gl.STREAM_DRAW);\\r\\n\\tthis._mesh_maxparticles = this.max_particles;\\r\\n}\\r\\n\\r\\nParticleEmissor._tmp_quat = quat.create();\\r\\n\\r\\nParticleEmissor.prototype.updateMesh = function (camera)\\r\\n{\\r\\n\\tif(!camera) //no main camera specified (happens at early updates)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( this._mesh_maxparticles != this.max_particles) \\r\\n\\t\\tthis.createMesh();\\r\\n\\r\\n\\tvar center = camera.getEye(); \\r\\n\\r\\n\\tvar MIN_SIZE = this._min_particle_size;\\r\\n\\r\\n\\t/*\\r\\n\\tif(this.follow_emitter)\\r\\n\\t{\\r\\n\\t\\tvar iM = this._root.transform.getMatrix();\\r\\n\\t\\tmat4.multiplyVec3(iM, center);\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\tvar front = camera.getLocalVector([0,0,1]);\\r\\n\\tvar right = camera.getLocalVector([1,0,0]);\\r\\n\\tvar top = camera.getLocalVector([0,1,0]);\\r\\n\\tvar temp = vec3.create();\\r\\n\\tvar size = this.particle_size;\\r\\n\\r\\n\\tvar topleft = vec3.fromValues(-1,0,-1);\\r\\n\\tvar topright = vec3.fromValues(1,0,-1);\\r\\n\\tvar bottomleft = vec3.fromValues(-1,0,1);\\r\\n\\tvar bottomright = vec3.fromValues(1,0,1);\\r\\n\\r\\n\\tif(this.align_with_camera)\\r\\n\\t{\\r\\n\\t\\tvec3.subtract(topleft, top,right);\\r\\n\\t\\tvec3.add(topright, top,right);\\r\\n\\t\\tvec3.scale(bottomleft,topright,-1);\\r\\n\\t\\tvec3.scale(bottomright,topleft,-1);\\r\\n\\t}\\r\\n\\r\\n\\t//scaled versions\\r\\n\\tvar s_topleft = vec3.create()\\r\\n\\tvar s_topright = vec3.create()\\r\\n\\tvar s_bottomleft = vec3.create()\\r\\n\\tvar s_bottomright = vec3.create()\\r\\n\\r\\n\\tvar particles = this._particles;\\r\\n\\tif(this.sort_in_z)\\r\\n\\t{\\r\\n\\t\\tparticles = this._particles.concat(); //copy\\r\\n\\t\\tvar plane = geo.createPlane(center, front); //compute camera plane\\r\\n\\t\\tvar den = 1 / Math.sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); //delta\\r\\n\\t\\tfor(var i = 0; i < particles.length; ++i)\\r\\n\\t\\t\\tparticles[i]._dist = Math.abs(vec3.dot(particles[i]._pos,plane) + plane[3]) * den;\\r\\n\\t\\t\\t//particles[i]._dist = vec3.dist( center, particles[i].pos );\\r\\n\\t\\tparticles.sort(function(a,b) { return a._dist < b._dist ? 1 : (a._dist > b._dist ? -1 : 0); });\\r\\n\\t\\tthis._particles = particles;\\r\\n\\t}\\r\\n\\r\\n\\t//avoid errors\\r\\n\\tif(this.particle_life == 0)\\r\\n\\t\\tthis.particle_life = 0.0001;\\r\\n\\r\\n\\tvar color = new Float32Array([1,1,1,1]);\\r\\n\\tvar particle_start_color = this._particle_start_color;\\r\\n\\tvar particle_end_color = this._particle_end_color;\\r\\n\\r\\n\\t//used for grid based textures\\r\\n\\tvar recompute_coords = false;\\r\\n\\tif((this._computed_grid_size != this.texture_grid_size || this.texture_grid_size > 1) || this.point_particles)\\r\\n\\t{\\r\\n\\t\\trecompute_coords = true;\\r\\n\\t\\tthis._computed_grid_size = this.texture_grid_size;\\r\\n\\t}\\r\\n\\tvar texture_grid_size = this.texture_grid_size;\\r\\n\\tvar d_uvs = 1 / this.texture_grid_size;\\r\\n\\tvar offset_u = 0, offset_v = 0;\\r\\n\\tvar grid_frames = this.texture_grid_size<<2;\\r\\n\\tvar animated_texture = this.animated_texture;\\r\\n\\tvar loop_animation = this.loop_animation;\\r\\n\\tvar time = this._root.scene.getTime() * this.animation_fps;\\r\\n\\r\\n\\t//used for precompute curves to speed up (sampled at 60 frames per second)\\r\\n\\tvar recompute_colors = true;\\r\\n\\tvar opacity_curve = new Float32Array((this.particle_life * 60)<<0);\\r\\n\\tvar size_curve = new Float32Array((this.particle_life * 60)<<0);\\r\\n\\tvar particle_size = this.particle_size;\\r\\n\\r\\n\\tvar dI = 1 / (this.particle_life * 60);\\r\\n\\tfor(var i = 0; i < opacity_curve.length; i += 1)\\r\\n\\t{\\r\\n\\t\\topacity_curve[i] = LS.getCurveValueAt(this.particle_opacity_curve,0,1,0, i * dI );\\r\\n\\t\\tsize_curve[i] = LS.getCurveValueAt(this.particle_size_curve,0,1,0, i * dI );\\r\\n\\t}\\r\\n\\r\\n\\t//references\\r\\n\\tvar points = this.point_particles;\\r\\n\\tvar max_vertices = this._vertices.length;\\r\\n\\tvar vertices = this._vertices;\\r\\n\\tvar colors = this._colors;\\r\\n\\tvar extra2 = this._extra2;\\r\\n\\tvar coords = this._coords;\\r\\n\\r\\n\\t//used for rotations\\r\\n\\tvar rot = ParticleEmissor._tmp_quat;\\r\\n\\r\\n\\t//generate quads\\r\\n\\tvar i = 0, f = 0;\\r\\n\\tfor( var iParticle = 0, l = particles.length; iParticle < l; ++iParticle )\\r\\n\\t{\\r\\n\\t\\tvar p = particles[iParticle];\\r\\n\\t\\tif(p.life <= 0)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tf = 1.0 - p.life / this.particle_life;\\r\\n\\r\\n\\t\\tif(recompute_colors) //compute color and opacity\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar a = opacity_curve[(f*opacity_curve.length)<<0]; //getCurveValueAt(this.particle_opacity_curve,0,1,0,f);\\r\\n\\r\\n\\t\\t\\tif(this.independent_color && p.c)\\r\\n\\t\\t\\t\\tvec3.clone(color,p.c);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tvec3.lerp(color, particle_start_color, particle_end_color, f);\\r\\n\\r\\n\\t\\t\\tif(this.premultiplied_alpha)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvec3.scale(color,color,a);\\r\\n\\t\\t\\t\\tcolor[3] = 1.0;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tcolor[3] = a;\\r\\n\\r\\n\\t\\t\\tif(a < 0.001)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar s = p.size * size_curve[(f*size_curve.length)<<0]; //getCurveValueAt(this.particle_size_curve,0,1,0,f);\\r\\n\\r\\n\\t\\tif(Math.abs(s * particle_size) < MIN_SIZE)\\r\\n\\t\\t\\tcontinue; //ignore almost transparent particles\\r\\n\\r\\n\\t\\tif(points)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvertices.set(p._pos, i*3);\\r\\n\\t\\t\\tcolors.set(color, i*4);\\r\\n\\t\\t\\tif(recompute_coords)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar iG = (animated_texture ? ((loop_animation?time:f)*grid_frames)<<0 : p.id) % grid_frames;\\r\\n\\t\\t\\t\\toffset_u = iG * d_uvs;\\r\\n\\t\\t\\t\\toffset_v = 1 - (offset_u<<0) * d_uvs - d_uvs;\\r\\n\\t\\t\\t\\toffset_u = offset_u%1;\\r\\n\\t\\t\\t\\tcoords[i*2] = offset_u;\\r\\n\\t\\t\\t\\tcoords[i*2+1] = offset_v;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\textra2[i*2] = s;\\r\\n\\t\\t\\textra2[i*2+1] = i;\\r\\n\\t\\t\\t++i;\\r\\n\\t\\t\\tif(i*3 >= max_vertices)\\r\\n\\t\\t\\t\\tbreak; //too many particles\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\ts *= particle_size;\\r\\n\\r\\n\\t\\tvec3.scale(s_bottomleft, bottomleft, s)\\r\\n\\t\\tvec3.scale(s_topright, topright, s);\\r\\n\\t\\tvec3.scale(s_topleft, topleft, s);\\r\\n\\t\\tvec3.scale(s_bottomright, bottomright, s);\\r\\n\\r\\n\\t\\tif(p.angle != 0)\\r\\n\\t\\t{\\r\\n\\t\\t\\tquat.setAxisAngle( rot , front, p.angle * DEG2RAD);\\r\\n\\t\\t\\tvec3.transformQuat(s_bottomleft, s_bottomleft, rot);\\r\\n\\t\\t\\tvec3.transformQuat(s_topright, s_topright, rot);\\r\\n\\t\\t\\tvec3.transformQuat(s_topleft, s_topleft, rot);\\r\\n\\t\\t\\tvec3.transformQuat(s_bottomright, s_bottomright, rot);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvec3.add(temp, p._pos, s_topright);\\r\\n\\t\\tvertices.set(temp, i*6*3);\\r\\n\\r\\n\\t\\tvec3.add(temp, p._pos, s_topleft);\\r\\n\\t\\tvertices.set(temp, i*6*3 + 3);\\r\\n\\r\\n\\t\\tvec3.add(temp, p._pos, s_bottomright);\\r\\n\\t\\tvertices.set(temp, i*6*3 + 3*2);\\r\\n\\r\\n\\t\\tvec3.add(temp, p._pos, s_topleft);\\r\\n\\t\\tvertices.set(temp, i*6*3 + 3*3);\\r\\n\\r\\n\\t\\tvec3.add(temp, p._pos, s_bottomleft);\\r\\n\\t\\tvertices.set(temp, i*6*3 + 3*4);\\r\\n\\r\\n\\t\\tvec3.add(temp, p._pos, s_bottomright);\\r\\n\\t\\tvertices.set(temp, i*6*3 + 3*5);\\r\\n\\r\\n\\t\\tif(recompute_colors)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcolors.set(color, i*6*4);\\r\\n\\t\\t\\tcolors.set(color, i*6*4 + 4);\\r\\n\\t\\t\\tcolors.set(color, i*6*4 + 4*2);\\r\\n\\t\\t\\tcolors.set(color, i*6*4 + 4*3);\\r\\n\\t\\t\\tcolors.set(color, i*6*4 + 4*4);\\r\\n\\t\\t\\tcolors.set(color, i*6*4 + 4*5);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(recompute_coords)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar iG = (animated_texture ? ((loop_animation?time:f)*grid_frames)<<0 : p.id) % grid_frames;\\r\\n\\t\\t\\toffset_u = iG * d_uvs;\\r\\n\\t\\t\\toffset_v = 1 - (offset_u<<0) * d_uvs - d_uvs;\\r\\n\\t\\t\\toffset_u = offset_u%1;\\r\\n\\t\\t\\tcoords.set([offset_u+d_uvs,offset_v+d_uvs, offset_u,offset_v+d_uvs, offset_u+d_uvs,offset_v, offset_u,offset_v+d_uvs, offset_u,offset_v, offset_u+d_uvs,offset_v], i*6*2);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t++i;\\r\\n\\t\\tif(i*6*3 >= max_vertices)\\r\\n\\t\\t\\tbreak; //too many particles\\r\\n\\t}\\r\\n\\tthis._visible_particles = i;\\r\\n\\r\\n\\t//upload geometry\\r\\n\\tthis._mesh.vertexBuffers[\\\"vertices\\\"].data = this._vertices;\\r\\n\\tthis._mesh.vertexBuffers[\\\"vertices\\\"].upload(gl.STREAM_DRAW);\\r\\n\\r\\n\\tthis._mesh.vertexBuffers[\\\"colors\\\"].data = this._colors;\\r\\n\\tthis._mesh.vertexBuffers[\\\"colors\\\"].upload(gl.STREAM_DRAW);\\r\\n\\r\\n\\tthis._mesh.vertexBuffers[\\\"extra2\\\"].data = this._extra2;\\r\\n\\tthis._mesh.vertexBuffers[\\\"extra2\\\"].upload(gl.STREAM_DRAW);\\r\\n\\r\\n\\tif(recompute_coords)\\r\\n\\t{\\r\\n\\t\\tthis._mesh.vertexBuffers[\\\"coords\\\"].data = this._coords;\\r\\n\\t\\tthis._mesh.vertexBuffers[\\\"coords\\\"].upload(gl.STREAM_DRAW);\\r\\n\\t}\\r\\n}\\r\\n\\r\\nParticleEmissor._identity = mat4.create();\\r\\n\\r\\n//ParticleEmissor.prototype.getRenderInstance = function(options,camera)\\r\\nParticleEmissor.prototype.onCollectInstances = function(e, instances, options)\\r\\n{\\r\\n\\tif(!this._root || !this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar camera = LS.Renderer._current_camera;\\r\\n\\r\\n\\tif(!this._material)\\r\\n\\t\\tthis._material = new LS.StandardMaterial({ shader_name:\\\"lowglobal\\\" });\\r\\n\\r\\n\\tthis._material.opacity = this.opacity - 0.01; //try to keep it under 1\\r\\n\\tthis._material.setTexture( \\\"color\\\", this.texture );\\r\\n\\tthis._material.blend_mode = this.additive_blending ? LS.Blend.ADD : LS.Blend.ALPHA;\\r\\n\\tthis._material.constant_diffuse = true;\\r\\n\\tthis._material.uvs_matrix[0] = this._material.uvs_matrix[4] = 1 / this.texture_grid_size;\\r\\n\\tthis._material.flags.depth_write = false;\\r\\n\\tthis._material.flags.ignore_lights = this.ignore_lights;\\r\\n\\r\\n\\tif(!this._mesh)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar RI = this._render_instance;\\r\\n\\tif(!RI)\\r\\n\\t\\tthis._render_instance = RI = new LS.RenderInstance(this._root, this);\\r\\n\\r\\n\\tif(this.follow_emitter)\\r\\n\\t\\tmat4.translate( RI.matrix, ParticleEmissor._identity, this._root.transform._position );\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tmat4.copy( RI.matrix, ParticleEmissor._identity );\\r\\n\\t\\tif(this._root.transform)\\r\\n\\t\\t\\tthis._root.transform.getGlobalPosition( RI.center );\\r\\n\\t}\\r\\n\\r\\n\\tvar material = (this._root.material && this.use_node_material) ? this._root.getMaterial() : this._material;\\r\\n\\tmat4.multiplyVec3(RI.center, RI.matrix, vec3.create());\\r\\n\\r\\n\\tRI.setMaterial( material );\\r\\n\\r\\n\\tif(this.point_particles)\\r\\n\\t{\\r\\n\\t\\tRI.setMesh( this._mesh, gl.POINTS );\\r\\n\\t\\tRI.uniforms.u_point_size = this.particle_size;\\r\\n\\t\\tRI.setRange(0, this._visible_particles);\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tRI.setMesh( this._mesh, gl.TRIANGLES );\\r\\n\\t\\tRI.setRange(0, this._visible_particles * 6); //6 vertex per particle\\r\\n\\t\\tdelete RI.uniforms[\\\"u_point_size\\\"];\\r\\n\\t}\\r\\n\\r\\n\\tRI.use_bounding = false; //bounding is not valid\\r\\n\\tinstances.push( RI );\\r\\n}\\r\\n\\r\\nLS.Particle = Particle;\\r\\nLS.registerComponent(ParticleEmissor);\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n//shader\\r\\n// - apply light per vertex before expanding\\r\\n// - inflate with camera vectors\\r\\n\\r\\n///@FILE:../src/components/label.js\\r\\n///@INFO: UNCOMMON\\r\\nfunction Label(o)\\r\\n{\\r\\n\\tthis.text = \\\"\\\";\\r\\n\\tthis.className = \\\"\\\";\\r\\n\\tthis.use_html = false;\\r\\n\\tthis._world_pos = vec3.create();\\r\\n\\tthis._screen_pos = vec3.create();\\r\\n\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nLabel.icon = \\\"mini-icon-text.png\\\";\\r\\nLabel.CSS_classname = \\\"LS3D_label\\\";\\r\\n\\r\\nLabel.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\t//events\\r\\n\\tLEvent.bind( scene,\\\"renderGUI\\\",this.render,this);\\r\\n}\\r\\n\\r\\nLabel.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind( scene, \\\"renderGUI\\\", this.render, this);\\r\\n\\tthis.removeHTML();\\r\\n}\\r\\n\\r\\nLabel.prototype.createHTML = function()\\r\\n{\\r\\n\\t//create html\\r\\n\\tvar elem = document.createElement(\\\"div\\\");\\r\\n\\telem.innerHTML = this.text;\\r\\n\\tvar style = elem.style;\\r\\n\\tstyle.className = this.constructor.CSS_classname;\\r\\n\\tstyle.position = \\\"absolute\\\";\\r\\n\\tstyle.top = 0;\\r\\n\\tstyle.left = 0;\\r\\n\\tstyle.fontSize = \\\"20px\\\";\\r\\n\\tstyle.padding = \\\"10px\\\";\\r\\n\\tstyle.color = \\\"white\\\";\\r\\n\\tstyle.minWidth = \\\"100px\\\";\\r\\n\\tstyle.pointerEvents = \\\"none\\\";\\r\\n\\tstyle.backgroundColor = \\\"rgba(0,0,0,0.5)\\\";\\r\\n\\tstyle.borderRadius = \\\"2px\\\";\\r\\n\\r\\n\\tvar parent = LS.getGUIElement();\\r\\n\\tparent.appendChild( elem );\\r\\n\\r\\n\\tthis._element = elem;\\r\\n}\\r\\n\\r\\nLabel.prototype.removeHTML = function()\\r\\n{\\r\\n\\tif(!this._element)\\r\\n\\t\\treturn;\\r\\n\\tif(this._element.parentNode)\\r\\n\\t\\tthis._element.parentNode.removeChild( this._element );\\r\\n\\tthis._element = null;\\r\\n}\\r\\n\\r\\nLabel.prototype.render = function(e, render_settings)\\r\\n{\\r\\n\\tvar node = this._root;\\r\\n\\r\\n\\tvar camera = LS.Renderer._main_camera;\\r\\n\\tif(!camera)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tnode.transform.getGlobalPosition(this._world_pos);\\r\\n\\tcamera.project(this._world_pos, null, this._screen_pos );\\r\\n\\r\\n\\tif(this.use_html)\\r\\n\\t{\\r\\n\\t\\tif(!this._element)\\r\\n\\t\\t\\tthis.createHTML();\\r\\n\\r\\n\\t\\tif(this._element.innerHTML != this.text)\\r\\n\\t\\t\\tthis._element.innerHTML = this.text;\\r\\n\\r\\n\\t\\tthis._element.style.display = node.flags.visible === false ? \\\"none\\\" : \\\"block\\\";\\r\\n\\t\\tif(!this.text)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._element.style.display = \\\"none\\\";\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar classname = this.constructor.CSS_classname + \\\" \\\" + this.className;\\r\\n\\t\\tif(this._element.className != classname)\\r\\n\\t\\t\\tthis._element.className = classname;\\r\\n\\r\\n\\t\\tthis._element.style.left = this._screen_pos[0].toFixed(0) + \\\"px\\\";\\r\\n\\t\\tthis._element.style.top = (gl.canvas.height - (this._screen_pos[1]|0) - 10) + \\\"px\\\";\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tif(this._element)\\r\\n\\t\\t\\tthis.removeHTML();\\r\\n\\r\\n\\t\\tif(gl.start2D)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar x = this._screen_pos[0] + 5;\\r\\n\\t\\t\\tvar y = gl.canvas.height - this._screen_pos[1] + 10;\\r\\n\\t\\t\\tgl.start2D();\\r\\n\\t\\t\\tgl.font = \\\"20px Arial\\\";\\r\\n\\t\\t\\tvar info = gl.measureText( this.text );\\r\\n\\t\\t\\tgl.fillStyle = \\\"black\\\";\\r\\n\\t\\t\\tgl.globalAlpha = 0.5;\\r\\n\\t\\t\\tgl.fillRect( x - 5, y - 20, info.width + 10, 26 );\\r\\n\\t\\t\\tgl.globalAlpha = 1;\\r\\n\\t\\t\\tgl.fillStyle = \\\"white\\\";\\r\\n\\t\\t\\tgl.fillText( this.text, x, y );\\r\\n\\t\\t\\tgl.finish2D();\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nLS.registerComponent(Label);\\r\\n\\r\\n\\r\\n///@FILE:../src/components/pointCloud.js\\r\\n///@INFO: UNCOMMON\\r\\n/* pointCloud.js */\\r\\n\\r\\nfunction PointCloud(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.max_points = 1024;\\r\\n\\tthis.mesh = null; //use a mesh\\r\\n\\tthis._points = [];\\r\\n\\r\\n\\tthis.size = 1;\\r\\n\\tthis.texture_grid_size = 1;\\r\\n\\r\\n\\t//material\\r\\n\\tthis.texture = null;\\r\\n\\tthis.global_opacity = 1;\\r\\n\\tthis.color = vec3.fromValues(1,1,1);\\r\\n\\tthis.additive_blending = false;\\r\\n\\r\\n\\tthis.use_node_material = false; \\r\\n\\tthis.premultiplied_alpha = false;\\r\\n\\tthis.in_world_coordinates = false;\\r\\n\\tthis.sort_in_z = false; //slower\\r\\n\\r\\n\\tthis.serialize_points = true; //disable if the points shouldnt be stored (if they are generated by another component on runtime)\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n\\r\\n\\tthis._last_id = 0;\\r\\n\\r\\n\\t//debug\\r\\n\\t/*\\r\\n\\tfor(var i = 0; i < 100; i++)\\r\\n\\t{\\r\\n\\t\\tvar pos = vec3.create();\\r\\n\\t\\tvec3.random( pos );\\r\\n\\t\\tvec3.scale( pos, pos, 50 * Math.random() );\\r\\n\\t\\tthis.addPoint( pos, [Math.random(),1,1,1], 1 + Math.random() * 2);\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\tif(global.gl)\\r\\n\\t\\tthis.createMesh();\\r\\n\\r\\n}\\r\\nPointCloud.icon = \\\"mini-icon-points.png\\\";\\r\\nPointCloud[\\\"@texture\\\"] = { widget: \\\"texture\\\" };\\r\\nPointCloud[\\\"@color\\\"] = { widget: \\\"color\\\" };\\r\\nPointCloud[\\\"@num_points\\\"] = { widget: \\\"info\\\" };\\r\\nPointCloud[\\\"@size\\\"] = { type: \\\"number\\\", step: 0.001, precision: 3 };\\r\\n\\r\\nPointCloud.default_color = vec4.fromValues(1,1,1,1);\\r\\n\\r\\nObject.defineProperty( PointCloud.prototype, \\\"num_points\\\", {\\r\\n\\tset: function(v) {},\\r\\n\\tget: function() { return this._points.length; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nPointCloud.prototype.addPoint = function( position, color, size, frame_id )\\r\\n{\\r\\n\\tvar data = new Float32Array(3+4+2+1); //+1 extra por distance\\r\\n\\tdata.set(position,0);\\r\\n\\tif(color)\\r\\n\\t\\tdata.set(color,3);\\r\\n\\telse\\r\\n\\t\\tdata.set( PointCloud.default_color, 3 );\\r\\n\\tif(size !== undefined)\\r\\n\\t\\tdata[7] = size;\\r\\n\\telse\\r\\n\\t\\tdata[7] = 1;\\r\\n\\tif(frame_id != undefined )\\r\\n\\t\\tdata[8] = frame_id;\\r\\n\\telse\\r\\n\\t\\tdata[8] = 0;\\r\\n\\r\\n\\tthis._points.push( data );\\r\\n\\tthis._must_update = true;\\r\\n\\r\\n\\treturn this._points.length - 1;\\r\\n}\\r\\n\\r\\nPointCloud.prototype.clear = function()\\r\\n{\\r\\n\\tthis._points.length = 0;\\r\\n}\\r\\n\\r\\nPointCloud.prototype.reset = PointCloud.prototype.clear;\\r\\n\\r\\nPointCloud.prototype.setPoint = function(id, position, color, size, frame_id )\\r\\n{\\r\\n\\tvar data = this._points[id];\\r\\n\\tif(!data) return;\\r\\n\\r\\n\\tif(position)\\r\\n\\t\\tdata.set(position,0);\\r\\n\\tif(color)\\r\\n\\t\\tdata.set(color,3);\\r\\n\\tif(size !== undefined )\\r\\n\\t\\tdata[7] = size;\\r\\n\\tif(frame_id !== undefined )\\r\\n\\t\\tdata[8] = frame_id;\\r\\n\\r\\n\\tthis._must_update = true;\\r\\n}\\r\\n\\r\\nPointCloud.prototype.setPointsFromMesh = function( mesh )\\r\\n{\\r\\n\\t//TODO\\r\\n}\\r\\n\\r\\n\\r\\nPointCloud.prototype.removePoint = function(id)\\r\\n{\\r\\n\\tthis._points.splice(id,1);\\r\\n\\tthis._must_update = true;\\r\\n}\\r\\n\\r\\n\\r\\nPointCloud.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tLEvent.bind(node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nPointCloud.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tLEvent.unbind(node, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n}\\r\\n\\r\\nPointCloud.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(this.mesh) res[ this.emissor_mesh ] = Mesh;\\r\\n\\tif(this.texture) res[ this.texture ] = Texture;\\r\\n}\\r\\n\\r\\nPointCloud.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.mesh == old_name)\\r\\n\\t\\tthis.mesh = new_name;\\r\\n\\tif(this.texture == old_name)\\r\\n\\t\\tthis.texture = new_name;\\r\\n}\\r\\n\\r\\nPointCloud.prototype.createMesh = function ()\\r\\n{\\r\\n\\tvar max_points = this.max_points|0;\\r\\n\\tif( this._mesh_max_points == max_points)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._vertices = new Float32Array(max_points * 3); \\r\\n\\tthis._colors = new Float32Array(max_points * 4);\\r\\n\\tthis._extra2 = new Float32Array(max_points * 2); //size and texture frame\\r\\n\\r\\n\\tvar default_size = 1;\\r\\n\\tfor(var i = 0; i < max_points; i++)\\r\\n\\t{\\r\\n\\t\\tthis._colors.set( PointCloud.default_color, i*4);\\r\\n\\t\\tthis._extra2[i*2] = default_size;\\r\\n\\t\\t//this._extra2[i*2+1] = 0;\\r\\n\\t}\\r\\n\\r\\n\\tif(this._mesh)\\r\\n\\t\\tthis._mesh.deleteBuffers();\\r\\n\\r\\n\\tthis._mesh = new GL.Mesh();\\r\\n\\tthis._mesh.addBuffers({ vertices:this._vertices, colors: this._colors, extra2: this._extra2 }, null, gl.STREAM_DRAW);\\r\\n\\tthis._mesh_max_points = max_points;\\r\\n}\\r\\n\\r\\nPointCloud.prototype.updateMesh = function ( camera )\\r\\n{\\r\\n\\tvar max_points = this.max_points|0;\\r\\n\\tif( this._mesh_max_points != max_points) \\r\\n\\t\\tthis.createMesh();\\r\\n\\r\\n\\tvar points = this._points;\\r\\n\\tif(this.sort_in_z && camera)\\r\\n\\t{\\r\\n\\t\\tvar center = camera.getEye(); \\r\\n\\t\\tvar front = camera.getFront();\\r\\n\\r\\n\\t\\tpoints = this._points.concat(); //copy array\\r\\n\\t\\tvar plane = geo.createPlane(center, front); //compute camera plane\\r\\n\\t\\tvar den = Math.sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); //delta\\r\\n\\t\\tfor(var i = 0; i < points.length; ++i)\\r\\n\\t\\t\\tpoints[i][9] = Math.abs(vec3.dot(points[i].subarray(0,3),plane) + plane[3])/den;\\r\\n\\r\\n\\t\\tpoints.sort(function(a,b) { return a[9] < b[9] ? 1 : (a[9] > b[9] ? -1 : 0); });\\r\\n\\t}\\r\\n\\r\\n\\t//update mesh\\r\\n\\tvar i = 0, f = 0;\\r\\n\\tvar vertices = this._vertices;\\r\\n\\tvar colors = this._colors;\\r\\n\\tvar extra2 = this._extra2;\\r\\n\\tvar premultiply = this.premultiplied_alpha;\\r\\n\\r\\n\\tfor(var iPoint = 0; iPoint < points.length; ++iPoint)\\r\\n\\t{\\r\\n\\t\\tif( iPoint*3 >= vertices.length)\\r\\n\\t\\t\\tbreak; //too many points\\r\\n\\t\\tvar p = points[iPoint];\\r\\n\\r\\n\\t\\tvertices.set(p.subarray(0,3), iPoint * 3);\\r\\n\\t\\tvar c = p.subarray(3,7);\\r\\n\\t\\tif(premultiply)\\r\\n\\t\\t\\tvec3.scale(c,c,c[3]);\\r\\n\\t\\tcolors.set(c, iPoint * 4);\\r\\n\\t\\textra2.set(p.subarray(7,9), iPoint * 2);\\r\\n\\t}\\r\\n\\r\\n\\t//upload geometry\\r\\n\\tthis._mesh.vertexBuffers[\\\"vertices\\\"].data = vertices;\\r\\n\\tthis._mesh.vertexBuffers[\\\"vertices\\\"].upload();\\r\\n\\r\\n\\tthis._mesh.vertexBuffers[\\\"colors\\\"].data = colors;\\r\\n\\tthis._mesh.vertexBuffers[\\\"colors\\\"].upload();\\r\\n\\r\\n\\tthis._mesh.vertexBuffers[\\\"extra2\\\"].data = extra2;\\r\\n\\tthis._mesh.vertexBuffers[\\\"extra2\\\"].upload();\\r\\n}\\r\\n\\r\\nPointCloud._identity = mat4.create();\\r\\n\\r\\nPointCloud.prototype.onCollectInstances = function(e, instances, options)\\r\\n{\\r\\n\\tif(!this._root) return;\\r\\n\\r\\n\\tif(this._points.length == 0 || !this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar camera = LS.Renderer._current_camera;\\r\\n\\r\\n\\tif(this._last_premultiply !== this.premultiplied_alpha )\\r\\n\\t\\tthis._must_update = true;\\r\\n\\r\\n\\tif(this._must_update)\\r\\n\\t\\tthis.updateMesh( camera );\\r\\n\\r\\n\\tif(!this._material)\\r\\n\\t{\\r\\n\\t\\tthis._material = new LS.StandardMaterial({});\\r\\n\\t\\tthis._material.extra_macros = { \\r\\n\\t\\t\\tUSE_POINT_CLOUD: \\\"\\\", //for the stream with sizes\\r\\n\\t\\t\\tUSE_TEXTURED_POINTS: \\\"\\\" //for texturing the points\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\tvar material = this._material;\\r\\n\\r\\n\\tmaterial.color.set(this.color);\\r\\n\\r\\n\\tif(this.premultiplied_alpha)\\r\\n\\t\\tmaterial.opacity = 1.0 - 0.01;\\r\\n\\telse\\r\\n\\t\\tmaterial.opacity = this.global_opacity - 0.01;\\r\\n\\tthis._last_premultiply = this.premultiplied_alpha;\\r\\n\\r\\n\\tmaterial.setTexture( LS.Material.COLOR, this.texture );\\r\\n\\tmaterial.blend_mode = this.additive_blending ? LS.Blend.ADD : LS.Blend.ALPHA;\\r\\n\\tmaterial.constant_diffuse = true;\\r\\n\\r\\n\\tif(!this._mesh)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar RI = this._render_instance;\\r\\n\\tif(!RI)\\r\\n\\t\\tthis._render_instance = RI = new LS.RenderInstance(this._root, this);\\r\\n\\r\\n\\tif(this.in_world_coordinates && this._root.transform )\\r\\n\\t\\tRI.matrix.set( this._root.transform._global_matrix );\\r\\n\\telse\\r\\n\\t\\tmat4.copy( RI.matrix, PointCloud._identity );\\r\\n\\r\\n\\t/*\\r\\n\\tif(this.follow_emitter)\\r\\n\\t\\tmat4.translate( RI.matrix, PointCloud._identity, this._root.transform._position );\\r\\n\\telse\\r\\n\\t\\tmat4.copy( RI.matrix, PointCloud._identity );\\r\\n\\t*/\\r\\n\\r\\n\\tvar material = (this._root.material && this.use_node_material) ? this._root.getMaterial() : this._material;\\r\\n\\tmat4.multiplyVec3(RI.center, RI.matrix, vec3.create());\\r\\n\\r\\n\\tRI.uniforms.u_point_size = this.size;\\r\\n\\r\\n\\tRI.setMaterial( material );\\r\\n\\tRI.setMesh( this._mesh, gl.POINTS );\\r\\n\\tvar primitives = this._points.length;\\r\\n\\tif(primitives > this._vertices.length / 3)\\r\\n\\t\\tprimitives = this._vertices.length / 3;\\r\\n\\r\\n\\tRI.setRange(0, primitives );\\r\\n\\tinstances.push(RI);\\r\\n}\\r\\n\\r\\nPointCloud.prototype.serialize = function()\\r\\n{\\r\\n\\tvar o = LS.cloneObject(this);\\r\\n\\to.object_class = \\\"PointCloud\\\";\\r\\n\\r\\n\\tif(this.uid) //special case, not enumerable\\r\\n\\t\\to.uid = this.uid;\\r\\n\\tif(this.serialize_points)\\r\\n\\t{\\r\\n\\t\\tvar points = Array(this._points.length);\\r\\n\\t\\tfor(var i = 0; i < this._points.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tpoints[i] = typedArrayToArray( this._points[i] );\\r\\n\\t\\t}\\r\\n\\t\\to.points = points;\\r\\n\\t}\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\nPointCloud.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(!o)\\r\\n\\t\\treturn;\\r\\n\\tif(o.uid) \\r\\n\\t\\tthis.uid = o.uid;\\r\\n\\r\\n\\tif(o.points && o.serialize_points)\\r\\n\\t{\\r\\n\\t\\tthis._points = Array( o.points.length );\\r\\n\\t\\tfor(var i = 0; i < o.points.length; i++)\\r\\n\\t\\t\\tthis._points[i] = new Float32Array( o.points[i] );\\r\\n\\t\\to.points = null;\\r\\n\\t}\\r\\n\\tLS.cloneObject( o, this );\\r\\n}\\r\\n\\r\\n\\r\\nLS.registerComponent( PointCloud );\\r\\n///@FILE:../src/components/linesRenderer.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Helps rendering several lines\\r\\n* @class LinesRenderer\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\nfunction LinesRenderer(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.max_lines = 1024;\\r\\n\\tthis._lines = [];\\r\\n\\r\\n\\t//material\\r\\n\\tthis.global_opacity = 1;\\r\\n\\tthis.color = vec3.fromValues(1,1,1);\\r\\n\\tthis.use_node_material = false; \\r\\n\\tthis.in_world_coordinates = false;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n\\r\\n\\tthis._last_id = 0;\\r\\n\\r\\n\\tif(global.gl)\\r\\n\\t\\tthis.createMesh();\\r\\n\\r\\n\\t/*\\r\\n\\tfor(var i = 0; i < 2;i++)\\r\\n\\t{\\r\\n\\t\\tvar pos = vec3.random(vec3.create());\\r\\n\\t\\tvec3.scale(pos, pos, 100);\\r\\n\\t\\tthis.addLine( [0,0,0], pos );\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n}\\r\\nLinesRenderer.icon = \\\"mini-icon-lines.png\\\";\\r\\nLinesRenderer[\\\"@color\\\"] = { widget: \\\"color\\\" };\\r\\nLinesRenderer[\\\"@lines\\\"] = { widget: \\\"null\\\" };\\r\\n\\r\\nObject.defineProperty( LinesRenderer.prototype, \\\"lines\\\", {\\r\\n\\tset: function(v) { this._lines = v; },\\r\\n\\tget: function() { return this._lines; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( LinesRenderer.prototype, \\\"num_lines\\\", {\\r\\n\\tset: function(v) {},\\r\\n\\tget: function() { return this._lines.length; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nLinesRenderer.prototype.clear = function()\\r\\n{\\r\\n\\tthis._lines.length = 0;\\r\\n}\\r\\n\\r\\nLinesRenderer.prototype.reset = LinesRenderer.prototype.clear;\\r\\n\\r\\n//Adds a point connect to the last one\\r\\nLinesRenderer.prototype.addPoint = function( point, color )\\r\\n{\\r\\n\\t//last\\r\\n\\tvar start = null;\\r\\n\\tvar start_color = null;\\r\\n\\tif(this._lines.length)\\r\\n\\t{\\r\\n\\t\\tvar last = this._lines[ this._lines.length - 1 ];\\r\\n\\t\\tstart = new Float32Array( last.subarray(3,6) );\\r\\n\\t\\tstart_color = new Float32Array( last.subarray(10,14) );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tstart = point;\\r\\n\\t\\tstart_color = color;\\r\\n\\t}\\r\\n\\tthis.addLine( start, point, start_color, color );\\r\\n}\\r\\n\\r\\nLinesRenderer.prototype.addLine = function( start, end, start_color, end_color )\\r\\n{\\r\\n\\tvar data = new Float32Array(3+3+4+4);\\r\\n\\tdata.set(start,0);\\r\\n\\tdata.set(end,3);\\r\\n\\r\\n\\tif(start_color)\\r\\n\\t\\tdata.set(start_color,6);\\r\\n\\telse\\r\\n\\t\\tdata.set([1,1,1,1],6);\\r\\n\\r\\n\\tif(end_color)\\r\\n\\t\\tdata.set(end_color,10);\\r\\n\\telse if(start_color)\\r\\n\\t\\tdata.set(start_color,10);\\r\\n\\telse\\r\\n\\t\\tdata.set([1,1,1,1],10);\\r\\n\\r\\n\\tthis._lines.push( data );\\r\\n\\tthis._must_update = true;\\r\\n\\r\\n\\treturn this._lines.length - 1;\\r\\n}\\r\\n\\r\\nLinesRenderer.prototype.setLine = function(id, start, end, start_color, end_color )\\r\\n{\\r\\n\\tvar data = this._lines[id];\\r\\n\\r\\n\\tif(start)\\r\\n\\t\\tdata.set(start,0);\\r\\n\\tif(end)\\r\\n\\t\\tdata.set(end,3);\\r\\n\\r\\n\\tif(start_color)\\r\\n\\t\\tdata.set(start_color,6);\\r\\n\\tif(end_color)\\r\\n\\t\\tdata.set(end_color,10);\\r\\n\\r\\n\\tthis._must_update = true;\\r\\n}\\r\\n\\r\\nLinesRenderer.prototype.removeLine = function(id)\\r\\n{\\r\\n\\tthis._lines.splice(id,1);\\r\\n\\tthis._must_update = true;\\r\\n}\\r\\n\\r\\n\\r\\nLinesRenderer.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"afterRenderScene\\\", this.onAfterRender, this);\\r\\n}\\r\\n\\r\\nLinesRenderer.prototype.onRemovedFromScene = function( scene )\\r\\n{\\r\\n\\tLEvent.unbind( scene, \\\"afterRenderScene\\\", this.onAfterRender, this);\\r\\n}\\r\\n\\r\\nLinesRenderer.prototype.createMesh = function ()\\r\\n{\\r\\n\\tif( this._mesh_max_lines == this.max_lines) return;\\r\\n\\r\\n\\tthis._vertices = new Float32Array(this.max_lines * 3 * 2); \\r\\n\\tthis._colors = new Float32Array(this.max_lines * 4 * 2);\\r\\n\\r\\n\\tthis._mesh = new GL.Mesh();\\r\\n\\tthis._mesh.addBuffers({ vertices:this._vertices, colors: this._colors }, null, gl.STREAM_DRAW);\\r\\n\\tthis._mesh_max_lines = this.max_lines;\\r\\n}\\r\\n\\r\\nLinesRenderer.prototype.updateMesh = function ()\\r\\n{\\r\\n\\tif( this._mesh_max_lines != this.max_lines)\\r\\n\\t\\tthis.createMesh();\\r\\n\\r\\n\\t//update mesh\\r\\n\\tvar i = 0, f = 0;\\r\\n\\tvar vertices = this._vertices;\\r\\n\\tvar colors = this._colors;\\r\\n\\r\\n\\tvar lines = this._lines;\\r\\n\\tvar l = this._lines.length;\\r\\n\\tvar vl = vertices.length;\\r\\n\\r\\n\\tfor(var i = 0; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tif( i*6 >= vl)\\r\\n\\t\\t\\tbreak; //too many lines\\r\\n\\t\\tvar p = lines[i];\\r\\n\\r\\n\\t\\tvertices.set(p.subarray(0,6), i * 6);\\r\\n\\t\\tcolors.set(p.subarray(6,14), i * 8);\\r\\n\\t}\\r\\n\\r\\n\\t//upload geometry\\r\\n\\tthis._mesh.vertexBuffers[\\\"vertices\\\"].data = vertices;\\r\\n\\tthis._mesh.vertexBuffers[\\\"vertices\\\"].upload();\\r\\n\\r\\n\\tthis._mesh.vertexBuffers[\\\"colors\\\"].data = colors;\\r\\n\\tthis._mesh.vertexBuffers[\\\"colors\\\"].upload();\\r\\n}\\r\\n\\r\\nLinesRenderer.prototype.onAfterRender = function(e)\\r\\n{\\r\\n\\tif( !this._root )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( this._lines.length == 0 || !this.enabled )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( this._must_update )\\r\\n\\t\\tthis.updateMesh();\\r\\n\\r\\n\\tLS.Draw.renderMesh( this._mesh, GL.LINES, null, null, 0, this._lines.length * 2 );\\r\\n}\\r\\n\\r\\n\\r\\nLS.registerComponent( LinesRenderer );\\r\\n///@FILE:../src/components/playAnimation.js\\r\\n///@INFO: ANIMATION\\r\\n/**\\r\\n* Reads animation tracks from an LS.Animation resource and applies the properties to the objects referenced\\r\\n* @class PlayAnimation\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\n\\r\\n\\r\\nfunction PlayAnimation(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\r\\n\\tthis._animation = \\\"\\\";\\r\\n\\tthis._take = \\\"default\\\";\\r\\n\\r\\n\\t/**\\r\\n\\t* the root node locator where to apply the animation, is none is specified it is applied using the scene root node\\r\\n\\t* if a \\\"@\\\" is set, then only to this node and its children\\r\\n\\t* @property root_node {String}\\r\\n\\t*/\\r\\n\\tthis.root_node = \\\"@\\\";\\r\\n\\tthis.playback_speed = 1.0;\\r\\n\\r\\n\\t/**\\r\\n\\t* how to play the animation, options are:\\r\\n * PlayAnimation.LOOP\\r\\n\\t*\\tPlayAnimation.PINGPONG\\r\\n\\t*\\tPlayAnimation.ONCE\\r\\n\\t*\\tPlayAnimation.PAUSED\\r\\n\\t* @property mode {Number}\\r\\n\\t*/\\r\\n\\tthis.mode = PlayAnimation.LOOP;\\r\\n\\tthis.playing = true;\\r\\n\\tthis.current_time = 0;\\r\\n\\tthis.blend_time = 0;\\r\\n\\tthis.range = null;\\r\\n\\r\\n\\t//coding callbacks\\r\\n\\tthis.onPreApply = null; //configurable callback to check if this track must be applied\\r\\n\\tthis.onApplySample = null; //configurable callback to check if this sample must be applied\\r\\n\\r\\n\\tthis._last_time = 0;\\r\\n\\r\\n\\tthis._use_blend_animation = false;\\r\\n\\tthis._blend_animation = null;\\r\\n\\tthis._blend_take = null;\\r\\n\\tthis._blend_current_time = 0;\\r\\n\\tthis._blend_remaining_time = 0;\\r\\n\\tthis._blend_updated_frame = -1; //used to avoid reseting time when animation and track are changed continuously\\r\\n\\r\\n\\tthis.disabled_tracks = {};\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nPlayAnimation.LOOP = 1;\\r\\nPlayAnimation.PINGPONG = 2;\\r\\nPlayAnimation.ONCE = 3;\\r\\nPlayAnimation.PAUSED = 4;\\r\\n\\r\\nPlayAnimation.MODES = {\\\"loop\\\":PlayAnimation.LOOP, \\\"pingpong\\\":PlayAnimation.PINGPONG, \\\"once\\\":PlayAnimation.ONCE, \\\"paused\\\":PlayAnimation.PAUSED };\\r\\n\\r\\nPlayAnimation.properties_order = [\\\"animation\\\",\\\"take\\\"]; //this ones should be the first ones to show in the inspector\\r\\n\\r\\nPlayAnimation[\\\"@animation\\\"] = { widget: \\\"animation\\\" };\\r\\nPlayAnimation[\\\"@root_node\\\"] = { type: \\\"node_id\\\" };\\r\\nPlayAnimation[\\\"@mode\\\"] = { type:\\\"enum\\\", values: PlayAnimation.MODES };\\r\\nPlayAnimation[\\\"@current_time\\\"] = { type: LS.TYPES.NUMBER, min: 0, units:\\\"s\\\" };\\r\\nPlayAnimation[\\\"@blend_time\\\"] = { type: LS.TYPES.NUMBER, min: 0, units:\\\"s\\\" };\\r\\nPlayAnimation[\\\"@take\\\"] = { type: \\\"enum\\\", values: function(){\\r\\n\\tvar anim = this.instance.getAnimation();\\r\\n\\tif(!anim)\\r\\n\\t\\treturn [\\\"default\\\"];\\r\\n\\tvar takes = anim.takes;\\r\\n\\tvar result = [];\\r\\n\\tfor(var i in takes)\\r\\n\\t\\tresult.push(i);\\r\\n\\treturn result;\\r\\n}};\\r\\n\\r\\n/**\\r\\n* the name of the LS.Animation resource where the takes and tracks are stored\\r\\n* @property animation {String}\\r\\n*/\\r\\nObject.defineProperty( PlayAnimation.prototype, \\\"animation\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tif(v == this._animation)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._blend_animation = this._animation;\\r\\n\\t\\tthis._animation = v;\\r\\n\\t\\tif(this.blend_time)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!this._root || !this._root.scene)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t\\tthis._use_blend_animation = true;\\r\\n\\t\\t\\tif( this._root.scene.frame != this._blend_updated_frame )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._blend_current_time = this.current_time;\\r\\n\\t\\t\\t\\tthis._blend_remaining_time = this.blend_time;\\r\\n\\t\\t\\t\\tthis._blend_updated_frame = this._root.scene.frame;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._animation;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* the name of the LS.Animation.Take to play from the LS.Animation\\r\\n* A take representes a set of tracks\\r\\n* @property take {String}\\r\\n*/\\r\\nObject.defineProperty( PlayAnimation.prototype, \\\"take\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tif(v == this._take)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._blend_take = this._take;\\r\\n\\t\\tthis._take = v;\\r\\n\\t\\tif(this.blend_time)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!this._root || !this._root.scene)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tthis._use_blend_animation = true;\\r\\n\\t\\t\\tif( this._root.scene.frame != this._blend_updated_frame )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._blend_current_time = this.current_time;\\r\\n\\t\\t\\t\\tthis._blend_remaining_time = this.blend_time;\\r\\n\\t\\t\\t\\tthis._blend_updated_frame = this._root.scene.frame;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._take;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nPlayAnimation.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(o.play) //LEGACY\\r\\n\\t\\tdelete o.play;\\r\\n\\r\\n\\tif(o.enabled !== undefined)\\r\\n\\t\\tthis.enabled = !!o.enabled;\\r\\n\\tif(o.range) \\r\\n\\t\\tthis.range = o.range.concat();\\r\\n\\tif(o.mode !== undefined) \\r\\n\\t\\tthis.mode = o.mode;\\r\\n\\tif(o.animation)\\r\\n\\t\\tthis._animation = o.animation;\\r\\n\\tif(o.take)\\r\\n\\t\\tthis._take = o.take;\\r\\n\\tif(o.playback_speed != null)\\r\\n\\t\\tthis.playback_speed = parseFloat( o.playback_speed );\\r\\n\\tif(o.root_node !== undefined)\\r\\n\\t\\tthis.root_node = o.root_node;\\r\\n\\tif(o.playing !== undefined)\\r\\n\\t\\tthis.playing = o.playing;\\r\\n\\tif(o.blend_time !== undefined)\\r\\n\\t\\tthis.blend_time = o.blend_time;\\r\\n}\\r\\n\\r\\nPlayAnimation.icon = \\\"mini-icon-clock.png\\\";\\r\\n\\r\\nPlayAnimation.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"update\\\", this.onUpdate, this);\\r\\n}\\r\\n\\r\\nPlayAnimation.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind( scene, \\\"update\\\", this.onUpdate, this);\\r\\n}\\r\\n\\r\\nPlayAnimation.prototype.onUpdate = function(e, dt)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this.playing)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( this.mode != PlayAnimation.PAUSED )\\r\\n\\t\\tthis.current_time += dt * this.playback_speed;\\r\\n\\r\\n\\tthis.onUpdateAnimation( dt );\\r\\n\\r\\n\\tif( this._use_blend_animation )\\r\\n\\t\\tthis.onUpdateBlendAnimation( dt );\\r\\n}\\r\\n\\r\\nPlayAnimation.prototype.onUpdateAnimation = function(dt)\\r\\n{\\r\\n\\tvar animation = this.getAnimation();\\r\\n\\tif(!animation) \\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar take = animation.takes[ this.take ];\\r\\n\\tif(!take) \\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar time = this.current_time;\\r\\n\\r\\n\\tvar start_time = 0;\\r\\n\\tvar duration = take.duration;\\r\\n\\tvar end_time = duration;\\r\\n\\r\\n\\tif(this.range)\\r\\n\\t{\\r\\n\\t\\tstart_time = this.range[0];\\r\\n\\t\\tend_time = this.range[1];\\r\\n\\t\\tduration = end_time - start_time;\\r\\n\\t}\\r\\n\\r\\n\\tif(time > end_time)\\r\\n\\t{\\r\\n\\t\\tswitch( this.mode )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase PlayAnimation.ONCE: \\r\\n\\t\\t\\t\\ttime = end_time; \\r\\n\\t\\t\\t\\t//time = start_time; //reset after\\r\\n\\t\\t\\t\\tLEvent.trigger( this, \\\"end_animation\\\" );\\r\\n\\t\\t\\t\\tthis.playing = false;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase PlayAnimation.PINGPONG:\\r\\n\\t\\t\\t\\tif( ((time / duration)|0) % 2 == 0 ) //TEST THIS\\r\\n\\t\\t\\t\\t\\ttime = this.current_time % duration; \\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\ttime = duration - (this.current_time % duration);\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase PlayAnimation.PINGPONG:\\r\\n\\t\\t\\t\\ttime = end_time; \\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase PlayAnimation.LOOP: \\r\\n\\t\\t\\tdefault: \\r\\n\\t\\t\\t\\ttime = ((this.current_time - start_time) % duration) + start_time;\\r\\n\\t\\t\\t\\tLEvent.trigger( this, \\\"animation_loop\\\" );\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(time < start_time)\\r\\n\\t\\ttime = start_time;\\r\\n\\r\\n\\tthis.applyAnimation( take, time, this._last_time );\\r\\n\\r\\n\\tthis._last_time = time; //TODO, add support for pingpong events in tracks\\r\\n\\t//take.actionPerSample( this.current_time, this._processSample.bind( this ), { disabled_tracks: this.disabled_tracks } );\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(scene)\\r\\n\\t\\tscene.requestFrame();\\r\\n}\\r\\n\\r\\nPlayAnimation.prototype.onUpdateBlendAnimation = function( dt )\\r\\n{\\r\\n\\tvar animation = this.getAnimation( this._blend_animation || this._animation || \\\"\\\" );\\r\\n\\tif(!animation) \\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar take = animation.takes[ this._blend_take || this._take || \\\"default\\\" ];\\r\\n\\tif(!take) \\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._blend_current_time += dt;\\r\\n\\tthis._blend_remaining_time -= dt;\\r\\n\\r\\n\\tif( this._blend_remaining_time <= 0 )\\r\\n\\t\\tthis._use_blend_animation = false; //next frame it will stop\\r\\n\\r\\n\\tvar time = this._blend_current_time * this.playback_speed;\\r\\n\\r\\n\\tvar start_time = 0;\\r\\n\\tvar duration = take.duration;\\r\\n\\tvar end_time = duration;\\r\\n\\r\\n\\tif(this.range)\\r\\n\\t{\\r\\n\\t\\tstart_time = this.range[0];\\r\\n\\t\\tend_time = this.range[1];\\r\\n\\t\\tduration = end_time - start_time;\\r\\n\\t}\\r\\n\\r\\n\\tif(time > end_time)\\r\\n\\t{\\r\\n\\t\\tswitch( this.mode )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase PlayAnimation.ONCE: \\r\\n\\t\\t\\t\\ttime = end_time; \\r\\n\\t\\t\\t\\tthis._use_blend_animation = false;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase PlayAnimation.PINGPONG:\\r\\n\\t\\t\\t\\tif( ((time / duration)|0) % 2 == 0 ) //TEST THIS\\r\\n\\t\\t\\t\\t\\ttime = this._blend_current_time % duration; \\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\ttime = duration - (this._blend_current_time % duration);\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase PlayAnimation.PINGPONG:\\r\\n\\t\\t\\t\\ttime = end_time; \\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase PlayAnimation.LOOP: \\r\\n\\t\\t\\tdefault: \\r\\n\\t\\t\\t\\ttime = ((this._blend_current_time - start_time) % duration) + start_time;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(time < start_time)\\r\\n\\t\\ttime = start_time;\\r\\n\\r\\n\\tthis.applyAnimation( take, time, null, this._blend_remaining_time / this.blend_time );\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(scene)\\r\\n\\t\\tscene.requestFrame();\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the current animation or an animation with a given name\\r\\n* @method getAnimation\\r\\n* @param {String} name [optional] the name of the animation, if omited then uses the animation set in the component\\r\\n* @return {LS.Animation} the animation container\\r\\n*/\\r\\nPlayAnimation.prototype.getAnimation = function( name )\\r\\n{\\r\\n\\tname = name === undefined ? this.animation : name;\\r\\n\\r\\n\\tif(!name || name[0] == \\\"@\\\") \\r\\n\\t\\treturn this._root.scene.animation;\\r\\n\\tvar anim = LS.ResourcesManager.getResource( name );\\r\\n\\tif( anim && anim.constructor === LS.Animation )\\r\\n\\t\\treturn anim;\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the current animation or an animation with a given name\\r\\n* @method getTake\\r\\n* @param {String} take_name [optional] if not specified then it uses the current take\\r\\n* @return {Number} the duration of the take, or -1 if the take was not found or the animation is not loaded\\r\\n*/\\r\\nPlayAnimation.prototype.getTake = function( take_name )\\r\\n{\\r\\n\\tvar animation = this.getAnimation();\\r\\n\\tif(!animation) \\r\\n\\t\\treturn null;\\r\\n\\ttake_name = take_name || this.take;\\r\\n\\tvar take = animation.takes[ take_name ];\\r\\n\\tif(take) \\r\\n\\t\\treturn take;\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Gets the duration of the current take in the current animation\\r\\n* @method getDuration\\r\\n* @return {Number} the duration of the take, or -1 if the take was not found or the animation is not loaded\\r\\n*/\\r\\nPlayAnimation.prototype.getDuration = function()\\r\\n{\\r\\n\\tvar take = this.getTake();\\r\\n\\tif(take) \\r\\n\\t\\treturn take.duration;\\r\\n\\treturn -1;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Resets the time to zero and starts playing the current take of the animation\\r\\n* It also triggers a \\\"start_animation\\\" event\\r\\n* @method play\\r\\n*/\\r\\nPlayAnimation.prototype.play = function()\\r\\n{\\r\\n\\tif(!this._root || !this._root.scene)\\r\\n\\t\\tconsole.error(\\\"cannot play an animation if the component doesnt belong to a node in a scene\\\");\\r\\n\\r\\n\\tthis.playing = true;\\r\\n\\r\\n\\tthis.current_time = 0;\\r\\n\\tif(this.range)\\r\\n\\t\\tthis.current_time = this.range[0];\\r\\n\\tthis._last_time = this.current_time;\\r\\n\\tLEvent.trigger( this, \\\"start_animation\\\" );\\r\\n\\r\\n\\t//this.applyAnimation( take, this.current_time );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Pauses the animation\\r\\n* @method pause\\r\\n*/\\r\\nPlayAnimation.prototype.pause = function()\\r\\n{\\r\\n\\tthis.playing = false;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Stops the animation and sets the time to zero\\r\\n* @method stop\\r\\n*/\\r\\nPlayAnimation.prototype.stop = function()\\r\\n{\\r\\n\\tthis.playing = false;\\r\\n\\r\\n\\tthis.current_time = 0;\\r\\n\\tif(this.range)\\r\\n\\t\\tthis.current_time = this.range[0];\\r\\n\\tthis._last_time = this.current_time;\\r\\n\\t//this.applyAnimation( take, this.current_time );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Starts playing the animation but only using a range of it\\r\\n* @method playRange\\r\\n* @param {Number} start start time\\r\\n* @param {Number} end end time\\r\\n*/\\r\\nPlayAnimation.prototype.playRange = function( start, end )\\r\\n{\\r\\n\\tthis.playing = true;\\r\\n\\tthis.current_time = start;\\r\\n\\tthis._last_time = this.current_time;\\r\\n\\tthis.range = [ start, end ];\\r\\n}\\r\\n\\r\\n/**\\r\\n* applys the animation to the scene nodes\\r\\n* @method applyAnimation\\r\\n* @param {String} take the name of the take\\r\\n* @param {Number} time the time where to sample the tracks\\r\\n* @param {Number} last_time [optional] the last time that was applied, (used to trigger events)\\r\\n* @param {Number} weight [optional] the weight of this animation (used for blending animation), if ommited 1 is used\\r\\n*/\\r\\nPlayAnimation.prototype.applyAnimation = function( take, time, last_time, weight )\\r\\n{\\r\\n\\tif( last_time === undefined )\\r\\n\\t\\tlast_time = time;\\r\\n\\r\\n\\t//this could happen if the component was removed during the onUpdate\\r\\n\\tif(!this._root) \\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar root_node = null;\\r\\n\\tif(this.root_node && this._root.scene)\\r\\n\\t{\\r\\n\\t\\tif(this.root_node == \\\"@\\\")\\r\\n\\t\\t\\troot_node = this._root;\\r\\n\\t\\telse\\r\\n\\t\\t\\troot_node = this._root.scene.getNode( this.root_node );\\r\\n\\t}\\r\\n\\ttake.applyTracks( time, last_time, undefined, root_node, this._root.scene, weight, this.onPreApply, this.onApplySample );\\r\\n}\\r\\n\\r\\n//not in use\\r\\nPlayAnimation.prototype._processSample = function(nodename, property, value, options)\\r\\n{\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn;\\r\\n\\tvar node = scene.getNode(nodename);\\r\\n\\tif(!node) \\r\\n\\t\\treturn;\\r\\n\\t\\t\\r\\n\\tvar trans = node.transform;\\r\\n\\r\\n\\tswitch(property)\\r\\n\\t{\\r\\n\\t\\tcase \\\"translate.X\\\": if(trans) trans.position[0] = value; break;\\r\\n\\t\\tcase \\\"translate.Y\\\": if(trans) trans.position[1] = value; break;\\r\\n\\t\\tcase \\\"translate.Z\\\": if(trans) trans.position[2] = value; break;\\r\\n\\t\\tcase \\\"matrix\\\": if(trans) trans.fromMatrix(value); break;\\r\\n\\t\\tdefault: break;\\r\\n\\t}\\r\\n\\t\\r\\n\\tif(node.transform)\\r\\n\\t\\tnode.transform.updateMatrix();\\r\\n}\\r\\n\\r\\nPlayAnimation.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(this.animation)\\r\\n\\t\\tres[ this.animation ] = LS.Animation;\\r\\n}\\r\\n\\r\\nPlayAnimation.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.animation == old_name)\\r\\n\\t\\tthis.animation = new_name;\\r\\n}\\r\\n\\r\\n//returns which events can trigger this component\\r\\nPlayAnimation.prototype.getEvents = function()\\r\\n{\\r\\n\\treturn { \\\"start_animation\\\": \\\"event\\\", \\\"end_animation\\\": \\\"event\\\" };\\r\\n}\\r\\n\\r\\n//returns which actions can be triggered in this component\\r\\nPlayAnimation.prototype.getActions = function( actions )\\r\\n{\\r\\n\\tactions = actions || {};\\r\\n\\tactions[\\\"play\\\"] = \\\"function\\\";\\r\\n\\tactions[\\\"pause\\\"] = \\\"function\\\";\\r\\n\\tactions[\\\"stop\\\"] = \\\"function\\\";\\r\\n\\treturn actions;\\r\\n}\\r\\n\\r\\n\\r\\nLS.registerComponent( PlayAnimation );\\r\\n///@FILE:../src/components/playSkeletalAnimation.js\\r\\n///@INFO: ANIMATION\\r\\n/**\\r\\n* Reads animation from a LS.SkeletalAnimation resource and applies the properties to the SkinDeformer in this node\\r\\n* @class PlaySkeletalAnimation\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\nfunction PlaySkeletalAnimation(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.animation = \\\"\\\";\\r\\n\\tthis.playback_speed = 1.0;\\r\\n\\r\\n\\tthis._skeleton = new LS.Skeleton();\\r\\n\\r\\n\\t/**\\r\\n\\t* how to play the animation, options are:\\r\\n * PlaySkeletalAnimation.LOOP\\r\\n\\t*\\tPlaySkeletalAnimation.PINGPONG\\r\\n\\t*\\tPlaySkeletalAnimation.ONCE\\r\\n\\t*\\tPlaySkeletalAnimation.PAUSED\\r\\n\\t* @property mode {Number}\\r\\n\\t*/\\r\\n\\tthis.mode = PlaySkeletalAnimation.LOOP;\\r\\n\\tthis.playing = true;\\r\\n\\tthis.current_time = 0;\\r\\n\\tthis.range = null;\\r\\n\\tthis.interpolate = true;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nPlaySkeletalAnimation.LOOP = 1;\\r\\nPlaySkeletalAnimation.PINGPONG = 2;\\r\\nPlaySkeletalAnimation.ONCE = 3;\\r\\nPlaySkeletalAnimation.PAUSED = 4;\\r\\n\\r\\nPlaySkeletalAnimation.MODES = {\\\"loop\\\":PlaySkeletalAnimation.LOOP, \\\"pingpong\\\":PlaySkeletalAnimation.PINGPONG, \\\"once\\\":PlaySkeletalAnimation.ONCE, \\\"paused\\\":PlaySkeletalAnimation.PAUSED };\\r\\n\\r\\nPlaySkeletalAnimation[\\\"@animation\\\"] = { widget: \\\"resource\\\", resource_classname:\\\"SkeletalAnimation\\\" };\\r\\nPlaySkeletalAnimation[\\\"@mode\\\"] = { type:\\\"enum\\\", values: PlaySkeletalAnimation.MODES };\\r\\nPlaySkeletalAnimation[\\\"@current_time\\\"] = { type: LS.TYPES.NUMBER, min: 0, units:\\\"s\\\" };\\r\\n\\r\\nPlaySkeletalAnimation.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(o.enabled !== undefined)\\r\\n\\t\\tthis.enabled = !!o.enabled;\\r\\n\\tif(o.range) \\r\\n\\t\\tthis.range = o.range.concat();\\r\\n\\tif(o.mode !== undefined) \\r\\n\\t\\tthis.mode = o.mode;\\r\\n\\tif(o.animation)\\r\\n\\t\\tthis.animation = o.animation;\\r\\n\\tif(o.playback_speed != null)\\r\\n\\t\\tthis.playback_speed = parseFloat( o.playback_speed );\\r\\n\\tif(o.current_time != null)\\r\\n\\t\\tthis.current_time = parseFloat( o.current_time );\\r\\n\\tif(o.playing !== undefined)\\r\\n\\t\\tthis.playing = o.playing;\\r\\n}\\r\\n\\r\\nPlaySkeletalAnimation.icon = \\\"mini-icon-clock.png\\\";\\r\\n\\r\\nPlaySkeletalAnimation.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"update\\\", this.onUpdate, this);\\r\\n}\\r\\n\\r\\nPlaySkeletalAnimation.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind( scene, \\\"update\\\", this.onUpdate, this);\\r\\n\\r\\n\\t//Remove from SkinDeformer\\r\\n}\\r\\n\\r\\nPlaySkeletalAnimation.prototype.onUpdate = function(e, dt)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this.playing)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( this.mode != PlaySkeletalAnimation.PAUSED )\\r\\n\\t\\tthis.current_time += dt * this.playback_speed;\\r\\n\\r\\n\\tthis.onUpdateAnimation( dt );\\r\\n}\\r\\n\\r\\nPlaySkeletalAnimation.prototype.onUpdateAnimation = function(dt)\\r\\n{\\r\\n\\tvar animation = this.getAnimation();\\r\\n\\tif( !animation || animation.constructor != LS.SkeletalAnimation ) \\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar time = this.current_time;\\r\\n\\tvar start_time = 0;\\r\\n\\tvar duration = animation.duration;\\r\\n\\tvar end_time = duration;\\r\\n\\r\\n\\tif(this.range)\\r\\n\\t{\\r\\n\\t\\tstart_time = this.range[0];\\r\\n\\t\\tend_time = this.range[1];\\r\\n\\t\\tduration = end_time - start_time;\\r\\n\\t}\\r\\n\\r\\n\\tif(time > end_time)\\r\\n\\t{\\r\\n\\t\\tswitch( this.mode )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase PlaySkeletalAnimation.ONCE: \\r\\n\\t\\t\\t\\ttime = end_time; \\r\\n\\t\\t\\t\\t//time = start_time; //reset after\\r\\n\\t\\t\\t\\tLEvent.trigger( this, \\\"end_animation\\\" );\\r\\n\\t\\t\\t\\tthis.playing = false;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase PlaySkeletalAnimation.PINGPONG:\\r\\n\\t\\t\\t\\tif( ((time / duration)|0) % 2 == 0 ) //TEST THIS\\r\\n\\t\\t\\t\\t\\ttime = this.current_time % duration; \\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\ttime = duration - (this.current_time % duration);\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase PlaySkeletalAnimation.PINGPONG:\\r\\n\\t\\t\\t\\ttime = end_time; \\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase PlaySkeletalAnimation.LOOP: \\r\\n\\t\\t\\tdefault: \\r\\n\\t\\t\\t\\ttime = ((this.current_time - start_time) % duration) + start_time;\\r\\n\\t\\t\\t\\tLEvent.trigger( this, \\\"animation_loop\\\" );\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(time < start_time)\\r\\n\\t\\ttime = start_time;\\r\\n\\r\\n\\tthis.applyAnimation( time );\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(scene)\\r\\n\\t\\tscene.requestFrame();\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns the current animation or an animation with a given name\\r\\n* @method getAnimation\\r\\n* @param {String} name [optional] the name of the animation, if omited then uses the animation set in the component\\r\\n* @return {LS.Animation} the animation container\\r\\n*/\\r\\nPlaySkeletalAnimation.prototype.getAnimation = function( name )\\r\\n{\\r\\n\\tname = name === undefined ? this.animation : name;\\r\\n\\tvar anim = LS.ResourcesManager.getResource( name );\\r\\n\\tif( anim && anim.constructor === LS.SkeletalAnimation )\\r\\n\\t\\treturn anim;\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Gets the duration of the current animation\\r\\n* @method getDuration\\r\\n* @return {Number} the duration of the animation, or -1 if the animation was not found\\r\\n*/\\r\\nPlaySkeletalAnimation.prototype.getDuration = function()\\r\\n{\\r\\n\\tvar anim = this.getAnimation();\\r\\n\\tif(anim) \\r\\n\\t\\treturn anim.duration;\\r\\n\\treturn -1;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Resets the time to zero and starts playing the current animation\\r\\n* It also triggers a \\\"start_animation\\\" event\\r\\n* @method play\\r\\n*/\\r\\nPlaySkeletalAnimation.prototype.play = function()\\r\\n{\\r\\n\\tif(!this._root || !this._root.scene)\\r\\n\\t\\tconsole.error(\\\"cannot play an animation if the component doesnt belong to a node in a scene\\\");\\r\\n\\r\\n\\tthis.playing = true;\\r\\n\\r\\n\\tthis.current_time = 0;\\r\\n\\tif(this.range)\\r\\n\\t\\tthis.current_time = this.range[0];\\r\\n\\tLEvent.trigger( this, \\\"start_animation\\\" );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Pauses the animation\\r\\n* @method pause\\r\\n*/\\r\\nPlaySkeletalAnimation.prototype.pause = function()\\r\\n{\\r\\n\\tthis.playing = false;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Stops the animation and sets the time to zero\\r\\n* @method stop\\r\\n*/\\r\\nPlaySkeletalAnimation.prototype.stop = function()\\r\\n{\\r\\n\\tthis.playing = false;\\r\\n\\r\\n\\tthis.current_time = 0;\\r\\n\\tif(this.range)\\r\\n\\t\\tthis.current_time = this.range[0];\\r\\n}\\r\\n\\r\\n/**\\r\\n* Starts playing the animation but only using a range of it\\r\\n* @method playRange\\r\\n* @param {Number} start start time\\r\\n* @param {Number} end end time\\r\\n*/\\r\\nPlaySkeletalAnimation.prototype.playRange = function( start, end )\\r\\n{\\r\\n\\tthis.playing = true;\\r\\n\\tthis.current_time = start;\\r\\n\\tthis.range = [ start, end ];\\r\\n}\\r\\n\\r\\n/**\\r\\n* applys the animation to the scene nodes\\r\\n* @method applyAnimation\\r\\n* @param {Number} time the time where to sample the tracks\\r\\n*/\\r\\nPlaySkeletalAnimation.prototype.applyAnimation = function( time )\\r\\n{\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar animation = this.getAnimation();\\r\\n\\tif(!animation)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tanimation.assignTime(time, true, this.interpolate );\\r\\n\\tthis._skeleton.copyFrom( animation.skeleton );\\r\\n\\r\\n\\tvar deformer = this._root.getComponent( LS.Components.SkinDeformer );\\r\\n\\tif(deformer)\\r\\n\\t\\tdeformer._skeleton = this._skeleton;\\r\\n}\\r\\n\\r\\nPlaySkeletalAnimation.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(this.animation)\\r\\n\\t\\tres[ this.animation ] = LS.SkeletalAnimation;\\r\\n}\\r\\n\\r\\nPlaySkeletalAnimation.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.animation == old_name)\\r\\n\\t\\tthis.animation = new_name;\\r\\n}\\r\\n\\r\\n//returns which events can trigger this component\\r\\nPlaySkeletalAnimation.prototype.getEvents = function()\\r\\n{\\r\\n\\treturn { \\\"start_animation\\\": \\\"event\\\", \\\"end_animation\\\": \\\"event\\\" };\\r\\n}\\r\\n\\r\\n//returns which actions can be triggered in this component\\r\\nPlaySkeletalAnimation.prototype.getActions = function( actions )\\r\\n{\\r\\n\\tactions = actions || {};\\r\\n\\tactions[\\\"play\\\"] = \\\"function\\\";\\r\\n\\tactions[\\\"pause\\\"] = \\\"function\\\";\\r\\n\\tactions[\\\"stop\\\"] = \\\"function\\\";\\r\\n\\treturn actions;\\r\\n}\\r\\n\\r\\nPlaySkeletalAnimation.prototype.getPropertiesInfo = function()\\r\\n{\\r\\n\\tvar properties = {\\r\\n\\t\\tenabled: \\\"boolean\\\",\\r\\n\\t\\tcurrent_time: \\\"number\\\",\\r\\n\\t\\tmode: \\\"number\\\",\\r\\n\\t\\tplaying: \\\"boolean\\\",\\r\\n\\t\\tplayback_speed: \\\"number\\\",\\r\\n\\t\\tskeleton: \\\"skeleton\\\"\\r\\n\\t};\\r\\n\\treturn properties;\\r\\n}\\r\\n\\r\\nLS.registerComponent( PlaySkeletalAnimation );\\r\\n///@FILE:../src/components/realtimeReflector.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Realtime Reflective surface\\r\\n* @class RealtimeReflector\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\n\\r\\n\\r\\nfunction RealtimeReflector(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.texture_size = 512;\\r\\n\\tthis.clip_offset = 0.5; //to avoid ugly edges near clipping plane\\r\\n\\tthis.texture_name = \\\"\\\";\\r\\n\\tthis.all_cameras = false; //renders the reflection for every active camera (very slow)\\r\\n\\tthis.blur = 0;\\r\\n\\tthis.generate_mipmaps = false;\\r\\n\\tthis.use_mesh_info = false;\\r\\n\\tthis.offset = vec3.create();\\r\\n\\tthis.ignore_this_mesh = true;\\r\\n\\tthis.skip_if_node_not_visible = true;\\r\\n\\tthis.high_precision = false;\\r\\n\\tthis.refresh_rate = 1; //in frames\\r\\n\\tthis.layers = 0xFF;\\r\\n\\r\\n\\tthis._clipping_plane = vec4.create();\\r\\n\\r\\n\\tthis._textures = {};\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nRealtimeReflector.icon = \\\"mini-icon-reflector.png\\\";\\r\\n\\r\\nRealtimeReflector[\\\"@texture_size\\\"] = { type:\\\"enum\\\", values:[\\\"viewport\\\",64,128,256,512,1024,2048] };\\r\\nRealtimeReflector[\\\"@layers\\\"] = { type:\\\"layers\\\" };\\r\\n\\r\\nRealtimeReflector.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, LS.EVENT.RENDER_REFLECTIONS, this.onRenderReflection, this );\\r\\n\\tLEvent.bind( scene, LS.EVENT.AFTER_CAMERA_ENABLED, this.onCameraEnabled, this );\\r\\n}\\r\\n\\r\\n\\r\\nRealtimeReflector.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbindAll( scene, this );\\r\\n\\tfor(var i in this._textures)\\r\\n\\t\\tLS.ResourcesManager.unregisterResource( \\\":reflection_\\\" + i );\\r\\n\\tthis._textures = {}; //clear textures\\r\\n}\\r\\n\\r\\n\\r\\nRealtimeReflector.prototype.onRenderReflection = function( e, render_settings )\\r\\n{\\r\\n\\tif( !this.enabled || !this._root || !this._root.visible )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis.refresh_rate = this.refresh_rate << 0;\\r\\n\\tif( (scene._frame == 0 || (scene._frame % this.refresh_rate) != 0) && this._rt)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar texture_size = parseInt( this.texture_size );\\r\\n\\tvar texture_width = texture_size;\\r\\n\\tvar texture_height = texture_size;\\r\\n\\r\\n\\tvar visible = this._root.flags.visible;\\r\\n\\tthis._root.visible = !this.ignore_this_mesh;\\r\\n\\r\\n\\t//add flags\\r\\n\\tvar old_layers = render_settings.layers;\\r\\n\\trender_settings.layers = this.layers;\\r\\n\\r\\n\\tvar cameras = LS.Renderer._visible_cameras;\\r\\n\\r\\n\\tLS.Renderer.clearSamplers();\\r\\n\\r\\n\\tfor(var i = 0; i < cameras.length; i++)\\r\\n\\t{\\r\\n\\t\\tvar camera = cameras[i];\\r\\n\\r\\n\\t\\t//test if node in frustum\\r\\n\\t\\tif( this.skip_if_node_not_visible )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( !this._root._instances.length )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar is_seen = false;\\r\\n\\t\\t\\tfor(var j = 0; j < this._root._instances.length; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar instance = this._root._instances[i];\\r\\n\\t\\t\\t\\tif(geo.frustumTestBox( camera._frustum_planes, instance.aabb ) != CLIP_OUTSIDE )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tis_seen = true;\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif(!is_seen)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( isNaN( texture_size ) && this.texture_size == \\\"viewport\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar viewport = camera.getLocalViewport(null, camera._viewport_in_pixels );\\r\\n\\t\\t\\ttexture_width = viewport[2];//gl.canvas.width;\\r\\n\\t\\t\\ttexture_height = viewport[3];//gl.canvas.height;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar texture_type = gl.TEXTURE_2D;\\r\\n\\t\\tvar type = this.high_precision ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE;\\r\\n\\r\\n\\t\\tvar texture = this._textures[ camera.uid ];\\r\\n\\t\\tif(!texture || texture.width != texture_width || texture.height != texture_height || texture.type != type || texture.texture_type != texture_type || texture.minFilter != (this.generate_mipmaps ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR) )\\r\\n\\t\\t{\\r\\n\\t\\t\\ttexture = new GL.Texture( texture_width, texture_height, { type: type, texture_type: texture_type, minFilter: this.generate_mipmaps ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR });\\r\\n\\t\\t\\ttexture.has_mipmaps = this.generate_mipmaps;\\r\\n\\t\\t\\tthis._textures[ camera.uid ] = texture;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\ttexture._locked = true; //avoid binding this texture during rendering\\r\\n\\r\\n\\t\\t//compute planes\\r\\n\\t\\tvar plane_center = this._root.transform.getGlobalPosition();\\r\\n\\t\\tvar plane_normal = this._root.transform.getTop();\\r\\n\\t\\tvar cam_eye = camera.getEye();\\r\\n\\t\\tvar cam_center = camera.getCenter();\\r\\n\\t\\tvar cam_up = camera.getUp();\\r\\n\\r\\n\\t\\t//use the first vertex and normal from a mesh\\r\\n\\t\\tif(this.use_mesh_info)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mesh = this._root.getMesh();\\r\\n\\t\\t\\tif(mesh)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tplane_center = this._root.transform.globalToLocal( BBox.getCenter( mesh.bounding ) );\\r\\n\\t\\t\\t\\tplane_normal = this._root.transform.globalVectorToLocal( LS.TOP );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvec3.add( plane_center, plane_center, this.offset );\\r\\n\\r\\n\\t\\t//camera\\r\\n\\t\\tvar reflected_camera = this._reflected_camera || new LS.Camera();\\r\\n\\t\\tthis._reflected_camera = reflected_camera;\\r\\n\\t\\treflected_camera.configure( camera.serialize() );\\r\\n\\r\\n\\t\\treflected_camera.fov = camera.fov;\\r\\n\\t\\treflected_camera.aspect = camera.aspect;\\r\\n\\r\\n\\t\\tvar new_eye = geo.reflectPointInPlane( cam_eye, plane_center, plane_normal );\\r\\n\\t\\tvar new_center = geo.reflectPointInPlane( cam_center, plane_center, plane_normal );\\r\\n\\t\\tvar new_up = geo.reflectPointInPlane( cam_up, [0,0,0], plane_normal );\\r\\n\\t\\treflected_camera.lookAt( new_eye, new_center, new_up );\\r\\n\\t\\t//reflected_camera.up = cam_up;\\r\\n\\r\\n\\t\\t//little offset\\r\\n\\t\\tvec3.add( plane_center, plane_center, vec3.scale(vec3.create(), plane_normal, -this.clip_offset) );\\r\\n\\t\\tthis._clipping_plane.set( plane_normal );\\r\\n\\t\\tthis._clipping_plane[3] = vec3.dot(plane_center, plane_normal);\\r\\n\\t\\t//render_settings.clipping_plane = clipping_plane;\\r\\n\\t\\tLS.Renderer._uniforms.u_clipping_plane.set( this._clipping_plane );\\r\\n\\r\\n\\t\\tLS.Renderer.renderInstancesToRT( reflected_camera, texture, render_settings );\\r\\n\\r\\n\\t\\tLS.Renderer._uniforms.u_clipping_plane.set([0,0,0,0]);\\r\\n\\r\\n\\t\\tif(this.blur)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar blur_texture = this._textures[ \\\"blur_\\\" + camera.uid ];\\r\\n\\t\\t\\tif( blur_texture && ( Texture.compareFormats( texture, blur_texture ) || blur_texture.minFilter != texture.minFilter ))\\r\\n\\t\\t\\t\\tblur_texture = null; //remove old one\\r\\n\\t\\t\\tblur_texture = texture.applyBlur( this.blur, this.blur, 1, null, blur_texture );\\r\\n\\t\\t\\t//this._textures[ \\\"blur_\\\" + camera.uid ] = blur_texture;\\r\\n\\t\\t\\t//LS.ResourcesManager.registerResource(\\\":BLUR\\\" + camera.uid, blur_texture);//debug\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(this.generate_mipmaps && isPowerOfTwo( texture_width ) && isPowerOfTwo( texture_height ) )\\r\\n\\t\\t{\\r\\n\\t\\t\\ttexture.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR );\\r\\n\\t\\t\\tgl.generateMipmap(texture.texture_type);\\r\\n\\t\\t\\ttexture.unbind();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\ttexture._locked = false;\\r\\n\\t\\ttexture._is_planar = true;\\r\\n\\r\\n\\t\\tif(this.texture_name)\\r\\n\\t\\t\\tLS.ResourcesManager.registerResource( this.texture_name, texture );\\r\\n\\t\\tLS.ResourcesManager.registerResource( \\\":reflection_\\\" + camera.uid, texture );\\r\\n\\r\\n\\t\\tif(!this.all_cameras)\\r\\n\\t\\t\\tbreak;\\r\\n\\t}\\r\\n\\r\\n\\t//remove flags\\r\\n\\trender_settings.layers = old_layers;\\r\\n\\tdelete render_settings.brightness_factor;\\r\\n\\tdelete render_settings.colorclip_factor;\\r\\n\\r\\n\\t//make it visible again\\r\\n\\tthis._root.flags.visible = visible;\\r\\n}\\r\\n\\r\\n\\r\\nRealtimeReflector.prototype.onCameraEnabled = function(e, camera)\\r\\n{\\r\\n\\tif(!this.enabled || !this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this._root.material)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar texture = this._textures[ camera.uid ];\\r\\n\\tif(!texture)\\r\\n\\t\\treturn;\\r\\n\\t\\r\\n\\tvar mat = this._root.getMaterial();\\r\\n\\tif(mat)\\r\\n\\t{\\r\\n\\t\\tvar sampler = mat.setTexture( Material.ENVIRONMENT_TEXTURE, \\\":reflection_\\\" + camera.uid );\\r\\n\\t\\tsampler.uvs = Material.COORDS_FLIPPED_SCREEN;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nLS.registerComponent( RealtimeReflector );\\r\\n///@FILE:../src/components/probes.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Realtime Reflective probe\\r\\n* @class RealtimeReflector\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\n\\r\\nfunction ReflectionProbe( o )\\r\\n{\\r\\n\\tthis._enabled = true;\\r\\n\\tthis.texture_size = 512;\\r\\n\\tthis.high_precision = false;\\r\\n\\tthis.texture_name = \\\"\\\";\\r\\n\\tthis.generate_irradiance = true;\\r\\n\\tthis.generate_mipmaps = false;\\r\\n\\tthis.refresh_rate = 0; //in frames, 0 means only on demand (on start or if ReflectionProbe.updateAll() is called)\\r\\n\\tthis.layers = 0xFF;\\r\\n\\r\\n\\tthis.near = 0.1;\\r\\n\\tthis.far = 1000;\\r\\n\\tthis.background_color = vec4.create();\\r\\n\\r\\n\\tthis._position = vec3.create(); //position where the cubemap was captured\\r\\n\\tthis._current = vec3.create(); //position where the node is\\r\\n\\tthis._version = -1;\\r\\n\\r\\n\\tthis._texture = null;\\r\\n\\tthis._irradiance_shs = new Float32Array( 3 * 9 );\\r\\n\\r\\n\\tthis._tex_id = \\\":probe_\\\" + ReflectionProbe.last_id;\\r\\n\\tReflectionProbe.last_id++;\\r\\n\\tthis._registered = false;\\r\\n\\tthis._must_update = false;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nReflectionProbe.last_id = 0;\\r\\n\\r\\nReflectionProbe.icon = \\\"mini-icon-reflector.png\\\";\\r\\n\\r\\nReflectionProbe[\\\"@texture_size\\\"] = { type:\\\"enum\\\", values:[\\\"viewport\\\",64,128,256,512,1024,2048] };\\r\\nReflectionProbe[\\\"@layers\\\"] = { type:\\\"layers\\\" };\\r\\nReflectionProbe[\\\"@background_color\\\"] = { type:\\\"color\\\" };\\r\\n\\r\\nObject.defineProperty( ReflectionProbe.prototype, \\\"texture\\\", {\\r\\n\\tget: function() { return this._texture; },\\r\\n\\tset: function() { throw(\\\"probe cubemap cannot be set\\\"); },\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( ReflectionProbe.prototype, \\\"enabled\\\", {\\r\\n\\tset: function(v){ \\r\\n\\t\\tif(v == this._enabled)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._enabled = v;\\r\\n\\t\\tvar scene = this._root ? this._root.scene : null;\\r\\n\\t\\tif(!this._enabled)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(this._registered)\\r\\n\\t\\t\\t\\tthis.unregister(scene);\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tif(!this._registered)\\r\\n\\t\\t\\tthis.register(scene);\\r\\n\\t\\tthis.onRenderReflection();\\r\\n\\t\\tLS.GlobalScene.requestFrame();\\r\\n\\t},\\r\\n\\tget: function() { return this._enabled; },\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nReflectionProbe.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene,\\\"start\\\", this.onRenderReflection, this );\\r\\n\\tLEvent.bind( scene,\\\"renderReflections\\\", this.onRenderReflection, this );\\r\\n\\t//LEvent.bind( scene,\\\"afterCameraEnabled\\\", this.onCameraEnabled, this );\\r\\n\\t//LEvent.bind( LS.Renderer,\\\"renderHelpers\\\", this.onVisualizeProbe, this );\\r\\n\\r\\n\\tthis.register( scene );\\r\\n}\\r\\n\\r\\nReflectionProbe.prototype.getExtraProperties = function(properties)\\r\\n{\\r\\n\\tproperties.push([\\\"texture\\\",\\\"texture\\\"]);\\r\\n}\\r\\n\\r\\nReflectionProbe.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbindAll( scene, this );\\r\\n\\t//LEvent.unbindAll( LS.Renderer, this );\\r\\n\\t\\r\\n\\tif(this._texture)\\r\\n\\t\\tLS.ResourcesManager.unregisterResource( this._tex_id );\\r\\n\\r\\n\\t//TODO: USE POOL!!\\r\\n\\tthis._texture = null;\\r\\n\\r\\n\\tthis.unregister( scene );\\r\\n}\\r\\n\\r\\nReflectionProbe.prototype.onSerialize = function(o)\\r\\n{\\r\\n\\to.irradiance_shs = typedArrayToArray( this._irradiance_shs );\\r\\n}\\r\\n\\r\\nReflectionProbe.prototype.onConfigure = function(o)\\r\\n{\\r\\n\\tif(o.irradiance_shs)\\r\\n\\t\\tthis._irradiance_shs.set( o.irradiance_shs );\\r\\n}\\r\\n\\r\\nReflectionProbe.prototype.onRenderReflection = function( e )\\r\\n{\\r\\n\\tif( !this._enabled )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( !this._root || !this._root.scene )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this._must_update)\\r\\n\\t{\\r\\n\\t\\tif( LS.ResourcesManager.isLoading() )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tthis.refresh_rate = this.refresh_rate|0;\\r\\n\\t\\tif( this.refresh_rate < 1 && this._texture )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tif ( this._texture && (this._root.scene._frame % this.refresh_rate) != 0 )\\r\\n\\t\\t\\treturn;\\r\\n\\t}\\r\\n\\tthis._must_update = false;\\r\\n\\r\\n\\tthis.recompute();\\r\\n}\\r\\n\\r\\nReflectionProbe.prototype.recompute = function( render_settings, generate_spherical_harmonics )\\r\\n{\\r\\n\\tif( !this._root || !this._root.scene )\\r\\n\\t\\treturn;\\r\\n\\tthis._root.transform.getGlobalPosition( this._current );\\r\\n\\tthis.updateCubemap( this._current, render_settings, true );\\r\\n\\r\\n\\t//compute SHs (VERY SLOW)\\r\\n\\tif(generate_spherical_harmonics)\\r\\n\\t{\\r\\n\\t\\t//TODO: copy to lowres cubemap\\r\\n\\t\\tvar temp_texture = ReflectionProbe._temp_cubemap;\\r\\n\\t\\tvar texture_size = IrradianceCache.capture_cubemap_size;\\r\\n\\t\\tvar texture_settings = { type: gl.FLOAT, texture_type: gl.TEXTURE_CUBE_MAP, format: gl.RGB };\\r\\n\\t\\tif( !temp_texture || temp_texture.width != texture_size || temp_texture.height != texture_size )\\r\\n\\t\\t\\tReflectionProbe._temp_cubemap = temp_texture = new GL.Texture( texture_size, texture_size, texture_settings );\\r\\n\\t\\tvar texture = this._texture;\\r\\n\\t\\ttexture.copyTo( temp_texture ); //downsample\\r\\n\\t\\tthis._irradiance_shs = IrradianceCache.computeSH( temp_texture );\\r\\n\\t}\\r\\n}\\r\\n\\r\\nReflectionProbe.use_float_for_high_precision = false; //by default it uses HALF_FLOAT\\r\\n\\r\\nReflectionProbe.prototype.updateCubemap = function( position, render_settings )\\r\\n{\\r\\n\\trender_settings = render_settings || LS.Renderer.default_render_settings;\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar texture_size = parseInt( this.texture_size );\\r\\n\\r\\n\\t//add flags\\r\\n\\tvar old_layers = render_settings.layers;\\r\\n\\trender_settings.layers = this.layers;\\r\\n\\r\\n\\tLS.Renderer.clearSamplers();\\r\\n\\r\\n\\tvar texture_type = gl.TEXTURE_CUBE_MAP;\\r\\n\\tvar type = this.high_precision ? ( ReflectionProbe.use_float_for_high_precision ? gl.FLOAT : gl.HIGH_PRECISION_FORMAT ) : gl.UNSIGNED_BYTE;\\r\\n\\r\\n\\tvar texture = this._texture;\\r\\n\\r\\n\\tif(!texture || texture.width != texture_size || texture.height != texture_size || texture.type != type || texture.texture_type != texture_type || texture.minFilter != (this.generate_mipmaps ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR) )\\r\\n\\t{\\r\\n\\t\\ttexture = new GL.Texture( texture_size, texture_size, { type: type, format: gl.RGB, texture_type: texture_type, minFilter: this.generate_mipmaps ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR });\\r\\n\\t\\ttexture.has_mipmaps = this.generate_mipmaps;\\r\\n\\t\\tthis._texture = texture;\\r\\n\\t}\\r\\n\\r\\n\\tif(position)\\r\\n\\t\\tthis._position.set( position );\\r\\n\\telse\\r\\n\\t\\tposition = this._root.transform.getGlobalPosition( this._position );\\r\\n\\r\\n\\ttexture._in_current_fbo = true; //block binding this texture during rendering of the reflection\\r\\n\\r\\n\\t//first render\\r\\n\\tif( !LS.Renderer._visible_instances )\\r\\n\\t{\\r\\n\\t\\tLS.Renderer.processVisibleData( scene, render_settings );\\r\\n\\t\\tLS.Renderer.regenerateShadowmaps( scene, render_settings );\\r\\n\\t}\\r\\n\\r\\n\\t//avoid reusing same irradiance from previous pass\\r\\n\\r\\n\\t//fix: there was a problem because there was no texture bind in ENVIRONMENT_SLOT, this fix it\\r\\n\\tfor(var i = 0; i < LS.Renderer._visible_instances.length; ++i)\\r\\n\\t\\tif( LS.Renderer._visible_instances[i]._nearest_reflection_probe == this )\\r\\n\\t\\t\\tLS.Renderer._visible_instances[i]._nearest_reflection_probe = null;\\r\\n\\r\\n\\t//render all the scene inside the cubemap\\r\\n\\tLS.Renderer.renderToCubemap( position, 0, texture, render_settings, this.near, this.far, this.background_color );\\r\\n\\r\\n\\ttexture._in_current_fbo = false;\\r\\n\\r\\n\\tif(this.generate_mipmaps && isPowerOfTwo( texture_size ) )\\r\\n\\t{\\r\\n\\t\\ttexture.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR );\\r\\n\\t\\tgl.generateMipmap(texture.texture_type);\\r\\n\\t\\ttexture.unbind();\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i = 0; i < LS.Renderer._visible_instances.length; ++i)\\r\\n\\t\\tif( LS.Renderer._visible_instances[i]._nearest_reflection_probe == null )\\r\\n\\t\\t\\tLS.Renderer._visible_instances[i]._nearest_reflection_probe = this;\\r\\n\\r\\n\\tif(this.texture_name)\\r\\n\\t\\tLS.ResourcesManager.registerResource( this.texture_name, texture );\\r\\n\\tLS.ResourcesManager.registerResource( this._tex_id, texture );\\r\\n\\r\\n\\t//remove flags\\r\\n\\trender_settings.layers = old_layers;\\r\\n}\\r\\n\\r\\n//assigns the cubemaps to the scene global\\r\\nReflectionProbe.prototype.assignCubemaps = function( scene )\\r\\n{\\r\\n\\tif(this._texture)\\r\\n\\t\\tLS.ResourcesManager.registerResource( this._tex_id, this._texture );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Adds a reflection probe to the scene\\r\\n*\\r\\n* @method register\\r\\n* @param {LS.Scene} scene\\r\\n*/\\r\\nReflectionProbe.prototype.register = function( scene )\\r\\n{\\r\\n\\tif( scene._reflection_probes.indexOf( this ) != -1 )\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"this reflection probe is already registered\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tscene._reflection_probes.push( this );\\r\\n\\tthis._registered = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* removes a reflection probe from the scene\\r\\n*\\r\\n* @method unregister\\r\\n* @param {ReflectionProbe} probe\\r\\n*/\\r\\nReflectionProbe.prototype.unregister = function( scene )\\r\\n{\\r\\n\\tvar index = scene._reflection_probes.indexOf( this );\\r\\n\\tif( index == -1 )\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"this reflection probe is not registered\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tscene._reflection_probes.splice( index, 1);\\r\\n\\tthis._registered = false;\\r\\n}\\r\\n\\r\\n/**\\r\\n* visualizes the content of a probe\\r\\n*\\r\\n* @method renderProbe\\r\\n* @param {bool} visualize_irradiance if true the irradiance is shown, otherwise the reflection\\r\\n* @param {bool} picking_color if true it is rendered with a giben color for mouse picking\\r\\n*/\\r\\nReflectionProbe.prototype.renderProbe = function( visualize_irradiance, picking_color )\\r\\n{\\r\\n\\tif( !this._texture || !this._enabled )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tLS.Draw.push();\\r\\n\\tLS.Draw.translate( this._position );\\r\\n\\tLS.Draw.scale( ReflectionProbe.helper_size );\\r\\n\\r\\n\\tif(!picking_color) //regular texture\\r\\n\\t{\\r\\n\\t\\tvar shader = GL.Shader.getCubemapShowShader();\\r\\n\\r\\n if(visualize_irradiance)\\r\\n\\t\\t this._irradiance_texture.bind(0);\\r\\n else\\r\\n\\t\\t this._texture.bind(0);\\r\\n \\r\\n\\t\\tLS.Draw.renderMesh( LS.Renderer._sphere_mesh, GL.TRIANGLES, shader );\\r\\n if(1) //contour\\r\\n {\\r\\n LS.Draw.setColor( LS.WHITE );\\r\\n LS.Draw.scale( 1.1 );\\r\\n gl.enable( gl.CULL_FACE );\\r\\n gl.frontFace( gl.CW );\\r\\n LS.Draw.renderMesh( LS.Renderer._sphere_mesh, GL.TRIANGLES );\\r\\n gl.frontFace( gl.CCW );\\r\\n }\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tLS.Draw.setColor( picking_color )\\r\\n\\t\\tLS.Draw.renderMesh( LS.Renderer._sphere_mesh, GL.TRIANGLES );\\r\\n\\t}\\r\\n\\r\\n\\tLS.Draw.pop();\\r\\n}\\r\\n\\r\\n/**\\r\\n* Static method to update all the reflection probes active in the scene\\r\\n*\\r\\n* @method ReflectionProbe.updateAll\\r\\n* @param {LS.Scene} scene the scene\\r\\n* @param {LS.RenderSettings} render_settings the render settings to use while rendering the cubemaps\\r\\n*/\\r\\n\\r\\nReflectionProbe.updateAll = function( scene, render_settings )\\r\\n{\\r\\n\\tscene = scene || LS.GlobalScene;\\r\\n\\r\\n\\tfor(var i = 0; i < scene._reflection_probes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar probe = scene._reflection_probes[i];\\r\\n\\t\\tprobe.recompute( render_settings, true );\\r\\n\\t}\\r\\n}\\r\\n\\r\\nReflectionProbe.visualize_helpers = true;\\r\\nReflectionProbe.visualize_irradiance = false;\\r\\nReflectionProbe.helper_size = 1;\\r\\n\\r\\nLS.registerComponent( ReflectionProbe );\\r\\n\\r\\n\\r\\n/**\\r\\n* Precomputed Irradiance probes\\r\\n* @class IrradianceCache\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\n\\r\\n\\r\\nfunction IrradianceCache( o )\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.size = vec3.fromValues(10,10,10); //grid size\\r\\n\\tthis.subdivisions = new Uint8Array([4,1,4]);\\r\\n\\tthis.layers = 0xFF; //layers that can contribute to the irradiance\\r\\n\\tthis.force_two_sided = false;\\r\\n\\r\\n\\tthis.near = 0.1;\\r\\n\\tthis.far = 1000;\\r\\n\\tthis.sampling_distance = 0.0;\\r\\n\\tthis.debug = 0.0;\\r\\n\\tthis.background_color = vec4.create();\\r\\n\\tthis.intensity_color = vec3.fromValues(1,1,1);\\r\\n\\r\\n\\tthis.mode = IrradianceCache.VERTEX_MODE;\\r\\n\\r\\n\\tthis._irradiance_cubemaps = [];\\r\\n\\tthis._irradiance_shs = [];\\r\\n\\tthis._irradiance_matrix = mat4.create();\\r\\n\\tthis._irradiance_subdivisions = vec3.clone( this.subdivisions );\\r\\n\\tthis._sh_texture = null;\\r\\n\\r\\n\\tthis._uniforms = {\\r\\n\\t\\tirradiance_texture: LS.Renderer.IRRADIANCE_TEXTURE_SLOT,\\r\\n\\t\\tu_irradiance_subdivisions: this._irradiance_subdivisions,\\r\\n\\t\\tu_irradiance_color: this.intensity_color,\\r\\n\\t\\tu_irradiance_imatrix: mat4.create(),\\r\\n\\t\\tu_irradiance_distance: 0\\r\\n\\t\\t//u_irradiance_debug: 0\\r\\n\\t};\\r\\n\\tthis._samplers = [];\\r\\n\\r\\n\\t//callback\\r\\n\\tthis.onRecomputingIrradiance = null; //called when recomputing\\r\\n\\tthis.onPreprocessCubemap = null; //called before generating SHs\\r\\n\\tthis.onComputedSphericalHarmonics = null; //called after generating SHs\\r\\n\\tthis.onRecomputingFinished = null; //called when recomputing\\r\\n\\r\\n\\tthis.cache_filename = \\\"\\\";\\r\\n\\tthis._cache_resource = null;\\r\\n\\r\\n\\tif(o)\\r\\n\\t{\\r\\n\\t\\tthis.configure(o);\\r\\n\\t\\tif(o.uid && !this.cache_filename)\\r\\n\\t\\t\\tthis.cache_filename = \\\"IR_cache_\\\" + o.uid.substr(1) + \\\".bin\\\";\\r\\n\\t}\\r\\n}\\r\\n\\r\\nIrradianceCache.show_probes = false;\\r\\nIrradianceCache.show_cubemaps = false;\\r\\nIrradianceCache.probes_size = 1;\\r\\nIrradianceCache.capture_cubemap_size = 64; //renders the scene to this size\\r\\nIrradianceCache.final_cubemap_size = 16; //downsamples to this size\\r\\n\\r\\nIrradianceCache.OBJECT_MODE = 1;\\r\\nIrradianceCache.VERTEX_MODE = 2;\\r\\nIrradianceCache.PIXEL_MODE = 3;\\r\\n\\r\\nIrradianceCache[\\\"@mode\\\"] = { type:\\\"enum\\\", values: { \\\"object\\\": IrradianceCache.OBJECT_MODE, \\\"vertex\\\": IrradianceCache.VERTEX_MODE, \\\"pixel\\\": IrradianceCache.PIXEL_MODE } };\\r\\nIrradianceCache[\\\"@size\\\"] = { type:\\\"vec3\\\", min: 0.1, step: 0.1, precision:3 };\\r\\nIrradianceCache[\\\"@subdivisions\\\"] = { type:\\\"vec3\\\", min: 1, max: 256, step: 1, precision:0 };\\r\\nIrradianceCache[\\\"@layers\\\"] = { widget:\\\"layers\\\" };\\r\\nIrradianceCache[\\\"@background_color\\\"] = { type:\\\"color\\\" };\\r\\nIrradianceCache[\\\"@intensity_color\\\"] = { type:\\\"color\\\" };\\r\\nIrradianceCache.default_coeffs = new Float32Array([ 0,0,0, 0.5,0.75,1, 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0 ]);\\r\\n\\r\\nIrradianceCache.use_sh_low = false; //set to false before shader compilation to use 9 coeffs instead of 4\\r\\n\\r\\nIrradianceCache.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"fillSceneUniforms\\\", this.fillSceneUniforms, this);\\r\\n}\\r\\n\\r\\nIrradianceCache.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind( scene, \\\"fillSceneUniforms\\\", this.fillSceneUniforms, this);\\r\\n}\\r\\n\\r\\nIrradianceCache.prototype.onConfigure = function(o)\\r\\n{\\r\\n\\tif(!this.cache_filename)\\r\\n\\t\\treturn; //???\\r\\n\\r\\n\\tvar that = this;\\r\\n\\tLS.ResourcesManager.load( this.cache_filename, function( res ){\\r\\n\\t\\tif(!res)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthat._cache_resource = res;\\r\\n\\t\\tif(!that._sh_texture && res._sh_texture) //optimization to avoid to create a texture every time\\r\\n\\t\\t\\tthat._sh_texture = res._sh_texture; \\r\\n\\t\\tthat.fromData( res.data );\\r\\n\\t\\tthat.encodeCacheInTexture();\\r\\n\\t\\tres._sh_texture = that._sh_texture;\\r\\n\\t\\tLS.GlobalScene.requestFrame();\\r\\n\\t});\\r\\n}\\r\\n\\r\\nIrradianceCache.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif( this.cache_filename && this._cache_resource )\\r\\n\\t\\tres[ this.cache_filename ] = LS.Resource;\\r\\n}\\r\\n\\r\\n\\r\\nIrradianceCache.prototype.onResourceRenamed = function(old_name, new_name)\\r\\n{\\r\\n\\tif( old_name == this.cache_filename)\\r\\n\\t\\tthis.cache_filename = new_name;\\r\\n}\\r\\n\\r\\nIrradianceCache.prototype.fillSceneUniforms = function()\\r\\n{\\r\\n\\tif(!this.enabled || !this._sh_texture)\\r\\n\\t\\treturn;\\r\\n\\tthis._samplers[ LS.Renderer.IRRADIANCE_TEXTURE_SLOT ] = this._sh_texture;\\r\\n\\tthis._uniforms.u_irradiance_distance = this.sampling_distance;\\r\\n\\t//this._uniforms.u_irradiance_debug = this.debug;\\r\\n\\tLS.Renderer.enableFrameShaderBlock( \\\"applyIrradiance\\\", this._uniforms, this._samplers );\\r\\n}\\r\\n\\r\\nIrradianceCache.prototype.recompute = function( camera )\\r\\n{\\r\\n\\tvar subs = this.subdivisions;\\r\\n\\tvar size = this.size;\\r\\n\\tif(subs[0] < 1) subs[0] = 1;\\r\\n\\tif(subs[1] < 1) subs[1] = 1;\\r\\n\\tif(subs[2] < 1) subs[2] = 1;\\r\\n\\r\\n\\tvar start = getTime();\\r\\n\\tconsole.log(\\\"Capturing irradiance...\\\");\\r\\n\\r\\n\\tvar iscale = vec3.fromValues( size[0]/subs[0], size[1]/subs[1], size[2]/subs[2] );\\r\\n\\r\\n\\t//cubemap\\r\\n\\tvar type = gl.FLOAT; //enforce floats even in low precision, they get better coefficients, I dont use gl.HIGH_PRECISION_FORMAT because I cant read them back\\r\\n\\tvar render_settings = LS.Renderer.default_render_settings;\\r\\n\\tvar old_layers = render_settings.layers;\\r\\n\\trender_settings.layers = this.layers;\\r\\n\\tLS.GlobalScene.info.textures.irradiance = null;\\r\\n\\r\\n\\tvar final_cubemap_size = IrradianceCache.final_cubemap_size;\\r\\n\\tvar texture_size = IrradianceCache.capture_cubemap_size; //default is 64\\r\\n\\tvar texture_settings = { type: type, texture_type: gl.TEXTURE_CUBE_MAP, format: gl.RGB };\\r\\n\\tvar texture = IrradianceCache._temp_cubemap;\\r\\n\\tif( !texture || texture.width != texture_size || texture.height != texture_size || texture.type != texture_settings.type )\\r\\n\\t\\tIrradianceCache._temp_cubemap = texture = new GL.Texture( texture_size, texture_size, texture_settings );\\r\\n\\r\\n\\tif(this.onRecomputingIrradiance)\\r\\n\\t\\tthis.onRecomputingIrradiance(size);\\r\\n\\r\\n\\t//first render\\r\\n\\tif( !LS.Renderer._visible_instances )\\r\\n\\t{\\r\\n\\t\\tvar scene = this._root.scene;\\r\\n\\t\\tif(!scene)\\r\\n\\t\\t\\tthrow(\\\"cannot compute irradiance without scene\\\");\\r\\n\\t\\tLS.Renderer.processVisibleData( scene, render_settings );\\r\\n\\t\\tLS.Renderer.regenerateShadowmaps( scene, render_settings );\\r\\n\\t}\\r\\n\\r\\n\\t//compute cache size\\r\\n\\tvar num_probes = this.subdivisions[0] * this.subdivisions[1] * this.subdivisions[2];\\r\\n\\tthis._irradiance_cubemaps.length = num_probes;\\r\\n\\tthis._irradiance_shs.length = num_probes;\\r\\n\\tthis._irradiance_subdivisions.set( this.subdivisions );\\r\\n\\r\\n\\tvar position = vec3.create();\\r\\n\\tvar matrix = this._irradiance_matrix;\\r\\n\\tmat4.identity( matrix );\\r\\n\\tif( this._root.transform )\\r\\n\\t\\tthis._root.transform.getGlobalMatrix( matrix );\\r\\n\\tmat4.scale( matrix, matrix, iscale );\\r\\n\\r\\n\\tvar i = 0, generated = 0;\\r\\n\\tfor(var y = 0; y < subs[1]; ++y)\\r\\n\\tfor(var z = 0; z < subs[2]; ++z)\\r\\n\\tfor(var x = 0; x < subs[0]; ++x)\\r\\n\\t{\\r\\n\\t\\tposition[0] = x + 0.5;\\r\\n\\t\\tposition[1] = y + 0.5;\\r\\n\\t\\tposition[2] = z + 0.5;\\r\\n\\t\\tif( matrix )\\r\\n\\t\\t\\tmat4.multiplyVec3( position, matrix, position );\\r\\n\\r\\n\\t\\tif( camera && !camera.testSphereInsideFrustum( position ) )\\r\\n\\t\\t{\\r\\n\\t\\t\\ti+=1;\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar cubemap = this._irradiance_cubemaps[ i ];\\r\\n\\t\\tif(!cubemap || cubemap.type != texture_settings.type || cubemap.width != final_cubemap_size )\\r\\n\\t\\t\\tthis._irradiance_cubemaps[ i ] = cubemap = new GL.Texture( final_cubemap_size, final_cubemap_size, texture_settings );\\r\\n\\r\\n\\t\\tIrradianceCache.captureIrradiance( position, cubemap, render_settings, this.near, this.far, this.background_color, true, IrradianceCache._temp_cubemap );\\r\\n\\t\\tthis._irradiance_shs[i] = this.computeCubemapSH( cubemap, position, i );\\r\\n\\r\\n\\t\\ti+=1;\\r\\n\\t\\tgenerated+=1;\\r\\n\\t}\\r\\n\\r\\n\\tvar end_irradiance_time = getTime();\\r\\n\\tconsole.log(\\\"Capturing light time: \\\" + (end_irradiance_time - start).toFixed(1) + \\\"ms. Num. probes updated: \\\" + generated );\\r\\n\\r\\n\\tthis.encodeCacheInTexture();\\r\\n\\tvar end_packing_time = getTime();\\r\\n\\tconsole.log(\\\"Packing in texture time: \\\" + (end_packing_time - end_irradiance_time).toFixed(1) + \\\"ms\\\");\\r\\n\\r\\n\\tconsole.log(\\\"Irradiance Total: \\\" + (getTime() - start).toFixed(1) + \\\"ms\\\");\\r\\n\\r\\n\\tif(this.onRecomputingFinished)\\r\\n\\t\\tthis.onRecomputingFinished();\\r\\n\\r\\n\\t//store in file\\r\\n\\tif(!this.cache_filename)\\r\\n\\t\\tthis.cache_filename = \\\"IR_cache_\\\" + this.uid.substr(1) + \\\".bin\\\";\\r\\n\\tvar cache_res = this._cache_resource = LS.ResourcesManager.getResource( cache_res );\\r\\n\\tif(!cache_res)\\r\\n\\t{\\r\\n\\t\\tthis._cache_resource = cache_res = new LS.Resource();\\r\\n\\t\\tLS.ResourcesManager.registerResource( this.cache_filename, cache_res );\\r\\n\\t}\\r\\n\\tcache_res.data = this.toData();\\r\\n\\tLS.RM.resourceModified( cache_res );\\r\\n\\r\\n\\t//remove flags\\r\\n\\trender_settings.layers = old_layers;\\r\\n}\\r\\n\\r\\n//captures the illumination to a cubemap\\r\\nIrradianceCache.captureIrradiance = function( position, output_cubemap, render_settings, near, far, bg_color, force_two_sided, temp_cubemap )\\r\\n{\\r\\n\\ttemp_cubemap = temp_cubemap;\\r\\n\\r\\n\\tLS.Renderer.clearSamplers();\\r\\n\\r\\n\\t//disable IR cache first\\r\\n\\tLS.Renderer.disableFrameShaderBlock(\\\"applyIrradiance\\\");\\r\\n\\r\\n\\tif( force_two_sided )\\r\\n\\t\\trender_settings.force_two_sided = true;\\r\\n\\r\\n\\t//render all the scene inside the cubemap\\r\\n\\tLS.Renderer.renderToCubemap( position, 0, temp_cubemap, render_settings, near, far, bg_color );\\r\\n\\r\\n\\tif( force_two_sided )\\r\\n\\t\\trender_settings.force_two_sided = false;\\r\\n\\r\\n\\t//downsample\\r\\n\\ttemp_cubemap.copyTo( output_cubemap );\\r\\n}\\r\\n\\r\\nIrradianceCache.prototype.computeCubemapSH = function( cubemap, position, index )\\r\\n{\\r\\n\\t//read 6 images from cubemap\\r\\n\\tvar faces = [];\\r\\n\\tfor(var i = 0; i < 6; ++i)\\r\\n\\t\\tfaces.push( cubemap.getPixels(i) );\\r\\n\\r\\n\\tif(this.onPreprocessCubemap)\\r\\n\\t\\tthis.onPreprocessCubemap( faces, position, cubemap, index );\\r\\n\\r\\n\\tvar coeffs = computeSH( faces, cubemap.width, 4 );\\r\\n\\r\\n\\tif(this.onComputedSphericalHarmonics)\\r\\n\\t\\tthis.onComputedSphericalHarmonics( coeffs, position );\\r\\n\\r\\n\\treturn coeffs;\\r\\n}\\r\\n\\r\\n\\r\\nIrradianceCache.computeSH = function( cubemap )\\r\\n{\\r\\n\\t//read 6 images from cubemap\\r\\n\\tvar faces = [];\\r\\n\\tfor(var i = 0; i < 6; ++i)\\r\\n\\t\\tfaces.push( cubemap.getPixels(i) );\\r\\n\\r\\n\\tvar coeffs = computeSH( faces, cubemap.width, 4 );\\r\\n\\treturn coeffs;\\r\\n}\\r\\n\\r\\nIrradianceCache.prototype.encodeCacheInTexture = function()\\r\\n{\\r\\n\\tif(!this._irradiance_shs.length)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar sh_temp_texture_type = gl.FLOAT; //HIGH_PRECISION_FORMAT\\r\\n\\tvar sh_texture_type = gl.HIGH_PRECISION_FORMAT;\\r\\n\\tif( !GL.FBO.testSupport( sh_texture_type, gl.RGB ) )\\r\\n\\t\\tsh_texture_type = gl.FLOAT;\\r\\n\\r\\n\\t//create texture\\r\\n\\tif( !this._sh_texture || this._sh_texture.height != this._irradiance_shs.length || this._sh_texture.type != sh_texture_type )\\r\\n\\t{\\r\\n\\t\\tthis._sh_texture = new GL.Texture(9, this._irradiance_shs.length, { format: gl.RGB, type: sh_texture_type, magFilter: gl.NEAREST, minFilter: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE });\\r\\n\\t\\tLS.ResourcesManager.registerResource( \\\":IR_SHs\\\", this._sh_texture ); //debug\\r\\n\\t}\\r\\n\\r\\n\\t///prepare data\\r\\n\\tvar data = new Float32Array( this._irradiance_shs.length * 27 );\\r\\n\\tfor(var i = 0; i < this._irradiance_shs.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar shs = this._irradiance_shs[i];\\r\\n\\t\\tif(shs) //if you do not regenerate all there could be missing SHs\\r\\n\\t\\t\\tdata.set( shs, i*27 );\\r\\n\\t}\\r\\n\\t\\r\\n\\t//upload to GPU\\r\\n\\tif( sh_texture_type == sh_temp_texture_type )\\r\\n\\t\\tthis._sh_texture.uploadData( data, { no_flip: true }, true );\\r\\n\\telse \\r\\n\\t{\\r\\n\\t\\t//we cannot upload Float16 directly, so we use the trick of uploading at 32bits and copying to 16 bits\\r\\n\\t\\tif( !this._sh_temp_texture || this._sh_temp_texture.height != this._sh_temp_texture.length || this._sh_temp_texture.type != sh_temp_texture_type )\\r\\n\\t\\t\\tthis._sh_temp_texture = new GL.Texture(9, this._irradiance_shs.length, { format: gl.RGB, type: sh_temp_texture_type, magFilter: gl.NEAREST, minFilter: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE });\\r\\n\\t\\tthis._sh_temp_texture.uploadData( data, { no_flip: true }, true );\\r\\n\\t\\tthis._sh_temp_texture.copyTo( this._sh_texture );\\r\\n\\t}\\r\\n\\r\\n\\t//uniforms\\r\\n\\tvar matrix = this._uniforms.u_irradiance_imatrix;\\r\\n\\tmatrix.set( this._irradiance_matrix );\\r\\n\\tmat4.invert( matrix, matrix );\\r\\n}\\r\\n\\r\\n/*\\r\\nIrradianceCache.prototype.getIrradiance = function( position, normal, out )\\r\\n{\\t\\r\\n\\tout = out || vec3.create();\\r\\n\\r\\n\\tvar subs = this._irradiance_subdivisionsl;\\r\\n\\tvar imatrix = this._uniforms.u_irradiance_imatrix;\\r\\n\\tvar shs = this._irradiance_shs;\\r\\n\\tif(!shs)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar local_pos = vec3.create();\\r\\n\\tvec3.transformMat4( local_pos, position, imatrix );\\r\\n\\tMath.clamp( local_pos[0], 0, subs[0] - 1 );\\r\\n\\tMath.clamp( local_pos[1], 0, subs[1] - 1 );\\r\\n\\tMath.clamp( local_pos[2], 0, subs[2] - 1 );\\r\\n\\tvar floor_probes = subs[0] * subs[2];\\r\\n\\tvar total_probes = floor_probes * subs[1];\\r\\n\\tvar i = Math.floor(local_pos[0]) + Math.floor(local_pos[2]) * subs[0] + Math.floor(local_pos[1]) * floor_probes;\\r\\n\\tvar sh = shs[i];\\r\\n\\r\\n\\t//TODO: read coeffs\\r\\n\\treturn out;\\r\\n}\\r\\n*/\\r\\n\\r\\nIrradianceCache.prototype.getSizeInBytes = function()\\r\\n{\\r\\n\\treturn this._irradiance_shs.length * 27 * 4;//( this.high_precision ? 4 : 1 );\\r\\n}\\r\\n\\r\\n//helper\\r\\nIrradianceCache.prototype.showStats = function()\\r\\n{\\r\\n\\tvar max_coeffs = new Float32Array(9);\\r\\n\\tvar min_coeffs = new Float32Array(9);\\r\\n\\tvar avg_coeffs = new Float32Array(9);\\r\\n\\r\\n\\tfor( var i = 0; i < this._irradiance_shs.length; ++i)\\r\\n\\t{\\r\\n\\t}\\r\\n}\\r\\n\\r\\nIrradianceCache.prototype.fromData = function(data)\\r\\n{\\r\\n\\tif(!data)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar uint8view = new Uint8Array( data );\\r\\n\\r\\n\\tvar header_str = LS.typedArrayToString( uint8view.subarray(0,4) );\\r\\n\\tif( header_str != \\\"IR_C\\\" )\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"Irradiance data do not match\\\");\\r\\n\\t\\treturn false;\\r\\n\\t}\\r\\n\\r\\n\\tvar dv = new DataView(data);\\r\\n\\tvar subs = this._irradiance_subdivisions;\\r\\n\\tsubs[0] = dv.getUint8(4);\\r\\n\\tsubs[1] = dv.getUint8(5);\\r\\n\\tsubs[2] = dv.getUint8(6);\\r\\n\\tvar num_probes = subs[0] * subs[1] * subs[2];\\r\\n\\r\\n\\tvar float32view = new Float32Array( data, 16 );\\r\\n\\r\\n\\tthis._irradiance_matrix.set( float32view.subarray(0,16) );\\r\\n\\r\\n\\tvar shs = this._irradiance_shs;\\r\\n\\tshs.length = num_probes;\\r\\n\\tfor( var i = 0; i < shs.length; ++i)\\r\\n\\t\\tshs[i] = float32view.subarray( 16 + i*9*3, 16 + (i+1)*9*3 );\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\nIrradianceCache.prototype.toData = function()\\r\\n{\\r\\n\\tvar subs = this._irradiance_subdivisions;\\r\\n\\tvar num_probes = subs[0] * subs[1] * subs[2];\\r\\n\\tvar data = new ArrayBuffer( 16 + 16*4 + num_probes * 9 * 3 * 4); //16 bytes header + mat4x4 + probes(9,3 channels, float32)\\r\\n\\r\\n\\tvar uint8view = new Uint8Array( data );\\r\\n\\tuint8view.set( LS.stringToTypedArray(\\\"IR_C\\\"), 0 ); //from Irradiance Cache\\r\\n\\r\\n\\tvar dv = new DataView(data);\\r\\n\\tdv.setUint8(4,subs[0]);\\r\\n\\tdv.setUint8(5,subs[1]);\\r\\n\\tdv.setUint8(6,subs[2]);\\r\\n\\r\\n\\tvar float32view = new Float32Array( data, 16 );\\r\\n\\tfloat32view.set( this._irradiance_matrix );\\r\\n\\r\\n\\tvar shs = this._irradiance_shs;\\r\\n\\tfor( var i = 0; i < shs.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar sh = shs[i];\\r\\n\\t\\tif(sh)\\r\\n\\t\\t\\tfloat32view.set( sh, 16 + i*9*3 );\\r\\n\\t}\\r\\n\\r\\n\\treturn data;\\r\\n}\\r\\n\\r\\nIrradianceCache.prototype.renderEditor = function( is_selected )\\r\\n{\\r\\n\\tif(!this.enabled || !IrradianceCache.show_probes)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar shader = GL.Shader.getCubemapShowShader();\\r\\n\\tvar sh_shader = IrradianceCache.sh_shader;\\r\\n\\tif(!sh_shader)\\r\\n\\t\\tIrradianceCache.sh_shader = sh_shader = new GL.Shader( LS.Draw.vertex_shader_code, IrradianceCache.fs_shader_code );\\r\\n\\r\\n\\tvar mesh = LS.Renderer._sphere_mesh;\\r\\n\\tvar subs = this.subdivisions;\\r\\n\\tvar size = this.size;\\r\\n\\tvar iscale = vec3.fromValues( size[0]/subs[0], size[1]/subs[1], size[2]/subs[2] );\\r\\n\\r\\n\\tvar mesh = LS.Renderer._sphere_mesh;\\r\\n\\r\\n\\tvar default_cubemap = IrradianceCache.default_cubemap;\\r\\n\\tif(!default_cubemap)\\r\\n\\t\\tdefault_cubemap = IrradianceCache.default_cubemap = new GL.Texture(1,1,{ texture_type: GL.TEXTURE_CUBE_MAP, format: GL.RGB, pixel_data:[255,255,255] });\\r\\n\\r\\n\\tvar position = vec3.create();\\r\\n\\tvar matrix = this._irradiance_matrix;\\r\\n\\tmat4.identity( matrix );\\r\\n\\tif( this._root.transform )\\r\\n\\t\\tthis._root.transform.getGlobalMatrix( matrix );\\r\\n\\tmat4.scale( matrix, matrix, iscale );\\r\\n\\tvar start = mat4.multiplyVec3(vec3.create(),matrix,[0,0,0]);\\r\\n\\tvar end = mat4.multiplyVec3(vec3.create(),matrix,subs);\\r\\n\\tvar camera = LS.Renderer._current_camera;\\r\\n\\r\\n\\tvar i = 0;\\r\\n\\tfor(var y = 0; y < subs[1]; ++y)\\r\\n\\tfor(var z = 0; z < subs[2]; ++z)\\r\\n\\tfor(var x = 0; x < subs[0]; ++x)\\r\\n\\t{\\r\\n\\t\\tposition[0] = x + 0.5;\\r\\n\\t\\tposition[1] = y + 0.5;\\r\\n\\t\\tposition[2] = z + 0.5;\\r\\n\\t\\tmat4.multiplyVec3( position, matrix, position );\\r\\n\\r\\n\\t\\tif( camera && !camera.testSphereInsideFrustum( position, IrradianceCache.probes_size ) )\\r\\n\\t\\t{\\r\\n\\t\\t\\ti+=1;\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLS.Draw.push();\\r\\n\\t\\tLS.Draw.translate( position );\\r\\n\\t\\tLS.Draw.scale( IrradianceCache.probes_size );\\r\\n\\r\\n\\t\\tif(IrradianceCache.show_cubemaps )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar texture = this._irradiance_cubemaps[ i ] || default_cubemap;\\r\\n\\t\\t\\ttexture.bind(0);\\r\\n\\t\\t\\tLS.Draw.renderMesh( mesh, GL.TRIANGLES, shader );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar coeffs = this._irradiance_shs[i] || IrradianceCache.default_coeffs;\\r\\n\\t\\t\\tsh_shader.uniforms({ u_sh_coeffs: coeffs });\\r\\n\\t\\t\\tLS.Draw.renderMesh( mesh, GL.TRIANGLES, sh_shader );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tLS.Draw.pop();\\r\\n\\t\\ti++;\\r\\n\\t}\\r\\n\\r\\n\\t//gl.disable( gl.DEPTH_TEST );\\r\\n\\t//LS.Draw.renderLines([start,end],[[0,1,1,1],[1,1,1,1]]);\\r\\n\\t//gl.enable( gl.DEPTH_TEST );\\r\\n\\r\\n}\\r\\n\\r\\nLS.registerComponent( IrradianceCache );\\r\\n\\r\\nIrradianceCache.include_code = \\\"\\\\n\\\\\\r\\nconst float Pi = 3.141592654;\\\\n\\\\\\r\\nconst float CosineA0 = Pi;\\\\n\\\\\\r\\nconst float CosineA1 = (2.0 * Pi) / 3.0;\\\\n\\\\\\r\\nconst float CosineA2 = Pi * 0.25;\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nstruct SH9\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n float c[9];\\\\n\\\\\\r\\n};\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nstruct SH9Color\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n vec3 c[9];\\\\n\\\\\\r\\n};\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvoid SHCosineLobe(in vec3 dir, out SH9 sh)\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n // Band 0\\\\n\\\\\\r\\n sh.c[0] = 0.282095 * CosineA0;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n // Band 1\\\\n\\\\\\r\\n sh.c[1] = 0.488603 * dir.y * CosineA1;\\\\n\\\\\\r\\n sh.c[2] = 0.488603 * dir.z * CosineA1;\\\\n\\\\\\r\\n sh.c[3] = 0.488603 * dir.x * CosineA1;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n // Band 2\\\\n\\\\\\r\\n\\t#ifndef SH_LOW\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n sh.c[4] = 1.092548 * dir.x * dir.y * CosineA2;\\\\n\\\\\\r\\n sh.c[5] = 1.092548 * dir.y * dir.z * CosineA2;\\\\n\\\\\\r\\n sh.c[6] = 0.315392 * (3.0 * dir.z * dir.z - 1.0) * CosineA2;\\\\n\\\\\\r\\n sh.c[7] = 1.092548 * dir.x * dir.z * CosineA2;\\\\n\\\\\\r\\n sh.c[8] = 0.546274 * (dir.x * dir.x - dir.y * dir.y) * CosineA2;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvec3 ComputeSHIrradiance(in vec3 normal, in SH9Color radiance)\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n // Compute the cosine lobe in SH, oriented about the normal direction\\\\n\\\\\\r\\n SH9 shCosine;\\\\n\\\\\\r\\n\\tSHCosineLobe(normal, shCosine);\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n // Compute the SH dot product to get irradiance\\\\n\\\\\\r\\n vec3 irradiance = vec3(0.0);\\\\n\\\\\\r\\n\\t#ifndef SH_LOW\\\\n\\\\\\r\\n\\tconst int num = 9;\\\\n\\\\\\r\\n\\t#else\\\\n\\\\\\r\\n\\tconst int num = 4;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n for(int i = 0; i < num; ++i)\\\\n\\\\\\r\\n irradiance += radiance.c[i] * shCosine.c[i];\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n return irradiance;\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\\n\\\\\\r\\nvec3 ComputeSHDiffuse(in vec3 normal, in SH9Color radiance)\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n // Diffuse BRDF is albedo / Pi\\\\n\\\\\\r\\n return ComputeSHIrradiance( normal, radiance ) * (1.0 / Pi);\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nIrradianceCache.fs_shader_code = \\\"\\\\n\\\\\\r\\nprecision mediump float;\\\\n\\\\\\r\\n\\\" + IrradianceCache.include_code + \\\"\\\\n\\\\\\r\\nvarying vec3 v_normal;\\\\n\\\\\\r\\nuniform vec3 u_sh_coeffs[9];\\\\n\\\\\\r\\nvoid main()\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n\\tvec3 normal = normalize( v_normal );\\\\n\\\\\\r\\n\\tSH9Color coeffs;\\\\n\\\\\\r\\n\\tcoeffs.c[0] = u_sh_coeffs[0];\\\\n\\\\\\r\\n\\tcoeffs.c[1] = u_sh_coeffs[1];\\\\n\\\\\\r\\n\\tcoeffs.c[2] = u_sh_coeffs[2];\\\\n\\\\\\r\\n\\tcoeffs.c[3] = u_sh_coeffs[3];\\\\n\\\\\\r\\n\\tcoeffs.c[4] = u_sh_coeffs[4];\\\\n\\\\\\r\\n\\tcoeffs.c[5] = u_sh_coeffs[5];\\\\n\\\\\\r\\n\\tcoeffs.c[6] = u_sh_coeffs[6];\\\\n\\\\\\r\\n\\tcoeffs.c[7] = u_sh_coeffs[7];\\\\n\\\\\\r\\n\\tcoeffs.c[8] = u_sh_coeffs[8];\\\\n\\\\\\r\\n\\tgl_FragColor = vec4( max( vec3(0.001), ComputeSHDiffuse( normal, coeffs ) ), 1.0 );\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nvar cubemapFaceNormals = [\\r\\n [ [0, 0, -1], [0, -1, 0], [1, 0, 0] ], // posx\\r\\n [ [0, 0, 1], [0, -1, 0], [-1, 0, 0] ], // negx\\r\\n\\r\\n [ [1, 0, 0], [0, 0, 1], [0, 1, 0] ], // posy\\r\\n [ [1, 0, 0], [0, 0, -1], [0, -1, 0] ], // negy\\r\\n\\r\\n [ [1, 0, 0], [0, -1, 0], [0, 0, 1] ], // posz\\r\\n [ [-1, 0, 0], [0, -1, 0], [0, 0, -1] ] // negz\\r\\n]\\r\\n\\r\\n// give me a cubemap, its size and number of channels\\r\\n// and i'll give you spherical harmonics\\r\\nfunction computeSH( faces, cubemapSize, ch) {\\r\\n var size = cubemapSize || 128\\r\\n var channels = ch || 4\\r\\n var cubeMapVecs = []\\r\\n\\r\\n // generate cube map vectors\\r\\n faces.forEach( function(face, index) {\\r\\n var faceVecs = []\\r\\n for (var v = 0; v < size; v++) {\\r\\n for (var u = 0; u < size; u++) {\\r\\n var fU = (2.0 * u / (size - 1.0)) - 1.0\\r\\n var fV = (2.0 * v / (size - 1.0)) - 1.0\\r\\n\\r\\n var vecX = []\\r\\n vec3.scale(vecX, cubemapFaceNormals[index][0], fU)\\r\\n var vecY = []\\r\\n vec3.scale(vecY, cubemapFaceNormals[index][1], fV)\\r\\n var vecZ = cubemapFaceNormals[index][2]\\r\\n\\r\\n var res = []\\r\\n vec3.add(res, vecX, vecY)\\r\\n vec3.add(res, res, vecZ)\\r\\n vec3.normalize(res, res)\\r\\n\\r\\n faceVecs.push(res)\\r\\n }\\r\\n }\\r\\n cubeMapVecs.push(faceVecs)\\r\\n })\\r\\n\\r\\n // generate shperical harmonics\\r\\n var sh = [\\r\\n new Float32Array(3),\\r\\n new Float32Array(3),\\r\\n new Float32Array(3),\\r\\n new Float32Array(3),\\r\\n new Float32Array(3),\\r\\n new Float32Array(3),\\r\\n new Float32Array(3),\\r\\n new Float32Array(3),\\r\\n new Float32Array(3)\\r\\n ]\\r\\n var weightAccum = 0\\r\\n \\r\\n\\r\\n faces.forEach( function(face, index) {\\r\\n var pixels = face\\r\\n var gammaCorrect = true\\r\\n\\tvar low_precision = true\\r\\n if (Object.prototype.toString.call(pixels) === '[object Float32Array]')\\r\\n\\t{\\r\\n\\t\\tgammaCorrect = false // this is probably HDR image, already in linear space\\r\\n\\t\\tlow_precision = false;\\r\\n\\t}\\r\\n for (var y = 0; y < size; y++) {\\r\\n for (var x = 0; x < size; x++) {\\r\\n var texelVect = cubeMapVecs[index][y * size + x]\\r\\n\\r\\n var weight = texelSolidAngle(x, y, size, size)\\r\\n // forsyths weights\\r\\n var weight1 = weight * 4 / 17\\r\\n var weight2 = weight * 8 / 17\\r\\n var weight3 = weight * 15 / 17\\r\\n var weight4 = weight * 5 / 68\\r\\n var weight5 = weight * 15 / 68\\r\\n\\r\\n var dx = texelVect[0]\\r\\n var dy = texelVect[1]\\r\\n var dz = texelVect[2]\\r\\n\\r\\n for (var c = 0; c < 3; c++) {\\r\\n var value = pixels[y * size * channels + x * channels + c]\\r\\n\\t\\t if(low_precision)\\r\\n\\t\\t\\t value /= 255;\\r\\n if (gammaCorrect)\\r\\n\\t\\t\\t value = Math.pow(value, 2.2)\\r\\n\\t\\t //value = Math.clamp( value, 0, 2 );\\r\\n\\t\\r\\n\\t\\t sh[0][c] += value * weight1\\r\\n sh[1][c] += value * weight2 * dy\\r\\n sh[2][c] += value * weight2 * dz\\r\\n sh[3][c] += value * weight2 * dx\\r\\n\\r\\n sh[4][c] += value * weight3 * dx * dy\\r\\n sh[5][c] += value * weight3 * dy * dz\\r\\n sh[6][c] += value * weight4 * (3.0 * dz * dz - 1.0)\\r\\n\\r\\n sh[7][c] += value * weight3 * dx * dz\\r\\n sh[8][c] += value * weight5 * (dx * dx - dy * dy)\\r\\n\\r\\n weightAccum += weight\\r\\n }\\r\\n }\\r\\n }\\r\\n })\\r\\n\\r\\n var linear_sh = new Float32Array(sh.length*3);\\r\\n for (var i = 0; i < sh.length; i++) {\\r\\n linear_sh[i*3] = sh[i][0] *= 4 * Math.PI / weightAccum;\\r\\n linear_sh[i*3+1] = sh[i][1] *= 4 * Math.PI / weightAccum;\\r\\n linear_sh[i*3+2] = sh[i][2] *= 4 * Math.PI / weightAccum;\\r\\n }\\r\\n\\r\\n return linear_sh\\r\\n}\\r\\n\\r\\nfunction texelSolidAngle (aU, aV, width, height) {\\r\\n // transform from [0..res - 1] to [- (1 - 1 / res) .. (1 - 1 / res)]\\r\\n // ( 0.5 is for texel center addressing)\\r\\n var U = (2.0 * (aU + 0.5) / width) - 1.0\\r\\n var V = (2.0 * (aV + 0.5) / height) - 1.0\\r\\n\\r\\n // shift from a demi texel, mean 1.0 / size with U and V in [-1..1]\\r\\n var invResolutionW = 1.0 / width\\r\\n var invResolutionH = 1.0 / height\\r\\n\\r\\n // U and V are the -1..1 texture coordinate on the current face.\\r\\n // get projected area for this texel\\r\\n var x0 = U - invResolutionW\\r\\n var y0 = V - invResolutionH\\r\\n var x1 = U + invResolutionW\\r\\n var y1 = V + invResolutionH\\r\\n var angle = areaElement(x0, y0) - areaElement(x0, y1) - areaElement(x1, y0) + areaElement(x1, y1)\\r\\n\\r\\n return angle\\r\\n}\\r\\n\\r\\nfunction areaElement (x, y) {\\r\\n return Math.atan2(x * y, Math.sqrt(x * x + y * y + 1.0))\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n// IRRADIANCE SHADER BLOCK *************************************\\r\\nvar irradiance_code = \\\"\\\\n\\\\\\r\\n\\tuniform sampler2D irradiance_texture;\\\\n\\\\\\r\\n\\tuniform vec3 u_irradiance_subdivisions;\\\\n\\\\\\r\\n\\tuniform vec3 u_irradiance_color;\\\\n\\\\\\r\\n\\tuniform mat4 u_irradiance_imatrix;\\\\n\\\\\\r\\n\\t//uniform float u_irradiance_debug;\\\\n\\\\\\r\\n\\tuniform float u_irradiance_distance;\\\\n\\\\\\r\\n\\t\\\" + ( IrradianceCache.use_sh_low ? \\\"#define SH_LOW\\\" : \\\"\\\" ) + \\\"\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\t\\\" + IrradianceCache.include_code + \\\"\\\\n\\\\\\r\\n\\tvec3 computeSHRadianceAtLocalPos( in vec3 local_pos, in vec3 normal )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tfloat floor_probes = u_irradiance_subdivisions.x * u_irradiance_subdivisions.z;\\\\n\\\\\\r\\n\\t\\tfloat total_probes = floor_probes * u_irradiance_subdivisions.y;\\\\n\\\\\\r\\n\\t\\tfloat i = floor(local_pos.x) + floor(local_pos.z) * u_irradiance_subdivisions.x + floor(local_pos.y) * floor_probes;\\\\n\\\\\\r\\n\\t\\ti = (i+0.5) / (total_probes);\\\\n\\\\\\r\\n\\t\\tSH9Color coeffs;\\\\n\\\\\\r\\n\\t\\tcoeffs.c[0] = texture2D( irradiance_texture, vec2(0.5/9.0, i)).xyz;\\\\n\\\\\\r\\n\\t\\tcoeffs.c[1] = texture2D( irradiance_texture, vec2(1.5/9.0, i)).xyz;\\\\n\\\\\\r\\n\\t\\tcoeffs.c[2] = texture2D( irradiance_texture, vec2(2.5/9.0, i)).xyz;\\\\n\\\\\\r\\n\\t\\tcoeffs.c[3] = texture2D( irradiance_texture, vec2(3.5/9.0, i)).xyz;\\\\n\\\\\\r\\n\\t\\t#ifndef SH_LOW\\\\n\\\\\\r\\n\\t\\tcoeffs.c[4] = texture2D( irradiance_texture, vec2(4.5/9.0, i)).xyz;\\\\n\\\\\\r\\n\\t\\tcoeffs.c[5] = texture2D( irradiance_texture, vec2(5.5/9.0, i)).xyz;\\\\n\\\\\\r\\n\\t\\tcoeffs.c[6] = texture2D( irradiance_texture, vec2(6.5/9.0, i)).xyz;\\\\n\\\\\\r\\n\\t\\tcoeffs.c[7] = texture2D( irradiance_texture, vec2(7.5/9.0, i)).xyz;\\\\n\\\\\\r\\n\\t\\tcoeffs.c[8] = texture2D( irradiance_texture, vec2(8.5/9.0, i)).xyz;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\treturn max( vec3(0.001), ComputeSHDiffuse( normal, coeffs ) );\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tfloat irr_expFunc(float f)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\t//f = f*f*f*(f*(f*6.0-15.0)+10.0);\\\\n\\\\\\r\\n\\t\\t//if( f < 0.0 || f > 1.0 ) return 0.0;\\\\n\\\\\\r\\n\\t\\treturn f;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tvec3 computeSHRadianceAtPositionSmooth( in vec3 pos, in vec3 normal )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tvec3 local_pos = (u_irradiance_imatrix * vec4(pos + u_irradiance_distance * normal, 1.0)).xyz - vec3(0.5);\\\\n\\\\\\r\\n\\t\\tlocal_pos = clamp( local_pos, vec3(0.0), u_irradiance_subdivisions - vec3(1.0));\\\\n\\\\\\r\\n\\t\\tfloat fx = fract(local_pos.x);\\\\n\\\\\\r\\n\\t\\tfloat fy = 1.0 - fract(local_pos.y);\\\\n\\\\\\r\\n\\t\\tfloat fz = 1.0 - fract(local_pos.z);\\\\n\\\\\\r\\n\\t\\tfx = irr_expFunc(fx);\\\\n\\\\\\r\\n\\t\\tfy = irr_expFunc(fy);\\\\n\\\\\\r\\n\\t\\tfz = irr_expFunc(fz);\\\\n\\\\\\r\\n\\t\\tvec3 LTF = computeSHRadianceAtLocalPos( vec3( floor(local_pos.x), ceil(local_pos.y), ceil(local_pos.z)), normal );\\\\n\\\\\\r\\n\\t\\tvec3 LTB = computeSHRadianceAtLocalPos( vec3( floor(local_pos.x), ceil(local_pos.y), floor(local_pos.z)), normal );\\\\n\\\\\\r\\n\\t\\tvec3 RTF = computeSHRadianceAtLocalPos( vec3( ceil(local_pos.x), ceil(local_pos.y), ceil(local_pos.z)), normal );\\\\n\\\\\\r\\n\\t\\tvec3 RTB = computeSHRadianceAtLocalPos( vec3( ceil(local_pos.x), ceil(local_pos.y), floor(local_pos.z)), normal );\\\\n\\\\\\r\\n\\t\\tvec3 LBF = computeSHRadianceAtLocalPos( vec3( floor(local_pos.x), floor(local_pos.y), ceil(local_pos.z)) , normal);\\\\n\\\\\\r\\n\\t\\tvec3 LBB = computeSHRadianceAtLocalPos( vec3( floor(local_pos.x), floor(local_pos.y), floor(local_pos.z)), normal);\\\\n\\\\\\r\\n\\t\\tvec3 RBF = computeSHRadianceAtLocalPos( vec3( ceil(local_pos.x), floor(local_pos.y), ceil(local_pos.z)), normal );\\\\n\\\\\\r\\n\\t\\tvec3 RBB = computeSHRadianceAtLocalPos( vec3( ceil(local_pos.x), floor(local_pos.y), floor(local_pos.z)), normal);\\\\n\\\\\\r\\n\\t\\tvec3 LT = mix(LTF,LTB,fz);\\\\n\\\\\\r\\n\\t\\tvec3 LB = mix(LBF,LBB,fz);\\\\n\\\\\\r\\n\\t\\tvec3 L = mix(LT,LB,fy);\\\\n\\\\\\r\\n\\t\\tvec3 RT = mix(RTF,RTB,fz);\\\\n\\\\\\r\\n\\t\\tvec3 RB = mix(RBF,RBB,fz);\\\\n\\\\\\r\\n\\t\\tvec3 R = mix(RT,RB,fy);\\\\n\\\\\\r\\n\\t\\treturn mix(L,R,fx);\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tvec3 computeSHRadianceAtPosition( in vec3 pos, in vec3 normal )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tvec3 local_pos = (u_irradiance_imatrix * vec4(pos + u_irradiance_distance * normal, 1.0)).xyz - vec3(0.5);\\\\n\\\\\\r\\n\\t\\tlocal_pos = clamp( local_pos, vec3(0.0), u_irradiance_subdivisions - vec3(1.0));\\\\n\\\\\\r\\n\\t\\treturn computeSHRadianceAtLocalPos( local_pos, normal );\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvoid applyIrradiance( in Input IN, in SurfaceOutput o, inout FinalLight FINALLIGHT )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tFINALLIGHT.Ambient = o.Ambient * u_irradiance_color * computeSHRadianceAtPositionSmooth( IN.worldPos, o.Normal );\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nvar irradiance_disabled_code = \\\"\\\\n\\\\\\r\\n\\tvoid applyIrradiance( in Input IN, in SurfaceOutput o, inout FinalLight FINALLIGHT )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\n//uniform grid\\r\\nvar irradiance_block = new LS.ShaderBlock(\\\"applyIrradiance\\\");\\r\\nShaderMaterial.irradiance_block = irradiance_block;\\r\\nirradiance_block.addCode( GL.FRAGMENT_SHADER, irradiance_code, irradiance_disabled_code );\\r\\nirradiance_block.register( true );\\r\\n\\r\\nvar irradiance_single_code = \\\"\\\\n\\\\\\r\\n\\tuniform vec3 u_sh_coeffs[9];\\\\n\\\\\\r\\n\\t\\\" + IrradianceCache.include_code + \\\"\\\\n\\\\\\r\\n\\tvoid applyIrradiance( in Input IN, in SurfaceOutput o, inout FinalLight FINALLIGHT )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tSH9Color coeffs;\\\\n\\\\\\r\\n\\t\\tcoeffs.c[0] = u_sh_coeffs[0];\\\\n\\\\\\r\\n\\t\\tcoeffs.c[1] = u_sh_coeffs[1];\\\\n\\\\\\r\\n\\t\\tcoeffs.c[2] = u_sh_coeffs[2];\\\\n\\\\\\r\\n\\t\\tcoeffs.c[3] = u_sh_coeffs[3];\\\\n\\\\\\r\\n\\t\\tcoeffs.c[4] = u_sh_coeffs[4];\\\\n\\\\\\r\\n\\t\\tcoeffs.c[5] = u_sh_coeffs[5];\\\\n\\\\\\r\\n\\t\\tcoeffs.c[6] = u_sh_coeffs[6];\\\\n\\\\\\r\\n\\t\\tcoeffs.c[7] = u_sh_coeffs[7];\\\\n\\\\\\r\\n\\t\\tcoeffs.c[8] = u_sh_coeffs[8];\\\\n\\\\\\r\\n\\t\\tvec3 irr_color = ComputeSHDiffuse( o.Normal, coeffs );\\\\n\\\\\\r\\n\\t\\tFINALLIGHT.Ambient = o.Ambient * irr_color;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\n//single\\r\\nvar irradiance_single_block = new LS.ShaderBlock(\\\"applyIrradianceSingle\\\");\\r\\nShaderMaterial.irradiance_single_block = irradiance_single_block;\\r\\nirradiance_single_block.addCode( GL.FRAGMENT_SHADER, irradiance_single_code, irradiance_disabled_code );\\r\\nirradiance_single_block.register( true );\\r\\n\\r\\n\\r\\n///@FILE:../src/components/script.js\\r\\n///@INFO: SCRIPTS\\r\\n/** Script is the component in charge of executing scripts to control the behaviour of the application.\\r\\n* Script must be coded in Javascript and they have full access to all the engine, so one script could replace the behaviour of any part of the engine.\\r\\n* Scripts are executed inside their own context, the context is local to the script so any variable defined in the context that is not attached to the context wont be accessible from other parts of the engine.\\r\\n* To interact with the engine Scripts must bind callback to events so the callbacks will be called when those events are triggered, however, there are some generic methods that will be called\\r\\n* @class Script\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\nfunction Script(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.code = this.constructor.templates[\\\"script\\\"];\\r\\n\\tthis._blocked_functions = new Set(); //used to block functions that has errors\\r\\n\\tthis._name = \\\"\\\";\\r\\n\\r\\n\\tthis._script = new LScript();\\r\\n\\r\\n\\t//this are the methods that will be in the prototype of the script context by default\\r\\n\\tthis._script.extra_methods = {\\r\\n\\t\\tgetComponent: (function(type,index) { \\r\\n\\t\\t\\tif(!arguments.length)\\r\\n\\t\\t\\t\\treturn this;\\r\\n\\t\\t\\tif(!this._root)\\r\\n\\t\\t\\t\\treturn null;\\r\\n\\t\\t\\treturn this._root.getComponent(type,index)\\r\\n\\t\\t\\t}).bind(this),\\r\\n\\t\\tgetLocator: function() { return this.getComponent().getLocator() + \\\"/context\\\"; },\\r\\n\\t\\tcreateProperty: LS.BaseComponent.prototype.createProperty,\\r\\n\\t\\tcreateAction: LS.BaseComponent.prototype.createAction,\\r\\n\\t\\tbind: LS.BaseComponent.prototype.bind,\\r\\n\\t\\tunbind: LS.BaseComponent.prototype.unbind,\\r\\n\\t\\ttrigger: (function(event,param) { return LEvent.trigger( this, event, param); }).bind(this),\\r\\n\\t\\tunbindAll: LS.BaseComponent.prototype.unbindAll\\r\\n\\t};\\r\\n\\r\\n\\tthis._script.onerror = this.onError.bind(this);\\r\\n\\tthis._script.exported_functions = [];\\r\\n\\tthis._last_error = null;\\r\\n\\tthis._breakpoints = null;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nScript.secure_module = false; //this module is not secure (it can execute code)\\r\\nScript.block_execution = false; //avoid executing code\\r\\nScript.catch_important_exceptions = true; //catch exception during parsing, otherwise configuration could fail\\r\\n\\r\\nScript.icon = \\\"mini-icon-script.png\\\";\\r\\nScript.templates = {\\r\\n\\t\\\"script\\\":\\\"//@unnamed\\\\n//defined: component, node, scene, transform, globals\\\\nthis.onStart = function()\\\\n{\\\\n}\\\\n\\\\nthis.onUpdate = function(dt)\\\\n{\\\\n\\\\t//node.scene.refresh();\\\\n}\\\"\\r\\n};\\r\\n\\r\\nScript[\\\"@code\\\"] = {type:'script'};\\r\\n\\r\\n//used to determine to which object to bind\\r\\nScript.BIND_TO_COMPONENT = 1;\\r\\nScript.BIND_TO_NODE = 2;\\r\\nScript.BIND_TO_SCENE = 3;\\r\\nScript.BIND_TO_RENDERER = 4;\\r\\n\\r\\n//Here we specify which methods of the script will be automatically binded to events in the system\\r\\n//This way developing is much more easy as you dont have to bind or unbind anything\\r\\nScript.API_functions = {};\\r\\nScript.API_events_to_function = {};\\r\\n\\r\\nScript.defineAPIFunction = function( func_name, target, event, info ) {\\r\\n\\tevent = event || func_name;\\r\\n\\ttarget = target || Script.BIND_TO_SCENE;\\r\\n\\tvar data = { name: func_name, target: target, event: event, info: info };\\r\\n\\tScript.API_functions[ func_name ] = data;\\r\\n\\tScript.API_events_to_function[ event ] = data;\\r\\n}\\r\\n\\r\\n//init\\r\\nScript.defineAPIFunction( \\\"onStart\\\", Script.BIND_TO_SCENE, \\\"start\\\" );\\r\\nScript.defineAPIFunction( \\\"onAwake\\\", Script.BIND_TO_SCENE, \\\"awake\\\" );\\r\\nScript.defineAPIFunction( \\\"onFinish\\\", Script.BIND_TO_SCENE, \\\"finish\\\" );\\r\\nScript.defineAPIFunction( \\\"onPrefabReady\\\", Script.BIND_TO_NODE, \\\"prefabReady\\\" );\\r\\n//behaviour\\r\\nScript.defineAPIFunction( \\\"onUpdate\\\", Script.BIND_TO_SCENE, \\\"update\\\" );\\r\\nScript.defineAPIFunction( \\\"onFixedUpdate\\\", Script.BIND_TO_SCENE, \\\"fixedUpdate\\\" );\\r\\nScript.defineAPIFunction( \\\"onNodeClicked\\\", Script.BIND_TO_NODE, \\\"node_clicked\\\" );\\r\\nScript.defineAPIFunction( \\\"onClicked\\\", Script.BIND_TO_NODE, \\\"clicked\\\" );\\r\\n//rendering\\r\\nScript.defineAPIFunction( \\\"onSceneRender\\\", Script.BIND_TO_SCENE, \\\"beforeRender\\\" );\\r\\nScript.defineAPIFunction( \\\"onCollectRenderInstances\\\", Script.BIND_TO_NODE, \\\"collectRenderInstances\\\" ); //TODO: move to SCENE\\r\\nScript.defineAPIFunction( \\\"onRender\\\", Script.BIND_TO_SCENE, \\\"beforeRenderInstances\\\" );\\r\\nScript.defineAPIFunction( \\\"onAfterRender\\\", Script.BIND_TO_SCENE, \\\"afterRenderInstances\\\" );\\r\\nScript.defineAPIFunction( \\\"onAfterSceneRender\\\", Script.BIND_TO_SCENE, \\\"afterRender\\\" );\\r\\nScript.defineAPIFunction( \\\"onRenderHelpers\\\", Script.BIND_TO_SCENE, \\\"renderHelpers\\\" );\\r\\nScript.defineAPIFunction( \\\"onRenderGUI\\\", Script.BIND_TO_SCENE, \\\"renderGUI\\\" );\\r\\nScript.defineAPIFunction( \\\"onEnableFrameContext\\\", Script.BIND_TO_SCENE, \\\"enableFrameContext\\\" );\\r\\nScript.defineAPIFunction( \\\"onShowFrameContext\\\", Script.BIND_TO_SCENE, \\\"showFrameContext\\\" );\\r\\n//input\\r\\nScript.defineAPIFunction( \\\"onMouseDown\\\", Script.BIND_TO_SCENE, \\\"mousedown\\\" );\\r\\nScript.defineAPIFunction( \\\"onMouseMove\\\", Script.BIND_TO_SCENE, \\\"mousemove\\\" );\\r\\nScript.defineAPIFunction( \\\"onMouseUp\\\", Script.BIND_TO_SCENE, \\\"mouseup\\\" );\\r\\nScript.defineAPIFunction( \\\"onMouseWheel\\\", Script.BIND_TO_SCENE, \\\"mousewheel\\\" );\\r\\nScript.defineAPIFunction( \\\"onKeyDown\\\", Script.BIND_TO_SCENE, \\\"keydown\\\" );\\r\\nScript.defineAPIFunction( \\\"onKeyUp\\\", Script.BIND_TO_SCENE, \\\"keyup\\\" );\\r\\nScript.defineAPIFunction( \\\"onGamepadConnected\\\", Script.BIND_TO_SCENE, \\\"gamepadconnected\\\" );\\r\\nScript.defineAPIFunction( \\\"onGamepadDisconnected\\\", Script.BIND_TO_SCENE, \\\"gamepaddisconnected\\\" );\\r\\nScript.defineAPIFunction( \\\"onButtonDown\\\", Script.BIND_TO_SCENE, \\\"buttondown\\\" );\\r\\nScript.defineAPIFunction( \\\"onButtonUp\\\", Script.BIND_TO_SCENE, \\\"buttonup\\\" );\\r\\nScript.defineAPIFunction( \\\"onDragStart\\\", Script.BIND_TO_SCENE, \\\"dragstart\\\" );\\r\\n\\r\\n//global\\r\\nScript.defineAPIFunction( \\\"onFileDrop\\\", Script.BIND_TO_SCENE, \\\"fileDrop\\\" );\\r\\n//editor stuff\\r\\nScript.defineAPIFunction( \\\"onEditorEvent\\\", Script.BIND_TO_SCENE, \\\"editorEvent\\\" );\\r\\nScript.defineAPIFunction( \\\"onEditorRender\\\", Script.BIND_TO_SCENE, \\\"renderEditor\\\" );\\r\\nScript.defineAPIFunction( \\\"onEditorRenderGUI\\\", Script.BIND_TO_SCENE, \\\"renderEditorGUI\\\" );\\r\\n\\r\\nScript.coding_help = \\\"For a complete guide check: Scripting Guide\\\";\\r\\n\\r\\nScript.active_scripts = {};\\r\\n\\r\\nObject.defineProperty( Script.prototype, \\\"context\\\", {\\r\\n\\tset: function(v){ \\r\\n\\t\\tconsole.error(\\\"Script: context cannot be assigned\\\");\\r\\n\\t},\\r\\n\\tget: function() { \\r\\n\\t\\tif(this._script)\\r\\n\\t\\t\\t\\treturn this._script._context;\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\tenumerable: false //if it was enumerable it would be serialized\\r\\n});\\r\\n\\r\\nObject.defineProperty( Script.prototype, \\\"name\\\", {\\r\\n\\tset: function(v){ \\r\\n\\t\\tconsole.error(\\\"Script: name cannot be assigned, add to the first line //@name\\\");\\r\\n\\t},\\r\\n\\tget: function() { \\r\\n\\t\\treturn this._name;\\r\\n\\t},\\r\\n\\tenumerable: false //if it was enumerable it would be serialized\\r\\n});\\r\\n\\r\\nScript.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(o.uid)\\r\\n\\t\\tthis.uid = o.uid;\\r\\n\\tif(o.enabled !== undefined)\\r\\n\\t\\tthis.enabled = o.enabled;\\r\\n\\tif(o.code !== undefined)\\r\\n\\t\\tthis.code = o.code;\\r\\n\\r\\n\\tif(this._root && this._root.scene)\\r\\n\\t\\tthis.processCode();\\r\\n\\r\\n\\t//do this before processing the code if you want the script to overwrite the vars\\r\\n\\tif(o.properties)\\r\\n\\t\\t this.setContextProperties( o.properties );\\r\\n}\\r\\n\\r\\nScript.prototype.serialize = function()\\r\\n{\\r\\n\\treturn {\\r\\n\\t\\tobject_class: \\\"Script\\\",\\r\\n\\t\\tuid: this.uid,\\r\\n\\t\\tenabled: this.enabled,\\r\\n\\t\\tcode: this.code,\\r\\n\\t\\tproperties: LS.cloneObject( this.getContextProperties() )\\r\\n\\t};\\r\\n}\\r\\n\\r\\nScript.prototype.getContext = function()\\r\\n{\\r\\n\\tif(this._script)\\r\\n\\t\\treturn this._script._context;\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\nScript.prototype.getCode = function()\\r\\n{\\r\\n\\treturn this.code;\\r\\n}\\r\\n\\r\\nScript.prototype.setCode = function( code, skip_events, reset_state )\\r\\n{\\r\\n\\tthis.code = code;\\r\\n\\tthis._blocked_functions.clear();\\r\\n\\tthis.processCode( skip_events, reset_state );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Force to reevaluate the code (only for special situations)\\r\\n* @method reload\\r\\n*/\\r\\nScript.prototype.reload = function()\\r\\n{\\r\\n\\tthis.processCode();\\r\\n}\\r\\n\\r\\n/**\\r\\n* It will stop the execution when this method is called, but only if the console is open and the method is an event related method (onRender, onUpdate, etc)\\r\\n* @method setBreakpoint\\r\\n* @param {String} method_name name of the method to add breakpoint\\r\\n* @param {Boolean} value true to set, false to remove\\r\\n*/\\r\\nScript.prototype.setBreakpoint = function( method_name, value )\\r\\n{\\r\\n\\tif(!this._breakpoints && !value)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this._breakpoints)\\r\\n\\t\\tthis._breakpoints = {};\\r\\n\\r\\n\\tif(value)\\r\\n\\t\\tthis._breakpoints[ method_name ] = true;\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tdelete this._breakpoints[ method_name ];\\r\\n\\t\\tif( Object.keys(this._breakpoints).length == 0 )\\r\\n\\t\\t\\tthis._breakpoints = null;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* tells if there is a breakpoint set (using addBreakpoint) in this method\\r\\n* @method hasBreakpoint\\r\\n* @param {String} method_name name of the method to check if it has a breakpoint\\r\\n* @return {Boolean} true if there is a breakpoint\\r\\n*/\\r\\nScript.prototype.hasBreakpoint = function( method_name )\\r\\n{\\r\\n\\tif(!this._breakpoints)\\r\\n\\t\\treturn false;\\r\\n\\treturn this._breakpoints[ method_name ];\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* This is the method in charge of compiling the code and executing the constructor, which also creates the context.\\r\\n* It is called everytime the code is modified, that implies that the context is created when the component is configured.\\r\\n* @method processCode\\r\\n*/\\r\\nScript.prototype.processCode = function( skip_events, reset_state )\\r\\n{\\r\\n\\tthis._blocked_functions.clear();\\r\\n\\tthis._script.code = this.code;\\r\\n\\r\\n\\t//extract name\\r\\n\\tthis._name = \\\"\\\";\\r\\n\\tif(this.code)\\r\\n\\t{\\r\\n\\t\\tvar line = this.code.substr(0,128);\\r\\n\\t\\tif(line.indexOf(\\\"//@\\\") == 0)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar last = line.indexOf(\\\"\\\\n\\\");\\r\\n\\t\\t\\tif(last == -1)\\r\\n\\t\\t\\t\\tlast = undefined;\\r\\n\\t\\t\\tthis._name = line.substr(3,last - 3).trim();\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif( !this._root || LS.Script.block_execution || !LS.allow_scripts )\\r\\n\\t\\treturn true;\\r\\n\\r\\n\\t//unbind old stuff\\r\\n\\tif(this._script && this._script._context)\\r\\n\\t\\tthis._script._context.unbindAll();\\r\\n\\r\\n\\t//save old state\\r\\n\\tvar old = this._stored_properties || this.getContextProperties();\\r\\n\\r\\n\\t//compiles and executes the context\\r\\n\\tvar ret = this._script.compile({component:this, node: this._root, scene: this._root.scene, transform: this._root.transform, globals: LS.Globals });\\r\\n\\tif(!skip_events)\\r\\n\\t\\tthis.hookEvents();\\r\\n\\r\\n\\tif(!reset_state)\\r\\n\\t\\tthis.setContextProperties( old );\\r\\n\\r\\n\\tthis._stored_properties = null;\\r\\n\\r\\n\\t//execute some starter functions\\r\\n\\tif( this._script._context && !this._script._context._initialized )\\r\\n\\t{\\r\\n\\t\\tif( this._root && this._script._context.onAddedToNode)\\r\\n\\t\\t\\tthis._script._context.onAddedToNode( this._root );\\r\\n\\r\\n\\t\\tif( this._root && this._root.scene )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this._script._context.onAddedToScene )\\r\\n\\t\\t\\t\\tthis._script._context.onAddedToScene( this._root.scene );\\r\\n\\r\\n\\t\\t\\tif( this._script._context.onBind )\\r\\n\\t\\t\\t\\tthis._script._context.onBind( this._root.scene );\\r\\n\\r\\n\\t\\t\\tif( this._root.scene._state === LS.PLAYING && this._script._context.start )\\r\\n\\t\\t\\t\\tthis._script._context.start();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._script._context._initialized = true; //avoid initializing it twice\\r\\n\\t}\\r\\n\\r\\n\\tif( this._name && this._root && this._root.scene )\\r\\n\\t\\tLS.Script.active_scripts[ this._name ] = this;\\r\\n\\r\\n\\tconsole.log(\\\" + Script: \\\" + this._name + \\\" CTX: \\\", this._script._context );\\r\\n\\r\\n\\treturn ret;\\r\\n}\\r\\n\\r\\nScript.prototype.getContextProperties = function()\\r\\n{\\r\\n\\tvar ctx = this.getContext();\\r\\n\\tif(!ctx)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(this.onSerialize)\\r\\n\\t\\treturn this.onSerialize();\\r\\n\\treturn LS.cloneObject( ctx, null, false, false, true );\\r\\n}\\r\\n\\r\\nScript.prototype.setContextProperties = function( properties )\\r\\n{\\r\\n\\tif(!properties)\\r\\n\\t\\treturn;\\r\\n\\tvar ctx = this.getContext();\\r\\n\\tif(!ctx) //maybe the context hasnt been crated yet\\r\\n\\t{\\r\\n\\t\\tthis._stored_properties = properties;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//to copy we use the clone in target method\\r\\n\\tLS.cloneObject( properties, ctx, false, true, true );\\r\\n\\r\\n\\tif(ctx.onConfigure)\\r\\n\\t\\tctx.onConfigure( properties );\\r\\n}\\r\\n\\r\\n//used for graphs\\r\\nScript.prototype.setProperty = function(name, value)\\r\\n{\\r\\n\\tvar ctx = this.getContext();\\r\\n\\r\\n\\tif( ctx && ctx[name] !== undefined )\\r\\n\\t{\\r\\n\\t\\tif(ctx[name].set)\\r\\n\\t\\t\\tctx[name](value);\\r\\n\\t\\telse\\r\\n\\t\\t\\tctx[name] = value;\\r\\n\\t}\\r\\n\\telse if(this[name])\\r\\n\\t\\tthis[name] = value;\\r\\n}\\r\\n\\r\\n\\r\\nScript.prototype.getPropertiesInfo = function()\\r\\n{\\r\\n\\tvar ctx = this.getContext();\\r\\n\\r\\n\\tif(!ctx)\\r\\n\\t\\treturn {enabled:\\\"boolean\\\"};\\r\\n\\r\\n\\tvar attrs = LS.getObjectProperties( ctx );\\r\\n\\tattrs.enabled = \\\"boolean\\\";\\r\\n\\treturn attrs;\\r\\n}\\r\\n\\r\\nScript.prototype.onAction = function( action, params )\\r\\n{\\r\\n\\tvar ctx = this.getContext();\\r\\n\\tif(ctx.onAction)\\r\\n\\t\\treturn ctx.onAction( action, params );\\r\\n}\\r\\n\\r\\n/**\\r\\n* get actions that could be performed from graphs or animations\\r\\n* @method getActions\\r\\n*/\\r\\nScript.prototype.getActions = function(actions)\\r\\n{\\r\\n\\tvar ctx = this.getContext();\\r\\n\\tif(!ctx || !ctx.getActions)\\r\\n\\t\\treturn actions;\\r\\n\\r\\n\\tvar new_actions = ctx.getActions();\\r\\n\\tif(new_actions)\\r\\n\\t\\tfor(var i in new_actions)\\r\\n\\t\\t\\tactions[i] = new_actions[i];\\r\\n\\treturn actions;\\r\\n}\\r\\n\\r\\n/**\\r\\n* get events that could be triggered by this component\\r\\n* the result \\r\\n* @method getEvents\\r\\n* @return {Array} an array with the name of the events: [\\\"event_1\\\",\\\"event_2\\\", ...]\\r\\n*/\\r\\nScript.prototype.getEvents = function()\\r\\n{\\r\\n\\tvar ctx = this.getContext();\\r\\n\\tif(!ctx || !ctx.getEvents)\\r\\n\\t\\treturn null;\\r\\n\\treturn ctx.getEvents();\\r\\n}\\r\\n\\r\\n/*\\r\\nScript.prototype.getPropertyValue = function( property )\\r\\n{\\r\\n\\tvar ctx = this.getContext();\\r\\n\\tif(!ctx)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\treturn ctx[ property ];\\r\\n}\\r\\n\\r\\nScript.prototype.setPropertyValue = function( property, value )\\r\\n{\\r\\n\\tvar context = this.getContext();\\r\\n\\tif(!context)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( context[ property ] === undefined )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(context[ property ] && context[ property ].set)\\r\\n\\t\\tcontext[ property ].set( value );\\r\\n\\telse\\r\\n\\t\\tcontext[ property ] = value;\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n*/\\r\\n\\r\\n//used for animation tracks\\r\\nScript.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif(path[0] != \\\"context\\\")\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar context = this.getContext();\\r\\n\\r\\n\\tif(path.length == 1)\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tname:\\\"context\\\",\\r\\n\\t\\t\\tnode: this._root,\\r\\n\\t\\t\\ttarget: context,\\r\\n\\t\\t\\ttype: \\\"object\\\",\\r\\n\\t\\t\\tvalue: context\\r\\n\\t\\t};\\r\\n\\r\\n\\tvar varname = path[1];\\r\\n\\tif(!context || context[ varname ] === undefined )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar value = context[ varname ];\\r\\n\\tvar extra_info = context[ \\\"@\\\" + varname ];\\r\\n\\tif(!extra_info)\\r\\n\\t\\textra_info = context.constructor[ \\\"@\\\" + varname ];\\r\\n\\r\\n\\tvar type = \\\"\\\";\\r\\n\\tif(extra_info)\\r\\n\\t\\ttype = extra_info.type;\\r\\n\\r\\n\\tif(!type && value !== null && value !== undefined)\\r\\n\\t{\\r\\n\\t\\tif(value.constructor === String)\\r\\n\\t\\t\\ttype = \\\"string\\\";\\r\\n\\t\\telse if(value.constructor === Boolean)\\r\\n\\t\\t\\ttype = \\\"boolean\\\";\\r\\n\\t\\telse if(value.length)\\r\\n\\t\\t\\ttype = \\\"vec\\\" + value.length;\\r\\n\\t\\telse if(value.constructor === Number)\\r\\n\\t\\t\\ttype = \\\"number\\\";\\r\\n\\t\\telse if(value.constructor === Function)\\r\\n\\t\\t\\ttype = \\\"function\\\";\\r\\n\\t}\\r\\n\\r\\n\\tif(type == \\\"function\\\")\\r\\n\\t\\tvalue = varname; //just to avoid doing assignments of functions\\r\\n\\r\\n\\treturn {\\r\\n\\t\\tnode: this._root,\\r\\n\\t\\ttarget: context,\\r\\n\\t\\tname: varname,\\r\\n\\t\\tvalue: value,\\r\\n\\t\\ttype: type\\r\\n\\t};\\r\\n}\\r\\n\\r\\nScript.prototype.setPropertyValueFromPath = function( path, value, offset )\\r\\n{\\r\\n\\toffset = offset || 0;\\r\\n\\r\\n\\tif( path.length < (offset+1) )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(path[offset] != \\\"context\\\" )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar context = this.getContext();\\r\\n\\tvar varname = path[offset+1];\\r\\n\\tif(!context || context[ varname ] === undefined )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( context[ varname ] === undefined )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//cannot assign functions this way\\r\\n\\tif( context[ varname ] && context[ varname ].constructor == Function )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(context[ varname ] && context[ varname ].set)\\r\\n\\t\\tcontext[ varname ].set( value );\\r\\n\\telse\\r\\n\\t\\tcontext[ varname ] = value;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* This check if the context binds engine events to the methods in the context with an specific name.\\r\\n* This way we dont have to bind manually all the methods.\\r\\n* @method hookEvents\\r\\n*/\\r\\nScript.prototype.hookEvents = function()\\r\\n{\\r\\n\\tvar node = this._root;\\r\\n\\tif(!node)\\r\\n\\t\\tthrow(\\\"hooking events of a Script without a node\\\");\\r\\n\\tvar scene = node.scene || LS.GlobalScene; //hack\\r\\n\\r\\n\\t//script context\\r\\n\\tvar context = this.getContext();\\r\\n\\tif(!context)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//hook events\\r\\n\\tfor(var i in LS.Script.API_functions)\\r\\n\\t{\\r\\n\\t\\tvar func_name = i;\\r\\n\\t\\tvar event_info = LS.Script.API_functions[ func_name ];\\r\\n\\r\\n\\t\\tvar target = null;\\r\\n\\t\\tswitch( event_info.target )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase Script.BIND_TO_COMPONENT: target = this; break;\\r\\n\\t\\t\\tcase Script.BIND_TO_NODE: target = node; break;\\r\\n\\t\\t\\tcase Script.BIND_TO_SCENE: target = scene; break;\\r\\n\\t\\t\\tcase Script.BIND_TO_RENDERER: target = LS.Renderer; break;\\r\\n\\t\\t}\\r\\n\\t\\tif(!target)\\r\\n\\t\\t\\tthrow(\\\"Script event without target?\\\");\\r\\n\\r\\n\\t\\t//check if this function exist\\r\\n\\t\\tif( context[ func_name ] && context[ func_name ].constructor === Function )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( !LEvent.isBind( target, event_info.event, this.onScriptEvent, this ) ) //not already binded\\r\\n\\t\\t\\t\\tLEvent.bind( target, event_info.event, this.onScriptEvent, this );\\r\\n\\t\\t}\\r\\n\\t\\telse //if it doesnt ensure we are not binding the event\\r\\n\\t\\t\\tLEvent.unbind( target, event_info.event, this.onScriptEvent, this );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Called every time an event should be redirected to one function in the script context\\r\\n* @method onScriptEvent\\r\\n*/\\r\\nScript.prototype.onScriptEvent = function( event_type, params )\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//special case: sometimes we want to pass several parameters to the \\r\\n\\tvar expand = false;\\r\\n\\tvar type = event_type;\\r\\n\\t/*\\r\\n\\tif( type.constructor !== String && type.constructor !== Number ) //you can pass an event directly, in that case it will send all directly\\r\\n\\t{\\r\\n\\t\\ttype = event_type.type;\\r\\n\\t\\tparams = Array.prototype.slice.call(arguments, 0);\\r\\n\\t\\texpand = true;\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\tif(!type)\\r\\n\\t\\tthrow(\\\"Event without type\\\");\\r\\n\\r\\n\\tvar event_info = LS.Script.API_events_to_function[ type ];\\r\\n\\tif(!event_info)\\r\\n\\t\\treturn; //????\\r\\n\\r\\n\\tvar has_breakpoint = false;\\r\\n\\tif( this._breakpoints )\\r\\n\\t{\\r\\n\\t\\tif( this._breakpoints[ event_info.name ] )\\r\\n\\t\\t{\\r\\n\\t\\t\\thas_breakpoint = true;\\r\\n\\t\\t\\tthis.setBreakpoint( event_info.name, false );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif( this._blocked_functions.has( event_info.name ) ) //prevent calling code with errors\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"Script: blocked function trying to be executed, skipping: \\\" + event_info.name );\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tif(has_breakpoint)\\r\\n\\t\\t{{debugger}}; //stops the execution if the console is open\\r\\n\\r\\n\\tvar r = this._script.callMethod( event_info.name, params, expand, this );\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\nScript.prototype.onAddedToNode = function( node )\\r\\n{\\r\\n\\tif(!node.script)\\r\\n\\t\\tnode.script = this;\\r\\n}\\r\\n\\r\\nScript.prototype.onRemovedFromNode = function( node )\\r\\n{\\r\\n\\tif(this._script._context && this._script._context.onDestroy)\\r\\n\\t\\tthis._script._context.onDestroy();\\r\\n\\r\\n\\tif(node.script == this)\\r\\n\\t\\tdelete node.script;\\r\\n}\\r\\n\\r\\nScript.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\tif( this._name )\\r\\n\\t\\tLS.Script.active_scripts[ this._name ] = this;\\r\\n\\r\\n\\t//avoid to parse it again\\r\\n\\tif(this._script && this._script._context && this._script._context._initialized )\\r\\n\\t{\\r\\n\\t\\tif(this._script._context.onBind)\\r\\n\\t\\t\\tthis._script._context.onBind();\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tif( !this.constructor.catch_important_exceptions )\\r\\n\\t{\\r\\n\\t\\tthis.processCode();\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//catch\\r\\n\\ttry\\r\\n\\t{\\r\\n\\t\\t//careful, if the code saved had an error, do not block the flow of the configure or the rest will be lost\\r\\n\\t\\tthis.processCode();\\r\\n\\t}\\r\\n\\tcatch (err)\\r\\n\\t{\\r\\n\\t\\tconsole.error(err);\\r\\n\\t}\\r\\n\\r\\n\\r\\n}\\r\\n\\r\\nScript.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tif( this._name && LS.Script.active_scripts[ this._name ] == this )\\r\\n\\t\\tdelete LS.Script.active_scripts[ this._name ];\\r\\n\\r\\n\\t//ensures no binded events\\r\\n\\tLEvent.unbindAll( scene, this );\\r\\n\\tif( this._context )\\r\\n\\t{\\r\\n\\t\\tLEvent.unbindAll( scene, this._context, this );\\r\\n\\t\\tif(this._script._context.onUnbind )\\r\\n\\t\\t\\tthis._script._context.onUnbind( scene );\\r\\n\\t\\tif(this._script._context.onRemovedFromScene )\\r\\n\\t\\t\\tthis._context.onRemovedFromScene( scene );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//used in editor\\r\\nScript.prototype.getComponentTitle = function()\\r\\n{\\r\\n\\treturn this.name; //name is a getter that reads the name from the code comment\\r\\n}\\r\\n\\r\\nScript.prototype.toInfoString = function()\\r\\n{\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn LS.getObjectClassName( this );\\r\\n\\treturn LS.getObjectClassName( this ) + \\\" in node \\\" + this._root.name;\\r\\n}\\r\\n\\r\\n\\r\\n//TODO stuff ***************************************\\r\\n/*\\r\\nScript.prototype.onAddedToProject = function( project )\\r\\n{\\r\\n\\ttry\\r\\n\\t{\\r\\n\\t\\t//just in case the script saved had an error, do not block the flow\\r\\n\\t\\tthis.processCode();\\r\\n\\t}\\r\\n\\tcatch (err)\\r\\n\\t{\\r\\n\\t\\tconsole.error(err);\\r\\n\\t}\\r\\n}\\r\\n\\r\\nScript.prototype.onRemovedFromProject = function( project )\\r\\n{\\r\\n\\t//ensures no binded events\\r\\n\\tif(this._context)\\r\\n\\t\\tLEvent.unbindAll( project, this._context, this );\\r\\n\\r\\n\\t//unbind evends\\r\\n\\tLEvent.unbindAll( project, this );\\r\\n}\\r\\n*/\\r\\n//*******************************\\r\\n\\r\\nScript.prototype.onError = function(e)\\r\\n{\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\te.script = this;\\r\\n\\te.node = this._root;\\r\\n\\r\\n\\tconsole.warn(\\\"Script: blocking function on script due to error: \\\" + e.method_name );\\r\\n\\tthis._blocked_functions.add( e.method_name );\\r\\n\\r\\n\\tLEvent.trigger( this, \\\"code_error\\\",e);\\r\\n\\tLEvent.trigger( scene, \\\"code_error\\\",e);\\r\\n\\tLEvent.trigger( LS, \\\"code_error\\\",e);\\r\\n\\r\\n\\t//conditional this?\\r\\n\\tconsole.log(\\\"app finishing due to error in script, scene state is keep as it was during the error.\\\");\\r\\n\\tscene.finish();\\r\\n}\\r\\n\\r\\n//called from the editor?\\r\\nScript.prototype.onCodeChange = function(code)\\r\\n{\\r\\n\\tthis.processCode();\\r\\n}\\r\\n\\r\\nScript.prototype.getResources = function(res)\\r\\n{\\r\\n\\tvar ctx = this.getContext();\\r\\n\\r\\n\\tfor(var i in ctx)\\r\\n\\t{\\r\\n\\t\\tvar value = ctx[i];\\r\\n\\t\\tvar info = ctx.constructor[ \\\"@\\\" + i];\\r\\n\\t\\tif( !value || !info )\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t//for basic resource types\\r\\n\\t\\tif( LS.RESOURCE_TYPES[ info.type ] )\\r\\n\\t\\t\\tres[ value ] = true;\\r\\n\\r\\n\\t\\t//for arrays\\r\\n\\t\\tif( info.type == LS.TYPES.ARRAY && value.length && info.data_type && info.data_type.constructor === String && LS.RESOURCE_TYPES[ (info.data_type).toLowerCase() ] )\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var j = 0; j < value.length; ++j)\\r\\n\\t\\t\\t\\tres[ value[j] ] = true;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif(ctx && ctx.onGetResources )\\r\\n\\t\\tctx.onGetResources( res );\\r\\n}\\r\\n\\r\\nScript.prototype.onResourceRenamed = function( old_name, new_name, resource )\\r\\n{\\r\\n\\tvar ctx = this.getContext();\\r\\n\\tif(ctx && ctx.onResourceRenamed )\\r\\n\\t\\tctx.onResourceRenamed( old_name, new_name, resource );\\r\\n}\\r\\n\\r\\nLS.registerComponent( Script );\\r\\nLS.Script = Script;\\r\\n\\r\\n//*****************\\r\\n\\r\\nfunction ScriptFromFile(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis._filename = \\\"\\\";\\r\\n\\tthis._name = \\\"\\\";\\r\\n\\r\\n\\tthis._script = new LScript();\\r\\n\\tthis._blocked_functions = new Set(); //used to block functions that has errors\\r\\n\\r\\n\\tthis._script.extra_methods = {\\r\\n\\t\\tgetComponent: (function() { return this; }).bind(this),\\r\\n\\t\\tgetLocator: function() { return this.getComponent().getLocator() + \\\"/context\\\"; },\\r\\n\\t\\tcreateProperty: LS.BaseComponent.prototype.createProperty,\\r\\n\\t\\tcreateAction: LS.BaseComponent.prototype.createAction,\\r\\n\\t\\tbind: LS.BaseComponent.prototype.bind,\\r\\n\\t\\tunbind: LS.BaseComponent.prototype.unbind,\\r\\n\\t\\tunbindAll: LS.BaseComponent.prototype.unbindAll\\r\\n\\t};\\r\\n\\r\\n\\tthis._script.onerror = this.onError.bind(this);\\r\\n\\tthis._script.exported_functions = [];//this.constructor.exported_callbacks;\\r\\n\\tthis._last_error = null;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nScriptFromFile.coding_help = Script.coding_help;\\r\\n\\r\\nObject.defineProperty( ScriptFromFile.prototype, \\\"filename\\\", {\\r\\n\\tset: function(v){ \\r\\n\\t\\tif(v) //to avoid double slashes\\r\\n\\t\\t\\tv = LS.ResourcesManager.cleanFullpath( v );\\r\\n\\t\\tthis._filename = v;\\r\\n\\t\\tthis.processCode();\\r\\n\\t},\\r\\n\\tget: function() { \\r\\n\\t\\treturn this._filename;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( ScriptFromFile.prototype, \\\"context\\\", {\\r\\n\\tset: function(v){ \\r\\n\\t\\tconsole.error(\\\"ScriptFromFile: context cannot be assigned\\\");\\r\\n\\t},\\r\\n\\tget: function() { \\r\\n\\t\\tif(this._script)\\r\\n\\t\\t\\t\\treturn this._script._context;\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\tenumerable: false //if it was enumerable it would be serialized\\r\\n});\\r\\n\\r\\nObject.defineProperty( ScriptFromFile.prototype, \\\"name\\\", {\\r\\n\\tset: function(v){ \\r\\n\\t\\tconsole.error(\\\"Script: name cannot be assigned, set the first line with //@name\\\");\\r\\n\\t},\\r\\n\\tget: function() { \\r\\n\\t\\treturn this._name;\\r\\n\\t},\\r\\n\\tenumerable: false //if it was enumerable it would be serialized\\r\\n});\\r\\n\\r\\nScriptFromFile.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\t//avoid to parse it again\\r\\n\\tif(this._script && this._script._context && this._script._context._initialized )\\r\\n\\t{\\r\\n\\t\\tif( this._script._context.onBind )\\r\\n\\t\\t\\tthis._script._context.onBind( scene );\\r\\n\\t\\tif( this._script._context.onAddedToScene )\\r\\n\\t\\t\\tthis._script._context.onAddedToScene( scene );\\r\\n\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tif( !this.constructor.catch_important_exceptions )\\r\\n\\t{\\r\\n\\t\\tthis.processCode();\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//catch\\r\\n\\ttry\\r\\n\\t{\\r\\n\\t\\t//careful, if the code saved had an error, do not block the flow of the configure or the rest will be lost\\r\\n\\t\\tthis.processCode();\\r\\n\\t}\\r\\n\\tcatch (err)\\r\\n\\t{\\r\\n\\t\\tconsole.error(err);\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Force to reevaluate the code (only for special situations like remove codes)\\r\\n* @method reload\\r\\n* @param {Function} [on_complete=null] \\r\\n*/\\r\\nScriptFromFile.prototype.reload = function( on_complete )\\r\\n{\\r\\n\\tif(!this.filename)\\r\\n\\t\\treturn;\\r\\n\\tvar that = this;\\r\\n\\tLS.ResourcesManager.load( this.filename, null, function( res, url ){\\r\\n\\t\\tif( url != that.filename )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthat.processCode();\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete(that);\\r\\n\\t}, true);\\r\\n}\\r\\n\\r\\n\\r\\nScriptFromFile.prototype.processCode = function( skip_events, on_complete, reset_state )\\r\\n{\\r\\n\\tvar that = this;\\r\\n\\tif(!this.filename)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar script_resource = LS.ResourcesManager.getResource( this.filename );\\r\\n\\tif(!script_resource)\\r\\n\\t{\\r\\n\\t\\tLS.ResourcesManager.load( this.filename, null, function( res, url ){\\r\\n\\t\\t\\tif( url != that.filename )\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tthat.processCode( skip_events );\\r\\n\\t\\t\\tif(on_complete)\\r\\n\\t\\t\\t\\ton_complete(that);\\r\\n\\t\\t});\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar code = script_resource.data;\\r\\n\\tif( code === undefined)\\r\\n\\t{\\r\\n\\t\\tthis._name = \\\"\\\";\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tif( this._script.code == code )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t// ****** CODE PROCESSED ***********************\\r\\n\\r\\n\\t//extract name\\r\\n\\tthis._name = \\\"\\\";\\r\\n\\tif(code)\\r\\n\\t{\\r\\n\\t\\tvar line = code.substr(0,128);\\r\\n\\t\\tif(line.indexOf(\\\"//@\\\") == 0)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar last = line.indexOf(\\\"\\\\n\\\");\\r\\n\\t\\t\\tif(last == -1)\\r\\n\\t\\t\\t\\tlast = undefined;\\r\\n\\t\\t\\tthis._name = line.substr(3,last - 3).trim();\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif(!this._root || LS.Script.block_execution )\\r\\n\\t\\treturn true;\\r\\n\\r\\n\\t//assigned inside because otherwise if it gets modified before it is attached to the scene tree then it wont be compiled\\r\\n\\tthis._script.code = code;\\r\\n\\r\\n\\t//unbind old stuff\\r\\n\\tif( this._script && this._script._context )\\r\\n\\t\\tthis._script._context.unbindAll();\\r\\n\\r\\n\\t//compiles and executes the context\\r\\n\\tvar old = this._stored_properties || this.getContextProperties();\\r\\n\\tvar ret = this._script.compile({component:this, node: this._root, scene: this._root.scene, transform: this._root.transform, globals: LS.Globals });\\r\\n\\tif(!skip_events)\\r\\n\\t\\tthis.hookEvents();\\r\\n\\tif(!reset_state)\\r\\n\\t\\tthis.setContextProperties( old );\\r\\n\\tthis._stored_properties = null;\\r\\n\\r\\n\\t//try to catch up with all the events missed while loading the script\\r\\n\\tif( this._script._context && !this._script._context._initialized )\\r\\n\\t{\\r\\n\\t\\tif( this._root && this._script._context.onAddedToNode)\\r\\n\\t\\t\\tthis._script._context.onAddedToNode( this._root );\\r\\n\\r\\n\\t\\tif( this._root && this._root.scene )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( this._script._context.onAddedToScene )\\r\\n\\t\\t\\t\\tthis._script._context.onAddedToScene( this._root.scene );\\r\\n\\r\\n\\t\\t\\tif( this._script._context.onBind )\\r\\n\\t\\t\\t\\tthis._script._context.onBind( this._root.scene );\\r\\n\\r\\n\\t\\t\\tif( this._root.scene._state === LS.PLAYING && this._script._context.start )\\r\\n\\t\\t\\t\\tthis._script._context.start();\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._script._context._initialized = true; //avoid initializing it twice\\r\\n\\t}\\r\\n\\r\\n\\tif( this._name && this._root && this._root.scene )\\r\\n\\t\\tLS.Script.active_scripts[ this._name ] = this;\\r\\n\\r\\n\\tif(on_complete)\\r\\n\\t\\ton_complete(this);\\r\\n\\r\\n\\tconsole.log(\\\" + Script: \\\" + this._name + \\\" CTX: \\\", this._script._context );\\r\\n\\r\\n\\treturn ret;\\r\\n}\\r\\n\\r\\nScriptFromFile.prototype.configure = function(o)\\r\\n{\\r\\n\\tif(o.uid)\\r\\n\\t\\tthis.uid = o.uid;\\r\\n\\tif(o.enabled !== undefined)\\r\\n\\t\\tthis.enabled = o.enabled;\\r\\n\\tif(o.filename !== undefined)\\r\\n\\t\\tthis.filename = o.filename;\\r\\n\\tif(o.properties)\\r\\n\\t\\t this.setContextProperties( o.properties );\\r\\n\\r\\n\\tif(this._root && this._root.scene)\\r\\n\\t\\tthis.processCode();\\r\\n}\\r\\n\\r\\nScriptFromFile.prototype.serialize = function()\\r\\n{\\r\\n\\treturn {\\r\\n\\t\\tobject_class: \\\"ScriptFromFile\\\",\\r\\n\\t\\tuid: this.uid,\\r\\n\\t\\tenabled: this.enabled,\\r\\n\\t\\tfilename: this.filename,\\r\\n\\t\\tproperties: LS.cloneObject( this.getContextProperties() )\\r\\n\\t};\\r\\n}\\r\\n\\r\\nScriptFromFile.prototype.onAction = function( action, params )\\r\\n{\\r\\n\\tvar ctx = this.getContext();\\r\\n\\tif(ctx && ctx.onAction)\\r\\n\\t\\treturn ctx.onAction( action, params );\\r\\n}\\r\\n\\r\\nScriptFromFile.prototype.getActions = Script.prototype.getActions();\\r\\nScriptFromFile.prototype.getEvents = Script.prototype.getEvents();\\r\\n\\r\\nScriptFromFile.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif(this.filename)\\r\\n\\t\\tres[this.filename] = LS.Resource;\\r\\n\\r\\n\\t//script resources\\r\\n\\tvar ctx = this.getContext();\\r\\n\\tif(!ctx || !ctx.getResources )\\r\\n\\t\\treturn;\\r\\n\\tctx.getResources( res );\\r\\n}\\r\\n\\r\\nScriptFromFile.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.filename == old_name)\\r\\n\\t\\tthis.filename = new_name;\\r\\n}\\r\\n\\r\\nScriptFromFile.prototype.getCodeResource = function()\\r\\n{\\r\\n\\treturn LS.ResourcesManager.getResource( this.filename );\\r\\n}\\r\\n\\r\\n\\r\\nScriptFromFile.prototype.getCode = function()\\r\\n{\\r\\n\\tvar script_resource = LS.ResourcesManager.getResource( this.filename );\\r\\n\\tif(!script_resource)\\r\\n\\t\\treturn \\\"\\\";\\r\\n\\treturn script_resource.data;\\r\\n}\\r\\n\\r\\nScriptFromFile.prototype.setCode = function( code, skip_events, reset_state )\\r\\n{\\r\\n\\tvar script_resource = LS.ResourcesManager.getResource( this.filename );\\r\\n\\tif(!script_resource)\\r\\n\\t\\treturn \\\"\\\";\\r\\n\\tscript_resource.data = code;\\r\\n\\tthis.processCode( skip_events, null, reset_state );\\r\\n}\\r\\n\\r\\nScriptFromFile.updateComponents = function( script, skip_events )\\r\\n{\\r\\n\\tif( !script )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar fullpath = script.fullpath || script.filename;\\r\\n\\tvar scene = LS.GlobalScene;\\r\\n\\tvar components = scene.findNodeComponents( LS.ScriptFromFile );\\r\\n\\tfor(var i = 0; i < components.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar compo = components[i];\\r\\n\\t\\tif( compo.filename == fullpath )\\r\\n\\t\\t\\tcompo.processCode(skip_events);\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\nLS.extendClass( ScriptFromFile, Script );\\r\\n\\r\\nLS.registerComponent( ScriptFromFile );\\r\\nLS.ScriptFromFile = ScriptFromFile;\\r\\n\\r\\n\\r\\n///@FILE:../src/components/cloner.js\\r\\n///@INFO: UNCOMMON\\r\\nfunction Cloner(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\r\\n\\tthis.mode = Cloner.GRID_MODE;\\r\\n\\r\\n\\tthis.createProperty( \\\"count\\\", vec3.fromValues(10,1,1) );\\r\\n\\tthis.createProperty( \\\"size\\\", vec3.fromValues(100,100,100) );\\r\\n\\r\\n\\tthis.mesh = null;\\r\\n\\tthis.lod_mesh = null;\\r\\n\\tthis.material = null;\\r\\n\\r\\n\\tthis._custom_matrices = [];\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n\\r\\n\\tif(!Cloner._identity) //used to avoir garbage\\r\\n\\t\\tCloner._identity = mat4.create();\\r\\n}\\r\\n\\r\\nCloner.GRID_MODE = 1;\\r\\nCloner.RADIAL_MODE = 2;\\r\\nCloner.MESH_MODE = 3;\\r\\nCloner.CHILDREN_MODE = 4;\\r\\nCloner.CUSTOM_MODE = 5;\\r\\n\\r\\nCloner.icon = \\\"mini-icon-cloner.png\\\";\\r\\n\\r\\n//vars\\r\\nCloner[\\\"@mesh\\\"] = { type: \\\"mesh\\\" };\\r\\nCloner[\\\"@lod_mesh\\\"] = { type: \\\"mesh\\\" };\\r\\nCloner[\\\"@mode\\\"] = { type:\\\"enum\\\", values: { \\\"Grid\\\": Cloner.GRID_MODE, \\\"Radial\\\": Cloner.RADIAL_MODE, /* \\\"Mesh\\\": Cloner.MESH_MODE ,*/ \\\"Children\\\": Cloner.CHILDREN_MODE, \\\"Custom\\\": Cloner.CUSTOM_MODE } };\\r\\nCloner[\\\"@count\\\"] = { type:\\\"vec3\\\", min:1, step:1, precision: 0 };\\r\\n\\r\\nCloner.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind(scene, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n\\tLEvent.bind(scene, \\\"afterCollectData\\\", this.onUpdateInstances, this);\\r\\n}\\r\\n\\r\\n\\r\\nCloner.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind(scene, \\\"collectRenderInstances\\\", this.onCollectInstances, this);\\r\\n\\tLEvent.unbind(scene, \\\"afterCollectData\\\", this.onUpdateInstances, this);\\r\\n}\\r\\n\\r\\nCloner.prototype.getMesh = function() {\\r\\n\\tif( this.mesh && this.mesh.constructor === String )\\r\\n\\t\\treturn ResourcesManager.meshes[ this.mesh ];\\r\\n\\treturn this.mesh;\\r\\n}\\r\\n\\r\\nCloner.prototype.getLODMesh = function() {\\r\\n\\tif( this.lod_mesh && this.lod_mesh.constructor === String )\\r\\n\\t\\treturn ResourcesManager.meshes[this.lod_mesh];\\r\\n\\treturn this.lod_mesh;\\r\\n}\\r\\n\\r\\nCloner.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif( this.mesh && this.mesh.constructor === String )\\r\\n\\t\\tres[this.mesh] = Mesh;\\r\\n\\tif( this.lod_mesh && this.lod_mesh.constructor === String )\\r\\n\\t\\tres[this.lod_mesh] = Mesh;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nCloner.prototype.onResourceRenamed = function( old_name, new_name, resource )\\r\\n{\\r\\n\\tif( this.mesh == old_name )\\r\\n\\t\\tthis.mesh = new_name;\\r\\n\\r\\n\\tif( this.lod_mesh == old_name )\\r\\n\\t\\tthis.lod_mesh = new_name;\\r\\n}\\r\\n\\r\\nCloner.generateTransformKey = function(count, hsize, offset)\\r\\n{\\r\\n\\tvar key = new Float32Array(9);\\r\\n\\tkey.set(count);\\r\\n\\tkey.set(hsize,3);\\r\\n\\tkey.set(offset,6);\\r\\n\\treturn key;\\r\\n}\\r\\n\\r\\nCloner.compareKeys = function(a,b)\\r\\n{\\r\\n\\tfor(var i = 0; i < a.length; ++i)\\r\\n\\t\\tif(a[i] != b[i])\\r\\n\\t\\t\\treturn false;\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n\\r\\nCloner.prototype.onCollectInstances = function(e, instances)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar mesh = this.getMesh();\\r\\n\\tif(!mesh) \\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar node = this._root;\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis.updateRenderInstancesArray();\\r\\n\\r\\n\\tvar RIs = this._RIs;\\r\\n\\tvar material = this.material || this._root.getMaterial();\\r\\n\\tvar flags = 0;\\r\\n\\r\\n\\tif(!RIs)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//resize the instances array to fit the new RIs (avoids using push)\\r\\n\\tvar start_array_pos = instances.length;\\r\\n\\tinstances.length = start_array_pos + RIs.length;\\r\\n\\r\\n\\t//update parameters\\r\\n\\tfor(var i = 0, l = RIs.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar RI = RIs[i];\\r\\n\\r\\n\\t\\tRI.setMesh(mesh);\\r\\n\\t\\tRI.layers = node.layers;\\r\\n\\t\\tRI.setMaterial( material );\\r\\n\\t\\tinstances[ start_array_pos + i ] = RI;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nCloner.prototype.updateRenderInstancesArray = function()\\r\\n{\\r\\n\\tvar total = 0;\\r\\n\\tif(this.mode === Cloner.GRID_MODE)\\r\\n\\t\\ttotal = (this.count[0]|0) * (this.count[1]|0) * (this.count[2]|0);\\r\\n\\telse if(this.mode === Cloner.RADIAL_MODE)\\r\\n\\t\\ttotal = this.count[0]|0;\\r\\n\\telse if(this.mode === Cloner.MESH_MODE)\\r\\n\\t{\\r\\n\\t\\ttotal = 0; //TODO\\r\\n\\t}\\r\\n\\telse if(this.mode === Cloner.CHILDREN_MODE)\\r\\n\\t{\\r\\n\\t\\tif(this._root && this._root._children)\\r\\n\\t\\t\\ttotal = this._root._children.length;\\r\\n\\t}\\r\\n\\r\\n\\tif(!total) \\r\\n\\t{\\r\\n\\t\\tif(this._RIs)\\r\\n\\t\\t\\tthis._RIs.length = 0;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tif(!this._RIs || this._RIs.length != total)\\r\\n\\t{\\r\\n\\t\\t//create RIs\\r\\n\\t\\tif(!this._RIs)\\r\\n\\t\\t\\tthis._RIs = new Array(total);\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis._RIs.length = total;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < total; ++i)\\r\\n\\t\\t\\tif(!this._RIs[i])\\r\\n\\t\\t\\t\\tthis._RIs[i] = new LS.RenderInstance(this._root, this);\\r\\n\\t}\\r\\n}\\r\\n\\r\\nCloner.prototype.onUpdateInstances = function(e, dt)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar RIs = this._RIs;\\r\\n\\tif(!RIs || !RIs.length)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar global = this._root.transform.getGlobalMatrix(mat4.create());\\r\\n\\r\\n\\tvar countx = this._count[0]|0;\\r\\n\\tvar county = this._count[1]|0;\\r\\n\\tvar countz = this._count[2]|0;\\r\\n\\r\\n\\tvar node = this._root;\\r\\n\\r\\n\\t//Set position according to the cloner mode\\r\\n\\tif(this.mode == Cloner.GRID_MODE)\\r\\n\\t{\\r\\n\\t\\t//compute offsets\\r\\n\\t\\tvar hsize = vec3.scale( vec3.create(), this.size, 0.5 );\\r\\n\\t\\tvar offset = vec3.create();\\r\\n\\t\\tif( countx > 1) offset[0] = this.size[0] / ( countx - 1);\\r\\n\\t\\telse hsize[0] = 0;\\r\\n\\t\\tif( county > 1) offset[1] = this.size[1] / ( county - 1);\\r\\n\\t\\telse hsize[1] = 0;\\r\\n\\t\\tif( countz > 1) offset[2] = this.size[2] / ( countz - 1);\\r\\n\\t\\telse hsize[2] = 0;\\r\\n\\r\\n\\t\\tvar i = 0;\\r\\n\\t\\tvar tmp = vec3.create(), zero = vec3.create();\\r\\n\\t\\tfor(var x = 0; x < countx; ++x)\\r\\n\\t\\tfor(var y = 0; y < county; ++y)\\r\\n\\t\\tfor(var z = 0; z < countz; ++z)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar RI = RIs[i];\\r\\n\\t\\t\\tif(!RI)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\ttmp[0] = x * offset[0] - hsize[0];\\r\\n\\t\\t\\ttmp[1] = y * offset[1] - hsize[1];\\r\\n\\t\\t\\ttmp[2] = z * offset[2] - hsize[2];\\r\\n\\t\\t\\tmat4.translate( RI.matrix, global, tmp );\\r\\n\\t\\t\\tRI.setMatrix( RI.matrix ); //force normal matrix generation\\r\\n\\t\\t\\tmat4.multiplyVec3( RI.center, RI.matrix, zero );\\r\\n\\t\\t\\t++i;\\r\\n\\t\\t\\tRI.picking_node = null;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(this.mode == Cloner.RADIAL_MODE)\\r\\n\\t{\\r\\n\\t\\tvar offset = Math.PI * 2 / RIs.length;\\r\\n\\t\\tvar tmp = vec3.create(), zero = vec3.create();\\r\\n\\t\\tfor(var i = 0, l = RIs.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar RI = RIs[i];\\r\\n\\t\\t\\tif(!RI)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t\\ttmp[0] = Math.sin( offset * i ) * this.size[0];\\r\\n\\t\\t\\ttmp[1] = 0;\\r\\n\\t\\t\\ttmp[2] = Math.cos( offset * i ) * this.size[0];\\r\\n\\t\\t\\tRI.matrix.set( global );\\r\\n\\t\\t\\tmat4.translate( RI.matrix, RI.matrix, tmp );\\r\\n\\t\\t\\tmat4.rotateY( RI.matrix,RI.matrix, offset * i );\\r\\n\\t\\t\\tRI.setMatrix( RI.matrix ); //force normal matrix generation\\r\\n\\t\\t\\tmat4.multiplyVec3( RI.center, RI.matrix, zero );\\r\\n\\t\\t\\tRI.picking_node = null;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if(this.mode == Cloner.CHILDREN_MODE)\\r\\n\\t{\\r\\n\\t\\tif(!this._root || !this._root._children)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tfor(var i = 0, l = RIs.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar RI = RIs[i];\\r\\n\\t\\t\\tif(!RI)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tvar childnode = this._root._children[i];\\r\\n\\t\\t\\tif(!childnode)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif( childnode.transform )\\r\\n\\t\\t\\t\\tchildnode.transform.getGlobalMatrix( global );\\r\\n\\t\\t\\tRI.setMatrix( global );\\r\\n\\t\\t\\tRI.picking_node = childnode;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\nLS.registerComponent(Cloner);\\r\\n///@FILE:../src/components/poser.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Transitions between different poses\\r\\n* @class Poser\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\nfunction Poser(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\r\\n\\tthis.only_internal_nodes = true;\\r\\n\\tthis.base_nodes = []; //uids and original transform of the nodes affected by the poser\\r\\n\\tthis.poses = []; //every pose contains \\r\\n\\tthis._poses_by_name = {};\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nPoser.icon = \\\"mini-icon-clock.png\\\";\\r\\n\\r\\nObject.defineProperty( Poser.prototype, \\\"weights\\\", {\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(!v || !v.length)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tfor(var i = 0; i < v.length; ++i)\\r\\n\\t\\t\\tif( this.poses[i] )\\r\\n\\t\\t\\t\\tthis.poses[i].weight = v[i] || 0;\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\tvar result = new Array( this.poses.length );\\r\\n\\t\\tfor(var i = 0; i < this.poses.length; ++i)\\r\\n\\t\\t\\tresult[i] = this.poses[i].weight;\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\tenumeration: false\\r\\n});\\r\\n\\r\\n//object with name:weight\\r\\nObject.defineProperty( Poser.prototype, \\\"name_weights\\\", {\\r\\n\\tset: function(v) {\\r\\n\\t\\tif(!v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tfor(var i in v)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pose = this._poses_by_name[i];\\r\\n\\t\\t\\tif(pose)\\r\\n\\t\\t\\t\\tpose.weight = Number(v[i]);\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\tvar result = {};\\r\\n\\t\\tfor(var i = 0; i < this.poses.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pose = this.poses[i];\\r\\n\\t\\t\\tresult[ pose.name ] = pose.weight;\\r\\n\\t\\t}\\r\\n\\t\\treturn result;\\r\\n\\t},\\r\\n\\tenumeration: false\\r\\n});\\r\\n\\r\\nPoser.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\tLEvent.bind(scene,\\\"update\\\",this.onUpdate, this);\\r\\n}\\r\\n\\r\\nPoser.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind(scene,\\\"update\\\",this.onUpdate, this);\\r\\n}\\r\\n\\r\\nPoser.prototype.onUpdate = function(e, dt)\\r\\n{\\r\\n\\tif(!this.enabled || !this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis.applyPoseFromWeights();\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(scene)\\r\\n\\t\\tscene.requestFrame();\\r\\n}\\r\\n\\r\\nPoser.prototype.addBaseNode = function( node )\\r\\n{\\r\\n\\tvar node_data = null;\\r\\n\\tvar uid = node.uid;\\r\\n\\r\\n\\t//check if it is already in this.base_nodes\\r\\n\\tfor(var i = 0; i < this.base_nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar v = this.base_nodes[i];\\r\\n\\t\\tif( v.node_uid != uid )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tnode_data = v;\\r\\n\\t\\tbreak;\\r\\n\\t}\\r\\n\\r\\n\\t//add new base node\\r\\n\\tif(!node_data)\\r\\n\\t{\\r\\n\\t\\tnode_data = { \\r\\n\\t\\t\\tnode_uid: uid, \\r\\n\\t\\t\\tposition: [0,0,0],\\r\\n\\t\\t\\trotation: [0,0,0,1],\\r\\n\\t\\t\\tscaling: [1,1,1]\\r\\n\\t\\t};\\r\\n\\t\\tthis.base_nodes.push( node_data );\\r\\n\\t}\\r\\n\\t\\r\\n\\tif(node.transform)\\r\\n\\t{\\r\\n\\t\\tvec3.copy( node_data.position, node.transform._position );\\r\\n\\t\\tquat.copy( node_data.rotation, node.transform._rotation );\\r\\n\\t\\tvec3.copy( node_data.scaling, node.transform._scaling );\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPoser.prototype.removeBaseNode = function( node )\\r\\n{\\r\\n\\tif(!node)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(node.constructor === String)\\r\\n\\t\\tnode = this._root.scene.getNode( node );\\r\\n\\r\\n\\tif(!node)\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"Node not found\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//check if it is already in this.base_nodes\\r\\n\\tfor(var i = 0; i < this.base_nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar v = this.base_nodes[i];\\r\\n\\t\\tif( v.node_uid != node.uid )\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tthis.base_nodes.splice(i,1);\\r\\n\\t\\tthis.purgePoses();\\r\\n\\t\\tbreak;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPoser.prototype.setChildrenAsBaseNodes = function( include_root )\\r\\n{\\r\\n\\tthis.base_nodes.length = 0;\\r\\n\\tif(include_root)\\r\\n\\t\\tthis.addBaseNode( this._root );\\r\\n\\tvar descendants = this._root.getDescendants();\\r\\n\\tfor(var i = 0; i < descendants.length; ++i)\\r\\n\\t\\tthis.addBaseNode( descendants[i] );\\r\\n}\\r\\n\\r\\nPoser.prototype.addPose = function( name )\\r\\n{\\r\\n\\tvar pose = {\\r\\n\\t\\tname: name,\\r\\n\\t\\tweight: 0,\\r\\n\\t\\tnodes: []\\r\\n\\t};\\r\\n\\tthis.poses.push( pose );\\r\\n\\tthis._poses_by_name[ name ] = pose;\\r\\n\\tthis.updatePose( name );\\r\\n}\\r\\n\\r\\nPoser.prototype.removePose = function( name )\\r\\n{\\r\\n\\tvar pose = this._poses_by_name[ name ];\\r\\n\\tif(!pose)\\r\\n\\t\\treturn;\\r\\n\\tvar index = this.poses.indexOf(pose);\\r\\n\\tif(index != -1)\\r\\n\\t\\tthis.poses.splice(index,1);\\r\\n\\tdelete this._poses_by_name[ name ];\\r\\n}\\r\\n\\r\\nPoser.prototype.setPoseWeight = function( name, weight )\\r\\n{\\r\\n\\tvar pose = this._poses_by_name[ name ];\\r\\n\\tif(pose)\\r\\n\\t\\tpose.weight = weight;\\r\\n}\\r\\n\\r\\n\\r\\n//updates the transform of a pose using the current nodes transform\\r\\nPoser.prototype.updatePose = function( name )\\r\\n{\\r\\n\\tif(!this._root || !this._root.scene) //could happen\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar pose = this._poses_by_name[ name ];\\r\\n\\tif(!pose)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tpose.nodes.length = 0; //clear\\r\\n\\r\\n\\tvar delta_pos = vec3.create();\\r\\n\\tvar delta_rot = quat.create();\\r\\n\\tvar delta_scale = vec3.create();\\r\\n\\r\\n\\tfor(var i = 0; i < this.base_nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar base_node_info = this.base_nodes[i];\\r\\n\\t\\tvar node = scene.getNode( base_node_info.node_uid );\\r\\n\\t\\tif(!node)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"addPose error, node not found in scene\\\");\\r\\n\\t\\t\\tcontinue; \\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//compute diff\\r\\n\\t\\tvec3.sub( delta_pos, node.transform._position, base_node_info.position );\\r\\n\\r\\n\\t\\tquat.invert( delta_rot, node.transform._rotation );\\r\\n\\t\\tquat.mul( delta_rot, base_node_info.rotation, delta_rot );\\r\\n\\t\\tquat.invert( delta_rot, delta_rot );\\r\\n\\r\\n\\t\\tvec3.div( delta_scale, node.transform._scaling, base_node_info.scaling );\\r\\n\\r\\n\\t\\tvar pose_info = {\\r\\n\\t\\t\\tnode_uid: node.uid\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\t//if they are below threshold, do not store deltas\\r\\n\\t\\tif( vec3.length(delta_pos) > 0.00001 )\\r\\n\\t\\t\\tpose_info.delta_pos = toArray( delta_pos );\\r\\n\\t\\tif( vec4.dist(delta_rot,LS.QUAT_IDENTITY) > 0.0001 )\\r\\n\\t\\t\\tpose_info.delta_rot = toArray( delta_rot );\\r\\n\\t\\tif( Math.abs(vec3.length(delta_scale) - 1.0) > 0.00001 )\\r\\n\\t\\t\\tpose_info.delta_scale = toArray( delta_scale );\\r\\n\\r\\n\\t\\tpose.nodes.push( pose_info );\\r\\n\\t}\\r\\n\\r\\n\\treturn pose;\\r\\n}\\r\\n\\r\\nPoser.prototype.applyBasePose = function()\\r\\n{\\r\\n\\tif( !this._root || !this._root.scene )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < this.base_nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar base_node_info = this.base_nodes[i];\\r\\n\\t\\tvar node = scene.getNode( base_node_info.node_uid );\\r\\n\\t\\tif(!node || !node.transform)\\r\\n\\t\\t\\tcontinue; \\r\\n\\t\\tnode.transform.position = base_node_info.position;\\r\\n\\t\\tnode.transform.rotation = base_node_info.rotation;\\r\\n\\t\\tnode.transform.scaling = base_node_info.scaling;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPoser.prototype.applyPose = function(name, skip_reset)\\r\\n{\\r\\n\\tif( !this._root || !this._root.scene )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar pose = this._poses_by_name[name];\\r\\n\\tif(!pose)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < this.base_nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar base_node_info = this.base_nodes[i];\\r\\n\\t\\tvar node = scene.getNode( base_node_info.node_uid );\\r\\n\\t\\tif(!node || !node.transform)\\r\\n\\t\\t\\tcontinue; \\r\\n\\r\\n\\t\\tvar pose_node_info = pose.nodes[i];\\r\\n\\t\\tif(!pose_node_info)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!skip_reset)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnode.transform.position = base_node_info.position;\\r\\n\\t\\t\\t\\tnode.transform.rotation = base_node_info.rotation;\\r\\n\\t\\t\\t\\tnode.transform.scaling = base_node_info.scaling;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( pose_node_info.delta_pos )\\r\\n\\t\\t\\tvec3.add( node.transform._position, skip_reset ? node.transform._position : base_node_info.position, pose_node_info.delta_pos );\\r\\n\\t\\telse if(!skip_reset)\\r\\n\\t\\t\\tnode.transform.position = base_node_info.position;\\r\\n\\r\\n\\t\\tif( pose_node_info.delta_rot )\\r\\n\\t\\t\\tquat.mul( node.transform._rotation, pose_node_info.delta_rot, skip_reset ? node.transform._rotation : base_node_info.rotation );\\r\\n\\t\\telse if(!skip_reset)\\r\\n\\t\\t\\tnode.transform.rotation = base_node_info.rotation;\\r\\n\\r\\n\\t\\t/*\\r\\n\\t\\tif( pose_node_info.delta_scale )\\r\\n\\t\\t\\tvec3.mul( node.transform._scaling, skip_reset ? node.transform._scaling : base_node_info.scaling, pose_node_info.delta_scale );\\r\\n\\t\\telse if(!skip_reset)\\r\\n\\t\\t\\tnode.transform.scaling = base_node_info.scaling;\\r\\n\\t\\t*/\\r\\n\\r\\n\\t\\tnode.transform._must_update = true;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPoser.temp_quat = quat.create();\\r\\n\\r\\nPoser.prototype.applyPoseFromWeights = function()\\r\\n{\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar num_nodes = this.base_nodes.length;\\r\\n\\tvar positions = this._positions_array;\\r\\n\\tvar rotations = this._rotations_array;\\r\\n\\tvar scalings = this._scalings_array;\\r\\n\\tif( !positions || positions.length != num_nodes * 3 )\\r\\n\\t{\\r\\n\\t\\tpositions = this._positions_array = new Float32Array(num_nodes * 3);\\r\\n\\t\\trotations = this._rotations_array = new Float32Array(num_nodes * 4);\\r\\n\\t\\tscalings = this._scalings_array = new Float32Array(num_nodes * 3);\\r\\n\\t}\\r\\n\\tvar temp_quat = Poser.temp_quat;\\r\\n\\r\\n\\tfor(var i = 0; i < num_nodes; ++i)\\r\\n\\t{\\r\\n\\t\\tpositions.set(LS.ZEROS,i*3);\\r\\n\\t\\trotations.set(LS.QUAT_IDENTITY, i*4);\\r\\n\\t\\tscalings.set(LS.ONES, i*3);\\r\\n\\t}\\r\\n\\r\\n\\tfor(var j = 0; j < this.poses.length; ++j )\\r\\n\\t{\\r\\n\\t\\tvar pose = this.poses[j];\\r\\n\\t\\tif(!pose.weight)\\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < pose.nodes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pose_node_info = pose.nodes[i];\\r\\n\\r\\n\\t\\t\\tif( pose_node_info.delta_pos )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar pos = positions.subarray(i*3,i*3+3);\\r\\n\\t\\t\\t\\tvec3.scaleAndAdd( pos, pos, pose_node_info.delta_pos, pose.weight );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif( pose_node_info.delta_rot )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar rot = rotations.subarray(i*4,i*4+4);\\r\\n\\t\\t\\t\\tquat.slerp( temp_quat, LS.QUAT_IDENTITY, pose_node_info.delta_rot, pose.weight );\\r\\n\\t\\t\\t\\t//quat.scale( temp_quat, pose_node_info.delta_rot, pose.weight );\\r\\n\\t\\t\\t\\tquat.mul( rot, rot, temp_quat );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\tif( pose_node_info.delta_scale )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar scale = scalings.subarray(i*3,i*3+3);\\r\\n\\t\\t\\t\\tvec3.scaleAndAdd( scale, scale, pose_node_info.delta_scale, pose.weight );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t*/\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i = 0; i < this.base_nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar base_node_info = this.base_nodes[i];\\r\\n\\t\\tvar node = scene.getNode( base_node_info.node_uid );\\r\\n\\t\\tif(!node || !node.transform)\\r\\n\\t\\t\\tcontinue; \\r\\n\\r\\n\\t\\tvar pos = positions.subarray(i*3,i*3+3);\\r\\n\\t\\tvar rot = rotations.subarray(i*4,i*4+4);\\r\\n\\t\\tvar scale = scalings.subarray(i*3,i*3+3);\\r\\n\\t\\tquat.normalize( rot, rot );\\r\\n\\r\\n\\t\\tvec3.add( node.transform._position, pos, base_node_info.position );\\r\\n\\t\\tquat.mul( node.transform._rotation, rot, base_node_info.rotation );\\r\\n\\t\\t//vec3.mul( node.transform._scaling, scale, base_node_info.scaling );\\r\\n\\t\\tnode.transform._must_update = true;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/*\\r\\n//call to apply one pose to the nodes\\r\\nPoser.prototype.applyPose = function( name, weight )\\r\\n{\\r\\n\\tif(!name || !this._root || !this._root.scene)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(weight === undefined)\\r\\n\\t\\tweight = 1;\\r\\n\\tif(weight <= 0)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar pose = this.poses[ name ];\\r\\n\\tif(!pose)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < pose.nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar info = pose.nodes[i];\\r\\n\\t\\tvar node = scene.getNode( info.node_uid );\\r\\n\\t\\tif(!node || !node.transform)\\r\\n\\t\\t\\tcontinue; //maybe the node was removed from the scene\\r\\n\\r\\n\\t\\t//overwrite\\r\\n\\t\\tif(weight >= 1)\\t\\t\\r\\n\\t\\t{\\r\\n\\t\\t\\tnode.transform.data = info.data;\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar a = node.transform;\\r\\n\\t\\tvar b = info.data;\\r\\n\\r\\n\\t\\t//interpolate\\r\\n\\t\\tvec3.lerp( a._position, a._position, b, weight ); //position\\r\\n\\t\\tvec3.lerp( a._scaling, a._scaling, b.subarray(7,10), weight ); //scale\\r\\n\\t\\tquat.slerp( a._rotation, a._rotation, b.subarray(3,7), weight ); //rotation\\r\\n\\t\\tnode.transform._must_update = true;\\r\\n\\t}\\r\\n\\r\\n\\tthis.poses[ name ] = pose;\\r\\n\\treturn pose;\\r\\n}\\r\\n*/\\r\\n\\r\\n//remove nodes from poses if they are not used\\r\\nPoser.prototype.purgePoses = function()\\r\\n{\\r\\n\\t//mark which nodes in the pose exist in the scene\\r\\n\\tvar valid_nodes = {};\\r\\n\\tvar scene = this._root.scene;\\r\\n\\tif(!scene)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tfor(var i = 0; i < this.base_nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar info = this.base_nodes[i];\\r\\n\\t\\tvar node = scene.getNode( info.node_uid );\\r\\n\\t\\tif(node)\\r\\n\\t\\t\\tvalid_nodes[ node.uid ] = true;\\r\\n\\t}\\r\\n\\r\\n\\t//now check all the poses, if they use a node that doesnt exist in the scene, remove it from the pose\\r\\n\\tfor(var i = 0; i < this.poses.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar pose = this.poses[i];\\r\\n\\t\\tvar pose_nodes = pose.nodes;\\r\\n\\r\\n\\t\\tfor( var j = 0; j < pose_nodes.length; ++j )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar uid = pose_nodes[j].node_uid;\\r\\n\\t\\t\\tif(valid_nodes[uid])\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tpose_nodes.splice(j,1);\\r\\n\\t\\t\\tj--;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//used for graphs\\r\\nPoser.prototype.setProperty = function(name, value)\\r\\n{\\r\\n\\tif( name == \\\"enabled\\\" )\\r\\n\\t\\tthis.enabled = value;\\r\\n\\telse if( name.substr(0,5) == \\\"pose_\\\" )\\r\\n\\t{\\r\\n\\t\\tname = name.substr(5);\\r\\n\\t\\tvar t = name.split(\\\"_\\\");\\r\\n\\t\\tvar index = Number(t[0]);\\r\\n\\t\\tvar pose = this.poses[ index ];\\r\\n\\t\\tif( pose )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( t[1] == \\\"weight\\\" )\\r\\n\\t\\t\\t\\tpose.weight = value;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\telse if( name == \\\"weights\\\" )\\r\\n\\t\\tthis.weights = value;\\r\\n\\telse if( name == \\\"name_weights\\\" )\\r\\n\\t\\tthis.name_weights = value;\\r\\n}\\r\\n\\r\\nPoser.prototype.getProperty = function(name)\\r\\n{\\r\\n\\tif(name.substr(0,5) == \\\"pose_\\\" && name.length > 5)\\r\\n\\t{\\r\\n\\t\\tvar t = name.substr(5).split(\\\"_\\\");\\r\\n\\t\\tvar index = Number(t[0]);\\r\\n\\t\\tvar pose = this.poses[ index ];\\r\\n\\t\\tif(pose)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(t[1] == \\\"weight\\\")\\r\\n\\t\\t\\t\\treturn pose.weight;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPoser.prototype.getPropertiesInfo = function()\\r\\n{\\r\\n\\tvar properties = {\\r\\n\\t\\tenabled: \\\"boolean\\\",\\r\\n\\t\\tweights: \\\"array\\\",\\r\\n\\t\\tname_weights: \\\"object\\\"\\r\\n\\t};\\r\\n\\r\\n\\tfor(var i = 0; i < this.poses.length; ++i)\\r\\n\\t\\tproperties[ \\\"pose_\\\" + i + \\\"_weight\\\" ] = \\\"number\\\";\\r\\n\\r\\n\\treturn properties;\\r\\n}\\r\\n\\r\\nPoser.prototype.configure = function(o)\\r\\n{\\r\\n\\tLS.BaseComponent.prototype.configure.call(this,o);\\r\\n\\r\\n\\tfor(var i = 0;i < this.poses.length; ++i)\\r\\n\\t\\tthis._poses_by_name[ this.poses[i].name ] = this.poses[i];\\r\\n}\\r\\n\\r\\nLS.registerComponent( Poser );\\r\\n///@FILE:../src/components/spline.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Spline allows to define splines in 3D\\r\\n* @class Spline\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\n\\r\\nfunction Spline( o )\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis._render_in_viewport = false;\\r\\n\\tthis.path = new LS.Path();\\r\\n\\tthis._must_update = false;\\r\\n\\tthis._subdivisions = 20;\\r\\n\\tthis.preserve_tangents = true; //for bezier\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n\\r\\n\\tthis._max_points = 1024;\\r\\n\\tthis._range = 0;\\r\\n\\tthis._middle_points = new Float32Array(3*1024);\\r\\n}\\r\\n\\r\\nSpline[\\\"@subdivisions\\\"] = { type: \\\"number\\\", step:1, min:1, max:100, precision:0 };\\r\\nSpline[\\\"@type\\\"] = { type: \\\"enum\\\", values: { line: LS.LINEAR, bezier: LS.BEZIER, hermite: LS.HERMITE } };\\r\\n\\r\\nSpline.prototype.serialize = function()\\r\\n{\\r\\n\\treturn {\\r\\n\\t\\tenabled: this.enabled,\\r\\n\\t\\trender: this._render_in_viewport,\\r\\n\\t\\tpath: this.path.serialize(),\\r\\n\\t\\tsubs: this._subdivisions,\\r\\n\\t\\ttangents: this.preserve_tangents\\r\\n\\t};\\r\\n}\\r\\n\\r\\nSpline.prototype.configure = function(o)\\r\\n{\\r\\n\\tthis._must_update = true;\\r\\n\\tthis.enabled = o.enabled;\\r\\n\\tthis.render_in_viewport = o.render;\\r\\n\\tthis.path.configure( o.path );\\r\\n\\tthis.preserve_tangents = o.tangents;\\r\\n\\tthis._subdivisions = o.subs || 1;\\r\\n}\\r\\n\\r\\nObject.defineProperty( Spline.prototype, 'render_in_viewport', {\\r\\n\\tget: function() { return this._render_in_viewport; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tif(this._render_in_viewport == v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._render_in_viewport = v;\\r\\n\\t\\t//set events\\r\\n\\t\\tif(!this._root)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(v)\\r\\n\\t\\t\\tLEvent.bind( this._root, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n\\t\\telse\\r\\n\\t\\t\\tLEvent.unbind( this._root, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( Spline.prototype, 'subdivisions', {\\r\\n\\tget: function() { return this._subdivisions; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._subdivisions = v|0;\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( Spline.prototype, 'closed', {\\r\\n\\tget: function() { return this.path.closed; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis.path.closed = v;\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( Spline.prototype, 'type', {\\r\\n\\tget: function() { return this.path.type; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis.path.type = v;\\r\\n\\t\\tthis._must_update = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( Spline.prototype, 'numberOfPoints', {\\r\\n\\tget: function() { return this.path.points.length; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthrow(\\\"number of points cannot be set, use addPoint\\\");\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( Spline.prototype, 'points', {\\r\\n\\tget: function() { return this.path.points; },\\r\\n\\tset: function(v) { \\r\\n\\t\\tthrow(\\\"points cannot be set, use addPoint\\\");\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nSpline.prototype.onAddedToNode = function(node)\\r\\n{\\r\\n\\tif(this._render_in_viewport)\\r\\n\\t\\tLEvent.bind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n}\\r\\n\\r\\nSpline.prototype.onRemovedFromNode = function(node)\\r\\n{\\r\\n\\tif(this._render_in_viewport)\\r\\n\\t\\tLEvent.unbind( node, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n}\\r\\n\\r\\nSpline.prototype.onCollectInstances = function(e, instances)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this._root)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(this.path.getSegments() == 0)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this._mesh || this._must_update)\\r\\n\\t\\tthis.updateMesh();\\r\\n\\r\\n\\tvar RI = this._render_instance;\\r\\n\\tif(!RI)\\r\\n\\t\\tthis._render_instance = RI = new LS.RenderInstance(this._root, this);\\r\\n\\r\\n\\tRI.fromNode( this._root );\\r\\n\\tRI.setMesh( this._mesh, gl.LINE_STRIP );\\r\\n\\tRI.setRange( 0, this._range );\\r\\n\\tRI.setMaterial( this._root.getMaterial() );\\r\\n\\r\\n\\tinstances.push(RI);\\t\\r\\n}\\r\\n\\r\\nSpline.prototype.updateMesh = function()\\r\\n{\\r\\n\\tif(!this._mesh)\\r\\n\\t\\tthis._mesh = GL.Mesh.load( { vertices: new Float32Array( this._max_points * 3 ) } );\\r\\n\\t\\r\\n\\tvar vertices_buffer = this._mesh.getVertexBuffer(\\\"vertices\\\");\\r\\n\\tvar vertices_data = vertices_buffer.data;\\r\\n\\r\\n\\tvar total = 0;\\r\\n\\r\\n\\tif( this.path.type == LS.LINEAR )\\r\\n\\t\\ttotal = this.path.getSegments() + 1;\\r\\n\\telse\\r\\n\\t\\ttotal = this.path.getSegments() * this._subdivisions; //20 points per segment\\r\\n\\r\\n\\tif(total > this._max_points)\\r\\n\\t\\ttotal = this._max_points;\\r\\n\\r\\n\\tthis.path.samplePointsTyped( total, vertices_data );\\r\\n\\tvertices_buffer.uploadRange( 0, total * 4 * 3 );\\r\\n\\r\\n\\tthis._range = total;\\r\\n\\r\\n\\tthis._must_update = false;\\r\\n}\\r\\n\\r\\nSpline.prototype.clear = function()\\r\\n{\\r\\n\\tthis._must_update = true;\\r\\n\\tthis.path.clear();\\r\\n}\\r\\n\\r\\nSpline.prototype.getPoint = function( f, out )\\r\\n{\\r\\n\\tout = out || vec3.create();\\r\\n\\r\\n\\tif(this.path.closed) //cycle\\r\\n\\t{\\r\\n\\t\\tf = f % 1;\\r\\n\\t\\tif(f < 0)\\r\\n\\t\\t\\tf = 1 + f;\\r\\n\\t}\\r\\n\\r\\n\\tthis.path.computePoint( f, out );\\r\\n\\r\\n\\tif( this._root.transform )\\r\\n\\t{\\r\\n\\t\\tvar model = this._root.transform.getGlobalMatrix();\\r\\n\\t\\tmat4.multiplyVec3( out, model, out );\\r\\n\\t}\\r\\n\\r\\n\\treturn out;\\r\\n}\\r\\n\\r\\nSpline.prototype.getPointRef = function( index )\\r\\n{\\r\\n\\tif( index < this.path.points.length )\\r\\n\\t\\treturn this.path.points[index];\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n\\r\\nSpline.prototype.addPoint = function( point )\\r\\n{\\r\\n\\tif( this._root.transform )\\r\\n\\t{\\r\\n\\t\\tvar model = this._root.transform.getGlobalMatrix();\\r\\n\\t\\tmat4.invert(model,model);\\r\\n\\t\\tpoint = mat4.multiplyVec3( vec3.create(), model, point );\\r\\n\\t}\\r\\n\\r\\n\\tthis.path.addPoint( point );\\r\\n\\tthis._must_update = true;\\r\\n}\\r\\n\\r\\nSpline.prototype.addPointLocal = function( point )\\r\\n{\\r\\n\\tthis.path.addPoint( point );\\r\\n\\tthis._must_update = true;\\r\\n}\\r\\n\\r\\n// to render in the editor\\r\\n\\r\\nSpline.prototype.renderEditor = function( is_selected )\\r\\n{\\r\\n\\tvar path = this.path;\\r\\n\\r\\n\\tif(path.points.length < 2)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\tLS.Draw.push();\\r\\n\\r\\n\\tif( this._root.transform )\\r\\n\\t\\tLS.Draw.setMatrix( this._root.transform.getGlobalMatrixRef(true) );\\r\\n\\r\\n\\t//draw points\\r\\n\\tif(is_selected)\\r\\n\\t{\\r\\n\\t\\tLS.Draw.setColor(0.9,0.5,0.9,1);\\r\\n\\t\\tLS.Draw.setPointSize( 9 );\\r\\n\\t\\tLS.Draw.renderRoundPoints( path.points );\\r\\n\\t}\\r\\n\\r\\n\\tif(this._render_in_viewport) //already rendered in the \\r\\n\\t{\\r\\n\\t\\tLS.Draw.pop();\\r\\n\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//selection\\r\\n\\tif(window.SelectionModule && SelectionModule.selection && SelectionModule.selection.instance == this && SelectionModule.selection.info != undefined )\\r\\n\\t{\\r\\n\\t\\tvar index = SelectionModule.selection.info;\\r\\n\\t\\tvar point = this.points[ index ];\\r\\n\\t\\tLS.Draw.setColor(1,1,0.4,1);\\r\\n\\t\\tLS.Draw.setPointSize( 14 );\\r\\n\\t\\tLS.Draw.renderRoundPoints( point );\\r\\n\\t}\\r\\n\\r\\n\\t//draw line\\r\\n\\tif(!this._mesh || this._must_update)\\r\\n\\t\\tthis.updateMesh();\\r\\n\\r\\n\\tif(!is_selected)\\r\\n\\t\\tLS.Draw.setColor(0.6,0.5,0.4,0.5);\\r\\n\\telse\\r\\n\\t\\tLS.Draw.setColor(0.6,0.6,0.6,0.8);\\r\\n\\tLS.Draw.renderMesh( this._mesh, GL.LINE_STRIP, null,null, 0, this._range );\\r\\n\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\tLS.Draw.pop();\\r\\n}\\r\\n\\r\\n//used to allow the editor to edit the points ****************\\r\\n\\r\\nSpline.prototype.renderPicking = function( ray )\\r\\n{\\r\\n\\tvar model = this._root.transform.getGlobalMatrixRef(true);\\r\\n\\r\\n\\tvar path = this.path;\\r\\n\\tfor(var i = 0; i < path.points.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar pos = path.points[i];\\r\\n\\t\\tif( this._root.transform )\\r\\n\\t\\t\\tpos = mat4.multiplyVec3( vec3.create(), model, pos );\\r\\n\\t\\tLS.Picking.addPickingPoint( pos, 9, { instance: this, info: i } );\\r\\n\\t}\\r\\n}\\r\\n\\r\\nSpline.prototype.applyTransformMatrix = function( matrix, center, info )\\r\\n{\\r\\n\\tvar p = this.path.points[info];\\r\\n\\tif(!p)\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\tthis._must_update = true;\\r\\n\\tvar new_pos = mat4.multiplyVec3( vec3.create(), matrix, p );\\r\\n\\tthis.path.movePoint( info, new_pos, this.preserve_tangents );\\r\\n\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\nSpline.prototype.getTransformMatrix = function( info )\\r\\n{\\r\\n\\tvar p = this.path.points[info];\\r\\n\\tif(!p)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tvar model = this._root.transform.getGlobalMatrix();\\r\\n\\tmat4.translate( model, model, p );\\r\\n\\treturn model;\\r\\n}\\r\\n\\r\\n\\r\\nLS.registerComponent( Spline );\\r\\n\\r\\n\\r\\n\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Allows to set an object position from a spline\\r\\n* @class FollowSpline\\r\\n* @constructor\\r\\n* @param {Object} object to configure from\\r\\n*/\\r\\n\\r\\nfunction FollowSpline( o )\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.spline = \\\"\\\";\\r\\n\\tthis.factor = 0;\\r\\n\\r\\n\\tthis._last_position = vec3.create();\\r\\n\\tthis._last_position_inc = vec3.create();\\r\\n\\tthis._last_rotation = quat.create();\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nFollowSpline[\\\"@spline\\\"] = { type: LS.TYPES.COMPONENT_ID };\\r\\nFollowSpline[\\\"@factor\\\"] = { type: \\\"number\\\", step:0.001, precision:3 };\\r\\n\\r\\nFollowSpline.prototype.serialize = function()\\r\\n{\\r\\n\\treturn {\\r\\n\\t\\tenabled: this.enabled,\\r\\n\\t\\tspline: this.spline,\\r\\n\\t\\tfactor: this.factor\\r\\n\\t};\\r\\n}\\r\\n\\r\\nFollowSpline.prototype.configure = function(o)\\r\\n{\\r\\n\\tthis.enabled = o.enabled;\\r\\n\\tthis.spline = o.spline;\\r\\n\\tthis.factor = o.factor;\\r\\n}\\r\\n\\r\\nFollowSpline.prototype.onAddedToScene = function( scene )\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"update\\\", this.onUpdate, this);\\r\\n}\\r\\n\\r\\nFollowSpline.prototype.onRemovedFromScene = function( scene )\\r\\n{\\r\\n\\tLEvent.unbind( scene, \\\"update\\\", this.onUpdate, this);\\r\\n}\\r\\n\\r\\nFollowSpline.prototype.onUpdate = function(e, dt)\\r\\n{\\r\\n\\tvar node = this._root;\\r\\n\\tif(!node || !node.transform )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar spline = LS.GlobalScene.findComponentByUId( this.spline );\\r\\n\\tif(!spline)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar pos = this._last_position;\\r\\n\\tvar pos2 = this._last_position_inc;\\r\\n\\tvar rot = this._last_rotation;\\r\\n\\r\\n\\tspline.getPoint( this.factor, pos );\\r\\n\\tspline.getPoint( this.factor+0.001, pos2 );\\r\\n\\r\\n\\tif( node._parentNode && node._parentNode.transform )\\r\\n\\t{\\r\\n\\t\\tvar mat = node._parentNode.transform.getGlobalMatrix();\\r\\n\\t\\tmat4.invert( mat, mat );\\r\\n\\t\\tmat4.multiplyVec3( pos, mat, pos );\\r\\n\\t\\tmat4.multiplyVec3( pos2, mat, pos2 );\\r\\n\\t}\\r\\n\\r\\n\\tif( vec3.distance(pos,pos2) < 0.00001 ) //too close\\r\\n\\t{\\r\\n\\t\\tnode.transform.setPosition(pos);\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tnode.transform.lookAt( pos, pos2, LS.TOP );\\r\\n\\r\\n\\t/*\\r\\n\\t//pos\\r\\n\\tnode.transform.setPosition(pos);\\r\\n\\r\\n\\t//rot\\r\\n\\tvar mat = mat4.lookAt( mat4.create(), pos, pos2, LS.TOP );\\r\\n\\t//mat4.invert(mat,mat);\\r\\n\\tquat.fromMat4( rot, mat );\\r\\n\\tnode.transform.rotation = rot;\\r\\n\\t*/\\r\\n}\\r\\n\\r\\nLS.registerComponent( FollowSpline );\\r\\n\\r\\n///@FILE:../src/components/canvas3d.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Allows to render 2d canvas primitives, but they are rendered into a plane that can be positioned in 3D space.\\r\\n* It also supports to store the texture so it can be used in another material.\\r\\n* \\r\\n* The CANVAS2D mode renders busing a native Canvas2D, which has all the features but it could be slower because it has to upload the full canvas every frame.\\r\\n* The WEBGL mode renders the canvas using WebGL calls, it is faster but the quality is worse and some features are not available (but you can render other textures as images)\\r\\n* To fill the canvas you must have a Script in the same node, that contains a method called OnRenderCanvas\\r\\n* @class Canvas3D\\r\\n* @namespace LS.Components\\r\\n* @constructor\\r\\n* @param {String} object to configure from\\r\\n*/\\r\\nfunction Canvas3D(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\r\\n\\tthis.mode = 1;\\r\\n\\tthis.width = 512;\\r\\n\\tthis.height = 512;\\r\\n\\tthis.texture_name = \\\":canvas3D\\\";\\r\\n\\tthis.visible = true;\\r\\n\\tthis.input_active = true; //used for LS.GUI methods\\r\\n\\tthis.use_node_material = false;\\r\\n\\tthis.generate_mipmaps = false;\\r\\n\\tthis.max_interactive_distance = 100; //distance beyong which the mouse is no longer projected\\r\\n\\tthis.high_precision = false; //use a texture format of more than one byte per channel\\r\\n\\tthis.opacity = 1.0;\\r\\n\\r\\n\\tthis._clear_buffer = true; //not public, just here in case somebody wants it\\r\\n\\tthis._skip_backside = true;\\r\\n\\tthis._texture = null;\\r\\n\\tthis._fbo = null;\\r\\n\\tthis._RI = null;\\r\\n\\tthis._standard_material = null;\\r\\n\\r\\n\\tthis._mouse = vec3.create();\\r\\n\\r\\n\\tthis._is_mouse_inside = false;\\r\\n\\r\\n\\tthis._local_mouse = {\\r\\n\\t\\tmousex: 0,\\r\\n\\t\\tmousey: 0,\\r\\n\\t\\tbuttons: 0\\r\\n\\t};\\r\\n\\r\\n\\tthis._local_mouse_click = {\\r\\n\\t\\tmousex: 0,\\r\\n\\t\\tmousey: 0\\r\\n\\t}\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nCanvas3D.icon = \\\"mini-icon-brush.png\\\";\\r\\n\\r\\nCanvas3D.MODE_CANVAS2D = 1; //renders to a canvas2D, then uploads the texture to the GPU after every frame\\r\\nCanvas3D.MODE_WEBGL = 2;\\t//renders to a WebGLTexture translating the Canvas2D calls to WebGL (using Canvas2DtoWebGL)\\r\\nCanvas3D.MODE_IMMEDIATE = 3; //renders directly to current viewport (using Canvas2DtoWebGL)\\r\\n\\r\\nCanvas3D[\\\"@mode\\\"] = { type:\\\"enum\\\", values: { \\\"Canvas2D\\\":Canvas3D.MODE_CANVAS2D, \\\"WebGL\\\":Canvas3D.MODE_WEBGL, \\\"Immediate\\\": Canvas3D.MODE_IMMEDIATE } };\\r\\nCanvas3D[\\\"@width\\\"] = { type:\\\"number\\\", step:1, precision:0 };\\r\\nCanvas3D[\\\"@height\\\"] = { type:\\\"number\\\", step:1, precision:0 };\\r\\nCanvas3D[\\\"@texture_name\\\"] = { type:\\\"string\\\" };\\r\\n\\r\\nObject.defineProperty( Canvas3D.prototype, \\\"texture\\\", {\\r\\n\\tset: function(){\\r\\n\\t\\tthrow(\\\"Canvas3D texture cannot be set manually\\\");\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this._texture;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nCanvas3D.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind(scene, LS.EVENT.READY_TO_RENDER, this.onRender,this);\\r\\n\\tLEvent.bind(scene, LS.EVENT.AFTER_RENDER_INSTANCES, this.onRender,this);\\r\\n}\\r\\n\\r\\nCanvas3D.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbind(scene, LS.EVENT.READY_TO_RENDER, this.onRender,this);\\r\\n\\tLEvent.unbind(scene, LS.EVENT.AFTER_RENDER_INSTANCES, this.onRender,this);\\r\\n}\\r\\n\\r\\nCanvas3D.prototype.onAddedToNode = function( node )\\r\\n{\\r\\n\\tif(!this.texture_name)\\r\\n\\t\\tthis.texture_name = \\\":canvas3D\\\";\\r\\n\\r\\n\\tLEvent.bind( node, LS.EVENT.COLLECT_RENDER_INSTANCES, this.onCollectInstances, this );\\r\\n}\\r\\n\\r\\nCanvas3D.prototype.onRemovedFromNode = function( node )\\r\\n{\\r\\n\\tLEvent.unbind( node, LS.EVENT.COLLECT_RENDER_INSTANCES, this.onCollectInstances, this );\\r\\n}\\r\\n\\r\\n//called before rendering scene\\r\\nCanvas3D.prototype.onRender = function(e)\\r\\n{\\r\\n\\tvar camera = LS.Renderer._current_camera;\\r\\n\\tif(!this.enabled || !camera || !camera.checkLayersVisibility( this._root.layers ) )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(\\t(e == LS.EVENT.READY_TO_RENDER && ( this.mode == Canvas3D.MODE_CANVAS2D || this.mode == Canvas3D.MODE_WEBGL)) || \\r\\n\\t\\t(e == LS.EVENT.AFTER_RENDER_INSTANCES && this.mode == Canvas3D.MODE_IMMEDIATE)\\r\\n\\t)\\r\\n\\t{\\r\\n\\t\\tthis.drawCanvas();\\r\\n\\t}\\r\\n}\\r\\n\\r\\nCanvas3D.prototype.drawCanvas = function()\\r\\n{\\r\\n\\tvar w = this.width|0;\\r\\n\\tvar h = this.height|0;\\r\\n\\r\\n\\t//create resources\\r\\n\\tif( this.mode == Canvas3D.MODE_CANVAS2D )\\r\\n\\t{\\r\\n\\t\\tif(!this._canvas)\\r\\n\\t\\t\\tthis._canvas = document.createElement(\\\"canvas\\\");\\r\\n\\t\\tif(this._canvas.width != w)\\r\\n\\t\\t\\tthis._canvas.width = w;\\r\\n\\t\\tif(this._canvas.height != h)\\r\\n\\t\\t\\tthis._canvas.height = h;\\r\\n\\t}\\r\\n\\r\\n\\tvar type = this.high_precision ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE;\\r\\n\\r\\n\\tif(this.mode != Canvas3D.MODE_IMMEDIATE)\\r\\n\\t{\\r\\n\\t\\tif(!this._texture || this._texture.width != w || this._texture.height != h || this._texture.type != type)\\r\\n\\t\\t\\tthis._texture = new GL.Texture(w,h,{ type: type, format: GL.RGBA, filter: GL.LINEAR, wrap: GL.CLAMP_TO_EDGE });\\r\\n\\t}\\r\\n\\r\\n\\t//project mouse into the canvas plane\\r\\n\\tif(this.visible)\\r\\n\\t\\tthis.projectMouse();\\r\\n\\r\\n\\t//render the canvas\\r\\n\\tif( this.mode == Canvas3D.MODE_CANVAS2D )\\r\\n\\t{\\r\\n\\t\\tvar ctx = this._canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tif(this._clear_buffer)\\r\\n\\t\\t\\tctx.clearRect(0,0,this._canvas.width,this._canvas.height); //clear\\r\\n\\t\\tLS.GUI._ctx = ctx;\\r\\n\\t\\tthis._root.processActionInComponents(\\\"onRenderCanvas\\\",[ctx,this._canvas,this._mouse,this]);\\r\\n\\t\\tLS.GUI._ctx = gl;\\r\\n\\t\\tthis._texture.uploadImage( this._canvas );\\r\\n\\t}\\r\\n\\telse if ( this.mode == Canvas3D.MODE_WEBGL )\\r\\n\\t{\\r\\n\\t\\tvar ctx = gl;\\r\\n\\t\\tif(!this._fbo)\\r\\n\\t\\t\\tthis._fbo = new GL.FBO();\\r\\n\\t\\tthis._fbo.setTextures([this._texture]);\\r\\n\\t\\tthis._fbo.bind();\\r\\n\\t\\tgl.start2D();\\r\\n\\t\\tif(this._clear_buffer)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgl.clearColor(0,0,0,0);\\r\\n\\t\\t\\tgl.clear(GL.COLOR_BUFFER_BIT);\\r\\n\\t\\t}\\r\\n\\t\\tLS.GUI._ctx = gl;\\r\\n\\t\\tthis._root.processActionInComponents(\\\"onRenderCanvas\\\",[ctx,this._texture,this._mouse,this]);\\r\\n\\t\\tgl.finish2D();\\r\\n\\t\\tthis._fbo.unbind();\\r\\n\\t}\\r\\n\\telse if ( this.mode == Canvas3D.MODE_IMMEDIATE )\\r\\n\\t{\\r\\n\\t\\tvar ctx = gl;\\r\\n\\t\\tgl.start2D();\\r\\n\\t\\tLS.GUI._ctx = gl;\\r\\n\\r\\n\\t\\t//pass MVP matrix\\r\\n\\t\\tvar mvp = this._mvp;\\r\\n\\t\\tif(!mvp)\\r\\n\\t\\t\\tmvp = this._mvp = mat4.create();\\r\\n\\t\\tmat4.identity(mvp);\\r\\n\\t\\tif(this._root.transform)\\r\\n\\t\\t\\tmat4.multiply(mvp,mvp, this._root.transform.getGlobalMatrixRef() );\\r\\n\\t\\tmat4.scale(mvp,mvp,[1/this.width,-1/this.height,1]);\\r\\n\\t\\tmat4.translate(mvp,mvp,[this.width*-0.5,this.height*-0.5,0]);\\r\\n\\t\\tvar camera = LS.Renderer._current_camera;\\r\\n\\t\\tmat4.multiply( mvp, camera._viewprojection_matrix, mvp );\\r\\n\\t\\t//mat4.multiply( mvp, mvp, camera._viewprojection_matrix );\\r\\n\\t\\t//mat4.identity(mvp);\\r\\n\\t\\tgl.WebGLCanvas.set3DMatrix( mvp );\\r\\n\\t\\tif(!this._canvas_info)\\r\\n\\t\\t\\tthis._canvas_info = { width: 0, height: 0 };\\r\\n\\t\\tthis._canvas_info.width = this.width;\\r\\n\\t\\tthis._canvas_info.height = this.height;\\r\\n\\t\\tgl.disable( gl.CULL_FACE );\\r\\n\\t\\tgl.enable( gl.DEPTH_TEST );\\r\\n\\t\\tgl.depthFunc( gl.LEQUAL );\\r\\n\\t\\tgl.globalAlpha = this.opacity;\\r\\n\\r\\n\\t\\tthis._root.processActionInComponents(\\\"onRenderCanvas\\\",[ctx,this._canvas_info,this._mouse,this]);\\r\\n\\r\\n\\t\\tgl.globalAlpha = 1;\\r\\n\\t\\tgl.finish2D();\\r\\n\\t\\tgl.depthFunc( gl.LESS );\\r\\n\\t\\tgl.WebGLCanvas.set3DMatrix(null);\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//process and share the texture\\r\\n\\tif(this._texture)\\r\\n\\t{\\r\\n\\t\\tif(this.generate_mipmaps && isPowerOfTwo(w) && isPowerOfTwo(h) )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._texture.setParameter( GL.TEXTURE_MIN_FILTER, GL.LINEAR_MIPMAP_LINEAR );\\r\\n\\t\\t\\tgl.generateMipmap( gl.TEXTURE_2D );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis._texture.setParameter( GL.TEXTURE_MIN_FILTER, GL.LINEAR );\\r\\n\\t\\tLS.RM.registerResource( this.texture_name || \\\":canvas3D\\\", this._texture );\\r\\n\\t}\\r\\n\\r\\n\\t//restore stuff\\r\\n\\tif( this._prev_mouse )\\r\\n\\t{\\r\\n\\t\\t LS.Input.Mouse = this._prev_mouse;\\r\\n\\t\\t this._prev_mouse = null;\\r\\n\\t}\\r\\n\\tif( LS.Input.current_click && this._prev_click_mouse )\\r\\n\\t{\\r\\n\\t\\tLS.Input.current_click = this._prev_click_mouse;\\r\\n\\t\\tthis._prev_click_mouse = null;\\r\\n\\t}\\r\\n}\\r\\n\\r\\nCanvas3D.prototype.onCollectInstances = function(e,instances)\\r\\n{\\r\\n\\tif(!this.enabled || !this.visible || !this._texture || this.mode == Canvas3D.MODE_IMMEDIATE)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this._RI)\\r\\n\\t\\tthis._RI = new LS.RenderInstance();\\r\\n\\tvar RI = this._RI;\\r\\n\\tvar material = null;\\r\\n\\tif(this.use_node_material)\\r\\n\\t\\tmaterial = this._root.getMaterial();\\r\\n\\tif(!material)\\r\\n\\t\\tmaterial = this._standard_material;\\r\\n\\tif(!material)\\r\\n\\t\\tmaterial = this._standard_material = new LS.MaterialClasses.StandardMaterial({ flags: { ignore_lights: true, cast_shadows: false }, blend_mode: LS.Blend.ALPHA });\\r\\n\\r\\n\\tif(!this.use_node_material)\\r\\n\\t{\\r\\n\\t\\tmaterial.opacity = this.opacity;\\r\\n\\t\\tmaterial.blend_mode = material.opacity < 1 ? LS.Blend.ALPHA : LS.Blend.NORMAL;\\r\\n\\t}\\r\\n\\r\\n\\tmaterial.setTexture(\\\"color\\\", this.texture_name || \\\":canvas3D\\\" );\\r\\n\\tvar sampler = material.textures[\\\"color\\\"];\\r\\n\\r\\n\\tRI.fromNode( this._root );\\r\\n\\tRI.setMaterial( material );\\r\\n\\r\\n\\tif(!this._mesh)\\r\\n\\t\\tthis._mesh = GL.Mesh.plane();\\r\\n\\tRI.setMesh(this._mesh);\\r\\n\\tinstances.push( RI );\\r\\n\\r\\n\\treturn instances;\\r\\n}\\r\\n\\r\\n\\r\\nCanvas3D.prototype.clear = function( redraw )\\r\\n{\\r\\n\\tif( this.mode == Canvas3D.MODE_CANVAS2D )\\r\\n\\t{\\r\\n\\t\\tvar ctx = this._canvas.getContext(\\\"2d\\\");\\r\\n\\t\\tctx.clearRect(0,0,this._canvas.width,this._canvas.height); //clear\\r\\n\\t}\\r\\n\\telse if( this.mode == Canvas3D.MODE_WEBGL )\\r\\n\\t{\\r\\n\\t\\tif(this._texture)\\r\\n\\t\\t\\tthis._texture.fill([0,0,0,0]);\\r\\n\\t}\\r\\n\\tif(redraw)\\r\\n\\t\\tthis.onRender();\\r\\n}\\r\\n\\r\\nCanvas3D.prototype.projectMouse = function()\\r\\n{\\r\\n\\tvar camera = LS.Renderer._main_camera;\\r\\n\\tif(!camera)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//Canvas Plane\\r\\n\\tif(!this.root.transform)\\r\\n\\t{\\r\\n\\t\\tthis._mouse[0] = LS.Input.Mouse.canvasx;\\r\\n\\t\\tthis._mouse[1] = LS.Input.Mouse.canvasy;\\r\\n\\t\\tthis._mouse[2] = 0;\\r\\n\\t\\tthis._is_mouse_inside = true;\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tvar cam_dist = 0;\\r\\n\\tcam_dist = vec3.distance( camera.getEye(), this.root.transform.getGlobalPosition() );\\r\\n\\tvar too_far = cam_dist > this.max_interactive_distance;\\r\\n\\r\\n\\tthis._is_mouse_inside = false;\\r\\n\\r\\n\\tvar x = LS.Input.Mouse.canvasx;\\r\\n\\tvar y = LS.Input.Mouse.canvasy;\\r\\n\\tvar w = this.width|0;\\r\\n\\tvar h = this.height|0;\\r\\n\\r\\n\\tif( !this.input_active || too_far )\\r\\n\\t{\\r\\n\\t\\tx = -1;\\r\\n\\t\\ty = -1;\\r\\n\\t\\tthis._mouse[0] = x;\\r\\n\\t\\tthis._mouse[1] = y;\\r\\n\\t\\tthis._mouse[2] = -1;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t{\\r\\n\\t\\tthis._mouse[0] = -1;\\r\\n\\t\\tthis._mouse[1] = -1;\\r\\n\\t\\tthis._mouse[2] = cam_dist;\\r\\n\\r\\n\\t\\tvar ray = camera.getRay( x, y );\\r\\n\\t\\tif(!ray) //??\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tvar temp = vec3.create();\\r\\n\\t\\tvar plane_normal = this.root.transform.localVectorToGlobal( LS.FRONT, temp );\\r\\n\\r\\n\\t\\tif( !this._skip_backside || vec3.dot( ray.direction, plane_normal ) > 0.0 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar local_origin = this.root.transform.globalToLocal( ray.origin, temp );\\r\\n\\t\\t\\tvar local_direction = this.root.transform.globalVectorToLocal( ray.direction );\\r\\n\\r\\n\\t\\t\\tif( geo.testRayPlane( local_origin, local_direction, LS.ZEROS, LS.FRONT, this._mouse ) )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis._mouse[0] = (this._mouse[0] + 0.5) * w;\\r\\n\\t\\t\\t\\tthis._mouse[1] = (this._mouse[1] + 0.5) * h;\\r\\n\\t\\t\\t\\t//flip Y\\r\\n\\t\\t\\t\\tthis._mouse[1] = h - this._mouse[1];\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t//mark the mouse as inside\\r\\n\\tif( this._mouse[0] >= 0 && this._mouse[0] < w &&\\r\\n\\t\\tthis._mouse[1] >= 0 && this._mouse[1] < h )\\r\\n\\t\\tthis._is_mouse_inside = true;\\r\\n\\r\\n\\t//hacks to work with the LS.GUI...\\r\\n\\t//*\\r\\n\\tthis._local_mouse.mousex = this._mouse[0];\\r\\n\\tthis._local_mouse.mousey = this._mouse[1];\\r\\n\\tthis._prev_mouse = LS.Input.Mouse;\\r\\n\\tLS.Input.Mouse = this._local_mouse;\\r\\n\\r\\n\\tif( LS.Input.current_click )\\r\\n\\t{\\r\\n\\t\\tthis._local_mouse_click.mousex = this._mouse[0];\\r\\n\\t\\tthis._local_mouse_click.mousey = this._mouse[1];\\r\\n\\t\\tthis._prev_click_mouse = LS.Input.current_click;\\r\\n\\t\\tLS.Input.current_click = this._local_mouse_click;\\r\\n\\t}\\r\\n\\t//*/\\r\\n}\\r\\n\\r\\n/*\\r\\nCanvas3D.prototype.getResources = function(res)\\r\\n{\\r\\n\\tif( this.material && this.material.constructor === String )\\r\\n\\t\\tres[this.material] = LS.Material;\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nCanvas3D.prototype.onResourceRenamed = function (old_name, new_name, resource)\\r\\n{\\r\\n\\tif(this.material == old_name)\\r\\n\\t\\tthis.material = new_name;\\r\\n}\\r\\n*/\\r\\n\\r\\nLS.registerComponent( Canvas3D );\\r\\n///@FILE:../src/components/videoPlayer.js\\r\\n///@INFO: UNCOMMON\\r\\n//work in progress\\r\\n\\r\\nfunction VideoPlayer(o)\\r\\n{\\r\\n\\tthis._enabled = true;\\r\\n\\r\\n\\tthis._video = document.createElement(\\\"video\\\");\\r\\n\\tthis._video.muted = false;\\r\\n\\tthis._video.autoplay = false;\\r\\n\\tthis.bindVideoEvents( this._video );\\r\\n\\r\\n\\tthis._autoplay = true;\\r\\n\\tthis.generate_mipmaps = false;\\r\\n\\r\\n\\tthis._src = \\\"\\\";\\r\\n\\tthis._url_loading = null;\\r\\n\\tthis.texture_name = \\\":video\\\";\\r\\n\\tthis.render_mode = true;\\r\\n\\tthis._playback_rate = 1;\\r\\n\\r\\n\\tthis._ignore_proxy = false;\\r\\n\\r\\n\\tthis._texture = null;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nObject.defineProperty( VideoPlayer.prototype, \\\"enabled\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tthis._enabled = v;\\r\\n\\t\\tif(!v)\\r\\n\\t\\t\\tthis._video.pause();\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar scene = this._root ? this._root.scene : null;\\r\\n\\t\\t\\tif(scene && scene.state === LS.RUNNING && this._video.autoplay)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(this._video.currentTime >= this._video.duration)\\r\\n\\t\\t\\t\\t\\tthis._video.currentTime = 0;\\r\\n\\t\\t\\t\\tthis._video.play();\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._enabled;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n//in case you are referencing a url with video that allow cors\\r\\nObject.defineProperty( VideoPlayer.prototype, \\\"ignore_proxy\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tif( v == this._ignore_proxy )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._ignore_proxy = v;\\r\\n\\t\\tthis.load( this.src );\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._ignore_proxy;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( VideoPlayer.prototype, \\\"src\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tif(v == this._src)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._src = v;\\r\\n\\t\\tthis.load( this._src );\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._src;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( VideoPlayer.prototype, \\\"time\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tthis._video.currentTime = time;\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._video.currentTime;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( VideoPlayer.prototype, \\\"texture\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tthrow(\\\"videoPlayer texture cannot be set\\\");\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._texture;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( VideoPlayer.prototype, \\\"autoplay\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tthis._autoplay = v;\\r\\n\\t\\t//this._video.autoplay = v;\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._autoplay;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( VideoPlayer.prototype, \\\"muted\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tthis._video.muted = v;\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._video.muted;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( VideoPlayer.prototype, \\\"duration\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tthrow(\\\"VideoPlayer duration cannot be assigned, is read-only\\\");\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._video.duration;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nObject.defineProperty( VideoPlayer.prototype, \\\"playback_rate\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tif(v < 0)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._playback_rate = v;\\r\\n\\t\\tthis._video.playbackRate = v;\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._playback_rate;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\nObject.defineProperty( VideoPlayer.prototype, \\\"video\\\", {\\r\\n\\tset: function(v){\\r\\n\\t\\tif(!v || v.constructor !== HTMLVideoElement)\\r\\n\\t\\t\\tthrow(\\\"Video must a HTMLVideoElement\\\");\\r\\n\\t\\tif( v == this._video )\\r\\n\\t\\t\\treturn;\\r\\n\\t\\r\\n\\t\\tthis._video = v;\\r\\n\\t\\tthis._video.muted = false;\\r\\n\\t\\tthis._video.autoplay = false;\\r\\n\\t\\tthis._video.playbackRate = this._playback_rate;\\r\\n\\t\\tthis.bindVideoEvents( this._video );\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._video;\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\nVideoPlayer.NONE = 0;\\r\\nVideoPlayer.PLANE = 1;\\r\\nVideoPlayer.TO_MATERIAL = 2;\\r\\nVideoPlayer.BACKGROUND = 5;\\r\\nVideoPlayer.BACKGROUND_STRETCH = 6;\\r\\n\\r\\nVideoPlayer[\\\"@src\\\"] = { type: \\\"resource\\\" };\\r\\nVideoPlayer[\\\"@render_mode\\\"] = { type: \\\"enum\\\", values: {\\\"NONE\\\":VideoPlayer.NONE, \\\"PLANE\\\": VideoPlayer.PLANE, \\\"TO_MATERIAL\\\": VideoPlayer.TO_MATERIAL, /* \\\"BACKGROUND\\\": VideoPlayer.BACKGROUND,*/ \\\"BACKGROUND_STRETCH\\\": VideoPlayer.BACKGROUND_STRETCH } };\\r\\n\\r\\nVideoPlayer.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"start\\\", this.onStart, this);\\r\\n\\tLEvent.bind( scene, \\\"pause\\\", this.onPause, this);\\r\\n\\tLEvent.bind( scene, \\\"unpause\\\", this.onUnpause, this);\\r\\n\\tLEvent.bind( scene, \\\"beforeRender\\\", this.onBeforeRender, this ); //to upload texture\\r\\n\\tLEvent.bind( scene, \\\"beforeRenderScene\\\", this.onBeforeRenderScene, this ); //to render background quad\\r\\n\\tLEvent.bind( scene, \\\"collectRenderInstances\\\", this.onCollectInstances, this );\\r\\n\\t//LEvent.bind( scene, \\\"update\\\", this.onUpdate, this);\\r\\n\\tLEvent.bind( scene, \\\"finish\\\", this.onFinish, this);\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbindAll( scene, this );\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.onStart = function()\\r\\n{\\r\\n\\tif(this.autoplay)\\r\\n\\t\\tthis.play();\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.onPause = function()\\r\\n{\\r\\n\\tthis.pause();\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.onUnpause = function()\\r\\n{\\r\\n\\tif(this.autoplay)\\r\\n\\t\\tthis.play();\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.onFinish = function()\\r\\n{\\r\\n\\tthis.stop();\\r\\n}\\r\\n\\r\\n/*\\r\\nVideoPlayer.prototype.onUpdate = function( e, dt )\\r\\n{\\r\\n\\tif(!this.enabled || this._video.width == 0)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._time += dt;\\r\\n\\tthis._video.currentTime = this._time;\\r\\n\\tthis._video.dirty = true;\\r\\n}\\r\\n*/\\r\\n\\r\\nVideoPlayer.prototype.load = function( url, force )\\r\\n{\\r\\n\\tif(!url)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar final_url = LS.RM.getFullURL( url, { ignore_proxy: this.ignore_proxy } );\\r\\n\\r\\n\\tif( this._url_loading == final_url && !force )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._url_loading = url;\\r\\n\\tthis._video.crossOrigin = \\\"anonymous\\\";\\r\\n\\tthis._video.src = final_url;\\r\\n\\t//this._video.type = \\\"type=video/mp4\\\";\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.bindVideoEvents = function( video )\\r\\n{\\r\\n\\tvideo._component = this;\\r\\n\\r\\n\\tif(video.has_litescene_events)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvideo.has_litescene_events = true;\\r\\n\\r\\n\\tthis._video.addEventListener(\\\"loadedmetadata\\\",function(e) {\\r\\n\\t\\t//onload\\r\\n\\t\\tconsole.log(\\\"Duration: \\\" + this.duration + \\\" seconds\\\");\\r\\n\\t\\tconsole.log(\\\"Size: \\\" + this.videoWidth + \\\",\\\" + this.videoHeight);\\r\\n\\t\\tthis.width = this.videoWidth;\\r\\n\\t\\tthis.height = this.videoHeight;\\r\\n\\t\\tif(!this._component)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar scene = this._component._root ? this._component._root.scene : null;\\r\\n\\t\\tif(scene && scene.state === LS.RUNNING && this._component._autoplay)\\r\\n\\t\\t\\tthis._component.play();\\r\\n\\t});\\r\\n\\r\\n\\t/*\\r\\n\\tthis._video.addEventListener(\\\"progress\\\",function(e) {\\r\\n\\t\\t//onload\\r\\n\\t});\\r\\n\\t*/\\r\\n\\r\\n\\tthis._video.addEventListener(\\\"error\\\",function(e) {\\r\\n\\t\\tconsole.error(\\\"Error loading video: \\\" + this.src);\\r\\n\\t\\tif (this.error) {\\r\\n\\t\\t switch (this.error.code) {\\r\\n\\t\\t case this.error.MEDIA_ERR_ABORTED:\\r\\n\\t\\t\\t console.error(\\\"You stopped the video.\\\");\\r\\n\\t\\t\\t break;\\r\\n\\t\\t case this.error.MEDIA_ERR_NETWORK:\\r\\n\\t\\t\\t console.error(\\\"Network error - please try again later.\\\");\\r\\n\\t\\t\\t break;\\r\\n\\t\\t case this.error.MEDIA_ERR_DECODE:\\r\\n\\t\\t\\t console.error(\\\"Video is broken..\\\");\\r\\n\\t\\t\\t break;\\r\\n\\t\\t case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:\\r\\n\\t\\t\\t console.error(\\\"Sorry, your browser can't play this video.\\\");\\r\\n\\t\\t\\t break;\\r\\n\\t\\t }\\r\\n\\t\\t}\\r\\n\\t});\\r\\n\\r\\n\\tthis._video.addEventListener(\\\"ended\\\",function(e) {\\r\\n\\t\\tif(!this._component)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tconsole.log(\\\"Ended.\\\");\\r\\n\\t\\tvar scene = this._component._root ? this._component._root.scene : null;\\r\\n\\t\\tif(scene && scene.state === LS.RUNNING && this._component._autoplay)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.currentTime = 0;\\r\\n\\t\\t\\tthis._component.play(); //loop\\r\\n\\t\\t}\\r\\n\\t});\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.play = function()\\r\\n{\\r\\n\\tif(this._video.videoWidth)\\r\\n\\t\\tthis._video.play();\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.playPause = function()\\r\\n{\\r\\n\\tif(this._video.paused)\\r\\n\\t\\tthis.play();\\r\\n\\telse\\r\\n\\t\\tthis.pause();\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.stop = function()\\r\\n{\\r\\n\\tthis._video.pause();\\r\\n\\tthis._video.currentTime = 0;\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.pause = function()\\r\\n{\\r\\n\\tthis._video.pause();\\r\\n}\\r\\n\\r\\n//uploads the video frame to the GPU\\r\\nVideoPlayer.prototype.onBeforeRender = function(e)\\r\\n{\\r\\n\\t//no video assigned or not loaded yet\\r\\n\\tif(!this.enabled || this._video.videoWidth == 0)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar video = this._video;\\r\\n\\r\\n\\tvar must_have_mipmaps = this.generate_mipmaps;\\r\\n\\tif( !GL.isPowerOfTwo(video.videoWidth) || !GL.isPowerOfTwo(video.videoHeight) )\\r\\n\\t\\tmust_have_mipmaps = false;\\r\\n\\r\\n\\t//create destination texture\\r\\n\\tif(!this._texture || this._texture.width != video.videoWidth || this._texture.has_mipmaps != must_have_mipmaps )\\r\\n\\t{\\r\\n\\t\\tthis._texture = new GL.Texture( video.videoWidth, video.videoHeight, { format: GL.RGB, minFilter: must_have_mipmaps ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR, magFilter: gl.LINEAR });\\r\\n\\t\\tthis._texture.has_mipmaps = must_have_mipmaps;\\r\\n\\t}\\r\\n\\r\\n\\t//avoid reuploading the same frame again in case it is paused\\r\\n\\tif(this._texture._video_time != video.currentTime )\\r\\n\\t{\\r\\n\\t\\tthis._texture.uploadImage( video );\\t\\r\\n\\t\\tif( must_have_mipmaps )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._texture.bind(0);\\r\\n\\t\\t\\tgl.generateMipmap( this._texture.texture_type );\\r\\n\\t\\t\\tthis._texture.unbind(0);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis._texture._video_time = video.currentTime;\\r\\n\\t\\t\\r\\n\\t}\\r\\n\\r\\n\\t//make texture available to all the system\\r\\n\\tif(this.texture_name)\\r\\n\\t\\tLS.RM.registerResource( this.texture_name, this._texture );\\r\\n\\r\\n\\t//assign to material color texture\\r\\n\\tif(this.render_mode == VideoPlayer.TO_MATERIAL)\\r\\n\\t{\\r\\n\\t\\tvar material = this._root.getMaterial();\\r\\n\\t\\tif(material)\\r\\n\\t\\t\\tmaterial.setTexture( \\\"color\\\", this.texture_name );\\r\\n\\t}\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.onBeforeRenderScene = function( e )\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(this.render_mode != VideoPlayer.BACKGROUND && this.render_mode != VideoPlayer.BACKGROUND_STRETCH)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!this._texture)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tgl.disable( gl.BLEND );\\r\\n\\tgl.disable( gl.CULL_FACE );\\r\\n\\tgl.disable( gl.DEPTH_TEST );\\r\\n\\tthis._texture.toViewport();\\r\\n}\\r\\n\\r\\nVideoPlayer.prototype.onCollectInstances = function( e, RIs )\\r\\n{\\r\\n\\tif( !this.enabled || this.render_mode != VideoPlayer.PLANE )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( !this._material )\\r\\n\\t\\tthis._material = new LS.StandardMaterial({ flags: { ignore_lights: true, two_sided: true }});\\r\\n\\r\\n\\tif(!this._plane_ri)\\r\\n\\t{\\r\\n\\t\\tvar RI = this._plane_ri = new LS.RenderInstance();\\r\\n\\t\\tvar mesh = GL.Mesh.plane();\\r\\n\\t\\tRI.setMesh( mesh );\\r\\n\\t\\tRI.setMaterial( this._material );\\r\\n\\t}\\r\\n\\r\\n\\tthis._plane_ri.fromNode( this._root ); //update model\\r\\n\\tthis._material.setTexture(\\\"color\\\", this._texture );\\r\\n\\tRIs.push( this._plane_ri);\\r\\n}\\r\\n\\r\\nLS.registerComponent( VideoPlayer );\\r\\n///@FILE:../src/components/interactiveController.js\\r\\n///@INFO: UNCOMMON\\r\\n/**\\r\\n* Allows to easily test interaction between the user and the scene, attach the InteractiveController to the root and the mouse down,move and up events will\\r\\n* be processed using a raycast and trigger events.\\r\\n* @namespace LS\\r\\n* @class InteractiveController\\r\\n* @constructor\\r\\n* @param {Object} last serialized data [optional]\\r\\n*/\\r\\nfunction InteractiveController(o)\\r\\n{\\r\\n\\tthis.enabled = true;\\r\\n\\tthis.mode = InteractiveController.PICKING;\\r\\n\\tthis.layers = 3;\\r\\n\\r\\n\\tthis._last_collision = null;\\r\\n\\r\\n\\tif(o)\\r\\n\\t\\tthis.configure(o);\\r\\n}\\r\\n\\r\\nInteractiveController.icon = \\\"mini-icon-cursor.png\\\";\\r\\n\\r\\nInteractiveController.PICKING = 1;\\r\\nInteractiveController.BOUNDING = 2;\\r\\nInteractiveController.COLLIDERS = 3;\\r\\nInteractiveController.RENDER_INSTANCES = 4;\\r\\n\\r\\nInteractiveController[\\\"@mode\\\"] = { type: \\\"enum\\\", values: { \\\"Picking\\\": InteractiveController.PICKING, \\\"Bounding\\\": InteractiveController.BOUNDING, \\\"Colliders\\\": InteractiveController.COLLIDERS }};\\r\\nInteractiveController[\\\"@layers\\\"] = { type: \\\"layers\\\" };\\r\\n\\r\\nInteractiveController.prototype.onAddedToScene = function(scene)\\r\\n{\\r\\n\\tLEvent.bind( scene, \\\"mousedown\\\", this._onMouse, this );\\r\\n\\tLEvent.bind( scene, \\\"mousemove\\\", this._onMouse, this );\\r\\n\\tLEvent.bind( scene, \\\"mouseup\\\", this._onMouse, this );\\r\\n}\\r\\n\\r\\nInteractiveController.prototype.onRemovedFromScene = function(scene)\\r\\n{\\r\\n\\tLEvent.unbindAll( scene, this );\\r\\n}\\r\\n\\r\\nInteractiveController.prototype.getNodeUnderMouse = function( e )\\r\\n{\\r\\n\\tvar layers = this.layers;\\r\\n\\r\\n\\tif(this.mode == InteractiveController.PICKING)\\r\\n\\t\\treturn LS.Picking.getNodeAtCanvasPosition( e.canvasx, e.canvasy, null, layers );\\r\\n\\r\\n\\tvar camera = LS.Renderer.getCameraAtPosition( e.canvasx, e.canvasy );\\r\\n\\tif(!camera)\\r\\n\\t\\treturn null;\\r\\n\\tvar ray = camera.getRay( e.canvasx, e.canvasy );\\r\\n\\r\\n\\tif(this.mode == InteractiveController.BOUNDING)\\r\\n\\t{\\r\\n\\t\\tvar collisions = LS.Physics.raycastRenderInstances( ray.origin, ray.direction, { layers: layers } );\\r\\n\\t\\tif(!collisions || !collisions.length)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tthis._last_collision = collisions[0];\\r\\n\\t\\treturn collisions[0].node;\\r\\n\\t}\\r\\n\\r\\n\\tif(this.mode == InteractiveController.RENDER_INSTANCES)\\r\\n\\t{\\r\\n\\t\\tvar collisions = LS.Physics.raycastRenderInstances( ray.origin, ray.direction, { layers: layers, triangle_collision: true } );\\r\\n\\t\\tif(!collisions || !collisions.length)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tthis._last_collision = collisions[0];\\r\\n\\t\\treturn collisions[0].node;\\r\\n\\t}\\r\\n\\r\\n\\tif(this.mode == InteractiveController.COLLIDERS)\\r\\n\\t{\\r\\n\\t\\tvar collisions = LS.Physics.raycast( ray.origin, ray.direction, { layers: layers } );\\r\\n\\t\\tif(!collisions || !collisions.length)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tthis._last_collision = collisions[0];\\r\\n\\t\\treturn collisions[0].node;\\r\\n\\t}\\r\\n\\r\\n\\treturn null;\\r\\n\\r\\n}\\r\\n\\r\\nInteractiveController.prototype._onMouse = function(type, e)\\r\\n{\\r\\n\\tif(!this.enabled)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//Intereactive: check which node was clicked (this is a mode that helps clicking stuff)\\r\\n\\tif(e.eventType == \\\"mousedown\\\" || e.eventType == \\\"mousewheel\\\" )\\r\\n\\t{\\r\\n\\t\\tvar node = this.getNodeUnderMouse(e);\\r\\n\\t\\tthis._clicked_node = node;\\r\\n\\t\\tif(this._clicked_node && e.eventType == \\\"mousedown\\\" && e.button == 0 )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.log(\\\"Node clicked: \\\" + this._clicked_node.name );\\r\\n\\t\\t\\tLEvent.trigger( this._clicked_node, \\\"clicked\\\", this._clicked_node ); //event in node clicked\\r\\n\\t\\t\\tLEvent.trigger( this._root, \\\"node_clicked\\\", this._clicked_node ); //event in this node\\r\\n\\t\\t\\tLEvent.trigger( this._root.scene, \\\"node_clicked\\\", this._clicked_node ); //event in scene\\r\\n\\t\\t\\tif(this.onNodeClicked) //extra method, if you inject a new method in this component\\r\\n\\t\\t\\t\\tthis.onNodeClicked( this._clicked_node );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar levent = null; //levent dispatched\\r\\n\\r\\n\\t//send event to clicked node\\r\\n\\tif(this._clicked_node) // && this._clicked_node.flags.interactive)\\r\\n\\t{\\r\\n\\t\\te.scene_node = this._clicked_node;\\r\\n\\t\\tlevent = LEvent.trigger( this._clicked_node, e.eventType, e );\\r\\n\\t}\\r\\n\\r\\n\\tif(e.eventType == \\\"mouseup\\\")\\r\\n\\t\\tthis._clicked_node = null;\\r\\n\\r\\n\\tif(this._clicked_node)\\r\\n\\t\\treturn true;\\r\\n}\\r\\n\\r\\n\\r\\nLS.registerComponent( InteractiveController );\\r\\n\\r\\n///@FILE:../src/scene.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* The Scene contains all the info about the Scene and nodes\\r\\n*\\r\\n* @class Scene\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\n//event definitions for scene\\r\\nEVENT.INIT = \\\"init\\\";\\r\\nEVENT.CLEAR = \\\"clear\\\";\\r\\nEVENT.PRECONFIGURE = \\\"preConfigure\\\";\\r\\nEVENT.CONFIGURE = \\\"configure\\\";\\r\\nEVENT.CHANGE = \\\"change\\\";\\r\\nEVENT.LOAD = \\\"load\\\";\\r\\nEVENT.LOAD_COMPLETED = \\\"load_completed\\\";\\r\\nEVENT.AWAKE = \\\"awake\\\";\\r\\nEVENT.START = \\\"start\\\";\\r\\nEVENT.PAUSE = \\\"pause\\\";\\r\\nEVENT.UNPAUSE = \\\"unpause\\\";\\r\\nEVENT.COLLECT_RENDER_INSTANCES = \\\"collectRenderInstances\\\";\\r\\nEVENT.COLLECT_PHYSIC_INSTANCES = \\\"collectPhysicInstances\\\";\\r\\nEVENT.COLLECT_LIGHTS = \\\"collectLights\\\";\\r\\nEVENT.COLLECT_CAMERAS = \\\"collectCameras\\\";\\r\\nEVENT.COLLECT_DATA = \\\"collectData\\\";\\r\\nEVENT.SERIALIZE = \\\"serialize\\\";\\r\\nEVENT.FINISH = \\\"finish\\\";\\r\\n\\r\\nfunction Scene()\\r\\n{\\r\\n\\tthis.uid = LS.generateUId(\\\"TREE-\\\");\\r\\n\\r\\n\\tthis._state = LS.STOPPED;\\r\\n\\r\\n\\tthis._root = new LS.SceneNode(\\\"root\\\");\\r\\n\\tthis._root.removeAllComponents();\\r\\n\\tthis._root._is_root = true;\\r\\n\\tthis._root._in_tree = this;\\r\\n\\tthis._nodes = [ this._root ];\\r\\n\\tthis._nodes_by_name = { \\\"root\\\" : this._root };\\r\\n\\tthis._nodes_by_uid = {};\\r\\n\\tthis._nodes_by_uid[ this._root.uid ] = this._root;\\r\\n\\tthis._components_by_uid = {};\\r\\n\\r\\n\\t//used to stored info when collecting from nodes\\r\\n\\tthis._uniforms = {};\\r\\n\\tthis._instances = [];\\r\\n\\tthis._lights = [];\\r\\n\\tthis._cameras = [];\\r\\n\\tthis._colliders = [];\\r\\n\\tthis._reflection_probes = [];\\r\\n\\r\\n\\t//MOST OF THE PARAMETERS ARE CREATED IN init() METHOD\\r\\n\\r\\n\\t//in case the resources base path are located somewhere else, if null the default is used\\r\\n\\tthis.external_repository = null;\\r\\n\\r\\n\\t//work in progress, not finished yet. This will contain all the objects in cells\\r\\n\\tthis._spatial_container = new LS.SpatialContainer();\\r\\n\\r\\n\\tthis.external_scripts = []; //external scripts that must be loaded before initializing the scene (mostly libraries used by this scene)\\r\\n\\tthis.global_scripts = []; //scripts that are located in the resources folder and must be loaded before launching the app\\r\\n\\tthis.preloaded_resources = {}; //resources that must be loaded, appart from the ones in the components\\r\\n\\r\\n\\t//track with global animations of the scene\\r\\n\\tthis.animation = null;\\r\\n\\r\\n\\t//FEATURES NOT YET FULLY IMPLEMENTED\\r\\n\\tthis._local_resources = {}; //used to store resources that go with the scene\\r\\n\\tthis.texture_atlas = null;\\r\\n\\r\\n\\tthis.layer_names = [\\\"main\\\",\\\"secondary\\\"];\\r\\n\\r\\n\\tLEvent.bind( this, \\\"treeItemAdded\\\", this.onNodeAdded, this );\\r\\n\\tLEvent.bind( this, \\\"treeItemRemoved\\\", this.onNodeRemoved, this );\\r\\n\\r\\n\\tthis._shaderblock_info = null;\\r\\n\\r\\n\\tthis.init();\\r\\n}\\r\\n\\r\\n//LS.extendClass( Scene, ComponentContainer ); //scene could also have components\\r\\n\\r\\nObject.defineProperty( Scene.prototype, \\\"root\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._root;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"Root node cannot be replaced\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( Scene.prototype, \\\"time\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._time;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"Cannot set time directly\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( Scene.prototype, \\\"state\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._state;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"Cannot set state directly, use start, finish, pause, unpause\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( Scene.prototype, \\\"globalTime\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._global_time;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"Cannot set global_time directly\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\nObject.defineProperty( Scene.prototype, \\\"frame\\\", {\\r\\n\\tenumerable: true,\\r\\n\\tget: function() {\\r\\n\\t\\treturn this._frame;\\r\\n\\t},\\r\\n\\tset: function(v) {\\r\\n\\t\\tthrow(\\\"Cannot set frame directly\\\");\\r\\n\\t}\\r\\n});\\r\\n\\r\\n//Some useful events\\r\\nScene.supported_events = [\\\"start\\\",\\\"update\\\",\\\"finish\\\",\\\"clear\\\",\\\"beforeReload\\\",\\\"change\\\",\\\"afterRender\\\",\\\"configure\\\",\\\"nodeAdded\\\",\\\"nodeChangeParent\\\",\\\"nodeComponentRemoved\\\",\\\"reload\\\",\\\"renderPicking\\\",\\\"scene_loaded\\\",\\\"serialize\\\"];\\r\\n\\r\\n//methods\\r\\n\\r\\n/**\\r\\n* This initializes the content of the scene.\\r\\n* Call it to clear the scene content\\r\\n*\\r\\n* @method init\\r\\n* @return {Boolean} Returns true on success\\r\\n*/\\r\\nScene.prototype.init = function()\\r\\n{\\r\\n\\tthis.id = \\\"\\\";\\r\\n\\t//this.materials = {}; //shared materials cache: moved to LS.RM.resources\\r\\n\\tthis.external_repository = null;\\r\\n\\r\\n\\tthis.global_scripts = [];\\r\\n\\tthis.external_scripts = [];\\r\\n\\tthis.preloaded_resources = {};\\r\\n\\tthis.texture_atlas = null;\\r\\n\\r\\n\\tthis._root.removeAllComponents();\\r\\n\\tthis._root.uid = LS.generateUId(\\\"NODE-\\\");\\r\\n\\r\\n\\tthis._nodes = [ this._root ];\\r\\n\\tthis._nodes_by_name = { \\\"root\\\": this._root };\\r\\n\\tthis._nodes_by_uid = {};\\r\\n\\tthis._nodes_by_uid[ this._root.uid ] = this._root;\\r\\n\\tthis._components_by_uid = {};\\r\\n\\r\\n\\t//WIP\\r\\n\\tthis._spatial_container.clear();\\r\\n\\r\\n\\t//default components\\r\\n\\tthis.info = new LS.Components.GlobalInfo();\\r\\n\\tthis._root.addComponent( this.info );\\r\\n\\tthis._root.addComponent( new LS.Camera({ eye:[0,100,100], center:[0,0,0]} ) );\\r\\n\\tthis._root.addComponent( new LS.Light({ position: vec3.fromValues(100,100,100), target: vec3.fromValues(0,0,0) }) );\\r\\n\\r\\n\\tthis._frame = 0;\\r\\n\\tthis._last_collect_frame = -1; //force collect\\r\\n\\tthis._state = LS.STOPPED;\\r\\n\\r\\n\\tthis._time = 0;\\r\\n\\tthis._global_time = 0; //in seconds\\r\\n\\tthis._start_time = 0; //in seconds\\r\\n\\tthis._last_dt = 1/60; //in seconds\\r\\n\\tthis._must_redraw = true;\\r\\n\\tthis._fixed_update_timestep = 1/60;\\r\\n\\tthis._remaining_fixed_update_time = 0;\\r\\n\\r\\n\\tif(this.selected_node) \\r\\n\\t\\tdelete this.selected_node;\\r\\n\\r\\n\\tthis.layer_names = [\\\"main\\\",\\\"secondary\\\"];\\r\\n\\tthis.animation = null;\\r\\n\\tthis._local_resources = {}; //not used yet\\r\\n\\tthis.extra = {};\\r\\n}\\r\\n\\r\\n/**\\r\\n* Clears the scene using the init function\\r\\n* and trigger a \\\"clear\\\" LEvent\\r\\n*\\r\\n* @method clear\\r\\n*/\\r\\nScene.prototype.clear = function()\\r\\n{\\r\\n\\t//remove all nodes to ensure no lose callbacks are left\\r\\n\\twhile(this._root._children && this._root._children.length)\\r\\n\\t\\tthis._root.removeChild(this._root._children[0], false, true ); //recompute_transform, remove_components\\r\\n\\r\\n\\t//remove scene components\\r\\n\\tthis._root.processActionInComponents(\\\"onRemovedFromNode\\\",this); //send to components\\r\\n\\tthis._root.processActionInComponents(\\\"onRemovedFromScene\\\",this); //send to components\\r\\n\\r\\n\\tthis._instances.length = 0;\\r\\n\\tthis._lights.length = 0;\\r\\n\\tthis._cameras.length = 0;\\r\\n\\tthis._colliders.length = 0;\\r\\n\\tthis._reflection_probes.length = 0;\\r\\n\\tthis._local_resources = {};\\r\\n\\r\\n\\tthis.init();\\r\\n\\t/**\\r\\n\\t * Fired when the whole scene is cleared\\r\\n\\t *\\r\\n\\t * @event clear\\r\\n\\t */\\r\\n\\tLEvent.trigger(this, EVENT.CLEAR );\\r\\n\\tLEvent.trigger(this, EVENT.CHANGE );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Configure the Scene using an object (the object can be obtained from the function serialize)\\r\\n* Inserts the nodes, configure them, and change the parameters\\r\\n* ATTENTION: Destroys all previously existing info\\r\\n*\\r\\n* @method configure\\r\\n* @param {Object} scene_info the object containing all the info about the nodes and config of the scene\\r\\n*/\\r\\nScene.prototype.configure = function( scene_info )\\r\\n{\\r\\n\\tif(!scene_info || scene_info.constructor === String)\\r\\n\\t\\tthrow(\\\"Scene configure requires object\\\");\\r\\n\\r\\n\\tLEvent.trigger(this, EVENT.PRECONFIGURE, scene_info);\\r\\n\\r\\n\\tthis._root.removeAllComponents(); //remove light, camera, skybox\\r\\n\\r\\n\\t//this._components = [];\\r\\n\\t//this.camera = this.light = null; //legacy\\r\\n\\r\\n\\tif(scene_info.uid)\\r\\n\\t\\tthis.uid = scene_info.uid;\\r\\n\\r\\n\\tif((scene_info.object_class || scene_info.object_type) != \\\"Scene\\\") //legacy\\r\\n\\t\\tconsole.warn(\\\"Warning: object set to scene doesnt look like a propper one.\\\", scene_info);\\r\\n\\r\\n\\tif(scene_info.external_repository)\\r\\n\\t\\tthis.external_repository = scene_info.external_repository;\\r\\n\\r\\n\\t//extra info that the user wanted to save (comments, etc)\\r\\n\\tif(scene_info.extra)\\r\\n\\t\\tthis.extra = scene_info.extra;\\r\\n\\r\\n\\t//this clears all the nodes\\r\\n\\tif(scene_info.root)\\r\\n\\t{\\r\\n\\t\\tthis._spatial_container.clear(); // is this necessary?\\r\\n\\t\\tLS._pending_encoded_objects = [];\\r\\n\\t\\tthis._root.configure( scene_info.root );\\r\\n\\t\\tLS.resolvePendingEncodedObjects();\\r\\n\\t}\\r\\n\\r\\n\\tif( scene_info.global_scripts )\\r\\n\\t\\tthis.global_scripts = scene_info.global_scripts.concat();\\r\\n\\r\\n\\tif( scene_info.external_scripts )\\r\\n\\t\\tthis.external_scripts = scene_info.external_scripts.concat();\\r\\n\\r\\n\\tif( scene_info.preloaded_resources )\\r\\n\\t\\tthis.preloaded_resources = LS.cloneObject( scene_info.preloaded_resources );\\r\\n\\r\\n\\tif( scene_info.local_resources )\\r\\n\\t\\tthis._local_resources = scene_info.local_resources;\\r\\n\\r\\n\\tif( scene_info.layer_names )\\r\\n\\t\\tthis.layer_names = scene_info.layer_names.concat();\\r\\n\\r\\n\\tif( scene_info.animation )\\r\\n\\t\\tthis.animation = new LS.Animation( scene_info.animation );\\r\\n\\r\\n\\t//if(scene_info.components)\\r\\n\\t//\\tthis.configureComponents( scene_info );\\r\\n\\r\\n\\tif( scene_info.editor )\\r\\n\\t\\tthis._editor = scene_info.editor;\\r\\n\\r\\n\\tif( scene_info.texture_atlas )\\r\\n\\t\\tthis.texture_atlas = scene_info.texture_atlas;\\r\\n\\r\\n\\t/**\\r\\n\\t * Fired after the scene has been configured\\r\\n\\t * @event configure\\r\\n\\t * @param {Object} scene_info contains all the info to do the configuration\\r\\n\\t */\\r\\n\\tLEvent.trigger(this, EVENT.CONFIGURE,scene_info);\\r\\n\\tLEvent.trigger(this, EVENT.AWAKE );\\r\\n\\t/**\\r\\n\\t * Fired when something changes in the scene\\r\\n\\t * @event change\\r\\n\\t * @param {Object} scene_info contains all the info to do the configuration\\r\\n\\t */\\r\\n\\tLEvent.trigger(this, EVENT.CHANGE );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Creates and object containing all the info about the scene and nodes.\\r\\n* The oposite of configure.\\r\\n* It calls the serialize method in every node\\r\\n*\\r\\n* @method serialize\\r\\n* @return {Object} return a JS Object with all the scene info\\r\\n*/\\r\\n\\r\\nScene.prototype.serialize = function( simplified )\\r\\n{\\r\\n\\tvar o = {};\\r\\n\\r\\n\\to.uid = this.uid;\\r\\n\\to.object_class = LS.getObjectClassName(this);\\r\\n\\r\\n\\to.external_repository = this.external_repository;\\r\\n\\r\\n\\t//o.nodes = [];\\r\\n\\to.extra = this.extra || {};\\r\\n\\r\\n\\t//add nodes\\r\\n\\to.root = this.root.serialize( false, simplified );\\r\\n\\r\\n\\tif(this.animation)\\r\\n\\t\\to.animation = this.animation.serialize();\\r\\n\\r\\n\\to.layer_names = this.layer_names.concat();\\r\\n\\to.global_scripts = this.global_scripts.concat();\\r\\n\\to.external_scripts = this.external_scripts.concat();\\r\\n\\to.preloaded_resources = LS.cloneObject( this.preloaded_resources );\\r\\n\\to.texture_atlas = LS.cloneObject( this.texture_atlas );\\r\\n\\to.local_resources = LS.cloneObject( this._local_resources );\\r\\n\\r\\n\\tif( this._editor )\\r\\n\\t\\to.editor = this._editor;\\r\\n\\r\\n\\t//this.serializeComponents( o );\\r\\n\\r\\n\\t/**\\r\\n\\t * Fired after the scene has been serialized to an object\\r\\n\\t * @event serialize\\r\\n\\t * @param {Object} object to store the persistent info\\r\\n\\t */\\r\\n\\tLEvent.trigger(this,EVENT.SERIALIZE,o);\\r\\n\\r\\n\\treturn o;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Assigns a scene from a JSON description (or WBIN,ZIP)\\r\\n*\\r\\n* @method setFromJSON\\r\\n* @param {String} data JSON object containing the scene\\r\\n* @param {Function}[on_complete=null] the callback to call when the scene is ready\\r\\n* @param {Function}[on_error=null] the callback to call if there is a loading error\\r\\n* @param {Function}[on_progress=null] it is called while loading the scene info (not the associated resources)\\r\\n* @param {Function}[on_resources_loaded=null] it is called when all the resources had been loaded\\r\\n* @param {Function}[on_scripts_loaded=null] the callback to call when the loading is complete but before assigning the scene\\r\\n*/\\r\\n\\r\\nScene.prototype.setFromJSON = function( data, on_complete, on_error, on_progress, on_resources_loaded, on_scripts_loaded )\\r\\n{\\r\\n\\tif(!data)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar that = this;\\r\\n\\r\\n\\tif(data.constructor === String)\\r\\n\\t{\\r\\n\\t\\ttry\\r\\n\\t\\t{\\r\\n\\t\\t\\tdata = JSON.parse( data );\\r\\n\\t\\t}\\r\\n\\t\\tcatch (err)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.log(\\\"Error: \\\" + err );\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar scripts = LS.Scene.getScriptsList( data, true );\\r\\n\\r\\n\\t//check JSON for special scripts\\r\\n\\tif ( scripts.length )\\r\\n\\t\\tthis.loadScripts( scripts, function(){ inner_success( data ); }, inner_error );\\r\\n\\telse\\r\\n\\t\\tinner_success( data );\\r\\n\\r\\n\\tfunction inner_success( response )\\r\\n\\t{\\r\\n\\t\\tif(on_scripts_loaded)\\r\\n\\t\\t\\ton_scripts_loaded(that,response);\\r\\n\\r\\n\\t\\tthat.init();\\r\\n\\t\\tthat.configure(response);\\r\\n\\r\\n\\t\\tif( that.texture_atlas )\\r\\n\\t\\t\\tLS.RM.loadTextureAtlas( that.texture_atlas, inner_preloaded_all );\\r\\n\\t\\telse\\r\\n\\t\\t\\tinner_preloaded_all();\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_preloaded_all()\\r\\n\\t{\\r\\n\\t\\tthat.loadResources( inner_all_loaded );\\r\\n\\t\\t/**\\r\\n\\t\\t * Fired when the scene has been loaded but before the resources\\r\\n\\t\\t * @event load\\r\\n\\t\\t */\\r\\n\\t\\tLEvent.trigger(that, EVENT.LOAD );\\r\\n\\r\\n\\t\\tif(!LS.ResourcesManager.isLoading())\\r\\n\\t\\t\\tinner_all_loaded();\\r\\n\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete(that);\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_all_loaded()\\r\\n\\t{\\r\\n\\t\\tif(on_resources_loaded)\\r\\n\\t\\t\\ton_resources_loaded(that);\\r\\n\\t\\t/**\\r\\n\\t\\t * Fired after all resources have been loaded\\r\\n\\t\\t * @event loadCompleted\\r\\n\\t\\t */\\r\\n\\t\\tLEvent.trigger( that, EVENT.LOAD_COMPLETED );\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_error(err,script_url)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"Error loading script: \\\" + script_url);\\r\\n\\t\\tif(on_error)\\r\\n\\t\\t\\ton_error(err);\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Loads a scene from a relative url pointing to a JSON description (or WBIN,ZIP)\\r\\n* Warning: this url is not passed through the LS.ResourcesManager so the url is absolute\\r\\n*\\r\\n* @method load\\r\\n* @param {String} url where the JSON object containing the scene is stored\\r\\n* @param {Function}[on_complete=null] the callback to call when the loading is complete\\r\\n* @param {Function}[on_error=null] the callback to call if there is a loading error\\r\\n* @param {Function}[on_progress=null] it is called while loading the scene info (not the associated resources)\\r\\n* @param {Function}[on_resources_loaded=null] it is called when all the resources had been loaded\\r\\n*/\\r\\n\\r\\nScene.prototype.load = function( url, on_complete, on_error, on_progress, on_resources_loaded, on_loaded )\\r\\n{\\r\\n\\tif(!url)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar that = this;\\r\\n\\r\\n\\tvar extension = LS.ResourcesManager.getExtension( url );\\r\\n\\tvar format_info = LS.Formats.getFileFormatInfo( extension );\\r\\n\\tif(!format_info) //hack, to avoid errors\\r\\n\\t\\tformat_info = { dataType: \\\"json\\\" };\\r\\n\\r\\n\\t//request scene file using our own library\\r\\n\\tLS.Network.request({\\r\\n\\t\\turl: url,\\r\\n\\t\\tnocache: true,\\r\\n\\t\\tdataType: extension == \\\"json\\\" ? \\\"json\\\" : (format_info.dataType || \\\"text\\\"), //datatype of json is text...\\r\\n\\t\\tsuccess: extension == \\\"json\\\" ? inner_json_loaded : inner_data_loaded,\\r\\n\\t\\tprogress: on_progress,\\r\\n\\t\\terror: inner_error\\r\\n\\t});\\r\\n\\r\\n\\tthis._state = LS.LOADING;\\r\\n\\r\\n\\t/**\\r\\n\\t * Fired before loading scene\\r\\n\\t * @event beforeLoad\\r\\n\\t */\\r\\n\\tLEvent.trigger(this,\\\"beforeLoad\\\");\\r\\n\\r\\n\\tfunction inner_data_loaded( response )\\r\\n\\t{\\r\\n\\t\\t//process whatever we loaded (in case it is a pack)\\r\\n\\t\\tLS.ResourcesManager.processResource( url, response, null, inner_data_processed );\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_data_processed( pack_url, pack )\\r\\n\\t{\\r\\n\\t\\tif(!pack)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//for DAEs\\r\\n\\t\\tif( pack.object_class == \\\"Scene\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tinner_json_loaded( pack );\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\telse if( pack.object_class == \\\"SceneNode\\\") \\r\\n\\t\\t{\\r\\n\\t\\t\\tvar root = pack.serialize();\\r\\n\\t\\t\\tinner_json_loaded( { object_class: \\\"Scene\\\", root: root } );\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//for packs\\r\\n\\t\\tif( !pack._data || !pack._data[\\\"scene.json\\\"] )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"Error loading PACK, doesnt look like it has a valid scene inside\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tvar scene = JSON.parse( pack._data[\\\"scene.json\\\"] );\\r\\n\\r\\n\\t\\tinner_json_loaded( scene );\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_json_loaded( response )\\r\\n\\t{\\r\\n\\t\\tif( response.constructor !== Object )\\r\\n\\t\\t\\tthrow(\\\"response must be object\\\");\\r\\n\\r\\n\\t\\tvar scripts = LS.Scene.getScriptsList( response, true );\\r\\n\\r\\n\\t\\t//check JSON for special scripts\\r\\n\\t\\tif ( scripts.length )\\r\\n\\t\\t\\tthat.loadScripts( scripts, function(){ inner_success(response); }, on_error );\\r\\n\\t\\telse\\r\\n\\t\\t\\tinner_success( response );\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_success( response )\\r\\n\\t{\\r\\n\\t\\tif(on_loaded)\\r\\n\\t\\t\\ton_loaded(that, url);\\r\\n\\r\\n\\t\\tthat.init();\\r\\n\\t\\tthat.configure(response);\\r\\n\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete(that, url);\\r\\n\\r\\n\\t\\tthat.loadResources( inner_all_loaded );\\r\\n\\t\\tLEvent.trigger(that, EVENT.LOAD );\\r\\n\\r\\n\\t\\tif(!LS.ResourcesManager.isLoading())\\r\\n\\t\\t\\tinner_all_loaded();\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_all_loaded()\\r\\n\\t{\\r\\n\\t\\tif(on_resources_loaded)\\r\\n\\t\\t\\ton_resources_loaded(that, url);\\r\\n\\t\\tLEvent.trigger(that, EVENT.LOAD_COMPLETED );\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_error(e)\\r\\n\\t{\\r\\n\\t\\tvar err_code = (e && e.target) ? e.target.status : 0;\\r\\n\\t\\tconsole.warn(\\\"Error loading scene: \\\" + url + \\\" -> \\\" + err_code);\\r\\n\\t\\tif(on_error)\\r\\n\\t\\t\\ton_error(url, err_code, e);\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Loads a scene from a relative url pointing to a JSON description (or WBIN,ZIP)\\r\\n* It uses the resources folder as the root folder (in comparison with the regular load function)\\r\\n*\\r\\n* @method loadFromResources\\r\\n* @param {String} url where the JSON object containing the scene is stored\\r\\n* @param {Function}[on_complete=null] the callback to call when the loading is complete\\r\\n* @param {Function}[on_error=null] the callback to call if there is a loading error\\r\\n* @param {Function}[on_progress=null] it is called while loading the scene info (not the associated resources)\\r\\n* @param {Function}[on_resources_loaded=null] it is called when all the resources had been loaded\\r\\n*/\\r\\nScene.prototype.loadFromResources = function( url, on_complete, on_error, on_progress, on_resources_loaded )\\r\\n{\\r\\n\\turl = LS.ResourcesManager.getFullURL( url );\\r\\n\\tthis.load( url, on_complete, on_error, on_progress, on_resources_loaded );\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\n/**\\r\\n* Static method, returns a list of all the scripts that must be loaded, in order and with the full path\\r\\n*\\r\\n* @method Scene.getScriptsList\\r\\n* @param {Scene|Object} scene the object containing info about the scripts (could be a scene or a JSON object)\\r\\n* @param {Boolean} allow_local if we allow local resources\\r\\n* @param {Boolean} full_paths if true it will return the full path to every resource\\r\\n*/\\r\\nScene.getScriptsList = function( scene, allow_local, full_paths )\\r\\n{\\r\\n\\tif(!scene)\\r\\n\\t\\tthrow(\\\"Scene.getScriptsList: scene cannot be null\\\");\\r\\n\\r\\n\\tvar scripts = [];\\r\\n\\tif ( scene.external_scripts && scene.external_scripts.length )\\r\\n\\t\\tscripts = scripts.concat( scene.external_scripts );\\r\\n\\tif ( scene.global_scripts && scene.global_scripts.length )\\r\\n\\t{\\r\\n\\t\\tfor(var i in scene.global_scripts)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar script_url = scene.global_scripts[i];\\r\\n\\t\\t\\tif(!script_url || LS.ResourcesManager.getExtension( script_url ) != \\\"js\\\" )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar res = LS.ResourcesManager.getResource( script_url );\\r\\n\\t\\t\\tif(res)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( allow_local )\\r\\n\\t\\t\\t\\t\\tscript_url = LS.ResourcesManager.cleanFullpath( script_url );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(full_paths)\\r\\n\\t\\t\\t\\tscript_url = LS.ResourcesManager.getFullURL( script_url );\\r\\n\\r\\n\\t\\t\\tscripts.push( script_url );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn scripts;\\r\\n}\\r\\n\\r\\n//reloads external and global scripts taking into account if they come from wbins\\r\\nScene.prototype.loadScripts = function( scripts, on_complete, on_error, force_reload )\\r\\n{\\r\\n\\tif(!LS.allow_scripts)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"LiteScene.allow_scripts is set to false, so scripts imported into this scene are ignored.\\\");\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete();\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//get a list of scripts (they cannot be fullpaths)\\r\\n\\tscripts = scripts || LS.Scene.getScriptsList( this, true );\\r\\n\\r\\n\\tif(!scripts.length)\\r\\n\\t{\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete();\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tif( LS._block_scripts )\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"Safety: LS.block_scripts enabled, cannot request script\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//All this is to allow the use of scripts that are in memory (they came packed inside a WBin with the scene)\\r\\n\\tvar final_scripts = [];\\r\\n\\tvar revokable = [];\\r\\n\\r\\n\\tfor(var i in scripts)\\r\\n\\t{\\r\\n\\t\\tvar script_url = scripts[i];\\r\\n\\t\\tvar is_external = this.external_scripts.indexOf( script_url ) != -1;\\r\\n\\t\\tif( !is_external )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar res = LS.ResourcesManager.getResource( script_url );\\r\\n\\t\\t\\tif(!res || force_reload)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar final_url = LS.ResourcesManager.getFullURL( script_url );\\r\\n\\t\\t\\t\\tfinal_scripts.push( final_url );\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar blob = new Blob([res.data],{encoding:\\\"UTF-8\\\", type: 'text/plain;charset=UTF-8'});\\r\\n\\t\\t\\tvar objectURL = URL.createObjectURL( blob );\\r\\n\\t\\t\\tfinal_scripts.push( objectURL );\\r\\n\\t\\t\\trevokable.push( objectURL );\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tfinal_scripts.push( script_url );\\r\\n\\t}\\r\\n\\r\\n\\tLS.Network.requestScript( final_scripts, inner_complete, on_error );\\r\\n\\r\\n\\tfunction inner_complete()\\r\\n\\t{\\r\\n\\t\\t//revoke urls created\\r\\n\\t\\tfor(var i in revokable)\\r\\n\\t\\t\\tURL.revokeObjectURL( revokable[i] );\\r\\n\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete();\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//used to ensure that components use the right class when the class comes from a global script\\r\\nScene.prototype.checkComponentsCodeModification = function()\\r\\n{\\r\\n\\tfor(var i = 0; i < this._nodes.length; ++i )\\r\\n\\t{\\r\\n\\t\\t//current components\\r\\n\\t\\tvar node = this._nodes[i];\\r\\n\\t\\tfor(var j = 0; j < node._components.length; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar compo = node._components[j];\\r\\n\\t\\t\\tvar class_name = LS.getObjectClassName( compo );\\r\\n\\t\\t\\tif( compo.constructor == LS.MissingComponent )\\r\\n\\t\\t\\t\\tclass_name = compo._comp_class;\\r\\n\\r\\n\\t\\t\\tvar current_class = LS.Components[ class_name ];\\r\\n\\t\\t\\tif( !current_class || current_class == compo.constructor ) //already uses the right class\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//replace class instance in-place\\r\\n\\t\\t\\tvar data = compo.serialize();\\r\\n\\t\\t\\tvar new_compo = new current_class( data );\\r\\n\\t\\t\\tvar index = node.getIndexOfComponent( compo );\\r\\n\\t\\t\\tnode.removeComponent( compo );\\r\\n\\t\\t\\tnode.addComponent( new_compo, index );\\r\\n\\t\\t\\tconsole.log(\\\"Class replaced: \\\" + class_name );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nScene.prototype.appendScene = function(scene)\\r\\n{\\r\\n\\t//clone: because addNode removes it from scene.nodes array\\r\\n\\tvar nodes = scene.root.childNodes;\\r\\n\\r\\n\\t/*\\r\\n\\t//bring materials\\r\\n\\tfor(var i in scene.materials)\\r\\n\\t\\tthis.materials[i] = scene.materials[i];\\r\\n\\t*/\\r\\n\\t\\r\\n\\t//add every node one by one\\r\\n\\tfor(var i in nodes)\\r\\n\\t{\\r\\n\\t\\tvar node = nodes[i];\\r\\n\\t\\tvar new_node = new LS.SceneNode( node.id );\\r\\n\\t\\tthis.root.addChild( new_node );\\r\\n\\t\\tnew_node.configure( node.constructor == LS.SceneNode ? node.serialize() : node );\\r\\n\\t}\\r\\n}\\r\\n\\r\\nScene.prototype.getCamera = function()\\r\\n{\\r\\n\\tvar camera = this._root.camera;\\r\\n\\tif(camera) \\r\\n\\t\\treturn camera;\\r\\n\\r\\n\\tif(this._cameras && this._cameras.length)\\r\\n\\t\\treturn this._cameras[0];\\r\\n\\r\\n\\tthis.collectData(); //slow\\r\\n\\treturn this._cameras[0];\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns an array with all the cameras enabled in the scene\\r\\n*\\r\\n* @method getActiveCameras\\r\\n* @param {boolean} force [optional] if you want to collect the cameras again, otherwise it returns the last ones collected\\r\\n* @return {Array} cameras\\r\\n*/\\r\\nScene.prototype.getActiveCameras = function( force )\\r\\n{\\r\\n\\tif(force)\\r\\n\\t\\tLEvent.trigger(this, EVENT.COLLECT_CAMERAS, this._cameras );\\r\\n\\treturn this._cameras;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns an array with all the cameras in the scene (even if they are disabled)\\r\\n*\\r\\n* @method getAllCameras\\r\\n* @return {Array} cameras\\r\\n*/\\r\\nScene.prototype.getAllCameras = function()\\r\\n{\\r\\n\\tvar cameras = [];\\r\\n\\tfor(var i = 0; i < this._nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar node = this._nodes[i];\\r\\n\\t\\tvar node_cameras = node.getComponents( LS.Components.Camera );\\r\\n\\t\\tif(node_cameras && node_cameras.length)\\r\\n\\t\\t\\tcameras = cameras.concat( node_cameras );\\r\\n\\t}\\r\\n\\treturn cameras;\\r\\n}\\r\\n\\r\\nScene.prototype.getLight = function()\\r\\n{\\r\\n\\treturn this._root.light;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns an array with all the lights enabled in the scene\\r\\n*\\r\\n* @method getActiveLights\\r\\n* @param {boolean} force [optional] if you want to collect the lights again, otherwise it returns the last ones collected\\r\\n* @return {Array} lights\\r\\n*/\\r\\nScene.prototype.getActiveLights = function( force )\\r\\n{\\r\\n\\tif(force)\\r\\n\\t\\tLEvent.trigger(this, EVENT.COLLECT_LIGHTS, this._lights );\\r\\n\\treturn this._lights;\\r\\n}\\r\\n\\r\\nScene.prototype.onNodeAdded = function(e,node)\\r\\n{\\r\\n\\t//remove from old scene\\r\\n\\tif(node._in_tree && node._in_tree != this)\\r\\n\\t\\tthrow(\\\"Cannot add a node from other scene, clone it\\\");\\r\\n\\r\\n\\tif( node._name && !this._nodes_by_name[ node._name ] )\\r\\n\\t\\tthis._nodes_by_name[ node._name ] = node;\\r\\n\\r\\n\\t/*\\r\\n\\t//generate unique id\\r\\n\\tif(node.id && node.id != -1)\\r\\n\\t{\\r\\n\\t\\tif(this._nodes_by_id[node.id] != null)\\r\\n\\t\\t\\tnode.id = node.id + \\\"_\\\" + (Math.random() * 1000).toFixed(0);\\r\\n\\t\\tthis._nodes_by_id[node.id] = node;\\r\\n\\t}\\r\\n\\t*/\\r\\n\\r\\n\\t//store by uid\\r\\n\\tif(!node.uid || this._nodes_by_uid[ node.uid ])\\r\\n\\t\\tnode._uid = LS.generateUId(\\\"NODE-\\\");\\r\\n\\t//if( this._nodes_by_uid[ node.uid ] )\\r\\n\\t//\\tconsole.warn(\\\"There are more than one node with the same UID: \\\", node.uid );\\r\\n\\tthis._nodes_by_uid[ node.uid ] = node;\\r\\n\\r\\n\\t//store nodes linearly\\r\\n\\tthis._nodes.push(node);\\r\\n\\r\\n\\tnode.processActionInComponents(\\\"onAddedToScene\\\",this); //send to components\\r\\n\\tfor(var i = 0; i < node._components.length; ++i)\\r\\n\\t\\tif(node._components[i].uid)\\r\\n\\t\\t\\tthis._components_by_uid[ node._components[i].uid ] = node._components[i];\\r\\n\\t\\telse\\r\\n\\t\\t\\tconsole.warn(\\\"component without uid?\\\", node._components[i].uid );\\r\\n\\r\\n\\t/**\\r\\n\\t * Fired when a new node is added to this scene\\r\\n\\t *\\r\\n\\t * @event nodeAdded\\r\\n\\t * @param {LS.SceneNode} node\\r\\n\\t */\\r\\n\\tLEvent.trigger(this,\\\"nodeAdded\\\", node);\\r\\n\\tLEvent.trigger(this, EVENT.CHANGE );\\r\\n}\\r\\n\\r\\nScene.prototype.onNodeRemoved = function(e,node)\\r\\n{\\r\\n\\tvar pos = this._nodes.indexOf(node);\\r\\n\\tif(pos == -1) \\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._nodes.splice(pos,1);\\r\\n\\tif(node._name && this._nodes_by_name[ node._name ] == node )\\r\\n\\t\\tdelete this._nodes_by_name[ node._name ];\\r\\n\\tif(node.uid)\\r\\n\\t\\tdelete this._nodes_by_uid[ node.uid ];\\r\\n\\r\\n\\t//node.processActionInComponents(\\\"onRemovedFromNode\\\",node);\\r\\n\\tnode.processActionInComponents(\\\"onRemovedFromScene\\\",this); //send to components\\r\\n\\tfor(var i = 0; i < node._components.length; ++i)\\r\\n\\t\\tdelete this._components_by_uid[ node._components[i].uid ];\\r\\n\\r\\n\\t/**\\r\\n\\t * Fired after a node has been removed\\r\\n\\t *\\r\\n\\t * @event nodeRemoved\\r\\n\\t * @param {LS.SceneNode} node\\r\\n\\t */\\r\\n\\tLEvent.trigger(this,\\\"nodeRemoved\\\", node);\\r\\n\\tLEvent.trigger(this, EVENT.CHANGE );\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* all nodes are stored in an array, this function recomputes the array so they are in the right order in case one has changed order\\r\\n*\\r\\n* @method recomputeNodesArray\\r\\n*/\\r\\nScene.prototype.recomputeNodesArray = function()\\r\\n{\\r\\n\\tvar nodes = this._nodes;\\r\\n\\tvar pos = 0;\\r\\n\\tinner( this._root );\\r\\n\\r\\n\\tfunction inner(node)\\r\\n\\t{\\r\\n\\t\\tnodes[pos] = node;\\r\\n\\t\\tpos+=1;\\r\\n\\t\\tif(!node._children || !node._children.length)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tfor(var i = 0; i < node._children.length; ++i)\\r\\n\\t\\t\\tinner( node._children[i] );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//WIP\\r\\nScene.prototype.attachSceneElement = function( element )\\r\\n{\\r\\n\\tthis._spatial_container.add( element );\\r\\n}\\r\\n\\r\\nScene.prototype.detachSceneElement = function( element )\\r\\n{\\r\\n\\tthis._spatial_container.remove( element );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns the array containing all the nodes in the scene\\r\\n*\\r\\n* @method getNodes\\r\\n* @param {bool} recompute [optional] in case you want to rearrange the nodes\\r\\n* @return {Array} array containing every SceneNode in the scene\\r\\n*/\\r\\nScene.prototype.getNodes = function( recompute )\\r\\n{\\r\\n\\tif(recompute)\\r\\n\\t\\tthis.recomputeNodesArray();\\r\\n\\treturn this._nodes;\\r\\n}\\r\\n\\r\\n/**\\r\\n* retrieves a Node based on the name, path ( name|childname|etc ) or uid\\r\\n*\\r\\n* @method getNode\\r\\n* @param {String} name node name to search\\r\\n* @return {Object} the node or null if it didnt find it\\r\\n*/\\r\\nScene.prototype.getNode = function( name )\\r\\n{\\r\\n\\tif(name == \\\"\\\")\\r\\n\\t\\treturn this.root;\\r\\n\\tif(!name || name.constructor !== String)\\r\\n\\t\\treturn null;\\r\\n\\tif(name.charAt(0) == LS._uid_prefix)\\r\\n\\t\\treturn this._nodes_by_uid[ name ];\\r\\n\\r\\n\\t// the | char is used to specify a node child of another node\\r\\n\\tif( name.indexOf(\\\"|\\\") != -1)\\r\\n\\t{\\r\\n\\t\\tvar tokens = name.split(\\\"|\\\");\\r\\n\\t\\tvar node = this.root; //another option could be to start in this._nodes_by_name[ tokens[0] ]\\r\\n\\t\\tfor(var i = 0; i < tokens.length && node; ++i)\\r\\n\\t\\t\\tnode = node.getChildByName( tokens[i] );\\r\\n\\t\\treturn node;\\r\\n\\t}\\r\\n\\r\\n\\treturn this._nodes_by_name[ name ];\\r\\n}\\r\\n\\r\\n/**\\r\\n* retrieves a Node that matches that name. It is fast because they are stored in an object.\\r\\n* If more than one object has the same name, the first one added to the tree is returned\\r\\n*\\r\\n* @method getNodeByName\\r\\n* @param {String} name name of the node\\r\\n* @return {Object} the node or null if it didnt find it\\r\\n*/\\r\\nScene.prototype.getNodeByName = function( name )\\r\\n{\\r\\n\\treturn this._nodes_by_name[ name ];\\r\\n}\\r\\n\\r\\n/**\\r\\n* retrieves a Node based on a given uid. It is fast because they are stored in an object\\r\\n*\\r\\n* @method getNodeByUId\\r\\n* @param {String} uid uid of the node\\r\\n* @return {Object} the node or null if it didnt find it\\r\\n*/\\r\\nScene.prototype.getNodeByUId = function( uid )\\r\\n{\\r\\n\\treturn this._nodes_by_uid[ uid ];\\r\\n}\\r\\n\\r\\n/**\\r\\n* retrieves a Node by its index\\r\\n*\\r\\n* @method getNodeByIndex\\r\\n* @param {Number} node index\\r\\n* @return {Object} returns the node at the 'index' position in the nodes array\\r\\n*/\\r\\nScene.prototype.getNodeByIndex = function(index)\\r\\n{\\r\\n\\treturn this._nodes[ index ];\\r\\n}\\r\\n\\r\\n//for those who are more traditional\\r\\nScene.prototype.getElementById = Scene.prototype.getNode;\\r\\n\\r\\n/**\\r\\n* retrieves a node array filtered by the filter function\\r\\n*\\r\\n* @method filterNodes\\r\\n* @param {function} filter a callback function that receives every node and must return true or false\\r\\n* @return {Array} array containing the nodes that passes the filter\\r\\n*/\\r\\nScene.prototype.filterNodes = function( filter )\\r\\n{\\r\\n\\tvar r = [];\\r\\n\\tfor(var i = 0; i < this._nodes.length; ++i)\\r\\n\\t\\tif( filter(this._nodes[i]) )\\r\\n\\t\\t\\tr.push(this._nodes[i]);\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\n/**\\r\\n* searches the component with this uid, it iterates through all the nodes and components (slow)\\r\\n*\\r\\n* @method findComponentByUId\\r\\n* @param {String} uid uid of the node\\r\\n* @return {Object} component or null\\r\\n*/\\r\\nScene.prototype.findComponentByUId = function(uid)\\r\\n{\\r\\n\\tfor(var i = 0; i < this._nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar compo = this._nodes[i].getComponentByUId( uid );\\r\\n\\t\\tif(compo)\\r\\n\\t\\t\\treturn compo;\\r\\n\\t}\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* searches the material with this uid, it iterates through all the nodes (slow)\\r\\n*\\r\\n* @method findMaterialByUId\\r\\n* @param {String} uid uid of the material\\r\\n* @return {Object} Material or null\\r\\n*/\\r\\nScene.prototype.findMaterialByUId = function(uid)\\r\\n{\\r\\n\\tif(LS.RM.materials[uid])\\r\\n\\t\\treturn LS.RM.materials[uid];\\r\\n\\r\\n\\tfor(var i = 0; i < this._nodes.length; ++i)\\r\\n\\t{\\r\\n\\t\\tvar material = this._nodes[i].getMaterial();\\r\\n\\t\\tif(material && material.uid == uid)\\r\\n\\t\\t\\treturn material;\\r\\n\\t}\\r\\n\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns information of a node component property based on the locator of that property\\r\\n* Locators are in the form of \\\"{NODE_UID}/{COMPONENT_UID}/{property_name}\\\"\\r\\n*\\r\\n* @method getPropertyInfo\\r\\n* @param {String} locator locator of the property\\r\\n* @return {Object} object with node, component, name, and value\\r\\n*/\\r\\nScene.prototype.getPropertyInfo = function( property_uid )\\r\\n{\\r\\n\\tvar path = property_uid.split(\\\"/\\\");\\r\\n\\r\\n\\tvar start = path[0].substr(0,5);\\r\\n\\r\\n\\t//for global materials\\r\\n\\tif( start == \\\"@MAT-\\\")\\r\\n\\t{\\r\\n\\t\\tvar material = LS.RM.materials_by_uid[ path[0] ];\\r\\n\\t\\tif(!material)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn material.getPropertyInfoFromPath( path.slice(1) );\\r\\n\\t}\\r\\n\\r\\n\\t//for components\\r\\n\\tif( start == \\\"@COMP\\\")\\r\\n\\t{\\r\\n\\t\\tvar comp = this.findComponentByUId( path[0] );\\r\\n\\t\\tif(!comp)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tif(path.length == 1)\\r\\n\\t\\t\\treturn {\\r\\n\\t\\t\\t\\tnode: comp.root,\\r\\n\\t\\t\\t\\ttarget: comp,\\r\\n\\t\\t\\t\\tname: comp ? LS.getObjectClassName( comp ) : \\\"\\\",\\r\\n\\t\\t\\t\\ttype: \\\"component\\\",\\r\\n\\t\\t\\t\\tvalue: comp\\r\\n\\t\\t\\t};\\r\\n\\t\\treturn comp.getPropertyInfoFromPath( path.slice(1) );\\r\\n\\t}\\r\\n\\r\\n\\t//for regular locators\\r\\n\\tvar node = this.getNode( path[0] );\\r\\n\\tif(!node)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\treturn node.getPropertyInfoFromPath( path.slice(1) );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns information of a node component property based on the locator of that property\\r\\n* Locators are in the form of \\\"{NODE_UID}/{COMPONENT_UID}/{property_name}\\\"\\r\\n*\\r\\n* @method getPropertyInfoFromPath\\r\\n* @param {Array} path\\r\\n* @return {Object} object with node, component, name, and value\\r\\n*/\\r\\nScene.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tif(path[0].substr(0,5) == \\\"@MAT-\\\")\\r\\n\\t{\\r\\n\\t\\tvar material = LS.RM.materials_by_uid[ path[0] ];\\r\\n\\t\\tif(!material)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn material.getPropertyInfoFromPath( path.slice(1) );\\r\\n\\t}\\r\\n\\r\\n\\tvar node = this.getNode( path[0] );\\r\\n\\tif(!node)\\r\\n\\t\\treturn null;\\r\\n\\treturn node.getPropertyInfoFromPath( path.slice(1) );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Assigns a value to the property of a component in a node based on the locator of that property\\r\\n* Locators are in the form of \\\"{NODE_UID}/{COMPONENT_UID}/{property_name}\\\"\\r\\n*\\r\\n* @method getPropertyValue\\r\\n* @param {String} locator locator of the property\\r\\n* @param {*} value the value to assign\\r\\n* @param {SceneNode} root [Optional] if you want to limit the locator to search inside a node\\r\\n* @return {Component} the target where the action was performed\\r\\n*/\\r\\nScene.prototype.getPropertyValue = function( locator, root_node )\\r\\n{\\r\\n\\tvar path = property_uid.split(\\\"/\\\");\\r\\n\\r\\n\\tif(path[0].substr(0,5) == \\\"@MAT-\\\")\\r\\n\\t{\\r\\n\\t\\tvar material = LS.RM.materials_by_uid[ path[0] ];\\r\\n\\t\\tif(!material)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn material.getPropertyValueFromPath( path.slice(1) );\\r\\n\\t}\\r\\n\\r\\n\\tvar node = this.getNode( path[0] );\\r\\n\\tif(!node)\\r\\n\\t\\treturn null;\\r\\n\\treturn node.getPropertyValueFromPath( path.slice(1) );\\r\\n}\\r\\n\\r\\nScene.prototype.getPropertyValueFromPath = function( path )\\r\\n{\\r\\n\\tif(path[0].substr(0,5) == \\\"@MAT-\\\")\\r\\n\\t{\\r\\n\\t\\tvar material = LS.RM.materials_by_uid[ path[0] ];\\r\\n\\t\\tif(!material)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn material.getPropertyValueFromPath( path.slice(1) );\\r\\n\\t}\\r\\n\\tvar node = this.getNode( path[0] );\\r\\n\\tif(!node)\\r\\n\\t\\treturn null;\\r\\n\\treturn node.getPropertyValueFromPath( path.slice(1) );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Assigns a value to the property of a component in a node based on the locator of that property\\r\\n* Locators are in the form of \\\"{NODE_UID}/{COMPONENT_UID}/{property_name}\\\"\\r\\n*\\r\\n* @method setPropertyValue\\r\\n* @param {String} locator locator of the property\\r\\n* @param {*} value the value to assign\\r\\n* @param {SceneNode} root [Optional] if you want to limit the locator to search inside a node\\r\\n* @return {Component} the target where the action was performed\\r\\n*/\\r\\nScene.prototype.setPropertyValue = function( locator, value, root_node )\\r\\n{\\r\\n\\tvar path = locator.split(\\\"/\\\");\\r\\n\\tthis.setPropertyValueFromPath( path, value, root_node, 0 );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Assigns a value to the property of a component in a node based on the locator that property\\r\\n* Locators are in the form of \\\"{NODE_UID}/{COMPONENT_UID}/{property_name}\\\"\\r\\n*\\r\\n* @method setPropertyValueFromPath\\r\\n* @param {Array} path a property locator split by \\\"/\\\"\\r\\n* @param {*} value the value to assign\\r\\n* @param {SceneNode} root_node [optional] the root node where you want to search the locator (this is to limit the locator to a branch of the scene tree)\\r\\n* @param {Number} offset [optional] used to avoir generating garbage, instead of slicing the array every time, we pass the array index\\r\\n* @return {Component} the target where the action was performed\\r\\n*/\\r\\nScene.prototype.setPropertyValueFromPath = function( path, value, root_node, offset )\\r\\n{\\r\\n\\toffset = offset || 0;\\r\\n\\tif(path.length < (offset+1))\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(path[offset].substr(0,5) == \\\"@MAT-\\\")\\r\\n\\t{\\r\\n\\t\\tvar material = LS.RM.materials_by_uid[ path[offset] ];\\r\\n\\t\\tif(!material)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\treturn material.setPropertyValueFromPath( path, value, offset + 1 );\\r\\n\\t}\\r\\n\\r\\n\\t//get node\\r\\n\\tvar node = root_node ? root_node.findNode( path[offset] ) : this.getNode( path[offset] );\\r\\n\\tif(!node)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\treturn node.setPropertyValueFromPath( path, value, offset + 1 );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Returns the resources used by the scene\\r\\n* includes the nodes, components, preloads and global_scripts\\r\\n* doesn't include external_scripts\\r\\n*\\r\\n* @method getResources\\r\\n* @param {Object} resources [optional] object with resources\\r\\n* @param {Boolean} as_array [optional] returns data in array format instead of object format\\r\\n* @param {Boolean} skip_in_pack [optional] skips resources that come from a pack\\r\\n* @param {Boolean} skip_local [optional] skips resources whose name starts with \\\":\\\" (considered local resources)\\r\\n* @return {Object|Array} the resources in object format (or if as_array is true, then an array)\\r\\n*/\\r\\nScene.prototype.getResources = function( resources, as_array, skip_in_pack, skip_local )\\r\\n{\\r\\n\\tresources = resources || {};\\r\\n\\r\\n\\t//to get the resources as array\\r\\n\\tvar array = null;\\r\\n\\tif(resources.constructor === Array)\\r\\n\\t{\\r\\n\\t\\tarray = resources;\\r\\n\\t\\tresources = {};\\r\\n\\t\\tas_array = true;\\r\\n\\t}\\r\\n\\r\\n\\t//first the preload\\r\\n\\t//resources that must be preloaded (because they will be used in the future)\\r\\n\\tif(this.preloaded_resources)\\r\\n\\t\\tfor(var i in this.preloaded_resources)\\r\\n\\t\\t\\tresources[ i ] = true;\\r\\n\\r\\n\\tif(this.texture_atlas)\\r\\n\\t\\tresources[ this.texture_atlas.filename ] = true;\\r\\n\\r\\n\\t//global scripts\\r\\n\\tfor(var i = 0; i < this.global_scripts.length; ++i)\\r\\n\\t\\tif( this.global_scripts[i] )\\r\\n\\t\\t\\tresources[ this.global_scripts[i] ] = true;\\r\\n\\r\\n\\t//resources from nodes\\r\\n\\tfor(var i = 0; i < this._nodes.length; ++i)\\r\\n\\t\\tthis._nodes[i].getResources( resources );\\r\\n\\r\\n\\t//remove the resources that belong to packs or prefabs\\r\\n\\tif(skip_in_pack)\\r\\n\\t\\tfor(var i in resources)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar resource = LS.ResourcesManager.resources[i];\\r\\n\\t\\t\\tif(!resource)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif(resource && (resource.from_prefab || resource.from_pack))\\r\\n\\t\\t\\t\\tdelete resources[i];\\r\\n\\t\\t}\\r\\n\\r\\n\\t//remove the resources that are local (generated by the system)\\r\\n\\tif(skip_local)\\r\\n\\t\\tfor(var i in resources)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(i[0] == \\\":\\\")\\r\\n\\t\\t\\t\\tdelete resources[i];\\r\\n\\t\\t}\\r\\n\\r\\n\\t//check if any resource requires another resource (a material that requires textures)\\r\\n\\tfor(var i in resources)\\r\\n\\t{\\r\\n\\t\\tvar resource = LS.ResourcesManager.resources[i];\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tif(resource.getResources)\\r\\n\\t\\t\\tresource.getResources(resources);\\r\\n\\t}\\r\\n\\r\\n\\t//Hack: sometimes some component add this shit\\r\\n\\tdelete resources[\\\"\\\"];\\r\\n\\tdelete resources[\\\"null\\\"];\\r\\n\\r\\n\\t//return as object\\r\\n\\tif(!as_array)\\r\\n\\t\\treturn resources;\\r\\n\\r\\n\\t//return as array\\r\\n\\tvar r = array || [];\\r\\n\\tfor(var i in resources)\\r\\n\\t\\tr.push(i);\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Loads all the resources of all the nodes in this scene\\r\\n* it sends a signal to every node to get all the resources info\\r\\n* and load them in bulk using the ResourceManager\\r\\n*\\r\\n* @method loadResources\\r\\n* @param {Function} on_complete called when the load of all the resources is complete\\r\\n*/\\r\\nScene.prototype.loadResources = function( on_complete )\\r\\n{\\r\\n\\t//resources is an object format\\r\\n\\tvar resources = this.getResources([]);\\r\\n\\r\\n\\t//used for scenes with special repository folders\\r\\n\\tvar options = {};\\r\\n\\tif( this.external_repository )\\r\\n\\t\\toptions.external_repository = this.external_repository;\\r\\n\\r\\n\\t//count resources\\r\\n\\tvar num_resources = 0;\\r\\n\\tfor(var i in resources)\\r\\n\\t\\t++num_resources;\\r\\n\\r\\n\\t//load them\\r\\n\\tif(num_resources == 0)\\r\\n\\t{\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete();\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tLEvent.bind( LS.ResourcesManager, \\\"end_loading_resources\\\", on_loaded );\\r\\n\\tLS.ResourcesManager.loadResources( resources );\\r\\n\\r\\n\\tfunction on_loaded()\\r\\n\\t{\\r\\n\\t\\tLEvent.unbind( LS.ResourcesManager, \\\"end_loading_resources\\\", on_loaded );\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete();\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Adds a resource that must be loaded when the scene is loaded\\r\\n*\\r\\n* @method addPreloadResource\\r\\n* @param {String} fullpath the name of the resource\\r\\n*/\\r\\nScene.prototype.addPreloadResource = function( fullpath )\\r\\n{\\r\\n\\tthis.preloaded_resources[ fullpath ] = true;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Remove a resource from the list of resources to preload\\r\\n*\\r\\n* @method removePreloadResource\\r\\n* @param {String} fullpath the name of the resource\\r\\n*/\\r\\nScene.prototype.removePreloadResource = function( fullpath )\\r\\n{\\r\\n\\tdelete this.preloaded_resources[ fullpath ];\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* start the scene (triggers an \\\"start\\\" event)\\r\\n*\\r\\n* @method start\\r\\n* @param {Number} dt delta time\\r\\n*/\\r\\nScene.prototype.start = function()\\r\\n{\\r\\n\\tif(this._state == LS.PLAYING)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._state = LS.PLAYING;\\r\\n\\tthis._start_time = getTime() * 0.001;\\r\\n\\t/**\\r\\n\\t * Fired when the nodes need to be initialized\\r\\n\\t *\\r\\n\\t * @event init\\r\\n\\t * @param {LS.Scene} scene\\r\\n\\t */\\r\\n\\tLEvent.trigger(this, EVENT.INIT, this);\\r\\n\\tthis.triggerInNodes( EVENT.INIT );\\r\\n\\t/**\\r\\n\\t * Fired when the scene is starting to play\\r\\n\\t *\\r\\n\\t * @event start\\r\\n\\t * @param {LS.Scene} scene\\r\\n\\t */\\r\\n\\tLEvent.trigger(this, EVENT.START ,this);\\r\\n\\tthis.triggerInNodes( EVENT.START );\\r\\n}\\r\\n\\r\\n/**\\r\\n* pauses the scene (triggers an \\\"pause\\\" event)\\r\\n*\\r\\n* @method pause\\r\\n*/\\r\\nScene.prototype.pause = function()\\r\\n{\\r\\n\\tif( this._state != LS.PLAYING )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._state = LS.PAUSED;\\r\\n\\t/**\\r\\n\\t * Fired when the scene pauses (mostly in the editor)\\r\\n\\t *\\r\\n\\t * @event pause\\r\\n\\t * @param {LS.Scene} scene\\r\\n\\t */\\r\\n\\tLEvent.trigger(this, EVENT.PAUSE,this);\\r\\n\\tthis.triggerInNodes( EVENT.PAUSE );\\r\\n\\tthis.purgeResidualEvents();\\r\\n}\\r\\n\\r\\n/**\\r\\n* unpauses the scene (triggers an \\\"unpause\\\" event)\\r\\n*\\r\\n* @method unpause\\r\\n*/\\r\\nScene.prototype.unpause = function()\\r\\n{\\r\\n\\tif(this._state != LS.PAUSED)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._state = LS.PLAYING;\\r\\n\\t/**\\r\\n\\t * Fired when the scene unpauses (mostly in the editor)\\r\\n\\t *\\r\\n\\t * @event unpause\\r\\n\\t * @param {LS.Scene} scene\\r\\n\\t */\\r\\n\\tLEvent.trigger(this, EVENT.UNPAUSE,this);\\r\\n\\tthis.triggerInNodes( EVENT.UNPAUSE );\\r\\n\\tthis.purgeResidualEvents();\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* stop the scene (triggers an \\\"finish\\\" event)\\r\\n*\\r\\n* @method finish\\r\\n* @param {Number} dt delta time\\r\\n*/\\r\\nScene.prototype.finish = function()\\r\\n{\\r\\n\\tif(this._state == LS.STOPPED)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tthis._state = LS.STOPPED;\\r\\n\\t/**\\r\\n\\t * Fired when the scene stops playing\\r\\n\\t *\\r\\n\\t * @event finish\\r\\n\\t * @param {LS.Scene} scene\\r\\n\\t */\\r\\n\\tLEvent.trigger(this, EVENT.FINISH,this);\\r\\n\\tthis.triggerInNodes( EVENT.FINISH );\\r\\n\\tthis.purgeResidualEvents();\\r\\n}\\r\\n\\r\\n/**\\r\\n* This methods crawls the whole tree and collects all the useful info (cameras, lights, render instances, colliders, etc)\\r\\n* Mostly rendering stuff but also some collision info.\\r\\n* TO DO: refactor this so it doesnt redo the same task in every frame, only if changes are made\\r\\n* @param {Array} cameras [optional] an array of cameras in case we want to force some viewpoint\\r\\n* @method collectData\\r\\n*/\\r\\nScene.prototype.collectData = function( cameras )\\r\\n{\\r\\n\\tvar instances = this._instances;\\r\\n\\tvar lights = this._lights;\\r\\n\\tvar colliders = this._colliders;\\r\\n\\r\\n\\t//empty containers\\r\\n\\tinstances.length = 0;\\r\\n\\tlights.length = 0;\\r\\n\\tcolliders.length = 0;\\r\\n\\r\\n\\t//first collect cameras (in case we want to filter nodes by proximity to camera\\r\\n\\tif(!cameras || cameras.length == 0)\\r\\n\\t{\\r\\n\\t\\tcameras = this._cameras;\\r\\n\\t\\tcameras.length = 0;\\r\\n\\t\\tLEvent.trigger( this, EVENT.COLLECT_CAMERAS, cameras );\\r\\n\\t}\\r\\n\\r\\n\\t//get nodes: TODO find nodes close to the active cameras\\r\\n\\tvar nodes = this.getNodes();\\r\\n\\r\\n\\t//collect render instances and lights\\r\\n\\tfor(var i = 0, l = nodes.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar node = nodes[i];\\r\\n\\r\\n\\t\\t//skip stuff inside invisible nodes\\r\\n\\t\\tif(node.flags.visible == false) \\r\\n\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t//compute global matrix: shouldnt it be already computed?\\r\\n\\t\\tif(node.transform)\\r\\n\\t\\t\\tnode.transform.updateGlobalMatrix();\\r\\n\\r\\n\\t\\t//clear instances per node: TODO: if static maybe just leave it as it is\\r\\n\\t\\tnode._instances.length = 0;\\r\\n\\r\\n\\t\\t//get render instances: remember, triggers only support one parameter\\r\\n\\t\\tLEvent.trigger( node, EVENT.COLLECT_RENDER_INSTANCES, node._instances );\\r\\n\\t\\tLEvent.trigger( node, EVENT.COLLECT_PHYSIC_INSTANCES, colliders );\\r\\n\\r\\n\\t\\t//concatenate all instances in a single array\\r\\n\\t\\tinstances.push.apply(instances, node._instances);\\r\\n\\t}\\r\\n\\r\\n\\t//we also collect from the scene itself (used for lights, skybox, etc)\\r\\n\\tLEvent.trigger( this, EVENT.COLLECT_RENDER_INSTANCES, instances );\\r\\n\\tLEvent.trigger( this, EVENT.COLLECT_PHYSIC_INSTANCES, colliders );\\r\\n\\tLEvent.trigger( this, EVENT.COLLECT_LIGHTS, lights );\\r\\n\\r\\n\\t//before processing (in case somebody wants to add some data to the containers)\\r\\n\\tLEvent.trigger( this, EVENT.COLLECT_DATA );\\r\\n\\r\\n\\t//for each render instance collected\\r\\n\\tfor(var i = 0, l = instances.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar instance = instances[i];\\r\\n\\r\\n\\t\\t//compute the axis aligned bounding box\\r\\n\\t\\tif(instance.use_bounding)\\r\\n\\t\\t\\tinstance.updateAABB();\\r\\n\\t}\\r\\n\\r\\n\\t//for each physics instance collected\\r\\n\\tfor(var i = 0, l = colliders.length; i < l; ++i)\\r\\n\\t{\\r\\n\\t\\tvar collider = colliders[i];\\r\\n\\t\\tcollider.updateAABB();\\r\\n\\t}\\r\\n\\r\\n\\t//remember when was last time I collected to avoid repeating it\\r\\n\\tthis._last_collect_frame = this._frame;\\r\\n}\\r\\n\\r\\n/**\\r\\n* updates the scene (it handles variable update and fixedUpdate)\\r\\n*\\r\\n* @method update\\r\\n* @param {Number} dt delta time in seconds\\r\\n*/\\r\\nScene.prototype.update = function(dt)\\r\\n{\\r\\n\\t/**\\r\\n\\t * Fired before doing an update\\r\\n\\t *\\r\\n\\t * @event beforeUpdate\\r\\n\\t * @param {LS.Scene} scene\\r\\n\\t */\\r\\n\\tLEvent.trigger(this,\\\"beforeUpdate\\\", this);\\r\\n\\r\\n\\tthis._global_time = getTime() * 0.001;\\r\\n\\t//this._time = this._global_time - this._start_time;\\r\\n\\tthis._time += dt;\\r\\n\\tthis._last_dt = dt;\\r\\n\\r\\n\\t/**\\r\\n\\t * Fired while updating\\r\\n\\t *\\r\\n\\t * @event update\\r\\n\\t * @param {number} dt\\r\\n\\t */\\r\\n\\tLEvent.trigger(this,\\\"update\\\", dt);\\r\\n\\r\\n\\t/**\\r\\n\\t * Fired while updating but using a fixed timestep (1/60)\\r\\n\\t *\\r\\n\\t * @event fixedUpdate\\r\\n\\t * @param {number} dt\\r\\n\\t */\\r\\n\\tif(this._fixed_update_timestep > 0)\\r\\n\\t{\\r\\n\\t\\tthis._remaining_fixed_update_time += dt;\\r\\n\\t\\tif(LEvent.hasBind(this,\\\"fixedUpdate\\\"))\\r\\n\\t\\t\\twhile( this._remaining_fixed_update_time > this._fixed_update_timestep )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tLEvent.trigger(this, \\\"fixedUpdate\\\", this._fixed_update_timestep );\\r\\n\\t\\t\\t\\tthis._remaining_fixed_update_time -= this._fixed_update_timestep;\\r\\n\\t\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tthis._remaining_fixed_update_time = this._remaining_fixed_update_time % this._fixed_update_timestep;\\r\\n\\t}\\r\\n\\r\\n\\t/**\\r\\n\\t * Fired after updating the scene\\r\\n\\t *\\r\\n\\t * @event afterUpdate\\r\\n\\t */\\r\\n\\tLEvent.trigger(this,\\\"afterUpdate\\\", this);\\r\\n}\\r\\n\\r\\n/**\\r\\n* triggers an event to all nodes in the scene\\r\\n* this is slow if the scene has too many nodes, thats why we use bindings\\r\\n*\\r\\n* @method triggerInNodes\\r\\n* @param {String} event_type event type name\\r\\n* @param {Object} data data to send associated to the event\\r\\n*/\\r\\n\\r\\nScene.prototype.triggerInNodes = function(event_type, data)\\r\\n{\\r\\n\\tLEvent.triggerArray( this._nodes, event_type, data);\\r\\n}\\r\\n\\r\\n/**\\r\\n* generate a unique node name given a prefix\\r\\n*\\r\\n* @method generateUniqueNodeName\\r\\n* @param {String} prefix the prefix, if not given then \\\"node\\\" is used\\r\\n* @return {String} a node name that it is not in the scene\\r\\n*/\\r\\nScene.prototype.generateUniqueNodeName = function(prefix)\\r\\n{\\r\\n\\tprefix = prefix || \\\"node\\\";\\r\\n\\tvar i = 1;\\r\\n\\r\\n\\tvar pos = prefix.lastIndexOf(\\\"_\\\");\\r\\n\\tif(pos)\\r\\n\\t{\\r\\n\\t\\tvar n = prefix.substr(pos+1);\\r\\n\\t\\tif( parseInt(n) )\\r\\n\\t\\t{\\r\\n\\t\\t\\ti = parseInt(n);\\r\\n\\t\\t\\tprefix = prefix.substr(0,pos);\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tvar node_name = prefix + \\\"_\\\" + i;\\r\\n\\twhile( this.getNode(node_name) != null )\\r\\n\\t\\tnode_name = prefix + \\\"_\\\" + (i++);\\r\\n\\treturn node_name;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Marks that this scene must be rendered again\\r\\n*\\r\\n* @method requestFrame\\r\\n*/\\r\\nScene.prototype.requestFrame = function()\\r\\n{\\r\\n\\tthis._must_redraw = true;\\r\\n\\tLEvent.trigger( this, \\\"requestFrame\\\" );\\r\\n}\\r\\n\\r\\nScene.prototype.refresh = Scene.prototype.requestFrame; //DEPRECATED\\r\\n\\r\\n/**\\r\\n* returns current scene time (remember that scene time remains freezed if the scene is not playing)\\r\\n*\\r\\n* @method getTime\\r\\n* @return {Number} scene time in seconds\\r\\n*/\\r\\nScene.prototype.getTime = function()\\r\\n{\\r\\n\\treturn this._time;\\r\\n}\\r\\n\\r\\n//This is ugly but sometimes if scripts fail there is a change the could get hooked to the scene forever\\r\\n//so this way we remove any event that belongs to a component thats doesnt belong to this scene tree\\r\\nScene.prototype.purgeResidualEvents = function()\\r\\n{\\r\\n\\tif(!this.__events)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//crawl all \\r\\n\\tfor(var i in this.__events)\\r\\n\\t{\\r\\n\\t\\tvar event = this.__events[i];\\r\\n\\t\\tif(!event)\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tvar to_keep = [];\\r\\n\\t\\tfor(var j = 0; j < event.length; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar inst = event[j][1];\\r\\n\\t\\t\\tif(inst && LS.isClassComponent( inst.constructor ) )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//no attached node or node not attached to any scene\\r\\n\\t\\t\\t\\tif(!inst._root || inst._root.scene !== this )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"Event attached to the Scene belongs to a removed node, purged. Event:\\\",i,\\\"Class:\\\", LS.getObjectClassName( inst ) );\\r\\n\\t\\t\\t\\t\\tcontinue; //skip keeping it, so it will no longer exist\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tto_keep.push(event[j]);\\r\\n\\t\\t}\\r\\n\\t\\tthis.__events[i] = to_keep;\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* returns an array with the name of all the layers given a layers mask\\r\\n*\\r\\n* @method getLayerNames\\r\\n* @param {Number} layers a number with the enabled layers in bit mask format, if ommited all layers are returned\\r\\n* @return {Array} array of strings with the layer names\\r\\n*/\\r\\nScene.prototype.getLayerNames = function(layers)\\r\\n{\\r\\n\\tvar r = [];\\r\\n\\r\\n\\tfor(var i = 0; i < 32; ++i)\\r\\n\\t{\\r\\n\\t\\tif( layers === undefined || layers & (1< min_dist )\\r\\n\\t\\t\\tcontinue;\\r\\n\\t\\tmin_dist = dist;\\r\\n\\t\\tnearest_probe = probe;\\r\\n\\t}\\r\\n\\treturn nearest_probe;\\r\\n}\\r\\n\\r\\n\\r\\n//tells to all the components, nodes, materials, etc, that one resource has changed its name so they can update it inside\\r\\nScene.prototype.sendResourceRenamedEvent = function( old_name, new_name, resource )\\r\\n{\\r\\n\\t//scene globals that use resources\\r\\n\\tfor(var i = 0; i < this.external_scripts.length; i++)\\r\\n\\t{\\r\\n\\t\\tif(this.external_scripts[i] == old_name)\\r\\n\\t\\t\\tthis.external_scripts[i] = new_name;\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i = 0; i < this.global_scripts.length; i++)\\r\\n\\t{\\r\\n\\t\\tif(this.global_scripts[i] == old_name)\\r\\n\\t\\t\\tthis.global_scripts[i] = new_name;\\r\\n\\t}\\r\\n\\r\\n\\tfor(var i in this.preloaded_resources)\\r\\n\\t{\\r\\n\\t\\tif(i == old_name)\\r\\n\\t\\t{\\r\\n\\t\\t\\tdelete this.preloaded_resources[old_name];\\r\\n\\t\\t\\tthis.preloaded_resources[ new_name ] = true;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif( this.texture_atlas && this.texture_atlas.filename == old_name )\\r\\n\\t\\tthis.texture_atlas.filename = new_name;\\r\\n\\r\\n\\t//to nodes\\r\\n\\tvar nodes = this._nodes.concat();\\r\\n\\r\\n\\t//for every node\\r\\n\\tfor(var i = 0; i < nodes.length; i++)\\r\\n\\t{\\r\\n\\t\\t//nodes\\r\\n\\t\\tvar node = nodes[i];\\r\\n\\r\\n\\t\\t//prefabs\\r\\n\\t\\tif( node.prefab && node.prefab === old_name )\\r\\n\\t\\t\\tnode.prefab = new_name; //does this launch a reload prefab? dont know\\r\\n\\r\\n\\t\\t//components\\r\\n\\t\\tfor(var j = 0; j < node._components.length; j++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar component = node._components[j];\\r\\n\\t\\t\\tif(component.onResourceRenamed)\\r\\n\\t\\t\\t\\tcomponent.onResourceRenamed( old_name, new_name, resource )\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//materials\\r\\n\\t\\tif( node.material )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( node.material == old_name )\\r\\n\\t\\t\\t\\tnode.material = new_name;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar material = node.getMaterial();\\r\\n\\t\\t\\t\\tif( material && material.onResourceRenamed )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar modified = material.onResourceRenamed( old_name, new_name, resource );\\r\\n\\t\\t\\t\\t\\tif(modified) //we need this to remove material._original_data or anything that could interfiere\\r\\n\\t\\t\\t\\t\\t\\tLS.RM.resourceModified( material );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"sendResourceRenamedEvent: Material not found or it didnt have a onResourceRenamed\\\");\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//used to search a resource according to the data path of this scene\\r\\nScene.prototype.getDataPath = function( path )\\r\\n{\\r\\n\\tpath = path || \\\"\\\";\\r\\n\\tvar folder = this.extra.data_folder || this.extra.folder;\\r\\n\\treturn LS.RM.cleanFullpath( folder + \\\"/\\\" + path );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Creates and returns an scene animation track\\r\\n*\\r\\n* @method createAnimation\\r\\n* @return {LS.Animation} the animation track\\r\\n*/\\r\\nScene.prototype.createAnimation = function()\\r\\n{\\r\\n\\tif(this.animation)\\r\\n\\t\\treturn this.animation;\\r\\n\\tthis.animation = new LS.Animation();\\r\\n\\tthis.animation.name = LS.Animation.DEFAULT_SCENE_NAME;\\r\\n\\tthis.animation.createTake( \\\"default\\\", LS.Animation.DEFAULT_DURATION );\\r\\n\\treturn this.animation;\\r\\n}\\r\\n\\r\\nLS.Scene = Scene;\\r\\nLS.Classes.Scene = Scene;\\r\\nLS.Classes.SceneTree = Scene; //LEGACY\\r\\n\\r\\n\\r\\n///@FILE:../src/sceneNode.js\\r\\n///@INFO: BASE\\r\\n//****************************************************************************\\r\\n\\r\\n/**\\r\\n* The SceneNode class represents and object in the scene\\r\\n* Is the base class for all objects in the scene as meshes, lights, cameras, and so\\r\\n*\\r\\n* @class SceneNode\\r\\n* @param {String} name the name for this node (otherwise a random one is computed)\\r\\n* @constructor\\r\\n*/\\r\\n\\r\\nfunction SceneNode( name )\\r\\n{\\r\\n\\tif(name && name.constructor !== String)\\r\\n\\t{\\r\\n\\t\\tname = null;\\r\\n\\t\\tconsole.warn(\\\"SceneNode constructor first parameter must be a String with the name\\\");\\r\\n\\t}\\r\\n\\r\\n\\t//Generic identifying info\\r\\n\\tthis._name = name || (\\\"node_\\\" + (Math.random() * 10000).toFixed(0)); //generate random number\\r\\n\\tthis._uid = LS.generateUId(\\\"NODE-\\\");\\r\\n\\tthis._classList = {}; //to store classes\\r\\n\\tthis.layers = 3|0; //32 bits for layers (force to int)\\r\\n\\tthis.node_type = null; //used to store a string defining the node info\\r\\n\\r\\n\\t//more generic info\\r\\n\\tthis._prefab = null;\\r\\n\\tthis._material = null;\\r\\n\\r\\n\\t//from Componentcontainer\\r\\n\\tthis._components = []; //used for logic actions\\r\\n\\r\\n\\t//from CompositePattern\\r\\n\\tthis._parentNode = null;\\r\\n\\tthis._children = null;\\r\\n\\tthis._in_tree = null;\\r\\n\\tthis._instances = []; //render instances\\r\\n\\r\\n\\t//flags\\r\\n\\tthis.flags = {\\r\\n\\t\\tvisible: true,\\r\\n\\t\\tis_static: false,\\r\\n\\t\\tselectable: true,\\r\\n\\t\\tlocked: false\\r\\n\\t};\\r\\n\\r\\n\\tthis.init(false,true);\\r\\n\\r\\n\\t/** Fired here (from Transform) when the node transform changes\\r\\n\\t * @event transformChanged\\r\\n\\t */\\r\\n}\\r\\n\\r\\nSceneNode.prototype.init = function( keep_components, keep_info )\\r\\n{\\r\\n\\tif(!keep_info)\\r\\n\\t{\\r\\n\\t\\tthis.layers = 3|0; //32 bits for layers (force to int)\\r\\n\\t\\tthis._name = name || (\\\"node_\\\" + (Math.random() * 10000).toFixed(0)); //generate random number\\r\\n\\t\\tthis._uid = LS.generateUId(\\\"NODE-\\\");\\r\\n\\t\\tthis._classList = {};\\r\\n\\r\\n\\t\\t//material\\r\\n\\t\\tthis._material = null;\\r\\n\\t\\tthis.extra = {}; //for extra info\\r\\n\\t\\tthis.node_type = null;\\r\\n\\r\\n\\t\\t//flags\\r\\n\\t\\tthis.flags = {\\r\\n\\t\\t\\tvisible: true,\\r\\n\\t\\t\\tis_static: false,\\r\\n\\t\\t\\tselectable: true\\r\\n\\t\\t};\\r\\n\\t}\\r\\n\\r\\n\\t//Basic components\\r\\n\\tif(!keep_components)\\r\\n\\t{\\r\\n\\t\\tif( this._components && this._components.length )\\r\\n\\t\\t\\tconsole.warn(\\\"SceneNode.init() should not be called if it contains components, call clear instead\\\");\\r\\n\\t\\tthis._components = []; //used for logic actions\\r\\n\\t\\tthis.addComponent( new LS.Transform() );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n//get methods from other classes\\r\\nLS.extendClass( SceneNode, ComponentContainer ); //container methods\\r\\nLS.extendClass( SceneNode, CompositePattern ); //container methods\\r\\n\\r\\n/**\\r\\n* changes the node name\\r\\n* @method setName\\r\\n* @param {String} new_name the new name\\r\\n* @return {Object} returns true if the name changed\\r\\n*/\\r\\n\\r\\nObject.defineProperty( SceneNode.prototype, 'name', {\\r\\n\\tset: function(name)\\r\\n\\t{\\r\\n\\t\\tthis.setName( name );\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this._name;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( SceneNode.prototype, 'fullname', {\\r\\n\\tset: function(name)\\r\\n\\t{\\r\\n\\t\\tthrow(\\\"You cannot set fullname, it depends on the parent nodes\\\");\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this.getPathName();\\r\\n\\t},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n//Changing the UID has lots of effects (because nodes are indexed by UID in the scene)\\r\\n//If you want to catch the event of the uid_change, remember, the previous uid is stored in LS.SceneNode._last_uid_changed (it is not passed in the event)\\r\\nObject.defineProperty( SceneNode.prototype, 'uid', {\\r\\n\\tset: function(uid)\\r\\n\\t{\\r\\n\\t\\tif(!uid)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t//valid uid?\\r\\n\\t\\tif(uid[0] != LS._uid_prefix)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"Invalid UID, renaming it to: \\\" + uid );\\r\\n\\t\\t\\tuid = LS._uid_prefix + uid;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//no changes?\\r\\n\\t\\tif(uid == this._uid)\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tSceneNode._last_uid_changed = this._uid; //hack, in case we want the previous uid of a node \\r\\n\\r\\n\\t\\t//update scene tree indexing\\r\\n\\t\\tif( this._in_tree && this._in_tree._nodes_by_uid[ this.uid ] )\\r\\n\\t\\t\\tdelete this._in_tree._nodes_by_uid[ this.uid ];\\r\\n\\t\\tthis._uid = uid;\\r\\n\\t\\tif( this._in_tree )\\r\\n\\t\\t\\tthis._in_tree._nodes_by_uid[ this.uid ] = this;\\r\\n\\t\\t//events\\r\\n\\t\\tLEvent.trigger( this, \\\"uid_changed\\\", uid );\\r\\n\\t\\tif(this._in_tree)\\r\\n\\t\\t\\tLEvent.trigger( this._in_tree, \\\"node_uid_changed\\\", this );\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this._uid;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n\\r\\nObject.defineProperty( SceneNode.prototype, 'visible', {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tthis.flags.visible = v;\\r\\n\\t\\tif( this._children )\\r\\n\\t\\tfor(var i = 0; i < this._children.length; ++i )\\r\\n\\t\\t\\tthis._children[i].visible = v;\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this.flags.visible;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( SceneNode.prototype, 'is_static', {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tthis.flags.is_static = v;\\r\\n\\t\\tif( v && this._children )\\r\\n\\t\\tfor(var i = 0; i < this._children.length; ++i )\\r\\n\\t\\t\\tthis._children[i].is_static = v;\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this.flags.is_static;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( SceneNode.prototype, 'material', {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tif( this._material == v )\\r\\n\\t\\t\\treturn;\\r\\n\\r\\n\\t\\tthis._material = v;\\r\\n\\t\\tif(v)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(v.constructor === String)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\t\\t\\tif(v._root && v._root != this) //has root and its not me\\r\\n\\t\\t\\t\\tconsole.warn( \\\"Cannot assign a material of one SceneNode to another, you must clone it or register it\\\" )\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tv._root = this; //link\\r\\n\\t\\t}\\r\\n\\t\\tLEvent.trigger( this, \\\"materialChanged\\\" );\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this._material;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nObject.defineProperty( SceneNode.prototype, 'prefab', {\\r\\n\\tset: function(name)\\r\\n\\t{\\r\\n\\t\\tthis._prefab = name;\\r\\n\\t\\tif(!this._prefab)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar prefab = LS.RM.getResource(name);\\r\\n\\t\\tvar that = this;\\r\\n\\t\\tif(prefab)\\r\\n\\t\\t\\tthis.reloadFromPrefab();\\r\\n\\t\\telse \\r\\n\\t\\t\\tLS.ResourcesManager.load( name, function(){\\r\\n\\t\\t\\t\\tthat.reloadFromPrefab();\\r\\n\\t\\t\\t});\\r\\n\\t},\\r\\n\\tget: function(){\\r\\n\\t\\treturn this._prefab;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\nSceneNode.prototype.clear = function()\\r\\n{\\r\\n\\tthis.removeAllComponents();\\r\\n\\tthis.removeAllChildren();\\r\\n\\tthis.init();\\r\\n}\\r\\n\\r\\nSceneNode.prototype.setName = function(new_name)\\r\\n{\\r\\n\\tif(this._name == new_name) \\r\\n\\t\\treturn true; //no changes\\r\\n\\r\\n\\t//check that the name is valid (doesnt have invalid characters)\\r\\n\\tif(!LS.validateName(new_name))\\r\\n\\t{\\r\\n\\t\\tconsole.warn(\\\"invalid name for node: \\\" + new_name );\\r\\n\\t\\t//new_name = new_name.replace(/[^a-z0-9\\\\.\\\\-]/gi,\\\"_\\\");\\r\\n\\t\\treturn false;\\r\\n\\t}\\r\\n\\r\\n\\tvar scene = this._in_tree;\\r\\n\\tif(!scene)\\r\\n\\t{\\r\\n\\t\\tthis._name = new_name;\\r\\n\\t\\treturn true;\\r\\n\\t}\\r\\n\\r\\n\\t//remove old link\\r\\n\\tif( this._name )\\r\\n\\t\\tdelete scene._nodes_by_name[ this._name ];\\r\\n\\r\\n\\t//assign name\\r\\n\\tthis._name = new_name;\\r\\n\\r\\n\\t//we already have another node with this name\\r\\n\\tif( new_name && !scene._nodes_by_name[ new_name ] )\\r\\n\\t\\tscene._nodes_by_name[ this._name ] = this;\\r\\n\\r\\n\\t/**\\r\\n\\t * Node changed name\\r\\n\\t *\\r\\n\\t * @event name_changed\\r\\n\\t * @param {String} new_name\\r\\n\\t */\\r\\n\\tLEvent.trigger( this, \\\"name_changed\\\", new_name );\\r\\n\\tif(scene)\\r\\n\\t\\tLEvent.trigger( scene, \\\"node_name_changed\\\", this );\\r\\n\\treturn true;\\r\\n}\\r\\n\\r\\nObject.defineProperty( SceneNode.prototype, 'classList', {\\r\\n\\tget: function() { return this._classList },\\r\\n\\tset: function(v) {},\\r\\n\\tenumerable: false\\r\\n});\\r\\n\\r\\n/**\\r\\n* @property className {String}\\r\\n*/\\r\\nObject.defineProperty( SceneNode.prototype, 'className', {\\r\\n\\tget: function() {\\r\\n\\t\\t\\tvar keys = null;\\r\\n\\t\\t\\tif(Object.keys)\\r\\n\\t\\t\\t\\tkeys = Object.keys(this._classList); \\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tkeys = [];\\r\\n\\t\\t\\t\\tfor(var k in this._classList)\\r\\n\\t\\t\\t\\t\\tkeys.push(k);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn keys.join(\\\" \\\");\\r\\n\\t\\t},\\r\\n\\tset: function(v) { \\r\\n\\t\\tthis._classList = {};\\r\\n\\t\\tif(!v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar t = v.split(\\\" \\\");\\r\\n\\t\\tfor(var i in t)\\r\\n\\t\\t\\tthis._classList[ t[i] ] = true;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* Destroys this node\\r\\n* @method destroy\\r\\n* @param {number} time [optional] time in seconds to wait till destroying the node\\r\\n**/\\r\\nSceneNode.prototype.destroy = function( time )\\r\\n{\\r\\n\\tif(time && time.constructor === Number && time > 0)\\r\\n\\t{\\r\\n\\t\\tsetTimeout( this.destroy.bind(this,0), time * 0.001 );\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tLEvent.trigger( this, \\\"destroy\\\" );\\r\\n\\tthis.removeAllComponents();\\r\\n\\tif(this.children)\\r\\n\\t\\twhile(this.children.length)\\r\\n\\t\\t\\tthis.children[0].destroy();\\r\\n\\tif(this._parentNode)\\r\\n\\t\\tthis._parentNode.removeChild( this );\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the locator string of this node\\r\\n* @method getLocator\\r\\n* @param {string} property_name [optional] you can pass the name of a property in this node to get the locator of that one\\r\\n* @return {String} the locator string of this node\\r\\n**/\\r\\nSceneNode.prototype.getLocator = function( property_name )\\r\\n{\\r\\n\\tif(!property_name)\\r\\n\\t\\treturn this.uid;\\r\\n\\treturn this.uid + \\\"/\\\" + property_name;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns and object with info about a property given a locator\\r\\n* @method getPropertyInfo\\r\\n* @param {string} locator\\r\\n* @return {Object} object with { node, target, name, value and type }\\r\\n**/\\r\\nSceneNode.prototype.getPropertyInfo = function( locator )\\r\\n{\\r\\n\\tvar path = locator.split(\\\"/\\\");\\r\\n\\treturn this.getPropertyInfoFromPath(path);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns and object with info about a property given a locator in path format\\r\\n* @method getPropertyInfoFromPath\\r\\n* @param {Array} path a locator in path format (split by /)\\r\\n* @return {Object} object with { node, target, name, value and type }\\r\\n**/\\r\\nSceneNode.prototype.getPropertyInfoFromPath = function( path )\\r\\n{\\r\\n\\tvar target = this;\\r\\n\\tvar varname = path[0];\\r\\n\\tvar no_slice = false;\\r\\n\\r\\n\\tif(path.length == 0)\\r\\n\\t{\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tnode: this,\\r\\n\\t\\t\\ttarget: null,\\r\\n\\t\\t\\tname: \\\"\\\",\\r\\n\\t\\t\\tvalue: this,\\r\\n\\t\\t\\ttype: \\\"node\\\" //node because thats the global type for nodes\\r\\n\\t\\t};\\r\\n\\t}\\r\\n else if(path.length == 1) //compo or //var\\r\\n\\t{\\r\\n\\t\\tif(path[0][0] == \\\"@\\\") //compo uid\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.getComponentByUId( path[0] );\\r\\n\\t\\t\\treturn {\\r\\n\\t\\t\\t\\tnode: this,\\r\\n\\t\\t\\t\\ttarget: target,\\r\\n\\t\\t\\t\\tname: target ? LS.getObjectClassName( target ) : \\\"\\\",\\r\\n\\t\\t\\t\\ttype: \\\"component\\\",\\r\\n\\t\\t\\t\\tvalue: target\\r\\n\\t\\t\\t};\\r\\n\\t\\t}\\r\\n\\t\\telse if (path[0] == \\\"material\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.getMaterial();\\r\\n\\t\\t\\treturn {\\r\\n\\t\\t\\t\\tnode: this,\\r\\n\\t\\t\\t\\ttarget: target,\\r\\n\\t\\t\\t\\tname: target ? LS.getObjectClassName( target ) : \\\"\\\",\\r\\n\\t\\t\\t\\ttype: \\\"material\\\",\\r\\n\\t\\t\\t\\tvalue: target\\r\\n\\t\\t\\t};\\r\\n\\t\\t}\\r\\n\\t\\telse if (path[0] == \\\"visible\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn {\\r\\n\\t\\t\\t\\tnode: this,\\r\\n\\t\\t\\t\\ttarget: this,\\r\\n\\t\\t\\t\\tname: \\\"visible\\\",\\r\\n\\t\\t\\t\\ttype: \\\"boolean\\\",\\r\\n\\t\\t\\t\\tvalue: this.visible\\r\\n\\t\\t\\t};\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar target = this.getComponent( path[0] );\\r\\n\\t\\tif(target)\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn {\\r\\n\\t\\t\\t\\tnode: this,\\r\\n\\t\\t\\t\\ttarget: target,\\r\\n\\t\\t\\t\\tname: target ? LS.getObjectClassName( target ) : \\\"\\\",\\r\\n\\t\\t\\t\\ttype: \\\"component\\\",\\r\\n\\t\\t\\t\\tvalue: target\\r\\n\\t\\t\\t};\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//special cases for a node\\r\\n\\t\\tswitch(path[0])\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase \\\"matrix\\\":\\r\\n\\t\\t\\tcase \\\"x\\\":\\r\\n\\t\\t\\tcase \\\"y\\\": \\r\\n\\t\\t\\tcase \\\"z\\\": \\r\\n\\t\\t\\tcase \\\"position\\\":\\r\\n\\t\\t\\tcase \\\"rotX\\\":\\r\\n\\t\\t\\tcase \\\"rotY\\\":\\r\\n\\t\\t\\tcase \\\"rotZ\\\":\\r\\n\\t\\t\\t\\ttarget = this.transform;\\r\\n\\t\\t\\t\\tvarname = path[0];\\r\\n\\t\\t\\t\\tno_slice = true;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tdefault: \\r\\n\\t\\t\\t\\ttarget = this;\\r\\n\\t\\t\\t\\tvarname = path[0];\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n else if(path.length > 1) //compo/var\\r\\n\\t{\\r\\n\\t\\tif(path[0][0] == \\\"@\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tvarname = path[1];\\r\\n\\t\\t\\ttarget = this.getComponentByUId( path[0] );\\r\\n\\t\\t}\\r\\n\\t\\telse if (path[0] == \\\"material\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.getMaterial();\\r\\n\\t\\t\\tvarname = path[1];\\r\\n\\t\\t}\\r\\n\\t\\telse if (path[0] == \\\"flags\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.flags;\\r\\n\\t\\t\\tvarname = path[1];\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.getComponent( path[0] );\\r\\n\\t\\t\\tvarname = path[1];\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!target)\\r\\n\\t\\t\\treturn null;\\r\\n\\t}\\r\\n\\telse //�?\\r\\n\\t{\\r\\n\\t}\\r\\n\\r\\n\\tif(!target) //unknown target\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\t//this was moved to Component.prototype.getPropertyInfoFromPath (if any errors check cases)\\r\\n\\tif( target != this && target.getPropertyInfoFromPath ) //avoid weird recursion\\r\\n\\t\\treturn target.getPropertyInfoFromPath( no_slice ? path : path.slice(1) );\\r\\n\\r\\n\\treturn null;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the value of a property given a locator in string format\\r\\n* @method getPropertyValue\\r\\n* @param {String} locaator\\r\\n* @return {*} the value of that property\\r\\n**/\\r\\nSceneNode.prototype.getPropertyValue = function( locator )\\r\\n{\\r\\n\\tvar path = locator.split(\\\"/\\\");\\r\\n\\treturn this.getPropertyValueFromPath(path);\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns the value of a property given a locator in path format\\r\\n* @method getPropertyValueFromPath\\r\\n* @param {Array} locator in path format (array)\\r\\n* @return {*} the value of that property\\r\\n**/\\r\\nSceneNode.prototype.getPropertyValueFromPath = function( path )\\r\\n{\\r\\n\\tvar target = this;\\r\\n\\tvar varname = path[0];\\r\\n\\tvar no_slice = false;\\r\\n\\r\\n\\tif(path.length == 0)\\r\\n\\t\\treturn null\\r\\n else if(path.length == 1) //compo or //var\\r\\n\\t{\\r\\n\\t\\tif(path[0][0] == \\\"@\\\")\\r\\n\\t\\t\\treturn this.getComponentByUId( path[0] );\\r\\n\\t\\telse if (path[0] == \\\"material\\\")\\r\\n\\t\\t\\treturn this.getMaterial();\\r\\n\\t\\tvar target = this.getComponent( path[0] );\\r\\n\\t\\tif(target)\\r\\n\\t\\t\\treturn target;\\r\\n\\r\\n\\t\\tswitch(path[0])\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase \\\"matrix\\\":\\r\\n\\t\\t\\tcase \\\"x\\\":\\r\\n\\t\\t\\tcase \\\"y\\\": \\r\\n\\t\\t\\tcase \\\"z\\\": \\r\\n\\t\\t\\tcase \\\"position\\\":\\r\\n\\t\\t\\tcase \\\"rotX\\\":\\r\\n\\t\\t\\tcase \\\"rotY\\\":\\r\\n\\t\\t\\tcase \\\"rotZ\\\":\\r\\n\\t\\t\\t\\ttarget = this.transform;\\r\\n\\t\\t\\t\\tvarname = path[0];\\r\\n\\t\\t\\t\\tno_slice = true;\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tdefault: \\r\\n\\t\\t\\t\\ttarget = this;\\r\\n\\t\\t\\t\\tvarname = path[0];\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n else if(path.length > 1) //compo/var\\r\\n\\t{\\r\\n\\t\\tif(path[0][0] == \\\"@\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tvarname = path[1];\\r\\n\\t\\t\\ttarget = this.getComponentByUId( path[0] );\\r\\n\\t\\t}\\r\\n\\t\\telse if (path[0] == \\\"material\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.getMaterial();\\r\\n\\t\\t\\tvarname = path[1];\\r\\n\\t\\t}\\r\\n\\t\\telse if (path[0] == \\\"flags\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.flags;\\r\\n\\t\\t\\tvarname = path[1];\\r\\n\\t\\t}\\r\\n\\t\\telse if (path[0] == \\\"visible\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this;\\r\\n\\t\\t\\tvarname = path[0];\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.getComponent( path[0] );\\r\\n\\t\\t\\tvarname = path[1];\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!target)\\r\\n\\t\\t\\treturn null;\\r\\n\\t}\\r\\n\\telse //�?\\r\\n\\t{\\r\\n\\t}\\r\\n\\r\\n\\tvar v = undefined;\\r\\n\\r\\n\\tif( target.getPropertyValueFromPath && target != this )\\r\\n\\t{\\r\\n\\t\\tvar r = target.getPropertyValueFromPath( no_slice ? path : path.slice(1) );\\r\\n\\t\\tif(r)\\r\\n\\t\\t\\treturn r;\\r\\n\\t}\\r\\n\\r\\n\\t//to know the value of a property of the given target\\r\\n\\tif( target.getPropertyValue && target != this )\\r\\n\\t\\tv = target.getPropertyValue( varname );\\r\\n\\r\\n\\t//special case when the component doesnt specify any locator info but the property referenced does\\r\\n\\t//used in TextureFX\\r\\n\\tif (v === undefined && path.length > 2 && target[ varname ] && target[ varname ].getPropertyValueFromPath )\\r\\n\\t{\\r\\n\\t\\tvar r = target[ varname ].getPropertyValueFromPath( no_slice ? path.slice(1) : path.slice(2) );\\r\\n\\t\\tif(r)\\r\\n\\t\\t{\\r\\n\\t\\t\\tr.node = this;\\r\\n\\t\\t\\treturn r;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif(v === undefined && target[ varname ] === undefined )\\r\\n\\t\\treturn null;\\r\\n\\treturn v !== undefined ? v : target[ varname ];\\r\\n}\\r\\n\\r\\n/**\\r\\n* assigns a value to a property given the locator for that property\\r\\n* @method setPropertyValue\\r\\n* @param {String} locator\\r\\n* @param {*} value\\r\\n**/\\r\\nSceneNode.prototype.setPropertyValue = function( locator, value )\\r\\n{\\r\\n\\tvar path = locator.split(\\\"/\\\");\\r\\n\\treturn this.setPropertyValueFromPath(path, value, 0);\\r\\n}\\r\\n\\r\\n/**\\r\\n* given a locator in path mode (array) and a value, it searches for the corresponding value and applies it\\r\\n* @method setPropertyValueFromPath\\r\\n* @param {Array} path\\r\\n* @param {*} value\\r\\n* @param {Number} [optional] offset used to skip the firsst positions in the array\\r\\n**/\\r\\nSceneNode.prototype.setPropertyValueFromPath = function( path, value, offset )\\r\\n{\\r\\n\\toffset = offset || 0;\\r\\n\\r\\n\\tif(this.flags && this.flags.locked)\\r\\n\\t\\treturn; //lock ignores changes from animations or graphs\\r\\n\\r\\n\\tvar target = null;\\r\\n\\tvar varname = path[offset];\\r\\n\\r\\n\\tif(path.length > (offset+1))\\r\\n\\t{\\r\\n\\t\\tif(path[offset][0] == \\\"@\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tvarname = path[offset+1];\\r\\n\\t\\t\\ttarget = this.getComponentByUId( path[offset] );\\r\\n\\t\\t}\\r\\n\\t\\telse if( path[offset] == \\\"material\\\" )\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.getMaterial();\\r\\n\\t\\t\\tvarname = path[offset+1];\\r\\n\\t\\t}\\r\\n\\t\\telse if( path[offset] == \\\"flags\\\" )\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.flags;\\r\\n\\t\\t\\tvarname = path[offset+1];\\r\\n\\t\\t}\\r\\n\\t\\telse if( path[offset] == \\\"visible\\\" )\\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this;\\r\\n\\t\\t\\tvarname = path[offset];\\r\\n\\t\\t}\\r\\n\\t\\telse \\r\\n\\t\\t{\\r\\n\\t\\t\\ttarget = this.getComponent( path[offset] );\\r\\n\\t\\t\\tvarname = path[offset+1];\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!target)\\r\\n\\t\\t\\treturn null;\\r\\n\\t}\\r\\n\\telse { //special cases \\r\\n\\t\\tswitch ( path[offset] )\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase \\\"matrix\\\": target = this.transform; break;\\r\\n\\t\\t\\tcase \\\"position\\\":\\r\\n\\t\\t\\tcase \\\"rotation\\\":\\r\\n\\t\\t\\tcase \\\"x\\\":\\r\\n\\t\\t\\tcase \\\"y\\\":\\r\\n\\t\\t\\tcase \\\"z\\\":\\r\\n\\t\\t\\tcase \\\"xrotation\\\": \\r\\n\\t\\t\\tcase \\\"yrotation\\\": \\r\\n\\t\\t\\tcase \\\"zrotation\\\": \\r\\n\\t\\t\\t\\ttarget = this.transform; \\r\\n\\t\\t\\t\\tvarname = path[offset];\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\tcase \\\"translate.X\\\": target = this.transform; varname = \\\"x\\\"; break;\\r\\n\\t\\t\\tcase \\\"translate.Y\\\": target = this.transform; varname = \\\"y\\\"; break;\\r\\n\\t\\t\\tcase \\\"translate.Z\\\": target = this.transform; varname = \\\"z\\\"; break;\\r\\n\\t\\t\\tcase \\\"rotateX.ANGLE\\\": target = this.transform; varname = \\\"pitch\\\"; break;\\r\\n\\t\\t\\tcase \\\"rotateY.ANGLE\\\": target = this.transform; varname = \\\"yaw\\\"; break;\\r\\n\\t\\t\\tcase \\\"rotateZ.ANGLE\\\": target = this.transform; varname = \\\"roll\\\"; break;\\r\\n\\t\\t\\tdefault: target = this; //null\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif(!target)\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\tif(target.setPropertyValueFromPath && target != this)\\r\\n\\t\\tif( target.setPropertyValueFromPath( path, value, offset+1 ) === true )\\r\\n\\t\\t\\treturn target;\\r\\n\\t\\r\\n\\tif(target.setPropertyValue && target != this)\\r\\n\\t\\tif( target.setPropertyValue( varname, value ) === true )\\r\\n\\t\\t\\treturn target;\\r\\n\\r\\n\\tif( target[ varname ] === undefined )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//special case when the component doesnt specify any locator info but the property referenced does\\r\\n\\t//used in TextureFX\\r\\n\\tif ( path.length > 2 && target[ varname ] && target[ varname ].setPropertyValueFromPath )\\r\\n\\t\\treturn target[ varname ].setPropertyValueFromPath( path, value, offset+2 );\\r\\n\\r\\n\\t//disabled because if the vars has a setter it wont be called using the array.set\\r\\n\\t//if( target[ varname ] !== null && target[ varname ].set )\\r\\n\\t//\\ttarget[ varname ].set( value );\\r\\n\\t//else\\r\\n\\t\\ttarget[ varname ] = value;\\r\\n\\r\\n\\treturn target;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Returns all the resources used by this node and its components (you can include the resources from the children too)\\r\\n* @method getResources\\r\\n* @param {Object} res object where to store the resources used (in \\\"res_name\\\":LS.TYPE format)\\r\\n* @param {Boolean} include_children if you want to add also the resources used by the children nodes\\r\\n* @return {Object} the same object passed is returned \\r\\n**/\\r\\nSceneNode.prototype.getResources = function( res, include_children )\\r\\n{\\r\\n\\t//resources in components\\r\\n\\tfor(var i in this._components)\\r\\n\\t\\tif( this._components[i].getResources )\\r\\n\\t\\t\\tthis._components[i].getResources( res );\\r\\n\\r\\n\\t//res in material\\r\\n\\tif(this.material)\\r\\n\\t{\\r\\n\\t\\tif( this.material.constructor === String )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(this.material[0] != \\\":\\\") //not a local material, then its a reference\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tres[this.material] = LS.Material;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar mat = this.getMaterial();\\r\\n\\t\\tif(mat)\\r\\n\\t\\t\\tmat.getResources( res );\\r\\n\\t}\\r\\n\\r\\n\\t//prefab\\r\\n\\tif(this.prefab)\\r\\n\\t\\tres[ this.prefab ] = LS.Prefab;\\r\\n\\r\\n\\t//propagate\\r\\n\\tif(include_children)\\r\\n\\t\\tfor(var i in this._children)\\r\\n\\t\\t\\tthis._children[i].getResources(res, true);\\r\\n\\r\\n\\treturn res;\\r\\n}\\r\\n\\r\\nSceneNode.prototype.getTransform = function() {\\r\\n\\treturn this.transform;\\r\\n}\\r\\n\\r\\n//Helpers\\r\\n\\r\\nSceneNode.prototype.getMesh = function( use_lod_mesh ) {\\r\\n\\tvar mesh = this.mesh;\\r\\n\\tvar mesh_renderer = this.getComponent( LS.Components.MeshRenderer );\\r\\n\\tif(!mesh && mesh_renderer)\\r\\n\\t{\\r\\n\\t\\tif(use_lod_mesh)\\r\\n\\t\\t\\tmesh = mesh_renderer.lod_mesh;\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t\\tmesh = mesh_renderer.mesh;\\r\\n\\t}\\r\\n\\tif(!mesh)\\r\\n\\t\\treturn null;\\r\\n\\tif(mesh.constructor === String)\\r\\n\\t\\treturn LS.ResourcesManager.meshes[mesh];\\r\\n\\treturn mesh;\\r\\n}\\r\\n\\r\\n//Light component\\r\\nSceneNode.prototype.getLight = function() {\\r\\n\\treturn this.light;\\r\\n}\\r\\n\\r\\n//Camera component\\r\\nSceneNode.prototype.getCamera = function() {\\r\\n\\treturn this.camera;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Allows to load some kind of resource and associate it to this node.\\r\\n* It can be for prefabs, meshes, scenes from daes, etc\\r\\n* @method load\\r\\n* @param {string} url\\r\\n* @param {Function} on_complete\\r\\n**/\\r\\nSceneNode.prototype.load = function( url, on_complete )\\r\\n{\\r\\n\\tvar that = this;\\r\\n\\tLS.ResourcesManager.load( url, inner );\\r\\n\\tfunction inner( resource )\\r\\n\\t{\\r\\n\\t\\tif(!resource)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthat.assign( resource );\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete();\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Assign a resource/element inteligently to a node: if it is a mesh it creates a MeshRenderer, if it is a Material it assigns it, if it is an animation creates a PlayAnimation, if it is a prefab assigns the prefab. etc\\r\\n* @method assign\\r\\n* @param {*} resource the resource to assign (it also accepts a resource filename that has been previously loaded).\\r\\n* @param {Function} on_complete\\r\\n**/\\r\\nSceneNode.prototype.assign = function( item, extra )\\r\\n{\\r\\n\\tif(!item)\\r\\n\\t{\\r\\n\\t\\tconsole.error(\\\"assignResource cannot have null as resource\\\");\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\t//assume is the filename of a resource\\r\\n\\tif(item.constructor === String)\\r\\n\\t\\titem = LS.ResourcesManager.getResource( item );\\r\\n\\r\\n\\tif(!item)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tswitch( item.constructor )\\r\\n\\t{\\r\\n\\t\\tcase LS.SceneNode: \\r\\n\\t\\t\\tthis.addChild( item );\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase LS.Scene:\\r\\n\\t\\t\\tvar node = this;\\r\\n\\t\\t\\titem.loadScripts( null, function(){\\r\\n\\t\\t\\t\\titem.loadResources( function(){ \\r\\n\\t\\t\\t\\t\\tnode.addChild( item.root.clone() );\\r\\n\\t\\t\\t\\t});\\r\\n\\t\\t\\t});\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase LS.Prefab: \\r\\n\\t\\t\\tthis.prefab = item.fullpath || item.filename; \\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase GL.Mesh: \\r\\n\\t\\t\\tvar component = this.getComponent( LS.Components.MeshRenderer );\\r\\n\\t\\t\\tif(component)\\r\\n\\t\\t\\t\\tcomponent.configure({ mesh: item.fullpath || item.filename });\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tthis.addComponent( new LS.MeshRenderer({ mesh: mesh_name, submesh_id: submesh_id }) );\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase LS.Animation: \\r\\n\\t\\t\\tvar comp = this.getComponent( LS.Components.PlayAnimation );\\r\\n\\t\\t\\tif(!comp)\\r\\n\\t\\t\\t\\tcomp = this.addComponent( new LS.Components.PlayAnimation() );\\r\\n\\t\\t\\tcomp.animation = item.fullpath || item.filename;\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tcase LS.Resource: //generic resource\\r\\n\\t\\t\\tvar ext = LS.ResourcesManager.getExtension( item.filename );\\r\\n\\t\\t\\tif(ext == \\\"js\\\") //scripts\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar comp = this.getComponent( LS.Components.ScriptFromFile );\\r\\n\\t\\t\\t\\tif(!comp)\\r\\n\\t\\t\\t\\t\\tcomp = this.addComponent( new LS.Components.ScriptFromFile() );\\r\\n\\t\\t\\t\\tcomp.src = item.fullpath || item.filename;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\tconsole.error(\\\"feature not supported loading this type of resource\\\" , item );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Simple way to assign a mesh to a node, it created a MeshRenderer component or reuses and existing one and assigns the mesh\\r\\n* @method setMesh\\r\\n* @param {string} mesh_name the name of the mesh (path to the file)\\r\\n* @param {Number} submesh_id if you want to assign a submesh\\r\\n**/\\r\\nSceneNode.prototype.setMesh = function(mesh_name, submesh_id)\\r\\n{\\r\\n\\tvar component = this.getComponent( LS.Components.MeshRenderer );\\r\\n\\tif(component)\\r\\n\\t\\tcomponent.configure({ mesh: mesh_name, submesh_id: submesh_id });\\r\\n\\telse\\r\\n\\t\\tthis.addComponent( new LS.MeshRenderer({ mesh: mesh_name, submesh_id: submesh_id }) );\\r\\n}\\r\\n\\r\\nSceneNode.prototype.getMaterial = function()\\r\\n{\\r\\n\\tif (!this.material)\\r\\n\\t\\treturn null;\\r\\n\\tif(this.material.constructor === String)\\r\\n\\t{\\r\\n\\t\\tif( !this._in_tree )\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tif( this.material[0] == \\\"@\\\" )//uid\\r\\n\\t\\t\\treturn LS.ResourcesManager.materials_by_uid[ this.material ];\\r\\n\\t\\treturn LS.ResourcesManager.materials[ this.material ];\\r\\n\\t}\\r\\n\\treturn this.material;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Apply prefab info (skipping the root components) to node, so all children will be removed and components lost and overwritten\\r\\n* It is called from prefab.applyToNodes when a prefab is loaded in memory\\r\\n* @method reloadFromPrefab\\r\\n**/\\r\\nSceneNode.prototype.reloadFromPrefab = function()\\r\\n{\\r\\n\\tif(!this.prefab)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar prefab = LS.ResourcesManager.resources[ this.prefab ];\\r\\n\\tif(!prefab)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( prefab.constructor !== LS.Prefab )\\r\\n\\t\\tthrow(\\\"prefab must be a LS.Prefab class\\\");\\r\\n\\r\\n\\t//apply info\\r\\n\\tthis.removeAllChildren();\\r\\n\\tthis.init( true, true ); //keep components, keep_info\\r\\n\\tvar prefab_data = prefab.prefab_data;\\r\\n\\t\\r\\n\\t//remove all but children info (prefabs overwrite only children info)\\r\\n\\tprefab_data = { children: prefab.prefab_data.children };\\r\\n\\r\\n\\t//uid data is already removed from the prefab\\r\\n\\tthis.configure( prefab_data );\\r\\n\\r\\n\\t//load secondary resources \\r\\n\\tvar resources = this.getResources( {}, true );\\r\\n\\tLS.ResourcesManager.loadResources( resources );\\r\\n\\r\\n\\tLEvent.trigger( this, \\\"prefabReady\\\", prefab );\\r\\n}\\r\\n\\r\\n\\r\\n/**\\r\\n* Assigns this node to one layer\\r\\n* @method setLayer\\r\\n* @param {number|String} the index of the layer or the name (according to scene.layer_names)\\r\\n* @param {boolean} value \\r\\n*/\\r\\nSceneNode.prototype.setLayer = function( num_or_name, value )\\r\\n{\\r\\n\\tif( num_or_name == null )\\r\\n\\t\\tthrow(\\\"setLayer expects layer\\\");\\r\\n\\r\\n\\tvar num;\\r\\n\\r\\n\\tif(num_or_name.constructor === String)\\r\\n\\t{\\r\\n\\t\\tvar scene = this.scene || LS.GlobalScene;\\r\\n\\t\\tvar layer_num = scene.layer_names.indexOf( num_or_name );\\r\\n\\t\\tif(layer_num == -1)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"Layer with name:\\\",num_or_name,\\\"not found in scene\\\");\\r\\n\\t\\t\\treturn;\\r\\n\\t\\t}\\r\\n\\t\\tnum = layer_num;\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tnum = num_or_name;\\r\\n\\r\\n\\tvar f = 1<= LIGHT.Attenuation.y)\\\\n\\\\\\r\\n\\t\\t\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t\\t\\tif(LIGHT.Distance >= LIGHT.Attenuation.x)\\\\n\\\\\\r\\n\\t\\t\\t\\treturn 1.0 - (LIGHT.Distance - LIGHT.Attenuation.x) / (LIGHT.Attenuation.y - LIGHT.Attenuation.x);\\\\n\\\\\\r\\n\\t\\t}\\\\n\\\\\\r\\n\\t\\treturn 1.0;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\nLight._attenuation_disabled_fragment_code = \\\"\\\";\\r\\n\\r\\nvar attenuation_block = Light.attenuation_block = new LS.ShaderBlock(\\\"attenuation\\\");\\r\\nattenuation_block.addCode( GL.FRAGMENT_SHADER, Light._attenuation_enabled_fragment_code, Light._attenuation_disabled_fragment_code );\\r\\nattenuation_block.register();\\r\\n\\r\\n// LIGHT TEXTURE **********************************************\\r\\n//this block handles light cookies (textures modulating light)\\r\\nLight._light_texture_fragment_enabled_code =\\\"\\\\n\\\\\\r\\nuniform sampler2D light_texture;\\\\n\\\\\\r\\nvoid applyLightTexture( in Input IN, inout Light LIGHT )\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n\\tvec4 v = LIGHT.Matrix * vec4( IN.worldPos,1.0 );\\\\n\\\\\\r\\n\\tvec2 uv = v.xy / v.w * 0.5 + vec2(0.5);\\\\n\\\\\\r\\n\\tLIGHT.Color *= texture2D( light_texture, uv ).xyz;\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nLight._light_texture_fragment_disabled_code =\\\"\\\\n\\\\\\r\\nvoid applyLightTexture( in Input IN, inout Light LIGHT )\\\\n\\\\\\r\\n{\\\\n\\\\\\r\\n}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nvar light_texture_block = Light.light_texture_block = new LS.ShaderBlock(\\\"light_texture\\\");\\r\\nlight_texture_block.addCode( GL.FRAGMENT_SHADER, Light._light_texture_fragment_enabled_code, Light._light_texture_fragment_disabled_code );\\r\\nlight_texture_block.register();\\r\\n\\r\\n/*\\r\\n// OMNI LIGHT SHADOWMAP *****************************************\\r\\nLight._shadowmap_cubemap_code = \\\"\\\\n\\\\\\r\\n\\t#define SHADOWMAP_ACTIVE\\\\n\\\\\\r\\n\\tuniform samplerCube shadowmap;\\\\n\\\\\\r\\n\\tuniform vec4 u_shadow_params; // (1.0/(texture_size), bias, near, far)\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat VectorToDepthValue(vec3 Vec)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tvec3 AbsVec = abs(Vec);\\\\n\\\\\\r\\n\\t\\tfloat LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));\\\\n\\\\\\r\\n\\t\\tfloat n = u_shadow_params.z;\\\\n\\\\\\r\\n\\t\\tfloat f = u_shadow_params.w;\\\\n\\\\\\r\\n\\t\\tfloat NormZComp = (f+n) / (f-n) - (2.0*f*n)/(f-n)/LocalZcomp;\\\\n\\\\\\r\\n\\t\\treturn (NormZComp + 1.0) * 0.5;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat UnpackDepth32(vec4 depth)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tconst vec4 bitShifts = vec4( 1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1);\\\\n\\\\\\r\\n\\t\\treturn dot(depth.xyzw , bitShifts);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat testShadow( Light LIGHT, vec3 offset )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tfloat shadow = 0.0;\\\\n\\\\\\r\\n\\t\\tfloat depth = 0.0;\\\\n\\\\\\r\\n\\t\\tfloat bias = u_shadow_params.y;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvec3 l_vector = (v_pos - u_light_position);\\\\n\\\\\\r\\n\\t\\tfloat dist = length(l_vector);\\\\n\\\\\\r\\n\\t\\tfloat pixel_z = VectorToDepthValue( l_vector );\\\\n\\\\\\r\\n\\t\\tif(pixel_z >= 0.998)\\\\n\\\\\\r\\n\\t\\t\\treturn 0.0; //fixes a little bit the far edge bug\\\\n\\\\\\r\\n\\t\\tvec4 depth_color = textureCube( shadowmap, l_vector + offset * dist );\\\\n\\\\\\r\\n\\t\\tfloat ShadowVec = UnpackDepth32( depth_color );\\\\n\\\\\\r\\n\\t\\tif ( ShadowVec > pixel_z - bias )\\\\n\\\\\\r\\n\\t\\t\\treturn 0.0; //no shadow\\\\n\\\\\\r\\n\\t\\treturn 1.0; //full shadow\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nLight._shadowmap_vertex_enabled_code =\\\"\\\\n\\\\\\r\\n\\t#pragma snippet \\\\\\\"light_structs\\\\\\\"\\\\n\\\\\\r\\n\\tvarying vec4 v_light_coord;\\\\n\\\\\\r\\n\\tvoid applyLight( vec3 pos ) { v_light_coord = u_light_matrix * vec4(pos,1.0); }\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nLight._shadowmap_vertex_disabled_code =\\\"\\\\n\\\\\\r\\n\\tvoid applyLight(vec3 pos) {}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\n\\r\\n// DIRECTIONAL AND SPOTLIGHT SHADOWMAP *****************************************\\r\\nLight._shadowmap_2d_enabled_fragment_code = \\\"\\\\n\\\\\\r\\n\\t#ifndef TESTSHADOW\\\\n\\\\\\r\\n\\t\\t#define TESTSHADOW\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\tuniform sampler2D shadowmap;\\\\n\\\\\\r\\n\\tvarying vec4 v_light_coord;\\\\n\\\\\\r\\n\\tuniform vec4 u_shadow_params; // (1.0/(texture_size), bias, near, far)\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat UnpackDepth(vec4 depth)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\t#ifdef BLOCK_DEPTH_IN_COLOR\\\\n\\\\\\r\\n\\t\\t\\tconst vec4 bitShifts = vec4( 1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1);\\\\n\\\\\\r\\n\\t\\t\\treturn dot(depth.xyzw , bitShifts);\\\\n\\\\\\r\\n\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\treturn depth.x;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tfloat texsize = 1.0 / u_shadow_params.x;\\\\n\\\\\\r\\n\\tfloat real_depth = 0.0;\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat pixelShadow( vec2 uv )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tfloat sampleDepth = UnpackDepth( texture2D(shadowmap, uv) );\\\\n\\\\\\r\\n\\t\\tfloat depth = (sampleDepth == 1.0) ? 1.0e9 : sampleDepth; //on empty data send it to far away\\\\n\\\\\\r\\n\\t\\tif (depth > 0.0) \\\\n\\\\\\r\\n\\t\\t\\treturn real_depth > depth ? 0.0 : 1.0;\\\\n\\\\\\r\\n\\t\\treturn 0.0;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\tfloat expFunc(float f)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\treturn f*f*f*(f*(f*6.0-15.0)+10.0);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tfloat testShadow( Light LIGHT )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tvec3 offset = vec3(0.0);\\\\n\\\\\\r\\n\\t\\tfloat depth = 0.0;\\\\n\\\\\\r\\n\\t\\tfloat bias = u_shadow_params.y;\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\tvec2 sample = (v_light_coord.xy / v_light_coord.w) * vec2(0.5) + vec2(0.5) + offset.xy;\\\\n\\\\\\r\\n\\t\\t//is inside light frustum\\\\n\\\\\\r\\n\\t\\tif (clamp(sample, 0.0, 1.0) != sample) \\\\n\\\\\\r\\n\\t\\t\\treturn LIGHT.Info.x == 3.0 ? 1.0 : 0.0; //outside of shadowmap, no shadow\\\\n\\\\\\r\\n\\t\\t\\\\n\\\\\\r\\n\\t\\treal_depth = (v_light_coord.z - bias) / v_light_coord.w * 0.5 + 0.5;\\\\n\\\\\\r\\n\\t\\tvec2 topleft_uv = sample * texsize;\\\\n\\\\\\r\\n\\t\\tvec2 offset_uv = fract( topleft_uv );\\\\n\\\\\\r\\n\\t\\toffset_uv.x = expFunc(offset_uv.x);\\\\n\\\\\\r\\n\\t\\toffset_uv.y = expFunc(offset_uv.y);\\\\n\\\\\\r\\n\\t\\ttopleft_uv = floor(topleft_uv) * u_shadow_params.x;\\\\n\\\\\\r\\n\\t\\tfloat topleft = pixelShadow( topleft_uv );\\\\n\\\\\\r\\n\\t\\tfloat topright = pixelShadow( topleft_uv + vec2(u_shadow_params.x,0.0) );\\\\n\\\\\\r\\n\\t\\tfloat bottomleft = pixelShadow( topleft_uv + vec2(0.0, u_shadow_params.x) );\\\\n\\\\\\r\\n\\t\\tfloat bottomright = pixelShadow( topleft_uv + vec2(u_shadow_params.x, u_shadow_params.x) );\\\\n\\\\\\r\\n\\t\\tfloat top = mix( topleft, topright, offset_uv.x );\\\\n\\\\\\r\\n\\t\\tfloat bottom = mix( bottomleft, bottomright, offset_uv.x );\\\\n\\\\\\r\\n\\t\\treturn mix( top, bottom, offset_uv.y );\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nLight._shadowmap_2d_disabled_code = \\\"\\\\nfloat testShadow( Light LIGHT ) { return 1.0; }\\\\n\\\";\\r\\n\\r\\nvar shadowmapping_depth_in_color_block = new LS.ShaderBlock(\\\"depth_in_color\\\");\\r\\nshadowmapping_depth_in_color_block.register();\\r\\nLight.shadowmapping_depth_in_color_block = shadowmapping_depth_in_color_block;\\r\\n\\r\\nvar shadowmapping_block = new LS.ShaderBlock(\\\"testShadow\\\");\\r\\nshadowmapping_block.addCode( GL.VERTEX_SHADER, Light._shadowmap_vertex_enabled_code, Light._shadowmap_vertex_disabled_code);\\r\\nshadowmapping_block.addCode( GL.FRAGMENT_SHADER, Light._shadowmap_2d_enabled_fragment_code, Light._shadowmap_2d_disabled_code );\\r\\n//shadowmapping_block.defineContextMacros({\\\"SHADOWBLOCK\\\":\\\"testShadow\\\"});\\r\\nshadowmapping_block.register();\\r\\nLight.shadowmapping_2d_shader_block = shadowmapping_block;\\r\\nLight.registerShadowType( \\\"hard\\\", shadowmapping_block );\\r\\n\\r\\nvar shadowmapping_2D_hard_shader_block = new LS.ShaderBlock(\\\"testShadow2D_hard\\\");\\r\\nshadowmapping_2D_hard_shader_block.addCode( GL.VERTEX_SHADER, Light._shadowmap_vertex_enabled_code, Light._shadowmap_vertex_disabled_code );\\r\\nshadowmapping_2D_hard_shader_block.addCode( GL.FRAGMENT_SHADER, Light._shadowmap_2d_enabled_code, \\\"\\\" );\\r\\nshadowmapping_2D_hard_shader_block.register();\\r\\nLight.shadowmapping_2D_hard_shader_block = shadowmapping_2D_hard_shader_block;\\r\\n//Light.registerShadowType( \\\"hard\\\", shadowmapping_hard_2d_shader_block );\\r\\n\\r\\nvar shadowmapping_2D_soft_block = new LS.ShaderBlock(\\\"testShadow2D_soft\\\");\\r\\nshadowmapping_2D_soft_block.addCode( GL.VERTEX_SHADER, Light._shadowmap_vertex_enabled_code, Light._shadowmap_vertex_disabled_code );\\r\\nshadowmapping_2D_soft_block.addCode( GL.FRAGMENT_SHADER, Light._shadowmap_2d_enabled_code, \\\"\\\" );\\r\\nshadowmapping_2D_soft_block.register();\\r\\nLight.shadowmapping_2D_soft_block = shadowmapping_2D_soft_block;\\r\\n//Light.registerShadowType( \\\"soft\\\", shadowmappingsoft_block );\\r\\n*/\\r\\n\\r\\n// ENVIRONMENT *************************************\\r\\n//this block handles a reflective texture\\r\\n//it is not part of the illumination, shadars must include it manually\\r\\n//most of it is solved inside ShaderMaterial.prototype.renderInstance \\r\\n\\r\\nvar environment_code = \\\"\\\\n\\\\\\r\\n\\t#ifdef ENVIRONMENT_TEXTURE\\\\n\\\\\\r\\n\\t\\tuniform sampler2D environment_texture;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef ENVIRONMENT_PLANAR\\\\n\\\\\\r\\n\\t\\tuniform sampler2D environment_texture;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\t#ifdef ENVIRONMENT_CUBEMAP\\\\n\\\\\\r\\n\\t\\tuniform samplerCube environment_texture;\\\\n\\\\\\r\\n\\t#endif\\\\n\\\\\\r\\n\\tvec2 polarToCartesian(in vec3 V)\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\treturn vec2( 0.5 - (atan(V.z, V.x) / -6.28318531), asin(V.y) / 1.57079633 * 0.5 + 0.5);\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvec3 getEnvironmentColor( vec3 V, float area )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\t#ifdef ENVIRONMENT_TEXTURE\\\\n\\\\\\r\\n\\t\\t\\tvec2 uvs = polarToCartesian(V);\\\\n\\\\\\r\\n\\t\\t\\treturn texture2D( environment_texture, uvs ).xyz;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t#ifdef ENVIRONMENT_CUBEMAP\\\\n\\\\\\r\\n\\t\\t\\treturn textureCube( environment_texture, -V ).xyz;\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\treturn u_background_color.xyz;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\nvar environment_disabled_code = \\\"\\\\n\\\\\\r\\n\\tvec3 getEnvironmentColor( vec3 V, float area )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\treturn u_background_color.xyz;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nvar environment_cubemap_block = new LS.ShaderBlock(\\\"environment_cubemap\\\");\\r\\nenvironment_cubemap_block.addCode( GL.FRAGMENT_SHADER, environment_code, environment_disabled_code, { ENVIRONMENT_CUBEMAP: \\\"\\\" } );\\r\\nenvironment_cubemap_block.defineContextMacros({ENVIRONMENTBLOCK:\\\"environment_cubemap\\\"});\\r\\nenvironment_cubemap_block.register();\\r\\n\\r\\nvar environment_2d_block = new LS.ShaderBlock(\\\"environment_2D\\\");\\r\\nenvironment_2d_block.defineContextMacros({ENVIRONMENTBLOCK:\\\"environment_2D\\\"});\\r\\nenvironment_2d_block.addCode( GL.FRAGMENT_SHADER, environment_code, environment_disabled_code, { ENVIRONMENT_TEXTURE: \\\"\\\" } );\\r\\nenvironment_2d_block.register();\\r\\n\\r\\nvar environment_planar_block = new LS.ShaderBlock(\\\"environment_planar\\\");\\r\\nenvironment_planar_block.defineContextMacros({ENVIRONMENTBLOCK:\\\"environment_planar\\\"});\\r\\nenvironment_planar_block.addCode( GL.FRAGMENT_SHADER, environment_code, environment_disabled_code, { ENVIRONMENT_PLANAR: \\\"\\\" } );\\r\\nenvironment_planar_block.register();\\r\\n\\r\\nvar environment_block = new LS.ShaderBlock(\\\"environment\\\");\\r\\nenvironment_block.addCode( GL.FRAGMENT_SHADER, environment_code, environment_disabled_code );\\r\\nenvironment_block.register();\\r\\n\\r\\n\\r\\nvar reflection_code = \\\"\\\\n\\\\\\r\\n\\t#pragma shaderblock ENVIRONMENTBLOCK \\\\\\\"environment\\\\\\\"\\\\n\\\\\\r\\n\\t\\\\n\\\\\\r\\n\\tvec4 applyReflection( Input IN, SurfaceOutput o, vec4 final_color )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\tvec3 R = reflect( IN.viewDir, o.Normal );\\\\n\\\\\\r\\n\\t\\tvec3 bg = vec3(0.0);\\\\n\\\\\\r\\n\\t\\t//is last pass for this object?\\\\n\\\\\\r\\n\\t\\t#ifdef BLOCK_LASTPASS\\\\n\\\\\\r\\n\\t\\t\\t#ifdef ENVIRONMENT_PLANAR\\\\n\\\\\\r\\n\\t\\t\\t\\tvec2 screen_uv = gl_FragCoord.xy / u_viewport.zw;\\\\n\\\\\\r\\n\\t\\t\\t\\tscreen_uv.x = 1.0 - screen_uv.x;\\\\n\\\\\\r\\n\\t\\t\\t\\tscreen_uv.xy += (u_view * vec4(o.Normal - IN.worldNormal, 0.0)).xy * 0.1;\\\\n\\\\\\r\\n\\t\\t\\t\\tbg = texture2D( environment_texture, screen_uv ).xyz;\\\\n\\\\\\r\\n\\t\\t\\t#else\\\\n\\\\\\r\\n\\t\\t\\t\\tbg = getEnvironmentColor( R, 0.0 );\\\\n\\\\\\r\\n\\t\\t\\t#endif\\\\n\\\\\\r\\n\\t\\t#endif\\\\n\\\\\\r\\n\\t\\tfinal_color.xyz = mix( final_color.xyz, bg, clamp( o.Reflectivity, 0.0, 1.0) );\\\\n\\\\\\r\\n\\t\\treturn final_color;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nvar reflection_disabled_code = \\\"\\\\n\\\\\\r\\n\\tvec4 applyReflection( Input IN, SurfaceOutput o, vec4 final_color )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t\\treturn final_color;\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nvar reflection_block = new LS.ShaderBlock(\\\"applyReflection\\\");\\r\\nShaderMaterial.reflection_block = reflection_block;\\r\\nreflection_block.addCode( GL.FRAGMENT_SHADER, reflection_code, reflection_disabled_code );\\r\\nreflection_block.register();\\r\\n\\r\\n\\r\\n\\r\\n//dummy irradiance code (it is overwritten later) *****************************\\r\\nvar irradiance_disabled_code = \\\"\\\\n\\\\\\r\\n\\tvoid applyIrradiance( in Input IN, in SurfaceOutput o, inout FinalLight FINALLIGHT )\\\\n\\\\\\r\\n\\t{\\\\n\\\\\\r\\n\\t}\\\\n\\\\\\r\\n\\\";\\r\\n\\r\\nif( !LS.Shaders.getShaderBlock(\\\"applyIrradiance\\\") )\\r\\n{\\r\\n\\tvar irradiance_block = new LS.ShaderBlock(\\\"applyIrradiance\\\");\\r\\n\\tShaderMaterial.irradiance_block = irradiance_block;\\r\\n\\tirradiance_block.addCode( GL.FRAGMENT_SHADER, irradiance_disabled_code, irradiance_disabled_code );\\r\\n\\tirradiance_block.register();\\r\\n}\\r\\n///@FILE:../src/player.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* Player class allows to handle the app context easily without having to glue manually all events\\r\\n\\tThere is a list of options\\r\\n\\t==========================\\r\\n\\t- canvas: the canvas where the scene should be rendered, if not specified one will be created\\r\\n\\t- container_id: string with container id where to create the canvas, width and height will be those from the container\\r\\n\\t- width: the width for the canvas in case it is created without a container_id\\r\\n\\t- height: the height for the canvas in case it is created without a container_id\\r\\n\\t- resources: string with the path to the resources folder\\r\\n\\t- shaders: string with the url to the shaders.xml file\\r\\n\\t- proxy: string with the url where the proxy is located (useful to avoid CORS)\\r\\n\\t- filesystems: object that contains the virtual file systems info { \\\"VFS\\\":\\\"http://litefileserver.com/\\\" } ...\\r\\n\\t- redraw: boolean to force to render the scene constantly (useful for animated scenes)\\r\\n\\t- autoresize: boolean to automatically resize the canvas when the window is resized\\r\\n\\t- autoplay: boolean to automatically start playing the scene once the load is completed\\r\\n\\t- loadingbar: boolean to show a loading bar\\r\\n\\t- debug: boolean allows to render debug info like nodes and skeletons\\r\\n\\t- alpha: to set the canvas to transparent\\r\\n\\t- ignore_scroll: to skip mouse wheel events\\r\\n\\r\\n\\tOptional callbacks to attach\\r\\n\\t============================\\r\\n\\t- onPreDraw: executed before drawing a frame (in play mode)\\r\\n\\t- onDraw: executed after drawing a frame (in play mode)\\r\\n\\t- onPreUpdate(dt): executed before updating the scene (delta_time as parameter)\\r\\n\\t- onUpdate(dt): executed after updating the scene (delta_time as parameter)\\r\\n\\t- onDrawLoading: executed when loading\\r\\n\\t- onMouse(e): when a mouse event is triggered\\r\\n\\t- onKey(e): when a key event is triggered\\r\\n* @namespace LS\\r\\n* @class Player\\r\\n* @constructor\\r\\n* @param {Object} options settings for the webgl context creation\\r\\n*/\\r\\nfunction Player(options)\\r\\n{\\r\\n\\toptions = options || {};\\r\\n\\tthis.options = options;\\r\\n\\r\\n\\tif(!options.canvas)\\r\\n\\t{\\r\\n\\t\\tvar container = options.container;\\r\\n\\t\\tif(options.container_id)\\r\\n\\t\\t\\tcontainer = document.getElementById(options.container_id);\\r\\n\\r\\n\\t\\tif(!container)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.log(\\\"No container specified in LS.Player, using BODY as container\\\");\\r\\n\\t\\t\\tcontainer = document.body;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//create canvas\\r\\n\\t\\tvar canvas = document.createElement(\\\"canvas\\\");\\r\\n\\t\\tcanvas.width = container.offsetWidth;\\r\\n\\t\\tcanvas.height = container.offsetHeight;\\r\\n\\t\\tif(!canvas.width) canvas.width = options.width || 1;\\r\\n\\t\\tif(!canvas.height) canvas.height = options.height || 1;\\r\\n\\t\\tcontainer.appendChild(canvas);\\r\\n\\t\\toptions.canvas = canvas;\\r\\n\\t}\\r\\n\\r\\n\\tthis.debug = false;\\r\\n\\tthis.autoplay = false;\\r\\n\\tthis.skip_play_button = false;\\r\\n\\r\\n\\tthis.gl = GL.create(options); //create or reuse\\r\\n\\tthis.canvas = this.gl.canvas;\\r\\n\\tthis.render_settings = new LS.RenderSettings(); //this will be replaced by the scene ones.\\r\\n\\tthis.scene = LS.GlobalScene;\\r\\n\\tthis._file_drop_enabled = false; //use enableFileDrop\\r\\n\\r\\n\\tLS.Shaders.init();\\r\\n\\r\\n\\t//this allows to use your custom renderer\\r\\n\\tthis.renderer = options.renderer || LS.Renderer;\\r\\n\\tthis.renderer.init();\\r\\n\\r\\n\\t//this will repaint every frame and send events when the mouse clicks objects\\r\\n\\tthis.state = LS.Player.STOPPED;\\r\\n\\r\\n\\tif( this.gl.ondraw )\\r\\n\\t\\tthrow(\\\"There is already a litegl attached to this context\\\");\\r\\n\\r\\n\\t//set options\\r\\n\\tthis.configure( options );\\r\\n\\r\\n\\t//bind all the events \\r\\n\\tthis.gl.ondraw = LS.Player.prototype._ondraw.bind(this);\\r\\n\\tthis.gl.onupdate = LS.Player.prototype._onupdate.bind(this);\\r\\n\\r\\n\\tvar mouse_event_callback = LS.Player.prototype._onmouse.bind(this);\\r\\n\\tthis.gl.onmousedown = mouse_event_callback;\\r\\n\\tthis.gl.onmousemove = mouse_event_callback;\\r\\n\\tthis.gl.onmouseup = mouse_event_callback;\\r\\n\\tthis.gl.onmousewheel = mouse_event_callback;\\r\\n\\r\\n\\tvar key_event_callback = LS.Player.prototype._onkey.bind(this);\\r\\n\\tthis.gl.onkeydown = key_event_callback;\\r\\n\\tthis.gl.onkeyup = key_event_callback;\\r\\n\\r\\n\\tvar touch_event_callback = LS.Player.prototype._ontouch.bind(this);\\r\\n\\tthis.gl.ontouch = touch_event_callback;\\r\\n\\r\\n\\tvar gamepad_event_callback = LS.Player.prototype._ongamepad.bind(this);\\r\\n\\tthis.gl.ongamepadconnected = gamepad_event_callback;\\r\\n\\tthis.gl.ongamepaddisconnected = gamepad_event_callback;\\r\\n\\tthis.gl.ongamepadButtonDown = gamepad_event_callback;\\r\\n\\tthis.gl.ongamepadButtonUp = gamepad_event_callback;\\r\\n\\r\\n\\t//capture input\\r\\n\\tgl.captureMouse( !(options.ignore_scroll) );\\r\\n\\tgl.captureKeys(true);\\r\\n\\tgl.captureTouch( !(options.ignore_touch) );\\r\\n\\tgl.captureGamepads(true);\\r\\n\\r\\n\\tif(LS.Input)\\r\\n\\t\\tLS.Input.init();\\r\\n\\r\\n\\tif(options.enableFileDrop !== false)\\r\\n\\t\\tthis.setFileDrop(true);\\r\\n\\r\\n\\t//launch render loop\\r\\n\\tgl.animate();\\r\\n}\\r\\n\\r\\nObject.defineProperty( Player.prototype, \\\"file_drop_enabled\\\", {\\r\\n\\tset: function(v)\\r\\n\\t{\\r\\n\\t\\tthis.setFileDrop(v);\\r\\n\\t},\\r\\n\\tget: function()\\r\\n\\t{\\r\\n\\t\\treturn this._file_drop_enabled;\\r\\n\\t},\\r\\n\\tenumerable: true\\r\\n});\\r\\n\\r\\n/**\\r\\n* Loads a config file for the player, it could also load an scene if the config specifies one\\r\\n* @method loadConfig\\r\\n* @param {String} url url to the JSON file containing the config\\r\\n* @param {Function} on_complete callback trigged when the config is loaded\\r\\n* @param {Function} on_scene_loaded callback trigged when the scene and the resources are loaded (in case the config contains a scene to load)\\r\\n*/\\r\\nPlayer.prototype.loadConfig = function( url, on_complete, on_scene_loaded )\\r\\n{\\r\\n\\tvar that = this;\\r\\n\\tLS.Network.requestJSON( url, inner );\\r\\n\\tfunction inner( data )\\r\\n\\t{\\r\\n\\t\\tthat.configure( data, on_scene_loaded );\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete(data);\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPlayer.prototype.configure = function( options, on_scene_loaded )\\r\\n{\\r\\n\\tvar that = this;\\r\\n\\r\\n\\tthis.skip_play_button = options.skip_play_button !== undefined ? options.skip_play_button : false;\\r\\n\\tthis.autoplay = options.autoplay !== undefined ? options.autoplay : true;\\r\\n\\tif(options.debug)\\r\\n\\t\\tthis.enableDebug();\\r\\n\\telse\\r\\n\\t\\tthis.enableDebug(false);\\r\\n\\r\\n\\tif(options.resources !== undefined)\\r\\n\\t\\tLS.ResourcesManager.setPath( options.resources );\\r\\n\\r\\n\\tif(options.proxy)\\r\\n\\t\\tLS.ResourcesManager.setProxy( options.proxy );\\r\\n\\tif(options.filesystems)\\r\\n\\t{\\r\\n\\t\\tfor(var i in options.filesystems)\\r\\n\\t\\t\\tLS.ResourcesManager.registerFileSystem( i, options.filesystems[i] );\\r\\n\\t}\\r\\n\\r\\n\\tif(options.allow_base_files)\\r\\n\\t\\tLS.ResourcesManager.allow_base_files = options.allow_base_files;\\r\\n\\r\\n\\tif(options.autoresize && !this._resize_callback)\\r\\n\\t{\\r\\n\\t\\tthis._resize_callback = (function(){\\r\\n\\t\\t\\tthis.canvas.width = this.canvas.parentNode.offsetWidth;\\r\\n\\t\\t\\tthis.canvas.height = this.canvas.parentNode.offsetHeight;\\r\\n\\t\\t}).bind(this)\\r\\n\\t\\twindow.addEventListener(\\\"resize\\\",this._resize_callback);\\r\\n\\t}\\r\\n\\r\\n\\tif(options.loadingbar)\\r\\n\\t{\\r\\n\\t\\tif(!this.loading)\\r\\n\\t\\t\\tthis.enableLoadingBar();\\r\\n\\t}\\r\\n\\telse if(options.loadingbar === false)\\r\\n\\t\\tthis.loading = null;\\t\\r\\n\\r\\n\\tthis.force_redraw = options.redraw || false;\\r\\n\\tif(options.debug_render)\\r\\n\\t\\tthis.setDebugRender(true);\\r\\n\\r\\n\\tif(options.scene_url)\\r\\n\\t\\tthis.loadScene( options.scene_url, on_scene_loaded );\\r\\n}\\r\\n\\r\\nPlayer.STOPPED = 0;\\r\\nPlayer.PLAYING = 1;\\r\\nPlayer.PAUSED = 2;\\r\\n\\r\\n/**\\r\\n* Loads an scene and triggers start\\r\\n* @method loadScene\\r\\n* @param {String} url url to the JSON file containing all the scene info\\r\\n* @param {Function} on_complete callback trigged when the scene and the resources are loaded\\r\\n*/\\r\\nPlayer.prototype.loadScene = function(url, on_complete, on_progress)\\r\\n{\\r\\n\\tvar that = this;\\r\\n\\tvar scene = this.scene;\\r\\n\\tif(this.loading)\\r\\n\\t\\tthis.loading.visible = true;\\r\\n\\r\\n\\tscene.load( url, null, null, inner_progress, inner_start );\\r\\n\\r\\n\\tfunction inner_start()\\r\\n\\t{\\r\\n\\t\\t//start playing once loaded the json\\r\\n\\t\\tif(that.autoplay)\\r\\n\\t\\t\\tthat.play();\\r\\n\\t\\telse if(!that.skip_play_button)\\r\\n\\t\\t\\tthat.showPlayDialog();\\r\\n\\t\\t//console.log(\\\"Scene playing\\\");\\r\\n\\t\\tif(\\tthat.loading )\\r\\n\\t\\t\\tthat.loading.visible = false;\\r\\n\\t\\tthat._ondraw( true );\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete();\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_progress(e)\\r\\n\\t{\\r\\n\\t\\tif(that.loading == null)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tvar partial_load = 0;\\r\\n\\t\\tif(e.total) //sometimes we dont have the total so we dont know the amount\\r\\n\\t\\t\\tpartial_load = e.loaded / e.total;\\r\\n\\t\\tthat.loading.scene_loaded = partial_load;\\r\\n\\t\\tif(on_progress)\\r\\n\\t\\t\\ton_progress(partial_load);\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* loads Scene from object or JSON taking into account external and global scripts\\r\\n* @method setScene\\r\\n* @param {Object} scene\\r\\n* @param {Function} on_complete callback trigged when the scene and the resources are loaded\\r\\n*/\\r\\nPlayer.prototype.setScene = function( scene_info, on_complete, on_before_play )\\r\\n{\\r\\n\\tvar that = this;\\r\\n\\tvar scene = this.scene;\\r\\n\\r\\n\\t//reset old scene\\r\\n\\tif(this.state == LS.Player.PLAYING)\\r\\n\\t\\tthis.stop();\\r\\n\\tscene.clear();\\r\\n\\r\\n\\tif(scene_info && scene_info.constructor === String )\\r\\n\\t\\tscene_info = JSON.parse(scene_info);\\r\\n\\r\\n\\tvar scripts = LS.Scene.getScriptsList( scene_info );\\r\\n\\r\\n\\tif( scripts && scripts.length )\\r\\n\\t{\\r\\n\\t\\tscene.clear();\\r\\n\\t\\tscene.loadScripts( scripts, inner_external_ready );\\r\\n\\t}\\r\\n\\telse\\r\\n\\t\\tinner_external_ready();\\r\\n\\r\\n\\tfunction inner_external_ready()\\r\\n\\t{\\r\\n\\t\\tscene.configure( scene_info );\\r\\n\\t\\tscene.loadResources( inner_all_resources_loaded );\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_all_resources_loaded()\\r\\n\\t{\\r\\n\\t\\t//add here any extra step...\\r\\n\\t\\tthat.loading.visible = false;\\r\\n\\r\\n\\t\\t//on ready\\r\\n\\t\\tinner_all_loaded();\\r\\n\\t}\\r\\n\\r\\n\\tfunction inner_all_loaded()\\r\\n\\t{\\r\\n\\t\\tif( on_before_play )\\r\\n\\t\\t\\ton_before_play( scene );\\r\\n\\t\\tif(that.autoplay)\\r\\n\\t\\t\\tthat.play();\\r\\n\\t\\tscene._must_redraw = true;\\r\\n\\t\\tconsole.log(\\\"Scene playing\\\");\\r\\n\\t\\tif(on_complete)\\r\\n\\t\\t\\ton_complete( scene );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/**\\r\\n* Pauses the execution. This will launch a \\\"paused\\\" event and stop calling the update method\\r\\n* @method pause\\r\\n*/\\r\\nPlayer.prototype.pause = function()\\r\\n{\\r\\n\\tthis.state = LS.Player.PAUSED;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Starts the scene. This will launch a \\\"start\\\" event and start calling the update for every frame\\r\\n* @method play\\r\\n*/\\r\\nPlayer.prototype.play = function()\\r\\n{\\r\\n\\tif(this.state == LS.Player.PLAYING)\\r\\n\\t\\treturn;\\r\\n\\tif(this.debug)\\r\\n\\t\\tconsole.log(\\\"Start\\\");\\r\\n\\tthis.state = LS.Player.PLAYING;\\r\\n\\tif(LS.Input)\\r\\n\\t\\tLS.Input.reset(); //this force some events to be sent\\r\\n\\tif(LS.GUI)\\r\\n\\t\\tLS.GUI.reset(); //clear GUI\\r\\n\\tthis.scene.start();\\r\\n}\\r\\n\\r\\n/**\\r\\n* Stops the scene. This will launch a \\\"finish\\\" event and stop calling the update \\r\\n* @method stop\\r\\n*/\\r\\nPlayer.prototype.stop = function()\\r\\n{\\r\\n\\tthis.state = LS.Player.STOPPED;\\r\\n\\tthis.scene.finish();\\r\\n\\tif(LS.GUI)\\r\\n\\t\\tLS.GUI.reset(); //clear GUI\\r\\n}\\r\\n\\r\\n/**\\r\\n* Clears the current scene\\r\\n* @method clear\\r\\n*/\\r\\nPlayer.prototype.clear = function()\\r\\n{\\r\\n\\tif(LS.Input)\\r\\n\\t\\tLS.Input.reset(); //this force some events to be sent\\r\\n\\tif(LS.GUI)\\r\\n\\t\\tLS.GUI.reset(); //clear GUI\\r\\n\\tthis.scene.clear();\\r\\n}\\r\\n\\r\\n/**\\r\\n* Enable the functionality to catch files droped in the canvas so script can catch the \\\"fileDrop\\\" event (onFileDrop in the Script components).\\r\\n* @method setFileDrop\\r\\n* @param {boolean} v true if you want to allow file drop (true by default)\\r\\n*/\\r\\nPlayer.prototype.setFileDrop = function(v)\\r\\n{\\r\\n\\tif(this._file_drop_enabled == v)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tvar that = this;\\r\\n\\tvar element = this.canvas;\\r\\n\\r\\n\\tif(!v)\\r\\n\\t{\\r\\n\\t\\telement.removeEventListener(\\\"dragenter\\\", this._onDrag );\\r\\n\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tthis._file_drop_enabled = v;\\r\\n\\tthis._onDrag = onDrag.bind(this);\\r\\n\\tthis._onDrop = onDrop.bind(this);\\r\\n\\tthis._onDragStop = onDragStop.bind(this);\\r\\n\\r\\n\\telement.addEventListener(\\\"dragenter\\\", this._onDrag );\\r\\n\\r\\n\\tfunction onDragStop(evt)\\r\\n\\t{\\r\\n\\t\\tevt.stopPropagation();\\r\\n\\t\\tevt.preventDefault();\\r\\n\\t}\\r\\n\\r\\n\\tfunction onDrag(evt)\\r\\n\\t{\\r\\n\\t\\telement.addEventListener(\\\"dragexit\\\", this._onDragStop );\\r\\n\\t\\telement.addEventListener(\\\"dragover\\\", this._onDragStop );\\r\\n\\t\\telement.addEventListener(\\\"drop\\\", this._onDrop );\\r\\n\\t\\tevt.stopPropagation();\\r\\n\\t\\tevt.preventDefault();\\r\\n\\t\\t/*\\r\\n\\t\\tif(evt.type == \\\"dragenter\\\" && callback_enter)\\r\\n\\t\\t\\tcallback_enter(evt, this);\\r\\n\\t\\tif(evt.type == \\\"dragexit\\\" && callback_exit)\\r\\n\\t\\t\\tcallback_exit(evt, this);\\r\\n\\t\\t*/\\r\\n\\t}\\r\\n\\r\\n\\tfunction onDrop(evt)\\r\\n\\t{\\r\\n\\t\\tevt.stopPropagation();\\r\\n\\t\\tevt.preventDefault();\\r\\n\\r\\n\\t\\telement.removeEventListener(\\\"dragexit\\\", this._onDragStop );\\r\\n\\t\\telement.removeEventListener(\\\"dragover\\\", this._onDragStop );\\r\\n\\t\\telement.removeEventListener(\\\"drop\\\", this._onDrop );\\r\\n\\r\\n\\t\\tif( evt.dataTransfer.files.length )\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i = 0; i < evt.dataTransfer.files.length; ++i )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar file = evt.dataTransfer.files[i];\\r\\n\\t\\t\\t\\tvar r = this._onfiledrop(file,evt);\\r\\n\\t\\t\\t\\tif(r === false)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tevt.stopPropagation();\\r\\n\\t\\t\\t\\t\\tevt.stopImmediatePropagation();\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPlayer.prototype.enableLoadingBar = function()\\r\\n{\\r\\n\\tthis.loading = {\\r\\n\\t\\tvisible: true,\\r\\n\\t\\tscene_loaded: 0,\\r\\n\\t\\tresources_loaded: 0\\r\\n\\t};\\r\\n\\tLEvent.bind( LS.ResourcesManager, \\\"start_loading_resources\\\", (function(e,v){ \\r\\n\\t\\tif(!this.loading)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis.loading.resources_loaded = 0.0; \\r\\n\\t}).bind(this) );\\r\\n\\tLEvent.bind( LS.ResourcesManager, \\\"loading_resources_progress\\\", (function(e,v){ \\r\\n\\t\\tif(!this.loading)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif( this.loading.resources_loaded < v )\\r\\n\\t\\t\\tthis.loading.resources_loaded = v;\\r\\n\\t}).bind(this) );\\r\\n\\tLEvent.bind( LS.ResourcesManager, \\\"end_loading_resources\\\", (function(e,v){ \\r\\n\\t\\tif(!this.loading)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis._total_loading = undefined; \\r\\n\\t\\tthis.loading.resources_loaded = 1; \\r\\n\\t\\tthis.loading.visible = false;\\r\\n\\t}).bind(this) );\\r\\n}\\r\\n\\r\\nPlayer.prototype._onfiledrop = function( file, evt )\\r\\n{\\r\\n\\treturn LEvent.trigger( LS.GlobalScene, \\\"fileDrop\\\", { file: file, event: evt } );\\r\\n}\\r\\n\\r\\nPlayer.prototype.showPlayDialog = function()\\r\\n{\\r\\n\\tvar element = document.createElement(\\\"div\\\");\\r\\n\\telement.style.width = \\\"128px\\\";\\r\\n\\telement.style.position = \\\"absolute\\\";\\r\\n\\telement.style.top = ((this.canvas.offsetHeight * 0.5 - 64)|0) + \\\"px\\\";\\r\\n\\telement.style.left = ((this.canvas.offsetWidth * 0.5 - 64)|0) + \\\"px\\\";\\r\\n\\telement.style.cursor = \\\"pointer\\\";\\r\\n\\telement.style.borderRadius = \\\"10px\\\";\\r\\n\\telement.style.backgroundColor = \\\"rgba(0,0,0,0.5)\\\";\\r\\n\\telement.innerHTML = '';\\r\\n\\tthis.canvas.parentNode.appendChild(element);\\r\\n\\r\\n\\tvar that = this;\\r\\n\\telement.addEventListener(\\\"click\\\", function(){\\r\\n\\t\\tconsole.log(\\\"play!\\\");\\r\\n\\t\\tthis.parentNode.removeChild( this );\\r\\n\\t\\tthat.play();\\r\\n\\t});\\r\\n}\\r\\n\\r\\n//called by the render loop to draw every frame\\r\\nPlayer.prototype._ondraw = function( force )\\r\\n{\\r\\n\\tvar scene = this.scene;\\r\\n\\r\\n\\tif( this.state == LS.Player.PLAYING || force )\\r\\n\\t{\\r\\n\\t\\tif(this.onPreDraw)\\r\\n\\t\\t\\tthis.onPreDraw();\\r\\n\\r\\n\\t\\tif(scene._must_redraw || this.force_redraw )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis.renderer._in_player = true;\\r\\n\\t\\t\\tthis.renderer.render( scene, scene.info && scene.info.render_settings ? scene.info.render_settings : this.render_settings );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(this.onDraw)\\r\\n\\t\\t\\tthis.onDraw();\\r\\n\\t}\\r\\n\\r\\n\\tif(this.loading && this.loading.visible )\\r\\n\\t{\\r\\n\\t\\tthis.renderLoadingBar( this.loading );\\r\\n\\t\\tLEvent.trigger( this.scene, \\\"render_loading\\\" );\\r\\n\\t\\tif(this.onDrawLoading)\\r\\n\\t\\t\\tthis.onDrawLoading();\\r\\n\\t}\\r\\n}\\r\\n\\r\\nPlayer.prototype._onupdate = function(dt)\\r\\n{\\r\\n\\tif(this.state != LS.Player.PLAYING)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(LS.Tween)\\r\\n\\t\\tLS.Tween.update(dt);\\r\\n\\tif(LS.Input)\\r\\n\\t\\tLS.Input.update(dt);\\r\\n\\r\\n\\tif(this.onPreUpdate)\\r\\n\\t\\tthis.onPreUpdate(dt);\\r\\n\\r\\n\\tthis.scene.update(dt);\\r\\n\\r\\n\\tif(this.onUpdate)\\r\\n\\t\\tthis.onUpdate(dt);\\r\\n\\r\\n}\\r\\n\\r\\n//input\\r\\nPlayer.prototype._onmouse = function(e)\\r\\n{\\r\\n\\t//send to the input system (if blocked ignore it)\\r\\n\\tif( LS.Input && LS.Input.onMouse(e) == true )\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//console.log(e);\\r\\n\\tif(this.state != LS.Player.PLAYING)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tLEvent.trigger( this.scene, e.eventType || e.type, e, true );\\r\\n\\r\\n\\t//hardcoded event handlers in the player\\r\\n\\tif(this.onMouse)\\r\\n\\t\\tthis.onMouse(e);\\r\\n}\\r\\n\\r\\n//input\\r\\nPlayer.prototype._ontouch = function(e)\\r\\n{\\r\\n\\t//console.log(e);\\r\\n\\tif(this.state != LS.Player.PLAYING)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif( LEvent.trigger( this.scene, e.eventType || e.type, e, true ) === true )\\r\\n\\t\\treturn false;\\r\\n\\r\\n\\t//hardcoded event handlers in the player\\r\\n\\tif(this.onTouch)\\r\\n\\t\\tthis.onTouch(e);\\r\\n}\\r\\n\\r\\nPlayer.prototype._onkey = function(e)\\r\\n{\\r\\n\\t//send to the input system\\r\\n\\tif(LS.Input)\\r\\n\\t\\tLS.Input.onKey(e);\\r\\n\\r\\n\\tif(this.state != LS.Player.PLAYING)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//hardcoded event handlers in the player\\r\\n\\tif(this.onKey)\\r\\n\\t{\\r\\n\\t\\tvar r = this.onKey(e);\\r\\n\\t\\tif(r)\\r\\n\\t\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tLEvent.trigger( this.scene, e.eventType || e.type, e );\\r\\n}\\r\\n\\r\\nPlayer.prototype._ongamepad = function(e)\\r\\n{\\r\\n\\tif(this.state != LS.Player.PLAYING)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\t//hardcoded event handlers in the player\\r\\n\\tif(this.onGamepad)\\r\\n\\t{\\r\\n\\t\\tvar r = this.onGamepad(e);\\r\\n\\t\\tif(r)\\r\\n\\t\\t\\treturn;\\r\\n\\t}\\r\\n\\r\\n\\tLEvent.trigger( this.scene, e.eventType || e.type, e );\\r\\n}\\r\\n\\r\\n//renders the loading bar, you can replace it in case you want your own loading bar \\r\\nPlayer.prototype.renderLoadingBar = function( loading )\\r\\n{\\r\\n\\tif(!loading)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!global.enableWebGLCanvas)\\r\\n\\t\\treturn;\\r\\n\\r\\n\\tif(!gl.canvas.canvas2DtoWebGL_enabled)\\r\\n\\t\\tenableWebGLCanvas( gl.canvas );\\r\\n\\r\\n\\tgl.start2D();\\r\\n\\r\\n\\tvar y = 0;//gl.drawingBufferHeight - 6;\\r\\n\\tgl.fillColor = [0,0,0,1];\\r\\n\\tgl.fillRect( 0, y, gl.drawingBufferWidth, 8);\\r\\n\\t//scene\\r\\n\\tgl.fillColor = loading.bar_color || [0.5,0.9,1.0,1.0];\\r\\n\\tgl.fillRect( 0, y, gl.drawingBufferWidth * loading.scene_loaded, 4 );\\r\\n\\t//resources\\r\\n\\tgl.fillColor = loading.bar_color || [0.9,0.5,1.0,1.0];\\r\\n\\tgl.fillRect( 0, y + 4, gl.drawingBufferWidth * loading.resources_loaded, 4 );\\r\\n\\tgl.finish2D();\\r\\n}\\r\\n\\r\\nPlayer.prototype.enableDebug = function(v)\\r\\n{\\r\\n\\tthis.debug = !!v;\\r\\n\\tLS.Script.catch_important_exceptions = !v;\\r\\n\\tLS.catch_exceptions = !v;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Enable a debug renderer that shows gizmos for most of the things on the scene\\r\\n* @method setDebugRender\\r\\n* @param {boolean} v true if you want the debug render\\r\\n*/\\r\\nPlayer.prototype.setDebugRender = function(v)\\r\\n{\\r\\n\\tif(!this.debug_render)\\r\\n\\t{\\r\\n\\t\\tif(!v)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tthis.debug_render = new LS.DebugRender();\\r\\n\\t}\\r\\n\\r\\n\\tif(v)\\r\\n\\t\\tthis.debug_render.enable();\\r\\n\\telse\\r\\n\\t\\tthis.debug_render.disable();\\r\\n}\\r\\n\\r\\n\\r\\nLS.Player = Player;\\r\\n\\r\\n///@FILE:../src/parsers/parserDDS.js\\r\\n///@INFO: PARSER\\r\\nvar parserDDS = { \\r\\n\\textension: \\\"dds\\\",\\r\\n\\ttype: \\\"image\\\",\\r\\n\\tdataType:\\\"arraybuffer\\\",\\r\\n\\tresource: \\\"Texture\\\",\\r\\n\\tformat: \\\"binary\\\",\\r\\n\\r\\n\\tparse: function(data, options)\\r\\n\\t{\\r\\n\\t\\tif(!data || data.constructor !== ArrayBuffer)\\r\\n\\t\\t\\tthrow( \\\"ParserDDS: data must be ArrayBuffer\\\");\\r\\n\\t\\tvar ext = gl.getExtension(\\\"WEBKIT_WEBGL_compressed_texture_s3tc\\\");\\r\\n\\t\\tvar texture = new GL.Texture(0,0, options);\\r\\n\\t\\tif(!window.DDS)\\r\\n\\t\\t\\tthrow(\\\"dds.js script must be included, not found\\\");\\r\\n\\t\\tDDS.loadDDSTextureFromMemoryEx(gl,ext, data, texture, true);\\r\\n\\t\\t//console.log( DDS.getDDSTextureFromMemoryEx(data) );\\r\\n\\t\\t//texture.texture_type = texture.handler.texture_type;\\r\\n\\t\\t//texture.width = texture.handler.width;\\r\\n\\t\\t//texture.height = texture.handler.height;\\r\\n\\t\\t//texture.bind();\\r\\n\\t\\treturn texture;\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.Formats.addSupportedFormat( \\\"dds\\\", parserDDS );\\r\\n///@FILE:../src/parsers/parserOBJ.js\\r\\n///@INFO: PARSER\\r\\n//***** OBJ parser adapted from SpiderGL implementation *****************\\r\\nvar parserOBJ = {\\r\\n\\textension: 'obj',\\r\\n\\ttype: 'mesh',\\r\\n\\tresource: 'Mesh',\\r\\n\\tformat: 'text',\\r\\n\\tdataType:'text',\\r\\n\\r\\n\\tflipAxis: false,\\r\\n\\r\\n\\tparse: function(text, options)\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\t\\tvar support_uint = true;\\r\\n\\r\\n\\t\\t//unindexed containers\\r\\n\\t\\tvar vertices = [];\\r\\n\\t\\tvar normals = [];\\r\\n\\t\\tvar uvs = [];\\r\\n\\r\\n\\t\\t//final containers\\r\\n\\t\\tvar vertices_buffer_data = [];\\r\\n\\t\\tvar normals_buffer_data = [];\\r\\n\\t\\tvar uvs_buffer_data = [];\\r\\n\\r\\n\\t\\t//groups\\r\\n\\t\\tvar group_id = 0;\\r\\n\\t\\tvar groups = [];\\r\\n\\t\\tvar current_group_materials = {};\\r\\n\\t\\tvar last_group_name = null;\\r\\n\\t\\tvar materials_found = {};\\r\\n\\t\\tvar mtllib = null;\\r\\n\\t\\tvar group = createGroup();\\r\\n\\r\\n\\t\\tvar indices_map = new Map();\\r\\n\\t\\tvar next_index = 0;\\r\\n\\r\\n\\t\\tvar V_CODE = 1;\\r\\n\\t\\tvar VT_CODE = 2;\\r\\n\\t\\tvar VN_CODE = 3;\\r\\n\\t\\tvar F_CODE = 4;\\r\\n\\t\\tvar G_CODE = 5;\\r\\n\\t\\tvar O_CODE = 6;\\r\\n\\t\\tvar USEMTL_CODE = 7;\\r\\n\\t\\tvar MTLLIB_CODE = 8;\\r\\n\\t\\tvar codes = { v: V_CODE, vt: VT_CODE, vn: VN_CODE, f: F_CODE, g: G_CODE, o: O_CODE, usemtl: USEMTL_CODE, mtllib: MTLLIB_CODE };\\r\\n\\r\\n\\t\\tvar x,y,z;\\r\\n\\r\\n\\t\\tvar lines = text.split(\\\"\\\\n\\\");\\r\\n\\t\\tvar length = lines.length;\\r\\n\\t\\tfor (var lineIndex = 0; lineIndex < length; ++lineIndex) {\\r\\n\\r\\n\\t\\t\\tvar line = lines[lineIndex];\\r\\n\\t\\t\\tline = line.replace(/[ \\\\t]+/g, \\\" \\\").replace(/\\\\s\\\\s*$/, \\\"\\\"); //better than trim\\r\\n\\r\\n\\t\\t\\tif(line[ line.length - 1 ] == \\\"\\\\\\\\\\\") //breakline support\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tlineIndex += 1;\\r\\n\\t\\t\\t\\tvar next_line = lines[lineIndex].replace(/[ \\\\t]+/g, \\\" \\\").replace(/\\\\s\\\\s*$/, \\\"\\\"); //better than trim\\r\\n\\t\\t\\t\\tline = (line.substr(0,line.length - 1) + next_line).replace(/[ \\\\t]+/g, \\\" \\\").replace(/\\\\s\\\\s*$/, \\\"\\\");\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t\\r\\n\\t\\t\\tif (line[0] == \\\"#\\\")\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif(line == \\\"\\\")\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar tokens = line.split(\\\" \\\");\\r\\n\\t\\t\\tvar code = codes[ tokens[0] ];\\r\\n\\r\\n\\t\\t\\tif( code <= VN_CODE ) //v,vt,vn\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tx = parseFloat(tokens[1]);\\r\\n\\t\\t\\t\\ty = parseFloat(tokens[2]);\\r\\n\\t\\t\\t\\tif( code != VT_CODE ) //not always present\\r\\n\\t\\t\\t\\t\\tz = parseFloat(tokens[3]); \\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t\\r\\n\\t\\t\\tswitch(code)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase V_CODE: vertices.push(x,y,z);\\tbreak;\\r\\n\\t\\t\\t\\tcase VT_CODE: uvs.push(x,y);\\tbreak;\\r\\n\\t\\t\\t\\tcase VN_CODE: normals.push(x,y,z);\\tbreak;\\r\\n\\t\\t\\t\\tcase F_CODE: \\r\\n\\t\\t\\t\\t\\tif (tokens.length < 4)\\r\\n\\t\\t\\t\\t\\t\\tcontinue; //faces with less that 3 vertices? nevermind\\r\\n\\t\\t\\t\\t\\t//get the triangle indices\\r\\n\\t\\t\\t\\t\\tvar polygon_indices = [];\\r\\n\\t\\t\\t\\t\\tfor(var i = 1; i < tokens.length; ++i)\\r\\n\\t\\t\\t\\t\\t\\tpolygon_indices.push( getIndex( tokens[i] ) );\\r\\n\\t\\t\\t\\t\\tgroup.indices.push( polygon_indices[0], polygon_indices[1], polygon_indices[2] );\\r\\n\\t\\t\\t\\t\\t//polygons are break intro triangles\\r\\n\\t\\t\\t\\t\\tfor(var i = 2; i < polygon_indices.length-1; ++i)\\r\\n\\t\\t\\t\\t\\t\\tgroup.indices.push( polygon_indices[0], polygon_indices[i], polygon_indices[i+1] );\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase G_CODE: \\r\\n\\t\\t\\t\\tcase O_CODE: //whats the difference?\\r\\n\\t\\t\\t\\t\\tvar name = tokens[1];\\r\\n\\t\\t\\t\\t\\tlast_group_name = name;\\r\\n\\t\\t\\t\\t\\tif(!group.name)\\r\\n\\t\\t\\t\\t\\t\\tgroup.name = name;\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tcurrent_group_materials = {};\\r\\n\\t\\t\\t\\t\\t\\tgroup = createGroup( name );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase USEMTL_CODE: \\r\\n\\t\\t\\t\\t\\tchangeMaterial( tokens[1] );\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase MTLLIB_CODE:\\r\\n\\t\\t\\t\\t\\tmtllib = tokens[1];\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//generate indices\\r\\n\\t\\tvar indices = [];\\r\\n\\t\\tvar group_index = 0;\\r\\n\\t\\tvar final_groups = [];\\r\\n\\t\\tfor(var i = 0; i < groups.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar group = groups[i];\\r\\n\\t\\t\\tif(!group.indices) //already added?\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tgroup.start = group_index;\\r\\n\\t\\t\\tgroup.length = group.indices.length;\\r\\n\\t\\t\\tindices = indices.concat( group.indices );\\r\\n\\t\\t\\tdelete group.indices; //do not store indices in JSON format!\\r\\n\\t\\t\\tgroup_index += group.length;\\r\\n\\t\\t\\tfinal_groups.push( group );\\r\\n\\t\\t}\\r\\n\\t\\tgroups = final_groups;\\r\\n\\r\\n\\t\\t//finish mesh\\r\\n\\t\\tvar mesh = {};\\r\\n\\r\\n\\t\\tif(!vertices.length)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"mesh without vertices\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//create typed arrays\\r\\n\\t\\tmesh.vertices = new Float32Array( vertices_buffer_data );\\r\\n\\t\\tif ( normals_buffer_data.length )\\r\\n\\t\\t\\tmesh.normals = new Float32Array( normals_buffer_data );\\r\\n\\t\\tif ( uvs_buffer_data.length )\\r\\n\\t\\t\\tmesh.coords = new Float32Array( uvs_buffer_data );\\r\\n\\t\\tif ( indices && indices.length > 0 )\\r\\n\\t\\t\\tmesh.triangles = new ( support_uint && group_index > 256*256 ? Uint32Array : Uint16Array )(indices);\\r\\n\\r\\n\\t\\t//extra info\\r\\n\\t\\tmesh.bounding = GL.Mesh.computeBoundingBox( mesh.vertices );\\r\\n\\t\\tvar info = {};\\r\\n\\t\\tif(groups.length > 1)\\r\\n\\t\\t{\\r\\n\\t\\t\\tinfo.groups = groups;\\r\\n\\t\\t\\t//compute bounding of groups? //TODO: this is complicated, it is affected by indices, etc, better done afterwards\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tmesh.info = info;\\r\\n\\t\\tif( !mesh.bounding )\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.log(\\\"empty mesh\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif( mesh.bounding.radius == 0 || isNaN(mesh.bounding.radius))\\r\\n\\t\\t\\tconsole.log(\\\"no radius found in mesh\\\");\\r\\n\\t\\t//console.log(mesh);\\r\\n\\t\\treturn mesh;\\r\\n\\r\\n\\t\\t//this function helps reuse triplets that have been created before\\r\\n\\t\\tfunction getIndex( str )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar pos,tex,nor,f;\\r\\n\\t\\t\\tvar has_negative = false;\\r\\n\\r\\n\\t\\t\\t//cannot use negative indices as keys, convert them to positive\\r\\n\\t\\t\\tif(str.indexOf(\\\"-\\\") == -1)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar index = indices_map.get(str);\\r\\n\\t\\t\\t\\tif(index !== undefined)\\r\\n\\t\\t\\t\\t\\treturn index;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\thas_negative = true;\\r\\n\\r\\n\\t\\t\\tif(!f) //maybe it was parsed before\\r\\n\\t\\t\\t\\tf = str.split(\\\"/\\\");\\r\\n\\r\\n\\t\\t\\tif (f.length == 1) { //unpacked\\r\\n\\t\\t\\t\\tpos = parseInt(f[0]);\\r\\n\\t\\t\\t\\ttex = pos;\\r\\n\\t\\t\\t\\tnor = pos;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if (f.length == 2) { //no normals\\r\\n\\t\\t\\t\\tpos = parseInt(f[0]);\\r\\n\\t\\t\\t\\ttex = parseInt(f[1]);\\r\\n\\t\\t\\t\\tnor = pos;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if (f.length == 3) { //all three indexed\\r\\n\\t\\t\\t\\tpos = parseInt(f[0]);\\r\\n\\t\\t\\t\\ttex = parseInt(f[1]);\\r\\n\\t\\t\\t\\tnor = parseInt(f[2]);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse {\\r\\n\\t\\t\\t\\tconsole.log(\\\"Problem parsing: unknown number of values per face\\\");\\r\\n\\t\\t\\t\\treturn -1;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//negative indices are relative to the end\\r\\n\\t\\t\\tif(pos < 0) \\r\\n\\t\\t\\t\\tpos = vertices.length / 3 + pos + 1;\\r\\n\\t\\t\\tif(nor < 0)\\r\\n\\t\\t\\t\\tnor = normals.length / 2 + nor + 1;\\r\\n\\t\\t\\tif(tex < 0)\\r\\n\\t\\t\\t\\ttex = uvs.length / 2 + tex + 1;\\r\\n\\r\\n\\t\\t\\t//try again to see if we already have this\\r\\n\\t\\t\\tif(has_negative)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tstr = pos + \\\"/\\\" + tex + \\\"/\\\" + nor;\\r\\n\\t\\t\\t\\tvar index = indices_map.get(str);\\r\\n\\t\\t\\t\\tif(index !== undefined)\\r\\n\\t\\t\\t\\t\\treturn index;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//fill buffers\\r\\n\\t\\t\\tpos -= 1; tex -= 1; nor -= 1; //indices in obj start in 1, buffers in 0\\r\\n\\t\\t\\tvertices_buffer_data.push( vertices[pos*3+0], vertices[pos*3+1], vertices[pos*3+2] );\\r\\n\\t\\t\\tif(uvs.length)\\r\\n\\t\\t\\t\\tuvs_buffer_data.push( uvs[tex*2+0], uvs[tex*2+1] );\\r\\n\\t\\t\\tif(normals.length)\\r\\n\\t\\t\\t\\tnormals_buffer_data.push( normals[nor*3+0], normals[nor*3+1], normals[nor*3+2] );\\r\\n\\r\\n\\t\\t\\t//store index\\r\\n\\t\\t\\tvar index = next_index;\\r\\n\\t\\t\\tindices_map.set( str, index );\\r\\n\\t\\t\\t++next_index;\\r\\n\\t\\t\\treturn index;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfunction createGroup( name )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar g = {\\r\\n\\t\\t\\t\\tname: name || \\\"\\\",\\r\\n\\t\\t\\t\\tmaterial: \\\"\\\",\\r\\n\\t\\t\\t\\tstart: -1,\\r\\n\\t\\t\\t\\tlength: -1,\\r\\n\\t\\t\\t\\tindices: []\\r\\n\\t\\t\\t};\\r\\n\\t\\t\\tgroups.push(g);\\r\\n\\t\\t\\treturn g;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfunction changeMaterial( material_name )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif( !group.material )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tgroup.material = material_name + \\\".json\\\";\\r\\n\\t\\t\\t\\tcurrent_group_materials[ material_name ] = group;\\r\\n\\t\\t\\t\\treturn group;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar g = current_group_materials[ material_name ];\\r\\n\\t\\t\\tif(!g)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tg = createGroup( last_group_name + \\\"_\\\" + material_name );\\r\\n\\t\\t\\t\\tg.material = material_name + \\\".json\\\";\\r\\n\\t\\t\\t\\tcurrent_group_materials[ material_name ] = g;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tgroup = g;\\r\\n\\t\\t\\treturn g;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.Formats.addSupportedFormat( \\\"obj\\\", parserOBJ );\\r\\n\\r\\n\\r\\n//***** MTL parser *****************\\r\\n//info from: http://paulbourke.net/dataformats/mtl/\\r\\nvar parserMTL = {\\r\\n\\textension: 'mtl',\\r\\n\\ttype: 'material',\\r\\n\\tresource: 'StandardMaterial',\\r\\n\\tformat: 'text',\\r\\n\\tdataType:'text',\\r\\n\\r\\n\\tparse: function( text, options )\\r\\n\\t{\\r\\n\\t\\tvar lines = text.split(\\\"\\\\n\\\");\\r\\n\\t\\tvar length = lines.length;\\r\\n\\r\\n\\t\\tvar materials = {};\\r\\n\\t\\tvar current_material = null;\\r\\n\\r\\n\\t\\tfor (var lineIndex = 0; lineIndex < length; ++lineIndex)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar line = lines[lineIndex].replace(/[ \\\\t]+/g, \\\" \\\").replace(/\\\\s\\\\s*$/, \\\"\\\"); //trim\\r\\n\\t\\t\\tline = line.trim();\\r\\n\\r\\n\\t\\t\\tif (line[0] == \\\"#\\\" || line == \\\"\\\")\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar tokens = line.split(\\\" \\\");\\r\\n\\t\\t\\tvar c = tokens[0];\\r\\n\\r\\n\\t\\t\\tswitch(c)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase \\\"newmtl\\\":\\r\\n\\t\\t\\t\\t\\tvar filename = tokens[1] + \\\".json\\\";\\r\\n\\t\\t\\t\\t\\tcurrent_material = { filename: filename, textures: {} };\\r\\n\\t\\t\\t\\t\\tmaterials[ filename ] = current_material;\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"Ka\\\":\\r\\n\\t\\t\\t\\t\\tcurrent_material.ambient = readVector3(tokens);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"Kd\\\":\\r\\n\\t\\t\\t\\t\\tcurrent_material.color = readVector3(tokens);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"Ks\\\":\\r\\n\\t\\t\\t\\t\\tcurrent_material.specular_factor = parseFloat(tokens[1]); //readVector3(tokens);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"Ke\\\":\\r\\n\\t\\t\\t\\t\\tcurrent_material.emissive = readVector3(tokens); //readVector3(tokens);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"Ns\\\": //glossiness\\r\\n\\t\\t\\t\\t\\tcurrent_material.specular_gloss = parseFloat(tokens[1]);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"Tr\\\": //reflection coefficient\\r\\n\\t\\t\\t\\t\\tcurrent_material.reflection = parseFloat( tokens[1] );\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"map_Kd\\\":\\r\\n\\t\\t\\t\\t\\tcurrent_material.textures[\\\"color\\\"] = this.clearPath( tokens[1] );\\r\\n\\t\\t\\t\\t\\tcurrent_material.color = [1,1,1];\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"map_Ka\\\":\\r\\n\\t\\t\\t\\t\\tcurrent_material.textures[\\\"ambient\\\"] = this.clearPath( tokens[1] );\\r\\n\\t\\t\\t\\t\\tcurrent_material.ambient = [1,1,1];\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"map_Ks\\\":\\r\\n\\t\\t\\t\\t\\tcurrent_material.textures[\\\"specular\\\"] = this.clearPath( tokens[1] );\\r\\n\\t\\t\\t\\t\\tcurrent_material.specular_factor = 1;\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"map_d\\\":\\r\\n\\t\\t\\t\\t\\tcurrent_material.textures[\\\"opacity\\\"] = this.clearPath( tokens[1] );\\r\\n\\t\\t\\t\\t\\tcurrent_material.blend_mode = LS.Blend.ALPHA;\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"bump\\\":\\r\\n\\t\\t\\t\\tcase \\\"map_bump\\\":\\r\\n\\t\\t\\t\\t\\tcurrent_material.textures[\\\"bump\\\"] = this.clearPath( tokens[1] );\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"d\\\": //disolve is like transparency\\r\\n\\t\\t\\t\\t\\tcurrent_material.opacity = parseFloat( tokens[1] );\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"Tr\\\": //reflection coefficient\\r\\n\\t\\t\\t\\t\\tcurrent_material.opacity = parseFloat( tokens[1] );\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t//Not supported stuff\\r\\n\\t\\t\\t\\tcase \\\"illum\\\": //illumination model (raytrace related)\\r\\n\\t\\t\\t\\tcase \\\"Tf\\\": //reflection by components\\r\\n\\t\\t\\t\\tcase \\\"Ni\\\": //refraction coefficient\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\tconsole.log(\\\"Unknown MTL info: \\\", c);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var i in materials)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar material_info = materials[i];\\r\\n\\r\\n\\t\\t\\t//hack, ambient must be 1,1,1\\r\\n\\t\\t\\tmaterial_info.ambient = [1,1,1];\\r\\n\\r\\n\\t\\t\\tvar material = new LS.StandardMaterial(material_info);\\r\\n\\t\\t\\tLS.RM.registerResource( material_info.filename, material );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn null;\\r\\n\\r\\n\\t\\tfunction readVector3(v)\\r\\n\\t\\t{\\r\\n\\t\\t\\treturn [ parseFloat(v[1]), parseFloat(v[2]), parseFloat(v[3]) ];\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\tclearPath: function(path)\\r\\n\\t{\\r\\n\\t\\tvar pos = path.lastIndexOf(\\\"\\\\\\\\\\\");\\r\\n\\t\\tif(pos != -1)\\r\\n\\t\\t\\tpath = path.substr(pos+1);\\r\\n\\t\\tvar filename = LS.RM.getFilename(path);\\r\\n\\t\\tif( LS.RM.resources_renamed_recently[filename] )\\r\\n\\t\\t\\tfilename = LS.RM.resources_renamed_recently[filename];\\r\\n\\t\\treturn filename.toLowerCase();\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.Formats.addSupportedFormat( \\\"mtl\\\", parserMTL );\\r\\n///@FILE:../src/parsers/parserSTL.js\\r\\n///@INFO: PARSER\\r\\n//***** STL Parser *****************\\r\\n//based on https://github.com/tonylukasavage/jsstl\\r\\nvar parserSTL = {\\r\\n\\textension: 'stl',\\r\\n\\ttype: 'mesh',\\r\\n\\tformat: 'binary',\\r\\n\\tdataType:'arraybuffer',\\r\\n\\t\\r\\n\\tparse: function( data, options )\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\r\\n\\t\\tvar positionsArray = [];\\r\\n\\t\\tvar normalsArray = [];\\r\\n\\t\\tvar indicesArray = [];\\r\\n\\r\\n\\t\\tvar dv = new DataView(data, 80); // 80 == unused header\\r\\n\\t\\tvar isLittleEndian = true;\\r\\n\\t\\tvar triangles = dv.getUint32(0, isLittleEndian); \\r\\n\\t\\t// console.log('arraybuffer length: ' + stl.byteLength);\\r\\n\\t\\tconsole.log('number of triangles: ' + triangles);\\r\\n\\t\\tvar offset = 4;\\r\\n\\r\\n\\t\\tvar tempA = vec3.create();\\r\\n\\t\\tvar tempB = vec3.create();\\r\\n\\t\\tvar tempC = vec3.create();\\r\\n\\t\\tvar tempN = vec3.create();\\r\\n\\r\\n\\t\\tfor (var i = 0; i < triangles; i++) {\\r\\n\\t\\t\\t// Get the normal for this triangle\\r\\n\\t\\t\\tvar nx = dv.getFloat32(offset, isLittleEndian);\\r\\n\\t\\t\\tvar ny = dv.getFloat32(offset+4, isLittleEndian);\\r\\n\\t\\t\\tvar nz = dv.getFloat32(offset+8, isLittleEndian);\\r\\n\\r\\n\\t\\t\\toffset += 12;\\r\\n\\t\\t\\t// Get all 3 vertices for this triangle\\r\\n\\t\\t\\tfor (var j = 0; j < 3; j++) {\\r\\n\\t\\t\\t\\tvar x = dv.getFloat32(offset, isLittleEndian);\\r\\n\\t\\t\\t\\tvar y = dv.getFloat32(offset+4, isLittleEndian);\\r\\n\\t\\t\\t\\tvar z = dv.getFloat32(offset+8, isLittleEndian);\\r\\n\\t\\t\\t\\t//positionsArray.push(x,y,z);\\r\\n\\t\\t\\t\\tpositionsArray.push(x,z,-y); //flipped\\r\\n\\t\\t\\t\\toffset += 12\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t\\r\\n\\t\\t\\tif(nx == 0 && ny == 0 && nz == 0) //compute normal\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar l = positionsArray.length;\\r\\n\\t\\t\\t\\ttempA.set( positionsArray.slice(l-9,l-6) );\\r\\n\\t\\t\\t\\ttempB.set( positionsArray.slice(l-6,l-3) );\\r\\n\\t\\t\\t\\ttempC.set( positionsArray.slice(l-3,l) );\\r\\n\\t\\t\\t\\tvec3.sub( tempB, tempB, tempA );\\r\\n\\t\\t\\t\\tvec3.sub( tempC, tempC, tempA );\\r\\n\\t\\t\\t\\tvec3.cross( tempN, tempC, tempB );\\r\\n\\t\\t\\t\\tnx = tempN[0]; ny = tempN[1]; nz = tempN[2];\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//normalsArray.push(nx,ny,nz,nx,ny,nz,nx,ny,nz);\\r\\n\\t\\t\\tnormalsArray.push(nx,nz,-ny,nx,nz,-ny,nx,nz,-ny); //flipped\\r\\n\\r\\n\\t\\t\\t// there's also a Uint16 \\\"attribute byte count\\\" that we\\r\\n\\t\\t\\t// don't need, it should always be zero.\\r\\n\\t\\t\\toffset += 2; \\r\\n\\t\\t\\t// Create a new face for from the vertices and the normal \\r\\n\\t\\t\\t//indicesArray.push( i*3, i*3+1, i*3+2 );\\r\\n\\t\\t}\\r\\n\\t\\t// The binary STL I'm testing with seems to have all\\r\\n\\t\\t// zeroes for the normals, unlike its ASCII counterpart.\\r\\n\\t\\t// We can use three.js to compute the normals for us, though,\\r\\n\\t\\t// once we've assembled our geometry. This is a relatively \\r\\n\\t\\t// expensive operation, but only needs to be done once.\\r\\n\\r\\n\\t\\tvar mesh = { info: {} };\\r\\n\\r\\n\\t\\tmesh.vertices = new Float32Array(positionsArray);\\r\\n\\t\\tif (normalsArray.length > 0)\\r\\n\\t\\t\\tmesh.normals = new Float32Array(normalsArray);\\r\\n\\t\\tif (indicesArray && indicesArray.length > 0)\\r\\n\\t\\t\\tmesh.triangles = new Uint16Array(indicesArray);\\r\\n\\r\\n\\t\\t//extra info\\r\\n\\t\\tmesh.bounding = LS.Formats.computeMeshBounding( mesh.vertices );\\r\\n\\t\\treturn mesh;\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.Formats.addSupportedFormat( \\\"stl\\\", parserSTL );\\r\\n\\r\\n///@FILE:../src/parsers/parserTGA.js\\r\\n///@INFO: PARSER\\r\\nvar parserTGA = { \\r\\n\\textension: 'tga',\\r\\n\\ttype: 'image',\\r\\n\\tdataType:\\\"arraybuffer\\\",\\r\\n\\tformat: 'binary',\\r\\n\\r\\n\\tparse: function(data, options)\\r\\n\\t{\\r\\n\\t\\tif(!data || data.constructor !== ArrayBuffer)\\r\\n\\t\\t\\tthrow( \\\"ParserTGA: data must be ArrayBuffer\\\");\\r\\n\\r\\n\\t\\tdata = new Uint8Array(data);\\r\\n\\t\\tvar TGAheader = new Uint8Array( [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0] );\\r\\n\\t\\tvar TGAcompare = data.subarray(0,12);\\r\\n\\t\\tfor(var i = 0; i < TGAcompare.length; i++)\\r\\n\\t\\t\\tif(TGAheader[i] != TGAcompare[i])\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.error(\\\"TGA header is not valid\\\");\\r\\n\\t\\t\\t\\treturn null; //not a TGA\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\tvar header = data.subarray(12,18);\\r\\n\\r\\n\\t\\tvar img = {};\\r\\n\\t\\timg.width = header[1] * 256 + header[0];\\r\\n\\t\\timg.height = header[3] * 256 + header[2];\\r\\n\\t\\timg.bpp = header[4];\\r\\n\\t\\timg.bytesPerPixel = img.bpp / 8;\\r\\n\\t\\timg.imageSize = img.width * img.height * img.bytesPerPixel;\\r\\n\\t\\timg.pixels = data.subarray(18,18+img.imageSize);\\r\\n\\t\\timg.pixels = new Uint8Array( img.pixels ); \\t//clone\\r\\n\\t\\tvar pixels = img.pixels;\\r\\n\\r\\n\\t\\t//TGA comes in BGR format so we swap it, this is slooooow\\r\\n\\t\\tfor(var i = 0, l = img.imageSize, d = img.bytesPerPixel; i < l; i+= d )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar temp = pixels[i];\\r\\n\\t\\t\\tpixels[i] = pixels[i+2];\\r\\n\\t\\t\\tpixels[i+2] = temp;\\r\\n\\t\\t}\\r\\n\\t\\timg.format = img.bpp == 32 ? \\\"RGBA\\\" : \\\"RGB\\\";\\r\\n\\r\\n\\t\\t//if(\\t(header[5] & (1<<4)) == 0) //hack, needs swap\\r\\n\\t\\t//{\\r\\n\\t\\t\\t//header[5] |= 1<<5; //mark as Y swaped\\r\\n\\t\\t//}\\r\\n\\t\\t//else\\r\\n\\t\\t//\\timg.format = img.bpp == 32 ? \\\"RGBA\\\" : \\\"RGB\\\";\\r\\n\\r\\n\\t\\t//some extra bytes to avoid alignment problems\\r\\n\\t\\t//img.pixels = new Uint8Array( img.imageSize + 14);\\r\\n\\t\\t//img.pixels.set( data.subarray(18,18+img.imageSize), 0);\\r\\n\\r\\n\\t\\timg.flipY = true;\\r\\n\\t\\t//img.format = img.bpp == 32 ? \\\"BGRA\\\" : \\\"BGR\\\";\\r\\n\\t\\t//trace(\\\"TGA info: \\\" + img.width + \\\"x\\\" + img.height );\\r\\n\\t\\treturn img;\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.Formats.addSupportedFormat( \\\"tga\\\", parserTGA );\\r\\n///@FILE:../src/parsers/collada.js\\r\\n// Collada.js https://github.com/jagenjo/collada.js\\r\\n// Javi Agenjo 2015 \\r\\n// Specification from https://www.khronos.org/collada/wiki\\r\\nvar _collada;\\r\\n\\r\\n(function(global){\\r\\n\\r\\nvar isWorker = typeof(window) == \\\"undefined\\\" && typeof(exports) == \\\"undefined\\\";\\r\\nvar DEG2RAD = Math.PI * 2 / 360;\\r\\n\\r\\n//global temporal variables\\r\\nvar temp_mat4 = null;\\r\\nvar temp_vec2 = null;\\r\\nvar temp_vec3 = null;\\r\\nvar temp_vec4 = null;\\r\\nvar temp_quat = null;\\r\\n\\r\\nif( isWorker )\\r\\n{\\r\\n\\tglobal.console = {\\r\\n\\t\\tlog: function(msg) { \\r\\n\\t\\t\\tvar args = Array.prototype.slice.call(arguments, 0);\\r\\n\\t\\t\\tself.postMessage({action:\\\"log\\\", params: args});\\r\\n\\t\\t},\\r\\n\\t\\twarn: function(msg) { \\r\\n\\t\\t\\tvar args = Array.prototype.slice.call(arguments, 0);\\r\\n\\t\\t\\tself.postMessage({action:\\\"warn\\\", params: args});\\r\\n\\t\\t},\\r\\n\\t\\terror: function(msg) { \\r\\n\\t\\t\\tvar args = Array.prototype.slice.call(arguments, 0);\\r\\n\\t\\t\\tself.postMessage({action:\\\"error\\\", params: args});\\r\\n\\t\\t}\\r\\n\\t};\\r\\n\\r\\n\\tglobal.alert = console.error;\\r\\n}\\r\\n\\r\\n//Collada parser\\r\\nglobal.Collada = _collada = {\\r\\n\\r\\n\\tlibsPath: \\\"./\\\",\\r\\n\\tworkerPath: \\\"./\\\",\\r\\n\\tno_flip: true,\\r\\n\\tuse_transferables: true, //for workers\\r\\n\\tonerror: null,\\r\\n\\tverbose: false,\\r\\n\\tconfig: { forceParser:false },\\r\\n\\r\\n\\tinit: function (config)\\r\\n\\t{\\r\\n\\t\\tconfig = config || {}\\r\\n\\t\\tfor(var i in config)\\r\\n\\t\\t\\tthis[i] = config[i];\\r\\n\\t\\tthis.config = config;\\r\\n\\r\\n\\t\\tif( isWorker )\\r\\n\\t\\t{\\r\\n\\t\\t\\ttry\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\timportScripts( this.libsPath + \\\"gl-matrix-min.js\\\", this.libsPath + \\\"tinyxml.js\\\" );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tCollada.throwException( Collada.LIBMISSING_ERROR );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//init glMatrix\\r\\n\\t\\ttemp_mat4 = mat4.create();\\r\\n\\t\\ttemp_vec2 = vec3.create();\\r\\n\\t\\ttemp_vec3 = vec3.create();\\r\\n\\t\\ttemp_vec4 = vec3.create();\\r\\n\\t\\ttemp_quat = quat.create();\\r\\n\\r\\n\\t\\tif( isWorker )\\r\\n\\t\\t\\tconsole.log(\\\"Collada worker ready\\\");\\r\\n\\t},\\r\\n\\r\\n\\tload: function(url, callback)\\r\\n\\t{\\r\\n\\t\\trequest(url, function(data)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!data)\\r\\n\\t\\t\\t\\tcallback( null );\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tcallback( Collada.parse( data ) );\\r\\n\\t\\t});\\r\\n\\t},\\r\\n\\r\\n\\t_xmlroot: null,\\r\\n\\t_nodes_by_id: null,\\r\\n\\t_nodes_by_sid: null,\\r\\n\\t_transferables: null,\\r\\n\\t_controllers_found: null,\\r\\n\\t_geometries_found: null,\\r\\n\\r\\n\\tsafeString: function (str) { \\r\\n\\t\\tif(!str)\\r\\n\\t\\t\\treturn \\\"\\\";\\r\\n\\r\\n\\t\\tif(this.convertID)\\r\\n\\t\\t\\treturn this.convertID(str);\\r\\n\\r\\n\\t\\treturn str.replace(/ /g,\\\"_\\\"); \\r\\n\\t},\\r\\n\\r\\n\\tLIBMISSING_ERROR: \\\"Libraries loading error, when using workers remember to pass the URL to the tinyxml.js in the options.libsPath\\\",\\r\\n\\tNOXMLPARSER_ERROR: \\\"TinyXML not found, when using workers remember to pass the URL to the tinyxml.js in the options.libsPath (Workers do not allow to access the native XML DOMParser)\\\",\\r\\n\\tthrowException: function(msg)\\r\\n\\t{\\r\\n\\t\\tif(isWorker)\\r\\n\\t\\t\\tself.postMessage({action:\\\"exception\\\", msg: msg});\\r\\n\\t\\telse\\r\\n\\t\\t\\tif(Collada.onerror)\\r\\n\\t\\t\\t\\tCollada.onerror(msg);\\r\\n\\t\\tthrow(msg);\\r\\n\\t},\\r\\n\\r\\n\\tgetFilename: function(filename)\\r\\n\\t{\\r\\n\\t\\tvar pos = filename.lastIndexOf(\\\"\\\\\\\\\\\");\\r\\n\\t\\tif(pos != -1)\\r\\n\\t\\t\\tfilename = filename.substr(pos+1);\\r\\n\\t\\t//strip unix slashes\\r\\n\\t\\tpos = filename.lastIndexOf(\\\"/\\\");\\r\\n\\t\\tif(pos != -1)\\r\\n\\t\\t\\tfilename = filename.substr(pos+1);\\r\\n\\t\\treturn filename;\\r\\n\\t},\\r\\n\\r\\n\\tlast_name: 0,\\r\\n\\r\\n\\tgenerateName: function(v)\\r\\n\\t{\\r\\n\\t\\tv = v || \\\"name_\\\";\\r\\n\\t\\tvar name = v + this.last_name;\\r\\n\\t\\tthis.last_name++;\\r\\n\\t\\treturn name;\\r\\n\\t},\\r\\n\\r\\n\\tparse: function(data, options, filename)\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\t\\tfilename = filename || \\\"_dae_\\\" + Date.now() + \\\".dae\\\";\\r\\n\\r\\n\\t\\t//console.log(\\\"Parsing collada\\\");\\r\\n\\t\\tvar flip = false;\\r\\n\\r\\n\\t\\tvar xmlparser = null;\\r\\n\\t\\tvar root = null;\\r\\n\\t\\tthis._transferables = [];\\r\\n\\t\\t\\r\\n\\t\\tif(this.verbose)\\r\\n\\t\\t\\tconsole.log(\\\" - XML parsing...\\\");\\r\\n\\r\\n\\t\\tif(global[\\\"DOMParser\\\"] && !this.config.forceParser )\\r\\n\\t\\t{\\r\\n\\t\\t\\txmlparser = new DOMParser();\\r\\n\\t\\t\\troot = xmlparser.parseFromString(data,\\\"text/xml\\\");\\r\\n\\t\\t\\tif(this.verbose)\\r\\n\\t\\t\\t\\tconsole.log(\\\" - XML parsed\\\");\\t\\t\\t\\r\\n\\t\\t}\\r\\n\\t\\telse //USING JS XML PARSER IMPLEMENTATION (much slower)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!global[\\\"DOMImplementation\\\"] )\\r\\n\\t\\t\\t\\treturn Collada.throwException( Collada.NOXMLPARSER_ERROR );\\r\\n\\t\\t\\t//use tinyxmlparser\\r\\n\\t\\t\\ttry\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\txmlparser = new DOMImplementation();\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\treturn Collada.throwException( Collada.NOXMLPARSER_ERROR );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\troot = xmlparser.loadXML(data);\\r\\n\\t\\t\\tif(this.verbose)\\r\\n\\t\\t\\t\\tconsole.log(\\\" - XML parsed\\\");\\r\\n\\r\\n\\t\\t\\t//for every node...\\r\\n\\t\\t\\tvar by_ids = root._nodes_by_id = {};\\r\\n\\t\\t\\tfor(var i = 0, l = root.all.length; i < l; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar node = root.all[i];\\r\\n\\t\\t\\t\\tby_ids[ node.id ] = node;\\r\\n\\t\\t\\t\\tif(node.getAttribute(\\\"sid\\\"))\\r\\n\\t\\t\\t\\t\\tby_ids[ node.getAttribute(\\\"sid\\\") ] = node;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(!this.extra_functions)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.extra_functions = true;\\r\\n\\t\\t\\t\\t//these methods are missing so here is a lousy implementation\\r\\n\\t\\t\\t\\tDOMDocument.prototype.querySelector = DOMElement.prototype.querySelector = function(selector)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar tags = selector.split(\\\" \\\");\\r\\n\\t\\t\\t\\t\\tvar current_element = this;\\r\\n\\r\\n\\t\\t\\t\\t\\twhile(tags.length)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar current = tags.shift();\\r\\n\\t\\t\\t\\t\\t\\tvar tokens = current.split(\\\"#\\\");\\r\\n\\t\\t\\t\\t\\t\\tvar tagname = tokens[0];\\r\\n\\t\\t\\t\\t\\t\\tvar id = tokens[1];\\r\\n\\t\\t\\t\\t\\t\\tvar elements = tagname ? current_element.getElementsByTagName(tagname) : current_element.childNodes;\\r\\n\\t\\t\\t\\t\\t\\tif(!id) //no id filter\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tif(tags.length == 0)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\treturn elements.item(0);\\r\\n\\t\\t\\t\\t\\t\\t\\tcurrent_element = elements.item(0);\\r\\n\\t\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t//has id? check for all to see if one matches the id\\r\\n\\t\\t\\t\\t\\t\\tfor(var i = 0; i < elements.length; i++)\\r\\n\\t\\t\\t\\t\\t\\t\\tif( elements.item(i).getAttribute(\\\"id\\\") == id)\\r\\n\\t\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif(tags.length == 0)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\treturn elements.item(i);\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tcurrent_element = elements.item(i);\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\treturn null;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tDOMDocument.prototype.querySelectorAll = DOMElement.prototype.querySelectorAll = function( selector )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar tags = selector.split(\\\" \\\");\\r\\n\\t\\t\\t\\t\\tif(tags.length == 1)\\r\\n\\t\\t\\t\\t\\t\\treturn this.getElementsByTagName( selector );\\r\\n\\r\\n\\t\\t\\t\\t\\tvar current_element = this;\\r\\n\\t\\t\\t\\t\\tvar result = [];\\r\\n\\r\\n\\t\\t\\t\\t\\tinner(this, tags);\\r\\n\\r\\n\\t\\t\\t\\t\\tfunction inner(root, tags )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tif(!tags)\\r\\n\\t\\t\\t\\t\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t\\t\\t\\t\\tvar current = tags.shift();\\r\\n\\t\\t\\t\\t\\t\\tvar elements = root.getElementsByTagName( current );\\r\\n\\t\\t\\t\\t\\t\\tif(tags.length == 0)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tfor(var i = 0; i < elements.length; i++)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tresult.push( elements.item(i) );\\r\\n\\t\\t\\t\\t\\t\\t\\treturn;\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\tfor(var i = 0; i < elements.length; i++)\\r\\n\\t\\t\\t\\t\\t\\t\\tinner( elements.item(i), tags.concat() );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tvar list = new DOMNodeList(this.documentElement);\\r\\n\\t\\t\\t\\t\\tlist._nodes = result;\\r\\n\\t\\t\\t\\t\\tlist.length = result.length;\\r\\n\\r\\n\\t\\t\\t\\t\\treturn list;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tObject.defineProperty( DOMElement.prototype, \\\"textContent\\\", { \\r\\n\\t\\t\\t\\t\\tget: function() { \\r\\n\\t\\t\\t\\t\\t\\tvar nodes = this.getChildNodes();\\r\\n\\t\\t\\t\\t\\t\\treturn nodes.item(0).toString(); \\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\tset: function() {} \\r\\n\\t\\t\\t\\t});\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tthis._xmlroot = root;\\r\\n\\t\\tvar xmlcollada = root.querySelector(\\\"COLLADA\\\");\\r\\n\\t\\tif(xmlcollada)\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._current_DAE_version = xmlcollada.getAttribute(\\\"version\\\");\\r\\n\\t\\t\\tconsole.log(\\\"DAE Version:\\\" + this._current_DAE_version);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//var xmlvisual_scene = root.querySelector(\\\"visual_scene\\\");\\r\\n\\t\\tvar xmlvisual_scene = root.getElementsByTagName(\\\"visual_scene\\\").item(0);\\r\\n\\t\\tif(!xmlvisual_scene)\\r\\n\\t\\t\\tthrow(\\\"visual_scene XML node not found in DAE\\\");\\r\\n\\r\\n\\t\\t//hack to avoid problems with bones with spaces in names\\r\\n\\t\\tthis._nodes_by_id = {}; //clear\\r\\n\\t\\tthis._nodes_by_sid = {}; //clear\\r\\n\\t\\tthis._controllers_found = {};//we need to check what controllers had been found, in case we miss one at the end\\r\\n\\t\\tthis._geometries_found = {};\\r\\n\\r\\n\\t\\t//Create a scene tree\\r\\n\\t\\tvar scene = { \\r\\n\\t\\t\\tobject_class:\\\"Scene\\\", \\r\\n\\t\\t\\tlight: null,\\r\\n\\t\\t\\tmaterials: {},\\r\\n\\t\\t\\tmeshes: {},\\r\\n\\t\\t\\tresources: {}, //used to store animation tracks\\r\\n\\t\\t\\troot:{ children:[] },\\r\\n\\t\\t\\texternal_files: {} //store info about external files mentioned in this \\r\\n\\t\\t};\\r\\n\\r\\n\\t\\t//scene metadata (like author, tool, up vector, dates, etc)\\r\\n\\t\\tvar xmlasset = root.getElementsByTagName(\\\"asset\\\")[0];\\r\\n\\t\\tif(xmlasset)\\r\\n\\t\\t\\tscene.metadata = this.readAsset( xmlasset );\\r\\n\\r\\n\\t\\t//parse nodes tree to extract names and ierarchy only\\r\\n\\t\\tvar xmlnodes = xmlvisual_scene.childNodes;\\r\\n\\t\\tfor(var i = 0; i < xmlnodes.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar item = xmlnodes.item(i);\\r\\n\\t\\t\\tif(item.nodeType != 1 ) //not tag\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar node = null;\\r\\n\\t\\t\\tif(item.localName == \\\"node\\\")\\r\\n\\t\\t\\t\\tnode = this.readNodeTree( item, scene, 0, flip );\\r\\n\\t\\t\\telse \\r\\n\\t\\t\\t\\tconsole.warn(\\\"DAE contains unknown node type: \\\" + item.localName );\\r\\n\\r\\n\\t\\t\\tif(node)\\r\\n\\t\\t\\t\\tscene.root.children.push(node);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//parse nodes content (two steps so we have first all the scene tree info)\\r\\n\\t\\tfor(var i = 0; i < xmlnodes.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(xmlnodes.item(i).localName != \\\"node\\\")\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tthis.readNodeInfo( xmlnodes.item(i), scene, 0, flip );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//read remaining controllers (in some cases some controllers are not linked from the nodes or the geometries)\\r\\n\\t\\tthis.readLibraryControllers( scene );\\r\\n\\r\\n\\t\\t//read animations\\r\\n\\t\\tvar animations = this.readAnimations( root, scene );\\r\\n\\t\\tif(animations)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar animations_name = \\\"animations_\\\" + filename.substr(0,filename.indexOf(\\\".\\\"));\\r\\n\\t\\t\\tscene.resources[ animations_name ] = animations;\\r\\n\\t\\t\\tscene.root.animation = animations_name;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//read external files (images)\\r\\n\\t\\tscene.images = this.readImages(root);\\r\\n\\r\\n\\t\\t//clear memory\\r\\n\\t\\tthis._nodes_by_id = {};\\r\\n\\t\\tthis._nodes_by_sid = {};\\r\\n\\t\\tthis._controllers_found = {};\\r\\n\\t\\tthis._geometries_found = {};\\r\\n\\t\\tthis._xmlroot = null;\\r\\n\\r\\n\\t\\t//console.log(scene);\\r\\n\\t\\treturn scene;\\r\\n\\t},\\r\\n\\r\\n\\t/* Collect node ids, in case there is bones (with spaces in name) I need to know the nodenames in advance */\\r\\n\\t/*\\r\\n\\treadAllNodeNames: function(xmlnode)\\r\\n\\t{\\r\\n\\t\\tvar node_id = this.safeString( xmlnode.getAttribute(\\\"id\\\") );\\r\\n\\t\\tif(node_id)\\r\\n\\t\\t\\tthis._nodes_by_id[node_id] = true; //node found\\r\\n\\t\\t//nodes seem to have to possible ids, id and sid, I guess one is unique, the other user-defined\\r\\n\\t\\tvar node_sid = this.safeString( xmlnode.getAttribute(\\\"sid\\\") );\\r\\n\\t\\tif(node_sid)\\r\\n\\t\\t\\tthis._nodes_by_id[node_sid] = true; //node found\\r\\n\\r\\n\\r\\n\\t\\tfor( var i = 0; i < xmlnode.childNodes.length; i++ )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlchild = xmlnode.childNodes.item(i);\\r\\n\\r\\n\\t\\t\\t//children\\r\\n\\t\\t\\tif(xmlchild.localName != \\\"node\\\")\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tthis.readAllNodeNames(xmlchild);\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\t\\t*/\\r\\n\\r\\n\\treadAsset: function( xmlasset )\\r\\n\\t{\\r\\n\\t\\tvar metadata = {};\\r\\n\\r\\n\\t\\tfor( var i = 0; i < xmlasset.childNodes.length; i++ )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlchild = xmlasset.childNodes.item(i);\\r\\n\\t\\t\\tif(xmlchild.nodeType != 1 ) //not tag\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tswitch( xmlchild.localName )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase \\\"contributor\\\": \\r\\n\\t\\t\\t\\t\\tvar tool = xmlchild.querySelector(\\\"authoring_tool\\\");\\r\\n\\t\\t\\t\\t\\tif(tool)\\r\\n\\t\\t\\t\\t\\t\\tmetadata[\\\"authoring_tool\\\"] = tool.textContext;\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"unit\\\": metadata[\\\"unit\\\"] = xmlchild.getAttribute(\\\"name\\\"); break;\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\tmetadata[ xmlchild.localName ] = xmlchild.textContent; break;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn metadata;\\r\\n\\t},\\r\\n\\r\\n\\t//reads the tree structure (also the transform)\\r\\n\\treadNodeTree: function( xmlnode, scene, level, flip )\\r\\n\\t{\\r\\n\\t\\tvar node_id = this.safeString( xmlnode.getAttribute(\\\"id\\\") );\\r\\n\\t\\tvar node_sid = this.safeString( xmlnode.getAttribute(\\\"sid\\\") );\\r\\n\\t\\tvar node_name = this.safeString( xmlnode.getAttribute(\\\"name\\\") );\\r\\n\\r\\n\\t\\tvar unique_name = node_id || node_sid || node_name;\\r\\n\\r\\n\\t\\tif(!unique_name)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"Collada: node without name or id, skipping it\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//here we create the node\\r\\n\\t\\tvar node = { \\r\\n\\t\\t\\tname: node_name,\\r\\n\\t\\t\\tid: unique_name, \\r\\n\\t\\t\\tsid: node_sid, //just in case\\r\\n\\t\\t\\tchildren:[], \\r\\n\\t\\t\\t_depth: level \\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tvar node_type = xmlnode.getAttribute(\\\"type\\\");\\r\\n\\t\\tif(node_type)\\r\\n\\t\\t\\tnode.type = node_type;\\r\\n\\r\\n\\t\\tthis._nodes_by_id[ unique_name ] = node;\\r\\n\\t\\tif( node_id )\\r\\n\\t\\t\\tthis._nodes_by_id[ node_id ] = node;\\r\\n\\t\\tif( node_sid )\\r\\n\\t\\t{\\r\\n\\t\\t\\tthis._nodes_by_sid[ node_sid ] = node;\\r\\n\\t\\t\\tthis._nodes_by_id[ node_sid ] = node;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//transform\\r\\n\\t\\tnode.model = this.readTransform(xmlnode, level, flip );\\r\\n\\r\\n\\t\\t//node elements\\r\\n\\t\\tfor( var i = 0; i < xmlnode.childNodes.length; i++ )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlchild = xmlnode.childNodes.item(i);\\r\\n\\t\\t\\tif(xmlchild.nodeType != 1 ) //not tag\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//children\\r\\n\\t\\t\\tif(xmlchild.localName == \\\"node\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar child_node = this.readNodeTree(xmlchild, scene, level+1, flip);\\r\\n\\t\\t\\t\\tif(child_node)\\r\\n\\t\\t\\t\\t\\tnode.children.push( child_node );\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if (xmlchild.localName == \\\"instance_node\\\") //DAE allows instancing (referencing nodes in the three so it can be reused several times)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar xmllibrarynodes = this._xmlroot.querySelector(\\\"library_nodes\\\");\\r\\n\\t\\t\\t\\tif(!xmllibrarynodes)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tvar url = xmlchild.getAttribute(\\\"url\\\");\\r\\n\\t\\t\\t\\turl = url.replace(/\\\\./gi,\\\"\\\\\\\\.\\\"); //url could contain dots which invalidates the querySelector, we need to escape them\\r\\n\\t\\t\\t\\tvar xmlchild2 = xmllibrarynodes.querySelector( url );\\r\\n\\t\\t\\t\\tif(!xmlchild2)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"instanced node not found:\\\" + url );\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tvar child_node = this.readNodeTree( xmlchild2, scene, level+1, flip);\\r\\n\\t\\t\\t\\tif(child_node)\\r\\n\\t\\t\\t\\t\\tnode.children.push( child_node );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn node;\\r\\n\\t},\\r\\n\\r\\n\\t//extracts the info of a node\\r\\n\\t//this is done AFTER having created the scene tree to avoid problems of nodes referencing other nodes that are not yet created\\r\\n\\treadNodeInfo: function( xmlnode, scene, level, flip, parent )\\r\\n\\t{\\r\\n\\t\\tvar node_id = this.safeString( xmlnode.getAttribute(\\\"id\\\") );\\r\\n\\t\\tvar node_sid = this.safeString( xmlnode.getAttribute(\\\"sid\\\") );\\r\\n\\t\\tvar node_name = this.safeString( xmlnode.getAttribute(\\\"name\\\") );\\r\\n\\r\\n\\t\\tvar unique_name = node_id || node_sid || node_name;\\r\\n\\t\\tvar node;\\r\\n\\t\\tif(!unique_name) {\\r\\n\\t\\t\\t//if there is no id, then either all of this node's properties \\r\\n\\t\\t\\t//should be assigned directly to its parent node, or the node doesn't\\r\\n\\t\\t\\t//have a parent node, in which case its a light or something. \\r\\n\\t\\t\\t//So we get the parent by its id, and if there is no parent, we return null\\r\\n\\t\\t\\tif (parent)\\r\\n\\t\\t\\t\\tnode = this._nodes_by_id[ parent.id || parent.sid || parent.name ];\\r\\n\\t\\t\\telse \\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Collada: node without name or id, skipping it\\\");\\r\\n\\t\\t\\t\\treturn null;\\r\\n\\t\\t\\t}\\r\\n\\t\\t} \\r\\n\\t\\telse //retrieve node\\r\\n\\t\\t\\tnode = this._nodes_by_id[ unique_name ];\\r\\n\\r\\n\\t\\tif(!node)\\r\\n\\t\\t\\tnode = this._nodes_by_sid[ node_sid ];\\r\\n\\r\\n\\t\\tif(!node)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"Collada: Node not found by id: \\\" + (node_id || node_sid));\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//node elements\\r\\n\\t\\tfor( var i = 0; i < xmlnode.childNodes.length; i++ )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlchild = xmlnode.childNodes.item(i);\\r\\n\\t\\t\\tif(xmlchild.nodeType != 1 ) //not tag\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//children\\r\\n\\t\\t\\tif(xmlchild.localName == \\\"node\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//pass parent node in case child node is a 'dead' node (has no id or sid)\\r\\n\\t\\t\\t\\tthis.readNodeInfo( xmlchild, scene, level+1, flip, xmlnode );\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(xmlchild.localName == \\\"instance_node\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.readInstancedNodeInfo( xmlchild, scene, level+1, flip, xmlnode );\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(xmlchild.localName == \\\"instance_geometry\\\") //geometry\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar url = xmlchild.getAttribute(\\\"url\\\");\\r\\n\\t\\t\\t\\tvar mesh_id = url.toString().substr(1);\\r\\n\\r\\n\\t\\t\\t\\tif(!node.mesh)\\r\\n\\t\\t\\t\\t\\tnode.mesh = mesh_id;\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(!node.meshes)\\r\\n\\t\\t\\t\\t\\t\\tnode.meshes = [node.mesh];\\r\\n\\t\\t\\t\\t\\tnode.meshes.push( mesh_id );\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif(!scene.meshes[ url ])\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar mesh_data = this.readGeometry(url, flip);\\r\\n\\t\\t\\t\\t\\tif(mesh_data)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tmesh_data.name = mesh_id;\\r\\n\\t\\t\\t\\t\\t\\tscene.meshes[ mesh_id ] = mesh_data;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"DAE node references mesh but not found: \\\" + url );\\r\\n\\r\\n\\t\\t\\t\\t//binded material\\r\\n\\t\\t\\t\\tvar xmlmaterials = xmlchild.querySelectorAll(\\\"instance_material\\\");\\r\\n\\t\\t\\t\\tif(xmlmaterials)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tfor(var iMat = 0; iMat < xmlmaterials.length; ++iMat)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar xmlmaterial = xmlmaterials.item(iMat);\\r\\n\\t\\t\\t\\t\\t\\tif(!xmlmaterial)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tconsole.warn(\\\"instance_material not found: \\\" + i);\\r\\n\\t\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\tvar matname = xmlmaterial.getAttribute(\\\"target\\\").toString().substr(1);\\r\\n\\t\\t\\t\\t\\t\\t//matname = matname.replace(/ /g,\\\"_\\\"); //names cannot have spaces\\r\\n\\t\\t\\t\\t\\t\\tif(!scene.materials[ matname ])\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\tvar material = this.readMaterial(matname);\\r\\n\\t\\t\\t\\t\\t\\t\\tif(material)\\r\\n\\t\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tmaterial.id = matname; \\r\\n\\t\\t\\t\\t\\t\\t\\t\\tscene.materials[ material.id ] = material;\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tif(iMat == 0)\\r\\n\\t\\t\\t\\t\\t\\t\\tnode.material = matname;\\r\\n\\t\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tif(!node.materials)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tnode.materials = [];\\r\\n\\t\\t\\t\\t\\t\\t\\tnode.materials.push(matname);\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(xmlchild.localName == \\\"instance_controller\\\") //this node has a controller: skinning, morph targets or even multimaterial are controllers\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//warning: I detected that some nodes could have a controller but they are not referenced here. ??\\r\\n\\t\\t\\t\\tvar url = xmlchild.getAttribute(\\\"url\\\");\\r\\n\\t\\t\\t\\tvar safe_url = url.replace(/\\\\./gi,\\\"\\\\\\\\.\\\"); //url could contain dots which invalidates the querySelector, we need to escape them\\r\\n\\t\\t\\t\\tvar xmlcontroller = this._xmlroot.querySelector(\\\"controller\\\" + safe_url);\\r\\n\\r\\n\\t\\t\\t\\tif( !xmlcontroller ) //search manually because ids could have invalid characters that querySelector doesnt support\\r\\n\\t\\t\\t\\t\\txmlcontroller = this.searchManuallyById( \\\"library_controllers\\\", url.substr(1) ); //remove #\\r\\n\\r\\n\\t\\t\\t\\tif(!xmlcontroller)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"DAE, node controller not found: \\\" + url );\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tvar mesh_data = this.readController( xmlcontroller, flip, scene );\\r\\n\\r\\n\\t\\t\\t\\t//binded materials\\r\\n\\t\\t\\t\\tvar xmlbind_material = xmlchild.querySelector(\\\"bind_material\\\");\\r\\n\\t\\t\\t\\tif(xmlbind_material){\\r\\n\\t\\t\\t\\t\\t//removed readBindMaterials up here for consistency\\r\\n\\t\\t\\t\\t\\tvar xmltechniques = xmlbind_material.querySelectorAll(\\\"technique_common\\\");\\r\\n\\t\\t\\t\\t\\tfor(var iTec = 0; iTec < xmltechniques.length; iTec++)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar xmltechnique = xmltechniques.item(iTec);\\r\\n\\t\\t\\t\\t\\t\\tvar xmlinstance_materials = xmltechnique.querySelectorAll(\\\"instance_material\\\");\\r\\n\\t\\t\\t\\t\\t\\tfor(var iMat = 0; iMat < xmlinstance_materials.length; iMat++)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tvar xmlinstance_material = xmlinstance_materials.item(iMat);\\r\\n\\t\\t\\t\\t\\t\\t\\tif(!xmlinstance_material)\\r\\n\\t\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconsole.warn(\\\"instance_material for controller not found: \\\" + xmlinstance_material);\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tvar matname = xmlinstance_material.getAttribute(\\\"target\\\").toString().substr(1);\\r\\n\\t\\t\\t\\t\\t\\t\\tif(!scene.materials[ matname ])\\r\\n\\t\\t\\t\\t\\t\\t\\t{\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tvar material = this.readMaterial(matname);\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif(material)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tmaterial.id = matname; \\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tscene.materials[ material.id ] = material;\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tif(iMat == 0)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tnode.material = matname;\\r\\n\\t\\t\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif(!node.materials)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tnode.materials = [];\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tnode.materials.push(matname);\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif(mesh_data)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar mesh = mesh_data;\\r\\n\\t\\t\\t\\t\\tif( mesh_data.type == \\\"morph\\\" )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tmesh = mesh_data.mesh;\\r\\n\\t\\t\\t\\t\\t\\tnode.morph_targets = mesh_data.morph_targets;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tmesh.name = url.toString();\\r\\n\\t\\t\\t\\t\\tnode.mesh = url.toString();\\r\\n\\t\\t\\t\\t\\tscene.meshes[ url ] = mesh;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(xmlchild.localName == \\\"instance_light\\\") //light\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar url = xmlchild.getAttribute(\\\"url\\\");\\r\\n\\t\\t\\t\\tthis.readLight(node, url);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(xmlchild.localName == \\\"instance_camera\\\") //camera\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar url = xmlchild.getAttribute(\\\"url\\\");\\r\\n\\t\\t\\t\\tthis.readCamera(node, url);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t//else //disable to avoid translate, rotate, scale, etc\\r\\n\\t\\t\\t//\\tconsole.warn(\\\"DAE contains unknown nodes: \\\" + xmlchild.localName );\\r\\n\\t\\t\\t//other possible tags?\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\treadInstancedNodeInfo: function( xmlnode, scene, level, flip, parent )\\r\\n\\t{\\r\\n\\t\\tvar xmllibrarynodes = this._xmlroot.querySelector(\\\"library_nodes\\\");\\r\\n\\t\\tif(!xmllibrarynodes)\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\tvar url = xmlnode.getAttribute(\\\"url\\\");\\r\\n\\t\\turl = url.replace(/\\\\./gi,\\\"\\\\\\\\.\\\"); //url could contain dots which invalidates the querySelector, we need to escape them\\r\\n\\t\\tvar xmlnode2 = xmllibrarynodes.querySelector( url );\\r\\n\\t\\tif(!xmlnode2)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"instanced node not found:\\\" + url );\\r\\n\\t\\t\\treturn false;\\r\\n\\t\\t}\\r\\n\\t\\tthis.readNodeInfo( xmlnode2, scene, level+1, flip);\\r\\n\\t\\treturn true;\\r\\n\\t},\\r\\n\\r\\n\\tsearchManuallyById: function( base_node_selector, id )\\r\\n\\t{\\r\\n\\t\\tvar xmlbase = this._xmlroot.querySelector( base_node_selector );\\r\\n\\t\\tif(!xmlbase)\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tfor(var i = 0; i < xmlbase.childNodes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlchild = xmlbase.childNodes[i];\\r\\n\\t\\t\\tif( xmlchild.id == id )\\r\\n\\t\\t\\t\\treturn xmlchild;\\r\\n\\t\\t}\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\t//if you want to rename some material names\\r\\n\\tmaterial_translate_table: {\\r\\n\\t\\t/*\\r\\n\\t\\ttransparency: \\\"opacity\\\",\\r\\n\\t\\treflectivity: \\\"reflection_factor\\\",\\r\\n\\t\\tspecular: \\\"specular_factor\\\",\\r\\n\\t\\tshininess: \\\"specular_gloss\\\",\\r\\n\\t\\temission: \\\"emissive\\\",\\r\\n\\t\\tdiffuse: \\\"color\\\"\\r\\n\\t\\t*/\\r\\n\\t},\\r\\n\\r\\n\\tlight_translate_table: {\\r\\n\\r\\n\\t\\tpoint: \\\"omni\\\",\\r\\n\\t\\tdirectional: \\\"directional\\\",\\r\\n\\t\\tspot: \\\"spot\\\"\\t\\t\\r\\n\\t},\\r\\n\\r\\n\\tcamera_translate_table: {\\r\\n\\t\\txfov: \\\"fov\\\",\\r\\n\\t\\taspect_ratio: \\\"aspect\\\",\\r\\n\\t\\tznear: \\\"near\\\",\\r\\n\\t\\tzfar: \\\"far\\\"\\r\\n\\t},\\r\\n\\r\\n\\t//used when id have spaces (regular selector do not support spaces)\\r\\n\\tquerySelectorAndId: function(root, selector, id)\\r\\n\\t{\\r\\n\\t\\t//TODO: what about escaping spaces and dots?\\r\\n\\t\\tvar nodes = root.querySelectorAll(selector);\\r\\n\\t\\tfor(var i = 0; i < nodes.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar attr_id = nodes.item(i).getAttribute(\\\"id\\\");\\r\\n\\t\\t\\tif( !attr_id ) \\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tattr_id = attr_id.toString();\\r\\n\\t\\t\\tif(attr_id == id )\\r\\n\\t\\t\\t\\treturn nodes.item(i);\\r\\n\\t\\t}\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\t//returns the first element that matches a tag name, if not tagname is specified then the first tag element\\r\\n\\tgetFirstChildElement: function(root, localName)\\r\\n\\t{\\r\\n\\t\\tvar c = root.childNodes;\\r\\n\\t\\tfor(var i = 0; i < c.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar item = c.item(i);\\r\\n\\t\\t\\tif( (item.localName && !localName) || (localName && localName == item.localName) )\\r\\n\\t\\t\\t\\treturn item;\\r\\n\\t\\t}\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\treadMaterial: function(url)\\r\\n\\t{\\r\\n\\t\\tvar xmlmaterial = this.querySelectorAndId( this._xmlroot, \\\"library_materials material\\\", url );\\r\\n\\r\\n\\t\\tif(!xmlmaterial)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\t//get effect name\\r\\n\\t\\tvar xmleffect = xmlmaterial.querySelector(\\\"instance_effect\\\");\\r\\n\\t\\tif(!xmleffect) return null;\\r\\n\\r\\n\\t\\tvar effect_url = xmleffect.getAttribute(\\\"url\\\").substr(1);\\r\\n\\r\\n\\t\\t//get effect\\r\\n\\t\\tvar xmleffects = this.querySelectorAndId( this._xmlroot, \\\"library_effects effect\\\", effect_url );\\r\\n\\r\\n\\t\\tif(!xmleffects) return null;\\r\\n\\r\\n\\t\\t//get common\\r\\n\\t\\tvar xmltechnique = xmleffects.querySelector(\\\"technique\\\");\\r\\n\\t\\tif(!xmltechnique) \\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\t//get newparams and convert to js object\\r\\n\\t\\tvar xmlnewparams = xmleffects.querySelectorAll(\\\"newparam\\\");\\r\\n\\t\\tvar newparams = {}\\r\\n\\t\\tfor (var i = 0; i < xmlnewparams.length; i++) {\\r\\n\\r\\n\\t\\t\\tvar init_from = xmlnewparams[i].querySelector(\\\"init_from\\\");\\r\\n\\t\\t\\tvar parent;\\r\\n\\t\\t\\tif (init_from)\\r\\n\\t\\t\\t\\tparent = init_from.innerHTML;\\r\\n\\t\\t\\telse {\\r\\n\\t\\t\\t\\tvar source = xmlnewparams[i].querySelector(\\\"source\\\");\\r\\n\\t\\t\\t\\tif(!parent)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"no source found for material for newparam\\\");\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tparent = source.innerHTML;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tnewparams[xmlnewparams[i].getAttribute(\\\"sid\\\")] = {\\r\\n\\t\\t\\t\\tparent: parent\\r\\n\\t\\t\\t};\\r\\n\\t\\t}\\r\\n\\r\\n\\r\\n\\r\\n\\t\\tvar material = {};\\r\\n\\r\\n\\t\\t//read the images here because we need to access them to assign texture names\\r\\n\\t\\tvar images = this.readImages(this._xmlroot);\\r\\n\\r\\n\\r\\n\\t\\tvar xmlphong = xmltechnique.querySelector(\\\"phong\\\");\\r\\n\\t\\tif(!xmlphong) \\r\\n\\t\\t\\txmlphong = xmltechnique.querySelector(\\\"blinn\\\");\\r\\n\\t\\tif(!xmlphong) \\r\\n\\t\\t\\txmlphong = xmltechnique.querySelector(\\\"lambert\\\");\\r\\n\\t\\tif(!xmlphong) \\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tmaterial.type = xmlphong.localName;\\r\\n\\r\\n\\t\\t//for every tag of properties\\r\\n\\t\\tfor(var i = 0; i < xmlphong.childNodes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlparam = xmlphong.childNodes.item(i);\\r\\n\\r\\n\\t\\t\\tif(!xmlparam.localName) //text tag\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//translate name\\r\\n\\t\\t\\tvar param_name = xmlparam.localName.toString();\\r\\n\\t\\t\\tif(this.material_translate_table[param_name])\\r\\n\\t\\t\\t\\tparam_name = this.material_translate_table[param_name];\\r\\n\\r\\n\\t\\t\\t//value\\r\\n\\t\\t\\tvar xmlparam_value = this.getFirstChildElement( xmlparam );\\r\\n\\t\\t\\tif(!xmlparam_value)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(xmlparam_value.localName.toString() == \\\"color\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar value = this.readContentAsFloats( xmlparam_value );\\r\\n\\t\\t\\t\\tvar opaque_value = xmlparam.getAttribute(\\\"opaque\\\");\\r\\n\\t\\t\\t\\tif(opaque_value)\\r\\n\\t\\t\\t\\t\\tmaterial.opaque_info = opaque_value;\\r\\n\\t\\t\\t\\tif( opaque_value == \\\"RGB_ZERO\\\")\\r\\n\\t\\t\\t\\t\\tmaterial[ param_name ] = value.subarray(0,4);\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tmaterial[ param_name ] = value.subarray(0,3);\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(xmlparam_value.localName.toString() == \\\"float\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tmaterial[ param_name ] = this.readContentAsFloats( xmlparam_value )[0];\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(xmlparam_value.localName.toString() == \\\"texture\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(!material.textures)\\r\\n\\t\\t\\t\\t\\tmaterial.textures = {};\\r\\n\\t\\t\\t\\tvar map_id = xmlparam_value.getAttribute(\\\"texture\\\");\\r\\n\\t\\t\\t\\tif(!map_id)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\t// if map_id is not a filename, lets go and look for it.\\r\\n\\t\\t\\t\\tif (map_id.indexOf('.') === -1){\\r\\n\\t\\t\\t\\t\\t//check effect parents\\r\\n\\t\\t\\t\\t\\tmap_id = this.getParentParam(newparams, map_id);\\r\\n\\r\\n\\t\\t\\t\\t\\tif (images[map_id])\\r\\n\\t\\t\\t\\t\\t\\tmap_id = images[map_id].path;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t//now get the texture filename from images\\r\\n\\r\\n\\t\\t\\t\\tvar map_info = { map_id: map_id };\\r\\n\\t\\t\\t\\tvar uvs = xmlparam_value.getAttribute(\\\"texcoord\\\");\\r\\n\\t\\t\\t\\tmap_info.uvs = uvs;\\r\\n\\t\\t\\t\\tmaterial.textures[ param_name ] = map_info;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tmaterial.object_class = \\\"Material\\\";\\r\\n\\t\\treturn material;\\r\\n\\t},\\r\\n\\r\\n\\tgetParentParam: function(newparams, param) {\\r\\n\\t\\tif (!newparams[param])\\r\\n\\t\\t\\treturn param;\\r\\n\\r\\n\\t\\tif (newparams[param].parent)\\r\\n\\t\\t\\treturn this.getParentParam(newparams, newparams[param].parent)\\r\\n\\t\\telse\\r\\n\\t\\t\\treturn param;\\r\\n\\t},\\r\\n\\r\\n\\treadLight: function(node, url)\\r\\n\\t{\\r\\n\\t\\tvar light = {};\\r\\n\\r\\n\\t\\tvar xmlnode = null;\\r\\n\\t\\t\\r\\n\\t\\tif(url.length > 1) //weird cases with id == #\\r\\n\\t\\t\\txmlnode = this._xmlroot.querySelector(\\\"library_lights \\\" + url);\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlliblights = this._xmlroot.querySelector(\\\"library_lights\\\");\\r\\n\\t\\t\\txmlnode = this.getFirstChildElement( xmlliblights, \\\"light\\\" );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!xmlnode)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\t//pack\\r\\n\\t\\tvar children = [];\\r\\n\\t\\tvar xml = xmlnode.querySelector(\\\"technique_common\\\");\\r\\n\\t\\tif(xml)\\r\\n\\t\\t\\tfor(var i = 0; i < xml.childNodes.length; i++ )\\r\\n\\t\\t\\t\\tif( xml.childNodes.item(i).nodeType == 1 ) //tag\\r\\n\\t\\t\\t\\t\\tchildren.push( xml.childNodes.item(i) );\\r\\n\\r\\n\\t\\tvar xmls = xmlnode.querySelectorAll(\\\"technique\\\");\\r\\n\\t\\tfor(var i = 0; i < xmls.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xml2 = xmls.item(i);\\r\\n\\t\\t\\tfor(var j = 0; j < xml2.childNodes.length; j++ )\\r\\n\\t\\t\\t\\tif( xml2.childNodes.item(j).nodeType == 1 ) //tag\\r\\n\\t\\t\\t\\t\\tchildren.push( xml2.childNodes.item(j) );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//get\\r\\n\\t\\tfor(var i = 0; i < children.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xml = children[i];\\r\\n\\t\\t\\tswitch( xml.localName )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase \\\"point\\\": \\r\\n\\t\\t\\t\\t\\tlight.type = this.light_translate_table[ xml.localName ]; \\r\\n\\t\\t\\t\\t\\tparse_params(light, xml);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"directional\\\":\\r\\n\\t\\t\\t\\t\\tlight.type = this.light_translate_table[ xml.localName ]; \\r\\n\\t\\t\\t\\t\\tparse_params(light, xml);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"spot\\\": \\r\\n\\t\\t\\t\\t\\tlight.type = this.light_translate_table[ xml.localName ]; \\r\\n\\t\\t\\t\\t\\tparse_params(light, xml);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\r\\n\\t\\t\\t\\tcase \\\"intensity\\\": \\r\\n\\t\\t\\t\\t\\tlight.intensity = this.readContentAsFloats( xml )[0]; \\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfunction parse_params(light, xml)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i = 0; i < xml.childNodes.length; i++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar child = xml.childNodes.item(i);\\r\\n\\t\\t\\t\\tif( !child || child.nodeType != 1 ) //tag\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tswitch( child.localName )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tcase \\\"color\\\": \\r\\n\\t\\t\\t\\t\\t\\tlight.color = Collada.readContentAsFloats( child ); break;\\r\\n\\t\\t\\t\\t\\tcase \\\"falloff_angle\\\": \\r\\n\\t\\t\\t\\t\\t\\tlight.angle_end = Collada.readContentAsFloats( child )[0]; \\r\\n\\t\\t\\t\\t\\t\\tlight.angle = light.angle_end - 10; \\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t\\r\\n\\t\\tif(node.model)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//light position is final column of model\\r\\n\\t\\t\\tlight.position = [node.model[12],node.model[13],node.model[14]];\\r\\n\\t\\t\\t//light forward vector is reverse of third column of model\\r\\n\\t\\t\\tvar forward = [ - node.model[8], - node.model[9], - node.model[10]];\\r\\n\\t\\t\\t//so light target is position + forward\\r\\n\\t\\t\\tlight.target = [light.position[0] + forward[0],\\r\\n\\t\\t\\t\\t\\t\\t\\tlight.position[1] + forward[1],\\r\\n\\t\\t\\t\\t\\t\\t\\tlight.position[2] + forward[2] ];\\r\\n\\t\\t}\\r\\n\\t\\telse {\\r\\n\\t\\t\\tconsole.warn( \\\"Could not read light position for light: \\\" + node.name + \\\". Setting defaults.\\\");\\r\\n\\t\\t\\tlight.position = [0,0,0];\\r\\n\\t\\t\\tlight.target = [0,-1,0];\\r\\n\\t\\t}\\r\\n\\t\\t\\r\\n\\r\\n\\t\\tnode.light = light;\\r\\n\\t},\\r\\n\\r\\n\\treadCamera: function(node, url)\\r\\n\\t{\\r\\n\\t\\tvar camera = {};\\r\\n\\r\\n\\t\\tvar xmlnode = this._xmlroot.querySelector(\\\"library_cameras \\\" + url);\\r\\n\\t\\tif(!xmlnode) return null;\\r\\n\\r\\n\\t\\t//pack\\r\\n\\t\\tvar children = [];\\r\\n\\t\\tvar xml = xmlnode.querySelector(\\\"technique_common\\\");\\r\\n\\t\\tif(xml) //grab all internal stuff\\r\\n\\t\\t\\tfor(var i = 0; i < xml.childNodes.length; i++ )\\r\\n\\t\\t\\t\\tif( xml.childNodes.item(i).nodeType == 1 ) //tag\\r\\n\\t\\t\\t\\t\\tchildren.push( xml.childNodes.item(i) );\\r\\n\\r\\n\\t\\t//\\r\\n\\t\\tfor(var i = 0; i < children.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar tag = children[i];\\r\\n\\t\\t\\tparse_params(camera, tag);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfunction parse_params(camera, xml)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i = 0; i < xml.childNodes.length; i++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar child = xml.childNodes.item(i);\\r\\n\\t\\t\\t\\tif( !child || child.nodeType != 1 ) //tag\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tvar translated = Collada.camera_translate_table[ child.localName ] || child.localName;\\r\\n\\t\\t\\t\\tcamera[ translated ] = parseFloat( child.textContent );\\r\\n\\t\\t\\t\\t\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//parse to convert yfov to standard (x) fov\\r\\n\\t\\tif ( camera.yfov && !camera.fov ) {\\r\\n\\t\\t\\tif ( camera.aspect ) {\\r\\n\\t\\t\\t\\tcamera.fov = camera.yfov * camera.aspect;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Could not convert camera yfov to xfov because aspect ratio not set\\\")\\r\\n\\t\\t} \\r\\n\\r\\n\\t\\tnode.camera = camera;\\r\\n\\t},\\r\\n\\r\\n\\treadTransform: function(xmlnode, level, flip)\\r\\n\\t{\\r\\n\\t\\t//identity\\r\\n\\t\\tvar matrix = mat4.create(); \\r\\n\\t\\tvar temp = mat4.create(); \\r\\n\\t\\tvar tmpq = quat.create();\\r\\n\\t\\t\\r\\n\\t\\tvar flip_fix = false;\\r\\n\\r\\n\\t\\t//search for the matrix\\r\\n\\t\\tfor(var i = 0; i < xmlnode.childNodes.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xml = xmlnode.childNodes.item(i);\\r\\n\\t\\t\\tif( !xml || xml.nodeType != 1 ) //tag\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(xml.localName == \\\"matrix\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar matrix = this.readContentAsFloats(xml);\\r\\n\\t\\t\\t\\t//console.log(\\\"Nodename: \\\" + xmlnode.getAttribute(\\\"id\\\"));\\r\\n\\t\\t\\t\\t//console.log(matrix);\\r\\n\\t\\t\\t\\tthis.transformMatrix(matrix, level == 0);\\r\\n\\t\\t\\t\\t//console.log(matrix);\\r\\n\\t\\t\\t\\treturn matrix;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(xml.localName == \\\"translate\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar values = this.readContentAsFloats(xml);\\r\\n\\t\\t\\t\\tif(flip && level > 0)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar tmp = values[1];\\r\\n\\t\\t\\t\\t\\tvalues[1] = values[2];\\r\\n\\t\\t\\t\\t\\tvalues[2] = -tmp; //swap coords\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tmat4.translate( matrix, matrix, values );\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//rotate\\r\\n\\t\\t\\tif(xml.localName == \\\"rotate\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar values = this.readContentAsFloats(xml);\\r\\n\\t\\t\\t\\tif(values.length == 4) //x,y,z, angle\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar id = xml.getAttribute(\\\"sid\\\");\\r\\n\\t\\t\\t\\t\\tif(id == \\\"jointOrientX\\\")\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvalues[3] += 90;\\r\\n\\t\\t\\t\\t\\t\\tflip_fix = true;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t//rotateX & rotateY & rotateZ done below\\r\\n\\r\\n\\t\\t\\t\\t\\tif(flip)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar tmp = values[1];\\r\\n\\t\\t\\t\\t\\t\\tvalues[1] = values[2];\\r\\n\\t\\t\\t\\t\\t\\tvalues[2] = -tmp; //swap coords\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tif(values[3] != 0.0)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tquat.setAxisAngle( tmpq, values.subarray(0,3), values[3] * DEG2RAD);\\r\\n\\t\\t\\t\\t\\t\\tmat4.fromQuat( temp, tmpq );\\r\\n\\t\\t\\t\\t\\t\\tmat4.multiply(matrix, matrix, temp);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//scale\\r\\n\\t\\t\\tif(xml.localName == \\\"scale\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar values = this.readContentAsFloats(xml);\\r\\n\\t\\t\\t\\tif(flip)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar tmp = values[1];\\r\\n\\t\\t\\t\\t\\tvalues[1] = values[2];\\r\\n\\t\\t\\t\\t\\tvalues[2] = -tmp; //swap coords\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tmat4.scale( matrix, matrix, values );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn matrix;\\r\\n\\t},\\r\\n\\r\\n\\treadTransform2: function(xmlnode, level, flip)\\r\\n\\t{\\r\\n\\t\\t//identity\\r\\n\\t\\tvar matrix = mat4.create(); \\r\\n\\t\\tvar rotation = quat.create();\\r\\n\\t\\tvar tmpmatrix = mat4.create();\\r\\n\\t\\tvar tmpq = quat.create();\\r\\n\\t\\tvar translate = vec3.create();\\r\\n\\t\\tvar scale = vec3.fromValues(1,1,1);\\r\\n\\t\\t\\r\\n\\t\\tvar flip_fix = false;\\r\\n\\r\\n\\t\\t//search for the matrix\\r\\n\\t\\tfor(var i = 0; i < xmlnode.childNodes.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xml = xmlnode.childNodes.item(i);\\r\\n\\r\\n\\t\\t\\tif(xml.localName == \\\"matrix\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar matrix = this.readContentAsFloats(xml);\\r\\n\\t\\t\\t\\t//console.log(\\\"Nodename: \\\" + xmlnode.getAttribute(\\\"id\\\"));\\r\\n\\t\\t\\t\\t//console.log(matrix);\\r\\n\\t\\t\\t\\tthis.transformMatrix(matrix, level == 0);\\r\\n\\t\\t\\t\\t//console.log(matrix);\\r\\n\\t\\t\\t\\treturn matrix;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(xml.localName == \\\"translate\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar values = this.readContentAsFloats(xml);\\r\\n\\t\\t\\t\\ttranslate.set(values);\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//rotate\\r\\n\\t\\t\\tif(xml.localName == \\\"rotate\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar values = this.readContentAsFloats(xml);\\r\\n\\t\\t\\t\\tif(values.length == 4) //x,y,z, angle\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar id = xml.getAttribute(\\\"sid\\\");\\r\\n\\t\\t\\t\\t\\tif(id == \\\"jointOrientX\\\")\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvalues[3] += 90;\\r\\n\\t\\t\\t\\t\\t\\tflip_fix = true;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t//rotateX & rotateY & rotateZ done below\\r\\n\\r\\n\\t\\t\\t\\t\\tif(flip)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar tmp = values[1];\\r\\n\\t\\t\\t\\t\\t\\tvalues[1] = values[2];\\r\\n\\t\\t\\t\\t\\t\\tvalues[2] = -tmp; //swap coords\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tif(values[3] != 0.0)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tquat.setAxisAngle( tmpq, values.subarray(0,3), values[3] * DEG2RAD);\\r\\n\\t\\t\\t\\t\\t\\tquat.multiply(rotation,rotation,tmpq);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//scale\\r\\n\\t\\t\\tif(xml.localName == \\\"scale\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar values = this.readContentAsFloats(xml);\\r\\n\\t\\t\\t\\tif(flip)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar tmp = values[1];\\r\\n\\t\\t\\t\\t\\tvalues[1] = values[2];\\r\\n\\t\\t\\t\\t\\tvalues[2] = -tmp; //swap coords\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tscale.set(values);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(flip && level > 0)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar tmp = translate[1];\\r\\n\\t\\t\\ttranslate[1] = translate[2];\\r\\n\\t\\t\\ttranslate[2] = -tmp; //swap coords\\r\\n\\t\\t}\\r\\n\\t\\tmat4.translate(matrix, matrix, translate);\\r\\n\\r\\n\\t\\tmat4.fromQuat( tmpmatrix , rotation );\\r\\n\\t\\t//mat4.rotateX(tmpmatrix, tmpmatrix, Math.PI * 0.5);\\r\\n\\t\\tmat4.multiply( matrix, matrix, tmpmatrix );\\r\\n\\t\\tmat4.scale( matrix, matrix, scale );\\r\\n\\r\\n\\r\\n\\t\\treturn matrix;\\r\\n\\t},\\r\\n\\r\\n\\t//for help read this: https://www.khronos.org/collada/wiki/Using_accessors\\r\\n\\treadGeometry: function( id, flip, scene )\\r\\n\\t{\\r\\n\\t\\t//already read, could happend if several controllers point to the same mesh\\r\\n\\t\\tif( this._geometries_found[ id ] !== undefined )\\r\\n\\t\\t\\treturn this._geometries_found[ id ];\\r\\n\\r\\n\\t\\t//var xmlgeometry = this._xmlroot.querySelector(\\\"geometry\\\" + id);\\r\\n\\t\\tvar xmlgeometry = this._xmlroot.getElementById(id.substr(1));\\r\\n\\t\\tif(!xmlgeometry) \\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"readGeometry: geometry not found: \\\" + id);\\r\\n\\t\\t\\tthis._geometries_found[ id ] = null;\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//if the geometry has morph targets then instead of storing it in a geometry, it is in a controller\\r\\n\\t\\tif(xmlgeometry.localName == \\\"controller\\\") \\r\\n\\t\\t{\\r\\n\\t\\t\\tvar geometry = this.readController( xmlgeometry, flip, scene );\\r\\n\\t\\t\\tthis._geometries_found[ id ] = geometry;\\r\\n\\t\\t\\treturn geometry;\\r\\n\\t\\t}\\r\\n\\r\\n\\r\\n\\t\\tif(xmlgeometry.localName != \\\"geometry\\\") \\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"readGeometry: tag should be geometry, instead it was found: \\\" + xmlgeometry.localName);\\r\\n\\t\\t\\tthis._geometries_found[ id ] = null;\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar xmlmesh = xmlgeometry.querySelector(\\\"mesh\\\");\\r\\n\\t\\tif(!xmlmesh)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"readGeometry: mesh not found in geometry: \\\" + id);\\r\\n\\t\\t\\tthis._geometries_found[ id ] = null;\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\t\\t\\r\\n\\t\\t//get data sources\\r\\n\\t\\tvar sources = xmlgeometry.sources = {};\\r\\n\\t\\tvar xmlsources = xmlmesh.querySelectorAll(\\\"source\\\");\\r\\n\\t\\tfor(var i = 0; i < xmlsources.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlsource = xmlsources.item(i);\\r\\n\\t\\t\\tif(!xmlsource.querySelector)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar float_array = xmlsource.querySelector(\\\"float_array\\\");\\r\\n\\t\\t\\tif(!float_array)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar floats = this.readContentAsFloats( float_array );\\r\\n\\r\\n\\t\\t\\tvar xmlaccessor = xmlsource.querySelector(\\\"accessor\\\");\\r\\n\\t\\t\\tvar stride = parseInt( xmlaccessor.getAttribute(\\\"stride\\\") );\\r\\n\\t\\t\\t//here we are not reading the order to know if they are in X,Y,Z order, because we assume they are, this could lead to wrong meshes\\r\\n\\t\\t\\tvar xmlparams = xmlaccessor.querySelector(\\\"param\\\");\\r\\n\\t\\t\\tsources[ xmlsource.getAttribute(\\\"id\\\") ] = { stride: stride, data: floats, params: xmlparams.length };\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//get streams\\r\\n\\t\\tvar xmlvertices = xmlmesh.querySelector(\\\"vertices input\\\");\\r\\n\\t\\tvar vertices_source = sources[ xmlvertices.getAttribute(\\\"source\\\").substr(1) ];\\r\\n\\t\\tsources[ xmlmesh.querySelector(\\\"vertices\\\").getAttribute(\\\"id\\\") ] = vertices_source;\\r\\n\\r\\n\\t\\tvar mesh = null;\\r\\n\\t\\tvar xmlpolygons = xmlmesh.querySelector(\\\"polygons\\\");\\r\\n\\t\\tif( xmlpolygons )\\r\\n\\t\\t\\tmesh = this.readPolygons( xmlpolygons, sources );\\r\\n\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmltriangles = xmlmesh.querySelectorAll(\\\"triangles\\\");\\r\\n\\t\\t\\tif(xmltriangles && xmltriangles.length)\\r\\n\\t\\t\\t\\tmesh = this.readTriangles( xmltriangles, sources );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//polylist = true;\\r\\n\\t\\t\\t//var vcount = null;\\r\\n\\t\\t\\t//var xmlvcount = xmlpolygons.querySelector(\\\"vcount\\\");\\r\\n\\t\\t\\t//var vcount = this.readContentAsUInt32( xmlvcount );\\r\\n\\t\\t\\tvar xmlpolylist_array = xmlmesh.querySelectorAll(\\\"polylist\\\");\\r\\n\\t\\t\\tif( xmlpolylist_array && xmlpolylist_array.length )\\r\\n\\t\\t\\t\\tmesh = this.readPolylistArray( xmlpolylist_array, sources );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmllinestrip = xmlmesh.querySelector(\\\"linestrips\\\");\\r\\n\\t\\t\\tif(xmllinestrip)\\r\\n\\t\\t\\t\\tmesh = this.readLineStrip( sources, xmllinestrip );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.log(\\\"no polygons or triangles in mesh: \\\" + id);\\r\\n\\t\\t\\tthis._geometries_found[ id ] = null;\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\t\\r\\n\\t\\t//swap coords (X,Y,Z) -> (X,Z,-Y)\\r\\n\\t\\tif(flip && !this.no_flip)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar tmp = 0;\\r\\n\\t\\t\\tvar array = mesh.vertices;\\r\\n\\t\\t\\tfor(var i = 0, l = array.length; i < l; i += 3)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttmp = array[i+1]; \\r\\n\\t\\t\\t\\tarray[i+1] = array[i+2];\\r\\n\\t\\t\\t\\tarray[i+2] = -tmp; \\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tarray = mesh.normals;\\r\\n\\t\\t\\tfor(var i = 0, l = array.length; i < l; i += 3)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttmp = array[i+1]; \\r\\n\\t\\t\\t\\tarray[i+1] = array[i+2];\\r\\n\\t\\t\\t\\tarray[i+2] = -tmp; \\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//transferables for worker\\r\\n\\t\\tif(isWorker && this.use_transferables)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i in mesh)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar data = mesh[i];\\r\\n\\t\\t\\t\\tif(data && data.buffer && data.length > 100)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tthis._transferables.push(data.buffer);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//extra info\\r\\n\\t\\tmesh.filename = id;\\r\\n\\t\\tmesh.object_class = \\\"Mesh\\\";\\r\\n\\r\\n\\t\\tthis._geometries_found[ id ] = mesh;\\r\\n\\t\\treturn mesh;\\r\\n\\t},\\r\\n\\r\\n\\treadPolygons: function( xmlpolygons, sources )\\r\\n\\t{\\r\\n\\t\\tvar use_indices = false;\\r\\n\\r\\n\\t\\tvar groups = [];\\r\\n\\t\\tvar last_index = 0;\\r\\n\\t\\tvar facemap = {};\\r\\n\\t\\tvar vertex_remap = []; //maps DAE vertex index to Mesh vertex index (because when meshes are triangulated indices are changed\\r\\n\\t\\tvar indicesArray = [];\\r\\n\\t\\tvar last_start = 0;\\r\\n\\t\\tvar group_name = \\\"\\\";\\r\\n\\t\\tvar material_name = xmlpolygons.getAttribute(\\\"material\\\");\\r\\n\\t\\tvar buffers = [];\\r\\n\\r\\n\\t\\tvar xmlp_array = [];\\r\\n\\t\\tvar split_to_triangles = true;\\r\\n\\r\\n\\t\\t//for every triangles set (warning, some times they are repeated...)\\r\\n\\t\\tfor(var i = 0; i < xmlpolygons.childNodes.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xml_elem = xmlpolygons.childNodes.item(i);\\r\\n\\r\\n\\t\\t\\tif(xml_elem.localName == \\\"input\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar buffer = this.readInput( xml_elem, sources );\\r\\n\\t\\t\\t\\tif(buffer)\\r\\n\\t\\t\\t\\t\\tbuffers.push( buffer );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if( xml_elem.localName == \\\"p\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\txmlp_array.push( xml_elem );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(xml_elem.localName)\\r\\n\\t\\t\\t\\tconsole.warn(\\\"unknown xml tag in : \\\" + xml_elem.localName);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//assuming buffers are ordered by offset\\r\\n\\t\\t//iterate data\\r\\n\\t\\tvar num_data_vertex = buffers.length; //one value per input buffer\\r\\n\\r\\n\\t\\t//compute data to read per vertex\\r\\n\\t\\tvar num_values_per_vertex = 1;\\r\\n\\t\\tvar buffers_length = buffers.length;\\r\\n\\t\\tfor(var b = 0; b < buffers_length; ++b)\\r\\n\\t\\t\\tnum_values_per_vertex = Math.max( num_values_per_vertex, buffers[b][4] + 1);\\r\\n\\r\\n\\t\\t//for every polygon (could be one with all the indices, could be several, depends on the program)\\r\\n\\t\\tfor(var i = 0; i < xmlp_array.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlp = xmlp_array[i];\\r\\n\\t\\t\\tif(!xmlp || !xmlp.textContent) \\r\\n\\t\\t\\t\\tbreak;\\r\\n\\r\\n\\t\\t\\tvar data = xmlp.textContent.trim().split(\\\" \\\");\\r\\n\\r\\n\\t\\t\\t//used for triangulate polys\\r\\n\\t\\t\\tvar first_index = -1;\\r\\n\\t\\t\\tvar current_index = -1;\\r\\n\\t\\t\\tvar prev_index = -1;\\r\\n\\r\\n\\t\\t\\t//discomment to force 16bits indices\\r\\n\\t\\t\\t//if(use_indices && last_index >= 256*256)\\r\\n\\t\\t\\t//\\tbreak;\\r\\n\\r\\n\\t\\t\\t//for every pack of indices in the polygon (vertex, normal, uv, ... )\\r\\n\\t\\t\\tfor(var k = 0, l = data.length; k < l; k += num_values_per_vertex)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar vertex_id = data.slice(k,k+num_values_per_vertex).join(\\\" \\\"); //generate unique id\\r\\n\\r\\n\\t\\t\\t\\tprev_index = current_index;\\r\\n\\t\\t\\t\\tif(facemap.hasOwnProperty(vertex_id)) //add to arrays, keep the index\\r\\n\\t\\t\\t\\t\\tcurrent_index = facemap[vertex_id];\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t//for every data buffer associated to this vertex\\r\\n\\t\\t\\t\\t\\tfor(var j = 0; j < buffers_length; ++j)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar buffer = buffers[j];\\r\\n\\t\\t\\t\\t\\t\\tvar array = buffer[1]; //array where we accumulate the final data as we extract if from sources\\r\\n\\t\\t\\t\\t\\t\\tvar source = buffer[3]; //where to read the data from\\r\\n\\t\\t\\t\\t\\t\\t\\r\\n\\t\\t\\t\\t\\t\\t//compute the index inside the data source array\\r\\n\\t\\t\\t\\t\\t\\tvar index = parseInt( data[ k + buffer[4] ] );\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t//remember this index in case we need to remap\\r\\n\\t\\t\\t\\t\\t\\tif(j == 0)\\r\\n\\t\\t\\t\\t\\t\\t\\tvertex_remap[ array.length / buffer[2] ] = index; //not sure if buffer[2], it should be number of floats per vertex (usually 3)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t//compute the position inside the source buffer where the final data is located\\r\\n\\t\\t\\t\\t\\t\\tindex *= buffer[2]; //this works in most DAEs (not all)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t//extract every value of this element and store it in its final array (every x,y,z, etc)\\r\\n\\t\\t\\t\\t\\t\\tfor(var x = 0; x < buffer[2]; ++x)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t//if(source[index+x] === undefined) throw(\\\"UNDEFINED!\\\"); //DEBUG\\r\\n\\t\\t\\t\\t\\t\\t\\tarray.push( source[index+x] );\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\r\\n\\t\\t\\t\\t\\tcurrent_index = last_index;\\r\\n\\t\\t\\t\\t\\tlast_index += 1;\\r\\n\\t\\t\\t\\t\\tfacemap[vertex_id] = current_index;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif(split_to_triangles) //the xml element is not triangles? then split polygons in triangles\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(k == 0)\\r\\n\\t\\t\\t\\t\\t\\tfirst_index = current_index;\\r\\n\\t\\t\\t\\t\\t//if(k > 2 * num_data_vertex) //not sure if use this or the next line, the next one works in some DAEs but not sure if it works in all\\r\\n\\t\\t\\t\\t\\tif( (k/num_values_per_vertex) > 2) //triangulate polygons: k is the float, but is divided by num_values_per_vertex because every vertex could have several indices (for normals, etc)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tindicesArray.push( first_index );\\r\\n\\t\\t\\t\\t\\t\\tindicesArray.push( prev_index );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tindicesArray.push( current_index );\\r\\n\\t\\t\\t}//per vertex\\r\\n\\t\\t}//per polygon\\r\\n\\r\\n\\t\\tvar group = {\\r\\n\\t\\t\\tname: group_name || (\\\"group\\\"),\\r\\n\\t\\t\\tstart: last_start,\\r\\n\\t\\t\\tlength: indicesArray.length - last_start,\\r\\n\\t\\t\\tmaterial: material_name || \\\"\\\"\\r\\n\\t\\t};\\r\\n\\t\\tlast_start = indicesArray.length;\\r\\n\\t\\tgroups.push( group );\\r\\n\\r\\n\\t\\tif(!buffers.length)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"collada: without buffers\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar mesh = {\\r\\n\\t\\t\\tvertices: new Float32Array( buffers[0][1] ),\\r\\n\\t\\t\\tinfo: { groups: groups },\\r\\n\\t\\t\\t_remap: new Uint32Array(vertex_remap)\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.transformMeshInfo( mesh, buffers, indicesArray );\\r\\n\\r\\n\\t\\treturn mesh;\\r\\n\\t},\\r\\n\\r\\n\\t//receives an array of \\r\\n\\treadTriangles: function( xmltriangles, sources )\\r\\n\\t{\\r\\n\\t\\tvar use_indices = false;\\r\\n\\r\\n\\t\\tvar groups = [];\\r\\n\\t\\tvar buffers = [];\\r\\n\\t\\tvar last_index = 0;\\r\\n\\t\\tvar facemap = {};\\r\\n\\t\\tvar vertex_remap = []; //maps DAE vertex index to Mesh vertex index (because when meshes are triangulated indices are changed\\r\\n\\t\\tvar indicesArray = [];\\r\\n\\t\\tvar last_start = 0;\\r\\n\\t\\tvar group_name = \\\"\\\";\\r\\n\\t\\tvar material_name = \\\"\\\";\\r\\n\\t\\tvar count = -1;\\r\\n\\r\\n\\t\\t//for every triangles set (warning, some times they are repeated...)\\r\\n\\t\\tfor(var tris = 0; tris < xmltriangles.length; tris++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xml_shape_root = xmltriangles.item(tris);\\r\\n\\r\\n\\t\\t\\tmaterial_name = xml_shape_root.getAttribute(\\\"material\\\");\\r\\n\\t\\t\\tcount = parseFloat( xml_shape_root.getAttribute(\\\"count\\\") );\\r\\n\\t\\t\\t//for each buffer (input) build the structure info\\r\\n\\t\\t\\tif(tris == 0)\\r\\n\\t\\t\\t\\tbuffers = this.readShapeInputs( xml_shape_root, sources );\\r\\n\\r\\n\\t\\t\\t//assuming buffers are ordered by offset\\r\\n\\r\\n\\t\\t\\t//iterate data\\r\\n\\t\\t\\tvar xmlps = xml_shape_root.querySelectorAll(\\\"p\\\");\\r\\n\\t\\t\\tvar num_data_vertex = buffers.length; //one value per input buffer\\r\\n\\r\\n\\t\\t\\t//compute data to read per vertex\\r\\n\\t\\t\\tvar num_values_per_vertex = 1;\\r\\n\\t\\t\\tvar buffers_length = buffers.length;\\r\\n\\t\\t\\tfor(var b = 0; b < buffers_length; ++b)\\r\\n\\t\\t\\t\\tnum_values_per_vertex = Math.max( num_values_per_vertex, buffers[b][4] + 1);\\r\\n\\r\\n\\t\\t\\t//for every polygon (could be one with all the indices, could be several, depends on the program)\\r\\n\\t\\t\\tfor(var i = 0; i < xmlps.length; i++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar xmlp = xmlps.item(i);\\r\\n\\t\\t\\t\\tif(!xmlp || !xmlp.textContent) \\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\r\\n\\t\\t\\t\\tvar data = xmlp.textContent.trim().split(\\\" \\\");\\r\\n\\r\\n\\t\\t\\t\\t//used for triangulate polys\\r\\n\\t\\t\\t\\tvar first_index = -1;\\r\\n\\t\\t\\t\\tvar current_index = -1;\\r\\n\\t\\t\\t\\tvar prev_index = -1;\\r\\n\\r\\n\\t\\t\\t\\t//for every pack of indices in the polygon (vertex, normal, uv, ... )\\r\\n\\t\\t\\t\\tfor(var k = 0, l = data.length; k < l; k += num_values_per_vertex)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar vertex_id = data.slice(k,k+num_values_per_vertex).join(\\\" \\\"); //generate unique id\\r\\n\\r\\n\\t\\t\\t\\t\\tprev_index = current_index;\\r\\n\\t\\t\\t\\t\\tif(facemap.hasOwnProperty(vertex_id)) //add to arrays, keep the index\\r\\n\\t\\t\\t\\t\\t\\tcurrent_index = facemap[vertex_id];\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t//for every data buffer associated to this vertex\\r\\n\\t\\t\\t\\t\\t\\tfor(var j = 0; j < buffers_length; ++j)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tvar buffer = buffers[j];\\r\\n\\t\\t\\t\\t\\t\\t\\tvar array = buffer[1]; //array where we accumulate the final data as we extract if from sources\\r\\n\\t\\t\\t\\t\\t\\t\\tvar source = buffer[3]; //where to read the data from\\r\\n\\t\\t\\t\\t\\t\\t\\t\\r\\n\\t\\t\\t\\t\\t\\t\\t//compute the index inside the data source array\\r\\n\\t\\t\\t\\t\\t\\t\\tvar index = parseInt( data[ k + buffer[4] ] );\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t//remember this index in case we need to remap\\r\\n\\t\\t\\t\\t\\t\\t\\tif(j == 0)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tvertex_remap[ array.length / buffer[2] ] = index; //not sure if buffer[2], it should be number of floats per vertex (usually 3)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t//compute the position inside the source buffer where the final data is located\\r\\n\\t\\t\\t\\t\\t\\t\\tindex *= buffer[2]; //this works in most DAEs (not all)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t//extract every value of this element and store it in its final array (every x,y,z, etc)\\r\\n\\t\\t\\t\\t\\t\\t\\tfor(var x = 0; x < buffer[2]; ++x)\\r\\n\\t\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t//if(source[index+x] === undefined) throw(\\\"UNDEFINED!\\\"); //DEBUG\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tarray.push( source[index+x] );\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\r\\n\\t\\t\\t\\t\\t\\tcurrent_index = last_index;\\r\\n\\t\\t\\t\\t\\t\\tlast_index += 1;\\r\\n\\t\\t\\t\\t\\t\\tfacemap[vertex_id] = current_index;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tindicesArray.push( current_index );\\r\\n\\t\\t\\t\\t}//per vertex\\r\\n\\t\\t\\t}//per triangle\\r\\n\\r\\n\\t\\t\\tvar group = {\\r\\n\\t\\t\\t\\tname: group_name || (\\\"group\\\" + tris),\\r\\n\\t\\t\\t\\tstart: last_start,\\r\\n\\t\\t\\t\\tlength: indicesArray.length - last_start,\\r\\n\\t\\t\\t\\tmaterial: material_name || \\\"\\\"\\r\\n\\t\\t\\t};\\r\\n\\t\\t\\tlast_start = indicesArray.length;\\r\\n\\t\\t\\tgroups.push( group );\\r\\n\\t\\t}//per triangles group\\r\\n\\r\\n\\t\\tif(!buffers.length)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar mesh = {\\r\\n\\t\\t\\tvertices: new Float32Array( buffers[0][1] ),\\r\\n\\t\\t\\tinfo: { groups: groups },\\r\\n\\t\\t\\t_remap: new Uint32Array(vertex_remap)\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.transformMeshInfo( mesh, buffers, indicesArray );\\r\\n\\r\\n\\t\\treturn mesh;\\r\\n\\t},\\r\\n\\r\\n\\treadPolylistArray: function( xml_polylist_array, sources )\\r\\n\\t{\\r\\n\\t\\tvar meshes = [];\\r\\n\\r\\n\\t\\tfor(var i = 0; i < xml_polylist_array.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xml_polylist = xml_polylist_array[i];\\r\\n\\t\\t\\tvar mesh = this.readPolylist( xml_polylist, sources );\\r\\n\\t\\t\\tif(mesh)\\r\\n\\t\\t\\t\\tmeshes.push( mesh );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//one or none\\r\\n\\t\\tif( meshes.length < 2)\\r\\n\\t\\t\\treturn meshes[0];\\r\\n\\r\\n\\t\\t//merge meshes\\r\\n\\t\\tvar mesh = this.mergeMeshes( meshes );\\r\\n\\t\\treturn mesh;\\r\\n\\t},\\r\\n\\r\\n\\treadPolylist: function( xml_polylist, sources )\\r\\n\\t{\\r\\n\\t\\tvar use_indices = false;\\r\\n\\r\\n\\t\\tvar groups = [];\\r\\n\\t\\tvar buffers = [];\\r\\n\\t\\tvar last_index = 0;\\r\\n\\t\\tvar facemap = {};\\r\\n\\t\\tvar vertex_remap = [];\\r\\n\\t\\tvar indicesArray = [];\\r\\n\\t\\tvar last_start = 0;\\r\\n\\t\\tvar group_name = \\\"\\\";\\r\\n\\t\\tvar material_name = \\\"\\\";\\r\\n\\r\\n\\t\\tmaterial_name = xml_polylist.getAttribute(\\\"material\\\") || \\\"\\\";\\r\\n\\t\\tbuffers = this.readShapeInputs( xml_polylist, sources );\\r\\n\\r\\n\\t\\tvar xmlvcount = xml_polylist.querySelector(\\\"vcount\\\");\\r\\n\\t\\tvar vcount = this.readContentAsUInt32( xmlvcount );\\r\\n\\r\\n\\t\\tvar xmlp = xml_polylist.querySelector(\\\"p\\\");\\r\\n\\t\\tvar data = this.readContentAsUInt32( xmlp );\\r\\n\\t\\tvar pos = 0;\\r\\n\\r\\n\\t\\tvar num_values_per_vertex = 1;\\r\\n\\t\\tvar buffers_length = buffers.length;\\r\\n\\t\\tfor(var b = 0; b < buffers_length; ++b)\\r\\n\\t\\t\\tnum_values_per_vertex = Math.max( num_values_per_vertex, buffers[b][4] + 1);\\r\\n\\r\\n\\t\\tfor(var i = 0, l = vcount.length; i < l; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar num_vertices = vcount[i];\\r\\n\\r\\n\\t\\t\\tvar first_index = -1;\\r\\n\\t\\t\\tvar current_index = -1;\\r\\n\\t\\t\\tvar prev_index = -1;\\r\\n\\r\\n\\t\\t\\tif( pos >= data.length )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"DAE has wrong number of polygons in polylist\\\");\\r\\n\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//iterate vertices of this polygon\\r\\n\\t\\t\\tfor(var k = 0; k < num_vertices; ++k)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar vertex_id = data.slice( pos, pos + num_values_per_vertex).join(\\\" \\\"); //generate unique id\\r\\n\\r\\n\\t\\t\\t\\tprev_index = current_index;\\r\\n\\t\\t\\t\\tif( facemap.hasOwnProperty( vertex_id ) ) //add to arrays, keep the index\\r\\n\\t\\t\\t\\t\\tcurrent_index = facemap[ vertex_id ];\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t//for vertex, normal, uv\\r\\n\\t\\t\\t\\t\\tfor(var j = 0; j < buffers_length; ++j)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar buffer = buffers[j];\\r\\n\\t\\t\\t\\t\\t\\tvar array = buffer[1]; //array with all the data\\r\\n\\t\\t\\t\\t\\t\\tvar source = buffer[3]; //where to read the data from\\r\\n\\r\\n\\t\\t\\t\\t\\t\\tvar index = parseInt( data[ pos + buffer[4] ] );\\r\\n\\r\\n\\t\\t\\t\\t\\t\\tif(j == 0)\\r\\n\\t\\t\\t\\t\\t\\t\\tvertex_remap[ array.length / buffer[2] ] = index; //not sure if buffer[2], it should be number of floats per vertex (usually 3)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t//compute the position inside the source buffer where the final data is located\\r\\n\\t\\t\\t\\t\\t\\tindex *= buffer[2]; //this works in most DAEs (not all)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t//extract every value of this element and store it in its final array (every x,y,z, etc)\\r\\n\\t\\t\\t\\t\\t\\tfor(var x = 0; x < buffer[2]; ++x)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t//if(source[index+x] === undefined) throw(\\\"UNDEFINED!\\\"); //DEBUG\\r\\n\\t\\t\\t\\t\\t\\t\\tarray.push( source[index+x] );\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\r\\n\\t\\t\\t\\t\\tcurrent_index = last_index;\\r\\n\\t\\t\\t\\t\\tlast_index += 1;\\r\\n\\t\\t\\t\\t\\tfacemap[vertex_id] = current_index;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif(num_vertices > 3) //split polygons then\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(k == 0)\\r\\n\\t\\t\\t\\t\\t\\tfirst_index = current_index;\\r\\n\\t\\t\\t\\t\\t//if(k > 2 * num_data_vertex) //not sure if use this or the next line, the next one works in some DAEs but not sure if it works in all\\r\\n\\t\\t\\t\\t\\tif(k > 2) //triangulate polygons: tested, this works\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tindicesArray.push( first_index );\\r\\n\\t\\t\\t\\t\\t\\tindicesArray.push( prev_index );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tindicesArray.push( current_index );\\r\\n\\t\\t\\t\\tpos += num_values_per_vertex;\\r\\n\\t\\t\\t}//per vertex\\r\\n\\t\\t}//per polygon\\r\\n\\r\\n\\t\\tvar mesh = {\\r\\n\\t\\t\\tvertices: new Float32Array( buffers[0][1] ),\\r\\n\\t\\t\\tinfo: {\\r\\n\\t\\t\\t\\tmaterial: material_name\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\t_remap: new Uint32Array( vertex_remap )\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tthis.transformMeshInfo( mesh, buffers, indicesArray );\\r\\n\\t\\treturn mesh;\\r\\n\\t},\\r\\n\\r\\n\\treadShapeInputs: function(xml_shape_root, sources)\\r\\n\\t{\\r\\n\\t\\tvar buffers = [];\\r\\n\\r\\n\\t\\tvar xmlinputs = xml_shape_root.querySelectorAll(\\\"input\\\");\\r\\n\\t\\tfor(var i = 0; i < xmlinputs.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xml_input = xmlinputs.item(i);\\r\\n\\t\\t\\tif(!xml_input.getAttribute) \\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar buffer = this.readInput( xml_input, sources );\\r\\n\\t\\t\\tif(buffer)\\r\\n\\t\\t\\t\\tbuffers.push(buffer);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tconsole.warn(\\\"no buffer in collada\\\");\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn buffers;\\r\\n\\t},\\r\\n\\r\\n\\treadInput: function( xml_input, sources )\\r\\n\\t{\\r\\n\\t\\tif(!xml_input.getAttribute) \\r\\n\\t\\t\\treturn null;\\r\\n\\t\\tvar source_name = xml_input.getAttribute(\\\"source\\\").substr(1);\\r\\n\\t\\tvar stream_source = null;\\r\\n\\t\\tif(source_name.indexOf(\\\"/\\\") != -1)\\r\\n\\t\\t{\\r\\n\\t\\t\\t//the source name uses ierarchical name\\r\\n\\t\\t\\tvar path = source_name.split(\\\"/\\\");\\r\\n\\t\\t\\tvar geometry_node = this._xmlroot.getElementById(path[0]);\\r\\n\\t\\t\\tif(geometry_node && geometry_node.sources) //little hack, we have already parsed vertices probably...\\r\\n\\t\\t\\t\\tstream_source = geometry_node.sources[ path[1] ];\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tstream_source = sources[ source_name ];\\r\\n\\r\\n\\t\\tif(!stream_source)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\" source not found: \\\" + source_name );\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar semantic = xml_input.getAttribute(\\\"semantic\\\").toUpperCase();\\r\\n\\t\\tvar offset = parseInt( xml_input.getAttribute(\\\"offset\\\") );\\r\\n\\t\\tvar data_set = 0;\\r\\n\\t\\tif(xml_input.getAttribute(\\\"set\\\"))\\r\\n\\t\\t\\tdata_set = parseInt( xml_input.getAttribute(\\\"set\\\") );\\r\\n\\t\\treturn [ semantic, [], stream_source.stride, stream_source.data, offset, data_set ];\\r\\n\\t},\\r\\n\\r\\n\\t//renames buffers to they match an standard imposed by the library\\r\\n\\ttransformMeshInfo: function( mesh, buffers, indicesArray )\\r\\n\\t{\\r\\n\\t\\t//rename buffers (DAE has other names)\\r\\n\\t\\tvar translator = {\\r\\n\\t\\t\\t\\\"normal\\\":\\\"normals\\\",\\r\\n\\t\\t\\t\\\"texcoord\\\":\\\"coords\\\"\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tfor(var i = 1; i < buffers.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar name = buffers[i][0].toLowerCase();\\r\\n\\t\\t\\tvar data = buffers[i][1];\\r\\n\\t\\t\\tif(!data.length)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tif(translator[name])\\r\\n\\t\\t\\t\\tname = translator[name];\\r\\n\\t\\t\\tif(mesh[name])\\r\\n\\t\\t\\t\\tname = name + buffers[i][5];\\r\\n\\t\\t\\tmesh[ name ] = new Float32Array(data); //are they always float32? I think so\\r\\n\\t\\t}\\r\\n\\t\\t\\r\\n\\t\\tif(indicesArray && indicesArray.length)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(mesh.vertices.length > 256*256)\\r\\n\\t\\t\\t\\tmesh.triangles = new Uint32Array(indicesArray);\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tmesh.triangles = new Uint16Array(indicesArray);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn mesh;\\r\\n\\t},\\r\\n\\r\\n\\treadLineStrip: function(sources, xmllinestrip)\\r\\n\\t{\\r\\n\\t\\tvar use_indices = false;\\r\\n\\r\\n\\t\\tvar buffers = [];\\r\\n\\t\\tvar last_index = 0;\\r\\n\\t\\tvar facemap = {};\\r\\n\\t\\tvar vertex_remap = [];\\r\\n\\t\\tvar indicesArray = [];\\r\\n\\t\\tvar last_start = 0;\\r\\n\\t\\tvar group_name = \\\"\\\";\\r\\n\\t\\tvar material_name = \\\"\\\";\\r\\n\\r\\n\\t\\tvar tris = 0; //used in case there are several strips\\r\\n\\r\\n\\t\\t//for each buffer (input) build the structure info\\r\\n\\t\\tvar xmlinputs = xmllinestrip.querySelectorAll(\\\"input\\\");\\r\\n\\t\\tif(tris == 0) //first iteration, create buffers\\r\\n\\t\\t\\tfor(var i = 0; i < xmlinputs.length; i++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar xmlinput = xmlinputs.item(i);\\r\\n\\t\\t\\t\\tif(!xmlinput.getAttribute) \\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tvar semantic = xmlinput.getAttribute(\\\"semantic\\\").toUpperCase();\\r\\n\\t\\t\\t\\tvar stream_source = sources[ xmlinput.getAttribute(\\\"source\\\").substr(1) ];\\r\\n\\t\\t\\t\\tvar offset = parseInt( xmlinput.getAttribute(\\\"offset\\\") );\\r\\n\\t\\t\\t\\tvar data_set = 0;\\r\\n\\t\\t\\t\\tif(xmlinput.getAttribute(\\\"set\\\"))\\r\\n\\t\\t\\t\\t\\tdata_set = parseInt( xmlinput.getAttribute(\\\"set\\\") );\\r\\n\\r\\n\\t\\t\\t\\tbuffers.push([semantic, [], stream_source.stride, stream_source.data, offset, data_set]);\\r\\n\\t\\t\\t}\\r\\n\\t\\t//assuming buffers are ordered by offset\\r\\n\\r\\n\\t\\t//iterate data\\r\\n\\t\\tvar xmlps = xmllinestrip.querySelectorAll(\\\"p\\\");\\r\\n\\t\\tvar num_data_vertex = buffers.length; //one value per input buffer\\r\\n\\r\\n\\t\\t//for every polygon (could be one with all the indices, could be several, depends on the program)\\r\\n\\t\\tfor(var i = 0; i < xmlps.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlp = xmlps.item(i);\\r\\n\\t\\t\\tif(!xmlp || !xmlp.textContent) \\r\\n\\t\\t\\t\\tbreak;\\r\\n\\r\\n\\t\\t\\tvar data = xmlp.textContent.trim().split(\\\" \\\");\\r\\n\\r\\n\\t\\t\\t//used for triangulate polys\\r\\n\\t\\t\\tvar first_index = -1;\\r\\n\\t\\t\\tvar current_index = -1;\\r\\n\\t\\t\\tvar prev_index = -1;\\r\\n\\r\\n\\t\\t\\t//if(use_indices && last_index >= 256*256)\\r\\n\\t\\t\\t//\\tbreak;\\r\\n\\r\\n\\t\\t\\t//for every pack of indices in the polygon (vertex, normal, uv, ... )\\r\\n\\t\\t\\tfor(var k = 0, l = data.length; k < l; k += num_data_vertex)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar vertex_id = data.slice(k,k+num_data_vertex).join(\\\" \\\"); //generate unique id\\r\\n\\r\\n\\t\\t\\t\\tprev_index = current_index;\\r\\n\\t\\t\\t\\tif(facemap.hasOwnProperty(vertex_id)) //add to arrays, keep the index\\r\\n\\t\\t\\t\\t\\tcurrent_index = facemap[vertex_id];\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tfor(var j = 0; j < buffers.length; ++j)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar buffer = buffers[j];\\r\\n\\t\\t\\t\\t\\t\\tvar index = parseInt(data[k + j]);\\r\\n\\t\\t\\t\\t\\t\\tvar array = buffer[1]; //array with all the data\\r\\n\\t\\t\\t\\t\\t\\tvar source = buffer[3]; //where to read the data from\\r\\n\\t\\t\\t\\t\\t\\tif(j == 0)\\r\\n\\t\\t\\t\\t\\t\\t\\tvertex_remap[ array.length / num_data_vertex ] = index;\\r\\n\\t\\t\\t\\t\\t\\tindex *= buffer[2]; //stride\\r\\n\\t\\t\\t\\t\\t\\tfor(var x = 0; x < buffer[2]; ++x)\\r\\n\\t\\t\\t\\t\\t\\t\\tarray.push( source[index+x] );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\r\\n\\t\\t\\t\\t\\tcurrent_index = last_index;\\r\\n\\t\\t\\t\\t\\tlast_index += 1;\\r\\n\\t\\t\\t\\t\\tfacemap[vertex_id] = current_index;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tindicesArray.push( current_index );\\r\\n\\t\\t\\t}//per vertex\\r\\n\\t\\t}//per polygon\\r\\n\\r\\n\\t\\tvar mesh = {\\r\\n\\t\\t\\tprimitive: \\\"line_strip\\\",\\r\\n\\t\\t\\tvertices: new Float32Array( buffers[0][1] ),\\r\\n\\t\\t\\tinfo: {}\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\treturn this.transformMeshInfo( mesh, buffers, indicesArray );\\r\\n\\t},\\r\\n\\r\\n\\t//like querySelector but allows spaces in names because COLLADA allows space in names\\r\\n\\tfindXMLNodeById: function(root, nodename, id)\\r\\n\\t{\\r\\n\\t\\t//precomputed\\r\\n\\t\\tif( this._xmlroot._nodes_by_id )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar n = this._xmlroot._nodes_by_id[ id ];\\r\\n\\t\\t\\tif( n && n.localName == nodename)\\r\\n\\t\\t\\t\\treturn n;\\r\\n\\t\\t}\\r\\n\\t\\telse //for the native parser\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar n = this._xmlroot.getElementById( id );\\r\\n\\t\\t\\tif(n)\\r\\n\\t\\t\\t\\treturn n;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//recursive: slow\\r\\n\\t\\tvar childs = root.childNodes;\\r\\n\\t\\tfor(var i = 0; i < childs.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlnode = childs.item(i);\\r\\n\\t\\t\\tif(xmlnode.nodeType != 1 ) //no tag\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif(xmlnode.localName != nodename)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar node_id = xmlnode.getAttribute(\\\"id\\\");\\r\\n\\t\\t\\tif(node_id == id)\\r\\n\\t\\t\\t\\treturn xmlnode;\\r\\n\\t\\t}\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\treadImages: function(root)\\r\\n\\t{\\r\\n\\t\\tvar xmlimages = root.querySelector(\\\"library_images\\\");\\r\\n\\t\\tif(!xmlimages)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar images = {};\\r\\n\\r\\n\\t\\tvar xmlimages_childs = xmlimages.childNodes;\\r\\n\\t\\tfor(var i = 0; i < xmlimages_childs.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlimage = xmlimages_childs.item(i);\\r\\n\\t\\t\\tif(xmlimage.nodeType != 1 ) //no tag\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar xmlinitfrom = xmlimage.querySelector(\\\"init_from\\\");\\r\\n\\t\\t\\tif(!xmlinitfrom)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif(xmlinitfrom.textContent)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar filename = this.getFilename( xmlinitfrom.textContent );\\r\\n\\t\\t\\t\\tvar id = xmlimage.getAttribute(\\\"id\\\");\\r\\n\\t\\t\\t\\timages[id] = { filename: filename, map: id, name: xmlimage.getAttribute(\\\"name\\\"), path: xmlinitfrom.textContent };\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn images;\\r\\n\\t},\\r\\n\\r\\n\\treadAnimations: function(root, scene)\\r\\n\\t{\\r\\n\\t\\tvar xmlanimations = root.querySelector(\\\"library_animations\\\");\\r\\n\\t\\tif(!xmlanimations)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar xmlanimation_childs = xmlanimations.childNodes;\\r\\n\\r\\n\\t\\tvar animations = {\\r\\n\\t\\t\\tobject_class: \\\"Animation\\\",\\r\\n\\t\\t\\ttakes: {}\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tvar default_take = { tracks: [] };\\r\\n\\t\\tvar tracks = default_take.tracks;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < xmlanimation_childs.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlanimation = xmlanimation_childs.item(i);\\r\\n\\t\\t\\tif(xmlanimation.nodeType != 1 || xmlanimation.localName != \\\"animation\\\") //no tag\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar anim_id = xmlanimation.getAttribute(\\\"id\\\");\\r\\n\\t\\t\\tif(!anim_id) //nested animation (DAE 1.5)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar xmlanimation2_childs = xmlanimation.querySelectorAll(\\\"animation\\\");\\r\\n\\t\\t\\t\\tif(xmlanimation2_childs.length)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tfor(var j = 0; j < xmlanimation2_childs.length; ++j)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar xmlanimation2 = xmlanimation2_childs.item(j);\\r\\n\\t\\t\\t\\t\\t\\tthis.readAnimation( xmlanimation2, tracks );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse //source tracks?\\r\\n\\t\\t\\t\\t\\tthis.readAnimation( xmlanimation, tracks );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse //no nested (DAE 1.4)\\r\\n\\t\\t\\t\\tthis.readAnimation( xmlanimation, tracks );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!tracks.length) \\r\\n\\t\\t\\treturn null; //empty animation\\r\\n\\r\\n\\t\\t//compute animation duration\\r\\n\\t\\tvar max_time = 0;\\r\\n\\t\\tfor(var i = 0; i < tracks.length; ++i)\\r\\n\\t\\t\\tif( max_time < tracks[i].duration )\\r\\n\\t\\t\\t\\tmax_time = tracks[i].duration;\\r\\n\\r\\n\\t\\tdefault_take.name = \\\"default\\\";\\r\\n\\t\\tdefault_take.duration = max_time;\\r\\n\\t\\tanimations.takes[ default_take.name ] = default_take;\\r\\n\\t\\treturn animations;\\r\\n\\t},\\r\\n\\r\\n\\t//animation xml\\r\\n\\treadAnimation: function( xmlanimation, result )\\r\\n\\t{\\r\\n\\t\\tif(xmlanimation.localName != \\\"animation\\\")\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\t//this could be missing when there are lots of anims packed in one \\r\\n\\t\\tvar anim_id = xmlanimation.getAttribute(\\\"id\\\");\\r\\n\\r\\n\\t\\t//channels are like animated properties\\r\\n\\t\\tvar xmlchannel_list = xmlanimation.querySelectorAll(\\\"channel\\\");\\r\\n\\t\\tif(!xmlchannel_list.length)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar tracks = result || [];\\r\\n\\r\\n\\t\\tfor(var i = 0; i < xmlchannel_list.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar anim = this.readChannel( xmlchannel_list.item(i), xmlanimation );\\r\\n\\t\\t\\tif(anim)\\r\\n\\t\\t\\t\\ttracks.push( anim );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn tracks;\\r\\n\\t},\\r\\n\\r\\n\\treadChannel: function( xmlchannel, xmlanimation )\\r\\n\\t{\\r\\n\\t\\tif(xmlchannel.localName != \\\"channel\\\" || xmlanimation.localName != \\\"animation\\\")\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar source = xmlchannel.getAttribute(\\\"source\\\");\\r\\n\\t\\tvar target = xmlchannel.getAttribute(\\\"target\\\");\\r\\n\\r\\n\\t\\t//sampler, is in charge of the interpolation\\r\\n\\t\\t//var xmlsampler = xmlanimation.querySelector(\\\"sampler\\\" + source);\\r\\n\\t\\tvar xmlsampler = this.findXMLNodeById( xmlanimation, \\\"sampler\\\", source.substr(1) );\\r\\n\\t\\tif(!xmlsampler)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"Error DAE: Sampler not found in \\\" + source);\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar inputs = {};\\r\\n\\t\\tvar params = {};\\r\\n\\t\\tvar sources = {};\\r\\n\\t\\tvar xmlinputs = xmlsampler.querySelectorAll(\\\"input\\\");\\r\\n\\r\\n\\t\\tvar time_data = null;\\r\\n\\r\\n\\t\\t//iterate inputs: collada separates the keyframe info in independent streams, like time, interpolation method, value )\\r\\n\\t\\tfor(var j = 0; j < xmlinputs.length; j++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlinput = xmlinputs.item(j);\\r\\n\\t\\t\\tvar source_name = xmlinput.getAttribute(\\\"source\\\");\\r\\n\\r\\n\\t\\t\\t//there are three \\r\\n\\t\\t\\tvar semantic = xmlinput.getAttribute(\\\"semantic\\\");\\r\\n\\r\\n\\t\\t\\t//Search for source\\r\\n\\t\\t\\tvar xmlsource = this.findXMLNodeById( xmlanimation, \\\"source\\\", source_name.substr(1) );\\r\\n\\t\\t\\tif(!xmlsource)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar xmlparam = xmlsource.querySelector(\\\"param\\\");\\r\\n\\t\\t\\tif(!xmlparam)\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar type = xmlparam.getAttribute(\\\"type\\\");\\r\\n\\t\\t\\tinputs[ semantic ] = { source: source_name, type: type };\\r\\n\\r\\n\\t\\t\\tvar data_array = null;\\r\\n\\r\\n\\t\\t\\tif(type == \\\"float\\\" || type == \\\"float4x4\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar xmlfloatarray = xmlsource.querySelector(\\\"float_array\\\");\\r\\n\\t\\t\\t\\tvar floats = this.readContentAsFloats( xmlfloatarray );\\r\\n\\t\\t\\t\\tsources[ source_name ] = floats;\\r\\n\\t\\t\\t\\tdata_array = floats;\\r\\n\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse //only floats and matrices are supported in animation\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar param_name = xmlparam.getAttribute(\\\"name\\\");\\r\\n\\t\\t\\tif(param_name == \\\"TIME\\\")\\r\\n\\t\\t\\t\\ttime_data = data_array;\\r\\n\\t\\t\\tif(semantic == \\\"OUTPUT\\\")\\r\\n\\t\\t\\t\\tparam_name = semantic;\\r\\n\\t\\t\\tif(param_name)\\r\\n\\t\\t\\t\\tparams[ param_name ] = type;\\r\\n\\t\\t\\telse\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Collada: without name attribute in \\\");\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!time_data)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"Error DAE: no TIME info found in : \\\" + xmlchannel.getAttribute(\\\"source\\\") );\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//construct animation\\r\\n\\t\\tvar path = target.split(\\\"/\\\");\\r\\n\\r\\n\\t\\tvar anim = {};\\r\\n\\t\\tvar nodename = this.safeString( path[0] ); //safeString ?\\r\\n\\t\\tvar node = this._nodes_by_id[ nodename ];\\r\\n\\t\\tif(!node)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"Node target '\\\" + nodename + \\\"' not found reading animation channel\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\t\\tvar locator = node.id + \\\"/\\\" + path[1];\\r\\n\\t\\t//anim.nodename = this.safeString( path[0] ); //where it goes\\r\\n\\t\\tanim.name = path[1];\\r\\n\\t\\tanim.property = locator;\\r\\n\\t\\tvar type = \\\"number\\\";\\r\\n\\t\\tvar element_size = 1;\\r\\n\\t\\tvar param_type = params[\\\"OUTPUT\\\"];\\r\\n\\t\\tswitch(param_type)\\r\\n\\t\\t{\\r\\n\\t\\t\\tcase \\\"float\\\": element_size = 1; break;\\r\\n\\t\\t\\tcase \\\"float3x3\\\": element_size = 9; type = \\\"mat3\\\"; break;\\r\\n\\t\\t\\tcase \\\"float4x4\\\": element_size = 16; type = \\\"mat4\\\"; break;\\r\\n\\t\\t\\tdefault: break;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tanim.type = type;\\r\\n\\t\\tanim.value_size = element_size;\\r\\n\\t\\tanim.duration = time_data[ time_data.length - 1]; //last sample\\r\\n\\r\\n\\t\\tvar value_data = sources[ inputs[\\\"OUTPUT\\\"].source ];\\r\\n\\t\\tif(!value_data)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\t//Pack data ****************\\r\\n\\t\\tvar num_samples = time_data.length;\\r\\n\\t\\tvar sample_size = element_size + 1;\\r\\n\\t\\tvar anim_data = new Float32Array( num_samples * sample_size );\\r\\n\\t\\t//for every sample\\r\\n\\t\\tfor(var j = 0; j < time_data.length; ++j)\\r\\n\\t\\t{\\r\\n\\t\\t\\tanim_data[j * sample_size] = time_data[j]; //set time\\r\\n\\t\\t\\tvar value = value_data.subarray( j * element_size, (j+1) * element_size );\\r\\n\\t\\t\\tif(param_type == \\\"float4x4\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis.transformMatrix( value, node ? node._depth == 0 : 0 );\\r\\n\\t\\t\\t\\t//mat4.transpose(value, value);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tanim_data.set(value, j * sample_size + 1); //set data\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(isWorker && this.use_transferables)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar data = anim_data;\\r\\n\\t\\t\\tif(data && data.buffer && data.length > 100)\\r\\n\\t\\t\\t\\tthis._transferables.push(data.buffer);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tanim.data = anim_data;\\r\\n\\t\\treturn anim;\\r\\n\\t},\\r\\n\\r\\n\\tfindNode: function(root, id)\\r\\n\\t{\\r\\n\\t\\tif(root.id == id) return root;\\r\\n\\t\\tif(root.children)\\r\\n\\t\\t\\tfor(var i in root.children)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar ret = this.findNode(root.children[i], id);\\r\\n\\t\\t\\t\\tif(ret) return ret;\\r\\n\\t\\t\\t}\\r\\n\\t\\treturn null;\\r\\n\\t},\\r\\n\\r\\n\\t//reads controllers and stores them in \\r\\n\\treadLibraryControllers: function( scene )\\r\\n\\t{\\r\\n\\t\\tvar xmllibrarycontrollers = this._xmlroot.querySelector(\\\"library_controllers\\\");\\r\\n\\t\\tif(!xmllibrarycontrollers)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar xmllibrarycontrollers_childs = xmllibrarycontrollers.childNodes;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < xmllibrarycontrollers_childs.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlcontroller = xmllibrarycontrollers_childs.item(i);\\r\\n\\t\\t\\tif(xmlcontroller.nodeType != 1 || xmlcontroller.localName != \\\"controller\\\") //no tag\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tvar id = xmlcontroller.getAttribute(\\\"id\\\");\\r\\n\\t\\t\\t//we have already processed this controller\\r\\n\\t\\t\\tif( this._controllers_found[ id ] )\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t//read it (we wont use the returns, we will get it from this._controllers_found\\r\\n\\t\\t\\tthis.readController( xmlcontroller, null, scene );\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t//used for skinning and morphing\\r\\n\\treadController: function( xmlcontroller, flip, scene )\\r\\n\\t{\\r\\n\\t\\tif(!xmlcontroller.localName == \\\"controller\\\")\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"readController: not a controller: \\\" + xmlcontroller.localName);\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar id = xmlcontroller.getAttribute(\\\"id\\\");\\r\\n\\r\\n\\t\\t//use cached\\r\\n\\t\\tif( this._controllers_found[ id ] )\\r\\n\\t\\t\\treturn this._controllers_found[ id ];\\r\\n\\r\\n\\t\\tvar use_indices = false;\\r\\n\\t\\tvar mesh = null;\\r\\n\\t\\tvar xmlskin = xmlcontroller.querySelector(\\\"skin\\\");\\r\\n\\t\\tif(xmlskin) {\\r\\n\\t\\t\\tmesh = this.readSkinController( xmlskin, flip, scene);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar xmlmorph = xmlcontroller.querySelector(\\\"morph\\\");\\r\\n\\t\\tif(xmlmorph)\\r\\n\\t\\t\\tmesh = this.readMorphController( xmlmorph, flip, scene, mesh );\\r\\n\\r\\n\\t\\t//cache and return\\r\\n\\t\\tthis._controllers_found[ id ] = mesh;\\r\\n\\r\\n\\t\\treturn mesh;\\r\\n\\t},\\r\\n\\r\\n\\t//read this to more info about DAE and skinning https://collada.org/mediawiki/index.php/Skinning\\r\\n\\treadSkinController: function( xmlskin, flip, scene )\\r\\n\\t{\\r\\n\\t\\t//base geometry\\r\\n\\t\\tvar id_geometry = xmlskin.getAttribute(\\\"source\\\");\\r\\n\\r\\n\\r\\n\\t\\tvar mesh = this.readGeometry( id_geometry, flip, scene );\\r\\n\\t\\tif(!mesh)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar sources = this.readSources(xmlskin, flip);\\r\\n\\t\\tif(!sources)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\t//matrix\\r\\n\\t\\tvar bind_matrix = null;\\r\\n\\t\\tvar xmlbindmatrix = xmlskin.querySelector(\\\"bind_shape_matrix\\\");\\r\\n\\t\\tif(xmlbindmatrix)\\r\\n\\t\\t{\\r\\n\\t\\t\\tbind_matrix = this.readContentAsFloats( xmlbindmatrix );\\r\\n\\t\\t\\tthis.transformMatrix(bind_matrix, true, true );\\t\\t\\t\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t\\tbind_matrix = mat4.create(); //identity\\r\\n\\r\\n\\t\\t//joints\\r\\n\\t\\tvar joints = [];\\r\\n\\t\\tvar xmljoints = xmlskin.querySelector(\\\"joints\\\");\\r\\n\\t\\tif(xmljoints)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar joints_source = null; //which bones\\r\\n\\t\\t\\tvar inv_bind_source = null; //bind matrices\\r\\n\\t\\t\\tvar xmlinputs = xmljoints.querySelectorAll(\\\"input\\\");\\r\\n\\t\\t\\tfor(var i = 0; i < xmlinputs.length; i++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar xmlinput = xmlinputs[i];\\r\\n\\t\\t\\t\\tvar sem = xmlinput.getAttribute(\\\"semantic\\\").toUpperCase();\\r\\n\\t\\t\\t\\tvar src = xmlinput.getAttribute(\\\"source\\\");\\r\\n\\t\\t\\t\\tvar source = sources[ src.substr(1) ];\\r\\n\\t\\t\\t\\tif(sem == \\\"JOINT\\\")\\r\\n\\t\\t\\t\\t\\tjoints_source = source;\\r\\n\\t\\t\\t\\telse if(sem == \\\"INV_BIND_MATRIX\\\")\\r\\n\\t\\t\\t\\t\\tinv_bind_source = source;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//save bone names and inv matrix\\r\\n\\t\\t\\tif(!inv_bind_source || !joints_source)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.error(\\\"Error DAE: no joints or inv_bind sources found\\\");\\r\\n\\t\\t\\t\\treturn null;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tfor(var i = 0; i < joints_source.length; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//get the inverse of the bind pose\\r\\n\\t\\t\\t\\tvar inv_mat = inv_bind_source.subarray(i*16,i*16+16);\\r\\n\\t\\t\\t\\tvar nodename = joints_source[i];\\r\\n\\t\\t\\t\\tvar node = this._nodes_by_id[ nodename ];\\r\\n\\t\\t\\t\\tif(!node)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"Node \\\" + nodename + \\\" not found\\\");\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tthis.transformMatrix(inv_mat, node._depth == 0, true );\\r\\n\\t\\t\\t\\tjoints.push([ nodename, inv_mat ]);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar too_many_bones = 0;\\r\\n\\t\\tvar all_bones = [];\\r\\n\\r\\n\\t\\t//weights\\r\\n\\t\\tvar xmlvertexweights = xmlskin.querySelector(\\\"vertex_weights\\\");\\r\\n\\t\\tif(xmlvertexweights)\\r\\n\\t\\t{\\r\\n\\r\\n\\t\\t\\t//here we see the order \\r\\n\\t\\t\\tvar weights_indexed_array = null;\\r\\n\\t\\t\\tvar xmlinputs = xmlvertexweights.querySelectorAll(\\\"input\\\");\\r\\n\\t\\t\\tfor(var i = 0; i < xmlinputs.length; i++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( xmlinputs[i].getAttribute(\\\"semantic\\\").toUpperCase() == \\\"WEIGHT\\\" )\\r\\n\\t\\t\\t\\t\\tweights_indexed_array = sources[ xmlinputs.item(i).getAttribute(\\\"source\\\").substr(1) ];\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(!weights_indexed_array)\\r\\n\\t\\t\\t\\tthrow(\\\"no weights found\\\");\\r\\n\\r\\n\\t\\t\\tvar xmlvcount = xmlvertexweights.querySelector(\\\"vcount\\\");\\r\\n\\t\\t\\tvar vcount = this.readContentAsUInt32( xmlvcount );\\r\\n\\r\\n\\t\\t\\tvar xmlv = xmlvertexweights.querySelector(\\\"v\\\");\\r\\n\\t\\t\\tvar v = this.readContentAsUInt32( xmlv );\\r\\n\\r\\n\\t\\t\\tvar num_vertices = mesh.vertices.length / 3; //3 components per vertex\\r\\n\\t\\t\\tvar weights_array = new Float32Array(4 * num_vertices); //4 bones per vertex\\r\\n\\t\\t\\tvar bone_index_array = new Uint8Array(4 * num_vertices); //4 bones per vertex\\r\\n\\r\\n\\t\\t\\tvar pos = 0;\\r\\n\\t\\t\\tvar remap = mesh._remap;\\r\\n\\t\\t\\tif(!remap)\\r\\n\\t\\t\\t\\tthrow(\\\"no remap info found in mesh\\\");\\r\\n\\t\\t\\tvar max_bone = 0; //max bone affected\\r\\n\\r\\n\\t\\t\\t//for every bone affecting this vertex\\r\\n\\t\\t\\tfor(var i = 0, l = vcount.length; i < l; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar num_bones = vcount[i]; //num bones influencing this vertex\\r\\n\\t\\t\\t\\tvar offset = pos;\\r\\n\\r\\n\\t\\t\\t\\t//get 4 most important bones\\r\\n\\t\\t\\t\\tall_bones.length = num_bones;\\r\\n\\t\\t\\t\\tfor(var j = 0; j < num_bones; ++j)\\r\\n\\t\\t\\t\\t\\tall_bones[j] = [ weights_indexed_array[ v[offset + j*2 + 1] ], v[offset + j*2] ]; //[weight,bone_index]\\r\\n\\t\\t\\t\\tall_bones.sort( this._bones_sort_func );\\r\\n\\t\\t\\t\\tif( all_bones.length > 4 )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tall_bones.length = 4; //remove extra bones\\r\\n\\t\\t\\t\\t\\ttoo_many_bones += 1;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tvar b = bone_index_array.subarray(i*4, i*4 + 4);\\r\\n\\t\\t\\t\\tvar w = weights_array.subarray(i*4, i*4 + 4);\\r\\n\\r\\n\\t\\t\\t\\tvar sum = 0; //check total weight of selected bones (after skipping some because of the 4 bones limit)\\r\\n\\t\\t\\t\\tfor(var j = 0; j < all_bones.length; ++j)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tb[j] = all_bones[j][1];\\r\\n\\t\\t\\t\\t\\tif(b[j] > max_bone)\\r\\n\\t\\t\\t\\t\\t\\tmax_bone = b[j];\\r\\n\\t\\t\\t\\t\\tw[j] = all_bones[j][0];\\r\\n\\t\\t\\t\\t\\tsum += w[j];\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t//normalize weights after removing some\\r\\n\\t\\t\\t\\tif(sum < 1.0)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar inv_sum = 1/sum;\\r\\n\\t\\t\\t\\t\\tfor(var j = 0; j < 4; ++j)\\r\\n\\t\\t\\t\\t\\t\\tw[j] *= inv_sum;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tpos += num_bones * 2;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(too_many_bones)\\r\\n\\t\\t\\t\\tconsole.warn(\\\"This mesh has \\\"+too_many_bones+\\\" vertices with more than 4 bones, skipping extra bones. This could cause errors in the skinning.\\\");\\r\\n\\r\\n\\r\\n\\t\\t\\t//remap: because vertices order is now changed after parsing the mesh\\r\\n\\t\\t\\tvar final_weights = new Float32Array(4 * num_vertices); //4 bones per vertex\\r\\n\\t\\t\\tvar final_bone_indices = new Uint8Array(4 * num_vertices); //4 bones per vertex\\r\\n\\t\\t\\tvar used_joints = [];\\r\\n\\r\\n\\t\\t\\t//for every vertex in the mesh, process bone indices and weights\\r\\n\\t\\t\\tfor(var i = 0; i < num_vertices; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar p = remap[ i ] * 4;\\r\\n\\t\\t\\t\\tvar w = weights_array.subarray(p,p+4);\\r\\n\\t\\t\\t\\tvar b = bone_index_array.subarray(p,p+4);\\r\\n\\r\\n\\t\\t\\t\\t//sort by weight so relevant ones goes first\\r\\n\\t\\t\\t\\tfor(var k = 0; k < 3; ++k)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar max_pos = k;\\r\\n\\t\\t\\t\\t\\tvar max_value = w[k];\\r\\n\\t\\t\\t\\t\\tfor(var j = k+1; j < 4; ++j)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tif(w[j] <= max_value)\\r\\n\\t\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\t\\tmax_pos = j;\\r\\n\\t\\t\\t\\t\\t\\tmax_value = w[j];\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tif(max_pos != k)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar tmp = w[k];\\r\\n\\t\\t\\t\\t\\t\\tw[k] = w[max_pos];\\r\\n\\t\\t\\t\\t\\t\\tw[max_pos] = tmp;\\r\\n\\t\\t\\t\\t\\t\\ttmp = b[k];\\r\\n\\t\\t\\t\\t\\t\\tb[k] = b[max_pos]; \\r\\n\\t\\t\\t\\t\\t\\tb[max_pos] = tmp;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t//store\\r\\n\\t\\t\\t\\tfinal_weights.set( w, i*4);\\r\\n\\t\\t\\t\\tfinal_bone_indices.set( b, i*4);\\r\\n\\r\\n\\t\\t\\t\\t//mark bones used\\r\\n\\t\\t\\t\\tif(w[0]) used_joints[b[0]] = true;\\r\\n\\t\\t\\t\\tif(w[1]) used_joints[b[1]] = true;\\r\\n\\t\\t\\t\\tif(w[2]) used_joints[b[2]] = true;\\r\\n\\t\\t\\t\\tif(w[3]) used_joints[b[3]] = true;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(max_bone >= joints.length)\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Mesh uses higher bone index than bones found\\\");\\r\\n\\r\\n\\t\\t\\t//trim unused bones (collada could give you 100 bones for an object that only uses a fraction of them)\\r\\n\\t\\t\\tif(1)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar new_bones = [];\\r\\n\\t\\t\\t\\tvar bones_translation = {};\\r\\n\\t\\t\\t\\tfor(var i = 0; i < used_joints.length; ++i)\\r\\n\\t\\t\\t\\t\\tif(used_joints[i])\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tbones_translation[i] = new_bones.length;\\r\\n\\t\\t\\t\\t\\t\\tnew_bones.push( joints[i] );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t//in case there are less bones in use...\\r\\n\\t\\t\\t\\tif(new_bones.length < joints.length)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t//remap\\r\\n\\t\\t\\t\\t\\tfor(var i = 0; i < final_bone_indices.length; i++)\\r\\n\\t\\t\\t\\t\\t\\tfinal_bone_indices[i] = bones_translation[ final_bone_indices[i] ];\\r\\n\\t\\t\\t\\t\\tjoints = new_bones;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t//console.log(\\\"Bones: \\\", joints.length, \\\" used:\\\", num_used_joints );\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//check bone names are correct\\r\\n\\t\\t\\tfor(var i = 0; i < joints.length; ++i)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar joint = joints[i];\\r\\n\\t\\t\\t\\tvar bone_node = this._nodes_by_id[ joint[0] ] || this._nodes_by_sid[ joint[0] ];\\r\\n\\t\\t\\t\\tif(bone_node)\\r\\n\\t\\t\\t\\t\\tjoint[0] = bone_node.id || bone_node.name;\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"Bone not found\\\");\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//console.log(\\\"Bones: \\\", joints.length, \\\"Max bone: \\\", max_bone);\\r\\n\\r\\n\\t\\t\\tmesh.weights = final_weights;\\r\\n\\t\\t\\tmesh.bone_indices = final_bone_indices;\\r\\n\\t\\t\\tmesh.bones = joints;\\r\\n\\t\\t\\tmesh.bind_matrix = bind_matrix;\\r\\n\\r\\n\\t\\t\\t//delete mesh[\\\"_remap\\\"];\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn mesh;\\r\\n\\t},\\r\\n\\r\\n\\t_bones_sort_func: function(a,b)\\r\\n\\t{\\r\\n\\t\\treturn b[0] - a[0];\\r\\n\\t},\\r\\n\\r\\n\\t//NOT TESTED\\r\\n\\treadMorphController: function(xmlmorph, flip, scene, mesh)\\r\\n\\t{\\r\\n\\t\\tvar id_geometry = xmlmorph.getAttribute(\\\"source\\\");\\r\\n\\t\\tvar base_mesh = this.readGeometry( id_geometry, flip, scene );\\r\\n\\t\\tif(!base_mesh)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\t//read sources with blend shapes info (which ones, and the weight)\\r\\n\\t\\tvar sources = this.readSources(xmlmorph, flip);\\r\\n\\r\\n\\t\\tvar morphs = [];\\r\\n\\r\\n\\t\\t//targets\\r\\n\\t\\tvar xmltargets = xmlmorph.querySelector(\\\"targets\\\");\\r\\n\\t\\tif(!xmltargets)\\r\\n\\t\\t\\treturn null;\\r\\n\\r\\n\\t\\tvar xmlinputs = xmltargets.querySelectorAll(\\\"input\\\");\\r\\n\\t\\tvar targets = null;\\r\\n\\t\\tvar weights = null;\\r\\n\\r\\n\\t\\tfor(var i = 0; i < xmlinputs.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlinput = xmlinputs.item(i);\\r\\n\\t\\t\\tvar semantic = xmlinput.getAttribute(\\\"semantic\\\").toUpperCase();\\r\\n\\t\\t\\tvar data = sources[ xmlinput.getAttribute(\\\"source\\\").substr(1) ];\\r\\n\\t\\t\\tif( semantic == \\\"MORPH_TARGET\\\" )\\r\\n\\t\\t\\t\\ttargets = data;\\r\\n\\t\\t\\telse if( semantic == \\\"MORPH_WEIGHT\\\" )\\r\\n\\t\\t\\t\\tweights = data;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!targets || !weights)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.warn(\\\"Morph controller without targets or weights. Skipping it.\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//get targets\\r\\n\\t\\tfor(var i in targets)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar id = \\\"#\\\" + targets[i];\\r\\n\\t\\t\\tvar geometry = this.readGeometry( id, flip, scene );\\r\\n\\t\\t\\tscene.meshes[ id ] = geometry;\\r\\n\\t\\t\\tmorphs.push( { mesh: id, weight: weights[i]} );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tbase_mesh.morph_targets = morphs;\\r\\n\\t\\treturn base_mesh;\\r\\n\\t},\\r\\n\\r\\n\\treadBindMaterials: function( xmlbind_material, mesh )\\r\\n\\t{\\r\\n\\t\\tvar materials = [];\\r\\n\\r\\n\\t\\tvar xmltechniques = xmlbind_material.querySelectorAll(\\\"technique_common\\\");\\r\\n\\t\\tfor(var i = 0; i < xmltechniques.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmltechnique = xmltechniques.item(i);\\r\\n\\t\\t\\tvar xmlinstance_materials = xmltechnique.querySelectorAll(\\\"instance_material\\\");\\r\\n\\t\\t\\tfor(var j = 0; j < xmlinstance_materials.length; j++)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar xmlinstance_material = xmlinstance_materials.item(j);\\r\\n\\t\\t\\t\\tif(xmlinstance_material)\\r\\n\\t\\t\\t\\t\\tmaterials.push( xmlinstance_material.getAttribute(\\\"symbol\\\") );\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn materials;\\r\\n\\t},\\r\\n\\r\\n\\treadSources: function(xmlnode, flip)\\r\\n\\t{\\r\\n\\t\\t//for data sources\\r\\n\\t\\tvar sources = {};\\r\\n\\t\\tvar xmlsources = xmlnode.querySelectorAll(\\\"source\\\");\\r\\n\\t\\tfor(var i = 0; i < xmlsources.length; i++)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar xmlsource = xmlsources.item(i);\\r\\n\\t\\t\\tif(!xmlsource.querySelector) //??\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar float_array = xmlsource.querySelector(\\\"float_array\\\");\\r\\n\\t\\t\\tif(float_array)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar floats = this.readContentAsFloats( xmlsource );\\r\\n\\t\\t\\t\\tsources[ xmlsource.getAttribute(\\\"id\\\") ] = floats;\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar name_array = xmlsource.querySelector(\\\"Name_array\\\");\\r\\n\\t\\t\\tif(name_array)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar names = this.readContentAsStringsArray( name_array );\\r\\n\\t\\t\\t\\tif(!names)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tsources[ xmlsource.getAttribute(\\\"id\\\") ] = names;\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar ref_array = xmlsource.querySelector(\\\"IDREF_array\\\");\\r\\n\\t\\t\\tif(ref_array)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar names = this.readContentAsStringsArray( ref_array );\\r\\n\\t\\t\\t\\tif(!names)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tsources[ xmlsource.getAttribute(\\\"id\\\") ] = names;\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn sources;\\r\\n\\t},\\r\\n\\r\\n\\treadContentAsUInt32: function(xmlnode)\\r\\n\\t{\\r\\n\\t\\tif(!xmlnode) return null;\\r\\n\\t\\tvar text = xmlnode.textContent;\\r\\n\\t\\ttext = text.replace(/\\\\n/gi, \\\" \\\"); //remove line breaks\\r\\n\\t\\ttext = text.replace(/\\\\s\\\\s+/gi, \\\" \\\"); //remove double spaces\\r\\n\\t\\ttext = text.trim(); //remove empty spaces\\r\\n\\t\\tif(text.length == 0) return null;\\r\\n\\t\\tvar numbers = text.split(\\\" \\\"); //create array\\r\\n\\t\\t//numbers.filter(function(v){ return v != \\\"\\\"; }); //remove break lines that become empty values\\r\\n\\t\\tvar floats = new Uint32Array( numbers.length );\\r\\n\\t\\tfor(var k = 0; k < numbers.length; k++)\\r\\n\\t\\t\\tfloats[k] = parseInt( numbers[k] );\\r\\n\\t\\treturn floats;\\r\\n\\t},\\r\\n\\r\\n\\treadContentAsFloats: function(xmlnode)\\r\\n\\t{\\r\\n\\t\\tif(!xmlnode) return null;\\r\\n\\t\\tvar text = xmlnode.textContent;\\r\\n\\t\\ttext = text.replace(/\\\\n/gi, \\\" \\\"); //remove line breaks\\r\\n\\t\\ttext = text.replace(/\\\\s\\\\s+/gi, \\\" \\\"); //remove double spaces\\r\\n\\t\\ttext = text.replace(/\\\\t/gi, \\\"\\\");\\r\\n\\t\\ttext = text.trim(); //remove empty spaces\\r\\n\\t\\tvar numbers = text.split(\\\" \\\"); //create array\\r\\n\\t\\t//numbers.filter(function(v){ return v != \\\"\\\"; }); //remove break lines that become empty values\\r\\n\\t\\tvar count = xmlnode.getAttribute(\\\"count\\\"); //WARNING: this is misleading, count is not the number of floats, in an array [1,0,0] if could say 1 because its 1 vertex\\r\\n\\t\\tvar length = numbers.length; //count ? parseInt( count ) : numbers.length;\\r\\n\\t\\tvar floats = new Float32Array( length );\\r\\n\\t\\tfor(var k = 0; k < numbers.length; k++)\\r\\n\\t\\t\\tfloats[k] = parseFloat( numbers[k] );\\r\\n\\t\\treturn floats;\\r\\n\\t},\\r\\n\\t\\r\\n\\treadContentAsStringsArray: function(xmlnode)\\r\\n\\t{\\r\\n\\t\\tif(!xmlnode) return null;\\r\\n\\t\\tvar text = xmlnode.textContent;\\r\\n\\t\\ttext = text.replace(/\\\\n/gi, \\\" \\\"); //remove line breaks\\r\\n\\t\\ttext = text.replace(/\\\\s\\\\s/gi, \\\" \\\");\\r\\n\\t\\ttext = text.trim(); //remove empty spaces\\r\\n\\t\\tvar words = text.split(\\\" \\\"); //create array\\r\\n\\t\\tfor(var k = 0; k < words.length; k++)\\r\\n\\t\\t\\twords[k] = words[k].trim();\\r\\n\\t\\tif(xmlnode.getAttribute(\\\"count\\\") && parseInt(xmlnode.getAttribute(\\\"count\\\")) != words.length)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar merged_words = [];\\r\\n\\t\\t\\tvar name = \\\"\\\";\\r\\n\\t\\t\\tfor (var i in words)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(!name)\\r\\n\\t\\t\\t\\t\\tname = words[i];\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tname += \\\" \\\" + words[i];\\r\\n\\t\\t\\t\\tif(!this._nodes_by_id[ this.safeString(name) ])\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tmerged_words.push( this.safeString(name) );\\r\\n\\t\\t\\t\\tname = \\\"\\\";\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tvar count = parseInt(xmlnode.getAttribute(\\\"count\\\"));\\r\\n\\t\\t\\tif(merged_words.length == count)\\r\\n\\t\\t\\t\\treturn merged_words;\\r\\n\\r\\n\\t\\t\\tconsole.error(\\\"Error: bone names have spaces, avoid using spaces in names\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\t\\treturn words;\\r\\n\\t},\\r\\n\\r\\n\\tmax3d_matrix_0: new Float32Array([0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, -0, 0, 0, 0, 1]),\\r\\n\\t//max3d_matrix_other: new Float32Array([0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, -0, 0, 0, 0, 1]),\\r\\n\\r\\n\\ttransformMatrix: function(matrix, first_level, inverted)\\r\\n\\t{\\r\\n\\t\\tmat4.transpose(matrix,matrix);\\r\\n\\r\\n\\t\\tif(this.no_flip)\\r\\n\\t\\t\\treturn matrix;\\r\\n\\r\\n\\t\\t//WARNING: DO NOT CHANGE THIS FUNCTION, THE SKY WILL FALL\\r\\n\\t\\tif(first_level){\\r\\n\\r\\n\\t\\t\\t//flip row two and tree\\r\\n\\t\\t\\tvar temp = new Float32Array(matrix.subarray(4,8)); //swap rows\\r\\n\\t\\t\\tmatrix.set( matrix.subarray(8,12), 4 );\\r\\n\\t\\t\\tmatrix.set( temp, 8 );\\r\\n\\r\\n\\t\\t\\t//reverse Z\\r\\n\\t\\t\\ttemp = matrix.subarray(8,12);\\r\\n\\t\\t\\tvec4.scale(temp,temp,-1);\\r\\n\\t\\t}\\r\\n\\t\\telse \\r\\n\\t\\t{\\r\\n\\t\\t\\tvar M = mat4.create();\\r\\n\\t\\t\\tvar m = matrix;\\r\\n\\r\\n\\t\\t\\t//if(inverted) mat4.invert(m,m);\\r\\n\\r\\n\\t\\t\\t/* non trasposed\\r\\n\\t\\t\\tM.set([m[0],m[8],-m[4]], 0);\\r\\n\\t\\t\\tM.set([m[2],m[10],-m[6]], 4);\\r\\n\\t\\t\\tM.set([-m[1],-m[9],m[5]], 8);\\r\\n\\t\\t\\tM.set([m[3],m[11],-m[7]], 12);\\r\\n\\t\\t\\t*/\\r\\n\\r\\n\\t\\t\\tM.set([m[0],m[2],-m[1]], 0);\\r\\n\\t\\t\\tM.set([m[8],m[10],-m[9]], 4);\\r\\n\\t\\t\\tM.set([-m[4],-m[6],m[5]], 8);\\r\\n\\t\\t\\tM.set([m[12],m[14],-m[13]], 12);\\r\\n\\r\\n\\t\\t\\tm.set(M);\\r\\n\\r\\n\\t\\t\\t//if(inverted) mat4.invert(m,m);\\r\\n\\r\\n\\t\\t}\\r\\n\\t\\treturn matrix;\\r\\n\\t},\\r\\n\\r\\n\\tmergeMeshes: function( meshes, options )\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\r\\n\\t\\tvar vertex_buffers = {};\\r\\n\\t\\tvar index_buffers = {};\\r\\n\\t\\tvar offsets = {}; //tells how many positions indices must be offseted\\r\\n\\t\\tvar vertex_offsets = [];\\r\\n\\t\\tvar current_vertex_offset = 0;\\r\\n\\t\\tvar groups = [];\\r\\n\\r\\n\\t\\tvar index_buffer_names = {\\r\\n\\t\\t\\ttriangles: true,\\r\\n\\t\\t\\twireframe: true\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tvar remap = null;\\r\\n\\t\\tvar remap_offset = 0;\\r\\n\\r\\n\\t\\t//vertex buffers\\r\\n\\t\\t//compute size\\r\\n\\t\\tfor(var i = 0; i < meshes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mesh = meshes[i];\\r\\n\\t\\t\\tvar offset = current_vertex_offset;\\r\\n\\t\\t\\tvertex_offsets.push( offset );\\r\\n\\t\\t\\tvar length = mesh.vertices.length / 3;\\r\\n\\t\\t\\tcurrent_vertex_offset += length;\\r\\n\\r\\n\\t\\t\\tfor(var j in mesh)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar buffer = mesh[j];\\r\\n\\r\\n\\t\\t\\t\\tif( j == \\\"info\\\" || j == \\\"_remap\\\" )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tif( index_buffer_names[j] )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(!index_buffers[j])\\r\\n\\t\\t\\t\\t\\t\\tindex_buffers[j] = buffer.length;\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\tindex_buffers[j] += buffer.length;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(!vertex_buffers[j])\\r\\n\\t\\t\\t\\t\\t\\tvertex_buffers[j] = buffer.length;\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\tvertex_buffers[j] += buffer.length;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//groups\\r\\n\\t\\t\\tvar group = {\\r\\n\\t\\t\\t\\tname: \\\"mesh_\\\" + ( mesh.info.material || i ),\\r\\n\\t\\t\\t\\tstart: offset,\\r\\n\\t\\t\\t\\tlength: length,\\r\\n\\t\\t\\t\\tmaterial: ( mesh.info.material || \\\"\\\" )\\r\\n\\t\\t\\t};\\r\\n\\r\\n\\t\\t\\tgroups.push( group );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//allocate\\r\\n\\t\\tfor(var j in vertex_buffers)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar datatype = options[j];\\r\\n\\t\\t\\tif(datatype === null)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tdelete vertex_buffers[j];\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif(!datatype)\\r\\n\\t\\t\\t\\tdatatype = Float32Array;\\r\\n\\r\\n\\t\\t\\tvertex_buffers[j] = new datatype( vertex_buffers[j] );\\r\\n\\t\\t\\toffsets[j] = 0;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor(var j in index_buffers)\\r\\n\\t\\t{\\r\\n\\t\\t\\tindex_buffers[j] = new Uint32Array( index_buffers[j] );\\r\\n\\t\\t\\toffsets[j] = 0;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//store\\r\\n\\t\\tfor(var i = 0; i < meshes.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mesh = meshes[i];\\r\\n\\t\\t\\tvar offset = 0;\\r\\n\\r\\n\\t\\t\\tvar buffer = mesh.vertices;\\r\\n\\t\\t\\tif(!buffer)\\r\\n\\t\\t\\t\\treturn console.error(\\\"mesh without vertices\\\");\\r\\n\\t\\t\\tvar length = buffer.length / 3;\\r\\n\\t\\t\\t\\r\\n\\t\\t\\tfor(var j in mesh)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar buffer = mesh[j];\\r\\n\\t\\t\\t\\tif( j == \\\"info\\\")\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tif(j == \\\"_remap\\\")\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(remap_offset)\\r\\n\\t\\t\\t\\t\\t\\tapply_offset( buffer, 0, buffer.length, remap_offset );\\r\\n\\r\\n\\t\\t\\t\\t\\tif(!remap)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tremap = new Uint32Array( buffer.length );\\r\\n\\t\\t\\t\\t\\t\\tremap.set( buffer );\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar new_remap = new Uint32Array( remap.length + buffer.length );\\r\\n\\t\\t\\t\\t\\t\\tnew_remap.set( remap );\\r\\n\\t\\t\\t\\t\\t\\tnew_remap.set( buffer, remap.length );\\r\\n\\t\\t\\t\\t\\t\\tremap = new_remap;\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tremap_offset += length;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t//INDEX BUFFER\\r\\n\\t\\t\\t\\tif( index_buffer_names[j] )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tindex_buffers[j].set( buffer, offsets[j] );\\r\\n\\t\\t\\t\\t\\tapply_offset( index_buffers[j], offsets[j], buffer.length, vertex_offsets[i] );\\r\\n\\t\\t\\t\\t\\toffsets[j] += buffer.length;\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t//VERTEX BUFFER\\r\\n\\t\\t\\t\\tif(!vertex_buffers[j])\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tvertex_buffers[j].set( buffer, offsets[j] );\\r\\n\\t\\t\\t\\toffsets[j] += buffer.length;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfunction apply_offset( array, start, length, offset )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar l = start + length;\\r\\n\\t\\t\\tfor(var i = start; i < l; ++i)\\r\\n\\t\\t\\t\\tarray[i] += offset;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar extra = { info: { groups: groups } };\\r\\n\\t\\tvar final_mesh = { info: { groups: groups } };\\r\\n\\t\\tfor(var i in vertex_buffers)\\r\\n\\t\\t\\tfinal_mesh[i] = vertex_buffers[i];\\r\\n\\t\\tfor(var i in index_buffers)\\r\\n\\t\\t\\tfinal_mesh[i] = index_buffers[i];\\r\\n\\r\\n\\t\\tif( remap )\\r\\n\\t\\t\\tfinal_mesh._remap = remap;\\r\\n\\t\\treturn final_mesh;\\r\\n\\t}\\r\\n};\\r\\n\\r\\nvar Collada = global.Collada;\\r\\n\\r\\n//add worker launcher\\r\\nif(!isWorker)\\r\\n{\\r\\n\\tCollada.launchWorker = function()\\r\\n\\t{\\r\\n\\t\\tvar worker = this.worker = new Worker( Collada.workerPath + \\\"collada.js\\\" );\\r\\n\\t\\tworker.callback_ids = {};\\r\\n\\r\\n\\t\\tworker.addEventListener('error', function(e){\\r\\n\\t\\t\\tif (Collada.onerror)\\r\\n\\t\\t\\t\\tCollada.onerror(err);\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\t//main thread receives a message from worker\\r\\n\\t\\tworker.addEventListener('message', function(e) {\\r\\n\\t\\t\\tif(!e.data)\\r\\n\\t\\t\\t\\treturn;\\r\\n\\r\\n\\t\\t\\tvar data = e.data;\\r\\n\\r\\n\\t\\t\\tswitch(data.action)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tcase \\\"log\\\": console.log.apply( console, data.params ); break;\\r\\n\\t\\t\\t\\tcase \\\"warn\\\": console.warn.apply( console, data.params ); break;\\r\\n\\t\\t\\t\\tcase \\\"exception\\\": \\r\\n\\t\\t\\t\\t\\tconsole.error.apply( console, data.params ); \\r\\n\\t\\t\\t\\t\\tif(Collada.onerror)\\r\\n\\t\\t\\t\\t\\t\\tCollada.onerror(data.msg);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tcase \\\"error\\\": console.error.apply( console, data.params ); break;\\r\\n\\t\\t\\t\\tcase \\\"result\\\": \\r\\n\\t\\t\\t\\t\\tvar callback = this.callback_ids[ data.callback_id ];\\r\\n\\t\\t\\t\\t\\tif(!callback)\\r\\n\\t\\t\\t\\t\\t\\tthrow(\\\"callback not found\\\");\\r\\n\\t\\t\\t\\t\\tcallback( data.result );\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\tconsole.warn(\\\"Unknown action:\\\", data.action);\\r\\n\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t}\\r\\n\\t\\t});\\r\\n\\r\\n\\t\\tthis.callback_ids = {};\\r\\n\\t\\tthis.last_callback_id = 1;\\r\\n\\r\\n\\t\\tthis.toWorker(\\\"init\\\", [this.config] );\\r\\n\\t}\\r\\n\\r\\n\\tCollada.toWorker = function( func_name, params, callback )\\r\\n\\t{\\r\\n\\t\\tif(!this.worker)\\r\\n\\t\\t\\tthis.launchWorker();\\r\\n\\r\\n\\t\\tvar id = this.last_callback_id++;\\r\\n\\t\\tthis.worker.callback_ids[ id ] = callback;\\r\\n\\t\\tthis.worker.postMessage({ func: func_name, params: params, callback_id: id });\\r\\n\\t}\\r\\n\\r\\n\\tCollada.loadInWorker = function( url, callback )\\r\\n\\t{\\r\\n\\t\\tthis.toWorker(\\\"loadInWorker\\\", [url], callback );\\r\\n\\t}\\r\\n\\r\\n\\tCollada.parseInWorker = function( data, callback )\\r\\n\\t{\\r\\n\\t\\tthis.toWorker(\\\"parseInWorker\\\", [data], callback );\\r\\n\\t}\\r\\n\\r\\n}\\r\\nelse //in worker\\r\\n{\\r\\n\\tCollada.loadInWorker = function(callback, url) { \\r\\n\\t\\tCollada.load(url, callback);\\r\\n\\t}\\r\\n\\r\\n\\tCollada.parseInWorker = function(callback, data) { \\r\\n\\t\\tcallback( Collada.parse(data) );\\r\\n\\t}\\r\\n}\\r\\n\\r\\n\\r\\nfunction request(url, callback)\\r\\n{\\r\\n\\tvar req = new XMLHttpRequest();\\r\\n\\treq.onload = function() {\\r\\n\\t\\tvar response = this.response;\\r\\n\\t\\tif(this.status != 200)\\r\\n\\t\\t\\treturn;\\r\\n\\t\\tif(callback)\\r\\n\\t\\t\\tcallback(this.response);\\r\\n\\t};\\r\\n\\tif(url.indexOf(\\\"://\\\") == -1)\\r\\n\\t\\turl = Collada.dataPath + url;\\r\\n\\treq.open(\\\"get\\\", url, true);\\r\\n\\treq.send();\\r\\n}\\r\\n\\r\\n//global event catcher\\r\\nif(isWorker)\\r\\n{\\r\\n\\tself.addEventListener('message', function(e) {\\r\\n\\r\\n\\t\\tif(e.data.func == \\\"init\\\")\\r\\n\\t\\t\\treturn Collada.init.apply( Collada, e.data.params );\\r\\n\\r\\n\\t\\tvar func_name = e.data.func;\\r\\n\\t\\tvar params = e.data.params;\\r\\n\\t\\tvar callback_id = e.data.callback_id;\\r\\n\\r\\n\\t\\t//callback when the work is done\\r\\n\\t\\tvar callback = function(result){\\r\\n\\t\\t\\tself.postMessage({action:\\\"result\\\", callback_id: callback_id, result: result}, Collada._transferables );\\r\\n\\t\\t\\tCollada._transferables = null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar func = Collada[func_name];\\r\\n\\r\\n\\t\\tif( func === undefined)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"function not found:\\\", func_name);\\r\\n\\t\\t\\tcallback(null);\\r\\n\\t\\t}\\r\\n\\t\\telse\\r\\n\\t\\t{\\r\\n\\t\\t\\ttry\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfunc.apply( Collada, params ? [callback].concat(params) : [callback]);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcatch (err)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.error(\\\"Error inside worker function call to \\\" + func_name + \\\" :: \\\" + err);\\r\\n\\t\\t\\t\\tcallback(null);\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t}, false);\\r\\n}\\r\\n\\r\\n//})( typeof(window) != \\\"undefined\\\" ? window : self );\\r\\n})( typeof(window) != \\\"undefined\\\" ? window : ( typeof(exports) != \\\"undefined\\\" ? exports : self ) );\\r\\n\\r\\n\\r\\n///@FILE:../src/parsers/parserDAE.js\\r\\n///@INFO: PARSER\\r\\nvar parserDAE = {\\r\\n\\textension: \\\"dae\\\",\\r\\n\\ttype: \\\"scene\\\",\\r\\n\\tresource: \\\"SceneNode\\\",\\r\\n\\tformat: \\\"text\\\",\\r\\n\\tdataType:'text',\\r\\n\\r\\n\\tconvert_filenames_to_lowercase: true,\\r\\n\\r\\n\\tparse: function( data, options, filename )\\r\\n\\t{\\r\\n\\t\\tif(!data || data.constructor !== String)\\r\\n\\t\\t{\\r\\n\\t\\t\\tconsole.error(\\\"DAE parser requires string\\\");\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tCollada.material_translate_table = {\\r\\n\\t\\t\\treflectivity: \\\"reflection_factor\\\",\\r\\n\\t\\t\\tspecular: \\\"specular_factor\\\",\\r\\n\\t\\t\\tshininess: \\\"specular_gloss\\\",\\r\\n\\t\\t\\temission: \\\"emissive\\\",\\r\\n\\t\\t\\tdiffuse: \\\"color\\\"\\r\\n\\t\\t}; //this is done to match LS specification\\r\\n\\r\\n\\t\\tvar clean_filename = LS.RM.getFilename( filename );\\r\\n\\r\\n\\t\\t//parser moved to Collada.js library\\r\\n\\t\\tvar scene = Collada.parse( data, options, clean_filename );\\r\\n\\t\\tconsole.log( scene ); \\r\\n\\r\\n\\t\\tscene.root.name = clean_filename;\\r\\n\\r\\n\\t\\t//apply 90 degrees rotation to match the Y UP AXIS of the system\\r\\n\\t\\tif( scene.metadata && scene.metadata.up_axis == \\\"Z_UP\\\" )\\r\\n\\t\\t\\tscene.root.model = mat4.rotateX( mat4.create(), mat4.create(), -90 * 0.0174532925 );\\r\\n\\r\\n\\t\\t//rename meshes, nodes, etc\\r\\n\\t\\tvar renamed = {};\\r\\n\\t\\tvar basename = clean_filename.substr(0, clean_filename.indexOf(\\\".\\\"));\\r\\n\\r\\n\\t\\t//rename meshes names\\r\\n\\t\\tvar renamed_meshes = {};\\r\\n\\t\\tfor(var i in scene.meshes)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar newmeshname = basename + \\\"__\\\" + i + \\\".wbin\\\";\\r\\n\\t\\t\\tnewmeshname = newmeshname.replace(/[^a-z0-9\\\\.\\\\-]/gi,\\\"_\\\"); //newmeshname.replace(/ /#/g,\\\"_\\\");\\r\\n\\t\\t\\trenamed[ i ] = newmeshname;\\r\\n\\t\\t\\trenamed_meshes[ newmeshname ] = scene.meshes[i];\\r\\n\\t\\t}\\r\\n\\t\\tscene.meshes = renamed_meshes;\\r\\n\\r\\n\\t\\tfor(var i in scene.meshes)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mesh = scene.meshes[i];\\r\\n\\t\\t\\tthis.processMesh( mesh, renamed );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//change local collada ids to valid uids \\r\\n\\t\\tinner_replace_names( scene.root );\\r\\n\\r\\n\\t\\tfunction inner_replace_names( node )\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(node.id == \\\"root\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tconsole.warn(\\\"DAE contains a node named root, renamed to _root\\\");\\r\\n\\t\\t\\t\\tnode.id = \\\"_root\\\";\\r\\n\\t\\t\\t\\trenamed[\\\"root\\\"] = node.id;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//change uid\\r\\n\\t\\t\\tif(node.id && !options.skip_renaming )\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnode.uid = \\\"@\\\" + basename + \\\"::\\\" + node.id;\\r\\n\\t\\t\\t\\trenamed[ node.id ] = node.uid;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t\\r\\n\\t\\t\\t//in case the node has some kind of type\\r\\n\\t\\t\\tif(node.type)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tnode.node_type = node.type;\\r\\n\\t\\t\\t\\tdelete node.type; //to be sure it doesnt overlaps with some existing var\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//rename materials\\r\\n\\t\\t\\tif(node.material)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar new_name = node.material.replace(/[^a-z0-9\\\\.\\\\-]/gi,\\\"_\\\") + \\\".json\\\";\\r\\n\\t\\t\\t\\trenamed[ node.material ] = new_name\\r\\n\\t\\t\\t\\tnode.material = new_name;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif(node.materials)\\r\\n\\t\\t\\t\\tfor(var i in node.materials)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar new_name = node.materials[i].replace(/[^a-z0-9\\\\.\\\\-]/gi,\\\"_\\\") + \\\".json\\\";\\r\\n\\t\\t\\t\\t\\trenamed[ node.material ] = new_name\\r\\n\\t\\t\\t\\t\\tnode.materials[i] = new_name;\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//change mesh names to engine friendly ids\\r\\n\\t\\t\\tif(node.meshes)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var i = 0; i < node.meshes.length; i++)\\r\\n\\t\\t\\t\\t\\tif(node.meshes[i] && renamed[ node.meshes[i] ])\\r\\n\\t\\t\\t\\t\\t\\tnode.meshes[i] = renamed[ node.meshes[i] ];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif(node.mesh && renamed[ node.mesh ])\\r\\n\\t\\t\\t\\tnode.mesh = renamed[ node.mesh ];\\r\\n\\r\\n\\t\\t\\tif(node.children)\\r\\n\\t\\t\\t\\tfor(var i in node.children)\\r\\n\\t\\t\\t\\t\\tinner_replace_names( node.children[i] );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//replace skinning joint ids\\r\\n\\t\\tfor(var i in scene.meshes)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mesh = scene.meshes[i];\\r\\n\\t\\t\\tif(mesh.bones)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var j in mesh.bones)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar id = mesh.bones[j][0];\\r\\n\\t\\t\\t\\t\\tvar uid = renamed[ id ];\\r\\n\\t\\t\\t\\t\\tif(uid)\\r\\n\\t\\t\\t\\t\\t\\tmesh.bones[j][0] = uid;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//replace animation name\\r\\n\\t\\tif(\\tscene.root.animation )\\r\\n\\t\\t\\tscene.root.animation = this.renameResource( scene.root.animation, scene.root.animation + \\\".wbin\\\", scene.resources );\\r\\n\\r\\n\\t\\t//Materials need some renames\\r\\n\\t\\tvar renamed_materials = {};\\r\\n\\t\\tfor(var i in scene.materials)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar mat = scene.materials[i];\\r\\n\\t\\t\\tthis.processMaterial( mat );\\r\\n\\t\\t\\trenamed_materials[ mat.id ] = mat;\\r\\n\\t\\t\\t//this.renameResource( i, mat.id, scene.resources ); //materials are not stored in the resources container\\r\\n\\t\\t}\\r\\n\\t\\tscene.materials = renamed_materials;\\r\\n\\r\\n\\t\\t//check resources\\r\\n\\t\\tfor(var i in scene.resources)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar res = scene.resources[i];\\r\\n\\t\\t\\tvar ext = LS.ResourcesManager.getBasename( i );\\r\\n\\t\\t\\tif(!ext)\\r\\n\\t\\t\\t\\tconsole.warn(\\\"DAE contains resources without extension: \\\" + i, res.constructor );\\r\\n\\t\\t\\tif(res.object_class == \\\"Animation\\\")\\r\\n\\t\\t\\t\\tthis.processAnimation( res, renamed );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn scene;\\r\\n\\t},\\r\\n\\r\\n\\trenameResource: function( old_name, new_name, resources )\\r\\n\\t{\\r\\n\\t\\tvar res = resources[ old_name ];\\r\\n\\t\\tif(!res)\\r\\n\\t\\t{\\r\\n\\t\\t\\tif(!resources[ new_name ])\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Resource not found: \\\" + old_name );\\r\\n\\t\\t\\treturn new_name;\\r\\n\\t\\t}\\r\\n\\t\\tdelete resources[ old_name ];\\r\\n\\t\\tresources[ new_name ] = res;\\r\\n\\t\\tres.filename = new_name;\\r\\n\\t\\treturn new_name;\\r\\n\\t},\\r\\n\\r\\n\\tprocessMesh: function( mesh, renamed )\\r\\n\\t{\\r\\n\\t\\tif(!mesh.vertices)\\r\\n\\t\\t\\treturn; //mesh without vertices?!\\r\\n\\r\\n\\t\\tvar num_vertices = mesh.vertices.length / 3;\\r\\n\\t\\tvar num_coords = mesh.coords ? mesh.coords.length / 2 : 0;\\r\\n\\r\\n\\t\\tif(num_coords && num_coords != num_vertices )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar old_coords = mesh.coords;\\r\\n\\t\\t\\tvar new_coords = new Float32Array( num_vertices * 2 );\\r\\n\\r\\n\\t\\t\\tif(num_coords > num_vertices) //check that UVS have 2 components (MAX export 3 components for UVs)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tfor(var i = 0; i < num_vertices; ++i )\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tnew_coords[i*2] = old_coords[i*3];\\r\\n\\t\\t\\t\\t\\tnew_coords[i*2+1] = old_coords[i*3+1];\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tmesh.coords = new_coords;\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//rename morph targets names\\r\\n\\t\\tif(mesh.morph_targets)\\r\\n\\t\\t\\tfor(var j = 0; j < mesh.morph_targets.length; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar morph = mesh.morph_targets[j];\\r\\n\\t\\t\\t\\tif(morph.mesh && renamed[ morph.mesh ])\\r\\n\\t\\t\\t\\t\\tmorph.mesh = renamed[ morph.mesh ];\\r\\n\\t\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\t//depending on the 3D software used, animation tracks could be tricky to handle\\r\\n\\tprocessAnimation: function( animation, renamed )\\r\\n\\t{\\r\\n\\t\\tfor(var i in animation.takes)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar take = animation.takes[i];\\r\\n\\r\\n\\t\\t\\t//apply renaming\\r\\n\\t\\t\\tfor(var j = 0; j < take.tracks.length; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar track = take.tracks[j];\\r\\n\\t\\t\\t\\tvar pos = track.property.indexOf(\\\"/\\\");\\r\\n\\t\\t\\t\\tif(!pos)\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\tvar nodename = track.property.substr(0,pos);\\r\\n\\t\\t\\t\\tvar extra = track.property.substr(pos);\\r\\n\\t\\t\\t\\tif(extra == \\\"/transform\\\") //blender exports matrices as transform\\r\\n\\t\\t\\t\\t\\textra = \\\"/matrix\\\";\\r\\n\\r\\n\\t\\t\\t\\tif( !renamed[nodename] )\\r\\n\\t\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\t\\tnodename = renamed[ nodename ];\\r\\n\\t\\t\\t\\ttrack.property = nodename + extra;\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t//rotations could come in different ways, some of them are accumulative, which doesnt work in litescene, so we have to accumulate them previously\\r\\n\\t\\t\\tvar rotated_nodes = {};\\r\\n\\t\\t\\tfor(var j = 0; j < take.tracks.length; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar track = take.tracks[j];\\r\\n\\t\\t\\t\\ttrack.packed_data = true; //hack: this is how it works my loader\\r\\n\\t\\t\\t\\tif(track.name == \\\"rotateX.ANGLE\\\" || track.name == \\\"rotateY.ANGLE\\\" || track.name == \\\"rotateZ.ANGLE\\\")\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar nodename = track.property.split(\\\"/\\\")[0];\\r\\n\\t\\t\\t\\t\\tif(!rotated_nodes[nodename])\\r\\n\\t\\t\\t\\t\\t\\trotated_nodes[nodename] = { tracks: [] };\\r\\n\\t\\t\\t\\t\\trotated_nodes[nodename].tracks.push( track );\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tfor(var j in rotated_nodes)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar info = rotated_nodes[j];\\r\\n\\t\\t\\t\\tvar newtrack = { data: [], type: \\\"quat\\\", value_size: 4, property: j + \\\"/Transform/rotation\\\", name: \\\"rotation\\\" };\\r\\n\\t\\t\\t\\tvar times = [];\\r\\n\\r\\n\\t\\t\\t\\t//collect timestamps\\r\\n\\t\\t\\t\\tfor(var k = 0; k < info.tracks.length; ++k)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar track = info.tracks[k];\\r\\n\\t\\t\\t\\t\\tvar data = track.data;\\r\\n\\t\\t\\t\\t\\tfor(var w = 0; w < data.length; w+=2)\\r\\n\\t\\t\\t\\t\\t\\ttimes.push( data[w] );\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t//create list of timestamps and remove repeated ones\\r\\n\\t\\t\\t\\ttimes.sort();\\r\\n\\t\\t\\t\\tvar last_time = -1;\\r\\n\\t\\t\\t\\tvar final_times = [];\\r\\n\\t\\t\\t\\tfor(var k = 0; k < times.length; ++k)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tif(times[k] == last_time)\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\tfinal_times.push( times[k] );\\r\\n\\t\\t\\t\\t\\tlast_time = times[k];\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\ttimes = final_times;\\r\\n\\r\\n\\t\\t\\t\\t//create samples\\r\\n\\t\\t\\t\\tnewtrack.data.length = times.length;\\r\\n\\t\\t\\t\\tfor(var k = 0; k < newtrack.data.length; ++k)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar time = times[k];\\r\\n\\t\\t\\t\\t\\tvar value = quat.create();\\r\\n\\t\\t\\t\\t\\t//create keyframe\\r\\n\\t\\t\\t\\t\\tnewtrack.data[k] = [time, value];\\r\\n\\r\\n\\t\\t\\t\\t\\tfor(var w = 0; w < info.tracks.length; ++w)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tvar track = info.tracks[w];\\r\\n\\t\\t\\t\\t\\t\\tvar sample = getTrackSample( track, time );\\r\\n\\t\\t\\t\\t\\t\\tif(!sample) //nothing to do if no sample or 0\\r\\n\\t\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\t\\tsample *= 0.0174532925; //degrees to radians\\r\\n\\t\\t\\t\\t\\t\\tswitch( track.name )\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tcase \\\"rotateX.ANGLE\\\": quat.rotateX( value, value, -sample ); break;\\r\\n\\t\\t\\t\\t\\t\\t\\tcase \\\"rotateY.ANGLE\\\": quat.rotateY( value, value, sample ); break;\\r\\n\\t\\t\\t\\t\\t\\t\\tcase \\\"rotateZ.ANGLE\\\": quat.rotateZ( value, value, sample ); break;\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t//add track\\r\\n\\t\\t\\t\\ttake.tracks.push( newtrack );\\r\\n\\r\\n\\t\\t\\t\\t//remove old rotation tracks\\r\\n\\t\\t\\t\\tfor(var w = 0; w < info.tracks.length; ++w)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar track = info.tracks[w];\\r\\n\\t\\t\\t\\t\\tvar pos = take.tracks.indexOf( track );\\r\\n\\t\\t\\t\\t\\tif(pos == -1)\\r\\n\\t\\t\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\t\\t\\ttake.tracks.splice(pos,1);\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t}//takes\\r\\n\\r\\n\\t\\tfunction getTrackSample( track, time )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar data = track.data;\\r\\n\\t\\t\\tvar l = data.length;\\r\\n\\t\\t\\tfor(var t = 0; t < l; t+=2)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(data[t] == time)\\r\\n\\t\\t\\t\\t\\treturn data[t+1];\\r\\n\\t\\t\\t\\tif(data[t] > time)\\r\\n\\t\\t\\t\\t\\treturn null;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\tprocessMaterial: function(material)\\r\\n\\t{\\r\\n\\t\\tvar rename_channels = {\\r\\n\\t\\t\\tspecular_factor: \\\"specular\\\",\\r\\n\\t\\t\\ttransparent: \\\"opacity\\\"\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tmaterial.object_class = \\\"StandardMaterial\\\";\\r\\n\\t\\tif(material.id)\\r\\n\\t\\t\\tmaterial.id = material.id.replace(/[^a-z0-9\\\\.\\\\-]/gi,\\\"_\\\") + \\\".json\\\";\\r\\n\\r\\n\\t\\tif( material.transparency !== undefined )\\r\\n\\t\\t{\\r\\n\\t\\t\\tmaterial.opacity = 1.0; //fuck it\\r\\n\\t\\t\\t//I have no idea how to parse the transparency info from DAEs...\\r\\n\\t\\t\\t//https://github.com/openscenegraph/OpenSceneGraph/blob/master/src/osgPlugins/dae/daeRMaterials.cpp#L1185\\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\tmaterial.opacity = 1.0 - parseFloat( material.transparency );\\r\\n\\t\\t\\tif( material.opaque_info == \\\"RGB_ZERO\\\")\\r\\n\\t\\t\\t\\tmaterial.opacity = 1.0 - parseFloat( material.transparent[0] ); //use the red channel\\r\\n\\t\\t\\t*/\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//collada supports materials with colors as specular_factor but StandardMaterial only support one value\\r\\n\\t\\tif(material.specular_factor && material.specular_factor.length)\\r\\n\\t\\t\\tmaterial.specular_factor = material.specular_factor[0];\\r\\n\\r\\n\\t\\tif(material.textures)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar textures = {};\\r\\n\\t\\t\\tfor(var i in material.textures)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar tex_info = material.textures[i];\\r\\n\\t\\t\\t\\t//channel name must be renamed because there is no consistency between programs\\r\\n\\t\\t\\t\\tvar channel_name = i;\\r\\n\\t\\t\\t\\tif( rename_channels[ channel_name ] )\\r\\n\\t\\t\\t\\t\\tchannel_name = rename_channels[ channel_name ];\\r\\n\\t\\t\\t\\tvar filename = tex_info.map_id;\\r\\n\\t\\t\\t\\t//convert to lowercase because webglstudio also converts them to lowercase\\r\\n\\t\\t\\t\\tif(this.convert_filenames_to_lowercase)\\r\\n\\t\\t\\t\\t\\tfilename = filename.toLowerCase(); \\r\\n\\t\\t\\t\\t//we allow two sets of texture coordinates\\r\\n\\t\\t\\t\\tvar coords = LS.Material.COORDS_UV0;\\r\\n\\t\\t\\t\\tif( tex_info.uvs == \\\"TEX1\\\")\\r\\n\\t\\t\\t\\t\\tcoords = LS.Material.COORDS_UV1;\\r\\n\\t\\t\\t\\ttex_info = { \\r\\n\\t\\t\\t\\t\\ttexture: filename,\\r\\n\\t\\t\\t\\t\\tuvs: coords\\r\\n\\t\\t\\t\\t};\\r\\n\\t\\t\\t\\ttextures[ channel_name ] = tex_info;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tmaterial.textures = textures;\\r\\n\\t\\t}\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.Formats.addSupportedFormat( \\\"dae\\\", parserDAE );\\r\\n\\r\\n///@FILE:../src/parsers/parserBVH.js\\r\\n///@INFO: PARSER\\r\\n//***** BVH Parser *****************\\r\\nvar parserBVH = {\\r\\n\\textension: \\\"bvh\\\",\\r\\n\\ttype: \\\"scene\\\",\\r\\n\\tresource: \\\"Scene\\\",\\r\\n\\tformat: 'text',\\r\\n\\tdataType:'text',\\r\\n\\t\\r\\n\\tparse: function( text, options, filename )\\r\\n\\t{\\r\\n\\t\\tvar MODE_HIERARCHY = 1;\\r\\n\\t\\tvar MODE_MOTION = 2;\\r\\n\\t\\tvar MODE_MOTION_DATA = 3;\\r\\n\\r\\n\\t\\tvar mode = 0;\\r\\n\\t\\tvar root = null;\\r\\n\\t\\tvar parent = null;\\r\\n\\t\\tvar node = null;\\r\\n\\t\\tvar stack = [];\\r\\n\\t\\tvar inside_of = null;\\r\\n\\t\\tvar channels = [];\\r\\n\\r\\n\\t\\tvar num_frames = -1;\\r\\n\\t\\tvar frame_time = -1;\\r\\n\\t\\tvar duration = -1;\\r\\n\\t\\tvar current_frame = 0;\\r\\n\\t\\tvar timestamps = [];\\r\\n\\r\\n\\t\\tvar translator = {\\r\\n\\t\\t\\t\\\"Xposition\\\":\\\"x\\\",\\\"Yposition\\\":\\\"y\\\",\\\"Zposition\\\":\\\"z\\\",\\\"Xrotation\\\":\\\"xrotation\\\",\\\"Yrotation\\\":\\\"yrotation\\\",\\\"Zrotation\\\":\\\"zrotation\\\"\\r\\n\\t\\t};\\r\\n\\r\\n\\t\\tvar ignore = false;\\r\\n\\r\\n\\t\\tvar lines = text.split(\\\"\\\\n\\\");\\r\\n\\t\\tvar length = lines.length;\\r\\n\\t\\tfor (var lineIndex = 0; lineIndex < length; ++lineIndex)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar line = lines[lineIndex].trim();\\r\\n\\r\\n\\t\\t\\tif (line[0] == \\\"#\\\")\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\t\\t\\tif(line == \\\"\\\")\\r\\n\\t\\t\\t\\tcontinue;\\r\\n\\r\\n\\t\\t\\tvar tokens = line.split(/[\\\\s]+/); //splits by spaces and tabs\\r\\n\\t\\t\\tvar cmd = tokens[0];\\r\\n\\r\\n\\t\\t\\tif(!mode)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tswitch(cmd)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tcase \\\"HIERARCHY\\\":\\r\\n\\t\\t\\t\\t\\t\\tmode = MODE_HIERARCHY;\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(mode == MODE_HIERARCHY)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tswitch(cmd)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tcase \\\"ROOT\\\":\\r\\n\\t\\t\\t\\t\\t\\tvar name = tokens[1];\\r\\n\\t\\t\\t\\t\\t\\tname = name.replace(/[^a-z0-9\\\\.\\\\-]/gi,\\\"_\\\");\\r\\n\\t\\t\\t\\t\\t\\troot = node = { name: name, node_type: \\\"JOINT\\\" };\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"JOINT\\\":\\r\\n\\t\\t\\t\\t\\t\\tparent = node;\\r\\n\\t\\t\\t\\t\\t\\tstack.push(parent);\\r\\n\\t\\t\\t\\t\\t\\tvar name = tokens[1];\\r\\n\\t\\t\\t\\t\\t\\tname = name.replace(/[^a-z0-9\\\\.\\\\-]/gi,\\\"_\\\");\\r\\n\\t\\t\\t\\t\\t\\tnode = { name: name, node_type: \\\"JOINT\\\" };\\r\\n\\t\\t\\t\\t\\t\\tif(!parent.children)\\r\\n\\t\\t\\t\\t\\t\\t\\tparent.children = [];\\r\\n\\t\\t\\t\\t\\t\\tparent.children.push(node);\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"End\\\":\\r\\n\\t\\t\\t\\t\\t\\t//ignore = true;\\r\\n\\t\\t\\t\\t\\t\\tparent = node;\\r\\n\\t\\t\\t\\t\\t\\tstack.push(parent);\\r\\n\\t\\t\\t\\t\\t\\tnode = { \\r\\n\\t\\t\\t\\t\\t\\t\\tname: parent.name + \\\"_end\\\", node_type: \\\"JOINT\\\"\\r\\n\\t\\t\\t\\t\\t\\t};\\r\\n\\t\\t\\t\\t\\t\\tif(!parent.children)\\r\\n\\t\\t\\t\\t\\t\\t\\tparent.children = [];\\r\\n\\t\\t\\t\\t\\t\\tparent.children.push(node);\\r\\n\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"{\\\":\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"}\\\":\\r\\n\\t\\t\\t\\t\\t\\tif(ignore)\\r\\n\\t\\t\\t\\t\\t\\t\\tignore = false; //ignoreEND\\r\\n\\t\\t\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tnode = stack.pop();\\r\\n\\t\\t\\t\\t\\t\\t\\tif(!node)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tnode = root;\\r\\n\\t\\t\\t\\t\\t\\t\\tinside_of = node;\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"CHANNELS\\\":\\r\\n\\t\\t\\t\\t\\t\\tfor(var j = 2; j < tokens.length; ++j)\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tvar property = tokens[j].toLowerCase();\\r\\n\\t\\t\\t\\t\\t\\t\\tif(translator[property])\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tproperty = translator[property];\\r\\n\\t\\t\\t\\t\\t\\t\\t//channels.push( { name: tokens[j], property: node.name + \\\"/\\\" + property, type: \\\"number\\\", value_size: 1, data: [], packed_data: true } );\\r\\n\\t\\t\\t\\t\\t\\t\\tvar channel_data = { node: node, property: property, data: [] };\\r\\n\\t\\t\\t\\t\\t\\t\\tchannels.push( channel_data );\\r\\n\\t\\t\\t\\t\\t\\t\\tif(!node._channels)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tnode._channels = {};\\r\\n\\t\\t\\t\\t\\t\\t\\tif(!node._channels_order)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tnode._channels_order = [];\\r\\n\\t\\t\\t\\t\\t\\t\\tnode._channels[ property ] = channel_data;\\r\\n\\t\\t\\t\\t\\t\\t\\tnode._channels_order.push( property );\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"OFFSET\\\":\\r\\n\\t\\t\\t\\t\\t\\tnode.transform = { position: readFloats(tokens,1) };\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\tcase \\\"MOTION\\\":\\r\\n\\t\\t\\t\\t\\t\\tmode = MODE_MOTION;\\r\\n\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}//mode hierarchy\\r\\n\\t\\t\\telse if(mode == MODE_MOTION)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(tokens[0] == \\\"Frames:\\\")\\r\\n\\t\\t\\t\\t\\tnum_frames = parseInt( tokens[1] );\\r\\n\\t\\t\\t\\telse if(tokens[0] == \\\"Frame\\\" && tokens[1] == \\\"Time:\\\")\\r\\n\\t\\t\\t\\t\\tframe_time = parseFloat( tokens[2] );\\r\\n\\r\\n\\t\\t\\t\\tif(num_frames != -1 && frame_time != -1)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tduration = num_frames * frame_time;\\r\\n\\t\\t\\t\\t\\tmode = MODE_MOTION_DATA;\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(mode == MODE_MOTION_DATA)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar current_time = current_frame * frame_time;\\r\\n\\t\\t\\t\\ttimestamps.push( current_time );\\r\\n\\t\\t\\t\\tfor(var j = 0; j < channels.length; ++j)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar channel = channels[j];\\r\\n\\t\\t\\t\\t\\t//channel.data.push( current_time, parseFloat( tokens[j] ) );\\r\\n\\t\\t\\t\\t\\tchannel.data.push( parseFloat( tokens[j] ) );\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t++current_frame;\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfunction readFloats(tokens, offset)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar r = tokens.slice(offset || 0);\\r\\n\\t\\t\\treturn r.map(parseFloat);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//process data\\r\\n\\t\\tvar tracks = [];\\r\\n\\t\\tthis.processMotion( root, tracks, timestamps );\\r\\n\\r\\n\\t\\tvar scene = { root: root, object_class: \\\"SceneNode\\\", resources: {} };\\r\\n\\r\\n\\t\\tfor(var i = 0; i < tracks.length; ++i)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar track = tracks[i];\\r\\n\\t\\t\\ttrack.duration = duration;\\r\\n\\t\\t}\\r\\n\\t\\tvar basename = LS.ResourcesManager.getBasename( filename );\\r\\n\\t\\tvar animation = { \\r\\n\\t\\t\\tname: basename + \\\"_animation.wbin\\\",\\r\\n\\t\\t\\tobject_class: \\\"Animation\\\",\\r\\n\\t\\t\\ttakes: { \\\"default\\\": { name: \\\"default\\\", duration: duration, tracks: tracks } }\\r\\n\\t\\t};\\r\\n\\t\\troot.animation = animation.name;\\r\\n\\t\\tscene.resources[ animation[\\\"name\\\"] ] = animation;\\r\\n\\r\\n\\t\\tconsole.log(scene);\\r\\n\\t\\treturn scene;\\r\\n\\t},\\r\\n\\r\\n\\tprocessMotion: function( node, tracks, timestamps )\\r\\n\\t{\\r\\n\\t\\tvar channels = node._channels;\\r\\n\\t\\tif(channels)\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar track_position = null;\\r\\n\\t\\t\\tvar track_rotation = null;\\r\\n\\r\\n\\t\\t\\tvar XAXIS = vec3.fromValues(1,0,0);\\r\\n\\t\\t\\tvar YAXIS = vec3.fromValues(0,1,0);\\r\\n\\t\\t\\tvar ZAXIS = vec3.fromValues(0,0,1);\\r\\n\\r\\n\\t\\t\\tif(channels.xposition || channels.yposition || channels.zposition )\\r\\n\\t\\t\\t\\ttrack_position = { name: node.name + \\\"/Transform/position\\\", property: node.name + \\\"/Transform/position\\\", type: \\\"vec3\\\", value_size: 3, data: [], packed_data: true };\\r\\n\\t\\t\\tif(channels.xrotation || channels.yrotation || channels.zrotation )\\r\\n\\t\\t\\t\\ttrack_rotation = { name: node.name + \\\"/Transform/rotation\\\", property: node.name + \\\"/Transform/rotation\\\", type: \\\"quat\\\", value_size: 4, data: [], packed_data: true };\\r\\n\\r\\n\\r\\n\\t\\t\\tfor(var j = 0; j < timestamps.length; ++j)\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar time = timestamps[j];\\r\\n\\t\\t\\t\\tvar pos = vec3.create();\\r\\n\\t\\t\\t\\tvar R = quat.create();\\r\\n\\t\\t\\t\\tvar ROT = quat.create();\\r\\n\\r\\n\\t\\t\\t\\tfor(var i = 0; i < node._channels_order.length; ++i)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tvar property = node._channels_order[i];\\r\\n\\r\\n\\t\\t\\t\\t\\tswitch( property )\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"xposition\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tpos[0] = channels.xposition.data[j] + node.transform.position[0];\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"yposition\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tpos[1] = channels.yposition.data[j] + node.transform.position[1];\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"zposition\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tpos[2] = channels.zposition.data[j] + node.transform.position[2];\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"xrotation\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tquat.setAxisAngle( ROT, XAXIS, channels.xrotation.data[j] * DEG2RAD );\\r\\n\\t\\t\\t\\t\\t\\t\\t//quat.mul( R, ROT, R );\\r\\n\\t\\t\\t\\t\\t\\t\\tquat.mul( R, R, ROT );\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"yrotation\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tquat.setAxisAngle( ROT, YAXIS, channels.yrotation.data[j] * DEG2RAD );\\r\\n\\t\\t\\t\\t\\t\\t\\t//quat.mul( R, ROT, R );\\r\\n\\t\\t\\t\\t\\t\\t\\tquat.mul( R, R, ROT );\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"zrotation\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tquat.setAxisAngle( ROT, ZAXIS, channels.zrotation.data[j] * DEG2RAD );\\r\\n\\t\\t\\t\\t\\t\\t\\t//quat.mul( R, ROT, R );\\r\\n\\t\\t\\t\\t\\t\\t\\tquat.mul( R, R, ROT );\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak;\\r\\n\\t\\t\\t\\t\\t};\\r\\n\\t\\t\\t\\t} //per channel\\r\\n\\r\\n\\t\\t\\t\\tif(track_position)\\r\\n\\t\\t\\t\\t\\ttrack_position.data.push( time, pos[0], pos[1], pos[2] );\\r\\n\\t\\t\\t\\tif(track_rotation)\\r\\n\\t\\t\\t\\t\\ttrack_rotation.data.push( time, R[0], R[1], R[2], R[3] );\\r\\n\\t\\t\\t}//per timestamp\\r\\n\\r\\n\\t\\t\\tif(track_position)\\r\\n\\t\\t\\t\\ttracks.push( track_position );\\r\\n\\t\\t\\tif(track_rotation)\\r\\n\\t\\t\\t\\ttracks.push( track_rotation );\\r\\n\\t\\t} //if channels\\r\\n\\r\\n\\t\\tif(node.children)\\r\\n\\t\\t{\\r\\n\\t\\t\\tfor(var i = 0; i < node.children.length; ++i)\\r\\n\\t\\t\\t\\tthis.processMotion( node.children[i], tracks, timestamps );\\r\\n\\t\\t}\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.Formats.addSupportedFormat( \\\"bvh\\\", parserBVH );\\r\\n///@FILE:../src/parsers/parserASE.js\\r\\n///@INFO: PARSER\\r\\n//***** ASE Parser *****************\\r\\nvar parserASE = {\\r\\n\\textension: \\\"ase\\\",\\r\\n\\ttype: \\\"mesh\\\",\\r\\n\\tresource: \\\"Mesh\\\",\\r\\n\\tformat: 'text',\\r\\n\\tdataType:'text',\\r\\n\\t\\r\\n\\tparse: function( text, options, filename )\\r\\n\\t{\\r\\n\\t\\toptions = options || {};\\r\\n\\r\\n\\t\\t//final arrays (packed, lineal [ax,ay,az, bx,by,bz ...])\\r\\n\\t\\tvar positionsArray = [ ];\\r\\n\\t\\tvar normalsArray = [ ];\\r\\n\\t\\tvar indicesArray = [ ];\\r\\n\\r\\n\\t\\tvar uvs_container = [ ];\\r\\n\\t\\tvar current_uvs = null;\\r\\n\\r\\n\\r\\n\\t\\t//unique arrays (not packed, lineal)\\r\\n\\t\\tvar positions = [ ];\\r\\n\\t\\tvar normals = [ ];\\r\\n\\t\\tvar indices = [ ];\\r\\n\\t\\tvar tvertlist = [ ];\\r\\n\\t\\tvar facemap = { };\\r\\n\\t\\tvar index = 0;\\r\\n\\r\\n\\t\\tvar line = null;\\r\\n\\t\\tvar f = null;\\r\\n\\t\\tvar pos = 0;\\r\\n\\t\\tvar tex = 0;\\r\\n\\t\\tvar nor = 0;\\r\\n\\t\\tvar x = 0.0;\\r\\n\\t\\tvar y = 0.0;\\r\\n\\t\\tvar z = 0.0;\\r\\n\\t\\tvar tokens = null;\\r\\n\\r\\n\\t\\tvar indices_offset = 0;\\r\\n\\t\\tvar mesh_index = 0;\\r\\n\\t\\tvar current_mat_id = -1;\\r\\n\\t\\tvar current_mesh_name = \\\"\\\";\\r\\n\\r\\n\\t\\t//used for mesh groups (submeshes)\\r\\n\\t\\tvar group = null;\\r\\n\\t\\tvar groups = [];\\r\\n\\r\\n\\t\\tvar flip_axis = this.flipAxis;\\r\\n\\t\\tif(options.flipAxis != null) flip_axis = options.flipAxis;\\r\\n\\t\\tvar flip_normals = (flip_axis || options.flipNormals);\\r\\n\\r\\n\\t\\tvar lines = text.split(\\\"\\\\n\\\");\\r\\n\\t\\tfor (var lineIndex = 0; lineIndex < lines.length; ++lineIndex) {\\r\\n\\t\\t\\tline = lines[lineIndex].replace(/[ \\\\t]+/g, \\\" \\\").replace(/\\\\s\\\\s*$/, \\\"\\\"); //trim\\r\\n\\t\\t\\tif(line[0] == \\\" \\\")\\r\\n\\t\\t\\t\\tline = line.substr(1,line.length);\\r\\n\\r\\n\\t\\t\\tif(line == \\\"\\\") continue;\\r\\n\\t\\t\\ttokens = line.split(\\\" \\\");\\r\\n\\r\\n\\t\\t\\tif(tokens[0] == \\\"*MESH\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tmesh_index += 1;\\r\\n\\t\\t\\t\\tpositions = [];\\r\\n\\r\\n\\t\\t\\t\\tif(mesh_index > 1)\\r\\n\\t\\t\\t\\t\\tbreak; //parse only the first mesh\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if (tokens[0] == \\\"*NODE_NAME\\\") {\\r\\n\\t\\t\\t\\tcurrent_mesh_name = tokens[1].substr(1, tokens[1].length - 2);\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(tokens[0] == \\\"*MESH_VERTEX\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(flip_axis) //maya and max notation style\\r\\n\\t\\t\\t\\t\\tpositions.push( [-1*parseFloat(tokens[2]), parseFloat(tokens[4]), parseFloat(tokens[3])] );\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tpositions.push( [parseFloat(tokens[2]), parseFloat(tokens[3]), parseFloat(tokens[4])] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(tokens[0] == \\\"*MESH_FACE\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t//material info\\r\\n\\t\\t\\t\\tvar mat_id = parseInt( tokens[17] );\\r\\n\\t\\t\\t\\tif(current_mat_id != mat_id)\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tcurrent_mat_id = mat_id;\\r\\n\\t\\t\\t\\t\\tif(group != null)\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tgroup.length = positionsArray.length / 3 - group.start;\\r\\n\\t\\t\\t\\t\\t\\tif(group.length > 0)\\r\\n\\t\\t\\t\\t\\t\\t\\tgroups.push(group);\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tgroup = {\\r\\n\\t\\t\\t\\t\\t\\tname: \\\"mat_\\\" + mat_id,\\r\\n\\t\\t\\t\\t\\t\\tstart: positionsArray.length / 3,\\r\\n\\t\\t\\t\\t\\t\\tlength: -1,\\r\\n\\t\\t\\t\\t\\t\\tmaterial: \\\"\\\"\\r\\n\\t\\t\\t\\t\\t};\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t//add vertices\\r\\n\\t\\t\\t\\tvar vertex = positions[ parseInt(tokens[3]) ];\\r\\n\\t\\t\\t\\tpositionsArray.push( vertex[0], vertex[1], vertex[2] );\\r\\n\\t\\t\\t\\tvertex = positions[ parseInt(tokens[5]) ];\\r\\n\\t\\t\\t\\tpositionsArray.push( vertex[0], vertex[1], vertex[2] );\\r\\n\\t\\t\\t\\tvertex = positions[ parseInt(tokens[7]) ];\\r\\n\\t\\t\\t\\tpositionsArray.push( vertex[0], vertex[1], vertex[2] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(tokens[0] == \\\"*MESH_TVERTLIST\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttvertlist = [];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(tokens[0] == \\\"*MESH_TVERT\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttvertlist.push( [parseFloat(tokens[2]), parseFloat(tokens[3])] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(tokens[0] == \\\"*MESH_TFACELIST\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( current_uvs && current_uvs.length )\\r\\n\\t\\t\\t\\t\\tuvs_container.push( current_uvs );\\r\\n\\t\\t\\t\\tcurrent_uvs = [];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(tokens[0] == \\\"*MESH_TFACE\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tvar coord = tvertlist[ parseInt(tokens[2]) ];\\r\\n\\t\\t\\t\\tcurrent_uvs.push( coord[0], coord[1] );\\r\\n\\t\\t\\t\\tcoord = tvertlist[ parseInt(tokens[3]) ];\\r\\n\\t\\t\\t\\tcurrent_uvs.push( coord[0], coord[1] );\\r\\n\\t\\t\\t\\tcoord = tvertlist[ parseInt(tokens[4]) ];\\r\\n\\t\\t\\t\\tcurrent_uvs.push( coord[0], coord[1] );\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(tokens[0] == \\\"*MESH_MAPPINGCHANNEL\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif( current_uvs )\\r\\n\\t\\t\\t\\t\\tuvs_container.push( current_uvs );\\r\\n\\t\\t\\t\\tcurrent_uvs = [];\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\telse if(tokens[0] == \\\"*MESH_VERTEXNORMAL\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tif(flip_normals) //maya and max notation style\\r\\n\\t\\t\\t\\t\\tnormalsArray.push(-1*parseFloat(tokens[2]),parseFloat(tokens[4]),parseFloat(tokens[3]));\\r\\n\\t\\t\\t\\telse\\r\\n\\t\\t\\t\\t\\tnormalsArray.push(parseFloat(tokens[2]),parseFloat(tokens[3]),parseFloat(tokens[4]));\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(current_uvs)\\r\\n\\t\\t\\tuvs_container.push( current_uvs );\\r\\n\\r\\n\\t\\tvar total_primitives = positionsArray.length / 3 - group.start;\\r\\n\\t\\tif(group && total_primitives > 1)\\r\\n\\t\\t{\\r\\n\\t\\t\\tgroup.length = total_primitives;\\r\\n\\t\\t\\tgroups.push(group);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tvar mesh = { info: {} };\\r\\n\\r\\n\\t\\tmesh.vertices = new Float32Array(positionsArray);\\r\\n\\t\\tif (normalsArray.length > 0)\\r\\n\\t\\t\\tmesh.normals = new Float32Array(normalsArray);\\r\\n\\t\\tfor(var i = 0; i < uvs_container.length; ++i )\\r\\n\\t\\t{\\r\\n\\t\\t\\tvar channel = \\\"\\\";\\r\\n\\t\\t\\tif(i > 0)\\r\\n\\t\\t\\t\\tchannel = i+1;\\r\\n\\t\\t\\tmesh[ \\\"coords\\\" + channel ] = new Float32Array( uvs_container[i] );\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t//extra info\\r\\n\\t\\tmesh.bounding = LS.Formats.computeMeshBounding( mesh.vertices );\\r\\n\\t\\tif(groups.length > 1)\\r\\n\\t\\t\\tmesh.info.groups = groups;\\r\\n\\t\\treturn mesh;\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.Formats.addSupportedFormat( \\\"ase\\\", parserASE );\\r\\n\\r\\n///@FILE:../src/parsers/parserJSMESH.js\\r\\n///@INFO: PARSER\\r\\n//legacy format\\r\\nvar parserJSMesh = { \\r\\n\\textension: 'jsmesh',\\r\\n\\ttype: 'mesh',\\r\\n\\tformat: 'text',\\r\\n\\tdataType:'string',\\r\\n\\r\\n\\tparse: function(data,options)\\r\\n\\t{\\r\\n\\t\\tvar mesh = null;\\r\\n\\r\\n\\t\\tif(typeof(data) == \\\"object\\\")\\r\\n\\t\\t\\tmesh = data;\\r\\n\\t\\telse if(typeof(data) == \\\"string\\\")\\r\\n\\t\\t\\tmesh = JSON.parse(data);\\r\\n\\r\\n\\t\\tif(mesh.vertices.constructor == Array) //for deprecated formats\\r\\n\\t\\t{\\r\\n\\t\\t\\tmesh.vertices = typeof( mesh.vertices[0] ) == \\\"number\\\" ? mesh.vertices : linearizeArray(mesh.vertices);\\r\\n\\t\\t\\tif(mesh.normals) mesh.normals = typeof( mesh.normals[0] ) == \\\"number\\\" ? mesh.normals : linearizeArray(mesh.normals);\\r\\n\\t\\t\\tif(mesh.coords) mesh.coords = typeof( mesh.coords[0] ) == \\\"number\\\" ? mesh.coords : linearizeArray(mesh.coords);\\r\\n\\t\\t\\tif(mesh.triangles) mesh.triangles = typeof( mesh.triangles[0] ) == \\\"number\\\" ? mesh.triangles : linearizeArray(mesh.triangles);\\r\\n\\r\\n\\t\\t\\tmesh.vertices = new Float32Array(mesh.vertices);\\r\\n\\t\\t\\tif(mesh.normals) mesh.normals = new Float32Array(mesh.normals);\\r\\n\\t\\t\\tif(mesh.coords) mesh.coords = new Float32Array(mesh.coords);\\r\\n\\t\\t\\tif(mesh.triangles) mesh.triangles = new Uint16Array(mesh.triangles);\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif(!mesh.bounding)\\r\\n\\t\\t\\tmesh.bounding = LS.Formats.computeMeshBounding(mesh.vertices);\\r\\n\\t\\treturn mesh;\\r\\n\\t}\\r\\n};\\r\\n\\r\\nLS.Formats.addSupportedFormat( \\\"jsmesh\\\", parserJSMesh );\\r\\n\\r\\n///@FILE:../src/utils.js\\r\\n///@INFO: BASE\\r\\n/**\\r\\n* Samples a curve and returns the resulting value \\r\\n*\\r\\n* @namespace LS\\r\\n* @method getCurveValueAt\\r\\n* @param {Array} values \\r\\n* @param {number} minx min x value\\r\\n* @param {number} maxx max x value\\r\\n* @param {number} defaulty default y value\\r\\n* @param {number} x the position in the curve to sample\\r\\n* @return {number}\\r\\n*/\\r\\nLS.getCurveValueAt = function(values,minx,maxx,defaulty, x)\\r\\n{\\r\\n\\tif(x < minx || x > maxx)\\r\\n\\t\\treturn defaulty;\\r\\n\\r\\n\\tvar last = [ minx, defaulty ];\\r\\n\\tvar f = 0;\\r\\n\\tfor(var i = 0; i < values.length; i += 1)\\r\\n\\t{\\r\\n\\t\\tvar v = values[i];\\r\\n\\t\\tif(x == v[0]) return v[1];\\r\\n\\t\\tif(x < v[0])\\r\\n\\t\\t{\\r\\n\\t\\t\\tf = (x - last[0]) / (v[0] - last[0]);\\r\\n\\t\\t\\treturn last[1] * (1-f) + v[1] * f;\\r\\n\\t\\t}\\r\\n\\t\\tlast = v;\\r\\n\\t}\\r\\n\\r\\n\\tv = [ maxx, defaulty ];\\r\\n\\tf = (x - last[0]) / (v[0] - last[0]);\\r\\n\\treturn last[1] * (1-f) + v[1] * f;\\r\\n}\\r\\n\\r\\n/**\\r\\n* Resamples a full curve in values (useful to upload to GPU array)\\r\\n*\\r\\n* @namespace LS\\r\\n* @method resampleCurve\\r\\n* @param {Array} values \\r\\n* @param {number} minx min x value\\r\\n* @param {number} maxx max x value\\r\\n* @param {number} defaulty default y value\\r\\n* @param {number} numsamples\\r\\n* @return {Array}\\r\\n*/\\r\\n\\r\\nLS.resampleCurve = function(values,minx,maxx,defaulty, samples)\\r\\n{\\r\\n\\tvar result = [];\\r\\n\\tresult.length = samples;\\r\\n\\tvar delta = (maxx - minx) / samples;\\r\\n\\tfor(var i = 0; i < samples; i++)\\r\\n\\t\\tresult[i] = LS.getCurveValueAt(values,minx,maxx,defaulty, minx + delta * i);\\r\\n\\treturn result;\\r\\n}\\r\\n\\r\\n//work in progress to create a new kind of property called attribute which comes with extra info\\r\\n//valid options are { type: \\\"number\\\"|\\\"string\\\"|\\\"vec2\\\"|\\\"vec3\\\"|\\\"color\\\"|\\\"Texture\\\"... , min, max, step }\\r\\nif( !Object.prototype.hasOwnProperty(\\\"defineAttribute\\\") )\\r\\n{\\r\\n\\tObject.defineProperty( Object.prototype, \\\"defineAttribute\\\", {\\r\\n\\t\\tvalue: function( name, value, options ) {\\r\\n\\t\\t\\tif(options && typeof(options) == \\\"string\\\")\\r\\n\\t\\t\\t\\toptions = { type: options };\\r\\n\\r\\n\\t\\t\\tvar root = this;\\r\\n\\t\\t\\tif(typeof(this) != \\\"function\\\")\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tthis[name] = value;\\r\\n\\t\\t\\t\\troot = this.constructor;\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tObject.defineProperty( root, \\\"@\\\" + name, {\\r\\n\\t\\t\\t\\tvalue: options || {},\\r\\n\\t\\t\\t\\tenumerable: false\\r\\n\\t\\t\\t});\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false,\\r\\n\\t\\twritable: true\\r\\n\\t});\\r\\n\\r\\n\\tObject.defineProperty( Object.prototype, \\\"getAttribute\\\", {\\r\\n\\t\\tvalue: function( name ) {\\r\\n\\t\\t\\tvar v = \\\"@\\\" + name;\\r\\n\\t\\t\\tif(this.hasOwnProperty(v))\\r\\n\\t\\t\\t\\treturn this[v];\\r\\n\\t\\t\\tif(this.constructor && this.constructor.hasOwnProperty(v))\\r\\n\\t\\t\\t\\treturn this.constructor[v];\\r\\n\\t\\t\\treturn null;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false,\\r\\n\\t\\twritable: true\\r\\n\\t});\\r\\n}\\r\\n\\r\\n\\r\\n\\r\\nfunction toArray(v) { return Array.apply( [], v ); }\\r\\n\\r\\n/*\\r\\nObject.defineProperty(Object.prototype, \\\"merge\\\", { \\r\\n value: function(v) {\\r\\n for(var i in v)\\r\\n\\t\\t\\tthis[i] = v[i];\\r\\n\\t\\treturn this;\\r\\n },\\r\\n configurable: false,\\r\\n writable: false,\\r\\n\\tenumerable: false // uncomment to be explicit, though not necessary\\r\\n});\\r\\n*/\\r\\n\\r\\n//used for hashing keys:TODO move from here somewhere else\\r\\nif( !String.prototype.hasOwnProperty( \\\"hashCode\\\" ) )\\r\\n{\\r\\n\\tObject.defineProperty( String.prototype, \\\"hashCode\\\", {\\r\\n\\t\\tvalue: function(){\\r\\n\\t\\t\\tvar hash = 0, i, c, l;\\r\\n\\t\\t\\tif (this.length == 0) return hash;\\r\\n\\t\\t\\tfor (i = 0, l = this.length; i < l; ++i) {\\r\\n\\t\\t\\t\\tc = this.charCodeAt(i);\\r\\n\\t\\t\\t\\thash = ((hash<<5)-hash)+c;\\r\\n\\t\\t\\t\\thash |= 0; // Convert to 32bit integer\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn hash;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false\\r\\n\\t});\\r\\n}\\r\\n\\r\\nObject.equals = function( x, y ) {\\r\\n if ( x === y ) return true;\\r\\n // if both x and y are null or undefined and exactly the same\\r\\n\\r\\n if ( ! ( x instanceof Object ) || ! ( y instanceof Object ) ) return false;\\r\\n // if they are not strictly equal, they both need to be Objects\\r\\n\\r\\n if ( x.constructor !== y.constructor ) return false;\\r\\n // they must have the exact same prototype chain, the closest we can do is\\r\\n // test there constructor.\\r\\n\\r\\n for ( var p in x ) {\\r\\n if ( ! x.hasOwnProperty( p ) ) continue;\\r\\n // other properties were tested using x.constructor === y.constructor\\r\\n\\r\\n if ( ! y.hasOwnProperty( p ) ) return false;\\r\\n // allows to compare x[ p ] and y[ p ] when set to undefined\\r\\n\\r\\n if ( x[ p ] === y[ p ] ) continue;\\r\\n // if they have the same strict value or identity then they are equal\\r\\n\\r\\n if ( typeof( x[ p ] ) !== \\\"object\\\" ) return false;\\r\\n // Numbers, Strings, Functions, Booleans must be strictly equal\\r\\n\\r\\n if ( ! Object.equals( x[ p ], y[ p ] ) ) return false;\\r\\n // Objects and Arrays must be tested recursively\\r\\n }\\r\\n\\r\\n for ( p in y ) {\\r\\n if ( y.hasOwnProperty( p ) && ! x.hasOwnProperty( p ) ) return false;\\r\\n // allows x[ p ] to be set to undefined\\r\\n }\\r\\n return true;\\r\\n}\\r\\n\\r\\n\\r\\n//used for on simplified serializations\\r\\nif( !Array.prototype.hasOwnProperty( \\\"equal\\\" ) )\\r\\n{\\r\\n\\tObject.defineProperty( Array.prototype, \\\"equal\\\", {\\r\\n\\t\\tvalue: function(v){\\r\\n\\t\\t\\tfor(var i = 0; i < this.length; ++i)\\r\\n\\t\\t\\t\\tif( this[i] != v[i] )\\r\\n\\t\\t\\t\\t\\treturn false;\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false\\r\\n\\t});\\r\\n}\\r\\n\\r\\nif( !Float32Array.prototype.hasOwnProperty( \\\"equal\\\" ) )\\r\\n{\\r\\n\\tObject.defineProperty( Float32Array.prototype, \\\"equal\\\", {\\r\\n\\t\\tvalue: function(v){\\r\\n\\t\\t\\tfor(var i = 0; i < this.length; ++i)\\r\\n\\t\\t\\t\\tif( this[i] != v[i] )\\r\\n\\t\\t\\t\\t\\treturn false;\\r\\n\\t\\t\\treturn true;\\r\\n\\t\\t},\\r\\n\\t\\tenumerable: false\\r\\n\\t});\\r\\n}\\r\\n\\r\\n\\r\\n//parsers usually need this\\r\\n//takes an string an returns a Uint8Array typed array containing that string\\r\\nfunction stringToTypedArray( str, fixed_length )\\r\\n{\\r\\n\\tvar r = new Uint8Array( fixed_length ? fixed_length : str.length);\\r\\n\\tfor(var i = 0; i < str.length; i++)\\r\\n\\t\\tr[i] = str.charCodeAt(i);\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\n//takes a typed array with ASCII codes and returns the string\\r\\nfunction typedArrayToString( typed_array, same_size )\\r\\n{\\r\\n\\tvar r = \\\"\\\";\\r\\n\\tfor(var i = 0; i < typed_array.length; i++)\\r\\n\\t\\tif (typed_array[i] == 0 && !same_size)\\r\\n\\t\\t\\tbreak;\\r\\n\\t\\telse\\r\\n\\t\\t\\tr += String.fromCharCode( typed_array[i] );\\r\\n\\treturn r;\\r\\n}\\r\\n\\r\\nLS.stringToTypedArray = stringToTypedArray;\\r\\nLS.typedArrayToString = typedArrayToString;\\r\\n///@FILE:../src/outro.js\\r\\n///@INFO: BASE\\r\\n//here goes the ending of commonjs stuff\\r\\n\\r\\n//create Global Scene\\r\\nvar Scene = LS.GlobalScene = new LS.Scene();\\r\\n\\r\\nLS.newMeshNode = function(id,mesh_name)\\r\\n{\\r\\n\\tvar node = new LS.SceneNode(id);\\r\\n\\tnode.addComponent( new LS.Components.MeshRenderer() );\\r\\n\\tnode.setMesh(mesh_name);\\r\\n\\treturn node;\\r\\n}\\r\\n\\r\\nLS.newLightNode = function(id)\\r\\n{\\r\\n\\tvar node = new LS.SceneNode(id);\\r\\n\\tnode.addComponent( new LS.Components.Light() );\\r\\n\\treturn node;\\r\\n}\\r\\n\\r\\nLS.newCameraNode = function(id)\\r\\n{\\r\\n\\tvar node = new LS.SceneNode(id);\\r\\n\\tnode.addComponent( new LS.Components.Camera() );\\r\\n\\treturn node;\\r\\n}\\r\\n\\r\\nglobal.LS = LS;\\r\\n\\r\\n//*******************************/\\r\\n})( typeof(window) != \\\"undefined\\\" ? window : self ); //TODO: add support for commonjs\\r\\n\"","require(\"!!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\script-loader\\\\addScript.js\")(require(\"!!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\raw-loader\\\\index.js!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\source-map-loader\\\\index.js!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\src\\\\webGL\\\\Canvas2DtoWebGL.js\"))","require(\"!!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\script-loader\\\\addScript.js\")(require(\"!!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\raw-loader\\\\index.js!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\source-map-loader\\\\index.js!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\src\\\\webGL\\\\gl-matrix-min.js\"))","require(\"!!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\script-loader\\\\addScript.js\")(require(\"!!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\raw-loader\\\\index.js!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\source-map-loader\\\\index.js!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\src\\\\webGL\\\\litegl.js\"))","require(\"!!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\script-loader\\\\addScript.js\")(require(\"!!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\raw-loader\\\\index.js!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\source-map-loader\\\\index.js!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\src\\\\webGL\\\\litegraph.js\"))","require(\"!!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\script-loader\\\\addScript.js\")(require(\"!!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\raw-loader\\\\index.js!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\node_modules\\\\source-map-loader\\\\index.js!D:\\\\a\\\\1\\\\s\\\\Haber.DriStack.Web\\\\Haber.DriStack.Web\\\\app\\\\src\\\\webGL\\\\litescene.js\"))","// All globals must be added to the custom_typings\\index.d.ts file so typescript knows about them.\r\nimport cloneDeep from 'lodash/cloneDeep';\r\nimport isEqual from 'react-fast-compare';\r\n\r\nconst currency5 = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 5, minimumFractionDigits: 5 }).format;\r\nconst currency2 = new Intl.NumberFormat(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 2, minimumFractionDigits: 2 }).format;\r\nconst quantity0 = new Intl.NumberFormat(undefined, { maximumFractionDigits: 0, minimumFractionDigits: 0 }).format;\r\nconst quantity2 = new Intl.NumberFormat(undefined, { maximumFractionDigits: 2, minimumFractionDigits: 2 }).format;\r\nconst quantity5 = new Intl.NumberFormat(undefined, { maximumFractionDigits: 5, minimumFractionDigits: 5 }).format;\r\nexport default function addGlobals() {\r\n // tslint:disable-next-line:no-string-literal\r\n global['FORMAT'] = {\r\n currency5: (value: number) => currency5(roundTo(value, 5)),\r\n currency2: (value: number) => currency2(roundTo(value, 2)),\r\n quantity0: (value: number) => quantity0(roundTo(value, 0)),\r\n quantity2: (value: number) => quantity2(roundTo(value, 2)),\r\n quantity5: (value: number) => quantity5(roundTo(value, 5)),\r\n };\r\n\r\n // tslint:disable-next-line:no-string-literal\r\n global['tryParseInt'] = function (value: any, defaultValue: number) {\r\n var retValue: number = defaultValue;\r\n if (value != null) {\r\n if (!isNaN(value) && typeof value === 'string' && value !== '') {\r\n retValue = parseInt(value, 10);\r\n } else if (typeof value === 'number') {\r\n retValue = Math.floor(value);\r\n }\r\n if (!isNaN(retValue)) {\r\n return retValue;\r\n }\r\n }\r\n return defaultValue;\r\n };\r\n\r\n // tslint:disable-next-line:no-string-literal\r\n global['roundTo'] = function (num: number, digits: number) {\r\n digits = digits || 0;\r\n var multiplicator = Math.pow(10, digits);\r\n num = Math.round((num + 0.000001) * multiplicator) / multiplicator;\r\n return num;\r\n };\r\n\r\n // tslint:disable-next-line:no-string-literal\r\n global['zeroPad'] = function (val: any, digits: number) {\r\n return ('0'.repeat(digits) + val).substr(-digits, digits);\r\n };\r\n\r\n // tslint:disable-next-line:no-string-literal\r\n global['removeObjectProperties'] = function (object: any, names: string[]) {\r\n if (!object) { return object; }\r\n\r\n let clone = cloneDeep(object);\r\n\r\n let loop = (obj: any, keys: string[]) => {\r\n var index;\r\n for (var prop in obj) {\r\n if (obj.hasOwnProperty(prop)) {\r\n if (typeof obj[prop] === 'object') {\r\n index = keys.indexOf(prop);\r\n if (index > -1) {\r\n delete obj[prop];\r\n } else {\r\n loop(obj[prop], keys);\r\n }\r\n } else {\r\n index = keys.indexOf(prop);\r\n if (index > -1) {\r\n delete obj[prop];\r\n }\r\n }\r\n }\r\n }\r\n };\r\n\r\n loop(clone, names);\r\n\r\n return clone;\r\n };\r\n\r\n // tslint:disable-next-line:no-string-literal\r\n global['deepClone'] = function (obj: any) {\r\n if (!obj) { return obj; }\r\n return cloneDeep(obj);\r\n };\r\n\r\n // tslint:disable-next-line:no-string-literal\r\n global['deepEqual'] = function (a: any, b: any) {\r\n if ((!a && b) || (a && !b)) { return false; }\r\n if (!a && !b) { return true; }\r\n return isEqual(a, b);\r\n };\r\n}\r\n","import { createBrowserHistory } from 'history';\r\nexport default createBrowserHistory();\r\n","import { generatePath } from 'react-router-dom';\r\nimport * as queryString from 'query-string';\r\n\r\nexport enum UserSource {\r\n Internal = \"internal\",\r\n MyShivvers = \"myshivvers\",\r\n}\r\nclass Routes {\r\n public static BASE_ROUTE = '';\r\n\r\n public static HOME_ROUTE = Routes.BASE_ROUTE + '/';\r\n public static LOGIN = Routes.BASE_ROUTE + '/login';\r\n public static PROFILE = Routes.BASE_ROUTE + '/profile';\r\n public static SETTINGS = Routes.BASE_ROUTE + '/settings';\r\n public static REQUEST_RESET_PASSWORD = Routes.BASE_ROUTE + '/reset-password';\r\n public static RESET_PASSWORD_LINK = Routes.BASE_ROUTE + '/reset-password-link';\r\n public static DASHBOARD = Routes.BASE_ROUTE + '/dashboard';\r\n public static ADMIN = Routes.BASE_ROUTE + '/admin';\r\n public static SITE_DETAIL = Routes.BASE_ROUTE + '/site-detail';\r\n public static BIN_STATS = Routes.BASE_ROUTE + '/bin-stats';\r\n public static BIN_STATS_SHIVVERS = Routes.BASE_ROUTE + '/bin-stats-shivvers';\r\n public static UNAUTHORIZED = Routes.BASE_ROUTE + '/unauthorized';\r\n public static BINVISUAL_TEST_PAGE = Routes.BASE_ROUTE + '/bin-test';\r\n public static SECURITY_BASE = Routes.BASE_ROUTE + '/security';\r\n public static SECURITY_USERS = Routes.SECURITY_BASE + '/users';\r\n public static SECURITY_USER_DETAIL = Routes.SECURITY_USERS + '/:id';\r\n public static SECURITY_ROLEGROUPS = Routes.SECURITY_BASE + '/roleGroups';\r\n public static SECURITY_ROLEGROUP_DETAIL = Routes.SECURITY_ROLEGROUPS + '/:id';\r\n public static BIN_OVERVIEW = Routes.BASE_ROUTE + '/binOverview';\r\n public static REPORT_VIEWER = Routes.BASE_ROUTE + '/ReportView';\r\n\r\n public static REPORTS_BASE = Routes.BASE_ROUTE + '/reports';\r\n public static REPORTS_SQLREPORT = Routes.REPORTS_BASE + '/sqlreport/:id';\r\n\r\n public static USERS_BASE = Routes.BASE_ROUTE + '/users';\r\n // public static USERS_DETAIL = Routes.REPORTS_BASE + '/users/:id';\r\n public static USERS_DETAIL = Routes.BASE_ROUTE + '/users/:id';\r\n public static USERS_INTERNAL = Routes.generate(Routes.USERS_BASE, {\r\n [\"userSource\"]: UserSource.Internal,\r\n });\r\n public static USERS_MYSHIVVERS = Routes.generate(Routes.USERS_BASE, {\r\n [\"userSource\"]: UserSource.MyShivvers,\r\n });\r\n\r\n public static SHIVVERS_USER_ADD = Routes.generate(\"/shivversUser/new\");\r\n\r\n /**\r\n * Generated a url from a route and parameters.\r\n * @param route Route that may contain parameter placeholders.\r\n * @param params Object where property names equal the parameter placeholders in the route an the property value is what will be injected.\r\n */\r\n public static generate(\r\n route: string,\r\n params?: { [paramName: string]: string | number | boolean },\r\n query?: { [name: string]: any }) {\r\n\r\n let path = generatePath(route, params);\r\n\r\n // Add any query string variables to the route if passed\r\n if (query) {\r\n let q = queryString.stringify(query);\r\n if (q) {\r\n path += `?${q}`;\r\n }\r\n }\r\n\r\n return path;\r\n }\r\n}\r\n\r\nexport default Routes;\r\n","import { Dispatch } from 'redux';\r\nimport BaseAction from './BaseAction';\r\nimport { StateStoreModel } from 'src/redux/state/StateStoreModel';\r\n\r\nexport type VersionAction = BaseAction;\r\n\r\nexport enum VersionActionTypes {\r\n VERSION_CHECK = 'VERSION_CHECK',\r\n VERSION_OUTDATED = 'VERSION_OUTDATED',\r\n VERSION_RESET = 'VERSION_RESET'\r\n}\r\n\r\nlet resetTimer: NodeJS.Timeout;\r\n\r\nexport const VersionActions = {\r\n check: VersionCheckAction,\r\n reset: VersionResetAction,\r\n};\r\n\r\nfunction VersionResetAction() {\r\n return (dispatch: Dispatch, getState: () => StateStoreModel) => {\r\n if (resetTimer) {clearTimeout(resetTimer); }\r\n resetTimer = setTimeout(() => {\r\n dispatch(_VersionResetAction());\r\n },\r\n 300000);\r\n };\r\n}\r\n\r\nfunction VersionCheckAction(client: string, api: string) {\r\n return (dispatch: Dispatch, getState: () => StateStoreModel) => {\r\n let outdated = getState().Version.Outdated;\r\n if (!outdated && client !== api) {\r\n dispatch(VersionOutdatedAction(client, api));\r\n }\r\n };\r\n}\r\n\r\nfunction VersionOutdatedAction(client: string, api: string): VersionAction {\r\n return {\r\n type: VersionActionTypes.VERSION_OUTDATED,\r\n data: { Client: client, Api: api }\r\n };\r\n}\r\nfunction _VersionResetAction(): VersionAction {\r\n return {\r\n type: VersionActionTypes.VERSION_RESET,\r\n data: null\r\n };\r\n}\r\n","import { Store } from 'redux';\r\nimport { StateStoreModel } from './state/StateStoreModel';\r\n\r\ntype AppStore = Store;\r\n\r\nvar storeVar: AppStore | null = null;\r\n/**\r\n * This will be used by the main index.tsx page to set the store after in has been created in the app.\r\n */\r\nexport function setStore (store: AppStore) {\r\n storeVar = store;\r\n}\r\n\r\n/**\r\n * This allows access to the redux store without taking a dependency on the store file\r\n * and all of its dependencies that can cause cyclic references if used in certain places.\r\n */\r\nexport default function getStore(): AppStore | null {\r\n return storeVar;\r\n}\r\n","import history from '../utils/HistoryUtil';\r\nimport Routes from '../consts/Routes';\r\nimport { VersionActions } from 'src/redux/actions/VersionActions';\r\nimport getStore from '../redux/getStore';\r\n\r\nexport class ApiError {\r\n public description: string | undefined;\r\n constructor(public status: number, public title: string, public message?: string, public errorDetails?: any) {\r\n }\r\n}\r\n\r\nclass ApiResultHandler {\r\n public static handle(response: Response): Response | Promise {\r\n if (response.ok || response.redirected) {\r\n let v = response.headers.get('api-version');\r\n if (v != null && $apiVersion !== v) {\r\n let store = getStore();\r\n if (store) {\r\n store.dispatch(VersionActions.check($apiVersion, v));\r\n }\r\n }\r\n if (response.redirected) {\r\n window.location.href = response.url;\r\n }\r\n return response;\r\n } else if (response.status === 401 || response.status === 405) {\r\n let url: string | null = history.location.pathname + history.location.search;\r\n\r\n const urlParser = new URL(url, window.location.origin);\r\n const returnUrl = urlParser.searchParams.get(\"returnUrl\");\r\n // the login page check ensures we don't override an already set return url, if 2 api requests were unauthorized.\r\n const alreadyAtLoginRoute = url === Routes.LOGIN;\r\n if (!returnUrl && !alreadyAtLoginRoute) {\r\n console.log(\"apihandler: setting returnUrl to: \", url?.toString());\r\n history.push(Routes.generate(Routes.LOGIN, {}, { returnUrl: url?.toString() })); \r\n }\r\n } else {\r\n let error = new ApiError(response.status, response.status + ' ' + response.statusText);\r\n let ct = response.headers.get('Content-Type');\r\n if (ct && (ct.indexOf('/json') > 0 || ct.includes(\"problem+json\"))) {\r\n return response.json().then((result: any) => {\r\n if (result.title) {\r\n error.message = result.title;\r\n }\r\n error.errorDetails = result;\r\n error.description = (result || {}).exceptionMessage;\r\n return Promise.reject(error);\r\n });\r\n } else {\r\n error.message += '\\n\\n\\n {RESPONSE}: ' + JSON.stringify(response);\r\n }\r\n throw error;\r\n }\r\n return response;\r\n }\r\n}\r\n\r\nexport default ApiResultHandler;\r\n","export default class Guid {\r\n\r\n public static NewGuid() {\r\n return Guid.s4() + Guid.s4() + '-' + Guid.s4() + '-' + Guid.s4() + '-' +\r\n Guid.s4() + '-' + Guid.s4() + Guid.s4() + Guid.s4();\r\n }\r\n\r\n private static s4() {\r\n return Math.floor((1 + Math.random()) * 0x10000)\r\n .toString(16)\r\n .substring(1);\r\n }\r\n}","import Guid from './Guid';\r\n\r\nclass DownloadContext {\r\n public token: string;\r\n public attempts: number;\r\n public callback: Function;\r\n public iframe: HTMLIFrameElement;\r\n public error: string | null;\r\n public isLoaded: boolean;\r\n}\r\n\r\nexport default class FileDownload {\r\n\r\n public static download(url: string, callback: Function): void {\r\n let that = this;\r\n let iframe = document.createElement('iframe');\r\n let context: DownloadContext = {\r\n token : Guid.NewGuid(),\r\n attempts : 60,\r\n callback : callback,\r\n iframe : iframe,\r\n error : null,\r\n isLoaded : false\r\n };\r\n iframe.id = context.token;\r\n iframe.style.display = 'none';\r\n // iframe.style.height = '300px';\r\n // iframe.style.width = '400px';\r\n document.body.appendChild(iframe);\r\n\r\n iframe.onload = function(e: Event) {\r\n try {\r\n let doc = context.iframe.contentDocument ||\r\n (context.iframe.contentWindow != null ? context.iframe.contentWindow.document : window.document);\r\n let error = JSON.parse(doc.body.innerText);\r\n context.error = !!error ? error.message : null;\r\n context.isLoaded = true;\r\n } catch {\r\n // Intentionally Blank\r\n }\r\n };\r\n\r\n if (url.indexOf('?') < 0) {\r\n url += '?';\r\n } else {\r\n url += '&';\r\n }\r\n\r\n iframe.src = url + 'downloadToken=' + context.token;\r\n // document.body.insertAdjacentHTML('beforeend', ``);\r\n that.checkCookie(context);\r\n\r\n }\r\n\r\n public static downloadBase64StringAsExcel(fileName: string, base64String: string) {\r\n var downloader = document.createElement('a');\r\n downloader.setAttribute('href', 'data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,' + base64String);\r\n downloader.setAttribute('download', fileName);\r\n downloader.click();\r\n }\r\n\r\n public static downloadBase64StringAsFile(fileName: string, contentType: string, base64String: string) {\r\n var downloader = document.createElement('a');\r\n downloader.setAttribute('href', 'data:' + contentType + ';base64,' + base64String);\r\n downloader.setAttribute('download', fileName);\r\n downloader.click();\r\n }\r\n\r\n private static getCookie(name: string | null): string | null {\r\n let parts: string[] = document.cookie.split(name + '=');\r\n let val: string | null = null;\r\n if (parts.length === 2) {\r\n let p1: string = parts.pop() || '';\r\n val = p1.split(';').shift() || null;\r\n }\r\n return val;\r\n }\r\n\r\n private static expireCookie(name: string | null): void {\r\n if (name == null) { return; }\r\n document.cookie = encodeURIComponent(name) + '=deleted; expires=' + new Date(0).toUTCString();\r\n }\r\n\r\n// private setFormToken(): string {\r\n// var guid:string = Guid.NewGuid();\r\n// (document.getElementById('downloadToken')as any).value = guid;\r\n// return guid;\r\n// }\r\n\r\n // Prevents double-submits by waiting for a cookie from the server.\r\n // private blockResubmit() {\r\n // let that = this;\r\n // let downloadToken = that.setFormToken();\r\n\r\n // that.downloadTimer = window.setInterval(function () {\r\n // let token = that.getCookie('downloadToken');\r\n\r\n // if ((token == downloadToken) || (that.attempts == 0)) {\r\n // that.unblockSubmit();\r\n // }\r\n\r\n // that.attempts--;\r\n // }, 1000);\r\n // }\r\n\r\n // private unblockSubmit() {\r\n // let that = this;\r\n // window.clearInterval(that.downloadTimer);\r\n // that.expireCookie('downloadToken');\r\n // that.attempts = 30;\r\n // }\r\n\r\n private static checkCookie(context: DownloadContext) {\r\n let that = this;\r\n let value = that.getCookie(context.token);\r\n if (value || context.isLoaded || context.attempts === 0) {\r\n if (context.attempts === 0) {\r\n context.error = 'Download timed out.';\r\n }\r\n that.expireCookie(context.token);\r\n document.body.removeChild(context.iframe);\r\n context.callback(context.error);\r\n } else {\r\n window.setTimeout(\r\n function () {\r\n context.attempts--;\r\n that.checkCookie(context);\r\n },\r\n 500);\r\n }\r\n }\r\n}\r\n","import ApiResultHandler from './ApiResultHandler';\r\nimport FileDownload from 'src/utils/FileDownload';\r\n\r\nfunction add(obj: {}, list: any[]): Function {\r\n list.push(obj);\r\n return () => {\r\n const index = list.indexOf(obj);\r\n if (index >= 0) {\r\n list.splice(index, 1);\r\n }\r\n };\r\n}\r\n\r\nlet globalPreRequests: RequestProcessor[] = [];\r\nlet globalPostRequests: ResponseProcessor[] = [];\r\n\r\nexport enum HttpVerb {\r\n GET = 'GET',\r\n POST = 'POST',\r\n PUT = 'PUT',\r\n DELETE = 'DELETE'\r\n}\r\n\r\nexport class RequestProcessor {\r\n public success: (url: string, config: RequestInit) => RequestInit | Promise;\r\n public error: (error: any) => any;\r\n}\r\n\r\nexport class ResponseProcessor {\r\n public success: (response: Response) => Response | Promise;\r\n public error: (error: any) => any;\r\n}\r\n\r\nabstract class BaseApi {\r\n private baseUrl = '/' + ($appPath && $appPath.length > 0 ? '/' : '');\r\n private preRequests: RequestProcessor[] = [];\r\n private postRequests: ResponseProcessor[] = [];\r\n\r\n /**\r\n * Ads a pre-request processor to the api service instance.\r\n * Returns function to call to remove pre-request processor.\r\n * @param preRequest The RequestProcessor to add.\r\n */\r\n public addPreRequestProcessor(preRequest: RequestProcessor) {\r\n return add(preRequest, this.preRequests);\r\n }\r\n\r\n /**\r\n * Ads a post-request processor to the api service instance.\r\n * Returns function to call to remove post-request processor.\r\n * @param postRequest The RequestProcessor to add.\r\n */\r\n public addPostRequestProcessor(postRequest: ResponseProcessor) {\r\n return add(postRequest, this.postRequests);\r\n }\r\n\r\n /**\r\n * Clears all pre-request processor from the api service instance.\r\n */\r\n public clearPreRequestProcessor() {\r\n this.preRequests = [];\r\n }\r\n\r\n /**\r\n * Clears all post-request processor from the api service instance.\r\n */\r\n public clearPostRequestProcessor() {\r\n this.postRequests = [];\r\n }\r\n\r\n /**\r\n * Main entry point for derived api service classes to call.\r\n * @param req Payload of the body of the request.\r\n * @param requestVerb HttpVerb to use for the request.\r\n * @param relativeUrl The app-relative url path of the endpoint to hit foer the request.\r\n * @param isJson Whether or not the request to a JSON or FormData request.\r\n * @param isDownload Whether the request is to downloa a file.\r\n */\r\n protected SendRequest(req: REQ, requestVerb: string, relativeUrl: string, isJson: boolean, isDownload: boolean, signal: AbortSignal) {\r\n var requestVerbUpper = requestVerb.toUpperCase();\r\n return this.send(req, relativeUrl, requestVerbUpper, isJson, isDownload, signal);\r\n }\r\n\r\n /**\r\n * Internal entry point for all requests.\r\n * @param req Payload of the body of the request.\r\n * @param relativeUrl The app-relative url path of the endpoint to hit foer the request.\r\n * @param method HttpVerb to use for the request.\r\n * @param isJson Whether or not the request to a JSON or FormData request.\r\n * @param isDownload Whether the request is to downloa a file.\r\n */\r\n private send(req: REQ, relativeUrl: string, method: string, isJson: boolean, isDownload: boolean, signal: AbortSignal): Promise {\r\n let promise: Promise;\r\n if (isDownload) {\r\n promise = this.getFilePromise(req, relativeUrl, method, signal);\r\n } else {\r\n promise = this.getBasePromise(req, relativeUrl, method, isJson, signal);\r\n }\r\n\r\n return promise\r\n .then(response => {\r\n let ct = response.headers.get('Content-Type');\r\n if (ct && ct.indexOf('/json') > 0) {\r\n return response.json();\r\n }\r\n return null;\r\n })\r\n .catch(e => {\r\n if (DEBUG) {\r\n console.log(e);\r\n }\r\n return Promise.reject(e);\r\n });\r\n }\r\n\r\n /**\r\n * Get a base promise for all request except file downloads.\r\n * @param req Payload of the body of the request.\r\n * @param relativeUrl The app-relative url path of the endpoint to hit foer the request.\r\n * @param method HttpVerb to use for the request.\r\n * @param shouldHaveJsonBody Whether or not the request to a JSON or FormData request.\r\n */\r\n private getBasePromise(req: REQ, relativeUrl: string, method: string, shouldHaveJsonBody: boolean, signal: AbortSignal): Promise {\r\n let promise = this.getRequestPromise(req, relativeUrl, method, shouldHaveJsonBody, signal);\r\n let url = this.resolveUrl(relativeUrl);\r\n let promise2 = promise.then(args => fetch(url, { ...args, signal }).then(ApiResultHandler.handle));\r\n return this.appendToResponsePromise(req, url, method, promise2);\r\n }\r\n\r\n /**\r\n * Get a base promise for file download request.\r\n * @param req Payload of the body of the request.\r\n * @param relativeUrl The app-relative url path of the endpoint to hit foer the request.\r\n * @param method HttpVerb to use for the request.\r\n */\r\n private getFilePromise(req: REQ, relativeUrl: string, method: string, signal: AbortSignal): Promise {\r\n let promise = this.getRequestPromise(req, relativeUrl, method, true, signal);\r\n\r\n let url = this.resolveUrl(relativeUrl);\r\n if (req) {\r\n url += '?_payload=' + JSON.stringify(req);\r\n }\r\n\r\n let promise2 = promise.then(args => new Promise((resolve, reject) => {\r\n FileDownload.download(\r\n url,\r\n function (error: string | null) {\r\n if (error) {\r\n reject(error);\r\n } else {\r\n resolve(new Response());\r\n }\r\n });\r\n })\r\n );\r\n return this.appendToResponsePromise(req, url, method, promise2);\r\n }\r\n\r\n /**\r\n * Creates the RequestInit object to use for the request.\r\n * @param req Payload of the body of the request.\r\n * @param method HttpVerb to use for the request.\r\n * @param isJson Whether or not the request to a JSON or FormData request.\r\n */\r\n private createRequestObject(req: REQ, method: string, isJson: boolean, signal: AbortSignal): RequestInit {\r\n if (!isJson) {\r\n return this.createRequestObjectFormData(req, method, signal);\r\n }\r\n\r\n var requestInfo: RequestInit = {\r\n method: method,\r\n headers: {\r\n 'Accept': 'application/json',\r\n 'Content-Type': 'application/json',\r\n 'Cache': 'no-cache'\r\n },\r\n credentials: 'include',\r\n signal: signal,\r\n };\r\n\r\n if (method !== HttpVerb.GET) {\r\n requestInfo.body = JSON.stringify(req);\r\n }\r\n return requestInfo;\r\n }\r\n\r\n /**\r\n * Creates the RequestInit object to use for the request that uses FormData.\r\n * @param req Payload of the body of the request.\r\n * @param method HttpVerb to use for the request.\r\n */\r\n private createRequestObjectFormData(req: REQ, method: string, signal: AbortSignal): RequestInit {\r\n if (!(req instanceof FormData)) {\r\n throw new Error('request object is not of type FormData');\r\n }\r\n var requestInfo: RequestInit = {\r\n method: method,\r\n body: req as any,\r\n credentials: 'include',\r\n signal: signal,\r\n };\r\n\r\n return requestInfo;\r\n }\r\n\r\n /**\r\n * Gets the initial promise based on the RequestInit with pre-request processors attached.\r\n * @param req Payload of the body of the request.\r\n * @param relativeUrl The app-relative url path of the endpoint to hit foer the request.\r\n * @param method HttpVerb to use for the request.\r\n * @param shouldHaveJsonBody Whether or not the request to a JSON or FormData request.\r\n */\r\n private getRequestPromise(req: REQ, relativeUrl: string, method: string, shouldHaveJsonBody: boolean, signal: AbortSignal): Promise {\r\n let info = this.createRequestObject(req, method, shouldHaveJsonBody, signal);\r\n let promise = Promise.resolve(info);\r\n\r\n let globalPreRequestsRev = globalPreRequests.reverse();\r\n globalPreRequestsRev.forEach(function (processor: RequestProcessor) {\r\n promise = promise.then(args => processor.success(relativeUrl, args), processor.error);\r\n });\r\n\r\n let preRequestsRev = this.preRequests.reverse();\r\n preRequestsRev.forEach(function (processor: RequestProcessor) {\r\n promise = promise.then(args => processor.success(relativeUrl, args), processor.error);\r\n });\r\n return promise;\r\n }\r\n\r\n /**\r\n * Append post-requests processors to Response promise.\r\n * @param req Payload of the body of the request.\r\n * @param resolvedUrl Resolved site-root url.\r\n * @param method HttpVerb to use for the request.\r\n * @param promise Whether or not the request to a JSON or FormData request.\r\n */\r\n private appendToResponsePromise(req: REQ, resolvedUrl: string, method: string, promise: Promise): Promise {\r\n let globalPostRequestsRev = globalPostRequests.reverse();\r\n globalPostRequestsRev.forEach(function (processor: ResponseProcessor) {\r\n promise = promise.then(args => processor.success, processor.error);\r\n });\r\n\r\n let postRequestsRev = this.postRequests.reverse();\r\n postRequestsRev.forEach(function (processor: ResponseProcessor) {\r\n promise = promise.then(processor.success, processor.error);\r\n });\r\n\r\n return promise;\r\n }\r\n\r\n /**\r\n * Resolves a relative url to a site-root url.\r\n * @param relativeUrl Relative url to resolve.\r\n */\r\n private resolveUrl(relativeUrl: string): string {\r\n return new URL(relativeUrl, window.location.origin).href;\r\n }\r\n}\r\n\r\n/**\r\n * Adds a pre-request processor to global list or just one api service instance.\r\n * Returns function to call to remove pre-request processor.\r\n * @param preRequest The RequestProcessor to add.\r\n * @param api optional api instance (if null add to global list).\r\n */\r\nexport function addPreRequestProcessor(preRequest: RequestProcessor, api?: BaseApi | null | undefined) {\r\n if (api) {\r\n return api.addPreRequestProcessor(preRequest);\r\n }\r\n return add(preRequest, globalPreRequests);\r\n\r\n}\r\n\r\n/**\r\n * Adds a post-request processor to global list or just one api service instance.\r\n * Returns function to call to remove post-request processor.\r\n * @param postRequest The RequestProcessor to add.\r\n * @param api optional api instance (if null add to global list).\r\n */\r\nexport function addPostRequestProcessor(postRequest: ResponseProcessor, api?: BaseApi | null | undefined) {\r\n if (api) {\r\n return api.addPostRequestProcessor(postRequest);\r\n }\r\n return add(postRequest, globalPostRequests);\r\n\r\n}\r\n\r\n/**\r\n * Clears all pre-request processor from the api service instance.\r\n * @param api optional api instance (if null clears global list).\r\n */\r\nexport function clearPreRequestProcessors(api: BaseApi | null | undefined) {\r\n if (api) {\r\n api.clearPreRequestProcessor();\r\n } else {\r\n globalPreRequests = [];\r\n }\r\n}\r\n\r\n/**\r\n * Clears all post-request processor from the api service instance.\r\n * @param api optional api instance (if null clears global list).\r\n */\r\nexport function clearPostRequestProcessors(api: BaseApi | null | undefined) {\r\n if (api) {\r\n api.clearPostRequestProcessor();\r\n } else {\r\n globalPostRequests = [];\r\n }\r\n}\r\n\r\nexport default BaseApi;\r\n","// Service TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nimport BaseApi from './BaseApi';\r\nimport GrowerDTO from '../models/GrowerDTO';\r\nimport BinInfoDTO from '../models/BinInfoDTO';\r\nimport UserAlertSettingsDTO from '../models/UserAlertSettingsDTO';\r\nimport ForecastDTO from '../models/ForecastDTO';\r\nimport HourlyDTO from '../models/HourlyDTO';\r\nimport EnterpriseDTO from '../models/EnterpriseDTO';\r\n\r\nexport class EnterpriseApiService extends BaseApi {\r\n\r\n // get: api/enterprise/grower/${growerId}\r\n public getGrower(growerId: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/grower/${growerId}`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/growerids?onlyActive=${onlyActive}&includeExternal=${includeExternal}\r\n public getGrowerIDs(onlyActive?: boolean | null, includeExternal?: boolean | null, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/growerids`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (onlyActive != null) {\r\n url += `${prefix}onlyActive=${onlyActive}`;\r\n prefix = '&';\r\n }\r\n if (includeExternal != null) {\r\n url += `${prefix}includeExternal=${includeExternal}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/groweridsofuser?userID=${userID}\r\n public getGrowerIDsOfUser(userID: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/groweridsofuser`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (userID != null) {\r\n url += `${prefix}userID=${userID}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/growersofuserbyusername?username=${encodeURIComponent(username)}\r\n public getGrowersOfUserByUsername(username: string, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/growersofuserbyusername`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (username != null) {\r\n url += `${prefix}username=${encodeURIComponent(username)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/enterprise/addGrower\r\n public addGrower(grower: GrowerDTO, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/addGrower`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(grower, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/enterprise/updateGrower\r\n public updateGrower(grower: GrowerDTO, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/updateGrower`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(grower, 'post', url, true, false, _signal);\r\n }\r\n\r\n // delete: api/enterprise/removeGrower?growerId=${growerId}\r\n public removeGrower(growerId: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/removeGrower`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (growerId != null) {\r\n url += `${prefix}growerId=${growerId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'delete', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/weather?lat=${lat}&lng=${lng}\r\n public getWeatherForecast(lat: number, lng: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/weather`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (lat != null) {\r\n url += `${prefix}lat=${lat}`;\r\n prefix = '&';\r\n }\r\n if (lng != null) {\r\n url += `${prefix}lng=${lng}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/hourlyWeather?lat=${lat}&lng=${lng}\r\n public get(lat: number, lng: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/hourlyWeather`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (lat != null) {\r\n url += `${prefix}lat=${lat}`;\r\n prefix = '&';\r\n }\r\n if (lng != null) {\r\n url += `${prefix}lng=${lng}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/enterprise/addbin\r\n public addBin(binInfo: BinInfoDTO, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/addbin`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(binInfo, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/enterprise/updatebin\r\n public updateBin(binInfo: BinInfoDTO, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/updatebin`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(binInfo, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/enterprise/deletebin?binId=${binId}\r\n public deleteBin(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/deletebin`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/getbinids\r\n public getBinIDs(signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/getbinids`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/getbinidsbygrower?growerID=${growerID}\r\n public getBinIDsByGrower(growerID: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/getbinidsbygrower`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (growerID != null) {\r\n url += `${prefix}growerID=${growerID}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/enterprise/targetMoisture/${binId}?mc=${mc}\r\n public changeBinTargetMoisture(binId: number, mc: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/targetMoisture/${binId}`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (mc != null) {\r\n url += `${prefix}mc=${mc}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/userAlertSettings\r\n public getUserAlertSettings(signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/userAlertSettings`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/enterprise/userAlertSettings\r\n public changeUserAlertSettings(dto: UserAlertSettingsDTO, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/userAlertSettings`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(dto, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/enterprise/targetPrice/${growerId}?tp=${tp}\r\n public changeGrowerTargetPrice(growerId: number, tp: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/targetPrice/${growerId}`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (tp != null) {\r\n url += `${prefix}tp=${tp}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/getbinbyid?id=${id}\r\n public getBinByID(id: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/getbinbyid`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (id != null) {\r\n url += `${prefix}id=${id}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/getbinbyShivversId?id=${id}\r\n public getBinByShivversId(id: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/getbinbyShivversId`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (id != null) {\r\n url += `${prefix}id=${id}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/getunuseddevices\r\n public getUnusedDevicesForBin(signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/getunuseddevices`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/detail/${growerId}?onlyActive=${onlyActive}\r\n public getEnterpriseDetail(growerId: number, onlyActive?: boolean | null, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/detail/${growerId}`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (onlyActive != null) {\r\n url += `${prefix}onlyActive=${onlyActive}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/enterprise/detail/byexternalid/${externalid}\r\n public getEnterpriseDetailByExternalId(externalid: number, signal?: AbortSignal): Promise {\r\n let url = `api/enterprise/detail/byexternalid/${externalid}`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n}\r\nvar service = new EnterpriseApiService();\r\nexport default service;\r\n","// Models TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\n// @ts-ignore\r\nimport dayjs, { Dayjs } from \"dayjs\";\r\nimport InterfaceConstructor from './InterfaceConstructor';\r\nimport EnterpriseCropDataDTO from './EnterpriseCropDataDTO';\r\nimport GrowerDTO from './GrowerDTO';\r\nimport BinInfoDTO from './BinInfoDTO';\r\n\r\ninterface EnterpriseDTO {\r\n id: number;\r\n allCropData: EnterpriseCropDataDTO[] | null;\r\n grower: GrowerDTO | null;\r\n binInfoList: BinInfoDTO[] | null;\r\n}\r\nconst EnterpriseDTO: InterfaceConstructor = {\r\n create: (initValues?: {} | null | undefined) => {\r\n return Object.assign(\r\n {\r\n id: 0,\r\n allCropData: [],\r\n grower: null,\r\n binInfoList: [],\r\n },\r\n initValues);\r\n }\r\n};\r\n\r\nexport default EnterpriseDTO;\r\n","// Models TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\n// @ts-ignore\r\nimport dayjs, { Dayjs } from \"dayjs\";\r\nimport InterfaceConstructor from './InterfaceConstructor';\r\nimport BinInfoDTO from './BinInfoDTO';\r\nimport EnterpriseCropDataDTO from './EnterpriseCropDataDTO';\r\n\r\ninterface SiteDTO {\r\n id: number;\r\n growerId: number;\r\n siteName: string | null;\r\n siteLat: number;\r\n siteLng: number;\r\n location: string | null;\r\n mapsZoomLevel: number | null;\r\n binList: BinInfoDTO[] | null;\r\n siteCropData: EnterpriseCropDataDTO[] | null;\r\n}\r\nconst SiteDTO: InterfaceConstructor = {\r\n create: (initValues?: {} | null | undefined) => {\r\n return Object.assign(\r\n {\r\n id: 0,\r\n growerId: 0,\r\n siteName: null,\r\n siteLat: 0,\r\n siteLng: 0,\r\n location: null,\r\n mapsZoomLevel: null,\r\n binList: [],\r\n siteCropData: [],\r\n },\r\n initValues);\r\n }\r\n};\r\n\r\nexport default SiteDTO;\r\n","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nexport default class Role {\r\n public static readonly ADMIN: string = 'Admin';\r\n public static readonly GROWER: string = 'Grower';\r\n public static readonly POWERUSER: string = 'PowerUser';\r\n public static readonly SUPPORT: string = 'Support';\r\n public static readonly DEALER: string = 'Dealer';\r\n public static readonly DSM: string = 'DSM';\r\n public static readonly HEMPDSM: string = 'Hemp DSM';\r\n public static readonly SUPERADMIN: string = 'Super Admin';\r\n public static readonly VIEWER: string = 'Viewer';\r\n}\r\n","// Service TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nimport BaseApi from './BaseApi';\r\nimport LoginDTO from '../models/LoginDTO';\r\nimport ResetPasswordRequestModel from '../models/ResetPasswordRequestModel';\r\nimport ChangePasswordRequestModel from '../models/ChangePasswordRequestModel';\r\nimport UserSecurityDTO from '../models/UserSecurityDTO';\r\n\r\nexport class AccountApiService extends BaseApi {\r\n\r\n // get: api/Account/backfillaccounts\r\n public backFillAccounts(signal?: AbortSignal): Promise {\r\n let url = `api/Account/backfillaccounts`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/Account/Login\r\n public login(vm: LoginDTO, signal?: AbortSignal): Promise {\r\n let url = `api/Account/Login`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(vm, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/Account/Logout\r\n public logout(signal?: AbortSignal): Promise {\r\n let url = `api/Account/Logout`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/Account/GetCurrentUser\r\n public getCurrentUser(signal?: AbortSignal): Promise {\r\n let url = `api/Account/GetCurrentUser`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/Account/RequestResetPassword\r\n public requestResetPassword(model: ResetPasswordRequestModel, signal?: AbortSignal): Promise {\r\n let url = `api/Account/RequestResetPassword`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(model, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/Account/CanResetPassword\r\n public canResetPassword(model: ResetPasswordRequestModel, signal?: AbortSignal): Promise {\r\n let url = `api/Account/CanResetPassword`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(model, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/Account/ResetPassword\r\n public resetPassword(model: ResetPasswordRequestModel, signal?: AbortSignal): Promise {\r\n let url = `api/Account/ResetPassword`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(model, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/Account/ChangePasswordAdminOverride?userId=${userId}&newPasswordDTO=${encodeURIComponent(newPasswordDTO)}\r\n public changePasswordAdminOverride(userId: number, newPasswordDTO: string, signal?: AbortSignal): Promise {\r\n let url = `api/Account/ChangePasswordAdminOverride`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (userId != null) {\r\n url += `${prefix}userId=${userId}`;\r\n prefix = '&';\r\n }\r\n if (newPasswordDTO != null) {\r\n url += `${prefix}newPasswordDTO=${encodeURIComponent(newPasswordDTO)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/Account/ChangePassword\r\n public changePassword(model: ChangePasswordRequestModel, signal?: AbortSignal): Promise {\r\n let url = `api/Account/ChangePassword`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(model, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/Account/LockUser?userId=${userId}\r\n public lockUser(userId: number, signal?: AbortSignal): Promise {\r\n let url = `api/Account/LockUser`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (userId != null) {\r\n url += `${prefix}userId=${userId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/Account/UnlockUser?userId=${userId}\r\n public unlockUser(userId: number, signal?: AbortSignal): Promise {\r\n let url = `api/Account/UnlockUser`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (userId != null) {\r\n url += `${prefix}userId=${userId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/Account/DeleteUser?userId=${userId}\r\n public deleteUser(userId: number, signal?: AbortSignal): Promise {\r\n let url = `api/Account/DeleteUser`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (userId != null) {\r\n url += `${prefix}userId=${userId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/Account/Ping\r\n public ping(signal?: AbortSignal): Promise {\r\n let url = `api/Account/Ping`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/Account/GetUserByIp?ip=${encodeURIComponent(ip)}\r\n public getUserByIp(ip: string, signal?: AbortSignal): Promise {\r\n let url = `api/Account/GetUserByIp`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (ip != null) {\r\n url += `${prefix}ip=${encodeURIComponent(ip)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n}\r\nvar service = new AccountApiService();\r\nexport default service;\r\n","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nexport default class PermissionNames {\r\n public static readonly CONTROLSREAD: string = 'controls.read';\r\n public static readonly CONTROLSWRITE: string = 'controls.write';\r\n}\r\n","import { userInfo } from 'os';\r\nimport PermissionNames from 'src/consts/PermissionNames';\r\nimport UserSecurityDTO from './UserSecurityDTO';\r\n\r\nexport default class UserSecurity {\r\n public readonly firstName: string | null;\r\n public readonly lastName: string | null;\r\n public readonly userId: number;\r\n public readonly userName: string | null;\r\n private readonly roles: ReadonlyArray;\r\n public readonly externalId: string | null;\r\n public readonly isExternal: boolean | null;\r\n public readonly permissions: ReadonlyArray;\r\n\r\n constructor(userinfo: UserSecurityDTO) {\r\n this.firstName = userinfo.firstName;\r\n this.lastName = userinfo.lastName;\r\n this.userId = userinfo.userId;\r\n this.userName = userinfo.userName;\r\n this.roles = Object.freeze(userinfo.roles);\r\n this.externalId = userinfo.externalId;\r\n this.isExternal = userinfo.isExternal;\r\n this.permissions = userinfo.permissions;\r\n }\r\n\r\n isInRole(role: UserSecurity['roles'][0]) {\r\n return this.roles.indexOf(role) >= 0;\r\n }\r\n\r\n canViewControls() {\r\n return this.permissions.includes(PermissionNames.CONTROLSREAD) || this.permissions.includes(PermissionNames.CONTROLSWRITE);\r\n }\r\n \r\n canModifyControls() {\r\n return this.permissions.includes(PermissionNames.CONTROLSWRITE);\r\n }\r\n\r\n getRoles() {\r\n return this.roles.slice(0);\r\n }\r\n}\r\n","import { Dispatch } from 'redux';\r\nimport BaseAction from './BaseAction';\r\nimport History from 'src/utils/HistoryUtil';\r\nimport AccountApiService from 'src/api/AccountApiService';\r\nimport LoginDTO from 'src/models/LoginDTO';\r\nimport UserSecurityDTO from 'src/models/UserSecurityDTO';\r\nimport UserSecurity from 'src/models/UserSecurity';\r\nimport Routes from 'src/consts/Routes';\r\nimport { StateStoreModel } from 'src/redux/state/StateStoreModel';\r\nimport { queryClient } from 'src';\r\n\r\nexport type UserSessionAction = BaseAction;\r\n\r\nexport enum UserSessionActionTypes {\r\n USER_SESSION_CHECKING = 'USER_SESSION_CHECKING',\r\n USER_SESSION_CHECK = 'USER_SESSION_CHECK',\r\n USER_SESSION_LOADING = 'USER_SESSION_LOADING',\r\n USER_SESSION_LOGIN = 'USER_SESSION_LOGIN',\r\n USER_SESSION_LOGIN_ERROR = 'USER_SESSION_LOGIN_ERROR',\r\n USER_SESSION_LOGOUT = 'USER_SESSION_LOGOUT',\r\n USER_SESSION_CHANGED = 'USER_SESSION_CHANGED',\r\n USER_GROWER_CHANGED = 'USER_GROWER_CHANGED'\r\n}\r\n\r\nexport const UserSessionActions = {\r\n login: UserSessionLoginAction,\r\n logout: UserSessionLogoutAction,\r\n check: UserSessionCheckAction,\r\n refresh: UserSessionRefreshAction,\r\n changeGrower: UserGrowerCheckedAction\r\n};\r\n\r\nfunction UserSessionLogoutAction() {\r\n return (dispatch: Dispatch) => {\r\n AccountApiService.logout()\r\n .then(() => {\r\n dispatch(UserSessionChangedAction(null));\r\n })\r\n .catch((results) => {\r\n dispatch(UserSessionLoadingAction(false));\r\n })\r\n .then(() => { // .finally()\r\n queryClient.clear();\r\n History.push(Routes.LOGIN);\r\n });\r\n };\r\n}\r\n\r\nfunction UserSessionLoginAction(model: LoginDTO) {\r\n return (dispatch: Dispatch) => {\r\n dispatch(UserSessionLoadingAction(true));\r\n AccountApiService.login(model)\r\n .then(() => {\r\n _UserSessionCheckAction(dispatch, true);\r\n })\r\n .catch((results) => {\r\n dispatch(UserSessionLoginErrorAction(results));\r\n dispatch(UserSessionLoadingAction(false));\r\n })\r\n .then(() => { // .finally()\r\n });\r\n };\r\n}\r\n\r\nfunction UserSessionCheckAction() {\r\n return (dispatch: Dispatch, getState: () => StateStoreModel) => {\r\n let current = getState().UserSession;\r\n if (current.Value) {\r\n dispatch(UserSessionChangedAction(current.Value));\r\n return;\r\n }\r\n dispatch(UserSessionCheckingAction(true));\r\n _UserSessionCheckAction(dispatch, false);\r\n };\r\n}\r\n\r\nfunction _UserSessionCheckAction(dispatch: Dispatch, isLoading: boolean) {\r\n let userAction = isLoading ? UserSessionLoadingAction : UserSessionCheckingAction;\r\n AccountApiService.getCurrentUser()\r\n .then((results: UserSecurityDTO | null) => {\r\n if (results) {\r\n dispatch(UserSessionChangedAction(new UserSecurity(results)));\r\n } else {\r\n dispatch(UserSessionChangedAction(null));\r\n }\r\n dispatch(userAction(false));\r\n }).catch((results) => {\r\n dispatch(userAction(false));\r\n });\r\n}\r\n\r\nfunction UserSessionRefreshAction(newUserSession: UserSecurityDTO | null) {\r\n return (dispatch: Dispatch) => {\r\n // Start session loading\r\n if (newUserSession) {\r\n dispatch(UserSessionChangedAction(new UserSecurity(newUserSession)));\r\n } else {\r\n dispatch(UserSessionChangedAction(null));\r\n }\r\n };\r\n}\r\n\r\nfunction UserSessionLoginErrorAction(error: any): UserSessionAction {\r\n return {\r\n type: UserSessionActionTypes.USER_SESSION_LOGIN_ERROR,\r\n data: error\r\n };\r\n}\r\n\r\nfunction UserSessionCheckingAction(isChecking: boolean): UserSessionAction {\r\n return {\r\n type: UserSessionActionTypes.USER_SESSION_CHECKING,\r\n data: isChecking\r\n };\r\n}\r\n\r\nfunction UserSessionLoadingAction(isLoading: boolean): UserSessionAction {\r\n return {\r\n type: UserSessionActionTypes.USER_SESSION_LOADING,\r\n data: isLoading\r\n };\r\n}\r\n\r\nfunction UserSessionChangedAction(model?: UserSecurity | null | undefined): UserSessionAction {\r\n return {\r\n type: UserSessionActionTypes.USER_SESSION_CHANGED,\r\n data: model\r\n };\r\n}\r\n\r\nfunction UserGrowerCheckedAction(grower: GrowerSlim | null) {\r\n return (dispatch: Dispatch) => {\r\n dispatch(UserGrowerChangedAction(grower));\r\n // History.push({\r\n // pathname: `/`,\r\n // });\r\n };\r\n}\r\n\r\nexport interface GrowerSlim {\r\n growerId: number,\r\n growerName: string,\r\n isExternal: boolean,\r\n externalId: string | null,\r\n}\r\n\r\nfunction UserGrowerChangedAction(grower: GrowerSlim | null): UserSessionAction {\r\n return {\r\n type: UserSessionActionTypes.USER_GROWER_CHANGED,\r\n data: grower\r\n };\r\n}\r\n","import { UserSessionStoreState } from '../state/StateStoreModel';\r\nimport { UserSessionActionTypes, UserSessionAction, GrowerSlim } from '../actions/UserSessionActions';\r\n\r\nexport const InitialUserSessionStoreState: UserSessionStoreState = {\r\n Loading: false,\r\n Checking: true,\r\n Value: null,\r\n Error: null,\r\n GrowerID: null,\r\n GrowerName: null,\r\n grower: null,\r\n};\r\n\r\nexport function UserSession(state: UserSessionStoreState = InitialUserSessionStoreState, action: UserSessionAction): UserSessionStoreState {\r\n let newState: UserSessionStoreState = state;\r\n switch (action.type) {\r\n case UserSessionActionTypes.USER_SESSION_CHANGED:\r\n let us = action.data ? Object.freeze(action.data) : action.data;\r\n\r\n // likely a logout since null is passed after the logout endpoint returns success\r\n // todo: this should really be its own action type...\r\n const isloggingOut = us == null;\r\n\r\n if (isloggingOut) {\r\n newState = { ...state, Value: us, Error: null, grower: null, GrowerID: null, GrowerName: null };\r\n }\r\n else {\r\n // just changing grower / reloading the homepage\r\n newState = { ...state, Value: us, Error: null };\r\n }\r\n break;\r\n case UserSessionActionTypes.USER_SESSION_LOADING:\r\n let error = state.Error;\r\n if (action.data) {\r\n error = null;\r\n }\r\n newState = { ...state, Loading: action.data, Error: error };\r\n break;\r\n case UserSessionActionTypes.USER_SESSION_CHECKING:\r\n newState = { ...state, Checking: action.data };\r\n break;\r\n case UserSessionActionTypes.USER_SESSION_LOGIN_ERROR:\r\n newState = { ...state, Error: action.data };\r\n break;\r\n case UserSessionActionTypes.USER_GROWER_CHANGED:\r\n console.log(\"user session changed\", action.data);\r\n const data = action.data as GrowerSlim;\r\n newState = { ...state, GrowerID: action.data.growerId, GrowerName: action.data.growerName, grower: {...data} };\r\n break;\r\n default:\r\n return newState;\r\n }\r\n return Object.freeze(newState);\r\n}\r\n","import { VersionStoreState } from '../state/StateStoreModel';\r\nimport { VersionActionTypes, VersionAction } from '../actions/VersionActions';\r\n\r\nexport const InitialVersionStoreState: VersionStoreState = {\r\n Client: $apiVersion,\r\n Api: null,\r\n Outdated: false\r\n};\r\n\r\nexport function Version(state: VersionStoreState = InitialVersionStoreState, action: VersionAction): VersionStoreState {\r\n let newState: VersionStoreState = state;\r\n switch (action.type) {\r\n case VersionActionTypes.VERSION_OUTDATED:\r\n let v = action.data || {};\r\n newState = { ...state, ...v, Outdated: true };\r\n break;\r\n case VersionActionTypes.VERSION_RESET:\r\n newState = { ...state, Outdated: false };\r\n break;\r\n default:\r\n break;\r\n }\r\n return newState;\r\n}\r\n","import { combineReducers } from 'redux';\r\nimport { UserSession } from './UserSessionReducer';\r\nimport { Version } from './VersionReducer';\r\nimport { StateStoreModel } from '../state/StateStoreModel';\r\nimport { AnyAction } from 'redux';\r\n\r\nconst rootReducer = combineReducers({\r\n UserSession,\r\n Version\r\n});\r\n\r\nexport default function freezeState(state: StateStoreModel, action: AnyAction): StateStoreModel {\r\n let nextState = rootReducer(state, action);\r\n return Object.isFrozen(nextState) ? nextState : Object.freeze(nextState);\r\n}\r\n","import { createStore, applyMiddleware } from 'redux';\r\nimport thunkMiddleware from 'redux-thunk';\r\nimport rootReducer from './reducers/RootReducer';\r\n\r\nexport const store = createStore(\r\n rootReducer,\r\n applyMiddleware(\r\n thunkMiddleware\r\n )\r\n);\r\n","import { store } from 'src/redux/Store';\r\nimport UserSecurity from 'src/models/UserSecurity';\r\n\r\nexport default class CurrentUser {\r\n public static Get(): UserSecurity | null {\r\n return store.getState().UserSession.Value;\r\n }\r\n}","import Role from 'src/consts/Role';\r\nimport UserSecurityDTO from 'src/models/UserSecurityDTO';\r\nimport CurrentUser from './CurrentUser';\r\nexport default class RoleUtil {\r\n \r\n public static currentUserName(): string{\r\n\r\n return CurrentUser.Get()?.userName ?? \"N/A\";\r\n }\r\n\r\n public static currentUserisAtLeastPowerUser(): boolean {\r\n const roles = [Role.ADMIN, Role.SUPERADMIN, Role.POWERUSER, Role.SUPPORT];\r\n const atLeastPowerUser = RoleUtil.currentUserHasAnyOfRoles(roles);\r\n return atLeastPowerUser;\r\n\r\n }\r\n public static currentUserIsAdmin(): boolean {\r\n const roles = [Role.ADMIN, Role.SUPERADMIN];\r\n const isAdmin = RoleUtil.currentUserHasAnyOfRoles(roles);\r\n return isAdmin;\r\n }\r\n\r\n public static currentUserIsDealer(): boolean {\r\n const roles = [Role.DEALER];\r\n return RoleUtil.currentUserHasAnyOfRoles(roles);\r\n }\r\n\r\n public static NearAdminRoles = [Role.ADMIN, Role.DEALER, Role.DSM, Role.HEMPDSM, Role.SUPERADMIN];\r\n public static AdminRoles = [Role.ADMIN, Role.SUPERADMIN];\r\n\r\n public static CanViewBinFleet(): boolean {\r\n return RoleUtil.currentUserHasAnyOfRoles(RoleUtil.NearAdminRoles);\r\n }\r\n\r\n public static CanModifyControls(): boolean {\r\n return CurrentUser.Get()?.canModifyControls() ?? false;\r\n }\r\n public static IsViewOnly() {\r\n return CurrentUser.Get()?.canViewControls() ?? false;\r\n }\r\n\r\n public static CanViewUserList(): boolean {\r\n return RoleUtil.currentUserHasAnyOfRoles(RoleUtil.NearAdminRoles);\r\n }\r\n\r\n public static CanViewSidenav(): boolean {\r\n return RoleUtil.currentUserHasAnyOfRoles(RoleUtil.NearAdminRoles);\r\n }\r\n\r\n public static CanSeeRegisterProductButton(): boolean {\r\n return !RoleUtil.currentUserHasAnyOfRoles([Role.ADMIN])\r\n }\r\n\r\n\r\n public static currentUserHasAnyOfRoles(roles: UserSecurityDTO['roles']): boolean {\r\n let currentUser = CurrentUser.Get();\r\n\r\n if (currentUser) {\r\n for (let role of roles) {\r\n if (currentUser.isInRole(role)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n}\r\n","// Service TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nimport BaseApi from './BaseApi';\r\nimport BinSettingsDTO from '../models/BinSettingsDTO';\r\nimport SecretDTO from '../models/SecretDTO';\r\nimport SyncBatchDTO from '../models/SyncBatchDTO';\r\nimport WeatherMonitorChangeSettingsDTO from '../models/WeatherMonitorChangeSettingsDTO';\r\nimport DesiredPropertiesDTO from '../models/DesiredPropertiesDTO';\r\nimport PowerCycleBoardDTO from '../models/PowerCycleBoardDTO';\r\nimport SetIgnoredSolenoidsRequestDTO from '../models/SetIgnoredSolenoidsRequestDTO';\r\nimport SetIgnoredSensorRequestDTO from '../models/SetIgnoredSensorRequestDTO';\r\nimport ValvePositionDTO from '../models/ValvePositionDTO';\r\nimport UnloadDTO from '../models/UnloadDTO';\r\nimport LoadDTO from '../models/LoadDTO';\r\nimport ReportedWeatherMonitorSettingsDTO from '../models/ReportedWeatherMonitorSettingsDTO';\r\nimport SetGrainTypeRquestDTO from '../models/SetGrainTypeRquestDTO';\r\nimport ChangeHeaterModeDTO from '../models/ChangeHeaterModeDTO';\r\nimport HeaterModeOptionsRequestDTO from '../models/HeaterModeOptionsRequestDTO';\r\nimport FanOnAllowedTimeRequestDTO from '../models/FanOnAllowedTimeRequestDTO';\r\nimport FanOnAllowedTimePerWeekRequestDTO from '../models/FanOnAllowedTimePerWeekRequestDTO';\r\nimport UpdateMaxLayerGrainTempRequestDTO from '../models/UpdateMaxLayerGrainTempRequestDTO';\r\nimport LayerPercentageDTO from '../models/LayerPercentageDTO';\r\nimport DesiredRequestSettingsDTO from '../models/DesiredRequestSettingsDTO';\r\nimport AlertHistoryRequestDTO from '../models/AlertHistoryRequestDTO';\r\nimport ConfigureBasicSettingsRequestDTO from '../models/ConfigureBasicSettingsRequestDTO';\r\nimport RegisterProductDTO from '../models/RegisterProductDTO';\r\nimport ConfigureSystemRequestDTO from '../models/ConfigureSystemRequestDTO';\r\nimport BatchDTO from '../models/BatchDTO';\r\nimport GeneralDTO from '../models/GeneralDTO';\r\nimport ECDeviceDTO from '../models/ECDeviceDTO';\r\nimport BinAssemblyDTO from '../models/BinAssemblyDTO';\r\nimport ErrorListDTO from '../models/ErrorListDTO';\r\nimport BinInfoDTO from '../models/BinInfoDTO';\r\nimport BinDTO from '../models/BinDTO';\r\nimport ResponseResultDTO from '../models/ResponseResultDTO';\r\nimport SnapshotDTO from '../models/SnapshotDTO';\r\nimport CustomRoutineNamesDTO from '../models/CustomRoutineNamesDTO';\r\nimport UploadResultDTO from '../models/UploadResultDTO';\r\nimport PingAndBinDTO from '../models/PingAndBinDTO';\r\nimport DristackmoduleLogsDTO from '../models/DristackmoduleLogsDTO';\r\nimport MoistureContentSnapshotDTO from '../models/MoistureContentSnapshotDTO';\r\nimport OfflineBinReportDTO from '../models/OfflineBinReportDTO';\r\nimport HourlyDTO from '../models/HourlyDTO';\r\nimport MoistureForecastDTO from '../models/MoistureForecastDTO';\r\nimport ResultCameraDTO from '../models/ResultCameraDTO';\r\nimport ResponseCameraImageDTO from '../models/ResponseCameraImageDTO';\r\nimport LiveStreamForDeviceDTO from '../models/LiveStreamForDeviceDTO';\r\nimport CellStrengthDatasetDTO from '../models/CellStrengthDatasetDTO';\r\nimport OpiCableDatasetDTO from '../models/OpiCableDatasetDTO';\r\nimport BinSenseCableDatasetDTO from '../models/BinSenseCableDatasetDTO';\r\nimport AlertHistoryResponseDTO from '../models/AlertHistoryResponseDTO';\r\nimport FanHeaterHistoryChartResponseDTO from '../models/FanHeaterHistoryChartResponseDTO';\r\nimport LayerSummaryDatasetDTO from '../models/LayerSummaryDatasetDTO';\r\nimport DatasetDoubleDTO from '../models/DatasetDoubleDTO';\r\nimport CO2DatasetDTO from '../models/CO2DatasetDTO';\r\nimport MoistureRemovalDatapointDTO from '../models/MoistureRemovalDatapointDTO';\r\nimport ModeChangeHistoryDatasetDTO from '../models/ModeChangeHistoryDatasetDTO';\r\nimport FanHeaterChangeTimeDatapointDTO from '../models/FanHeaterChangeTimeDatapointDTO';\r\nimport TargetLayerDatasetDTO from '../models/TargetLayerDatasetDTO';\r\nimport RangeFinderDatapointDTO from '../models/RangeFinderDatapointDTO';\r\nimport UserLoadDatapointDTO from '../models/UserLoadDatapointDTO';\r\nimport CalculateGrainHeightResponseDTO from '../models/CalculateGrainHeightResponseDTO';\r\nimport PremierPlusBinDTO from '../models/PremierPlusBinDTO';\r\n\r\nexport class BinApiService extends BaseApi {\r\n\r\n // post: api/grainbin/SetBinSettingsOverrides?deviceName=${encodeURIComponent(deviceName)}\r\n public setBinSettingsOverrides(deviceName: string, binSettings: BinSettingsDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetBinSettingsOverrides`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(binSettings, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/StopCustomRoutine?binId=${binId}\r\n public stopCustomRoutine(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/StopCustomRoutine`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/UploadCustomRoutine?binId=${binId}\r\n public uploadCustomRoutine(binId: number, file: File, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/UploadCustomRoutine`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(file, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/DeleteCustomRoutine?binId=${binId}&routineName=${encodeURIComponent(routineName)}\r\n public deleteCustomRoutine(binId: number, routineName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/DeleteCustomRoutine`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (routineName != null) {\r\n url += `${prefix}routineName=${encodeURIComponent(routineName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/RunCustomRoutineByName?binId=${binId}&routineName=${encodeURIComponent(routineName)}\r\n public runCustomRoutineByName(binId: number, routineName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/RunCustomRoutineByName`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (routineName != null) {\r\n url += `${prefix}routineName=${encodeURIComponent(routineName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/setApiKey\r\n public setApiKey(secret: SecretDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/setApiKey`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(secret, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/syncBatch\r\n public syncBatchWithEdge(syncBatchDTO: SyncBatchDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/syncBatch`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(syncBatchDTO, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/downloadallloads\r\n public downloadAllLoads(signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/downloadallloads`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, true, _signal);\r\n }\r\n\r\n // post: api/grainbin/setWeatherMonitorBypass?azureDeviceId=${encodeURIComponent(azureDeviceId)}\r\n public setWeatherMonitorBypass(azureDeviceId: string, weatherMonitorSettingsDTO: WeatherMonitorChangeSettingsDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/setWeatherMonitorBypass`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (azureDeviceId != null) {\r\n url += `${prefix}azureDeviceId=${encodeURIComponent(azureDeviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(weatherMonitorSettingsDTO, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/setBinTwin?azureDeviceId=${encodeURIComponent(azureDeviceId)}\r\n public setBinTwin(azureDeviceId: string, desiredPropertiesDTO: DesiredPropertiesDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/setBinTwin`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (azureDeviceId != null) {\r\n url += `${prefix}azureDeviceId=${encodeURIComponent(azureDeviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(desiredPropertiesDTO, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/getecdevice?azureDeviceId=${encodeURIComponent(azureDeviceId)}\r\n public getECDevice(azureDeviceId: string, signal?: AbortSignal): Promise> {\r\n let url = `api/grainbin/getecdevice`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (azureDeviceId != null) {\r\n url += `${prefix}azureDeviceId=${encodeURIComponent(azureDeviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest>(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/getRemoteModTwin\r\n public getRemoteModuleTwin(secret: SecretDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getRemoteModTwin`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(secret, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/getLocalModTwin\r\n public getLocalModuleTwin(secret: SecretDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getLocalModTwin`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(secret, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/getBinForAirTable\r\n public getBinForAirTable(secret: SecretDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getBinForAirTable`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(secret, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/getWeatherForecast\r\n public getWeatherForecastForBin(secret: SecretDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getWeatherForecast`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(secret, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/watchDogEndPoint\r\n public watchDogEndPoint(secretMsg: SecretDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/watchDogEndPoint`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(secretMsg, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetBinStateLogsByRange?clientDirectory=${encodeURIComponent(clientDirectory)}&startRange=${encodeURIComponent(String(startRange))}&endRange=${encodeURIComponent(String(endRange))}\r\n public getBinStateLogsByRange(clientDirectory: string, startRange: string, endRange: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetBinStateLogsByRange`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (clientDirectory != null) {\r\n url += `${prefix}clientDirectory=${encodeURIComponent(clientDirectory)}`;\r\n prefix = '&';\r\n }\r\n if (startRange != null) {\r\n url += `${prefix}startRange=${encodeURIComponent(startRange)}`;\r\n prefix = '&';\r\n }\r\n if (endRange != null) {\r\n url += `${prefix}endRange=${encodeURIComponent(endRange)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetGrainStateLogsByRange?clientDirectory=${encodeURIComponent(clientDirectory)}&startRange=${encodeURIComponent(String(startRange))}&endRange=${encodeURIComponent(String(endRange))}\r\n public getGrainStateeLogsByRange(clientDirectory: string, startRange: string, endRange: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetGrainStateLogsByRange`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (clientDirectory != null) {\r\n url += `${prefix}clientDirectory=${encodeURIComponent(clientDirectory)}`;\r\n prefix = '&';\r\n }\r\n if (startRange != null) {\r\n url += `${prefix}startRange=${encodeURIComponent(startRange)}`;\r\n prefix = '&';\r\n }\r\n if (endRange != null) {\r\n url += `${prefix}endRange=${encodeURIComponent(endRange)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetRunLogsByRange?clientDirectory=${encodeURIComponent(clientDirectory)}&startRange=${encodeURIComponent(String(startRange))}&endRange=${encodeURIComponent(String(endRange))}\r\n public getRunLogsByRange(clientDirectory: string, startRange: string, endRange: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetRunLogsByRange`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (clientDirectory != null) {\r\n url += `${prefix}clientDirectory=${encodeURIComponent(clientDirectory)}`;\r\n prefix = '&';\r\n }\r\n if (startRange != null) {\r\n url += `${prefix}startRange=${encodeURIComponent(startRange)}`;\r\n prefix = '&';\r\n }\r\n if (endRange != null) {\r\n url += `${prefix}endRange=${encodeURIComponent(endRange)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/getLatestSnapshot\r\n public getLatestBinStateSnapshotAnonymous(secretMsg: SecretDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getLatestSnapshot`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(secretMsg, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/getbinassembly?binId=${binId}\r\n public getBinAssembly(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getbinassembly`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/GetScript\r\n public getScript(secretMsg: SecretDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetScript`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(secretMsg, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/latestErrors?deviceId=${encodeURIComponent(deviceId)}\r\n public getLatestErrors(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/latestErrors`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/latestErrorsFromDevice?deviceId=${encodeURIComponent(deviceId)}\r\n public getLatestErrorsFromDevice(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/latestErrorsFromDevice`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/allBins\r\n public getAllBins(signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/allBins`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/getBinDetailFromAzure?deviceId=${encodeURIComponent(deviceId)}&binId=${binId}&offsetMinutes=${offsetMinutes}\r\n public getBinDetailFromAzure(deviceId: string, binId: number, offsetMinutes: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getBinDetailFromAzure`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (offsetMinutes != null) {\r\n url += `${prefix}offsetMinutes=${offsetMinutes}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/getBinDetailFromDevice?deviceId=${encodeURIComponent(deviceId)}&binId=${binId}&offsetMinutes=${offsetMinutes}\r\n public getBinDetailFromDevice(deviceId: string, binId: number, offsetMinutes: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getBinDetailFromDevice`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (offsetMinutes != null) {\r\n url += `${prefix}offsetMinutes=${offsetMinutes}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/UploadBinStateToAzure?deviceId=${encodeURIComponent(deviceId)}\r\n public uploadBinStateToAzure(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/UploadBinStateToAzure`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/getSnapshotDetailFromAzure?binId=${binId}\r\n public getSnapshotDetailFromAzure(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getSnapshotDetailFromAzure`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/getCustomRoutinesOnDisk?binId=${binId}\r\n public getCustomRoutinesOnDisk(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getCustomRoutinesOnDisk`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/triggerBinStateUpload?deviceId=${encodeURIComponent(deviceId)}\r\n public triggerBinStateUpload(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/triggerBinStateUpload`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/pingDeviceAndGetBinDTOFromAzure?deviceId=${encodeURIComponent(deviceId)}&offsetMinutes=${offsetMinutes}\r\n public pingDeviceAndGetBinDTOFromAzure(deviceId: string, offsetMinutes: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/pingDeviceAndGetBinDTOFromAzure`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n if (offsetMinutes != null) {\r\n url += `${prefix}offsetMinutes=${offsetMinutes}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/PingDevice?deviceId=${encodeURIComponent(deviceId)}\r\n public pingDevice(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/PingDevice`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/PingPremierPlusDevice?deviceId=${encodeURIComponent(deviceId)}\r\n public pingPremierPlusDevice(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/PingPremierPlusDevice`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/PowerCycleBoard\r\n public powerCycleBoard(powerCycleBoardDTO: PowerCycleBoardDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/PowerCycleBoard`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(powerCycleBoardDTO, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/RestartDristackModule?deviceId=${encodeURIComponent(deviceId)}\r\n public restartDristackModule(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/RestartDristackModule`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/GetLogsFromDristackmodule?deviceId=${encodeURIComponent(deviceId)}&lines=${lines}\r\n public getLogsFromDristackmodule(deviceId: string, lines: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetLogsFromDristackmodule`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n if (lines != null) {\r\n url += `${prefix}lines=${lines}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/ascii?deviceId=${encodeURIComponent(deviceId)}&binId=${binId}&offsetMinutes=${offsetMinutes}\r\n public getLatestBinAscii(deviceId: string, binId: number, offsetMinutes: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/ascii`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (offsetMinutes != null) {\r\n url += `${prefix}offsetMinutes=${offsetMinutes}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/latestmcsnapshots/${encodeURIComponent(deviceID)}\r\n public getMoistureContentSnapshotsOfLatestBatch(deviceID: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/latestmcsnapshots/${encodeURIComponent(deviceID)}`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/latestBinStateSnapshot/${encodeURIComponent(deviceID)}\r\n public getLatestBinStateSnapshot(deviceID: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/latestBinStateSnapshot/${encodeURIComponent(deviceID)}`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/gate/open?valveId=${encodeURIComponent(valveId)}&deviceName=${encodeURIComponent(deviceName)}\r\n public openGate(valveId: string, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/gate/open`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (valveId != null) {\r\n url += `${prefix}valveId=${encodeURIComponent(valveId)}`;\r\n prefix = '&';\r\n }\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/gate/close?valveId=${encodeURIComponent(valveId)}&deviceName=${encodeURIComponent(deviceName)}\r\n public closeGate(valveId: string, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/gate/close`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (valveId != null) {\r\n url += `${prefix}valveId=${encodeURIComponent(valveId)}`;\r\n prefix = '&';\r\n }\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/spool/open?valveId=${encodeURIComponent(valveId)}&deviceName=${encodeURIComponent(deviceName)}\r\n public openSpool(valveId: string, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/spool/open`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (valveId != null) {\r\n url += `${prefix}valveId=${encodeURIComponent(valveId)}`;\r\n prefix = '&';\r\n }\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/spool/close?valveId=${encodeURIComponent(valveId)}&deviceName=${encodeURIComponent(deviceName)}\r\n public closeSpool(valveId: string, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/spool/close`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (valveId != null) {\r\n url += `${prefix}valveId=${encodeURIComponent(valveId)}`;\r\n prefix = '&';\r\n }\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/fan/on?fanId=${encodeURIComponent(fanId)}&deviceName=${encodeURIComponent(deviceName)}\r\n public turnOnFan(fanId: string, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/fan/on`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (fanId != null) {\r\n url += `${prefix}fanId=${encodeURIComponent(fanId)}`;\r\n prefix = '&';\r\n }\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/fans/on?deviceName=${encodeURIComponent(deviceName)}\r\n public turnOnFans(deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/fans/on`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/fans/off?deviceName=${encodeURIComponent(deviceName)}\r\n public turnOffFans(deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/fans/off`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/fan/off?fanId=${encodeURIComponent(fanId)}&deviceName=${encodeURIComponent(deviceName)}\r\n public turnOffFan(fanId: string, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/fan/off`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (fanId != null) {\r\n url += `${prefix}fanId=${encodeURIComponent(fanId)}`;\r\n prefix = '&';\r\n }\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/heater/on?deviceName=${encodeURIComponent(deviceName)}&fanId=${encodeURIComponent(fanId)}\r\n public turnOnHeater(deviceName: string, fanId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/heater/on`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n if (fanId != null) {\r\n url += `${prefix}fanId=${encodeURIComponent(fanId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/heaters/on?deviceName=${encodeURIComponent(deviceName)}\r\n public turnOnHeaters(deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/heaters/on`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/heater/off?deviceName=${encodeURIComponent(deviceName)}&fanId=${encodeURIComponent(fanId)}\r\n public turnOffHeater(deviceName: string, fanId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/heater/off`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n if (fanId != null) {\r\n url += `${prefix}fanId=${encodeURIComponent(fanId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/heaters/off?deviceName=${encodeURIComponent(deviceName)}\r\n public turnOffHeaters(deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/heaters/off`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/compressor/on?deviceId=${encodeURIComponent(deviceId)}\r\n public turnOnCompressor(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/compressor/on`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/compressor/off?deviceId=${encodeURIComponent(deviceId)}\r\n public turnOffCompressor(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/compressor/off`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SetIgnoredSolenoids\r\n public setIgnoredSolenoids(request: SetIgnoredSolenoidsRequestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetIgnoredSolenoids`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(request, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SetIgnoredSensors\r\n public setIgnoredSensors(request: SetIgnoredSensorRequestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetIgnoredSensors`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(request, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SetValvePositions?deviceId=${encodeURIComponent(deviceId)}\r\n public setValvePositions(deviceId: string, valvePositions: ValvePositionDTO[], signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetValvePositions`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(valvePositions, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/addUnload?deviceName=${encodeURIComponent(deviceName)}&binId=${binId}\r\n public addUnload(unload: UnloadDTO, deviceName: string, binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/addUnload`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(unload, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/addLoad?deviceName=${encodeURIComponent(deviceName)}&binId=${binId}&batchNumber=${batchNumber}\r\n public addLoad(load: LoadDTO, deviceName: string, binId: number, batchNumber: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/addLoad`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (batchNumber != null) {\r\n url += `${prefix}batchNumber=${batchNumber}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(load, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/deleteLoad?loadId=${loadId}&binId=${binId}&batchNumber=${batchNumber}\r\n public deleteLoad(loadId: number, binId: number, batchNumber: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/deleteLoad`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (loadId != null) {\r\n url += `${prefix}loadId=${loadId}`;\r\n prefix = '&';\r\n }\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (batchNumber != null) {\r\n url += `${prefix}batchNumber=${batchNumber}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/deleteUnload?unloadId=${unloadId}&binId=${binId}&batchNumber=${batchNumber}\r\n public deleteUnload(unloadId: number, binId: number, batchNumber: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/deleteUnload`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (unloadId != null) {\r\n url += `${prefix}unloadId=${unloadId}`;\r\n prefix = '&';\r\n }\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (batchNumber != null) {\r\n url += `${prefix}batchNumber=${batchNumber}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SetWeatherMonitorSettings?deviceId=${encodeURIComponent(deviceId)}\r\n public setWeatherMonitorSettings(deviceId: string, settings: ReportedWeatherMonitorSettingsDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetWeatherMonitorSettings`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(settings, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SetGrainType?deviceId=${encodeURIComponent(deviceId)}\r\n public setGrainType(deviceId: string, body: SetGrainTypeRquestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetGrainType`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(body, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/Idle?deviceName=${encodeURIComponent(deviceName)}\r\n public idle(deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/Idle`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/Fill?deviceName=${encodeURIComponent(deviceName)}&binId=${binId}\r\n public fill(command: any, deviceName: string, binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/Fill`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(command, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/Dry?deviceName=${encodeURIComponent(deviceName)}&binId=${binId}\r\n public dry(command: any, deviceName: string, binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/Dry`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(command, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/ChangeHeaterMode\r\n public changeHeaterMode(changeHeaterModeDTO: ChangeHeaterModeDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/ChangeHeaterMode`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(changeHeaterModeDTO, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SetHeaterMode\r\n public setHeaterMode(options: HeaterModeOptionsRequestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetHeaterMode`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(options, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SetHeaterPlenumMcOffset?heaterPlenumMcOffset=${heaterPlenumMcOffset}&deviceId=${encodeURIComponent(deviceId)}\r\n public setHeaterPlenumMcOffset(heaterPlenumMcOffset: number, deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetHeaterPlenumMcOffset`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (heaterPlenumMcOffset != null) {\r\n url += `${prefix}heaterPlenumMcOffset=${heaterPlenumMcOffset}`;\r\n prefix = '&';\r\n }\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/IgnorePlenumTemperatureSensor?ignorePlenumTemperatureSensor=${ignorePlenumTemperatureSensor}&deviceId=${encodeURIComponent(deviceId)}\r\n public ignorePlenumTemperatureSensors(ignorePlenumTemperatureSensor: boolean, deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/IgnorePlenumTemperatureSensor`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (ignorePlenumTemperatureSensor != null) {\r\n url += `${prefix}ignorePlenumTemperatureSensor=${ignorePlenumTemperatureSensor}`;\r\n prefix = '&';\r\n }\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SetFanRemainingOnTime\r\n public setFanRemainingOnTime(options: FanOnAllowedTimeRequestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetFanRemainingOnTime`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(options, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SetFanRemainingOnTimePerWeek\r\n public setFanRemainingOnTimePerWeek(options: FanOnAllowedTimePerWeekRequestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetFanRemainingOnTimePerWeek`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(options, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/ResetFanRuntime?deviceId=${encodeURIComponent(deviceId)}\r\n public resetFanRuntime(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/ResetFanRuntime`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/UpdateMaxLayerGrainTemp\r\n public updateMaxLayerGrainTemp(options: UpdateMaxLayerGrainTempRequestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/UpdateMaxLayerGrainTemp`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(options, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SetLayersTimePercentages?deviceId=${encodeURIComponent(deviceId)}\r\n public setLayersTimePercentages(deviceId: string, layerPercentages: LayerPercentageDTO[], signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SetLayersTimePercentages`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(layerPercentages, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/ClearRoutineEngineSteps?deviceId=${encodeURIComponent(deviceId)}\r\n public clearRoutineEngineSteps(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/ClearRoutineEngineSteps`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/RunCoreManualRoutine?deviceId=${encodeURIComponent(deviceId)}\r\n public runCoreManualRoutine(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/RunCoreManualRoutine`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/CloseAllValvesAndClearRoutines?deviceId=${encodeURIComponent(deviceId)}\r\n public closeAllValvesAndClearRoutines(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/CloseAllValvesAndClearRoutines`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/ClearManualRoutine?deviceId=${encodeURIComponent(deviceId)}\r\n public clearManualRoutine(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/ClearManualRoutine`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/RunManualLayer?deviceId=${encodeURIComponent(deviceId)}&targetLayer=${targetLayer}\r\n public runManualLayer(deviceId: string, targetLayer: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/RunManualLayer`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n if (targetLayer != null) {\r\n url += `${prefix}targetLayer=${targetLayer}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/Storage?deviceName=${encodeURIComponent(deviceName)}\r\n public storage(command: any, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/Storage`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(command, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/Unload?deviceName=${encodeURIComponent(deviceName)}\r\n public unload(command: any, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/Unload`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(command, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/StartManual?deviceName=${encodeURIComponent(deviceName)}\r\n public startManual(command: any, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/StartManual`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(command, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/StopManual?deviceName=${encodeURIComponent(deviceName)}\r\n public stopManual(deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/StopManual`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/Pause?deviceName=${encodeURIComponent(deviceName)}\r\n public pause(command: any, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/Pause`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(command, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/Resume?deviceName=${encodeURIComponent(deviceName)}\r\n public resume(command: any, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/Resume`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(command, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/SelfCheck?deviceName=${encodeURIComponent(deviceName)}\r\n public selfCheck(command: any, deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/SelfCheck`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(command, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/getOfflineConnectionReport?deviceId=${encodeURIComponent(deviceId)}\r\n public getOfflineConnectionReport(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getOfflineConnectionReport`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetHourlyForecast?binId=${binId}\r\n public getHourlyForecast(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetHourlyForecast`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetMoistureForecast?binId=${binId}\r\n public getMoistureForecast(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetMoistureForecast`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/PingTest\r\n public pingTest(signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/PingTest`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/updateFanSettings?deviceId=${encodeURIComponent(deviceId)}\r\n public updateSettings(settings: DesiredRequestSettingsDTO, deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/updateFanSettings`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(settings, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/takeCameraPicture?binId=${binId}\r\n public takeCameraPicture(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/takeCameraPicture`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/getCameraImage?binId=${binId}\r\n public getCameraImage(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getCameraImage`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/StartLiveStream?binId=${binId}\r\n public startLiveStream(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/StartLiveStream`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/StopLiveStream?binId=${binId}\r\n public stopLiveStream(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/StopLiveStream`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetStreamsForDevice?binId=${binId}\r\n public getStreamsForDevice(binId: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetStreamsForDevice`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetCellularStrengthHistory?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getCellularStrengthHistory(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetCellularStrengthHistory`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetOPICableHistory?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getOPICableHistory(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetOPICableHistory`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetBinSenseCableHistory?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getBinSenseCableHistory(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetBinSenseCableHistory`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/GetAlertHistory\r\n public getBinAlertHistory(dto: AlertHistoryRequestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetAlertHistory`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(dto, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetFanHeaterChart?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getFanHeaterChart(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetFanHeaterChart`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetLayerSummaryHistory?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getLayerSummaryHistory(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetLayerSummaryHistory`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetLowestLayerTemperatureHistory?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getLowestLayerTemperatureHistory(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetLowestLayerTemperatureHistory`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetTemperatureCableHistory?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getTemperatureCableHistoryDTO(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetTemperatureCableHistory`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetCO2History?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getCO2History(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetCO2History`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetAmbientPlenumHistory?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getAmbientPlenumHistory(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetAmbientPlenumHistory`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetMoistureRemovalData?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getMoistureRemovalData(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetMoistureRemovalData`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetAmbientPlenumEMCHistory?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getAmbientPlenumEMCHistory(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetAmbientPlenumEMCHistory`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetCompressorDataset?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getCompressorDataset(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetCompressorDataset`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetOperatingModeChangesDataset?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getOperatingModeChangesDataset(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetOperatingModeChangesDataset`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetFanHeaterChangeHistory?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getFanHeaterChangeHistory(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetFanHeaterChangeHistory`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetTargetLayerChart?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getTargetLayerChart(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetTargetLayerChart`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetGrainHeightsHistory?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getGrainHeightsHistory(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetGrainHeightsHistory`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/GetGrainHeightsHistoryUserEntered?binId=${binId}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}&intervalSeconds=${intervalSeconds}\r\n public getGrainHeightsHistoryUserEntered(binId: number, from: string, to: string, intervalSeconds?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/GetGrainHeightsHistoryUserEntered`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n if (intervalSeconds != null) {\r\n url += `${prefix}intervalSeconds=${intervalSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/OverrideGrainHeight?binId=${binId}&eaveHeightFt=${eaveHeightFt}&peakHeightFt=${peakHeightFt}\r\n public overrideGrainHeight(binId: number, eaveHeightFt?: number | null, peakHeightFt?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/OverrideGrainHeight`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (eaveHeightFt != null) {\r\n url += `${prefix}eaveHeightFt=${eaveHeightFt}`;\r\n prefix = '&';\r\n }\r\n if (peakHeightFt != null) {\r\n url += `${prefix}peakHeightFt=${peakHeightFt}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/CalculateGrainHeight?binId=${binId}&eaveHeightFt=${eaveHeightFt}&peakHeightFt=${peakHeightFt}\r\n public calculateGrainHeight(binId: number, eaveHeightFt?: number | null, peakHeightFt?: number | null, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/CalculateGrainHeight`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (eaveHeightFt != null) {\r\n url += `${prefix}eaveHeightFt=${eaveHeightFt}`;\r\n prefix = '&';\r\n }\r\n if (peakHeightFt != null) {\r\n url += `${prefix}peakHeightFt=${peakHeightFt}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/grainbin/getPremierPlusBinDetailFromAzure?binId=${binId}&offsetMinutes=${offsetMinutes}\r\n public getPremierPlusBinDetailFromAzure(binId: number, offsetMinutes: number, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/getPremierPlusBinDetailFromAzure`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n if (offsetMinutes != null) {\r\n url += `${prefix}offsetMinutes=${offsetMinutes}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/modifyBasicSettings?binId=${binId}\r\n public premierPlus_ModifyBasicSettings(binId: number, settings: ConfigureBasicSettingsRequestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/modifyBasicSettings`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (binId != null) {\r\n url += `${prefix}binId=${binId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(settings, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/registerProduct\r\n public registerProduct(request: RegisterProductDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/registerProduct`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(request, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/grainbin/updateSystemConfigRegistry\r\n public updateSystemConfigRegistry(request: ConfigureSystemRequestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/grainbin/updateSystemConfigRegistry`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(request, 'post', url, true, false, _signal);\r\n }\r\n}\r\nvar service = new BinApiService();\r\nexport default service;\r\n","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum ErrorPriority {\r\n CriticalError = 'CriticalError',\r\n Warning = 'Warning',\r\n}\r\nexport default ErrorPriority;\r\n","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum BoardGroup {\r\n All = 0,\r\n Hub = 1,\r\n Fans = 2,\r\n FanHeaterExpansionBoards = 21,\r\n AugerControlBoards = 22,\r\n LevelDryBoards = 23,\r\n HeadspaceBoards = 24,\r\n StackBoards = 3,\r\n SensingBoards = 4,\r\n RHTBoards = 5,\r\n OPIBoards = 6,\r\n CmcSensorBoards = 7,\r\n}\r\nexport default BoardGroup;\r\n","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum HeaterMode {\r\n EnergyEff = 'EnergyEff',\r\n Ambient = 'Ambient',\r\n Speed5 = 'Speed5',\r\n Speed10 = 'Speed10',\r\n On = 'On',\r\n Off = 'Off',\r\n Auto = 'Auto',\r\n}\r\nexport default HeaterMode;\r\n","import { Rule } from \"antd/es/form/index\";\r\n\r\nexport const RequiredRule = {\r\n required: true\r\n} as Rule;\r\n\r\nexport const BasicValidateMessages = {\r\n required: '\\'${label}\\' is required',\r\n string: {\r\n range: '\\'${label}\\' must be between ${min} and ${max} characters',\r\n },\r\n};","\r\nexport type LogType = 'BinState' | 'GrainState' | 'Run';\r\n\r\nexport const viewBinStateLogsByRange = async (clientFolder: string, startDate: string, endDate: string) => {\r\n const url = `${window.location.protocol}${window.location.host}/api/grainbin/GetBinStateLogsByRange`;\r\n console.debug(`device log url = ${url}`);\r\n const binStateRangeUrl = new URL(url);\r\n binStateRangeUrl.searchParams.set('clientDirectory', clientFolder);\r\n binStateRangeUrl.searchParams.set('startRange', startDate);\r\n binStateRangeUrl.searchParams.set('endRange', endDate);\r\n window.open(binStateRangeUrl.toString(), '_blank');\r\n};\r\n\r\nexport const viewGrainStateLogsByRange = async (clientFolder: string, startDate: string, endDate: string) => {\r\n const url = `${window.location.protocol}${window.location.host}/api/grainbin/GetGrainStateLogsByRange`;\r\n console.debug(`device grainState url = ${url}`);\r\n var rangeURL = new URL(url);\r\n rangeURL.searchParams.set('clientDirectory', clientFolder);\r\n rangeURL.searchParams.set('startRange', startDate);\r\n rangeURL.searchParams.set('endRange', endDate);\r\n window.open(rangeURL.toString(), '_blank');\r\n};\r\n\r\nexport const viewRunLogsByRange = async (clientFolder: string, startDate: string, endDate: string) => {\r\n const url = `${window.location.protocol}${window.location.host}/api/grainbin/GetRunLogsByRange`;\r\n console.debug(`device RunLog url = ${url}`);\r\n var rangeURL = new URL(url);\r\n rangeURL.searchParams.set('clientDirectory', clientFolder);\r\n rangeURL.searchParams.set('startRange', startDate);\r\n rangeURL.searchParams.set('endRange', endDate);\r\n window.open(rangeURL.toString(), '_blank');\r\n};","import BinDTO from \"src/models/BinDTO\";\r\n\r\nexport enum AutomationType {\r\n AutoBin = \"AutoBin\",\r\n AutoFan = \"AutoFan\",\r\n DriStack = \"DriStack\",\r\n Special = \"Special\",\r\n PremierPlus = \"PremierPlus\"\r\n}\r\n\r\nexport const selectAutomationType = (data: BinDTO | null | undefined): AutomationType => {\r\n if (data == null) {\r\n console.debug(\"binDTO data is null, returning DriStack as default\");\r\n return AutomationType.DriStack;\r\n };\r\n const automationType = data?.automationType;\r\n switch (automationType) {\r\n case null:\r\n case \"DriStack\": {\r\n return AutomationType.DriStack;\r\n }\r\n case \"AutoBin\":\r\n return AutomationType.AutoBin;\r\n case \"PremierPlus\":\r\n return AutomationType.PremierPlus;\r\n default: {\r\n console.debug(`AutomationType \\`${automationType}\\` is unknown. Using DriStack`)\r\n return AutomationType.DriStack;\r\n }\r\n }\r\n}","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum DryingForecast {\r\n Poor = 0,\r\n Moderate = 1,\r\n Excellent = 2,\r\n}\r\nexport default DryingForecast;\r\n","export const PRIMARY_BLUE_SHIVVERS = \"#005893\";\r\nexport const ORANGE_SHIVVERS = \"#E38A3D\";","import * as React from 'react';\r\nimport { notification, Card, Col, Row, Result, ConfigProvider, Space, Badge } from 'antd';\r\nimport EnterpriseApiService from 'src/api/EnterpriseApiService';\r\nimport ForecastDTO from 'src/models/ForecastDTO';\r\nimport DryingForecast from 'src/consts/DryingForecast';\r\nimport { formatNumber } from './HeaterControls';\r\nimport { ORANGE_SHIVVERS, PRIMARY_BLUE_SHIVVERS } from 'src/app/theme';\r\n\r\ninterface State {\r\n loading: boolean;\r\n forecast: ForecastDTO;\r\n latitude: number;\r\n longitude: number;\r\n offline: boolean;\r\n}\r\n\r\nconst GOOD_WEATHER_COLOR = PRIMARY_BLUE_SHIVVERS;\r\nconst POOR_WEATHER_COLOR = ORANGE_SHIVVERS;\r\nexport default class FillingForcast extends React.Component<{ lat: number, lng: number }, State> {\r\n\r\n constructor(props: { lat: number, lng: number }) {\r\n super(props);\r\n this.state = {\r\n latitude: props.lat,\r\n longitude: props.lng,\r\n loading: true,\r\n offline: false,\r\n forecast: { forecastedDrying: null, threeDayScore: 0, lat: 0, lon: 0, daily: null, hourly: null }\r\n };\r\n this.getForecastData = this.getForecastData.bind(this);\r\n }\r\n\r\n componentDidMount() {\r\n this.getForecastData();\r\n }\r\n\r\n getForecastData = () => {\r\n EnterpriseApiService.getWeatherForecast(this.state.latitude, this.state.longitude).then(forecast => {\r\n this.setState({\r\n loading: false,\r\n forecast: forecast,\r\n });\r\n }).catch(error => {\r\n this.setState({ loading: false, offline: true });\r\n console.log(error);\r\n notification.error({\r\n message: error.message,\r\n description: error.description\r\n });\r\n });\r\n }\r\n\r\n render() {\r\n const offline = this.state.forecast == null;\r\n if (this.state.loading) {\r\n return (\r\n \r\n

loading...

\r\n
\r\n );\r\n } else {\r\n const forecast = this.state.forecast.daily;\r\n let days = [];\r\n if (forecast) {\r\n for (let i = 0; i < forecast.length; i++) {\r\n let dailyForecast = forecast[i].dailyForecast;\r\n if (dailyForecast) {\r\n let calColor;\r\n switch (dailyForecast.dryingForecast) {\r\n case 0:\r\n calColor = POOR_WEATHER_COLOR;\r\n break;\r\n default:\r\n calColor = GOOD_WEATHER_COLOR;\r\n break;\r\n }\r\n let weatherIconURL = 'http://openweathermap.org/img/wn/' + dailyForecast.icon + '@2x.png';\r\n days.push(\r\n \r\n \r\n

Temperature: {dailyForecast.temp}°F

\r\n

Humidity: {dailyForecast.rh}%

\r\n

Dew Point: {formatNumber(dailyForecast.dewPoint, {decimalPlaces: 0, suffix: \"°F\"})}

\r\n

{DryingForecast[dailyForecast.dryingForecast]}

\r\n \r\n \r\n \r\n );\r\n }\r\n }\r\n }\r\n return (\r\n } >\r\n {!offline ? days : }\r\n \r\n );\r\n }\r\n }\r\n}\r\n\r\nexport const WeatherCardColorLegend = () => {\r\n const config = React.useContext(ConfigProvider.ConfigContext);\r\n\r\n return \r\n \r\n \r\n \r\n \r\n \r\n}","import { Alert, Button, ButtonProps, message, Popconfirm, Space, SpaceProps } from \"antd\";\r\nimport Typography from \"antd/es/typography/Typography\";\r\nimport React, { useEffect } from \"react\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\n\r\ninterface RestartDristackModuleProps {\r\n deviceId: string | null | undefined;\r\n buttonProps?: ButtonProps;\r\n spaceProps?: SpaceProps;\r\n}\r\n\r\ninterface RestartDeviceHookReturns {\r\n restartStatus: RestartStatus,\r\n restartModule: () => void,\r\n}\r\n\r\nenum RestartStatus {\r\n Restarting = \"Restarting\",\r\n Idle = \"Idle\",\r\n Success = \"Success\",\r\n Failed = \"Failed\",\r\n}\r\n\r\n// enum ReachableStatus {\r\n// Unchecked = 'Unchecked',\r\n// Pinging = \"Pinging\",\r\n// Unreachable = 'Unreachable',\r\n// Reachable = 'Reachable',\r\n// }\r\n\r\n// const useReachableDevice = (deviceId: string) => {\r\n// const [reachable, setReachable] = React.useState(ReachableStatus.Unchecked);\r\n\r\n// // ping device for confirmation\r\n// setReachable(ReachableStatus.Pinging);\r\n// try {\r\n// const pingable = await BinApiService.pingDevice(deviceId);\r\n// if (pingable) {\r\n// setReachable(ReachableStatus.Reachable);\r\n// message.success(\"dristackmodule restarted and reachable\");\r\n// }\r\n// else {\r\n// setReachable(ReachableStatus.Unreachable);\r\n// message.error(\"dristackmodule is not reachable\");\r\n// }\r\n// } catch {\r\n// message.error(\"Unexpected error: dristackmodule may not be reachable\");\r\n// setReachable(ReachableStatus.Unreachable);\r\n// }\r\n// }\r\n\r\nconst useRestartDevice = (deviceId: string): RestartDeviceHookReturns => {\r\n const [restartStatus, setRestartStatus] = React.useState(RestartStatus.Idle);\r\n\r\n useEffect(() => {\r\n if (deviceId == null) {\r\n setRestartStatus(RestartStatus.Idle);\r\n return;\r\n }\r\n setRestartStatus(RestartStatus.Idle);\r\n }, [deviceId]);\r\n\r\n const restartModule = async () => {\r\n try {\r\n setRestartStatus(RestartStatus.Restarting);\r\n const restartResult = await BinApiService.restartDristackModule(deviceId);\r\n if (restartResult === true) {\r\n setRestartStatus(RestartStatus.Success);\r\n message.success(\"System is restarting, check back in 5 minutes.\");\r\n }\r\n else {\r\n message.error(\"Failed to get confirmation that the system began restarting\");\r\n setRestartStatus(RestartStatus.Failed);\r\n }\r\n } catch {\r\n message.error(\"Failed to get confirmation that the system began restarting. Unexpected server error\");\r\n setRestartStatus(RestartStatus.Failed);\r\n }\r\n\r\n };\r\n \r\n\r\n return {restartStatus, restartModule };\r\n}\r\n\r\nexport const RestartDriStackModuleButton = ({deviceId, buttonProps, spaceProps}: RestartDristackModuleProps) => {\r\n const {restartStatus, restartModule } = useRestartDevice(deviceId ?? \"\");\r\n\r\n let restartNotifcation = null;\r\n if (restartStatus === RestartStatus.Success) {\r\n restartNotifcation = \r\n }\r\n else if (restartStatus === RestartStatus.Failed) {\r\n restartNotifcation = \r\n }\r\n\r\n return (\r\n <>\r\n\r\n \r\n System will be unavailable for 5 minutes while it restarts.} onConfirm={restartModule}>\r\n \r\n \r\n\r\n {restartNotifcation}\r\n \r\n \r\n );\r\n}","import { Button, DatePicker, Form, Input, InputNumber, Modal } from \"antd\";\r\nimport { useForm } from \"antd/lib/form/Form\";\r\nimport FormItem from \"antd/lib/form/FormItem\";\r\nimport layout from \"antd/lib/layout\";\r\nimport dayjs, { Dayjs } from \"dayjs\";\r\nimport React, { useCallback } from \"react\";\r\nimport { RequiredRule } from \"src/consts/FormConstants\";\r\n\r\ninterface UnloadFormProps {\r\n open: boolean;\r\n onCancel: () => void;\r\n onSubmit: (vals: UnloadFormSubmitValues) => void;\r\n pendingSubmit: boolean;\r\n}\r\n\r\nexport interface UnloadFormValues {\r\n unloadTime: Dayjs,\r\n finalMC: number | null,\r\n estimatedBu: number | null,\r\n destination: string | null,\r\n}\r\n\r\nexport interface UnloadFormSubmitValues {\r\n unloadTime: Dayjs,\r\n finalMC: number,\r\n estimatedBu: number,\r\n destination: string | null,\r\n}\r\n\r\nexport const UnloadForm = (props: UnloadFormProps) => {\r\n const [form] = useForm();\r\n\r\n const initialValues: UnloadFormValues = {\r\n unloadTime: dayjs(),\r\n finalMC: null,\r\n estimatedBu: null,\r\n destination: null,\r\n };\r\n\r\n const onFinsh = useCallback(async function onFinish(vals: UnloadFormSubmitValues) {\r\n try {\r\n await form.validateFields();\r\n console.log(\"Unload form vals: \", vals);\r\n props.onSubmit(vals);\r\n } catch (err) {\r\n console.error(\"failed to validate form fields\", err);\r\n }\r\n }, [props.onSubmit, form]);\r\n\r\n return <>\r\n \r\n Cancel\r\n ,\r\n ,\r\n ]\r\n }>\r\n\r\n
\r\n\r\n {\r\n \r\n }\r\n\r\n {\r\n \r\n min={0}\r\n max={100}\r\n step={0.5}\r\n formatter={val => `${val}%`.replace(/[!@#$^&*()a-zA-Z]/g, '')}\r\n parser={value => Number(value!.replace('%', ''))} />\r\n }\r\n\r\n {\r\n \r\n step={.5}\r\n min={0}\r\n formatter={val => `${val} bu`}\r\n parser={value => Number(value!.replace(/[ !@#$%^&*()a-zA-Z]/g, ''))} />\r\n }\r\n {\r\n \r\n }\r\n
\r\n \r\n ;\r\n}","import CurrentUser from \"src/utils/CurrentUser\";\r\nimport React, { PropsWithChildren, useMemo } from 'react';\r\nimport { Tooltip } from \"antd\";\r\n\r\nexport const usePermissions = () => {\r\n\r\n const canWrite = CurrentUser.Get()?.canModifyControls() ?? false;\r\n const permissions = useMemo(() => {\r\n return {\r\n canWrite: canWrite,\r\n canRead: true,\r\n }\r\n }, [canWrite]);\r\n return permissions;\r\n}\r\n","import { DeleteOutlined } from \"@ant-design/icons\";\r\nimport { Button, Table, Typography } from \"antd\";\r\nimport Column from \"antd/lib/table/Column\";\r\nimport React, { useMemo } from \"react\";\r\nimport { usePermissions } from \"src/pages/features/usePermissions\";\r\nimport { Load, PendingLoadDeletes, PopconfirmYesNo } from \"./BinStatsPage\";\r\n\r\ninterface LoadTableProps {\r\n loadData: Load[],\r\n deleteLoad: (load: Load) => void,\r\n hasPendingLoadDeletes: () => boolean,\r\n pendingLoadDeletes: PendingLoadDeletes,\r\n\r\n}\r\n\r\nconst calcTotalBushels = (loads: Load[]): number | null => {\r\n if (loads == null) {\r\n return null;\r\n }\r\n if ((loads?.length ?? 0) <= 0) {\r\n return null;\r\n }\r\n\r\n let sum = 0;\r\n for (const load of loads) {\r\n if (load.estimatedBu == null || load.estimatedBu < 0) {\r\n continue;\r\n }\r\n sum += load.estimatedBu;\r\n }\r\n return sum;\r\n}\r\n\r\ninterface SumAndMC {\r\n totalBushels: number | null,\r\n avgMC: number | null,\r\n}\r\n\r\nconst calcAvgMC = (loads: Load[]): SumAndMC => {\r\n const totalBushels = calcTotalBushels(loads);\r\n let avgMC = 0;\r\n if (totalBushels == null || totalBushels <= 0) {\r\n return { totalBushels: totalBushels, avgMC: null };\r\n }\r\n\r\n for (const load of loads) {\r\n const weight = load.estimatedBu / totalBushels;\r\n if (load.MC == null) {\r\n continue;\r\n }\r\n const weightedAvgMC = weight * load.MC;\r\n avgMC += weightedAvgMC;\r\n }\r\n\r\n return { totalBushels: totalBushels, avgMC };\r\n}\r\n\r\nexport const ReviewLoadTable = (props: LoadTableProps) => {\r\n\r\n const { totalBushels, avgMC } = useMemo(() => calcAvgMC(props.loadData), [props.loadData]);\r\n\r\n return (\r\n {\r\n // const totalBushelsEntered = totalBushels;\r\n\r\n // if (totalBushelsEntered == null || avgMC == null) {\r\n // return <>;\r\n // }\r\n\r\n // // return <>\r\n // // \r\n // // Total\r\n // // \r\n // // \r\n // // {avgMC.toFixed(2)}\r\n // // \r\n // // \r\n // // {totalBushelsEntered.toFixed(0)}\r\n // // \r\n // // \r\n // // \r\n // // \r\n // // \r\n // }}\r\n >\r\n \r\n \r\n \r\n \r\n {/* */}\r\n \r\n props.deleteLoad(record)}>\r\n \r\n } />\r\n
\r\n );\r\n}\r\n\r\n\r\nexport const FillLoadTable = (props: LoadTableProps) => {\r\n const { totalBushels, avgMC } = useMemo(() => calcAvgMC(props.loadData), [props.loadData]);\r\n const permissions = usePermissions();\r\n return (\r\n {\r\n // const totalBushelsEntered = totalBushels;\r\n // if (totalBushelsEntered == null || avgMC == null) {\r\n // return <>;\r\n // }\r\n\r\n // // return <>\r\n // // \r\n // // Total\r\n // // \r\n // // \r\n // // {avgMC.toFixed(2)}\r\n // // \r\n // // \r\n // // {totalBushelsEntered.toFixed(0)}\r\n // // \r\n // // \r\n // // \r\n // // \r\n // // \r\n // }}\r\n >\r\n \r\n \r\n \r\n \r\n {/* */}\r\n \r\n props.deleteLoad(record)}>\r\n \r\n } />\r\n
\r\n );\r\n}","import { Card, Col, Popconfirm, Row, Tag } from \"antd\";\r\nimport React from \"react\";\r\nimport FanDTO from \"src/models/FanDTO\";\r\nimport { DISABLED_TAG_COLOR, fanDurationFormatted } from \"./BinVisualThree\";\r\n\r\nexport const formatFanAmps = (fanAmps: number | null | undefined) => {\r\n if (fanAmps == null) {\r\n return '';\r\n }\r\n return (fanAmps / 1000)?.toFixed(2);\r\n}\r\n\r\ninterface FanCardProps {\r\n fan: FanDTO;\r\n inManualMode: boolean;\r\n fanRuntimeSeconds: number | undefined;\r\n fanSetpointMCPercent: number | undefined;\r\n turnOnFan: (fanId: string, fanIndex: number) => void;\r\n turnOffFan: (fanId: string, fanIndex: number) => void;\r\n deviceId: string | null;\r\n}\r\n\r\nexport const FanCard = (props: FanCardProps) => {\r\n const fan = props.fan;\r\n const inManualMode = props.inManualMode;\r\n\r\n return <>\r\n \r\n \r\n \r\n {`Fan ${fan.id} `}\r\n \r\n\r\n \r\n {(\r\n fan.isOn ?\r\n props?.turnOffFan(fan.id!, fan.number - 1)}>\r\n On\r\n \r\n : props?.turnOnFan(fan.id!, fan.number - 1)}>\r\n Off\r\n )\r\n }\r\n \r\n \r\n \r\n \r\n \r\n Current: \r\n {`${formatFanAmps(fan.ampReading)}`}\r\n   Amps\r\n \r\n
\r\n EMC Set Point: {props.fanSetpointMCPercent?.toFixed(1) ?? '--'} %\r\n
\r\n Runtime: {fanDurationFormatted(props.fanRuntimeSeconds)}\r\n \r\n
\r\n \r\n {props.deviceId !== \"Horra\" && Plenum Pressure: {fan?.plenumPressure} inH₂O}\r\n \r\n
\r\n \r\n};","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum FanOffReason {\r\n Requested = 'Requested',\r\n KeepFansOffOverride = 'KeepFansOffOverride',\r\n PlenumTemperatureAboveLimit = 'PlenumTemperatureAboveLimit',\r\n WeatherConditions = 'WeatherConditions',\r\n FanTimer = 'FanTimer',\r\n PlenumTemperatureCoolDownDelay = 'PlenumTemperatureCoolDownDelay',\r\n GrainFillHeightBelowLimit = 'GrainFillHeightBelowLimit',\r\n Unknown = 'Unknown',\r\n}\r\nexport default FanOffReason;\r\n","import dayjs from \"dayjs\";\r\nimport { Duration } from \"dayjs/plugin/duration\";\r\nimport { bind, inRange } from \"lodash-es\";\r\nimport React, { useMemo } from \"react\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport { AdditionalWeatherConditionDerived, isAtLeastOneLayerBelowDewpoint } from \"src/pages/features/WeatherMonitorAntdOnly\";\r\n\r\nexport const BinDTOContext = React.createContext(undefined);\r\n\r\nexport const useHasCableSensors = (binDTO: BinDTO | null | undefined): boolean | null => {\r\n if (binDTO == null) {\r\n return null;\r\n }\r\n\r\n const hasCableSensors = (binDTO.moistureCables?.length ?? 0) > 0 || (binDTO.moistureCables_RF?.length ?? 0) > 0 || (binDTO.opiMoistureCables?.length ?? 0) > 0 || (binDTO.binSenseMoistureCables?.length ?? 0) > 0;\r\n return hasCableSensors;\r\n}\r\n\r\nexport const getIgnoredSensors = (binDTO: BinDTO | null | undefined): string[] => {\r\n return binDTO?.desiredProperties?.ignoredSensors ?? [];\r\n}\r\n\r\nexport const grainHeightOverridesPresent = (binDTO: BinDTO): boolean => {\r\n const overrides = binDTO?.desiredProperties?.overrides;\r\n const grainHeightOverridesPresent = overrides?.grainHeightAtMiddle != null || overrides?.grainHeightAtPeak != null;\r\n return grainHeightOverridesPresent;\r\n}\r\n\r\nexport const useBinDTOContext = () => {\r\n const binDTO = React.useContext(BinDTOContext)\r\n\r\n const hasInnerCo2Sensor = () => {\r\n if (binDTO?.innerCO2 == null) {\r\n return false;\r\n }\r\n return true;\r\n }\r\n const hasOuterCo2Sensor = () => {\r\n if (binDTO?.outerCO2 == null) {\r\n return false;\r\n }\r\n return true;\r\n }\r\n\r\n const hasCo2Sensors = () => {\r\n if (!hasInnerCo2Sensor() && !hasOuterCo2Sensor()) {\r\n return false;\r\n }\r\n return true;\r\n }\r\n\r\n const hasRangeFinder = (): boolean => {\r\n return (binDTO?.innerSonar != null || binDTO?.outerSonar != null)\r\n }\r\n\r\n const computeAdditionalWeatherInfoFn = () => {\r\n if (binDTO?.weatherMonitorState == null) {\r\n return null;\r\n }\r\n return computeWeatherConditionInfo(binDTO);\r\n }\r\n\r\n const hasCableSensors = useHasCableSensors(binDTO);\r\n\r\n const ignoredSensors = useMemo(() => getIgnoredSensors(binDTO), [binDTO]);\r\n\r\n const fanControlMoisturePreference = useMemo(() => {\r\n return {fanControlMoisturePreference: binDTO?.useRHForFanControl};\r\n }, [binDTO]);\r\n\r\n const returnValue = useMemo(() => {\r\n return {\r\n binDTO, hasInnerCo2Sensor, hasOuterCo2Sensor, hasCo2Sensors,\r\n hasRangeFinder,\r\n additionalWeatherInfo: computeAdditionalWeatherInfoFn(),\r\n hasCableSensors,\r\n ignoredSensors,\r\n fanControlMoisturePreference,\r\n }\r\n }, [binDTO]);\r\n\r\n return returnValue;\r\n};\r\nexport const weatherConditionEvaluation = (binDTO: BinDTO): WeatherConditionRangeStatus => {\r\n if (binDTO.weatherMonitorState == null) {\r\n return WeatherConditionRangeStatus.Unknown;\r\n }\r\n const weatherMonitorState = binDTO.weatherMonitorState;\r\n\r\n if (weatherMonitorState?.areWeatherConditionsWithinLimits && weatherMonitorState.timeWeatherConditionsExitedLimits == null) {\r\n return WeatherConditionRangeStatus.FullyIn;\r\n }\r\n\r\n if (weatherMonitorState.areWeatherConditionsWithinLimits && weatherMonitorState.timeWeatherConditionsExitedLimits) {\r\n return WeatherConditionRangeStatus.Exitting;\r\n }\r\n if (!weatherMonitorState.areWeatherConditionsWithinLimits && weatherMonitorState.timeWeatherConditionsEnteredLimits) {\r\n return WeatherConditionRangeStatus.Entering;\r\n }\r\n if (!weatherMonitorState.areWeatherConditionsWithinLimits && weatherMonitorState.timeWeatherConditionsEnteredLimits == null) {\r\n return WeatherConditionRangeStatus.Outside;\r\n }\r\n\r\n return WeatherConditionRangeStatus.Unknown;\r\n};\r\n\r\nexport enum WeatherConditionRangeStatus {\r\n FullyIn,\r\n Outside,\r\n Exitting,\r\n Entering,\r\n Unknown\r\n}\r\n\r\nexport const computeWeatherConditionInfo = (binDTO: BinDTO): AdditionalWeatherConditionDerived | null => {\r\n\r\n if (binDTO?.weatherMonitorState == null) {\r\n return null;\r\n }\r\n\r\n\r\n let inConditionsLong = false;\r\n if (binDTO?.weatherMonitorState?.areWeatherConditionsWithinLimits) {\r\n inConditionsLong = true;\r\n }\r\n else {\r\n inConditionsLong = false;\r\n }\r\n\r\n const currentWeatherConditions = binDTO.weatherMonitorState?.currentWeatherConditions;\r\n const weatherSettings = binDTO.weatherMonitorState;\r\n const heaterOffsetUsedInConditions = binDTO.heaterOffsetShouldApplyNext;\r\n\r\n let emcInRange: boolean | null = null;\r\n let temperatureInRange: boolean | null = null;\r\n if (weatherSettings != null) {\r\n if (currentWeatherConditions?.mc != null) {\r\n let emcMaxRange = weatherSettings.maxMcLimit;\r\n if (heaterOffsetUsedInConditions) {\r\n emcMaxRange = emcMaxRange + binDTO.weatherMonitorState.heaterPlenumMcOffset;\r\n }\r\n emcInRange = inRange(currentWeatherConditions?.mc, weatherSettings?.minMcLimit, emcMaxRange);\r\n }\r\n if (currentWeatherConditions?.temp != null) {\r\n temperatureInRange = inRange(currentWeatherConditions?.temp, weatherSettings?.minTemperatureLimitF, weatherSettings?.maxTemperatureLimitF);\r\n }\r\n\r\n }\r\n\r\n let inRangeInstant = null;\r\n if (emcInRange != null && temperatureInRange != null) {\r\n inRangeInstant = emcInRange && temperatureInRange;\r\n }\r\n\r\n let grainLayerAtRiskOfCondensation: boolean | null = isAtLeastOneLayerBelowDewpoint(binDTO);\r\n\r\n let resumeDuration: Duration | null = null;\r\n\r\n if (binDTO.weatherMonitorState?.timeWeatherConditionsEnteredLimits) {\r\n const date = dayjs(binDTO.weatherMonitorState.timeWeatherConditionsEnteredLimits);\r\n if (date.isValid()) {\r\n const diff = dayjs(date).add(dayjs.duration(binDTO.weatherMonitorState.timeInLimitsNeededSeconds ?? 0, 'seconds')).diff(binDTO.captureTimeUtc);\r\n resumeDuration = dayjs.duration(diff);\r\n }\r\n }\r\n if (binDTO.weatherMonitorState?.timeWeatherConditionsExitedLimits) {\r\n const date = dayjs(binDTO.weatherMonitorState.timeWeatherConditionsExitedLimits);\r\n if (date.isValid()) {\r\n const diff = dayjs(date).add(dayjs.duration(binDTO.weatherMonitorState.timeInLimitsNeededSeconds ?? 0, 'seconds')).diff(binDTO.captureTimeUtc);\r\n resumeDuration = dayjs.duration(diff);\r\n }\r\n }\r\n\r\n const weatherConditionRangeResult = weatherConditionEvaluation(binDTO)\r\n\r\n const vals: AdditionalWeatherConditionDerived = {\r\n inRangeLong: inConditionsLong,\r\n inRangeInstant: inRangeInstant,\r\n emcInRange: emcInRange,\r\n temperatureInRange: temperatureInRange,\r\n grainLayerAtRiskOfCondensation: grainLayerAtRiskOfCondensation,\r\n heaterOffsetApplied: heaterOffsetUsedInConditions,\r\n timeTillPhaseChange: resumeDuration,\r\n weatherConditionRangeStatus: weatherConditionRangeResult,\r\n };\r\n return vals;\r\n};\r\n","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum OperatingMode {\r\n Idle = 0,\r\n FillBin = 1,\r\n PreDry = 2,\r\n TopDry = 31,\r\n Dry = 3,\r\n Storage = 4,\r\n EmptyBin = 5,\r\n Manual = 20,\r\n FailSafe = 100,\r\n SelfCheck = 101,\r\n}\r\nexport default OperatingMode;\r\n","import React, { useMemo } from \"react\";\r\nimport { Tooltip, Typography } from \"antd\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport FanOffReason from \"src/consts/FanOffReason\";\r\nimport { formatNumber } from \"../dashboard/BinStatusPage/HeaterControls\";\r\nimport { InfoCircleOutlined } from \"@ant-design/icons\";\r\nimport dayjs from \"dayjs\";\r\nimport { AmbientConditionsStatusIndicator } from \"./WeatherMonitorAntdOnly\";\r\nimport { computeWeatherConditionInfo, useBinDTOContext } from \"src/queries/BinDTOContext\";\r\nimport { WeatherConditionRangeStatus } from \"src/queries/BinDTOContext\";\r\nimport OperatingMode from \"src/consts/OperatingMode\";\r\n\r\nexport const WeatherConditionsLimitTooltipText = () => {\r\n const bindtoContext = useBinDTOContext();\r\n const {binDTO, additionalWeatherInfo} = bindtoContext;\r\n\r\n if (binDTO == null) {\r\n return null;\r\n }\r\n\r\n const lastUpdatedDate = dayjs(binDTO?.weatherMonitorState?.currentWeatherConditions?.lastUpdated);\r\n\r\n return <>\r\n Last known conditions (10-minute average):\r\n
EMC: {formatNumber(binDTO?.weatherMonitorState?.currentWeatherConditions?.mc, {decimalPlaces: 1, filler: \"\", suffix: \"%\"})}\r\n
Temp: {formatNumber(binDTO?.weatherMonitorState?.currentWeatherConditions?.temp, {decimalPlaces: 1, filler: \"\", suffix: \"℉\"})}\r\n
{lastUpdatedDate.isValid() && lastUpdatedDate?.format('l LT')}
\r\n {[WeatherConditionRangeStatus.Entering].includes(additionalWeatherInfo?.weatherConditionRangeStatus!) && <>\r\n
\r\n \r\n Fan will un-pause in {additionalWeatherInfo?.timeTillPhaseChange?.humanize()} if conditions remain in range.\r\n \r\n }\r\n {[WeatherConditionRangeStatus.Exitting].includes(additionalWeatherInfo?.weatherConditionRangeStatus!) && <>\r\n
\r\n \r\n Fan will pause in {additionalWeatherInfo?.timeTillPhaseChange?.humanize()} if conditions do not improve.\r\n \r\n }\r\n \r\n\r\n}\r\n\r\nexport const AcVoltageIsOnTooltipText = (isPlugged: boolean | null) => {\r\n return <>\r\n {(isPlugged === null || isPlugged === undefined) ?\r\n \r\n It is unknown whether the AC voltage is on or not.\r\n : \r\n \r\n The AC voltage is {isPlugged ? \"on\" : \"off\"}.\r\n }\r\n \r\n}\r\n\r\nexport const fanStatusText = (bin: BinDTO | null) => {\r\n if (bin == null) {\r\n return null;\r\n }\r\n //if ([FanOffReason.WeatherConditions].includes(bin.fanOperations?.offReason!)) {\r\n // return null;\r\n //}\r\n\r\n if (bin.isFanOn) {\r\n return Fan(s) are running {!bin.fanOperations?.ignoreWeatherConditions && }\r\n }\r\n if (!bin.isFanOn) {\r\n return Fan(s) are OFF \r\n }\r\n\r\n return null;\r\n}\r\n\r\nconst LastUpdatedHelpText = (props: {bin: BinDTO | null}) => {\r\n if (props.bin?.weatherMonitorState?.currentWeatherConditions == null) {\r\n return null;\r\n }\r\n\r\n const lastUpdated = props.bin?.weatherMonitorState?.currentWeatherConditions?.lastUpdated;\r\n if (lastUpdated == null) {\r\n return null;\r\n }\r\n\r\n const formattedDate = dayjs(lastUpdated).format('MM/DD/YYYY h:mm a');\r\n\r\n return \r\n The incoming air EMC uses the last minute's average of readings to un-pause the fan. The last known value from {formattedDate} is {formatNumber(props.bin?.weatherMonitorState?.currentWeatherConditions?.mc, {decimalPlaces: 1, filler: \"\", suffix: \"%\"})}.\r\n \r\n\r\n}\r\n\r\nconst WeatherPauseReasonText = (props: {bin: BinDTO | null}) => {\r\n const bin = props.bin;\r\n if (bin == null) {\r\n return null;\r\n }\r\n const weatherState = bin.weatherMonitorState;\r\n if (weatherState == null) {\r\n return null;\r\n }\r\n if (weatherState.currentWeatherConditions == null) {\r\n return null;\r\n }\r\n\r\n return \r\n Conditions automatically checked every few minutes.\r\n
Last known value: {formatNumber(weatherState.currentWeatherConditions?.mc, {decimalPlaces: 1, suffix: \"%\", filler: \"\"})} \r\n } destroyTooltipOnHide>\r\n \r\n \r\n
\r\n}\r\n\r\nexport const pauseReasonText = (bin: BinDTO | null) => {\r\n if (bin == null) {\r\n return null;\r\n }\r\n\r\n if (bin.fanOperations?.offReason === null || bin.fanOperations?.offReason === undefined) {\r\n return null\r\n }\r\n\r\n let text = Fan pause reason: {bin?.fanOperations?.offReason};\r\n\r\n if (bin.fanOperations?.offReason === FanOffReason.Requested && bin.operatingMode === OperatingMode.Storage) {\r\n text = Fan pause reason: Weather conditions are not good enough to run aeration.\r\n } \r\n else if (bin.fanOperations?.offReason === FanOffReason.PlenumTemperatureAboveLimit) {\r\n text = Fan pause reason: Plenum temperature is above the set limit.\r\n } \r\n else if (bin.fanOperations?.offReason === FanOffReason.GrainFillHeightBelowLimit) {\r\n text = Fan pause reason: Grain height is not at the minimum height.\r\n }\r\n else if (bin?.fanOperations?.offReason === FanOffReason.WeatherConditions) {\r\n text =
Fan pause reason: incoming air conditions are not within the setpoints.\r\n The fan will resume when conditions are within proper conditions for 10 minutes. \r\n
\r\n }\r\n else if (bin?.fanOperations?.offReason === FanOffReason.FanTimer) {\r\n text = Fan pause reason: Fan timer reached zero.\r\n }\r\n else if (bin?.fanOperations?.offReason === FanOffReason.PlenumTemperatureCoolDownDelay) {\r\n text = Fan pause reason: the plenum temperature was above allowable limit for more than 5 mins. \r\n 10-min cooldown in process before attempting to re-start.\r\n }\r\n\r\n return (<>{text});\r\n}\r\n\r\nexport const hasRangeFinder = (binDTO: BinDTO | null | undefined): boolean => {\r\n return (binDTO?.innerSonar != null || binDTO?.outerSonar != null)\r\n}","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum ManualFanMode {\r\n AutoWeather = 'AutoWeather',\r\n AlwaysOff = 'AlwaysOff',\r\n AlwaysOn = 'AlwaysOn',\r\n Invalid = 'Invalid',\r\n}\r\nexport default ManualFanMode;\r\n","import { ConfigProvider, Spin, theme } from \"antd\";\r\nimport { SpinProps} from \"antd/es/spin\";\r\nimport React from \"react\";\r\n\r\ninterface CustomSpinProps extends SpinProps {\r\n invert?: boolean,\r\n}\r\n\r\nexport const Spinner = (props: CustomSpinProps) => {\r\n let {invert, ...rest} = props;\r\n invert = invert ?? false;\r\n const {token} = theme.useToken();\r\n\r\n return (\r\n \r\n \r\n \r\n )\r\n}","import { QuestionCircleOutlined, SaveFilled } from \"@ant-design/icons\";\r\nimport { Button, Col, Form, FormInstance, InputNumber, Popover, Radio, Row, Space, Tag, Typography } from \"antd\";\r\nimport useFormInstance from \"antd/es/form/hooks/useFormInstance\";\r\nimport dayjs, { Dayjs } from \"dayjs\";\r\nimport produce from \"immer\";\r\nimport React, { useCallback, useEffect, useMemo, useState } from \"react\";\r\nimport ManualFanMode from \"src/consts/ManualFanMode\";\r\nimport ReportedFanSettingsDTO from \"src/models/ReportedFanSettingsDTO\";\r\nimport { Spinner } from \"./CustomSpin\";\r\nimport { useForm } from \"antd/es/form/Form\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport StatePicker from \"../shared/StatePicker\";\r\nimport duration from 'dayjs/plugin/duration';\r\nimport relativeTime from 'dayjs/plugin/relativeTime';\r\nimport { useQuery } from '@tanstack/react-query';\r\nimport { round } from \"lodash-es\";\r\nimport { usePermissions } from \"./usePermissions\";\r\ndayjs.extend(duration);\r\ndayjs.extend(relativeTime);\r\n\r\n\r\nexport const defaultFormValues: SettingsFormValues = {\r\n mode: ManualFanMode.AutoWeather,\r\n minEMC: 0,\r\n maxEMC: 16,\r\n minTemp: 32,\r\n maxTemp: 105,\r\n fanRemainingTimeOn: null\r\n};\r\n\r\nexport const populateInitialValues = (current: Partial | null | undefined, fanRemainingOnTimeSeconds: number | null | undefined) => {\r\n console.log(\"current settings: \", current);\r\n const nextState = produce(defaultFormValues, initialValues => {\r\n if (current == null) {\r\n return;\r\n }\r\n const maybeFanRemainingOnTime = (fanRemainingOnTimeSeconds ?? defaultFormValues.fanRemainingTimeOn);\r\n initialValues.mode = current.mode ?? defaultFormValues.mode;\r\n initialValues.minEMC = current.minEMC ?? defaultFormValues.minEMC;\r\n initialValues.maxEMC = current.maxEMC ?? defaultFormValues.maxEMC;\r\n initialValues.minTemp = current.minTemp ?? defaultFormValues.minTemp;\r\n initialValues.maxTemp = current.maxTemp ?? defaultFormValues.maxTemp;\r\n initialValues.fanRemainingTimeOn = maybeFanRemainingOnTime != null ? round(maybeFanRemainingOnTime / 3600, 2) : null;\r\n });\r\n console.log(\"using these for initial values: \", nextState);\r\n return nextState;\r\n};\r\n\r\n\r\nexport interface ReportedFormFanSettings {\r\n mode: ManualFanMode | null;\r\n lastUpdated: string | null;\r\n minEMC: number | null;\r\n maxEMC: number | null;\r\n minTemp: number | null;\r\n maxTemp: number | null;\r\n}\r\n\r\ninterface WeatherMonitorFormProps {\r\n binDTO: BinDTO | null | undefined,\r\n deviceId: string,\r\n newFanSettings: Partial | null | undefined,\r\n onSubmit: (values: SettingsFormValues) => Promise,\r\n onCancel?: () => void;\r\n form?: FormInstance;\r\n greyControl: boolean\r\n}\r\n\r\n\r\nexport const WeatherMonitorForm = (props: WeatherMonitorFormProps) => {\r\n const binDto = props.binDTO;\r\n\r\n const originalFanRemainingOnTimer = props.binDTO?.fanOperations?.originalFanRemainingOnTimeSeconds == null ?\r\n 0 : props.binDTO?.fanOperations?.originalFanRemainingOnTimeSeconds / 3600;\r\n \r\n const timerIsRunning = props.binDTO?.fanOperations?.desiredFanOn && !props.binDTO?.fanOperations?.ignoreFanRemainingOnTime && props.binDTO?.fanOperations?.offReason == null;\r\n\r\n // const queryClient = useQuery();\r\n const [form] = useForm(props.form);\r\n const lastUpdated = props.newFanSettings?.lastUpdated ?? new Date().toISOString();\r\n // console.log(\"last updated being used\", lastUpdated);\r\n // console.log(\"bindto in form\", props.binDTO);\r\n const initialValues = useMemo(() => populateInitialValues(props.newFanSettings, props.binDTO?.fanOperations?.fanRemainingOnTimeSeconds), [lastUpdated, props.binDTO?.fanOperations?.fanRemainingOnTimeSeconds, props.binDTO?.fanOperations?.fanRemainingOnTimeId]);\r\n\r\n const [, forceUpdate] = useState(null);\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n const [submissionDate, setSubmissionDate] = useState();\r\n const [pendingMode, setPendingMode] = useState(null);\r\n\r\n const permissions = usePermissions();\r\n\r\n const setAllNotTouched = useCallback((values: SettingsFormValues) => {\r\n\r\n console.log(\"setting form values manually using values: \", values, \"ORRR inital values: \", initialValues);\r\n const defaults = initialValues;\r\n //form.resetFields();\r\n form.setFields([\r\n {name: \"minEMC\", touched: false, value: values.minEMC ?? defaults.minEMC},\r\n {name: \"maxEMC\", touched: false, value: values.maxEMC ?? defaults.maxEMC},\r\n {name: \"minTemp\", touched: false, value: values.minTemp ?? defaults.minTemp},\r\n {name: \"maxTemp\", touched: false, value: values.maxTemp ?? defaults.maxTemp},\r\n {name: \"mode\", touched: false, value: values.mode ?? defaults.mode},\r\n {name: \"fanRemainingTimeOn\", touched: false, value: originalFanRemainingOnTimer != null ? originalFanRemainingOnTimer : defaults.fanRemainingTimeOn},\r\n ])\r\n }, [form, initialValues]);\r\n\r\n const resetForm = useCallback((newValues: SettingsFormValues) => {\r\n console.log(\"resetting form\");\r\n setAllNotTouched(newValues);\r\n }, [form, initialValues])\r\n\r\n const cancelForm = useCallback(() => {\r\n resetForm(initialValues);\r\n props.onCancel?.();\r\n }, [initialValues, props.onCancel]);\r\n\r\n useEffect(() => {\r\nforceUpdate({});\r\n }, []);\r\n\r\n useEffect(() => {\r\n if (form.isFieldsTouched(false)) {\r\n // don't reset form to initial values, since the user is editing them / they haven't applied\r\n console.log(\"skipping due to touched fields\");\r\n return;\r\n }\r\n\r\n // todo: Let user know settings may take a few minutes to show properly.\r\n\r\n // todo: replacement for this with version number\r\n // if (submissionDate != null && props.newFanSettings?.ts != null && !dayjs(props.newFanSettings?.ts).isAfter(dayjs(submissionDate))) {\r\n // console.log(\"skipping due to new settings <= submission date\", submissionDate?.toDate(), props.newFanSettings);\r\n // return;\r\n // }\r\n\r\n // if (previousReportedFanSettings?.ts != null && props.newFanSettings?.ts != null && previousReportedFanSettings?.ts >= props.newFanSettings?.ts) {\r\n // console.log(\"skipping due to same TS or null\", previousReportedFanSettings?.ts, props.newFanSettings?.ts);\r\n // return;\r\n // }\r\n console.log(\"default values changed, resetting form...\", initialValues, \"submission date\", submissionDate?.toDate(), \"last updated\", lastUpdated);\r\n // todo: Current suspicion is that antd form's initialValues don't update when different ones are passed in.\r\n // auto => on => auto (uses original auto values instead of submitted ones OR even the newly updated ones)\r\n // Also. Could be because the form fields are destroyed when going to on. and thus populated with the original initial values?\r\n\r\n // try setfields instead.\r\n resetForm(initialValues);\r\n }, [form, initialValues, resetForm, lastUpdated]);\r\n\r\n const resetToSystemDefault = useCallback(() => {\r\n\r\n let newObj: Parameters[0] = Object.entries(defaultFormValues).map(([k, v]) => {\r\n\r\n return {name: k, value: v, touched: true};\r\n })\r\n\r\n form.setFields(newObj);\r\n }, [form, props.binDTO])\r\n\r\n const onFinish = useCallback(async (values: SettingsFormValues) => {\r\n try {\r\n const valuesCopy = {...values};\r\n console.log(\"values from form\", values);\r\n // skip sending the timer if we are in requesting a fan mode that doesn't use the timer\r\n // currently this is to prevent many timer elapsed notifications from getting sent if the timer is set or is 0 already.\r\n if (values.mode != ManualFanMode.AutoWeather) {\r\n valuesCopy.fanRemainingTimeOn = null;\r\n }\r\n setPendingMode(valuesCopy.mode);\r\n setIsSubmitting(true);\r\n const isSuccess = await props.onSubmit?.(valuesCopy);\r\n console.info(\"form submit status: \", isSuccess);\r\n if (isSuccess != null) {\r\n setSubmissionDate(dayjs());\r\n // not a mistake: Restore all original values including the timer, if submission fails\r\n resetForm(values);\r\n }\r\n } catch (err) {\r\n console.log(\"error while submitting fan settings to server\", err);\r\n } finally {\r\n setPendingMode(null);\r\n setIsSubmitting(false);\r\n }\r\n }, [props.onSubmit, setIsSubmitting, setPendingMode, setSubmissionDate, resetForm]);\r\n\r\n const automationMode = Form.useWatch(\"mode\", form);\r\n\r\n const submitFormManually = useCallback(async () => {\r\n try {\r\n const values = await form.validateFields();\r\n setPendingMode(values.mode);\r\n setIsSubmitting(true);\r\n const valuesCopy = {...values};\r\n if (values.mode != ManualFanMode.AutoWeather) {\r\n valuesCopy.fanRemainingTimeOn = null;\r\n }\r\n const isSuccess = await props.onSubmit?.(valuesCopy);\r\n\r\n console.info(\"form submit status: \", isSuccess, values);\r\n if (isSuccess === true) {\r\n setPendingMode(null);\r\n setSubmissionDate(dayjs());\r\n resetForm(values);\r\n }\r\n else if (!isSuccess) {\r\n console.log(\"form didn't submit successfully, resetting mode to previous: \", initialValues.mode);\r\n form.setFieldValue(\"mode\", initialValues.mode);\r\n }\r\n } catch (err) {\r\n console.error(\"error validating form fields\", err);\r\n }\r\n finally {\r\n setIsSubmitting(false);\r\n setPendingMode(null);\r\n }\r\n }, []);\r\n\r\n // const onAutomationModeChange = useCallback(async (e: RadioChangeEvent) => {\r\n // if (e.target.value == ManualFanMode.AlwaysOn || e.target.value == ManualFanMode.AlwaysOff) {\r\n // submitFormManually();\r\n // }\r\n // }, [submitFormManually]);\r\n\r\n return (
\r\n \r\n {/* Fan Settings */}\r\n {/* Desired Fan Setting */}\r\n \r\n \r\n {\r\n form.setFieldValue(\"mode\", (e.target as HTMLInputElement).value);\r\n submitFormManually();\r\n }} value={ManualFanMode.AlwaysOff}>Off \r\n {\r\n form.setFieldValue(\"mode\", (e.target as HTMLInputElement).value);\r\n submitFormManually();\r\n }} value={ManualFanMode.AlwaysOn}>On \r\n Auto \r\n \r\n \r\n\r\n {/* {automationMode === ManualFanMode.AutoWeather && \r\n\r\n\r\n\r\n {/* \r\n \r\n */}\r\n \r\n );\r\n};\r\n\r\n\r\nexport interface SettingsFormValues {\r\n minEMC: number,\r\n maxEMC: number,\r\n minTemp: number,\r\n maxTemp: number,\r\n mode: ManualFanMode,\r\n fanRemainingTimeOn: number | null\r\n}\r\n\r\ninterface SettingsAdjustmentProps {\r\n //formIsDirty: boolean,\r\n binDTO: BinDTO | null | undefined,\r\n isSubmitting: boolean\r\n hidden: boolean;\r\n reset: () => void;\r\n cancel: () => void;\r\n}\r\n\r\nexport const fillEmpty = (metric: number | null | undefined) => {\r\n if (metric == null) {\r\n return \"___\";\r\n }\r\n else {\r\n return metric;\r\n }\r\n\r\n}\r\n\r\nconst EMCHelp = () => {\r\n return <>\r\n Moisture Content of air at which grain will equalize to.\r\n ;\r\n}\r\n\r\nexport const formatTotalSecondsToDays = (d: duration.Duration) => {\r\n\r\n const formatted = `${Math.floor(d.asDays())} day(s), ${String(d.hours()).padStart(2, '0')}:${String(d.minutes()).padStart(2, '0')}`;\r\n return formatted;\r\n\r\n}\r\n\r\nconst SettingsAdjustment = (props: SettingsAdjustmentProps) => {\r\n\r\n const permissions = usePermissions();\r\n\r\n const antdForm = useFormInstance();\r\n const minEmcForm = Form.useWatch(\"minEMC\", antdForm);\r\n const maxEmcForm = Form.useWatch(\"maxEMC\", antdForm);\r\n const minTempForm = Form.useWatch(\"minTemp\", antdForm);\r\n const maxTempForm = Form.useWatch(\"maxTemp\", antdForm);\r\n const fanRemainingTimeOn = props.binDTO?.fanOperations?.fanRemainingOnTimeSeconds;\r\n const d = dayjs.duration(fanRemainingTimeOn! * 1000);\r\n const formatted = formatTotalSecondsToDays(d);\r\n const timerIsRunning = props.binDTO?.fanOperations?.desiredFanOn && !props.binDTO?.fanOperations?.ignoreFanRemainingOnTime && props.binDTO?.fanOperations?.offReason == null;\r\n const timerIsFinished = props.binDTO?.fanOperations?.offReason == \"FanTimer\" && props.binDTO?.fanOperations?.fanRemainingOnTimeSeconds == 0;\r\n \r\n const originalFanRemainingOnTimer = props.binDTO?.fanOperations?.originalFanRemainingOnTimeSeconds == null ?\r\n 0 : props.binDTO?.fanOperations?.originalFanRemainingOnTimeSeconds / 3600;\r\n \r\n //console.log(\"the settings adjust form values\", {minEmcForm, maxEmcForm, minTempForm, maxTempForm});\r\n //console.log(\"fields are touched: \", !antdForm.isFieldsTouched(false), \"form still submitting: \", props.isSubmitting);\r\n\r\n const [, forceUpdate] = useState();\r\n\r\n const formatFanEMCText = () => {\r\n\r\n const originalMinEMC = minEmcForm;\r\n const originalMaxEMC = maxEmcForm;\r\n\r\n let offsetMaxEMC = maxEmcForm;\r\n if (offsetMaxEMC != null) {\r\n offsetMaxEMC += props.binDTO?.weatherMonitorState?.heaterPlenumMcOffset! ?? -5;\r\n }\r\n const offsetActive = props.binDTO?.heaterOffsetShouldApplyNext;\r\n return \r\n Fan will run between {fillEmpty(originalMinEMC)}% and \r\n {fillEmpty(originalMaxEMC)}%\r\n {offsetActive && <> {fillEmpty(offsetMaxEMC)}%. +{props.binDTO?.weatherMonitorState?.heaterPlenumMcOffset}% Heater EMC Offset}\r\n \r\n\r\n\r\n }\r\n\r\n useEffect(() => {\r\n forceUpdate({});\r\n }, []);\r\n\r\n return <>\r\n \r\n
\r\n {/* Automated Fan Settings */}\r\n Starts and stops fan(s) based on weather conditions.\r\n
\r\n\r\n
\r\n \r\n EMC Setpoint\r\n \r\n \r\n \r\n \r\n {formatFanEMCText()}\r\n \r\n Min EMC %}\r\n rules={[{ type: \"number\", min: 0, max: 100, message: \"Between 0 and 100%\" }]}\r\n >\r\n \r\n \r\n\r\n Max EMC %}\r\n rules={[{ type: \"number\", min: 0, max: 100, message: \"Between 0 and 100%\" }]}\r\n >\r\n \r\n \r\n \r\n
\r\n\r\n
\r\n Temperature Setpoint\r\n Fan will run between {fillEmpty(minTempForm)}℉ and {fillEmpty(maxTempForm)}℉\r\n \r\n Min Temp ℉}>\r\n \r\n \r\n\r\n Max Temp ℉}>\r\n \r\n \r\n \r\n
\r\n\r\n {/*
\r\n Fan Timer\r\n Fan is set to run while within the specified temp & EMC setpoints\r\n \r\n Time in hours}>\r\n \r\n \r\n \r\n\r\n {timerIsRunning &&\r\n \r\n Current timer has {formatted} remaining\r\n {/*

*//*}\r\n {/* Original Time requested {originalFanRemainingOnTimer} hours *//*}\r\n
\r\n }\r\n\r\n {timerIsFinished &&\r\n \r\n Timer of {originalFanRemainingOnTimer} hours has completed\r\n \r\n }\r\n
*/}\r\n \r\n\r\n \r\n {() => (\r\n \r\n {antdForm.isFieldsTouched(false) && \r\n \r\n \r\n }\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n )\r\n}\r\n \r\n
\r\n \r\n}\r\n\r\nconst OnDescription = () => {\r\n return null;\r\n}\r\n\r\nconst OffDescription = () => {\r\n return null;\r\n}","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum RhSensorEnum {\r\n Opi = 'Opi',\r\n PowerCast = 'PowerCast',\r\n BinSense = 'BinSense',\r\n}\r\nexport default RhSensorEnum;\r\n","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum TemperatureSensorEnum {\r\n Thermocouple = 'Thermocouple',\r\n Opi = 'Opi',\r\n PowerCast = 'PowerCast',\r\n BinSense = 'BinSense',\r\n}\r\nexport default TemperatureSensorEnum;\r\n","import RhSensorEnum from \"src/consts/RhSensorEnum\";\r\nimport TemperatureSensorEnum from \"src/consts/TemperatureSensorEnum\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\n\r\nexport const getDefaultTemperatureSensorType = (binDTO: BinDTO): TemperatureSensorEnum => {\r\n\r\n if (binDTO == null) {\r\n return TemperatureSensorEnum.Thermocouple;\r\n }\r\n\r\n if (binDTO.temperatureCables?.length) {\r\n return TemperatureSensorEnum.Thermocouple;\r\n }\r\n else if (binDTO.opiMoistureCables?.length) {\r\n return TemperatureSensorEnum?.Opi;\r\n }\r\n else if (binDTO.binSenseMoistureCables?.length) {\r\n return TemperatureSensorEnum?.BinSense;\r\n }\r\n else {\r\n return TemperatureSensorEnum.PowerCast;\r\n }\r\n}\r\n\r\nexport const getDefaultRhSensorType = (binDTO: BinDTO): RhSensorEnum => {\r\n\r\n if (binDTO == null) {\r\n return RhSensorEnum.Opi;\r\n }\r\n\r\n if (binDTO.opiMoistureCables?.length) {\r\n return RhSensorEnum.Opi;\r\n }\r\n else if (binDTO.binSenseMoistureCables?.length) {\r\n return RhSensorEnum.BinSense;\r\n }\r\n else {\r\n return RhSensorEnum.PowerCast;\r\n }\r\n}","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum HardwareYear {\r\n UNDEFINED = 0,\r\n HARDWARE_2021 = 2021,\r\n HARDWARE_2022 = 2022,\r\n HARDWARE_2023 = 2023,\r\n HARDWARE_2024 = 2024,\r\n}\r\nexport default HardwareYear;\r\n","import React, {PropsWithChildren, useEffect, useMemo, useRef, useState } from \"react\";\r\nimport { Button, Col, Form, InputNumber, message, Modal, Popconfirm, Popover, Row, Space, Tag, Tooltip, Typography } from \"antd\";\r\n\r\n// @ts-ignore\r\nimport style from \"./form.module.css\";\r\n\r\nimport FanDTO from \"src/models/FanDTO\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport { OperatingMode, fanDurationFormatted } from \"../dashboard/BinStatusPage/BinVisualThree\";\r\nimport { formatFanAmps } from \"../dashboard/BinStatusPage/FanCard\";\r\n\r\nimport ReportedFanSettingsDTO from \"src/models/ReportedFanSettingsDTO\";\r\nimport { fanStatusText, pauseReasonText, WeatherConditionsLimitTooltipText, AcVoltageIsOnTooltipText } from \"./shared\";\r\nimport { fillEmpty, formatTotalSecondsToDays, SettingsFormValues, WeatherMonitorForm } from \"./WeatherMonitorForm\";\r\nimport { Prompt } from \"react-router\";\r\nimport { FormInstance } from \"antd/lib/form/Form\";\r\nimport { formatPlenumPressure } from \"src/utils/formatting\";\r\nimport { CheckCircleFilled, CiCircleFilled, CloseCircleFilled, EditOutlined, InfoCircleFilled, QuestionCircleOutlined, WarningFilled, ApiFilled } from \"@ant-design/icons\";\r\nimport { useForm, useWatch } from \"antd/es/form/Form\";\r\nimport { useMutation } from \"@tanstack/react-query\";\r\nimport BinAPIService from '../../api/BinApiService';\r\nimport { layout2, warningIconColor } from \"../dashboard/BinStatusPage/BinStatsPage\";\r\nimport dayjs from \"dayjs\";\r\nimport {bind, round} from 'lodash-es';\r\nimport { formatNumber } from \"../dashboard/BinStatusPage/HeaterControls\";\r\nimport { Duration, DurationUnitType } from \"dayjs/plugin/duration\";\r\nimport FanOffReason from \"src/consts/FanOffReason\";\r\nimport { getDefaultTemperatureSensorType } from \"../shared/binDTOUtils\";\r\nimport TemperatureSensorEnum from \"src/consts/TemperatureSensorEnum\";\r\nimport { useBinDTOContext } from \"src/queries/BinDTOContext\";\r\nimport { weatherConditionEvaluation } from \"src/queries/BinDTOContext\";\r\nimport { WeatherConditionRangeStatus } from \"src/queries/BinDTOContext\";\r\nimport { computeWeatherConditionInfo } from \"src/queries/BinDTOContext\";\r\nimport HardwareYear from \"src/consts/HardwareYear\";\r\nimport { usePermissions } from \"./usePermissions\";\r\n\r\nexport const isAnyFanOn = (fans: FanDTO[] | null): boolean | null => {\r\n if (fans == null) {\r\n return null;\r\n }\r\n\r\n for (const fan of fans) {\r\n if (fan.isOn === true) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n}\r\ninterface WeatherMonitorProps {\r\n binDTO: BinDTO,\r\n deviceId: string,\r\n loading: boolean,\r\n form?: FormInstance,\r\n onCancel?: () => void,\r\n /**\r\n *\r\n * @param data the fan form settings to apply\r\n * @returns if submit to server was successful\r\n */\r\n onSubmit: (data: SettingsFormValues) => Promise,\r\n newFanSettings: Partial | null | undefined,\r\n operatingMode: OperatingMode,\r\n\r\n}\r\n\r\ninterface FanReadingProps {\r\n ambientAirCombined: BinDTO['ambientAir'],\r\n plenumAirCombined: BinDTO['plenumAir'],\r\n fans: BinDTO['fans'],\r\n bin: BinDTO,\r\n fanRunTimeSeconds: number | undefined,\r\n}\r\n\r\n\r\nexport const isAtLeastOneLayerBelowDewpoint = (binDTO: BinDTO): boolean | null => {\r\n let grainLayerAtRiskOfCondensation: boolean | null = null;\r\n const preferredTemperatureSensorType = binDTO?.temperatureSensorsType ?? getDefaultTemperatureSensorType(binDTO);\r\n\r\n const theLayerBelowDewpoint = binDTO?.layerGrainStates?.find(layer => {\r\n\r\n const temperatureF = [TemperatureSensorEnum.Opi, TemperatureSensorEnum.PowerCast, TemperatureSensorEnum.BinSense].includes(preferredTemperatureSensorType) ? layer.temperatureF : layer.thermocoupleTemperatureF;\r\n if (temperatureF == null) {\r\n return false;\r\n }\r\n if (binDTO?.plenumAir?.dp == null) {\r\n return false;\r\n }\r\n return temperatureF < binDTO?.plenumAir?.dp;\r\n });\r\n grainLayerAtRiskOfCondensation = theLayerBelowDewpoint != null;\r\n return grainLayerAtRiskOfCondensation;\r\n}\r\n\r\nexport interface AdditionalWeatherConditionDerived {\r\n inRangeInstant: boolean | null,\r\n inRangeLong: boolean,\r\n emcInRange: boolean | null,\r\n temperatureInRange: boolean | null,\r\n grainLayerAtRiskOfCondensation: boolean | null,\r\n heaterOffsetApplied: boolean,\r\n timeTillPhaseChange: Duration | null,\r\n weatherConditionRangeStatus: WeatherConditionRangeStatus,\r\n}\r\n\r\n\r\ninterface WeatherConditionStatusIndicatorProps {\r\n binDTO: BinDTO,\r\n}\r\n\r\ninterface WeatherConditionRangeTooltip {\r\n\r\n}\r\n\r\nconst WeatherConditionRangeTooltip = (props: PropsWithChildren) => {\r\n \r\n return }>\r\n \r\n {props.children}\r\n \r\n \r\n}\r\n\r\nexport const AmbientConditionsStatusIndicator = (props: WeatherConditionStatusIndicatorProps) => {\r\n\r\n if (props.binDTO == null) {\r\n return null;\r\n }\r\n \r\n\r\n const weatherConditionExtraInfo = useMemo(() => computeWeatherConditionInfo(props.binDTO), [props.binDTO]);\r\n //console.log(\"weather conditions extra info: \", weatherConditionExtraInfo);\r\n if (weatherConditionExtraInfo == null) {\r\n return null;\r\n }\r\n\r\n // hide if in manual mode & not in auto fan mode.\r\n if (props.binDTO?.operatingMode === OperatingMode.Manual && (props.binDTO?.fanOperations?.desiredFanOn === false || (props.binDTO?.fanOperations?.desiredFanOn === true && props.binDTO?.fanOperations?.ignoreWeatherConditions))) {\r\n return null;\r\n }\r\n\r\n // only show in modes where weather monitor is used\r\n if (![OperatingMode.FillBin, OperatingMode.PreDry, OperatingMode.TopDry, OperatingMode.Dry, OperatingMode.Storage, OperatingMode.Manual].includes(props.binDTO?.operatingMode)) {\r\n return null;\r\n }\r\n\r\n const weatherConditionStatusResult = weatherConditionEvaluation(props.binDTO);\r\n \r\n if (weatherConditionStatusResult === WeatherConditionRangeStatus.FullyIn) {\r\n return }>\r\n \r\n \r\n \r\n \r\n }\r\n else if ([WeatherConditionRangeStatus.Entering, WeatherConditionRangeStatus.Exitting].includes(weatherConditionStatusResult)) {\r\n\r\n return }>\r\n \r\n \r\n \r\n \r\n }\r\n else if (weatherConditionStatusResult === WeatherConditionRangeStatus.Outside) {\r\n return }>\r\n \r\n \r\n \r\n \r\n }\r\n else {\r\n return null;\r\n }\r\n }\r\n\r\nexport const DewPointWarningTooltipByFanReadings = () => {\r\n\r\n return \r\n \r\n \r\n \r\n \r\n}\r\n\r\nexport const DewPointOkTooltipByFanReadings = () => {\r\n\r\n return \r\n \r\n \r\n \r\n \r\n}\r\n\r\nexport const CheckPassedIcon = (props: any) => {\r\n const {style = {}, ...rest} = props;\r\n return \r\n}\r\n\r\nexport const WarningInfoIcon = (props: any) => {\r\n const {style = {}, ...rest} = props;\r\n return \r\n}\r\n\r\nexport const CheckFailedIcon = (props: any) => {\r\n const {style = {}, ...rest} = props;\r\n return \r\n}\r\n\r\nexport const IsPlugConnected = (isPlugged: boolean | null) => {\r\n const iconColor = (isPlugged === null) ? \"gray\" : ((isPlugged) ? \"green\" : \"red\")\r\n return \r\n}\r\n\r\nexport const AcVoltageIsOnTooltip = (fanNumber: number) => {\r\n const {binDTO} = useBinDTOContext();\r\n const fanBoard = binDTO?.devices?.fanBoards?.find(board => board.deviceID == (0x200 + fanNumber))\r\n const fan = binDTO?.fans?.find(fan => fan.number == fanNumber)\r\n\r\n if (fanBoard == null || fanBoard.boardYear === HardwareYear.HARDWARE_2021 || \r\n fanBoard.boardYear === HardwareYear.HARDWARE_2022 || fan == null) {\r\n return null\r\n }\r\n\r\n const isPlugged = fanBoard.isACVoltageOn\r\n\r\n return \r\n \r\n {IsPlugConnected(isPlugged)}\r\n \r\n \r\n}\r\n\r\nexport const EMCInRangeToolTip = () => {\r\n\r\n return \r\n \r\n \r\n \r\n \r\n}\r\n\r\nexport const EMCRangeTransitionTooltip = () => {\r\n return \r\n \r\n \r\n \r\n \r\n}\r\n\r\nexport const EmcOutOfRangeTooltip = () => {\r\n return \r\n \r\n \r\n \r\n \r\n}\r\n\r\n\r\nexport const TemperatureInRangeToolTip = () => {\r\n\r\n return \r\n \r\n \r\n \r\n \r\n}\r\n\r\nexport const TemperatureRangeTransitionTooltip = () => {\r\n return \r\n \r\n \r\n \r\n \r\n}\r\n\r\nexport const TemperatureOutOfRangeTooltip = (props: {}) => {\r\n return \r\n \r\n \r\n \r\n \r\n}\r\n\r\nexport const TemperatureRangeIndicator = (props: {}) => {\r\n const {additionalWeatherInfo} = useBinDTOContext();\r\n if (additionalWeatherInfo?.temperatureInRange == null) {\r\n return null;\r\n }\r\n\r\n if (additionalWeatherInfo.temperatureInRange) {\r\n return ;\r\n }\r\n else {\r\n return ;\r\n }\r\n}\r\n\r\nexport const EmcRangeIndicator = () => {\r\n const {additionalWeatherInfo, binDTO} = useBinDTOContext();\r\n //console.log(\"additional weather info: \", {binDTO, additionalWeatherInfo});\r\n if (additionalWeatherInfo?.emcInRange == null) {\r\n return null;\r\n }\r\n\r\n if (additionalWeatherInfo.emcInRange) {\r\n return ;\r\n }\r\n else {\r\n return ;\r\n }\r\n}\r\n\r\nexport const AmbientemcText = (binDTO: BinDTO) => {\r\n if (binDTO?.ambientAir?.mc == null) {\r\n return no data;\r\n }\r\n return formatNumber(binDTO.ambientAir?.mc, {filler: \"no data\", suffix: \"%\", decimalPlaces: 1});\r\n}\r\n\r\nexport const plenumEMCText = (binDTO: BinDTO) => {\r\n if (binDTO.plenumAir?.mc == null) {\r\n return no data;\r\n }\r\n return formatNumber(binDTO.plenumAir?.mc, {filler: \"no data\", suffix: \"%\", decimalPlaces: 1});\r\n}\r\n\r\nexport const CurrentReadings = (props: FanReadingProps) => {\r\n\r\n const {additionalWeatherInfo} = useBinDTOContext();\r\n\r\n const anyFanOn = isAnyFanOn(props.fans);\r\n\r\n const AmbientTempText = () => {\r\n if (props.ambientAirCombined?.temp == null) {\r\n return no data;\r\n }\r\n return `${props.ambientAirCombined.temp.toFixed(1)}℉`;\r\n }\r\n\r\n const plenumTempText = () => {\r\n if (props.plenumAirCombined?.temp == null) {\r\n return no data;\r\n }\r\n return `${props.plenumAirCombined.temp.toFixed(1)}℉`;\r\n }\r\n\r\n const isAtLeastOneLayerBelowDewpointResult = useMemo(() => isAtLeastOneLayerBelowDewpoint(props.bin), [props.bin]);\r\n\r\n\r\n return <>\r\n Incoming Air Conditions\r\n \r\n \r\n \r\n {anyFanOn === false && <>\r\n \r\n Ambient EMC: {AmbientemcText(props.bin)}\r\n \r\n \r\n }\r\n {anyFanOn === false && <>\r\n \r\n Ambient Temp: {AmbientTempText()}\r\n \r\n \r\n \r\n }\r\n {anyFanOn === false && <>Ambient Dew Point: {formatNumber(props.bin?.ambientAir?.dp!, { suffix: \" ℉\", decimalPlaces: 1 })}\r\n {isAtLeastOneLayerBelowDewpointResult === true && <>  }\r\n {isAtLeastOneLayerBelowDewpointResult === false && <>  }\r\n \r\n \r\n }\r\n {anyFanOn === true && <>\r\n \r\n Plenum EMC: {plenumEMCText(props.bin!)}\r\n \r\n \r\n }\r\n {anyFanOn === true && <>\r\n \r\n Plenum Temp: {plenumTempText()}\r\n \r\n \r\n }\r\n {anyFanOn === true && <>Plenum Dew Point: {formatNumber(props.bin?.plenumAir?.dp!, { suffix: \" ℉\", decimalPlaces: 1 })}\r\n {isAtLeastOneLayerBelowDewpointResult === true && <>  }\r\n {isAtLeastOneLayerBelowDewpointResult === false && <>  }\r\n \r\n }\r\n\r\n \r\n \r\n \r\n \r\n Fan Status\r\n {fanStatusText(props.bin)}\r\n \r\n {pauseReasonText(props.bin)}\r\n \r\n \r\n \r\n\r\n \r\n \r\n {props.fans?.map(fan => {\r\n\r\n return (\r\n
\r\n \r\n \r\n Fan {fan.id}\r\n \r\n \r\n {fan.isOn ? \"ON\" : \"OFF\"}\r\n \r\n \r\n \r\n {formatFanAmps(fan.ampReading)} Amps\r\n {AcVoltageIsOnTooltip(fan.number)}\r\n \r\n {fan.plenumPressure != null && <>\r\n
\r\n {formatPlenumPressure(fan.plenumPressure)} H2O Plenum Pressure\r\n }\r\n
\r\n );\r\n\r\n\r\n })}\r\n
\r\n\r\n
\r\n {props.fanRunTimeSeconds != null &&\r\n \r\n Fan Runtime\r\n The fans have run for {fanDurationFormatted((props.fanRunTimeSeconds ?? 0))}
during the current batch.
\r\n\r\n
\r\n\r\n }\r\n
\r\n ;\r\n\r\n}\r\n\r\n// https://usehooks.com/usePrevious/\r\nexport const usePrevious = (value: T, updateFn?: (prev: T, current: T) => boolean): T => {\r\n\r\n // The ref object is a generic container whose current property is mutable ...\r\n // ... and can hold any value, similar to an instance property on a class\r\n const ref: any = useRef();\r\n // Store current value in ref\r\n useEffect(() => {\r\n // skip update if instructed\r\n if (updateFn?.(ref.current, value) === false) {\r\n return;\r\n }\r\n ref.current = value;\r\n }, [value]); // Only re-run if value changes\r\n // Return previous value (happens before update in useEffect above)\r\n return ref.current;\r\n\r\n}\r\n\r\ninterface NonManualSettingsProps {\r\n binDTO: BinDTO,\r\n deviceId: string,\r\n}\r\n\r\ninterface NonManualSettingsForm {\r\n binDTO: BinDTO;\r\n onFinish: (values: NonManualSettingsFormValues) => void;\r\n}\r\n\r\ninterface NonManualSettingsFormValues {\r\n weatherConditionsMaxMc: number | null;\r\n weatherConditionsMinMc: number | null;\r\n weatherConditionsMaxTemperatureF: number | null;\r\n weatherConditionsMinTemperatureF: number | null;\r\n}\r\n\r\n\r\nconst updateWeatherMonitorNonManualFormId = 'weatherSettings-nonstorage';\r\nconst updateFanTimerOnlyFormId = \"updateFanTimerOnly\";\r\nconst updateFanTimerOnlyPerWeekFormId = 'updateFanTimerOnlyPerWeekFormId';\r\nconst NonManualSettingsForm = (props: NonManualSettingsForm) => {\r\n\r\n const [form] = useForm();\r\n const fanRangeSettingsFormValues = useWatch([], form);\r\n\r\n const permissions = usePermissions();\r\n\r\n\r\n const initialValues: NonManualSettingsFormValues = {\r\n weatherConditionsMinMc: props.binDTO?.weatherMonitorState?.minMcLimit ?? null,\r\n weatherConditionsMaxMc: props.binDTO?.weatherMonitorState?.maxMcLimit ?? null,\r\n weatherConditionsMaxTemperatureF: props.binDTO?.weatherMonitorState?.maxTemperatureLimitF ?? null,\r\n weatherConditionsMinTemperatureF: props.binDTO?.weatherMonitorState?.minTemperatureLimitF ?? null,\r\n };\r\n\r\n return (\r\n
\r\n \r\n Fan will run when incoming air EMC is b/t {fillEmpty(fanRangeSettingsFormValues?.weatherConditionsMinMc)} & {fillEmpty(fanRangeSettingsFormValues?.weatherConditionsMaxMc)}% & temp is b/t {fillEmpty(fanRangeSettingsFormValues?.weatherConditionsMinTemperatureF)} & {fillEmpty(fanRangeSettingsFormValues?.weatherConditionsMaxTemperatureF)}℉\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n\r\n
\r\n );\r\n}\r\n\r\nexport const useSetWeatherMonitorSettings = (deviceId: string) => {\r\n\r\n return useMutation({\r\n mutationFn: async (values: NonManualSettingsFormValues) => {\r\n return await BinAPIService.setWeatherMonitorSettings(deviceId, {\r\n weatherConditionsMinMc: values.weatherConditionsMinMc,\r\n weatherConditionsMaxMc: values.weatherConditionsMaxMc,\r\n weatherConditionsMinTemperatureF: values.weatherConditionsMinTemperatureF,\r\n weatherConditionsMaxTemperatureF: values.weatherConditionsMaxTemperatureF,\r\n })\r\n }\r\n });\r\n}\r\n\r\nexport const useUpdateFanTimer = (deviceId: string) => {\r\n\r\n return useMutation({\r\n mutationFn: async (values: {seconds: number}) => {\r\n return await BinAPIService.setFanRemainingOnTime({\r\n deviceId: deviceId,\r\n seconds: values.seconds,\r\n })\r\n }\r\n });\r\n}\r\n\r\nexport const useUpdateFanTimerPerWeek = (deviceId: string) => {\r\n\r\n return useMutation({\r\n mutationFn: async (values: {secondsPerWeek: number}) => {\r\n return await BinAPIService.setFanRemainingOnTimePerWeek({\r\n deviceId: deviceId,\r\n seconds: values.secondsPerWeek,\r\n })\r\n }\r\n });\r\n}\r\n\r\ninterface UpdateFanTImerFormProps {\r\n binDTO: BinDTO;\r\n onFinish: (values: FanTimerFormValues) => void;\r\n}\r\n\r\nconst UpdateFanTimerForm = (props: UpdateFanTImerFormProps) => {\r\n\r\n const [form] = useForm();\r\n const getFanTimerFormInitialValues = (binDTO: BinDTO): FanTimerFormValues => {\r\n return {\r\n fanTimerHours: round((binDTO?.fanOperations?.fanRemainingOnTimeSeconds ?? 3600 * 2) / 3600, 2),\r\n }\r\n };\r\n\r\n const fanTimerInitialValues = getFanTimerFormInitialValues(props.binDTO);\r\n\r\n return (\r\n
\r\n \r\n \r\n \r\n
\r\n );\r\n}\r\n\r\nconst UpdateFanTimerPerWeekForm = (props: UpdateFanTImerFormProps) => {\r\n\r\n const [form] = useForm();\r\n const getFanTimerFormInitialValues = (binDTO: BinDTO): FanTimerFormValues => {\r\n return {\r\n fanTimerHours: round((binDTO?.fanOperations?.fanRemainingOnTimeSeconds ?? 3600 * 2) / 3600, 2),\r\n }\r\n };\r\n\r\n const fanTimerInitialValues = getFanTimerFormInitialValues(props.binDTO);\r\n\r\n return (\r\n
\r\n \r\n \r\n \r\n
\r\n );\r\n\r\n}\r\n\r\ninterface FanTimerFormValues {\r\n fanTimerHours: number,\r\n}\r\n\r\n\r\nconst NonManualSettings = (props: NonManualSettingsProps) => {\r\n\r\n const minEmcForm = props.binDTO?.weatherMonitorState?.minMcLimit;\r\n const maxEmcForm = props.binDTO?.weatherMonitorState?.maxMcLimit;\r\n const minTempForm = props.binDTO?.weatherMonitorState?.minTemperatureLimitF;\r\n const maxTempForm = props.binDTO?.weatherMonitorState?.maxTemperatureLimitF;\r\n const timeLimit = props.binDTO?.fanOperations?.fanRemainingOnTimeSeconds;\r\n\r\n const mutateSetWeatherMonitorSettings = useSetWeatherMonitorSettings(props.deviceId);\r\n const mutateFanTimer = useUpdateFanTimer(props.deviceId);\r\n const mutateFanTimerPerWeek = useUpdateFanTimerPerWeek(props.deviceId);\r\n\r\n const [openUpdateWeatherSettings, setOpenWeatherSettings] = useState(false);\r\n const [openChangeFanTimer, setOpenChangeFanTimer] = useState(false);\r\n const [openChangeFanTimerPerWeek, setOpenChangeFanTimerPerWeek] = useState(false);\r\n\r\n const showTimer = ![OperatingMode.FillBin, OperatingMode.Idle, OperatingMode.EmptyBin, OperatingMode.Dry, OperatingMode.PreDry, OperatingMode.TopDry].includes(props.binDTO?.operatingMode!);\r\n const permissions = usePermissions();\r\n\r\n const formatFanEMCText = () => {\r\n\r\n const originalMinEMC = minEmcForm;\r\n const originalMaxEMC = maxEmcForm;\r\n\r\n let offsetMaxEMC = maxEmcForm;\r\n if (offsetMaxEMC != null) {\r\n offsetMaxEMC += props.binDTO?.weatherMonitorState?.heaterPlenumMcOffset! ?? -5;\r\n }\r\n const offsetActive = props.binDTO?.heaterOffsetShouldApplyNext;\r\n return <>\r\n Fan will run between {fillEmpty(originalMinEMC)}% and \r\n {fillEmpty(originalMaxEMC)}%\r\n {offsetActive && <>{fillEmpty(offsetMaxEMC)}%. +{props.binDTO?.weatherMonitorState?.heaterPlenumMcOffset}% Heater EMC Offset}\r\n \r\n\r\n\r\n }\r\n\r\n return <>\r\n \r\n
\r\n Automated Fan Settings\r\n Starts and stops fan(s) based on weather conditions.\r\n
\r\n\r\n
\r\n \r\n EMC & Temperature Setpoint \r\n {/* \r\n \r\n */}\r\n \r\n \r\n {formatFanEMCText()}\r\n
\r\n AND while between {fillEmpty(minTempForm)} ℉ and {fillEmpty(maxTempForm)} ℉.\r\n
\r\n\r\n
\r\n\r\n {showTimer &&
\r\n Fan Timer {props.binDTO?.operatingMode === OperatingMode.Storage ? \" (Weekly)\" : \"\"} \r\n Fan is set to run while within the specified temp & EMC setpoints\r\n\r\n Current timer has {formatTotalSecondsToDays(dayjs.duration((timeLimit ?? 0) * 1000))} remaining{props.binDTO?.operatingMode === OperatingMode.Storage ? \" for the week\" : \"\"}.\r\n
}\r\n\r\n
\r\n\r\n {\r\n setOpenWeatherSettings(false);\r\n }} okButtonProps={{form: updateWeatherMonitorNonManualFormId, htmlType: 'submit', type: 'primary',\r\n loading: mutateSetWeatherMonitorSettings.isLoading,\r\n disabled: mutateSetWeatherMonitorSettings.isLoading,\r\n }}>\r\n {\r\n mutateSetWeatherMonitorSettings.mutate({\r\n ...values\r\n }, {onSuccess: async (data) => {\r\n if (data.success === true) {\r\n message.info(\"EMC & Temperature Settings updated\");\r\n setOpenWeatherSettings(false);\r\n }\r\n else {\r\n message.error(\"System did not update EMC & Temperature Settings\");\r\n }\r\n }, onError(error, variables, context) {\r\n console.error(\"Error updating EMC & Temperature Settings\", error);\r\n message.error(\"Problem updating EMC & Temperature Settings. Try again\");\r\n },})\r\n }\r\n } />\r\n \r\n\r\n {\r\n setOpenChangeFanTimer(false);\r\n }} okButtonProps={{form: updateFanTimerOnlyFormId, htmlType: 'submit', type: 'primary', loading: mutateFanTimer.isLoading, disabled: mutateFanTimer.isLoading}}>\r\n {props.binDTO?.operatingMode !== OperatingMode.Storage && {\r\n const seconds = round(values.fanTimerHours * 3600);\r\n mutateFanTimer.mutate({seconds: seconds}, {onSuccess: (result) => {\r\n if (result.success === true) {\r\n message.success(\"Updated fan timer\");\r\n setOpenChangeFanTimer(false);\r\n }\r\n else {\r\n message.error(\"Error updating fan timer\");\r\n }\r\n }});\r\n }} />}\r\n\r\n {props.binDTO?.operatingMode === OperatingMode.Storage && {\r\n const seconds = round(values.fanTimerHours * 3600);\r\n mutateFanTimerPerWeek.mutate({secondsPerWeek: seconds}, {onSuccess: (result) => {\r\n if (result.success === true) {\r\n message.success(\"Updated fan timer\");\r\n setOpenChangeFanTimer(false);\r\n }\r\n else {\r\n message.error(\"Error updating fan timer\");\r\n }\r\n }});\r\n }} />}\r\n \r\n\r\n\r\n \r\n {\r\n setOpenChangeFanTimerPerWeek(false);\r\n }} okButtonProps={{form: updateFanTimerOnlyPerWeekFormId, htmlType: 'submit', type: 'primary', loading: mutateFanTimerPerWeek.isLoading, disabled: mutateFanTimerPerWeek.isLoading}}>\r\n\r\n {\r\n const seconds = round(values.fanTimerHours * 3600);\r\n mutateFanTimerPerWeek.mutate({secondsPerWeek: seconds}, {onSuccess: (result) => {\r\n if (result.success === true) {\r\n message.success(\"Updated fan timer\");\r\n setOpenChangeFanTimerPerWeek(false);\r\n }\r\n else {\r\n message.error(\"Error updating fan timer\");\r\n }\r\n }});\r\n }} />\r\n \r\n \r\n}\r\n\r\nexport const WeatherMonitor = (props: WeatherMonitorProps) => {\r\n\r\n console.log(\"newSettings for fan\", props.newFanSettings);\r\n\r\n const [form] = Form.useForm(props.form);\r\n const permissions = usePermissions();\r\n\r\n // const previousReportedFanSettings = usePrevious(props.newFanSettings, (prev, current) => {\r\n // if (prev?.ts != null && current?.ts != null && prev?.ts >= current?.ts) {\r\n // console.log(\"do not update\", prev, current);\r\n // false;\r\n // }\r\n\r\n // if (submissionDate != null && props.newFanSettings?.ts != null && !dayjs(props.newFanSettings?.ts).isAfter(dayjs(submissionDate))) {\r\n // console.log(\"not update due to new settings <= submission date\", submissionDate, prev, current);\r\n // return false;\r\n // }\r\n // console.log(\"update the previous settings\", prev, current);\r\n // return true;\r\n // });\r\n\r\n if (props.loading) {\r\n return null;\r\n }\r\n\r\n console.log(\"weather monitor props\", props);\r\n const shouldHideNonManualAutomationSettinsg = [OperatingMode.Idle, OperatingMode.EmptyBin].includes(props.binDTO?.operatingMode!);\r\n\r\n return
\r\n \r\n\r\n \r\n {(props.operatingMode === OperatingMode.Manual || props.operatingMode === OperatingMode.FillBin ||\r\n props.operatingMode === OperatingMode.PreDry || props.operatingMode === OperatingMode.Dry) && \r\n \r\n }\r\n\r\n {props.operatingMode === OperatingMode.Idle && \r\n \r\n }\r\n\r\n {props.binDTO?.operatingMode === OperatingMode.Storage && \r\n \r\n }\r\n \r\n \r\n \r\n \r\n
;\r\n}\r\n","import HeaterMode from \"src/consts/HeaterMode\";\r\n\r\nexport const formatHeaterMode = (heaterMode: HeaterMode) => {\r\n if (heaterMode == null) {\r\n return \"--\";\r\n }\r\n if (heaterMode === HeaterMode.Ambient) {\r\n return \"Ambient\";\r\n }\r\n else if (heaterMode === HeaterMode.EnergyEff) {\r\n return \"Efficiency\";\r\n }\r\n else if (heaterMode === HeaterMode.Speed5) {\r\n return \"5 Deg\";\r\n }\r\n else if (heaterMode === HeaterMode.Speed10) {\r\n return \"10 Deg\";\r\n }\r\n else {\r\n return \"Unknown\";\r\n }\r\n}\r\n\r\nexport const formatPlenumPressure = (plenumPressure: number | null | undefined): string => {\r\n if (!Number.isFinite(plenumPressure)) {\r\n return \"\";\r\n }\r\n if (plenumPressure == null) {\r\n return \"\";\r\n }\r\n\r\n return plenumPressure.toFixed(2);\r\n}\r\n","import { Button, Col, Modal, Row, Typography } from \"antd\";\r\nimport React from \"react\";\r\nimport { ReportedFormFanSettings, SettingsFormValues, WeatherMonitorForm } from \"../features/WeatherMonitorForm\";\r\nimport { FormInstance } from \"antd/lib/form/Form\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport { CurrentReadings } from \"../features/WeatherMonitorAntdOnly\";\r\nimport { useForm } from \"antd/es/form/Form\";\r\n\r\n\r\ninterface ManualFanSetingsModal {\r\n open: boolean;\r\n form?: FormInstance,\r\n onSubmit: (values: SettingsFormValues) => Promise,\r\n onCancel: () => void;\r\n fanSettings: Partial | null | undefined,\r\n binDTO: BinDTO,\r\n deviceId: string,\r\n\r\n}\r\nexport const ManualFanSettingsModal = (props: ManualFanSetingsModal) => {\r\n\r\n console.log(\"manual fan modal props\", props);\r\n\r\n const [form] = useForm(props.form);\r\n\r\n\r\n\r\n return <>\r\n \r\n Cancel (No Changes)\r\n \r\n ]} width={\"700px\"} >\r\n \r\n \r\n The following fan settings are being used, make changes if necessary.\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ;\r\n}","import React from \"react\";\r\nimport BinInfoDTO from \"src/models/BinInfoDTO\";\r\n\r\nexport const BinInfoContext = React.createContext(null);\r\n\r\nexport const useBinInfoContext = () => {\r\n const binInfo = React.useContext(BinInfoContext); \r\n if (!binInfo) {\r\n throw new Error('BinInfoContext: No value provided')\r\n }\r\n return binInfo;\r\n }","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum DeviceConnectionState {\r\n Disconnected = 'Disconnected',\r\n Connected = 'Connected',\r\n}\r\nexport default DeviceConnectionState;\r\n","import { Tag, Typography } from \"antd\";\r\nimport React from \"react\";\r\nimport DeviceConnectionState from \"src/consts/DeviceConnectionState\";\r\nimport Role from \"src/consts/Role\";\r\nimport BinDTO from \"src/models/BinDTO\"\r\nimport CurrentUser from \"src/utils/CurrentUser\";\r\nimport RoleUtil from \"src/utils/RoleUtil\";\r\n\r\ninterface BinOfflineIndicatorProps {\r\n binDTO: BinDTO | null | undefined\r\n}\r\n\r\nexport const ExtraOfflineInfoContext = React.createContext<{\r\n showExtraOfflineInfo: boolean,\r\n toggleOfflineInfo: () => void\r\n}>({showExtraOfflineInfo: false, toggleOfflineInfo: () => {}});\r\n\r\nexport const useExtraOfflineInfoContext = () => {\r\n return React.useContext(ExtraOfflineInfoContext)\r\n}\r\n\r\nexport const BinOfflineIndicator = (props: BinOfflineIndicatorProps) => {\r\n const isAdmin = RoleUtil.currentUserIsAdmin();\r\n\r\n const offlieInfoContext = useExtraOfflineInfoContext();\r\n\r\n if (props.binDTO == null) {\r\n return null;\r\n }\r\n if (props.binDTO.moduleConnectionState != DeviceConnectionState.Disconnected) {\r\n return null;\r\n }\r\n return (\r\n \r\n BIN OFFLINE\r\n \r\n );\r\n}","// Service TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nimport BaseApi from './BaseApi';\r\nimport AdminUserSearchDTO from '../models/AdminUserSearchDTO';\r\nimport ReadingDTO from '../models/ReadingDTO';\r\nimport SettingsTextDTO from '../models/SettingsTextDTO';\r\nimport SettingsDTO from '../models/SettingsDTO';\r\nimport AlarmDTO from '../models/AlarmDTO';\r\nimport AverageCyclesDTO from '../models/AverageCyclesDTO';\r\n\r\nexport class ShivversService extends BaseApi {\r\n\r\n // get: api/shivvers/SearchUsers?lastName=${encodeURIComponent(lastName)}&onlyCustomers=${onlyCustomers}\r\n public searchUsers(lastName?: string | null, onlyCustomers?: boolean | null, signal?: AbortSignal): Promise {\r\n let url = `api/shivvers/SearchUsers`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (lastName != null) {\r\n url += `${prefix}lastName=${encodeURIComponent(lastName || '')}`;\r\n prefix = '&';\r\n }\r\n if (onlyCustomers != null) {\r\n url += `${prefix}onlyCustomers=${onlyCustomers}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/shivvers/dealersDisplayName\r\n public dealersDisplayName(signal?: AbortSignal): Promise {\r\n let url = `api/shivvers/dealersDisplayName`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/shivvers/Readings?ip=${encodeURIComponent(ip)}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}\r\n public readings(ip: string, from?: string | null, to?: string | null, signal?: AbortSignal): Promise {\r\n let url = `api/shivvers/Readings`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (ip != null) {\r\n url += `${prefix}ip=${encodeURIComponent(ip)}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from || '')}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to || '')}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/shivvers/LastReadings?ip=${encodeURIComponent(ip)}&lastAmount=${lastAmount}\r\n public lastReadings(ip: string, lastAmount: number, signal?: AbortSignal): Promise {\r\n let url = `api/shivvers/LastReadings`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (ip != null) {\r\n url += `${prefix}ip=${encodeURIComponent(ip)}`;\r\n prefix = '&';\r\n }\r\n if (lastAmount != null) {\r\n url += `${prefix}lastAmount=${lastAmount}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/shivvers/LastReadingsByDate?ip=${encodeURIComponent(ip)}&lastSeconds=${lastSeconds}\r\n public lastReadingsByTimespan(ip: string, lastSeconds: number, signal?: AbortSignal): Promise {\r\n let url = `api/shivvers/LastReadingsByDate`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (ip != null) {\r\n url += `${prefix}ip=${encodeURIComponent(ip)}`;\r\n prefix = '&';\r\n }\r\n if (lastSeconds != null) {\r\n url += `${prefix}lastSeconds=${lastSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/shivvers/Paragraph?ip=${encodeURIComponent(ip)}\r\n public paragraph(ip: string, signal?: AbortSignal): Promise {\r\n let url = `api/shivvers/Paragraph`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (ip != null) {\r\n url += `${prefix}ip=${encodeURIComponent(ip)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/shivvers/Settings?ip=${encodeURIComponent(ip)}\r\n public settings(ip: string, signal?: AbortSignal): Promise {\r\n let url = `api/shivvers/Settings`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (ip != null) {\r\n url += `${prefix}ip=${encodeURIComponent(ip)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/shivvers/Alarms?ip=${encodeURIComponent(ip)}&from=${encodeURIComponent(String(from))}&to=${encodeURIComponent(String(to))}\r\n public alarms(ip: string, from: string | null, to: string | null, signal?: AbortSignal): Promise {\r\n let url = `api/shivvers/Alarms`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (ip != null) {\r\n url += `${prefix}ip=${encodeURIComponent(ip)}`;\r\n prefix = '&';\r\n }\r\n if (from != null) {\r\n url += `${prefix}from=${encodeURIComponent(from)}`;\r\n prefix = '&';\r\n }\r\n if (to != null) {\r\n url += `${prefix}to=${encodeURIComponent(to)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/shivvers/CycleAverages?ip=${encodeURIComponent(ip)}&lastSeconds=${lastSeconds}\r\n public cycleAverages(ip: string, lastSeconds: number, signal?: AbortSignal): Promise {\r\n let url = `api/shivvers/CycleAverages`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (ip != null) {\r\n url += `${prefix}ip=${encodeURIComponent(ip)}`;\r\n prefix = '&';\r\n }\r\n if (lastSeconds != null) {\r\n url += `${prefix}lastSeconds=${lastSeconds}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n}\r\nvar service = new ShivversService();\r\nexport default service;\r\n","// Service TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nimport BaseApi from './BaseApi';\r\nimport AddAzureDeviceDTO from '../models/AddAzureDeviceDTO';\r\nimport BinAssemblySubmittalDTO from '../models/BinAssemblySubmittalDTO';\r\nimport BinAssemblyDTO from '../models/BinAssemblyDTO';\r\nimport PCBLookupDTO from '../models/PCBLookupDTO';\r\nimport CreateSoftwareLookupRequestDTO from '../models/CreateSoftwareLookupRequestDTO';\r\nimport UnifiedDeviceConfigDTO from '../models/UnifiedDeviceConfigDTO';\r\nimport ECDeviceListResponseDTO from '../models/ECDeviceListResponseDTO';\r\nimport ECDeviceDTO from '../models/ECDeviceDTO';\r\nimport AzureIoTEdgeDeviceDTO from '../models/AzureIoTEdgeDeviceDTO';\r\nimport SoftwareLookupDTO from '../models/SoftwareLookupDTO';\r\nimport BinInfoDTO from '../models/BinInfoDTO';\r\n\r\nexport class AdminApiService extends BaseApi {\r\n\r\n // get: api/admin/getallecdevices?offset=${offset}\r\n public getAllECDevices(offset: number, signal?: AbortSignal): Promise {\r\n let url = `api/admin/getallecdevices`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (offset != null) {\r\n url += `${prefix}offset=${offset}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/admin/getecdevice?ecDeviceId=${encodeURIComponent(ecDeviceId)}\r\n public getECDevice(ecDeviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/admin/getecdevice`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (ecDeviceId != null) {\r\n url += `${prefix}ecDeviceId=${encodeURIComponent(ecDeviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/admin/addAzureIotEdgeDevice\r\n public addAzureIotEdgeDevice(request: AddAzureDeviceDTO, signal?: AbortSignal): Promise {\r\n let url = `api/admin/addAzureIotEdgeDevice`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(request, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/admin/getAzureIoTEdgeDevices\r\n public getAllAzureIoTEdgeDevices(signal?: AbortSignal): Promise {\r\n let url = `api/admin/getAzureIoTEdgeDevices`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/admin/getAzureIotEdgeDeviceProvisions?deviceId=${encodeURIComponent(deviceId)}\r\n public getAzureIotEdgeDeviceProvisions(deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/admin/getAzureIotEdgeDeviceProvisions`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/admin/generateassembly\r\n public generateAssembly(submittalDTO: BinAssemblySubmittalDTO, signal?: AbortSignal): Promise {\r\n let url = `api/admin/generateassembly`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(submittalDTO, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/admin/createsubmittalfromtext?deviceId=${encodeURIComponent(deviceId)}\r\n public createSubmittalFromText(text: string, deviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/admin/createsubmittalfromtext`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceId != null) {\r\n url += `${prefix}deviceId=${encodeURIComponent(deviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(text, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/admin/uploadbinassembly\r\n public uploadBinAssembly(binAssemblyDTO: BinAssemblyDTO, signal?: AbortSignal): Promise {\r\n let url = `api/admin/uploadbinassembly`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(binAssemblyDTO, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/admin/deletepcblookup\r\n public deletePCBLookup(pcb: PCBLookupDTO, signal?: AbortSignal): Promise {\r\n let url = `api/admin/deletepcblookup`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(pcb, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/admin/uploadpcblookup\r\n public uploadPCBLookup(pcbEntry: PCBLookupDTO, signal?: AbortSignal): Promise {\r\n let url = `api/admin/uploadpcblookup`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(pcbEntry, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/admin/uploadsoftwarefile?softwareLookupId=${softwareLookupId}\r\n public uploadSoftwareFile(softwareLookupId: number, softwareFile: File, signal?: AbortSignal): Promise {\r\n let url = `api/admin/uploadsoftwarefile`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (softwareLookupId != null) {\r\n url += `${prefix}softwareLookupId=${softwareLookupId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(softwareFile, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/admin/createsoftwarelookup\r\n public createSoftwareLookup(request: FormData, signal?: AbortSignal): Promise {\r\n let url = `api/admin/createsoftwarelookup`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(request, 'post', url, false, false, _signal);\r\n }\r\n\r\n // get: api/admin/getsoftwarelookups\r\n public getSoftwareLookups(signal?: AbortSignal): Promise {\r\n let url = `api/admin/getsoftwarelookups`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/admin/getpcblookups\r\n public getPCBLookups(signal?: AbortSignal): Promise {\r\n let url = `api/admin/getpcblookups`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/admin/getallpremierbins\r\n public getAllPremierBins(signal?: AbortSignal): Promise {\r\n let url = `api/admin/getallpremierbins`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/admin/getbinassembly?azureDeviceId=${encodeURIComponent(azureDeviceId)}\r\n public getBinAssemblySubmittal(azureDeviceId: string, signal?: AbortSignal): Promise {\r\n let url = `api/admin/getbinassembly`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (azureDeviceId != null) {\r\n url += `${prefix}azureDeviceId=${encodeURIComponent(azureDeviceId)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/admin/getbin?azureDeviceId=${encodeURIComponent(azureDeviceId)}&useDeviceTwin=${useDeviceTwin}\r\n public getBinByID(azureDeviceId: string, useDeviceTwin: boolean, signal?: AbortSignal): Promise {\r\n let url = `api/admin/getbin`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (azureDeviceId != null) {\r\n url += `${prefix}azureDeviceId=${encodeURIComponent(azureDeviceId)}`;\r\n prefix = '&';\r\n }\r\n if (useDeviceTwin != null) {\r\n url += `${prefix}useDeviceTwin=${useDeviceTwin}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/admin/getDeviceConnectionString?deviceName=${encodeURIComponent(deviceName)}\r\n public getDeviceConnectionString(deviceName: string, signal?: AbortSignal): Promise {\r\n let url = `api/admin/getDeviceConnectionString`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/admin/setbin?deviceName=${encodeURIComponent(deviceName)}&useDeviceTwin=${useDeviceTwin}\r\n public setBin(deviceName: string, useDeviceTwin: boolean, desiredProperties: UnifiedDeviceConfigDTO, signal?: AbortSignal): Promise {\r\n let url = `api/admin/setbin`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (deviceName != null) {\r\n url += `${prefix}deviceName=${encodeURIComponent(deviceName)}`;\r\n prefix = '&';\r\n }\r\n if (useDeviceTwin != null) {\r\n url += `${prefix}useDeviceTwin=${useDeviceTwin}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(desiredProperties, 'post', url, true, false, _signal);\r\n }\r\n}\r\nvar service = new AdminApiService();\r\nexport default service;\r\n","// Service TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nimport BaseApi from './BaseApi';\r\nimport UserCreationDTO from '../models/UserCreationDTO';\r\nimport UserDetailDTO from '../models/UserDetailDTO';\r\nimport UpdateExternalUserInfoDTO from '../models/UpdateExternalUserInfoDTO';\r\nimport UpdateShivversUserIpRequestDTO from '../models/UpdateShivversUserIpRequestDTO';\r\nimport UserLookupDTO from '../models/UserLookupDTO';\r\nimport UserDTO from '../models/UserDTO';\r\nimport ShivversUserDTO from '../models/ShivversUserDTO';\r\n\r\nexport class UserApiService extends BaseApi {\r\n\r\n // get: api/user/lookups\r\n public getUsersLookup(signal?: AbortSignal): Promise {\r\n let url = `api/user/lookups`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/user\r\n public getUsers(signal?: AbortSignal): Promise {\r\n let url = `api/user`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/user/${userId}\r\n public getUser(userId: number, signal?: AbortSignal): Promise {\r\n let url = `api/user/${userId}`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/user/search/${encodeURIComponent(q)}\r\n public searchUsers(q: string, signal?: AbortSignal): Promise {\r\n let url = `api/user/search/${encodeURIComponent(q)}`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/user/lookupWithSearch?search=${encodeURIComponent(search)}\r\n public getLookupWithSearch(search: string, signal?: AbortSignal): Promise {\r\n let url = `api/user/lookupWithSearch`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (search != null) {\r\n url += `${prefix}search=${encodeURIComponent(search)}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // post: api/user/addUser?role=${role}\r\n public addUser(userCreationDetails: UserCreationDTO, role: number, signal?: AbortSignal): Promise {\r\n let url = `api/user/addUser`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (role != null) {\r\n url += `${prefix}role=${role}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(userCreationDetails, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/user/updateUser\r\n public updateUser(user: UserDetailDTO, signal?: AbortSignal): Promise {\r\n let url = `api/user/updateUser`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(user, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/user/updateExternalUserInfo\r\n public updateExternalUserInfo(user: UpdateExternalUserInfoDTO, signal?: AbortSignal): Promise {\r\n let url = `api/user/updateExternalUserInfo`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(user, 'post', url, true, false, _signal);\r\n }\r\n\r\n // post: api/user/updateShivversIpAndData\r\n public updateShivversIpAndData(update: UpdateShivversUserIpRequestDTO, signal?: AbortSignal): Promise {\r\n let url = `api/user/updateShivversIpAndData`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(update, 'post', url, true, false, _signal);\r\n }\r\n\r\n // get: api/user/getExternalUserDetail?shivversUserId=${shivversUserId}\r\n public getExternalUserDetail(shivversUserId: number, signal?: AbortSignal): Promise {\r\n let url = `api/user/getExternalUserDetail`;\r\n let prefix = url.indexOf('?') > 0 ? '&' : '?';\r\n if (shivversUserId != null) {\r\n url += `${prefix}shivversUserId=${shivversUserId}`;\r\n prefix = '&';\r\n }\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n}\r\nvar service = new UserApiService();\r\nexport default service;\r\n","import * as React from 'react';\r\nimport { Input } from 'antd';\r\n\r\ninterface State {\r\n value: any;\r\n}\r\ninterface InputProps {\r\n style?: any;\r\n initialValue?: string | null;\r\n onChange(value: any): void;\r\n}\r\nexport default class PhoneInput extends React.Component {\r\n constructor(props: InputProps) {\r\n super(props);\r\n this.state = {\r\n value: this.props.initialValue\r\n };\r\n }\r\n onChange = (e: any) => {\r\n var { value } = e.target;\r\n var lastVal = (value as string)[(value as string).length - 1];\r\n var secLastVal = (value as string)[(value as string).length - 2];\r\n const reg = /^-?\\d*(\\.\\d*)?$/;\r\n var test = (lastVal === '-' && secLastVal === undefined);\r\n if ((!isNaN(value) && reg.test(value)) || value === '' || test ) {\r\n\r\n this.props.onChange(value);\r\n this.setState({ value: value });\r\n }\r\n }\r\n\r\n render() {\r\n\r\n return (\r\n \r\n );\r\n }\r\n}\r\n","// Service TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nimport BaseApi from './BaseApi';\r\nimport RoleDTO from '../models/RoleDTO';\r\n\r\nexport class RoleApiService extends BaseApi {\r\n\r\n // get: api/role/getRoles\r\n public getRoles(signal?: AbortSignal): Promise {\r\n let url = `api/role/getRoles`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n\r\n // get: api/role/getRoles/${userID}\r\n public getRolesByUser(userID: number, signal?: AbortSignal): Promise {\r\n let url = `api/role/getRoles/${userID}`;\r\n\r\n url = url.replace(/null|undefined/gi, '');\r\n const _signal = signal ?? new AbortController().signal;\r\n return this.SendRequest(null, 'get', url, true, false, _signal);\r\n }\r\n}\r\nvar service = new RoleApiService();\r\nexport default service;\r\n","import * as React from 'react';\r\nimport { Select } from 'antd';\r\nconst { Option } = Select;\r\n\r\ninterface State {\r\n value: any;\r\n}\r\ninterface InputProps {\r\n style?: any;\r\n initialValue?: string | null | undefined;\r\n onChange(value: any): void;\r\n}\r\nexport default class StatePicker extends React.Component {\r\n constructor(props: InputProps) {\r\n super(props);\r\n this.state = {\r\n value: this.props.initialValue ? this.props.initialValue : 'AL'\r\n };\r\n }\r\n onChange = (value: any) => {\r\n\r\n this.props.onChange(value);\r\n this.setState({ value: value });\r\n }\r\n\r\n render() {\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n }\r\n}\r\n","import { useQuery } from \"@tanstack/react-query\";\r\nimport ShivversService from \"src/api/ShivversService\";\r\nimport { ExtendedColumnType, useColumnExpansion } from \"../binOverview/BinCommander\";\r\nimport AdminUserSearchDTO from \"src/models/AdminUserSearchDTO\";\r\nimport { Col, Input, Row, Select, Space, Table, Typography } from \"antd\";\r\nimport React, { useMemo, useState } from \"react\";\r\nimport { useHistory, useLocation } from \"react-router\";\r\nimport { useLocale } from \"antd/es/locale\";\r\nimport Routes from \"src/consts/Routes\";\r\nimport { useDebounce, useDebouncedCallback } from 'use-debounce';\r\nimport { DEBOUNCE_TIME_MS } from \"./UserList\";\r\nimport dayjs from \"dayjs\";\r\nimport { NavLink } from \"react-router-dom\";\r\nimport { EditOutlined } from \"@ant-design/icons\";\r\nimport RoleUtil from \"src/utils/RoleUtil\";\r\n\r\n\r\nexport const PremierUserList = () => {\r\n const [nameSearch, setNameSearch] = useState(\"\");\r\n const nameSearchDelayedValue = useDebouncedCallback((value) => {\r\n setNameSearch(value);\r\n }, DEBOUNCE_TIME_MS);\r\n\r\n const [securityFilter, setSecurityFilter] = useState([]);\r\n\r\n const query = useQuery({\r\n queryKey: [{purpose: \"userSearch\", scope: \"userEdit\"}] as const,\r\n queryFn: async (q) => {\r\n return await ShivversService.searchUsers(undefined, undefined, q.signal);\r\n }\r\n });\r\n const history = useHistory();\r\n const location = useLocation();\r\n\r\n const columns: Array> = useMemo(() => [\r\n {\r\n dataIndex: \"internalId\",\r\n hidden: true,\r\n },\r\n {\r\n dataIndex: \"externalId\",\r\n hidden: true,\r\n },\r\n {\r\n dataIndex: \"security\",\r\n title: \"Security\",\r\n hidden: true,\r\n },\r\n {\r\n title: \"Account\",\r\n dataIndex: \"account\",\r\n sortType: 'string',\r\n },\r\n {\r\n title: \"Last_Name\",\r\n dataIndex: \"lastName\",\r\n defaultSortOrder: \"ascend\",\r\n sortType: 'string',\r\n filterSearch: true,\r\n render(value, record, index) {\r\n if (record.security !== 1) {\r\n return {value}\r\n }\r\n return
\r\n {value}\r\n
\r\n },\r\n },\r\n {\r\n title: \"First_Name\",\r\n dataIndex: \"firstName\",\r\n sortType: 'string',\r\n },\r\n {\r\n title: \"Address\",\r\n dataIndex: \"address\",\r\n sortType: 'string',\r\n },\r\n {\r\n title: \"City\",\r\n dataIndex: \"city\",\r\n sortType: 'string',\r\n },\r\n {\r\n title: 'State',\r\n dataIndex: 'state',\r\n sortType: 'string',\r\n },\r\n {\r\n title: 'Dealer',\r\n dataIndex: 'dealer',\r\n sortType: 'string',\r\n },\r\n {\r\n title: \"DSM\",\r\n dataIndex: \"regionId\",\r\n sortType: \"string\",\r\n render(value, record, index) {\r\n if (value === 1100) {\r\n return \"1100 Glen Muench\";\r\n }\r\n else if (value === 1700) {\r\n return \"1700 Mark Sexton\";\r\n }\r\n else if (value === 2100) {\r\n return \"2100 Kyle Canady\";\r\n }\r\n else if (value === 2900) {\r\n return \"2900 Scott Graham\";\r\n }\r\n else if (value === 3000) {\r\n return \"3000 Tom Barron\";\r\n }\r\n else if (value === 3300) {\r\n return \"3300 BJ Arbuckle\";\r\n }\r\n else {\r\n return value;\r\n }\r\n },\r\n },\r\n {\r\n title: 'Premier',\r\n dataIndex: 'premier',\r\n sortType: 'string',\r\n },\r\n {\r\n title: 'Activation_Date',\r\n dataIndex: 'activationDateIso8601',\r\n render: (value, record, index) => {\r\n if (!value) {\r\n return value;\r\n }\r\n const parsed = dayjs(value);\r\n if (!parsed.isValid()) {\r\n return value;\r\n }\r\n return dayjs(value).tz(\"America/Chicago\", false)?.format('MMM D YYYY');\r\n },\r\n sorter: (a, b, sortOrder) => {\r\n // put nulls at end of sorts\r\n // https://github.com/TanStack/table/discussions/2371#discussioncomment-210260\r\n if (!a.activationDateIso8601 && !b.activationDateIso8601) {\r\n return 0;\r\n }\r\n\r\n if (!a.activationDateIso8601) {\r\n return sortOrder === \"descend\" ? -1 : 1;\r\n }\r\n\r\n if (!b.activationDateIso8601) {\r\n return sortOrder === \"descend\" ? 1 : -1;\r\n }\r\n return dayjs(a.activationDateIso8601).diff(b.activationDateIso8601);\r\n }\r\n },\r\n {\r\n title: \"Details\",\r\n hidden: !RoleUtil.currentUserIsAdmin(),\r\n render(value, record, index) {\r\n return View / Edit \r\n },\r\n },\r\n\r\n ], [query.data]);\r\n\r\n\r\n\r\n\r\n\r\n\r\n let transformed = useMemo(() => useColumnExpansion({ columns: columns, datasource: query.data ?? [] }), [query.data, columns]);\r\n const updateFilters = () => {\r\n if (nameSearch === \"\" && securityFilter.length === 0) {\r\n return query.data ?? [];\r\n }\r\n\r\n const filteredTable = query.data\r\n ?.filter((record) => {\r\n if (nameSearch == \"\") {\r\n return true;\r\n }\r\n\r\n return record.lastName?.toUpperCase()?.startsWith(nameSearch.toUpperCase())\r\n || record.firstName?.toUpperCase()?.startsWith(nameSearch.toUpperCase())\r\n || record.account === (nameSearch)}\r\n )?.filter((record) => {\r\n if (securityFilter.length === 0) {\r\n return true;\r\n }\r\n return securityFilter.includes(record.security!);\r\n }\r\n )\r\n ?? [];\r\n return filteredTable;\r\n };\r\n\r\n const finalTable = useMemo(() => updateFilters(), [nameSearch, securityFilter, query.data]);\r\n\r\n return <>\r\n \r\n \r\n \r\n nameSearchDelayedValue(evt.target.value)} placeholder='Search by Name or Account'>\r\n {\r\n const parsed = Number.parseInt(evt.target.value);\r\n if (!Number.isInteger(parsed)) {\r\n setSecurityFilter([]);\r\n return;\r\n }\r\n setSecurityFilter([parsed])}\r\n } placeholder='Search by Security'>\r\n \r\n \r\n \r\n {\r\n return {\r\n // onClick: (d) => {\r\n // if (data.security !== 1) {\r\n // return;\r\n // }\r\n // const binStatsRoute = `${Routes.BIN_STATS_SHIVVERS}/${data['internalId']}`;\r\n\r\n // history.push(binStatsRoute);\r\n // }\r\n }\r\n }}\r\n size=\"small\" rowKey=\"externalId\" pagination={{ defaultPageSize: 200, size: \"default\" }} loading={query.isLoading} dataSource={finalTable ?? []} columns={transformed}\r\n\r\n />\r\n \r\n \r\n\r\n ;\r\n}","import * as React from 'react';\r\nimport UserApiService from 'src/api/UserApiService';\r\n\r\nimport { Layout, notification, Button, Row, Col, Form, Input, Select, InputNumber, Table, FormInstance, Tabs, TabsProps, Card, Space } from 'antd';\r\nimport { PageHeader } from '@ant-design/pro-layout';\r\nimport UserDTO from 'src/models/UserDTO';\r\nimport {\r\n EditOutlined,\r\n UserAddOutlined\r\n} from '@ant-design/icons';\r\nimport Modal from 'antd/lib/modal/Modal';\r\nimport { RequiredRule } from 'src/consts/FormConstants';\r\nimport PhoneInput from '../shared/PhoneInput';\r\nimport RoleUtil from 'src/utils/RoleUtil';\r\nimport Role from 'src/consts/Role';\r\nimport RoleApiService from 'src/api/RoleApiService';\r\nimport RoleDTO from 'src/models/RoleDTO';\r\nimport StatePicker from '../shared/StatePicker';\r\nimport EnterpriseApiService from 'src/api/EnterpriseApiService';\r\nimport GrowerDTO from 'src/models/GrowerDTO';\r\n\r\nimport Routes, { UserSource } from 'src/consts/Routes';\r\nimport { NavLink } from 'react-router-dom';\r\nimport UserCreationDTO from 'src/models/UserCreationDTO';\r\nimport { ColumnsType } from 'antd/lib/table';\r\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\r\nimport { RuleObject } from 'antd/es/form/index';\r\nimport FormItem from 'antd/es/form/FormItem/index';\r\nimport { StringParam, useQueryParam } from 'use-query-params';\r\nimport { useDebounce, useDebouncedCallback } from 'use-debounce';\r\nimport Users from './Users';\r\nimport { PremierUserList } from './PremierUserList';\r\nimport HistoryUtil from 'src/utils/HistoryUtil';\r\nimport { formatBool } from '../dashboard/BinStatusPage/HeaterControls';\r\nconst { Option } = Select;\r\nconst { Content } = Layout;\r\n\r\nexport const DEBOUNCE_TIME_MS = 250;\r\n\r\n// interface State {\r\n// loading: boolean;\r\n// users: UserDTO[];\r\n// roles: RoleDTO[];\r\n// growers: [number, string][];\r\n// visible: boolean;\r\n// state: string | null;\r\n// }\r\ninterface DataTable {\r\n key: number;\r\n userName: string;\r\n firstName: string;\r\n lastName: string;\r\n id: number;\r\n userDetails: number;\r\n isActive: boolean;\r\n}\r\n\r\n// const filterData = (data: any) => (formatter: any) => data.map( (item: any) => ({\r\n// text: formatter(item),\r\n// value: formatter(item)\r\n// }));\r\n\r\nconst generateUserColumns = (userRows: DataTable[]): ColumnsType => {\r\n\r\n const columns: ColumnsType = [\r\n {\r\n title: \"Id\",\r\n dataIndex: \"id\",\r\n key: \"id\",\r\n sorter: (a, b) => a.id - b.id,\r\n },\r\n {\r\n title: \"First Name\",\r\n dataIndex: \"firstName\",\r\n key: \"firstName\",\r\n sorter: (a, b) => a.firstName?.localeCompare(b.firstName),\r\n },\r\n {\r\n title: \"Last Name\",\r\n dataIndex: \"lastName\",\r\n key: \"lastName\",\r\n defaultSortOrder: 'ascend',\r\n sorter: (a, b) => a.lastName?.localeCompare(b.lastName),\r\n // filters: filterData(userRows)((row: DataTable) => row.lastName),\r\n // filterSearch: true,\r\n // onFilter: (value: string, record) => record.lastName.toLocaleLowerCase(navigator.language).includes(value.toLocaleLowerCase(navigator.language)),\r\n },\r\n {\r\n title: \"User Name\",\r\n dataIndex: \"userName\",\r\n key: \"userName\",\r\n sorter: (a, b) => a.userName?.localeCompare(b.userName),\r\n },\r\n {\r\n title: \"Active\",\r\n dataIndex: \"isActive\",\r\n key: \"isActive\",\r\n filters: [\r\n { text: \"Yes\", value: true },\r\n { text: \"No\", value: false },\r\n ],\r\n onFilter: (value, record) => record.isActive === value,\r\n render(value, record, index) {\r\n return formatBool(value, {true: \"Yes\", false: \"No\"});\r\n },\r\n sorter: (a, b) => a.isActive?.toString()?.localeCompare(b.isActive?.toString()),\r\n },\r\n {\r\n title: \"User Details\",\r\n dataIndex: \"userDetails\",\r\n key: \"userDetails\",\r\n render: (id) => (\r\n View / Edit \r\n )\r\n }\r\n ];\r\n return columns;\r\n}\r\n\r\nexport const InternalUsersList = (props: { openModal: boolean, onModalOpenChange: (open: boolean) => void }) => {\r\n\r\n // const [dataTable, setDataTable] = useState();\r\n const formRef = useRef(null);\r\n\r\n const [users, setUsers] = useState([]);\r\n const [roles, setRoles] = useState([]);\r\n const [growers, setGrowers] = useState<[number, string][]>([]);\r\n const [loading, setLoading] = useState(true);\r\n\r\n const [USState, setUSState] = useState(null);\r\n\r\n const [nameSearch, setNameSearch] = useState(\"\");\r\n const nameSearchDelayedValue = useDebouncedCallback((value) => {\r\n setNameSearch(value);\r\n }, DEBOUNCE_TIME_MS);\r\n\r\n const fetchData = useCallback(() => {\r\n setLoading(true);\r\n\r\n Promise.all([\r\n UserApiService.getUsers(),\r\n RoleApiService.getRoles(),\r\n EnterpriseApiService.getGrowerIDs()\r\n ]).then(([users, roles, growerIDs]) => {\r\n let GrowerNameIDPairs: [number, string][] = growerIDs.map((grower) => [grower.growerID, grower.growerName ? grower.growerName : 'error: no name']);\r\n // filter out external users (username not set)\r\n setUsers(users.filter(u => u.username != null));\r\n setRoles(roles);\r\n setGrowers(GrowerNameIDPairs);\r\n setLoading(false);\r\n\r\n }).catch(error => {\r\n setLoading(false);\r\n notification.error({\r\n message: error.message,\r\n description: error.description\r\n });\r\n });\r\n }, [setLoading, setUsers, setGrowers, setRoles]);\r\n\r\n useEffect(() => {\r\n fetchData();\r\n }, []);\r\n\r\n const checkFormAndSubmit = useCallback(() => {\r\n formRef.current!.validateFields().then(values => {\r\n let growers = values.growers.map((growerID: number) => { return { growerID: growerID } as GrowerDTO; });\r\n let userToAdd = {\r\n username: values.username,\r\n password: values.password,\r\n firstName: values.firstName,\r\n lastName: values.lastName,\r\n email: values.email,\r\n phone: values.phone,\r\n addressLine1: values.addressLine1,\r\n addressLine2: values.addressLine2,\r\n city: values.city,\r\n state: values.state,\r\n zip: values.zip,\r\n isActive: true,\r\n growers: growers\r\n } as UserCreationDTO;\r\n UserApiService.addUser(userToAdd, values.role).then((res) => {\r\n props.onModalOpenChange(false);\r\n fetchData();\r\n notification.success({\r\n message: 'New User Added Succesfully'\r\n });\r\n\r\n }).catch(error => {\r\n // this.setState({ visible: false });\r\n notification.error({\r\n message: error.message,\r\n description: error.description\r\n });\r\n });\r\n });\r\n }, [fetchData, props.openModal, formRef]);\r\n\r\n const fillTable = () => {\r\n const newDataTable: DataTable[] = [];\r\n users.forEach((user, i) => {\r\n var u = {\r\n key: i,\r\n userName: user.username || '',\r\n firstName: user.firstName || '',\r\n lastName: user.lastName || '',\r\n id: user.id!,\r\n userDetails: user.id!,\r\n isActive: user.isActive,\r\n };\r\n newDataTable.push(u);\r\n });\r\n return newDataTable;\r\n // setDataTable(newDataTable);\r\n };\r\n\r\n\r\n\r\n const isAdmin = RoleUtil.currentUserIsAdmin();\r\n const layout = {\r\n labelCol: { span: 8 },\r\n wrapperCol: { span: 16 },\r\n };\r\n\r\n const userDataRows: DataTable[] = useMemo(() => fillTable(), [users]);\r\n const columns = useMemo(() => generateUserColumns(userDataRows), [userDataRows]);\r\n\r\n const updateFilters = () => {\r\n if (nameSearch === \"\") {\r\n return userDataRows;\r\n }\r\n\r\n const filteredTable = userDataRows\r\n .filter((record) => record.lastName.toUpperCase().includes(nameSearch.toUpperCase())\r\n || record.firstName.toUpperCase().includes(nameSearch.toUpperCase()\r\n )\r\n )\r\n return filteredTable;\r\n };\r\n\r\n const finalTable = useMemo(() => updateFilters(), [nameSearch, userDataRows]);\r\n\r\n return <>\r\n {\r\n props.onModalOpenChange(false);\r\n }}\r\n onOk={checkFormAndSubmit}\r\n open={props.openModal}\r\n >\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {\r\n formRef.current?.setFieldsValue({ phone: val });\r\n }} />\r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n ({\r\n validator(_: RuleObject, value: string) {\r\n if (!value || getFieldValue('password') === value) {\r\n return Promise.resolve();\r\n }\r\n return Promise.reject(new Error('Passwords do not match'));\r\n },\r\n }),\r\n ]}\r\n >\r\n \r\n \r\n\r\n \r\n \r\n {\r\n growers?.map((grower, index) => (\r\n \r\n ))\r\n }\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n setUSState(value)} />\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n nameSearchDelayedValue(evt.target.value)} placeholder='Search by Name'>\r\n \r\n \r\n \r\n
\r\n \r\n \r\n ;\r\n\r\n}\r\n\r\nconst UserList = () => {\r\n const isAdmin = RoleUtil.currentUserIsAdmin();\r\n const canViewUserList = RoleUtil.CanViewBinFleet();\r\n const [showAddUserModal, setShowAddUserModal] = useState(false);\r\n\r\n const [userSource, setUserSource] = useQueryParam('userSource', { ...StringParam, default: UserSource.MyShivvers });\r\n\r\n const items: TabsProps['items'] = [\r\n {\r\n key: UserSource.MyShivvers,\r\n label: \"Shivvers\",\r\n children: \r\n }];\r\n if (isAdmin) {\r\n items.push({\r\n key: UserSource.Internal,\r\n label: 'STIG',\r\n children: setShowAddUserModal(open)} />,\r\n });\r\n }\r\n\r\n return (\r\n \r\n {canViewUserList ? (<>\r\n {\r\n console.log(\"card tab value:\", key);\r\n setUserSource(key);\r\n }}\r\n defaultActiveTabKey=\"1\"\r\n extra={[<>\r\n } onClick={() => {\r\n if (userSource == UserSource.MyShivvers) {\r\n HistoryUtil.push(Routes.SHIVVERS_USER_ADD);\r\n }\r\n setShowAddUserModal(true);\r\n }}>Add User\r\n ]}>\r\n\r\n\r\n \r\n \r\n ) : ()}\r\n\r\n \r\n );\r\n\r\n\r\n}\r\n\r\nexport default UserList;\r\n","import { useQueryClient, useQueries, useQuery } from \"@tanstack/react-query\";\r\nimport {Button, Space, Table, Input } from \"antd\";\r\nimport dayjs from \"dayjs\";\r\nimport React, { useRef, useCallback, useState, useMemo } from \"react\";\r\nimport { CSVLink } from \"react-csv\";\r\nimport Link from 'antd/es/typography/Link';\r\n\r\nimport Routes from \"src/consts/Routes\";\r\nimport { ExtendedColumnType, useColumnExpansion, binDBKeys } from \"./BinCommander\";\r\nimport ShivversService from \"src/api/ShivversService\";\r\nimport ReadingDTO from \"src/models/ReadingDTO\";\r\nimport BinInfoDTO from \"src/models/BinInfoDTO\";\r\nimport AdminApiService from \"src/api/AdminApiService\";\r\nimport { CheckFailedIcon, CheckPassedIcon } from \"../features/WeatherMonitorAntdOnly\";\r\nimport { DEBOUNCE_TIME_MS } from \"../users/UserList\";\r\nimport { useDebouncedCallback } from \"use-debounce\";\r\n\r\nexport const useAllPremierBinsQuery = (options: { enabled?: boolean, selector?: (data: BinInfoDTO[]) => BinInfoDTO[] } = {}) => {\r\n const query = useQuery({\r\n queryKey: [\"allshivversBins\", \"bininfo\"],\r\n queryFn: async (q) => await AdminApiService.getAllPremierBins(q.signal),\r\n refetchOnWindowFocus: false,\r\n enabled: options.enabled,\r\n select: (data) => {\r\n if (options?.selector == null) {\r\n return data;\r\n }\r\n return options.selector(data);\r\n },\r\n onError: (err) => {\r\n console.error(err);\r\n },\r\n },\r\n )\r\n return query;\r\n}\r\n\r\nexport const PremierSpreadsheet = () => {\r\n const [nameSearch, setNameSearch] = useState(\"\");\r\n const nameSearchDelayedValue = useDebouncedCallback((value) => {\r\n setNameSearch(value);\r\n }, DEBOUNCE_TIME_MS);\r\n\r\n const queryClient = useQueryClient();\r\n const binsQuery = useAllPremierBinsQuery();\r\n\r\n // https://stackoverflow.com/a/68066447\r\n const csvLinkRef = useRef<\r\n CSVLink & HTMLAnchorElement & { link: HTMLAnchorElement }\r\n >(null); // setup the ref that we'll use for the CSVLink click once we've updated the filename\r\n\r\n const latestreadingsQuery = useQueries({\r\n queries: binsQuery.data?.filter(binInfo => !(binInfo.linkIpAddress == null)).map(binInfo => {\r\n return {\r\n queryKey: [...binDBKeys.shivversBin(binInfo.linkIpAddress!), { last: 1 }],\r\n queryFn: async (q: any) => {\r\n const result = await ShivversService.lastReadings(binInfo.linkIpAddress!, 1, q.signal);\r\n // share to bin\r\n if (result != null) {\r\n queryClient.setQueryData([...binDBKeys.shivversBin(binInfo.linkIpAddress!), { last: 1 }], result);\r\n }\r\n return result?.[0];\r\n }\r\n };\r\n }) ?? []\r\n });\r\n\r\n const pending = latestreadingsQuery.reduce((acc, obj) => { return acc + Number(obj.isLoading) }, 0);\r\n\r\n const columns: Array> = [\r\n {\r\n dataIndex: \"binName\",\r\n title: \"Bin Name\",\r\n fixed: \"left\",\r\n sortType: 'string',\r\n render(value, record, index) {\r\n return
\r\n {value}\r\n
\r\n },\r\n },\r\n {\r\n dataIndex: \"dateinUtc\",\r\n title: \"Last Reading\",\r\n width: \"160px\",\r\n sorter: (rowA, rowB, sortOrder) => {\r\n // put nulls at end of sorts\r\n // https://github.com/TanStack/table/discussions/2371#discussioncomment-210260\r\n if (!rowA.dateinUtc && !rowB.dateinUtc) {\r\n return 0;\r\n }\r\n\r\n if (!rowA.dateinUtc) {\r\n return sortOrder === \"descend\" ? -1 : 1;\r\n }\r\n\r\n if (!rowB.dateinUtc) {\r\n return sortOrder === \"descend\" ? 1 : -1;\r\n }\r\n return rowA.dateinUtc?.localeCompare(rowB.dateinUtc);\r\n },\r\n render(value, record, index) {\r\n if (value == null) {\r\n return null;\r\n }\r\n var date = dayjs(value)\r\n if (!date.isValid()) {\r\n return null;\r\n }\r\n return {date.format(\"hh:mm A MM/DD/YYYY\")};\r\n },\r\n sortType: 'date',\r\n defaultSortOrder: \"descend\",\r\n },\r\n {\r\n dataIndex: \"externalId\",\r\n hidden: true,\r\n },\r\n {\r\n title: \"Account\",\r\n dataIndex: 'account',\r\n sortType: \"string\",\r\n },\r\n {\r\n title: \"Moisture\",\r\n dataIndex: \"moistread\",\r\n align: 'right',\r\n },\r\n {\r\n title: \"Avg. Moisture\",\r\n dataIndex: \"moistavg\",\r\n align: 'right',\r\n },\r\n {\r\n title: \"Grain Temp\",\r\n dataIndex: \"graintemp\",\r\n align: 'right',\r\n },\r\n {\r\n title: \"Plenum Temp\",\r\n dataIndex: 'plenumtemp',\r\n align: 'right',\r\n },\r\n {\r\n title: \"Target Temp\",\r\n dataIndex: 'targettemp',\r\n align: 'right',\r\n },\r\n {\r\n title: \"Machine\",\r\n dataIndex: \"machine\",\r\n align: 'center',\r\n sortType: \"string\",\r\n render: (value, record, index) => {\r\n return \r\n {record?.machine}\r\n {record?.machine != null ? record?.machine?.includes(\"ON\") ? : : null}\r\n ;\r\n },\r\n filters: [\r\n {\r\n text: \"ON\",\r\n value: \"ON\"\r\n },\r\n {\r\n text: \"OFF\",\r\n value: \"OFF\"\r\n }\r\n ],\r\n onFilter: (value, record) => value.toString().trim() === record.machine?.trim(),\r\n },\r\n {\r\n title: \"Static Pressure\",\r\n dataIndex: \"pressure\",\r\n align: 'right',\r\n },\r\n ];\r\n\r\n const dataSources = [...(binsQuery?.data ?? [])].map((bininfo, index) => {\r\n\r\n const reading = latestreadingsQuery.find(latest => latest.data?.ip == bininfo?.linkIpAddress)\r\n return {\r\n ...(reading?.data ?? {}),\r\n externalId: bininfo?.externalId,\r\n account: bininfo?.linkIpAddress,\r\n binName: bininfo?.name,\r\n loading: reading?.isLoading,\r\n binLink: `${window.location.origin}${Routes.BIN_STATS_SHIVVERS}/${bininfo?.externalId}`,\r\n }\r\n });\r\n\r\n let transformed = useColumnExpansion({ columns: columns, datasource: dataSources });\r\n\r\n const generateCSVFilename = useCallback(() => {\r\n\r\n return `PremierOverview ${dayjs().format(\"YYYY-MM-DD HH_mm_ss\")}.csv`;\r\n }, []);\r\n\r\n const updateFilters = () => {\r\n if (nameSearch === \"\") {\r\n return dataSources ?? [];\r\n }\r\n\r\n const filteredTable = dataSources\r\n ?.filter((record) => record.binName?.toUpperCase()?.startsWith(nameSearch.toUpperCase())\r\n || record.binName?.split(\" \")\r\n ?.some(part => part?.toUpperCase()?.startsWith(nameSearch.toUpperCase())\r\n )\r\n || record.account === (nameSearch)\r\n )\r\n ?? [];\r\n return filteredTable;\r\n };\r\n\r\n const finalTable = useMemo(() => updateFilters(), [nameSearch, dataSources]);\r\n\r\n return (<>\r\n \r\n
Bins still loading: {pending}
\r\n\r\n nameSearchDelayedValue(evt.target.value)} placeholder='Search by Name or Account'>\r\n\r\n \r\n
\r\n
\r\n
\r\n );\r\n}","import BinInfoDTO from 'src/models/BinInfoDTO';\r\nimport * as React from 'react';\r\nimport BinApiService from 'src/api/BinApiService';\r\nimport BinDetails, { deviceQueryKeys, getUserTimezoneOffset, isAnyHeaterOn } from '../../components/BinDetails';\r\nimport { CameraOutlined, DownloadOutlined, FileTextOutlined, RedoOutlined, WarningFilled } from '@ant-design/icons';\r\nimport { Layout, Row, Spin, Col, Button, Radio, Space, Table, Modal, Typography, message, TabsProps, Tabs, Input } from 'antd';\r\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\r\nimport { useMutation, useQueries, useQuery, useQueryClient } from '@tanstack/react-query';\r\nimport { PageHeader } from '@ant-design/pro-layout';\r\nimport { ColumnType } from 'antd/es/table';\r\nimport PingAndBinDTO from 'src/models/PingAndBinDTO';\r\nimport Link from 'antd/es/typography/Link';\r\nimport Routes from 'src/consts/Routes';\r\nimport dayjs from 'dayjs';\r\nimport { get } from 'lodash-es';\r\nimport { SortOrder } from 'antd/es/table/interface';\r\nimport { downloadAsciiBinState } from '../dashboard/BinStatusPage/BinVisualThree';\r\nimport BinDTO from 'src/models/BinDTO';\r\nimport { CameraImage } from '../dashboard/Camera/CameraImage';\r\nimport { RefreshButton, getCurrentModeDescription } from '../dashboard/BinStatusPage/BinStatsPage';\r\nimport { formatNumber } from '../dashboard/BinStatusPage/HeaterControls';\r\nimport { SignalRContext } from 'src/app/App';\r\nimport { useBinStateFromAzure } from 'src/queries/useBinStateFromAzure';\r\nimport { CSVLink } from \"react-csv\";\r\nimport { ApiError } from 'src/api/ApiResultHandler';\r\nimport { useQueryParam, StringParam} from 'use-query-params';\r\nimport FanOffReason from 'src/consts/FanOffReason';\r\nimport { ExtraOfflineInfoContext } from '../dashboard/BinOfflineIndicator';\r\nimport { PremierSpreadsheet } from './PremierSpreadsheet';\r\nimport { useDebouncedCallback } from 'use-debounce';\r\nimport { DEBOUNCE_TIME_MS } from '../users/UserList';\r\nimport RoleUtil from 'src/utils/RoleUtil';\r\nimport AutomationType from 'src/consts/AutomationType';\r\n\r\n// interface State {\r\n// bins?: BinInfoDTO[];\r\n// loading: boolean;\r\n// refreshDelay: boolean;\r\n// }\r\n\r\nexport const binDBKeys = {\r\n all: ['binDB'] as const,\r\n allBin: () => [...binDBKeys.all, \"Bins\"] as const,\r\n bin: (id: number) => [...binDBKeys.all, id] as const,\r\n binInfo: (binId: number) => [...binDBKeys.bin(binId), \"binInfo\"] as const,\r\n dristackmoduletwin: (binId: number) => [...binDBKeys.bin(binId), \"dristackmodule\"] as const,\r\n livestream: {\r\n all: (binId: number) => [...binDBKeys.bin(binId), \"livestream\"] as const,\r\n camera: (options: {binId: number, cameraNumber: number}) => [...binDBKeys.livestream.all(options.binId), {cameraNumber: options.cameraNumber}] as const,\r\n },\r\n calculateGrainHeightName: (binId: number) => [...binDBKeys.bin(binId), \"calculateGrainHeight\"] as const,\r\n calculateGrainHeight: (binId: number, params: {eaveHeightFt: number | null, peakHeightFt: number | null}) => \r\n [...binDBKeys.calculateGrainHeightName(binId), {eaveHeightFt: params.eaveHeightFt, peakHeightFt: params.peakHeightFt}] as const,\r\n shivversBin: (ip: string) => [...binDBKeys.all, ip] as const,\r\n shivversBinInfo: (externalId: number) => [...binDBKeys.all, \"shivvers\", externalId, \"bininfo\"] as const,\r\n moistureDisplay: (ip: string) => [...binDBKeys.shivversBin(ip), \"moistureDisplay\"] as const,\r\n alertDataShivvers: (ip: string) => [...binDBKeys.shivversBin(ip), \"alertDataShivvers\"] as const,\r\n shivversSettingsHistory: (ip: string) => [...binDBKeys.shivversBin(ip), \"dryerSettings\"] as const,\r\n}\r\n\r\n\r\nexport const useAllBinsQuery = (enabled: boolean = true, options: {selector?: (data: BinInfoDTO[]) => BinInfoDTO[]} = {}) => {\r\n const query = useQuery(binDBKeys.allBin(), (q) => BinApiService.getAllBins(q.signal), {\r\n enabled: enabled,\r\n select: (data) => {\r\n if (options?.selector == null) {\r\n return data;\r\n }\r\n return options.selector(data);\r\n },\r\n onError: (err) => {\r\n console.error(err);\r\n },\r\n },\r\n )\r\n return query;\r\n}\r\n\r\nenum ViewType {\r\n Cards = \"Cards\",\r\n CSV = \"CSV\",\r\n}\r\n\r\nexport const ProductTypeQueryParamName = 'product';\r\n\r\nexport const BinCommander = () => {\r\n // const initialState: State = { loading: false, refreshDelay: false };\r\n // const [state, setState] = useState(initialState);\r\n const queryClient = useQueryClient();\r\n\r\n const binQuery = useAllBinsQuery();\r\n\r\n const setLoading = useCallback((value: boolean, i: number) => {\r\n // todo\r\n }, []);\r\n\r\n const [viewType, setViewType] = useQueryParam('viewType', {...StringParam, default: ViewType.CSV});\r\n const [productType, setProductType] = useQueryParam(ProductTypeQueryParamName, {...StringParam, default: 'shivvers'});\r\n const [showExtraOfflineInfoForBins, setShowExtraOfflineInfoForBins] = useState(false);\r\n\r\n const items: TabsProps['items'] = [\r\n {\r\n key: 'shivvers',\r\n label: 'Shivvers',\r\n children: ,\r\n }];\r\n if (RoleUtil.currentUserIsAdmin()) {\r\n items.push(\r\n {\r\n key: 'autobin',\r\n label: 'AutoBin',\r\n children: ,\r\n },\r\n {\r\n key: 'dristack',\r\n label: 'DriStack',\r\n children: ,\r\n }\r\n );\r\n\r\n //feature flag to hide it from admins in prod. turned to true when doing work\r\n if(false){\r\n items?.push({\r\n key: 'premierplus',\r\n label: 'Premier+',\r\n children: ,\r\n })\r\n }\r\n \r\n }\r\n\r\n const getBins = (binLength: number, bins: BinInfoDTO[] | null | undefined) => {\r\n if (bins) {\r\n return (\r\n bins.map((bin: BinInfoDTO, i: number) => (\r\n \r\n )));\r\n } else {\r\n return <>;\r\n }\r\n };\r\n\r\n return (\r\n \r\n\r\n \r\n setShowExtraOfflineInfoForBins(!showExtraOfflineInfoForBins) }}>\r\n\r\n setProductType(activeKey)} activeKey={productType!} items={items} tabBarExtraContent={{\r\n right: \r\n \r\n \r\n \r\n }} />\r\n {/* {viewType === ViewType.Cards && \r\n {\r\n // this.binArr\r\n getBins(binQuery.data?.length!, binQuery.data)\r\n }\r\n \r\n } */}\r\n {/* {viewType === ViewType.CSV && ['autobin', 'dristack'].includes(binSource!) && }\r\n {viewType === ViewType.CSV && binSource === \"premier\" && } */}\r\n \r\n\r\n \r\n \r\n );\r\n\r\n};\r\n\r\n\r\nconst interestStyle = {\r\n backgroundColor: \"#EEFF33 \",\r\n color:\"black\",\r\n fontWeight: \"bold\",\r\n};\r\nconst warningStyle = {\r\n backgroundColor: \"#FFB6C1\",\r\n color: \"black\",\r\n fontWeight: \"bold\",\r\n};\r\nconst okStyle = {\r\n backgroundColor: \"#FFFFFF\",\r\n color: \"green\",\r\n fontWeight: \"bold\",\r\n}\r\n\r\ntype DataIndexRequired = NonNullable['dataIndex']>;\r\n\r\nexport const sortBoolean = (params: { a: P, b: P, sortOrder: SortOrder, dataIndex: string | string[] }) => {\r\n\r\n const sorter = (a: P, b: P, sortOrder: SortOrder) => {\r\n\r\n let aValue = get(a, params.dataIndex);\r\n let bValue = get(b, params.dataIndex);\r\n if (aValue != null && bValue != null) {\r\n if (aValue < bValue) return -1;\r\n if (bValue < aValue) return 1;\r\n }\r\n else if (aValue == null) {\r\n // That means a is null , so b will come first.\r\n\r\n if (sortOrder === \"descend\") {\r\n return -1;\r\n }\r\n else {\r\n return 1;\r\n }\r\n }\r\n else if (bValue == null) {\r\n // this means b is null, a will be first\r\n if (sortOrder === \"descend\") {\r\n return 1;\r\n }\r\n else {\r\n return -1;\r\n }\r\n }\r\n return 0;\r\n };\r\n\r\n return sorter(params.a, params.b, params.sortOrder);\r\n}\r\n\r\nexport const sortNumberic = (params: { a: P, b: P, sortOrder: SortOrder, dataIndex: string | string[] }) => {\r\n\r\n const sorter = (a: P, b: P, sortOrder: SortOrder) => {\r\n\r\n let aValue = get(a, params.dataIndex);\r\n let bValue = get(b, params.dataIndex);\r\n if (aValue != null && bValue != null) {\r\n if (aValue < bValue) return -1;\r\n if (bValue < aValue) return 1;\r\n }\r\n else if (aValue == null) {\r\n // That means a is null , so b will come first.\r\n\r\n if (sortOrder === \"descend\") {\r\n return -1;\r\n }\r\n else {\r\n return 1;\r\n }\r\n }\r\n else if (bValue == null) {\r\n // this means b is null, a will be first\r\n if (sortOrder === \"descend\") {\r\n return 1;\r\n }\r\n else {\r\n return -1;\r\n }\r\n }\r\n return 0;\r\n };\r\n\r\n return sorter(params.a, params.b, params.sortOrder);\r\n}\r\n\r\n\r\nexport const sortISODate = (params: { a: P, b: P, sortOrder: SortOrder, dataIndex: string | string[] }) => {\r\n\r\n // works for ISO8601 dates\r\n return sortString({ a: params.a, b: params.b, sortOrder: params.sortOrder, dataIndex: params.dataIndex });\r\n}\r\n\r\nexport const sortString = (params: { a: P, b: P, sortOrder: SortOrder, dataIndex: string | string[] }) => {\r\n\r\n const sorter = (a: P, b: P, sortOrder: SortOrder) => {\r\n\r\n let aValue = get(a, params.dataIndex);\r\n let bValue = get(b, params.dataIndex);\r\n if (aValue != null && bValue != null) {\r\n return aValue?.localeCompare?.(bValue) ?? 0;\r\n }\r\n else if (aValue == null) {\r\n // That means a is null , so b will come first.\r\n\r\n if (sortOrder === \"descend\") {\r\n return -1;\r\n }\r\n else {\r\n return 1;\r\n }\r\n }\r\n else if (bValue == null) {\r\n // this means b is null, a will be first\r\n if (sortOrder === \"descend\") {\r\n return 1;\r\n }\r\n else {\r\n return -1;\r\n }\r\n }\r\n // both are null\r\n return 0;\r\n };\r\n\r\n return sorter(params.a, params.b, params.sortOrder);\r\n}\r\n\r\nexport const useRequestNewBinStateMutation = (deviceId: string) => {\r\n return useMutation({\r\n mutationFn: async () => {\r\n return await BinApiService.uploadBinStateToAzure(deviceId);\r\n }\r\n })\r\n}\r\n\r\nexport interface ExtendedColumnType

extends ColumnType

{\r\n sortType?: 'number' | 'string' | 'date' | 'boolean'\r\n}\r\n\r\nexport interface ColumnExpansionObj {\r\n columns: Array>;\r\n datasource: S,\r\n}\r\nexport const useColumnExpansion =

(params: ColumnExpansionObj) => {\r\n\r\n let newColumns = params.columns.map((column) => {\r\n\r\n let newColumn = { ...column };\r\n if (column.dataIndex == null) {\r\n console.warn(\"bail: \", column.title, \"has no dataIndex\");\r\n return newColumn;\r\n }\r\n\r\n // derive default\r\n if (column.sorter == null) {\r\n if (column.sortType === 'number') {\r\n newColumn.sorter = (a, b, sortOrder) => sortNumberic({ a, b, sortOrder: sortOrder!, dataIndex: column.dataIndex as string });\r\n }\r\n else if (column.sortType == 'date') {\r\n // assume locale compare, assume ISO 8601 string\r\n newColumn.sorter = (a, b, sortOrder) => sortISODate({ a, b, sortOrder: sortOrder!, dataIndex: column.dataIndex as string | string[] });\r\n\r\n }\r\n else if (column.sortType == 'boolean') {\r\n newColumn.sorter = (a, b, sortOrder) => sortBoolean({ a, b, sortOrder: sortOrder!, dataIndex: column.dataIndex as string | string[] });\r\n }\r\n else if (column.sortType == 'string') {\r\n newColumn.sorter = (a, b, sortOrder) => sortString({ a, b, sortOrder: sortOrder!, dataIndex: column.dataIndex as string | string[] });\r\n }\r\n else {\r\n return newColumn;\r\n }\r\n\r\n }\r\n return newColumn;\r\n })\r\n return newColumns;\r\n}\r\n\r\ninterface ShowCameraButtonProps {\r\n binId: number;\r\n binName: string\r\n}\r\nexport const ShowCameraButton = (props: ShowCameraButtonProps) => {\r\n\r\n const [open, setOpen] = useState(false);\r\n\r\n return (<>\r\n setOpen(false)} onOk={() => setOpen(false)}>\r\n \r\n \r\n \r\n );\r\n\r\n}\r\n\r\ninterface SpreadsheetDataSource extends BinDTO {\r\n binId: number | null | undefined;\r\n binName: string;\r\n deviceId: string | null ;\r\n loading: boolean | null | undefined;\r\n online: boolean | null | undefined;\r\n}\r\n\r\n\r\nconst formatOnlineStatus = (value: boolean | null | undefined) => {\r\n let renderText = '';\r\n if (value === false) {\r\n renderText = \"Offline\";\r\n }\r\n else if (value === true) {\r\n renderText = \"Online\";\r\n }\r\n\r\n return renderText;\r\n\r\n}\r\n\r\nexport const SpreadsheetView = (props: {productType: string}) => {\r\n const [nameSearch, setNameSearch] = useState(\"\");\r\n const nameSearchDelayedValue = useDebouncedCallback((value) => {\r\n setNameSearch(value);\r\n }, DEBOUNCE_TIME_MS);\r\n\r\n const queryClient = useQueryClient();\r\n const binsQuery = useAllBinsQuery(true, {selector(data) {\r\n if (data == null) {\r\n return data;\r\n }\r\n return data.filter(bin => bin.automationType?.toLocaleLowerCase?.() === props.productType);\r\n },});\r\n\r\n // https://stackoverflow.com/a/68066447\r\n const csvLinkRef = useRef<\r\n CSVLink & HTMLAnchorElement & { link: HTMLAnchorElement }\r\n >(null); // setup the ref that we'll use for the CSVLink click once we've updated the filename\r\n\r\n const formatYesNoBoolean = (value: boolean | null | undefined) => {\r\n let renderText = '';\r\n if (value === false) {\r\n renderText = \"No\";\r\n }\r\n else if (value === true) {\r\n renderText = \"Yes\";\r\n }\r\n\r\n return renderText;\r\n\r\n }\r\n\r\n const formatOnOffBoolean = (value: boolean | null | undefined) => {\r\n let renderText = '';\r\n if (value === false) {\r\n renderText = \"Off\";\r\n }\r\n else if (value === true) {\r\n renderText = \"On\";\r\n }\r\n\r\n return renderText;\r\n\r\n }\r\n\r\n const formatOnOffPaused = (bdto: BinDTO | null | undefined) => {\r\n \r\n if(bdto === null || bdto === undefined){\r\n return \"\";\r\n }\r\n\r\n if(bdto?.fanOperations?.offReason === null){\r\n return \"On\";\r\n }else if(bdto?.fanOperations?.offReason === FanOffReason.Requested){\r\n return \"Off\";\r\n }else{\r\n return \"Paused\";\r\n }\r\n }\r\n\r\n // const formatOnOffPausedFan = (bdto: BinDTO | null | undefined) => {\r\n // let renderText = '';\r\n // if (bdto?.fanOperations?.offReason?.toString() === \"Paused\"){\r\n // renderText = \"Paused\";\r\n // }else if (value===true){\r\n // renderText = \"\";\r\n // }\r\n\r\n // return renderText;\r\n // }\r\n\r\n const formatDateTime = (value: string | null | undefined) => {\r\n\r\n if (value == null) {\r\n return value;\r\n }\r\n return dayjs(value)?.format(\"L LT\");\r\n }\r\n\r\n useEffect(() => {\r\n binsQuery.data?.forEach(bin => {\r\n try {\r\n SignalRContext.invoke(\"SubscribeBin\", bin.id);\r\n console.log(`subscribed to bin ${bin.id}`);\r\n } catch (error) {\r\n console.error(\"error subscribing to bin\", error);\r\n }\r\n });\r\n\r\n return () => {\r\n binsQuery.data?.forEach(bin => {\r\n SignalRContext.invoke(\"UnsubscribeBin\", bin.id);\r\n console.log(`unsubsribed from bin ${bin.id}`);\r\n });\r\n };\r\n }, [binsQuery.data ?? null]);\r\n\r\n const snapshotsData = useQueries({\r\n queries: binsQuery.data?.filter(binInfo => !(binInfo.deviceId == null) || !(binInfo.id == null)).map(binInfo => {\r\n const userTimezoneOffset = getUserTimezoneOffset();\r\n return {\r\n queryKey: deviceQueryKeys.devicePing(binInfo.deviceId!), queryFn: async (q: any) => {\r\n const result = await BinApiService.pingDeviceAndGetBinDTOFromAzure(binInfo.deviceId!, userTimezoneOffset, q.signal);\r\n // share to bin detail from azure\r\n if (result.binDTO != null) {\r\n queryClient.setQueryData([...deviceQueryKeys.stateFromAzure(binInfo.deviceId!)], result.binDTO);\r\n }\r\n return result;\r\n }\r\n };\r\n }) ?? []\r\n });\r\n\r\n const pending = snapshotsData.reduce((acc, obj) => { return acc + Number(obj.isLoading) }, 0);\r\n\r\n // We cached the data above, and the infinite staletime will only read from cache OR refetch on cache invalidation (logout, signalr)\r\n const deviceQueries = useQueries({\r\n queries: snapshotsData.map?.((data, index) => {\r\n const deviceId = binsQuery.data?.[index]?.deviceId!;\r\n const userTimezoneOffset = getUserTimezoneOffset();\r\n return {\r\n staleTime: Infinity, queryKey: deviceQueryKeys.stateFromAzure(binsQuery.data?.[index]?.deviceId!),\r\n queryFn: async (q: any) => {\r\n return await BinApiService.getBinDetailFromAzure(deviceId, binsQuery.data?.[index]?.id!, userTimezoneOffset, q.signal);\r\n },\r\n enabled: data.data != null,\r\n };\r\n })\r\n });\r\n const columns: Array> = [\r\n {\r\n dataIndex: \"binName\",\r\n title: \"Bin Name\",\r\n width: \"16ch\",\r\n fixed: \"left\",\r\n defaultSortOrder: 'ascend',\r\n sortType: 'string',\r\n render(value, record, index) {\r\n return

\r\n {value}\r\n
\r\n },\r\n },\r\n {\r\n dataIndex: \"growerName\",\r\n title: \"Grower\",\r\n width: \"16ch\",\r\n fixed: \"left\",\r\n defaultSortOrder: 'ascend',\r\n sortType: 'string',\r\n },\r\n {\r\n dataIndex: \"online\",\r\n title: \"Status\",\r\n width: \"72px\",\r\n sortType: 'boolean',\r\n render(value, record, index) {\r\n if (record.loading === true) {\r\n return ;\r\n }\r\n let warning = false;\r\n if (value === false) {\r\n warning = true;\r\n }\r\n\r\n let renderText = formatOnlineStatus(record?.online);\r\n\r\n\r\n return {\r\n props: {\r\n style: { ...(warning ? warningStyle : okStyle) },\r\n },\r\n children:
{renderText}
\r\n }\r\n },\r\n },\r\n {\r\n dataIndex: \"captureTimeUtc\",\r\n title: \"Last Updated\",\r\n width: \"160px\",\r\n sortType: 'date',\r\n onCell: (data, index) => {\r\n let value = data.captureTimeUtc;\r\n if (data.captureTimeUtc == null) {\r\n return {};\r\n }\r\n else {\r\n let warning = false;\r\n let datetime = dayjs(value);\r\n if (datetime.isValid()) {\r\n let currentTime = dayjs();\r\n let diff = currentTime.diff(datetime, 'hours');\r\n if (diff > 24 || diff < -24) {\r\n warning = true;\r\n }\r\n }\r\n return {\r\n style: { ...(warning ? warningStyle : undefined) },\r\n }\r\n }\r\n },\r\n render(value, record, index) {\r\n if (value == null) {\r\n return ;\r\n }\r\n\r\n return {formatDateTime(value)};\r\n },\r\n },\r\n {\r\n title: \"Mode\",\r\n dataIndex: ['operatingMode'],\r\n sortType: \"string\",\r\n },\r\n {\r\n dataIndex: \"isFanOn\",\r\n sortType: \"boolean\",\r\n width: \"80px\",\r\n title: \"Fan\",\r\n render(value, record, index) {\r\n let warning = false;\r\n if (value === false) {\r\n warning = true;\r\n }\r\n\r\n let renderText = formatOnOffPaused(record);\r\n\r\n let someStyle= okStyle;\r\n if(renderText === \"Paused\"){\r\n someStyle=interestStyle;\r\n }else if(renderText === \"Off\"){\r\n someStyle=warningStyle;\r\n }\r\n return {\r\n props: {\r\n style: someStyle,\r\n },\r\n children:
{renderText}
\r\n }\r\n },\r\n },\r\n {\r\n dataIndex: \"isAnyHeaterOn\",\r\n sortType: \"boolean\",\r\n width: \"88px\",\r\n title: \"Heater\",\r\n render(value, record, index) {\r\n let warning = false;\r\n if (value === false) {\r\n warning = true;\r\n }\r\n let renderText = formatOnOffBoolean(value);\r\n\r\n\r\n return {\r\n props: {\r\n style: { ...(warning ? warningStyle : okStyle) },\r\n },\r\n children:
{renderText}
\r\n }\r\n },\r\n },\r\n {\r\n dataIndex: [\"compressorState\", 'isOn'],\r\n sortType: \"boolean\",\r\n title: \"Compressor\",\r\n render(value, record, index) {\r\n let warning = false;\r\n if (value === false) {\r\n warning = true;\r\n }\r\n\r\n let renderText = formatOnOffBoolean(value);\r\n\r\n\r\n return {\r\n props: {\r\n style: { ...(warning ? warningStyle : okStyle) },\r\n },\r\n children:
{renderText}
\r\n }\r\n },\r\n },\r\n {\r\n title: Fill Lv.
Sensor
,\r\n dataIndex: ['grain', 'percentFullCalculated'],\r\n sortType: \"number\",\r\n render(value, record, index) {\r\n return formatNumber(value, { decimalPlaces: 0, filler: \"\", suffix: \"%\" });\r\n },\r\n },\r\n {\r\n title: Fill Lv.
Entered
,\r\n dataIndex: ['grain', 'percentFullEntered'],\r\n sortType: \"number\",\r\n render(value, record, index) {\r\n return formatNumber(value, { decimalPlaces: 0, filler: \"\", suffix: \"%\" });\r\n },\r\n },\r\n {\r\n dataIndex: \"ambientTemp\",\r\n title: \"Ambient Temp\",\r\n sortType: 'number',\r\n render(value, record, index) {\r\n return formatNumber(value, { decimalPlaces: 1, filler: \"\", suffix: \"\" });\r\n },\r\n },\r\n {\r\n dataIndex: ['plenumAir', 'temp'],\r\n title: \"Plenum Temp\",\r\n sortType: 'number',\r\n render(value, record, index) {\r\n return formatNumber(value, { decimalPlaces: 1, filler: \"\", suffix: \"\" });\r\n },\r\n },\r\n {\r\n title: Temp Δ < br /> ambient v. plenum,\r\n ellipsis: true,\r\n dataIndex: 'plenumAmbientTmperatureDifference',\r\n sortType: 'number',\r\n showSorterTooltip: {\r\n title: \"Temp difference (ambient v. plenum)\",\r\n },\r\n render(value, record, index) {\r\n let warning = false;\r\n if (value >= 30 || value <= -30) {\r\n warning = true;\r\n }\r\n\r\n let formattedValue = formatNumber(value, { decimalPlaces: 1, filler: \"\" });\r\n\r\n\r\n return {\r\n props: {\r\n style: warning ? warningStyle : undefined\r\n },\r\n children: {formattedValue}\r\n }\r\n }\r\n },\r\n {\r\n title: \"CO2\",\r\n dataIndex: \"cO2Level\",\r\n width: \"72px\",\r\n sortType: 'number',\r\n render(value, record, index) {\r\n let warning = false;\r\n if (value > 2000) {\r\n warning = true;\r\n }\r\n\r\n\r\n return {\r\n props: {\r\n style: warning ? warningStyle : undefined\r\n },\r\n children:
{value}
\r\n }\r\n },\r\n },\r\n {\r\n dataIndex: \"hasCamera\",\r\n title: \"Camera\",\r\n width: \"88px\",\r\n render(value, record, index) {\r\n if (record.loading === true) {\r\n return null;\r\n }\r\n if (!record.hasCamera) {\r\n return null;\r\n }\r\n return {\r\n props: {},\r\n children: \r\n \r\n \r\n \r\n \r\n }\r\n },\r\n },\r\n {\r\n dataIndex: 'ascii',\r\n ellipsis: true,\r\n sorter: true,\r\n sortDirections: [],\r\n showSorterTooltip: {\r\n title: \"Diagnostic File\",\r\n },\r\n title: Diagnostic
File
,\r\n render(value, record, index) {\r\n if (record.loading === true) {\r\n return null;\r\n }\r\n return {\r\n props: {},\r\n children: \r\n \r\n \r\n \r\n \r\n }\r\n },\r\n },\r\n\r\n {\r\n title: \"Refresh\",\r\n width: \"72px\",\r\n render(value, record, index) {\r\n\r\n return <>\r\n \r\n \r\n },\r\n },\r\n {\r\n width: \"80px\",\r\n title: System
Type
,\r\n ellipsis: true,\r\n showSorterTooltip: {\r\n title: \"System Type\",\r\n },\r\n dataIndex: [\"automationType\"],\r\n sortType: \"string\",\r\n },\r\n\r\n ];\r\n\r\n const dataSources: Array = deviceQueries?.map((bin, index) => {\r\n\r\n const snapshotQuery: typeof snapshotsData[0] | undefined = snapshotsData?.[index];\r\n let plenumAmbientTemperatureDiff: number | string | null = null;\r\n if (bin?.data?.plenumAir?.temp == null) {\r\n //plenumAmbientTemperatureDiff = \"No plenum data\";\r\n plenumAmbientTemperatureDiff = null;\r\n }\r\n else if (bin?.data?.ambientAir?.temp == null) {\r\n //plenumAmbientTemperatureDiff = \"No Ambient data\";\r\n plenumAmbientTemperatureDiff = null;\r\n }\r\n else {\r\n plenumAmbientTemperatureDiff = roundTo(bin?.data?.plenumAir?.temp - bin?.data?.ambientAir?.temp, 2);\r\n }\r\n let isAnyHeaterOnValue = isAnyHeaterOn(bin.data!);\r\n return {\r\n ...(bin.data ?? {}),\r\n binId: binsQuery.data?.[index].id,\r\n binName: binsQuery.data?.[index]?.name,\r\n growerName: binsQuery.data?.[index]?.growerName,\r\n deviceId: binsQuery.data?.[index]?.deviceId!,\r\n online: snapshotQuery?.data?.ping,\r\n onlineFormatted: formatOnlineStatus(snapshotQuery?.data?.ping),\r\n loading: snapshotQuery?.isLoading,\r\n cO2Level: bin?.data?.cO2Level,\r\n captureTimeUtc: bin?.data?.captureTimeUtc,\r\n captureTimeFormatted: formatDateTime(bin?.data?.captureTimeUtc! as string | null | undefined),\r\n ambientTemp: bin?.data?.ambientTemp,\r\n plenumAmbientTmperatureDifference: plenumAmbientTemperatureDiff,\r\n isAnyHeaterOn: isAnyHeaterOnValue,\r\n isAnyHeaterOnFormatted: formatOnOffBoolean(isAnyHeaterOnValue),\r\n isFanOnFormatted: formatOnOffPaused(bin?.data),\r\n isCompressorOnFormatted: formatOnOffBoolean(bin?.data?.compressorState?.isOn),\r\n operatingMode: getCurrentModeDescription(bin?.data!),\r\n hasCameraFormatted: formatYesNoBoolean(bin?.data?.hasCamera),\r\n binLink: `${window.location.origin}/${Routes.BIN_STATS}/${binsQuery?.data?.[index]?.id}`,\r\n }\r\n });\r\n\r\n let transformed = useColumnExpansion({ columns: columns, datasource: dataSources });\r\n\r\n const generateCSVFilename = useCallback(() => {\r\n\r\n return `binOverview ${dayjs().format(\"YYYY-MM-DD HH_mm_ss\")}.csv`;\r\n }, []);\r\n\r\n const updateFilters = () => {\r\n if (nameSearch === \"\") {\r\n return dataSources ?? [];\r\n }\r\n\r\n const filteredTable = dataSources\r\n ?.filter((record) => record.binName?.toUpperCase().includes(nameSearch.toUpperCase()))\r\n ?? [];\r\n return filteredTable;\r\n };\r\n\r\n const finalTable = useMemo(() => updateFilters(), [nameSearch, dataSources]);\r\n\r\n\r\n return (<>\r\n \r\n
Bins still loading: {pending}
\r\n\r\n nameSearchDelayedValue(evt.target.value)} placeholder='Search by Name'>\r\n\r\n \r\n
\r\n \r\n\r\n
\r\n );\r\n}\r\n\r\ninterface requestNewBinStateButtonProps {\r\n deviceId: string;\r\n}\r\n\r\nexport const RequestNewBinStateButton = (props: requestNewBinStateButtonProps) => {\r\n\r\n const requestNewBinStateMutationMutation = useRequestNewBinStateMutation(props.deviceId!);\r\n\r\n\r\n return <>\r\n \r\n ;\r\n}\r\n\r\nexport default BinCommander;","import { VideoCameraOutlined } from \"@ant-design/icons\";\r\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\r\nimport { Alert, Button, Col, Modal, Popover, Row, Space, Tooltip, Typography, message, notification } from \"antd\";\r\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { useBinInfoContext } from \"src/queries/BinInfoContext\"\r\nimport ReactPlayer from \"react-player/file\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { ApiError } from \"src/api/ApiResultHandler\";\r\nimport RoleUtil from \"src/utils/RoleUtil\";\r\n\r\nconst ResponsivePlayer = (props: {url: string}) => {\r\n return (\r\n
\r\n \r\n
\r\n )\r\n }\r\n\r\nconst useStartLiveStream = (binId: number) => {\r\n\r\n return useMutation({\r\n mutationFn: async (params: {cameraNumber: number}) => {\r\n return await BinApiService.startLiveStream(binId);\r\n },\r\n onError(error: any, variables, context) {\r\n console.log(\"error starting live stream\", error);\r\n const isAdmin = RoleUtil.currentUserIsAdmin()\r\n notification.error({\r\n message: error?.errorDetails?.title ?? \"Livestream error\",\r\n description: isAdmin ? error?.errorDetails?.detail : undefined,\r\n });\r\n },\r\n });\r\n}\r\n\r\n\r\nconst useStopLiveStream = (binId: number) => {\r\n\r\n return useMutation({\r\n mutationFn: async () => {\r\n return await BinApiService.stopLiveStream(binId);\r\n },\r\n onError(error: any, variables, context) {\r\n console.log(\"error stopping live stream\", error);\r\n const isAdmin = RoleUtil.currentUserIsAdmin()\r\n notification.error({\r\n message: error?.errorDetails?.title ?? \"Livestream error\",\r\n description: isAdmin ? error?.errorDetails?.detail : undefined,\r\n });\r\n },\r\n });\r\n}\r\n\r\nexport const LiveStreaming = () => {\r\n const queryClient = useQueryClient();\r\n const binInfo = useBinInfoContext();\r\n const binId = binInfo?.id;\r\n\r\n const [open, setOpen] = useState(false);\r\n\r\n const close = useCallback(() => {\r\n setOpen(false);\r\n }, []);\r\n\r\n const query = useQuery({\r\n queryKey: binDBKeys.livestream.camera({binId: binId, cameraNumber: 1}),\r\n queryFn: async (q) => {\r\n return await BinApiService.getStreamsForDevice(binId, q.signal);\r\n },\r\n retry: 1,\r\n refetchIntervalInBackground: false,\r\n refetchOnWindowFocus: false,\r\n enabled: open,\r\n refetchInterval: (data, query) => {\r\n if (query.state.fetchFailureCount > 0) {\r\n return false;\r\n }\r\n return 3_000;\r\n },\r\n });\r\n\r\n const streams = query?.data?.streamUrls ?? [];\r\n const stream = streams?.[0];\r\n\r\n \r\n const mutateStartLiveStreaming = useStartLiveStream(binId);\r\n const mutateStopLiveStreaming = useStopLiveStream(binId);\r\n\r\n const onLiveStreamingClick = useCallback(async () => {\r\n queryClient.invalidateQueries(binDBKeys.livestream.all(binId));\r\n mutateStartLiveStreaming.mutate({cameraNumber: 1},\r\n {onSuccess: (data) => {\r\n if (data.success === true) {\r\n message.info(\"livestream will start in 15-20 seconds\");\r\n setOpen(true);\r\n }\r\n }}\r\n )\r\n }, [mutateStartLiveStreaming])\r\n\r\n \r\n const onLiveStreamingStopClick = useCallback(async () => {\r\n queryClient.invalidateQueries(binDBKeys.livestream.all(binId));\r\n mutateStopLiveStreaming.mutate(undefined,\r\n {onSuccess: (data) => {\r\n if (data.success === true) {\r\n if (streams?.length > 0) {\r\n message.info(\"livestream stopped\");\r\n }\r\n }\r\n else {\r\n console.warn(\"problem stopping livestream\");\r\n }\r\n }}\r\n )\r\n }, [mutateStopLiveStreaming, streams])\r\n\r\n const streamText = () => {\r\n if (stream) {\r\n return \"Restart Livestream\";\r\n }\r\n else {\r\n return \"Start Livestream\"\r\n }\r\n }\r\n const LIVESTREAM_DURATION_SECONDS = 90;\r\n const livestreamDurationReminderText = `Livestreams last for up to ${LIVESTREAM_DURATION_SECONDS} seconds`;\r\nreturn
\r\n
\r\n \r\n
\r\n {\r\n onLiveStreamingStopClick();\r\n }} title={\r\n \r\n {Livestream}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n } footer={null} open={open} onCancel={close} onOk={close}>\r\n\r\n{(query.isError || query.failureCount > 0) && query.refetch()} size=\"small\" danger loading={query.isFetching}>\r\n Retry\r\n \r\n }\r\n />\r\n}\r\n {stream != null && \r\n \r\n {livestreamDurationReminderText}\r\n \r\n \r\n }\r\n \r\n \r\n \r\n \r\n \r\n\r\n\r\n\r\n \r\n
\r\n}","import { CameraOutlined } from '@ant-design/icons';\r\nimport { useQuery } from '@tanstack/react-query';\r\nimport { Button, Col, Image, message, Row, Space, Typography } from 'antd';\r\nimport dayjs from 'dayjs';\r\nimport React, { useState } from 'react';\r\nimport { queryClient } from 'src';\r\nimport { ApiError } from 'src/api/ApiResultHandler';\r\nimport BinApiService from 'src/api/BinApiService';\r\nimport { LiveStreaming } from './LiveStreaming';\r\n\r\nexport const TakeCameraPicture = (props: { binId: number }) => {\r\n const [takingImage, setTakingImage] = useState(false);\r\n\r\n return ();\r\n};\r\n\r\n\r\nexport interface CameraImageProps {\r\n binId: number,\r\n}\r\n\r\nexport const CameraImage = (props: CameraImageProps) => {\r\n const imageQuery = useQuery([props.binId, \"camera\"], {\r\n queryFn: async (q) => {\r\n return await BinApiService.getCameraImage(props.binId, q.signal);\r\n }, enabled: props.binId > 0, staleTime: Infinity, retry: 0\r\n })\r\n\r\n console.log(\"the image data\", imageQuery.data);\r\n\r\n\r\n return (\r\n <>\r\n {imageQuery.isInitialLoading &&
Loading Camera Picture...
}\r\n {imageQuery.error && <>\r\n \r\n \r\n {(imageQuery.error as ApiError)?.message}\r\n \r\n \r\n \r\n \r\n \r\n \r\n }\r\n {imageQuery.data && <>
\r\n \r\n \r\n \r\n Date: {dayjs(imageQuery.data.lastModifiedUtc).format(\"MM/DD/YYYY hh:mm:ss A\")}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n }\r\n \r\n );\r\n};","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum CompressorOffReason {\r\n UserRequested = 'UserRequested',\r\n OverrideRequested = 'OverrideRequested',\r\n AmbientTemperature = 'AmbientTemperature',\r\n RestBetweenCycles = 'RestBetweenCycles',\r\n}\r\nexport default CompressorOffReason;\r\n","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\r\nimport { Card, Col, message, Radio, Row, Spin, Tooltip, Typography } from \"antd\"\r\nimport React from \"react\"\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport CompressorMode from \"src/consts/CompressorMode\";\r\nimport CompressorOffReason from \"src/consts/CompressorOffReason\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport { usePermissions } from \"src/pages/features/usePermissions\";\r\nimport { OperatingMode } from \"./BinVisualThree\";\r\nimport { formatNumber } from \"./HeaterControls\";\r\n\r\n\r\nexport const useTurnOffCompressorMutation = (deviceId: string) => {\r\n const queryClient = useQueryClient();\r\n return useMutation({\r\n mutationFn: async () => {\r\n const result = await BinApiService.turnOffCompressor(deviceId);\r\n return { success: result };\r\n }\r\n });\r\n};\r\n\r\nexport const useTurnOnCompressorMutation = (deviceId: string) => {\r\n const queryClient = useQueryClient();\r\n return useMutation({\r\n mutationFn: async () => {\r\n const result = await BinApiService.turnOnCompressor(deviceId);\r\n return { success: result };\r\n }\r\n });\r\n};\r\n\r\ninterface CompressorControlProps {\r\n binId: number;\r\n azureDeviceId: string;\r\n binDTO: BinDTO | null | undefined;\r\n}\r\n\r\nexport const CompressorControls = (props: CompressorControlProps) => {\r\n\r\n const ambientTempF = props.binDTO?.ambientAir?.temp;\r\n const compressorPSI = props.binDTO?.compressorState?.inlinePressure?.psi;\r\n const permissions = usePermissions();\r\n\r\n const formatAmbientText = (temp: number | null | undefined) => {\r\n if (temp == null || !Number.isFinite(temp)) {\r\n return \"No Data\";\r\n }\r\n return `${temp.toFixed(0)}°`;\r\n };\r\n\r\n const formatCompressorText = (psi: number | null | undefined) => {\r\n if (psi == null || !Number.isFinite(psi)) {\r\n return \"No Data\";\r\n }\r\n return {`${psi.toFixed(0)}`};\r\n };\r\n\r\n\r\n const formatCompressorStatus = (binDTO: BinDTO | null | undefined) => {\r\n // compressirOn: true/false\r\n // compressorOffReason: enum, null if compressor is on\r\n // cycle start time. compresosor will turn on in HH:MM\r\n\r\n const compressorStatus = binDTO?.compressorState?.isOn;\r\n if (compressorStatus === true) {\r\n return Compressor is ON;\r\n }\r\n else if (compressorStatus === false) {\r\n return Compressor is OFF;\r\n }\r\n return \"Uknown\";\r\n }\r\n\r\n const formatCompressorOffReason = (binDTO: BinDTO | null | undefined) => {\r\n const compressorOffReason = binDTO?.compressorState?.offReason;\r\n if (binDTO?.compressorState?.offReason === CompressorOffReason.RestBetweenCycles) {\r\n return Compressor is cycling for 3 minutes to cool down & will resume\r\n }\r\n else {\r\n return {compressorOffReason}\r\n }\r\n }\r\n\r\n const turnOffCompressorMutation = useTurnOffCompressorMutation(props.azureDeviceId);\r\n const turnOnCompressorMutation = useTurnOnCompressorMutation(props.azureDeviceId);\r\n const allowCompressorControl = props.binDTO?.operatingMode === OperatingMode.Manual;\r\n\r\n const getCompressorDisabledText = () => {\r\n if (!permissions.canWrite) {\r\n return \"View Only\";\r\n }\r\n if (!allowCompressorControl) {\r\n return \"Controllable in Dry Mode or User Control mode\";\r\n }\r\n return '';\r\n }\r\n\r\n const compressorControlText = getCompressorDisabledText();\r\n\r\n return \r\n\r\n \r\n \r\n
\r\n \r\n \r\n {\r\n turnOffCompressorMutation.mutate(undefined, {\r\n onSuccess: (result) => {\r\n if (result.success === true) {\r\n message.success('Requested compressor to turn Off.');\r\n } else {\r\n message.destroy();\r\n message.error('Compressor did not turn off');\r\n }\r\n }, onError: (error) => {\r\n message.error('Failed to turn off compressor');\r\n console.error('Failed to turn off compressor', error);\r\n\r\n }\r\n })\r\n }} value={false}>Off\r\n {\r\n turnOnCompressorMutation.mutate(undefined, {\r\n onSuccess: (result) => {\r\n if (result.success === true) {\r\n message.success('Requested compressor to turn On.');\r\n } else {\r\n message.destroy();\r\n message.error('Compressor did not turn On');\r\n }\r\n }, onError: (error) => {\r\n message.error('Failed to turn on compressor');\r\n console.error('Failed to turn on compressor', error);\r\n }\r\n })\r\n }}>On\r\n \r\n \r\n {(turnOffCompressorMutation.isLoading || turnOnCompressorMutation.isLoading) && }\r\n
\r\n\r\n
\r\n\r\n Automated Activation\r\n \r\n The compressor is used to control the valves & will automatically turn on when the valve controls are activated.\r\n \r\n\r\n Temp Warning\r\n \r\n If the ambient temp is below 32°F various parts of the system are at high risk of damage.\r\n \r\n \r\n\r\n\r\n \r\n Sensor Readings\r\n Ambient Air Temp: {formatAmbientText(ambientTempF)}\r\n PSI: {formatCompressorText(compressorPSI)}\r\n {(props.binDTO?.hardwareYear ?? 0) >= 2022 && \r\n Current: {formatNumber(props.binDTO?.compressorState?.current?.current_mA! / 1000, { decimalPlaces: 2 })} Amps\r\n }\r\n\r\n {(ambientTempF == null || ambientTempF <= 32) &&

WARNING - HIGH RISK OF SYSTEM DAMAGE!!!
}\r\n\r\n\r\n Compressor Status\r\n {formatCompressorStatus(props.binDTO)}\r\n \r\n {formatCompressorOffReason(props.binDTO)}\r\n \r\n \r\n
\r\n
\r\n}","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum GrainType {\r\n Corn = 'Corn',\r\n Soybeans = 'Soybeans',\r\n Wheat = 'Wheat',\r\n WheatDurum = 'WheatDurum',\r\n HardSpringWheat = 'HardSpringWheat',\r\n HardWinterWheat = 'HardWinterWheat',\r\n SoftWinterWheat = 'SoftWinterWheat',\r\n Rye = 'Rye',\r\n LongGrainRoughRice = 'LongGrainRoughRice',\r\n MediumGrainRoughRice = 'MediumGrainRoughRice',\r\n ShortGrainRoughRice = 'ShortGrainRoughRice',\r\n Sorghum = 'Sorghum',\r\n Barley = 'Barley',\r\n Oats = 'Oats',\r\n Sunflower = 'Sunflower',\r\n Canola = 'Canola',\r\n}\r\nexport default GrainType;\r\n","import { useMutation } from \"@tanstack/react-query\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\n\r\n\r\nexport const useUploadBinStateToAzure = (deviceId: string) => {\r\n return useMutation({\r\n mutationFn: async () => {\r\n return await BinApiService.uploadBinStateToAzure(deviceId);\r\n }\r\n });\r\n }\r\n ","import React, { useMemo } from \"react\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport { BinInfoContext, useBinInfoContext } from \"src/queries/BinInfoContext\";\r\nimport { useUploadBinStateToAzure } from \"src/queries/useUploadBinStateToAzure\";\r\n\r\ninterface ForceNewBinStateUploadContextValue {\r\n refresh: () => Promise,\r\n loading: boolean,\r\n}\r\n\r\nconst defaultValues: ForceNewBinStateUploadContextValue = {\r\n refresh: () => Promise.resolve(),\r\n loading: false,\r\n}\r\n\r\nexport const ForceNewBinStateUploadContext = React.createContext(defaultValues);\r\n\r\n\r\nexport const useForceNewBinStateUploadContext = () => {\r\n const binInfo = React.useContext(BinInfoContext);\r\n const updateRequest = React.useContext(ForceNewBinStateUploadContext);\r\n if (updateRequest == null) {\r\n throw new Error(\"no value provided for ForceNewBinStateUploadContext\");\r\n }\r\n\r\n const uploadBinStateToAzureMutation = useUploadBinStateToAzure(binInfo?.deviceId!);\r\n\r\n return useMemo(() => {\r\n return {\r\n refresh: updateRequest?.refresh,\r\n isLoading: updateRequest?.loading,\r\n };\r\n}, [updateRequest]);\r\n};\r\n","import { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\r\nimport { Button, Card, Col, Form, InputNumber, Row, Space, Tag, Typography, message, Tooltip, Flex, Popover, ConfigProvider, Badge, Input, Modal, Descriptions, Spin } from \"antd\"\r\nimport { useForm } from \"antd/es/form/Form\";\r\nimport { delay, round } from \"lodash\";\r\nimport React, { useCallback, useEffect, useLayoutEffect, useMemo, useState } from \"react\"\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport LayerPercentageDTO from \"src/models/LayerPercentageDTO\";\r\nimport { formatNumber } from \"./HeaterControls\";\r\nimport { CheckCircleOutlined, EditFilled, EditOutlined, SaveOutlined } from \"@ant-design/icons\";\r\nimport TemperatureSensorEnum from \"src/consts/TemperatureSensorEnum\";\r\nimport { getDefaultTemperatureSensorType } from \"src/pages/shared/binDTOUtils\";\r\nimport { CheckFailedIcon, CheckPassedIcon } from \"src/pages/features/WeatherMonitorAntdOnly\";\r\nimport { grainHeightOverridesPresent, useBinDTOContext } from \"src/queries/BinDTOContext\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { deviceQueryKeys } from \"../../../components/BinDetails\";\r\nimport { useDebounce } from 'use-debounce'; // You'll need to install this package: `npm install use-debounce`\r\nimport { useUploadBinStateToAzure } from \"src/queries/useUploadBinStateToAzure\";\r\nimport _ from \"lodash\";\r\nimport useFormInstance from \"antd/es/form/hooks/useFormInstance\";\r\nimport { useForceNewBinStateUploadContext } from \"./ForceNewBinStateUploadContext\";\r\nimport { RefreshButton } from \"./BinStatsPage\";\r\nimport { useBinInfoContext } from \"src/queries/BinInfoContext\";\r\nimport { usePermissions } from \"src/pages/features/usePermissions\";\r\n\r\nexport const useDryLayerPercentAdjustMutation = (deviceId: string) => {\r\n return useMutation({\r\n mutationFn: async (params: {layerPercentages: LayerPercentageDTO[]}) => {\r\n return await BinApiService.setLayersTimePercentages(deviceId, params.layerPercentages);\r\n },\r\n })\r\n}\r\n\r\nconst StatTagColor = \"rgb(254,213,64)\";\r\nconst activeLayerTagColor = \"rgb(64,131,255)\";\r\n\r\ninterface DryLayerAdjustProps {\r\n binDTO: BinDTO;\r\n deviceId: string;\r\n}\r\n\r\nexport interface LayerAdjustment {\r\n percentage: number,\r\n number: number,\r\n}\r\nexport interface FormValues {\r\n layers: LayerAdjustment[],\r\n}\r\n\r\ninterface DryLayerRowProps {\r\n binDTO: BinDTO,\r\n layerNumber: number,\r\n fieldName: number,\r\n}\r\n\r\nexport interface LayerFormatProps {\r\n binDTO: BinDTO;\r\n layerNumber: number;\r\n}\r\n\r\nexport const LayerFormat = (props: LayerFormatProps) => {\r\n\r\n const maxLayers = props.binDTO?.layersAir?.length ?? 0;\r\n const isTopLayer = maxLayers === props.layerNumber;\r\n const isBottomLayer = props.layerNumber === 1;\r\n const targetLayer = props.binDTO?.routineEngine?.targetLayer;\r\n\r\n const layerInProgress = targetLayer == null && props.binDTO?.manualRoutineSettings?.targetLayer === props.layerNumber;\r\n\r\n let additionalLayerText = \"\";\r\n if (isTopLayer) {\r\n additionalLayerText = \" (top valves)\";\r\n }\r\n if (isBottomLayer) {\r\n additionalLayerText = \" (bin floor)\";\r\n }\r\n\r\n const layerInfo = props.binDTO?.layerGrainStates?.find(layer => layer.number == props.layerNumber);\r\n\r\n return (\r\n \r\n Layer {props.layerNumber}{additionalLayerText}\r\n\r\n {/* Search the layer Heights form LayerGrainState.bottom and .top HeightFromFloor */}\r\n {formatNumber(layerInfo?.bottom?.heightFromFloor, {filler: \"\", decimalPlaces: 0})} - {formatNumber(layerInfo?.top?.heightFromFloor, {filler: \"\", decimalPlaces: 0})} ft\r\n \r\n {layerInProgress && }>Processing }\r\n {targetLayer === props.layerNumber && CURRENTLY ACTIVE}\r\n {false && HH:MM remaining}\r\n \r\n );\r\n}\r\nexport interface GrainLayerSummaryInfoProps {\r\n binDTO: BinDTO;\r\n layerNumber: number;\r\n}\r\nexport const GrainLayerSummaryInfo = (props: GrainLayerSummaryInfoProps) => {\r\n const preferredTemperatureSensorType = props.binDTO?.temperatureSensorsType ?? getDefaultTemperatureSensorType(props.binDTO);\r\n const layerHasGrain = props.binDTO?.layerGrainStates?.[props.layerNumber - 1]?.hasGrain ?? false;\r\n const layer = props.binDTO?.layerGrainStates?.[props.layerNumber - 1];\r\n const grainTemp = formatNumber([TemperatureSensorEnum.Opi, TemperatureSensorEnum.PowerCast, TemperatureSensorEnum.BinSense].includes(preferredTemperatureSensorType) ? layer?.temperatureF : layer?.thermocoupleTemperatureF , {decimalPlaces:0, filler:\"--\"})\r\n const grainRH = formatNumber(layer?.relitiveHumidity, {decimalPlaces: 0, filler:\"--\", suffix:\"%\"});\r\n const grainMc = formatNumber(layer?.moistureContent, {decimalPlaces:1, filler:\"--\", suffix:\"%\"});\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n MC: {grainMc}\r\n \r\n \r\n \r\n \r\n Temp: {grainTemp}°F\r\n \r\n \r\n \r\n \r\n RH: {grainRH}\r\n \r\n \r\n {layerHasGrain && \r\n \r\n This layer qualifies for drying based on the grain height.\r\n }\r\n >\r\n }>\r\n \r\n Grain\r\n \r\n \r\n }\r\n {!layerHasGrain && \r\n \r\n This layer DOES NOT qualify for drying based on the grain height.\r\n }>\r\n }>\r\n \r\n No Grain\r\n \r\n \r\n } \r\n \r\n \r\n );\r\n}\r\n\r\nconst DryLayerRow = (props: DryLayerRowProps) => {\r\n\r\n const maxLayers = props.binDTO?.layersAir?.length ?? 0;\r\n\r\n const placeholderValue = round(100 / maxLayers, 1);\r\n\r\n const formContext = useFormInstance();\r\n\r\n // https://chatgpt.com/share/66fc6284-5b7c-8005-8c00-75f0d46ef214 How to validate form.List on input change\r\n const validateListOnBlur = async () => {\r\n await formContext.validateFields(['layers']);\r\n };\r\n\r\n return <>\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n sourceLayer.number == props.layerNumber);\r\n const layerPassesGrainCheck = !(!sourceLayer?.hasGrain && value > 0);\r\n if (!layerPassesGrainCheck) {\r\n return Promise.reject(new Error(`Doesn't qualify`));\r\n }\r\n else {\r\n return Promise.resolve();\r\n }\r\n },\r\n }]}>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ;\r\n}\r\n\r\ninterface LayerAdjustmentListProps {\r\n binDTO: BinDTO,\r\n}\r\n\r\nexport const LayerAdjustmentList = (props: LayerAdjustmentListProps) => {\r\n\r\n return <>\r\n\r\n
\r\n {\r\n console.log(\"validation names\", names);\r\n\r\n const formatLayersGrainCheckErrorMessage = (layers: Array): string => {\r\n const isPlural = layers.length > 1;\r\n const formattedLayers = layers.map(layer => `L${layer.number}`).join(\", \");\r\n if (isPlural) {\r\n return `Layers ${formattedLayers} do not qualify for drying. Enter 0% to skip, or manually set grain heights to override the headspace sensor readings.`;\r\n }\r\n else {\r\n return `Layer ${formattedLayers} does not qualify for drying. Enter 0% to skip, or manually set grain heights to override the headspace sensor readings.`;\r\n }\r\n }\r\n\r\n const layersThatFailGrainCheck = (names as LayerPercentageDTO[]).flatMap((layer: LayerPercentageDTO) => {\r\n const sourceLayer = props.binDTO?.layers?.find(sourceLayer => sourceLayer.number == layer.number);\r\n if (sourceLayer == null) {\r\n return [];\r\n }\r\n\r\n if (!sourceLayer.hasGrain && layer.percentage > 0 ) {\r\n return [layer];\r\n }\r\n return [];\r\n }\r\n );\r\n const passedGrainCheck = layersThatFailGrainCheck.length === 0;\r\n if (!passedGrainCheck) {\r\n const layersFormatted = formatLayersGrainCheckErrorMessage(layersThatFailGrainCheck);\r\n return Promise.reject(new Error(layersFormatted));\r\n }\r\n\r\n let sum = 0;\r\n for (const layer of names) {\r\n sum += layer.percentage;\r\n }\r\n\r\n if (sum <= 99 || sum >= 100.5) {\r\n return Promise.reject(new Error(`Combined time allocation must be 100%. It was ${round(sum, 2)}%`));\r\n }\r\n return Promise.resolve();\r\n },\r\n },\r\n ]}\r\n >\r\n {(fields, { add, remove }, { errors }) => {\r\n\r\n return (<>\r\n\r\n \r\n \r\n {[...fields].reverse().map((field) => (\r\n \r\n \r\n \r\n ))}\r\n \r\n\r\n \r\n
\r\n \r\n
\r\n
\r\n\r\n );\r\n }\r\n }\r\n
\r\n\r\n
\r\n ;\r\n}\r\n\r\nexport const DryLayerAdjustHelpText = () => {\r\n\r\n return (\r\n Enter the percentage of time you would like each layer to be targeted with airflow. Set to 0% to skip a layer. Air will be directed layer by layer, starting from the top and proceeding to the bottom of the bin.\r\n );\r\n}\r\n\r\n\r\nexport const useUserOverrideGrainHeightMutation = (binId: number) => {\r\n const queryClient = useQueryClient();\r\n return useMutation({\r\n mutationFn: async (params: {eaveHeightFt: number | null, peakHeightFt: number | null}) => {\r\n const result = await BinApiService.overrideGrainHeight(binId, params.eaveHeightFt, params.peakHeightFt);\r\n return { success: result };\r\n }\r\n });\r\n};\r\n\r\n// uses a newly passed context to force a new binstate upload\r\nexport const RefreshDryLayerTargetButton = () => {\r\n\r\n const refreshContext = useForceNewBinStateUploadContext();\r\n\r\n return ;\r\n}\r\n\r\nexport const UserGrainHeights = (props: {}) => {\r\n\r\n const { binDTO } = useBinDTOContext();\r\n const grainHeightOverridesPresentValue = grainHeightOverridesPresent(binDTO!);\r\n\r\n return <>\r\n \r\n {grainHeightOverridesPresentValue && Override Active}\r\n \r\n Grain Heights\r\n \r\n \r\n \r\n ;\r\n}\r\n\r\ninterface UserGrainHeightForm {\r\n peakHeightFt: number | null;\r\n eaveHeightFt: number | null;\r\n}\r\n\r\nconst userOverrideGrainHeightsFormId = \"userOverrideGrainHeightsFormId\";\r\n\r\ninterface EditUserGrainHeightsProps {\r\n}\r\n\r\nconst formItemLayout = {\r\n labelCol: {\r\n xs: { span: 24 },\r\n sm: { span: 8 },\r\n },\r\n wrapperCol: {\r\n xs: { span: 24 },\r\n sm: { span: 16 },\r\n },\r\n }; \r\nexport const EditGrainHeights = (props: EditUserGrainHeightsProps) => {\r\n const { binDTO } = useBinDTOContext();\r\n const queryClient = useQueryClient();\r\n const grainHeightMutation = useUserOverrideGrainHeightMutation(binDTO?.id!);\r\n const [showEditHeights, setShowEditHeights] = useState(false);\r\n const permissions = usePermissions();\r\n\r\n const uploadNewBinStateMutation = useUploadBinStateToAzure(binDTO?.deviceId!);\r\n\r\n const [form] = useForm();\r\n\r\n const values = Form.useWatch([], { form, preserve: true });\r\n\r\n // todo: delayed values aren't working with use-debounce with Form.useWatch\r\n const delayedValues = values;\r\n \r\n const grainQuery = useQuery({\r\n queryKey: binDBKeys.calculateGrainHeight(binDTO?.id!, {eaveHeightFt: delayedValues?.eaveHeightFt, peakHeightFt: delayedValues?.peakHeightFt }),\r\n queryFn: async (q) => await BinApiService.calculateGrainHeight(binDTO?.id!, delayedValues?.eaveHeightFt, delayedValues?.peakHeightFt, q.signal),\r\n enabled: delayedValues?.eaveHeightFt != null || delayedValues?.peakHeightFt != null,\r\n })\r\n\r\n const qualifyingLayers = (peakHeight: number): number[] => {\r\n const qualifyingLayers = binDTO?.layers?.flatMap((layer, index, array) => {\r\n if (layer.bottom == null || layer.top == null) {\r\n return [];\r\n }\r\n\r\n const isTopLayer = array[array.length - 1].number === layer.number;\r\n if (isTopLayer) {\r\n // taken from ValveConfig.IsInGrain\r\n const qualifies = (layer.bottom.heightFromFloor + 3) <= peakHeight;\r\n if (qualifies) {\r\n return [layer.number];\r\n }\r\n else {\r\n return [];\r\n }\r\n }\r\n\r\n const halfway = (layer.bottom?.heightFromFloor + layer.top?.heightFromFloor) / 2;\r\n if (peakHeight >= halfway) {\r\n return [layer.number];\r\n }\r\n else {\r\n return [];\r\n }\r\n })\r\n return qualifyingLayers ?? [];\r\n }\r\n\r\n const grainHeightOverridesPresentValue = grainHeightOverridesPresent(binDTO!);\r\n\r\n const handleSubmit = (values: UserGrainHeightForm) => {\r\n\r\n grainHeightMutation.mutate({eaveHeightFt: values.eaveHeightFt, peakHeightFt: values.peakHeightFt},\r\n {\r\n onSuccess(data, variables, context) {\r\n // force IoT to upload a new state since the overrides update the grainHeights\r\n delay(() => {\r\n uploadNewBinStateMutation.mutate();\r\n }, 8_000);\r\n message.success(\"Grain height overrides saved. Grain heights will update in 10-15 seconds\");\r\n setShowEditHeights(false);\r\n },\r\n onError(error, variables, context) {\r\n console.error(\"Problem overriding grain height\", error, \"values: \", values);\r\n message.error(\"Problem overriding grain height. Try again.\");\r\n },\r\n }\r\n )\r\n }\r\n\r\n const handleCancel = () => {\r\n setShowEditHeights(false);\r\n };\r\n\r\n const onClickClearOverrides = () => {\r\n grainHeightMutation.mutate({eaveHeightFt: null, peakHeightFt: null}, {\r\n onSuccess(data, variables, context) {\r\n // force IoT to upload a new state since the overrides update the grainHeights\r\n delay(() => {\r\n uploadNewBinStateMutation.mutate();\r\n }, 8_000);\r\n message.success(\"Grain height overrides cleared. Grain heights will update in 10-15 seconds\");\r\n setShowEditHeights(false);\r\n },\r\n onError(error, variables, context) {\r\n console.error(\"Problem clearing grain height overrides.\", error, \"values: \", values);\r\n message.error(\"Problem clearing grain height overrides. Try again.\");\r\n },\r\n });\r\n }\r\n\r\n useLayoutEffect(() => {\r\n if (showEditHeights) {\r\n console.log(\"updating form values to\", defaultValues);\r\n form.setFieldsValue(defaultValues);\r\n }\r\n }, [form, showEditHeights])\r\n\r\n const defaultValues = {\r\n eaveHeightFt: grainHeightOverridesPresentValue ? binDTO?.grain?.heightAtEaves ?? null : null,\r\n peakHeightFt: grainHeightOverridesPresentValue ? binDTO?.grain?.heightAtPeak ?? null : null,\r\n };\r\n\r\n return <>\r\n \r\n\r\n Clear Overrides,\r\n ,\r\n ,\r\n ]}\r\n >\r\n
\r\n name=\"eaveHeightFt\" label=\"Eaves Grain Depth\"\r\n rules={[\r\n {type: \"number\", max: binDTO?.maxGrainEaveHeight, message: `Max ${_.floor(binDTO?.maxGrainEaveHeight ?? 0, 2)}`},\r\n ]}>\r\n \r\n \r\n name=\"peakHeightFt\" label=\"Peak Grain Depth\"\r\n rules={[\r\n {type: \"number\", max: binDTO?.maxGrainPeakHeight, message: `Max ${_.floor(binDTO?.maxGrainPeakHeight ?? 0, 2)}`},\r\n ]}\r\n >\r\n \r\n \r\n \r\n
\r\n \r\n {grainQuery.data && (values?.eaveHeightFt != null || values?.peakHeightFt != null) && <>\r\n\r\n
\r\n Calc. depth @ Perimeter stack: {formatNumber(grainQuery.data?.outerRingGrainHeightFt, { filler: \"\", decimalPlaces: 0, suffix: \"ft\" })}\r\n
\r\n Calc. depth @ Center stack: {formatNumber(grainQuery.data?.innerRingGrainHeightFt, { filler: \"\", decimalPlaces: 0, suffix: \"ft\" })}\r\n
\r\n Grain layers that qualify for drying: {qualifyingLayers(grainQuery.data?.innerRingGrainHeightFt).map(l => `L${l}`).join(\", \")}\r\n
\r\n
\r\n For Each Layer: The grain must reach halfway to the valves above them for the layer to qualify for drying.\r\n
For the Top Layer: The grain must cover the top valves by at least 3 feet for it to qualify for drying.
\r\n \r\n }\r\n
\r\n
\r\n\r\n
\r\n ;\r\n\r\n}\r\n\r\nexport const GrainHeightText = (props: { binDTO: BinDTO | undefined }) => {\r\n\r\n return \r\n \r\n Eaves: {formatNumber(props.binDTO?.grain?.heightAtEaves, { filler: \"\", decimalPlaces: 1, suffix: \" ft\" })}\r\n \r\n \r\n Peak: {formatNumber(props.binDTO?.grain?.heightAtPeak, { filler: \"\", decimalPlaces: 1, suffix: \" ft\" })}\r\n \r\n ;\r\n};\r\n\r\nconst dryLayerAdjustFormId = \"dry-layer-adjust-form-id\";\r\nexport const DryLayerAdjust = (props: DryLayerAdjustProps) => {\r\n\r\n const [form] = useForm();\r\n const permissions = usePermissions();\r\n //console.log(\"binDTO props\", props.binDTO);\r\n\r\n const numberOfLayers = props.binDTO?.layersAir?.length ?? 0;\r\n\r\n const initialValues: Partial = {};\r\n initialValues.layers = [];\r\n for (let layer of props.binDTO?.layersAir ?? []) {\r\n initialValues.layers.push({\r\n percentage: props.binDTO?.routineEngine?.layersPercentages?.[layer.number -1 ]?.percentage ?? round((1/numberOfLayers) * 100.0, 1),\r\n number: layer.number,\r\n });\r\n }\r\n\r\n useEffect(() => {\r\n if (form.isFieldsTouched()) {\r\n console.log(\"dry layer field are touched\");\r\n return;\r\n }\r\n else {\r\n console.log(\"dry layer fields are not touched\");\r\n form.resetFields();\r\n return;\r\n }\r\n }, [props.binDTO]);\r\n\r\n console.log(\"layer form initial values: \", initialValues);\r\n\r\n const layerAdjustMutation = useDryLayerPercentAdjustMutation(props.deviceId);\r\n\r\n return \r\n
\r\n \r\n
\r\n }>\r\n \r\n
{\r\n //console.log(\"layer adjust data values: \", values);\r\n layerAdjustMutation.mutate({layerPercentages: values.layers},\r\n {\r\n onSuccess(data, variables, context) {\r\n if (data.success === true) {\r\n message.info(\"Updated target layer settings\");\r\n }\r\n else {\r\n message.error(\"Problem updating target layer settings\");\r\n }\r\n },\r\n onError(error, variables, context) {\r\n console.error(error, \"Error updating target layer settings\");\r\n message.error(\"Error updating target layer settings\");\r\n },\r\n }\r\n );\r\n }} initialValues={initialValues} validateTrigger={['onBlur', 'onFocus', 'onInput']}>\r\n\r\n \r\n \r\n \r\n\r\n
\r\n \r\n
\r\n
\r\n}","import { CheckCircleFilled } from \"@ant-design/icons\";\r\nimport { Badge, Button, Tag } from \"antd\";\r\nimport React from \"react\";\r\nimport { CheckPassedIcon } from \"src/pages/features/WeatherMonitorAntdOnly\";\r\n\r\n\r\nexport const IdleHelpText = () => {\r\n\r\n return <>\r\n \r\n When the bin is in Idle Mode, the system is inactive. This means the valves will be closed, the fans, heaters & compressor will be off. You will still be able to view up-to-date sensor data, but you will not be able to make any control changes.\r\n \r\n ;\r\n}\r\n\r\nexport const UserControlModeHelpText = () => {\r\n return <>\r\n \r\n When the bin enters User Control Mode everything will be off. You will be able to turn on & off the fans & heaters, & set the position of the valves. You will also be able to define automated setpoints to run the fans, heaters & valves.\r\n \r\n ;\r\n};\r\n\r\nexport const FillModeHelpText = () => {\r\n\r\n return <>\r\n \r\n When the bin is in Fill & Aerate Mode, you will be able to add loads as you’re filling the bin. Once grain is covering the first valve of the stacks, the fans will automatically turn on when the incoming air conditions are within the temp & EMC setpoints. The heaters will not turn on in this mode. The compressor will turn on to power the valves & the stacks will automatically direct air to the top layer of grain as the bin is being filled.\r\n \r\n ;\r\n}\r\n\r\nexport const DryModeHelpText = () => {\r\n\r\n return <>\r\n \r\n When the bin is in Dry Mode, your bin will act as a batch dryer. The fans will automatically turn on when the incoming air conditions are within your temp & EMC setpoints. The heater will run based on your settings. The compressor will turn on to power the valves. The stacks will automatically direct air throughout the bin; starting by targeting the top, then the core, then moving sequentially targeting each layer of the bin from top to bottom. You can set the relative amount of time that each layer will be targeted. You can also set the amount of time you would like the bin to dry & it will cycle through each layer until the timer reaches zero.\r\n \r\n ;\r\n}\r\n\r\nexport const StorageModeHelpText = () => {\r\n\r\n return <>\r\n \r\n When the bin is in Storage Mode, the fans will automatically run for a set number of hours each week while incoming air conditions are within the temp & EMC setpoints. You can change your weekly runtime in the fan timer settings. \r\n ;\r\n}\r\n\r\nexport const UnloadModeHelpText = () => {\r\n return <>\r\n \r\n When the bin is in Unload Mode everything will be off & you will be able to log load outs as you’re unloading the bin.\r\n \r\n ;\r\n}","import React from 'react';\r\nimport { UploadOutlined } from '@ant-design/icons';\r\nimport type { UploadProps } from 'antd';\r\nimport { Button, message, Upload } from 'antd';\r\nimport { useQueryClient } from '@tanstack/react-query';\r\nimport { binDBKeys } from '../binOverview/BinCommander';\r\nimport { NumberKeyframeTrack } from 'three';\r\n\r\ninterface UploadRoutineProps {\r\n binId: number;\r\n}\r\n\r\ninterface UploadSoftwareProps {\r\n softwareLookupId: number;\r\n}\r\n\r\nexport const UploadFile = (props:UploadSoftwareProps) => {\r\n const queryClient = useQueryClient();\r\n const uploadProps: UploadProps = { \r\n name: 'softwareFile',\r\n action: `/api/admin/uploadsoftwarefile?softwareLookupId=${props.softwareLookupId}`,\r\n // fileList: [],\r\n headers: {\r\n authorization: 'authorization-text',\r\n },\r\n onChange(info) {\r\n if (info.file.status !== 'uploading') {\r\n // uploadProps.name = info.file.name;\r\n console.log(info.file, info.fileList);\r\n }\r\n if (info.file.status === 'done') {\r\n message.success(`${info.file.name} file uploaded successfully`);\r\n } else if (info.file.status === 'error') {\r\n message.error(`${info.file.name} file upload failed.`);\r\n }\r\n queryClient.invalidateQueries([\"uploadsoftwarelookup\"]);\r\n },\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport const UploadRoutine = (props: UploadRoutineProps) => {\r\n const queryClient = useQueryClient();\r\n const uploadProps: UploadProps = { \r\n name: 'file',\r\n action: `/api/grainbin/UploadCustomRoutine?binId=${props.binId}`,\r\n // fileList: [],\r\n headers: {\r\n authorization: 'authorization-text',\r\n },\r\n onChange(info) {\r\n if (info.file.status !== 'uploading') {\r\n // uploadProps.name = info.file.name;\r\n console.log(info.file, info.fileList);\r\n }\r\n if (info.file.status === 'done') {\r\n message.success(`${info.file.name} file uploaded successfully`);\r\n } else if (info.file.status === 'error') {\r\n message.error(`${info.file.name} file upload failed.`);\r\n }\r\n queryClient.invalidateQueries([...binDBKeys.bin(props.binId), \"customRoutineList\"]);\r\n },\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport const UploadAndRunRoutine = (props: UploadRoutineProps) => {\r\n const queryClient = useQueryClient();\r\n const uploadProps: UploadProps = { \r\n name: 'file',\r\n action: `/api/grainbin/UploadAndRunCustomRoutine?binId=${props.binId}`,\r\n \r\n headers: {\r\n authorization: 'authorization-text',\r\n },\r\n onChange(info) {\r\n if (info.file.status !== 'uploading') {\r\n \r\n // uploadProps.name = info.file.name;\r\n console.log(info.file, info.fileList);\r\n }\r\n if (info.file.status === 'done') {\r\n message.success(`${info.file.name} file uploaded successfully`);\r\n } else if (info.file.status === 'error') {\r\n message.error(`${info.file.name} file upload failed.`);\r\n }\r\n queryClient.invalidateQueries([...binDBKeys.bin(props.binId), \"customRoutineList\"]);\r\n },\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n );\r\n};","import React, { useEffect, useState } from 'react';\r\nimport InfiniteScroll from 'react-infinite-scroll-component';\r\nimport { List, Divider, Skeleton, Modal, Button, message, notification, Card, Space, Typography } from 'antd';\r\nimport { CloseOutlined } from '@ant-design/icons';\r\nimport BinApiService from 'src/api/BinApiService';\r\nimport CustomRoutineNamesDTO from 'src/models/CustomRoutineNamesDTO';\r\nimport { binDBKeys } from '../binOverview/BinCommander';\r\nimport { useQuery, QueryKey } from '@tanstack/react-query';\r\nimport { queryClient } from 'src';\r\nimport { th } from '@faker-js/faker';\r\nimport { UploadRoutine } from './UploadFile';\r\n\r\ninterface RoutineListProps {\r\n binId: number;\r\n url: string;\r\n}\r\n\r\nexport const RoutineList = (props: RoutineListProps) => {\r\n const [selectedItem, setSelectedItem] = useState(null);\r\n const [stopRoutineModal, setStopRoutineModal] = useState(false);\r\n\r\n const showModal = (item: string) => {\r\n setSelectedItem(item);\r\n };\r\n\r\n const handleCloseModal = () => {\r\n setSelectedItem(null);\r\n };\r\n \r\n const handleCloseStopRoutineModal = () => {\r\n setStopRoutineModal(false);\r\n }\r\n\r\n const showStopRoutineModal = () =>{\r\n setStopRoutineModal(true);\r\n }\r\n \r\n const onYes = async (item: string) => {\r\n\r\n try{\r\n //todo logic to send request to run custom routine\r\n await BinApiService.runCustomRoutineByName(props.binId, item);\r\n queryClient.invalidateQueries([...binDBKeys.bin(props.binId), \"customRoutineList\" ]);\r\n handleCloseModal();\r\n }catch(err){\r\n handleCloseModal();\r\n console.error(\"custom routine failure:\", err);\r\n notification.error(\r\n {\r\n message: err?.title,\r\n description: err?.description\r\n }\r\n );\r\n throw err;\r\n }\r\n }\r\n\r\n const onNo = () => {\r\n handleCloseModal();\r\n }\r\n\r\n const onDeleteRoutine = async(item:string) => {\r\n try{\r\n await BinApiService.deleteCustomRoutine(props.binId, item);\r\n queryClient.invalidateQueries([...binDBKeys.bin(props.binId), \"customRoutineList\" ]);\r\n handleCloseModal();\r\n }catch(err){\r\n handleCloseModal();\r\n console.error(\"custom routine failure:\", err);\r\n notification.error(\r\n {\r\n message: err?.title,\r\n description: err?.detail ?? err?.message,\r\n });\r\n throw err;\r\n // throw err;\r\n }\r\n }\r\n\r\n const onStopRoutine = async() => {\r\n try{\r\n await BinApiService.stopCustomRoutine(props.binId);\r\n queryClient.invalidateQueries([...binDBKeys.bin(props.binId), \"customRoutineList\" ]);\r\n handleCloseStopRoutineModal();\r\n }catch(err){\r\n handleCloseStopRoutineModal();\r\n console.error(\"custom routine failure:\", err);\r\n notification.error(\r\n {\r\n message: err?.title,\r\n description: err?.detail ?? err?.message,\r\n });\r\n throw err;\r\n // throw err;\r\n }\r\n }\r\n\r\n const routineListQuery = useQuery({\r\n queryKey: [...binDBKeys.bin(props.binId), \"customRoutineList\" ],\r\n queryFn: async(q) =>{\r\n try{\r\n // return await BinApiService.getCustomRoutinesOnDisk(props.binId, q.signal);\r\n return await BinApiService.getCustomRoutinesOnDisk(props.binId);\r\n }catch(err){\r\n console.error(\"error fetching routine list\", err);\r\n notification.error(\r\n {\r\n message: err?.title,\r\n description: err?.detail ?? err?.message,\r\n });\r\n throw err;\r\n }\r\n }, \r\n })\r\n\r\n return (\r\n <>\r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n { }}\r\n hasMore={(routineListQuery.data?.customRoutineNames?.length ?? 0) < 50}\r\n // loader={}\r\n loader={null}\r\n endMessage={It is all, nothing more 🤐}\r\n scrollableTarget=\"scrollableDiv\"\r\n >\r\n {\r\n const itemIsSelected = item === routineListQuery.data?.activeRoutineFileName;\r\n return (\r\n showModal(item)} // Call showModal with the item when clicked\r\n // style={{ cursor: 'pointer', backgroundColor: item === }}\r\n style={{ cursor: 'pointer', backgroundColor: itemIsSelected ? '#002366' : undefined, borderRadius:10 }}\r\n >\r\n {item}}\r\n />\r\n \r\n );\r\n }}\r\n />\r\n \r\n\r\n\r\n \r\n {/* Content of the modal */}\r\n {selectedItem && (\r\n <>\r\n

What would you like to do with the following custom routine?

\r\n

{selectedItem}

\r\n \r\n \r\n \r\n \r\n \r\n \r\n )}\r\n \r\n\r\n setStopRoutineModal(false)}\r\n footer={null}\r\n >\r\n {/* Content of the modal */}\r\n {stopRoutineModal && (\r\n <>\r\n

Are you sure you want to stop the currently running routine?

\r\n \r\n \r\n \r\n \r\n\r\n \r\n )}\r\n \r\n \r\n
\r\n \r\n );\r\n};\r\n","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum RoutineType {\r\n Fill = 'Fill',\r\n PreDry = 'PreDry',\r\n Core = 'Core',\r\n MeasureMC = 'MeasureMC',\r\n ReStack = 'ReStack',\r\n CrossFlow = 'CrossFlow',\r\n Aeration = 'Aeration',\r\n ManualDry = 'ManualDry',\r\n SelfCheck = 'SelfCheck',\r\n Paused = 'Paused',\r\n TopDry = 'TopDry',\r\n Custom = 'Custom',\r\n Unknown = 'Unknown',\r\n}\r\nexport default RoutineType;\r\n","import Icon, { ArrowDownOutlined, ArrowLeftOutlined, ArrowRightOutlined, ArrowUpOutlined, RedoOutlined } from \"@ant-design/icons\";\r\nimport { Button, Card, Col, message, Row, Skeleton, Space, Badge } from \"antd\";\r\nimport Table, { ColumnType } from \"antd/es/table\";\r\nimport Column from \"antd/es/table/Column\";\r\nimport React, { useCallback, useEffect, useMemo, useState } from \"react\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport StackDTO from \"src/models/StackDTO\";\r\nimport { useBinStateFromAzure } from \"src/queries/useBinStateFromAzure\";\r\nimport { OperatingMode } from \"./BinVisualThree\";\r\n// @ts-ignore\r\nimport { useIntervalWhen } from 'rooks';\r\n//@ts-ignore\r\nimport ExhaustIconSVG from 'src/statics/exhaust.svg';\r\n//@ts-ignore\r\nimport ClosedIconSVG from 'src/statics/closed.svg';\r\n//@ts-ignore\r\nimport VentIconSVG from 'src/statics/vent.svg';\r\n//@ts-ignore\r\nimport FlowIconSVG from 'src/statics/flow.svg';\r\nimport ValveDTO from \"src/models/ValveDTO\";\r\nimport { useRequestNewBinStateMutation } from \"src/pages/binOverview/BinCommander\";\r\nimport ValvePositionDTO from \"src/models/ValvePositionDTO\";\r\nimport { useTurnOnCompressorMutation } from \"./CompressorControls\";\r\nimport { CheckFailedIcon, CheckPassedIcon } from \"src/pages/features/WeatherMonitorAntdOnly\";\r\n\r\nexport function ClosedIcon(props: any) {\r\n const style = props.style ?? { width: \"100%\", height: \"100%\" };\r\n const { width, height, ...restStyle } = style;\r\n return (\r\n \"Closed\"\r\n );\r\n}\r\n\r\nexport function FlowIcon(props: any) {\r\n const style = props.style ?? { width: \"100%\", height: \"100%\" };\r\n const { width, height, ...restStyle } = style;\r\n return (\r\n \"Flow\"\r\n );\r\n}\r\n\r\nexport function VentIcon(props: any) {\r\n const style = props.style ?? { width: \"100%\", height: \"100%\" };\r\n const { width, height, ...restStyle } = style;\r\n return (\r\n \"Vent\"\r\n );\r\n}\r\n\r\nexport function ExhaustIcon(props: any) {\r\n const style = props.style ?? { width: \"100%\", height: \"100%\" };\r\n const { width, height, ...restStyle } = style;\r\n return (\r\n \"Exhaust\"\r\n );\r\n}\r\n\r\n\r\nenum ValveState {\r\n CLOSED = \"Closed\",\r\n FLOW = \"Flow\",\r\n VENT = \"Vent\",\r\n EXHAUST = \"Exhaust\",\r\n}\r\nexport interface NewValveDisplayProps {\r\n binDTO: BinDTO;\r\n binId: number;\r\n azureDeviceId: string;\r\n allowEdits: boolean;\r\n valveDisplayInstance: ValveDisplayInstance\r\n}\r\n\r\nconst COMMON_CLOSE_DEFAULT_PERIOD_SECONDS = 15;\r\n\r\nconst computeValvesHashFromStacksData = (stacksData: StackDTO[]): string => {\r\n const valvesHash = stacksData.flatMap(stacks => stacks.valves).map(valve => valve?.id).join(\",\");\r\n return valvesHash;\r\n\r\n}\r\n\r\nconst calcExtraNeeded = (len: number, pageLimit: number) => {\r\n if (len <= pageLimit) {\r\n return pageLimit - len;\r\n }\r\n const remainder = len % pageLimit;\r\n return (pageLimit - remainder) % pageLimit;\r\n}\r\n\r\nfunction stacksToLayer(stacksArr: StackDTO[]) {\r\n const stacksData = stacksArr;\r\n const numStacks = stacksData?.length ?? 0;\r\n if (numStacks === 0) {\r\n return [];\r\n }\r\n\r\n const highestLayer = Math.max(...stacksData?.map(stack => stack?.valves?.length ?? 0));\r\n\r\n // @ts-ignore\r\n const layers: Partial }>[] = Array.from({ length: highestLayer },\r\n () => ({ valves: Array.from({ length: numStacks }).fill({ Id: null }) }));\r\n\r\n stacksData.forEach((stack, stackIndex) => {\r\n stack.valves!.forEach((layer, layerIndex) => {\r\n layers[layerIndex].layerId = layer.number;\r\n layers[layerIndex!].valves![stackIndex] = { ...layer, stackId: stack.id! };\r\n\r\n })\r\n }\r\n );\r\n return layers;\r\n}\r\n\r\nconst positionToText = (position: ValveState) => {\r\n\r\n if (position === ValveState.CLOSED) {\r\n return \"Close\";\r\n }\r\n else if (position === ValveState.EXHAUST) {\r\n return \"Exhau\";\r\n }\r\n\r\n else if (position === ValveState.FLOW) {\r\n return \"Flow\";\r\n }\r\n else if (position === ValveState.VENT) {\r\n return \"Vent\";\r\n }\r\n else {\r\n return \"-\";\r\n }\r\n};\r\n\r\nconst getIcon = ( position: ValveState) => {\r\n return {positionToText(position)}\r\n}\r\n\r\n\r\nconst flattenValveStates = (stacksData: StackDTO[]) => {\r\n if (stacksData?.length == null || stacksData == null) {\r\n return {};\r\n }\r\n if (stacksData?.length === 0) {\r\n return {};\r\n }\r\n\r\n const valves: Record = {};\r\n stacksData.forEach(stack => {\r\n stack.valves?.forEach(valve => {\r\n valves[valve.id!] = valve;\r\n })\r\n });\r\n return valves;\r\n\r\n}\r\n\r\ninterface ValveDisplayInstance {\r\n hasValvesToSubmit: boolean;\r\n desiredValveStates: Record;\r\n clearAllDesiredValves: (options?: {clearSelections: boolean, clearFailedValves: boolean}) => void;\r\n clearAllSelectedValves: () => void;\r\n setDesiredValveStates: React.Dispatch>>;\r\n selectedValves: Record;\r\n setSelectedValves: React.Dispatch>>;\r\n failedValves: Record;\r\n setFailedValves: React.Dispatch>>;\r\n checkForNewStackData: boolean\r\n setCheckForNewStackData: React.Dispatch>;\r\n isProcessingStackData: boolean;\r\n setIsProcessingStackData: React.Dispatch>;\r\n disableSubmission: boolean;\r\n}\r\nexport const useValveDisplay = (): ValveDisplayInstance => {\r\n\r\n const [desiredValveStates, setDesiredValveStates] = useState>({});\r\n const hasValvesToSubmit = useMemo(() => Object.keys(desiredValveStates).length > 0, [desiredValveStates]);\r\n const [selectedValves, setSelectedValves] = useState>({});\r\n const [failedValves, setFailedValves] = useState>({});\r\n\r\n const [checkForNewStackData, setCheckForNewStackData] = useState(false);\r\n const [isProcessingStackData, setIsProcessingStackData] = useState(false);\r\n\r\n const disableSubmission = checkForNewStackData || isProcessingStackData;\r\n\r\n const clearAllSelectedValves = useCallback(() => {\r\n setSelectedValves({});\r\n }, [setDesiredValveStates]);\r\n\r\n const clearAllDesiredValves = useCallback((options?: {clearSelections?: boolean, clearFailedValves?: boolean}) => {\r\n setDesiredValveStates({});\r\n if (options?.clearSelections) {\r\n clearAllSelectedValves();\r\n }\r\n if (options?.clearFailedValves) {\r\n setFailedValves({});\r\n }\r\n }, [setDesiredValveStates, clearAllSelectedValves, setFailedValves]);\r\n\r\n\r\n\r\n\r\n return useMemo(() => {\r\n return {\r\n desiredValveStates, setDesiredValveStates, hasValvesToSubmit, clearAllDesiredValves, clearAllSelectedValves, selectedValves, setSelectedValves,\r\n failedValves, setFailedValves,\r\n disableSubmission, checkForNewStackData, setCheckForNewStackData, isProcessingStackData, setIsProcessingStackData,\r\n };\r\n }, [desiredValveStates, setDesiredValveStates, hasValvesToSubmit, clearAllDesiredValves, clearAllSelectedValves, selectedValves, setSelectedValves, failedValves, setFailedValves,\r\n disableSubmission, checkForNewStackData, setCheckForNewStackData, isProcessingStackData, setIsProcessingStackData,\r\n ]);\r\n}\r\n\r\ninterface GrainCoveredDotProps{\r\n covered: boolean,\r\n inIcon: boolean\r\n}\r\nexport const GrainCoveredDot = (props:GrainCoveredDotProps) => {\r\n return
\r\n {props.covered ? : }\r\n
\r\n}\r\n\r\nexport const NewValveDisplay = (props: NewValveDisplayProps) => {\r\n\r\n const allStackData = props.binDTO?.stacks;\r\n const stacksData = allStackData ?? [];\r\n const layers = stacksToLayer(stacksData);\r\n const {desiredValveStates, hasValvesToSubmit, setDesiredValveStates, selectedValves, setSelectedValves, failedValves, setFailedValves,\r\n checkForNewStackData, disableSubmission, isProcessingStackData, setCheckForNewStackData, setIsProcessingStackData\r\n } = props.valveDisplayInstance ?? useValveDisplay();\r\n const numLayers = layers?.length ?? 0;\r\n const numStacks = stacksData?.length ?? 0;\r\n const [submittedConfigHash, setSubmittedValvesHash] = useState(null);\r\n const priorValveStates = useMemo(() => flattenValveStates(stacksData), [stacksData]);\r\n const [stackPage, setStackPage] = useState(0);\r\n const [layerPage, setLayerPage] = useState(0);\r\n const stacksPerPage = 6;\r\n\r\n const endStackPage = Math.max(Math.ceil(numStacks / stacksPerPage) - 1, 0);\r\n\r\n const layersPerPage = 3;\r\n const endLayerPage = Math.max(Math.ceil(numLayers / layersPerPage) - 1, 0);\r\n\r\n const [staleCaptureTime, setStaleCaptureTime] = useState(null);\r\n const [submittedValveStates, setSubmittedValveStates] = useState | null>(null);\r\n\r\n\r\n\r\n const DEADLINE_STACK_SHOULD_UPDATE_IN_MS = (15 + (props.binDTO?.desiredProperties?.overrides?.commonCloseTimePeriod ?? COMMON_CLOSE_DEFAULT_PERIOD_SECONDS)) * 1000;\r\n\r\n const allowChangingStacks = props.allowEdits;\r\n const requestNewBinStateMutation = useRequestNewBinStateMutation(props.azureDeviceId);\r\n const turnOnCompressorMutation = useTurnOnCompressorMutation(props.azureDeviceId);\r\n\r\n useIntervalWhen(\r\n async () => {\r\n\r\n try {\r\n await requestNewBinStateMutation.mutateAsync();\r\n } catch (err) {\r\n console.log(\"error requesting upload of new binState\", err);\r\n }\r\n // check stack data\r\n const currentDateTime = new Date();\r\n const currentTimestamp = currentDateTime.getTime();\r\n const captureTimeString = currentDateTime.toISOString();\r\n console.debug(`new stack data: ${captureTimeString}`, allStackData?.[0].valves?.[0].positionLabel);\r\n const timeWaitingForData = currentTimestamp - staleCaptureTime!;\r\n console.debug(`time elapsed: ${timeWaitingForData}, out of ${DEADLINE_STACK_SHOULD_UPDATE_IN_MS}`);\r\n const deadlineExceeded = timeWaitingForData > DEADLINE_STACK_SHOULD_UPDATE_IN_MS;\r\n\r\n const newValvesHash = computeValvesHashFromStacksData(stacksData);\r\n\r\n // if the stack layout changed after submitting, ask to resubmit a new config and verify existing results\r\n const valveConfigChanged = submittedConfigHash !== newValvesHash;\r\n if (valveConfigChanged) {\r\n console.debug(`stack layout changed., oldHash=${submittedConfigHash} newHash=${newValvesHash}`);\r\n // invalidate config hash submitted\r\n setSubmittedValvesHash(null);\r\n setFailedValves({});\r\n setCheckForNewStackData(false);\r\n message.warning(\"Stack layout has changed since submission. Verify current valve positions and resubmit\");\r\n return;\r\n }\r\n\r\n const failedValvesResult = {};\r\n\r\n for (const stack of stacksData) {\r\n for (const valve of stack.valves ?? []) {\r\n const submittedValve = submittedValveStates?.[valve.id!];\r\n if (submittedValve == null) {\r\n failedValvesResult[valve.id!] = { valveId: valve.id, position: null };\r\n continue;\r\n }\r\n\r\n const desiredPosition = submittedValve.position;\r\n const newPosition = valve.positionLabel;\r\n\r\n const valveUpdateSucceeded = newPosition === desiredPosition;\r\n\r\n if (!valveUpdateSucceeded) {\r\n failedValvesResult[valve.id!] = {\r\n stackId: stack.id, layerId: valve.number, valveId: valve.id,\r\n position: submittedValve.position\r\n };\r\n }\r\n }\r\n }\r\n\r\n // todo: Show ignored solendoids\r\n // try {\r\n // // remove failed valves due to being forced in a position by ignoreSolenoids\r\n // for (const ignoreSolenoid of allStackData?.IgnoredSolenoids ?? []) {\r\n // // get only the valveId portion\r\n // const ignoreId: string = ignoreSolenoid.SolenoidId as string;\r\n // const ignoredValveId = ignoreId.slice(0, 2);\r\n // delete failedValvesResult[ignoredValveId];\r\n // console.log(`Ignoring ignored valveId: ${ignoredValveId}`);\r\n // }\r\n // } catch (err) {\r\n // console.error(\"problem processing ignored valves\", err);\r\n // message.error(\"problem processing ignored valves, this shouldn't happen\");\r\n // };\r\n\r\n const allPass = Object.keys(failedValvesResult).length === 0;\r\n\r\n // todo: Ignore valve selection\r\n // let ignoredValves: Set = new Set();\r\n // try {\r\n // ignoredValves = new Set(allStackData?.IgnoredSolenoids?.map(sol => sol?.SolenoidId?.slice(0, 2)));\r\n // } catch (err) {\r\n // console.log(err);\r\n // message.error(\"problem processing ignored valves, this shouldn't happen\");\r\n // }\r\n\r\n let ignoreMsg = '';\r\n // if (ignoredValves.size > 0) {\r\n // ignoreMsg = ` - ignored valves: ${[...ignoredValves].join(', ')}`\r\n // }\r\n\r\n if (allPass) {\r\n console.debug(`success: all valves updated. Took ${timeWaitingForData}ms`);\r\n message.info(`Valves updated${ignoreMsg}`);\r\n setCheckForNewStackData(false);\r\n setSubmittedValvesHash(null);\r\n setFailedValves({});\r\n return;\r\n }\r\n\r\n if (!deadlineExceeded && !allPass) {\r\n console.debug(\"waiting for valves... : \", failedValvesResult);\r\n }\r\n\r\n if (deadlineExceeded && !allPass) {\r\n message.warning(\"SetValvePositions: Not all valves updated\");\r\n console.warn(\"valve update deadline exceeded: failed to update these valves: \", failedValvesResult);\r\n setCheckForNewStackData(false);\r\n setSubmittedValvesHash(null);\r\n setFailedValves(failedValvesResult);\r\n }\r\n\r\n },\r\n 3000, // run callback every 3 second\r\n checkForNewStackData, // start the timer when it's true\r\n );\r\n\r\n\r\n\r\n useIntervalWhen(() => {\r\n\r\n const numFailingValves = Object.keys(failedValves).length;\r\n if (numFailingValves === 0) {\r\n return;\r\n }\r\n\r\n const valvesThatStillFail = {};\r\n for (const stack of allStackData ?? []) {\r\n for (const valve of stack.valves ?? []) {\r\n const priorFailingValve = failedValves[valve.id!];\r\n if (priorFailingValve == null) {\r\n continue;\r\n }\r\n\r\n if (valve.positionLabel === priorFailingValve.position) {\r\n continue\r\n }\r\n\r\n valvesThatStillFail[valve.id!] = priorFailingValve;\r\n }\r\n\r\n\r\n\r\n }\r\n\r\n setFailedValves(valvesThatStillFail);\r\n\r\n }, 3000, Object.keys(failedValves).length > 0);\r\n console.log(\"the stacks data\", stacksData);\r\n\r\n if (stacksData?.length <= 0) {\r\n \r\n }\r\n\r\n\r\n const lookupPositionToShow = (valveId: string, originalPosition: ValveState): ValveState => {\r\n const maybeChanged = desiredValveStates[valveId];\r\n if (maybeChanged == null) {\r\n return originalPosition;\r\n }\r\n const desiredPosition = maybeChanged.position;\r\n return desiredPosition;\r\n }\r\n\r\n const valveFailed = (valveId: string, currentPosition: ValveState): boolean => {\r\n const maybeFailed = failedValves[valveId];\r\n if (maybeFailed == null) {\r\n return false;\r\n }\r\n else {\r\n return true;\r\n }\r\n }\r\n\r\n const handleValveClick = (valve: ValveDTO) => {\r\n const valveId = valve?.id;\r\n // Empty cell\r\n if (valveId == null) {\r\n return;\r\n }\r\n\r\n const alreadySelected = selectedValves.hasOwnProperty(valveId);\r\n if (alreadySelected) {\r\n // remove from selected\r\n const { [valveId]: excluded, ...rest } = selectedValves;\r\n setSelectedValves({ ...rest });\r\n console.debug(`Deselected valve: ${valveId}`);\r\n }\r\n else {\r\n setSelectedValves({ ...selectedValves, [valveId]: valve })\r\n console.debug(`set selected valve: ${valveId}`);\r\n }\r\n }\r\n\r\n const handleStackSelect = (event: any, stackId: string) => {\r\n const stack = stacksData.find(stack => stack.id === stackId);\r\n if (stack == null) {\r\n return;\r\n }\r\n const valves = [...stack?.valves ?? []];\r\n const newValves = {};\r\n for (const valve of valves) {\r\n newValves[valve.id!] = valve;\r\n }\r\n const valveIds = valves.map(valve => valve.id!);\r\n\r\n const foundExistingSelection = Object.keys(selectedValves).find(valveId => valveId.startsWith(stackId));\r\n console.debug(`requested to ${foundExistingSelection ? \"Deselect\" : \"Select\"} all of stack ${stackId}`);\r\n\r\n if (foundExistingSelection) {\r\n // deSelect all valves from this stack\r\n const newSelectedValves = { ...selectedValves };\r\n for (const valveId of valveIds) {\r\n delete newSelectedValves[valveId];\r\n }\r\n\r\n setSelectedValves(newSelectedValves);\r\n }\r\n else {\r\n // add all valves\r\n setSelectedValves({ ...selectedValves, ...newValves });\r\n }\r\n\r\n }\r\n\r\n const handleQueueActions = (position: ValveState) => {\r\n\r\n console.log(`queue all selected valves to ${position}`);\r\n\r\n const changes = {};\r\n Object.keys(selectedValves)\r\n .forEach(valveId => {\r\n changes[valveId] = { position: position };\r\n }\r\n );\r\n\r\n setDesiredValveStates({ ...desiredValveStates, ...changes });\r\n setSelectedValves({});\r\n }\r\n\r\n const handleLayerSelect = (layerId: number) => {\r\n const layerAlreadySelected = Object.values(selectedValves).find(valve => valve.number === layerId);\r\n\r\n console.debug(`requested to ${layerAlreadySelected ? \"DeSelected\" : \"Select\"} all of layer ${layerId}`);\r\n const layer = layers.find(layer => layer.layerId === layerId);\r\n\r\n if (layer == null) {\r\n return;\r\n }\r\n\r\n const valves = layer.valves;\r\n\r\n const newValves = {};\r\n for (const valve of valves ?? []) {\r\n if (valve.id == null) {\r\n continue;\r\n }\r\n newValves[valve.id!] = valve;\r\n }\r\n\r\n const valveIds = valves?.map(valve => valve.id).filter(id => id != null) ?? [];\r\n\r\n if (!layerAlreadySelected) {\r\n // add all valves\r\n setSelectedValves({ ...selectedValves, ...newValves });\r\n }\r\n else {\r\n // deSelect all valves from this layer\r\n const newSelectedValves = { ...selectedValves };\r\n for (const valveId of valveIds) {\r\n delete newSelectedValves[valveId!];\r\n }\r\n\r\n setSelectedValves(newSelectedValves);\r\n }\r\n }\r\n\r\n const handleStackIncrement = () => {\r\n if (stackPage >= endStackPage) {\r\n return;\r\n }\r\n\r\n setStackPage(page => page + 1);\r\n }\r\n\r\n\r\n const handleStackDecrement = () => {\r\n if (stackPage <= 0) {\r\n return;\r\n }\r\n\r\n setStackPage(page => page - 1);\r\n }\r\n\r\n const handleLayerIncrement = () => {\r\n if (layerPage >= endLayerPage) {\r\n return;\r\n }\r\n\r\n setLayerPage(page => page + 1);\r\n }\r\n\r\n const handleLayerDecrement = () => {\r\n if (layerPage <= 0) {\r\n return;\r\n }\r\n\r\n setLayerPage(page => page - 1);\r\n }\r\n\r\n const submitNewValvesState = async () => {\r\n // turn on compressor\r\n\r\n let compressorRequestSucceeded = false;\r\n try {\r\n const result = await turnOnCompressorMutation.mutateAsync();\r\n if (result.success === true) {\r\n compressorRequestSucceeded = true;\r\n }\r\n else {\r\n message.error(\"Problem turning on compressor\");\r\n }\r\n } catch (error) {\r\n console.log(\"error turning on compressor\", error);\r\n message.error(`Error turning on compressor`);\r\n }\r\n if (!compressorRequestSucceeded) {\r\n return;\r\n }\r\n\r\n let merged: Record = {};\r\n\r\n // use prior valve states as defaults to send\r\n for (const [valveId, valve] of Object.entries(priorValveStates)) {\r\n merged[valveId] = { valveId: valveId, position: valve?.positionLabel! };\r\n }\r\n\r\n // override with desired changes\r\n for (const [valveId, valve] of Object.entries(desiredValveStates)) {\r\n merged[valveId] = { valveId: valveId, position: valve.position };\r\n }\r\n\r\n const valvePositions = Object.values(merged);\r\n\r\n const valvesHash = computeValvesHashFromStacksData(stacksData);\r\n console.debug(`setting config hash submission to, configHash=${valvesHash}`);\r\n setSubmittedValvesHash(valvesHash);\r\n setSubmittedValveStates(merged);\r\n\r\n\r\n let errorMsg: string | null | object = null;\r\n\r\n const lastCaptureDate = new Date();\r\n const captureTimeString = lastCaptureDate.toISOString();\r\n\r\n console.debug(`submitting new States @ ${captureTimeString}`);\r\n setIsProcessingStackData(true);\r\n setDesiredValveStates({});\r\n setSelectedValves({});\r\n\r\n\r\n try {\r\n const result = await BinApiService.setValvePositions(props.azureDeviceId, valvePositions);\r\n setIsProcessingStackData(false);\r\n if (result === true) {\r\n const statesSubmittedAt = new Date();\r\n setStaleCaptureTime(statesSubmittedAt.getTime());\r\n setCheckForNewStackData(true);\r\n console.debug(`submitted new States @ ${statesSubmittedAt.toISOString()}`);\r\n } else {\r\n\r\n }\r\n\r\n }\r\n catch (err) {\r\n console.log(err);\r\n errorMsg = 'Failed to send valves change request';\r\n setIsProcessingStackData(false);\r\n }\r\n if (errorMsg) {\r\n message.error(errorMsg);\r\n }\r\n }\r\n\r\n const failedLayerPageDirections = (): { before: boolean, current: boolean, after: boolean } => {\r\n const failedLayerIds = Object.values(failedValves).map(valve => valve.layerId);\r\n if (failedLayerIds.length === 0) {\r\n return { before: false, current: false, after: false };\r\n }\r\n\r\n let presentBefore = false;\r\n let presentCurrent = false;\r\n let presentAfter = false;\r\n\r\n\r\n let priorLayerPageheaders = new Set();\r\n if (layerPage === 0) {\r\n priorLayerPageheaders = new Set();\r\n }\r\n else {\r\n const _priorLayerSlice = layerHeaders;\r\n priorLayerPageheaders = new Set(_priorLayerSlice);\r\n }\r\n const currentLayerPageHeaders = new Set(layerHeaders);\r\n\r\n const nextLayerPageheaders = new Set(layerHeaders);\r\n\r\n for (const valveLayer of failedLayerIds) {\r\n if (priorLayerPageheaders.has(valveLayer)) {\r\n presentBefore = true;\r\n }\r\n if (currentLayerPageHeaders.has(valveLayer)) {\r\n presentCurrent = true;\r\n }\r\n if (nextLayerPageheaders.has(valveLayer)) {\r\n presentAfter = true;\r\n }\r\n\r\n }\r\n\r\n return { before: presentBefore, current: presentCurrent, after: presentAfter };\r\n\r\n\r\n }\r\n\r\n const failedStackPageDirections = (): { before: boolean, current: boolean, after: boolean } => {\r\n\r\n const failedStackIds = Object.values(failedValves).map(valve => valve.stackId);\r\n if (failedStackIds.length === 0) {\r\n return { before: false, current: false, after: false };\r\n }\r\n\r\n\r\n let priorStacksPageheaders = new Set();\r\n if (stackPage === 0) {\r\n priorStacksPageheaders = new Set();\r\n }\r\n else {\r\n const _priorStackSlice = stackHeaders;\r\n priorStacksPageheaders = new Set(_priorStackSlice);\r\n }\r\n const currentStacksPageHeaders = new Set(stackHeaders);\r\n\r\n const nextStacksPageheaders = new Set(stackHeaders);\r\n\r\n let presentBefore = false;\r\n let presentCurrent = false;\r\n let presentAfter = false;\r\n\r\n for (const valveStack of failedStackIds) {\r\n if (priorStacksPageheaders.has(valveStack)) {\r\n presentBefore = true;\r\n }\r\n if (currentStacksPageHeaders.has(valveStack)) {\r\n presentCurrent = true;\r\n }\r\n if (nextStacksPageheaders.has(valveStack)) {\r\n presentAfter = true;\r\n }\r\n\r\n }\r\n\r\n return { before: presentBefore, current: presentCurrent, after: presentAfter };\r\n\r\n }\r\n\r\n const stackHeaders = stacksData.map(stack => stack.id);\r\n const _uniqueLayers = new Set(stacksData.flatMap(stack => stack.valves).map(valve => valve?.number))\r\n const layerHeaders = Array.from(_uniqueLayers);\r\n\r\n const numFillerStacks = calcExtraNeeded(stacksData.length, stacksPerPage);\r\n const needFillerStacks = stackPage === endStackPage;\r\n const numFillerLayers = calcExtraNeeded(layers.length, layersPerPage);\r\n const needFillerLayers = layerPage === endLayerPage;\r\n\r\n const fillerLayers = Array.from({ length: numFillerLayers }).fill(null).map((empty, index) => {\r\n return \r\n \r\n\r\n \r\n \r\n });\r\n\r\n const isValveCovered = (valve: ValveDTO) => {\r\n const layers = props.binDTO?.layers ?? [];\r\n for (const layer of layers) {\r\n if (valve.number === (layer.number) && layer.hasGrain) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n return <>\r\n \r\n \r\n {isProcessingStackData && <>\r\n

Submitting stack changes...

\r\n }\r\n\r\n {checkForNewStackData && <>\r\n \r\n

Applying Valve Changes

\r\n\r\n
\r\n }\r\n\r\n\r\n {!checkForNewStackData && !isProcessingStackData && <>\r\n
\r\n \r\n\r\n\r\n \r\n \r\n \r\n {stacksData?.map((stack, i) => {\r\n return \r\n })}\r\n {needFillerStacks && Array.from({ length: numFillerStacks }).fill(null).map((empty, index) => )\r\n }\r\n \r\n \r\n\r\n \r\n {needFillerLayers && fillerLayers}\r\n {[...layers].reverse().map((layer) => {\r\n const layerSelector = \r\n\r\n const valveCells = layer?.valves?.map((valve, valveIndex) => {\r\n const hasValve = valve.id != null;\r\n const covered = isValveCovered(valve);\r\n const hasValveStyle = hasValve ? \"valve\" : \"\";\r\n const isSelected = selectedValves.hasOwnProperty(valve.id!);\r\n const selectedStyle = isSelected ? \"valveSelected\" : \"\";\r\n\r\n const failed = valveFailed(valve.id!, valve.positionLabel as ValveState);\r\n const failedStyle = failed ? \"valveFailed\" : \"\";\r\n let badgeColor = covered ? \"green\": \"red\";\r\n const positionStyle = lookupPositionToShow(valve.id!, valve.positionLabel as ValveState);\r\n const disabledSelectValveStyle = !props.allowEdits ? 'valveDisabled' : '';\r\n\r\n return \r\n });\r\n\r\n return \r\n\r\n {layerSelector}\r\n {valveCells}\r\n \r\n })\r\n }\r\n\r\n \r\n\r\n
\r\n \r\n \r\n
\r\n \r\n handleValveClick(valve)}\r\n >\r\n {hasValve && getIcon(lookupPositionToShow(valve.id!, valve?.positionLabel as ValveState))}\r\n {hasValve && }\r\n
\r\n
\r\n\r\n {allowChangingStacks &&
\r\n \r\n \r\n \r\n \r\n\r\n
\r\n \r\n \r\n
}\r\n \r\n }\r\n\r\n \r\n \r\n ;\r\n}","import { useMutation, useQueryClient } from \"@tanstack/react-query\";\r\nimport { Button, Card, Col, Flex, Form, message, Popconfirm, Radio, Row, Space, Tag, theme, Tooltip, Typography } from \"antd\";\r\nimport { useForm, useWatch } from \"antd/es/form/Form\";\r\nimport React, { useEffect, useState } from \"react\";\r\nimport { ApiError } from \"src/api/ApiResultHandler\";\r\nimport BinApiService from 'src/api/BinApiService';\r\nimport RoutineType from \"src/consts/RoutineType\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport StackDTO from \"src/models/StackDTO\";\r\nimport { ObjectColorInfo, OperatingMode } from \"./BinVisualThree\";\r\nimport { GrainLayerSummaryInfo, LayerFormat, UserGrainHeights } from \"./DryLayerAdjust\";\r\nimport RoleUtil from \"src/utils/RoleUtil\";\r\nimport Role from \"src/consts/Role\";\r\nimport ValvePositionDTO from \"src/models/ValvePositionDTO\";\r\nimport _ from \"lodash\";\r\nimport { deviceQueryKeys } from \"../../../components/BinDetails\";\r\nimport { RedoOutlined } from \"@ant-design/icons\";\r\nimport { NewValveDisplay, useValveDisplay } from \"./NewValveDisplay\";\r\nimport { GrainCoveredDot } from \"./NewValveDisplay\";\r\nimport { useTurnOnCompressorMutation } from \"./CompressorControls\";\r\nimport { usePermissions } from \"src/pages/features/usePermissions\";\r\n\r\nexport const useRunManaulLayerMutation = (deviceId: string) => {\r\n\r\n return useMutation({\r\n mutationFn: (variables: { targetLayer: number }) => {\r\n return BinApiService.runManualLayer(deviceId, variables.targetLayer);\r\n }\r\n });\r\n}\r\n\r\nexport const useRunCoreManualRoutineMutation = (deviceId: string) => {\r\n\r\n return useMutation({\r\n mutationFn: async () => {\r\n return await BinApiService.runCoreManualRoutine(deviceId);\r\n }\r\n });\r\n}\r\n\r\n\r\nexport const useCloseAllValvesAndClearRoutinesMutation = (deviceId: string) => {\r\n\r\n return useMutation({\r\n mutationFn: () => {\r\n return BinApiService.closeAllValvesAndClearRoutines(deviceId);\r\n }\r\n });\r\n}\r\n\r\ninterface ManualRoutineContolProps {\r\n updateValves?: (updates: StackDTO[]) => void;\r\n highlightArr: ObjectColorInfo[];\r\n highlightStack: (stackId: string) => void;\r\n deviceId: string,\r\n binDTO: BinDTO,\r\n binId: number,\r\n}\r\n\r\nenum ManualRoutineVariations {\r\n Closed = \"Closed\",\r\n Individual = \"Individual\",\r\n Core = \"Core\",\r\n TargetLayer = \"TargetLayer\",\r\n Custom = \"Custom\",\r\n}\r\n\r\nconst StatusText = (props: { manualRoutineType: ManualRoutineVariations | null, binDTO: BinDTO }) => {\r\n if (props.manualRoutineType === ManualRoutineVariations.Closed) {\r\n return Status: Valves Closed;\r\n }\r\n if (props.manualRoutineType === ManualRoutineVariations.Core) {\r\n return Status: Targeting the Core \r\n {/* < br /> Layer: {props.binDTO?.routineEngine?.targetLayer ?? } */}\r\n \r\n }\r\n if (props.manualRoutineType === ManualRoutineVariations.Individual) {\r\n return Status: Individual Valves\r\n }\r\n if (props.manualRoutineType === ManualRoutineVariations.TargetLayer) {\r\n return Status: Targeting layer {props.binDTO?.routineEngine?.targetLayer ?? }\r\n }\r\n if (props.binDTO?.desiredProperties?.isSeedBin) {\r\n return Status: Custom Routine
Name: {props.binDTO?.currentRoutineName}
\r\n }\r\n return null;\r\n}\r\n\r\nexport const useSetValvesMutation = (deviceId: string) => {\r\n const queryClient = useQueryClient();\r\n\r\n return useMutation({\r\n mutationFn: async (variables: { desiredValveChanges: Array }) => {\r\n const valvePositionUpdates = _.flatten(variables.desiredValveChanges\r\n .map(x => x.valves!.map(y => ({ valveId: y.id, position: y.positionLabel })))) as ValvePositionDTO[];\r\n const result = await BinApiService.setValvePositions(deviceId, valvePositionUpdates);\r\n return result;\r\n },\r\n onSettled: async (data, error, variables, context) => {\r\n await queryClient.invalidateQueries(deviceQueryKeys.stateFromAzure(deviceId));\r\n await queryClient.invalidateQueries(deviceQueryKeys.stateFromDevice(deviceId));\r\n },\r\n })\r\n}\r\n\r\nexport const ManualRoutineControl = (props: ManualRoutineContolProps) => {\r\n const queryClient = useQueryClient();\r\n const binDTO = props.binDTO;\r\n const [formIteration, setFormIteration] = useState(0);\r\n const { token } = theme.useToken();\r\n const [form] = useForm();\r\n const permissions = usePermissions();\r\n\r\n const setValvesMutation = useSetValvesMutation(props.deviceId);\r\n\r\n const [openCloseAllValvesConfirm, setOpenCloseAllValvesConfirm] = useState(false);\r\n const [openTargetCoreConfirm, setOpenTargetCoreConfirm] = useState(false);\r\n const [openTargetCoreTooltip, setOpenTargetCoreTooltip] = useState(false);\r\n\r\n const updateValves = async (desiredValveChanges: StackDTO[]) => {\r\n\r\n setValvesMutation.mutate({ desiredValveChanges }, {\r\n onSuccess(data, variables, context) {\r\n if (data === true) {\r\n message.success('Valve configuration changed. Watch Refresh button for update.');\r\n }\r\n else if (data === false) {\r\n message.error('Failed to update valve configuration in all/part. Make sure system is in User Control mode');\r\n }\r\n else {\r\n console.error(\"Unexpected valve response gotten: \", data);\r\n message.error(\"valve update failed with unknown response: contact support\");\r\n }\r\n },\r\n onError(error, variables, context) {\r\n console.error(\"error updating valve configuration\", error);\r\n message.error('Failed to update valve configuration.');\r\n\r\n },\r\n });\r\n }\r\n\r\n const currentValues = (binDTO: BinDTO) => {\r\n if (binDTO?.desiredProperties?.isSeedBin && binDTO.routineEngine?.routine === RoutineType.Custom) {\r\n return ManualRoutineVariations.Custom;\r\n }\r\n if (binDTO.routineEngine?.anyStaticValvePositions === false && binDTO?.manualRoutineSettings?.isManualRoutineSet === false) {\r\n return ManualRoutineVariations.Closed;\r\n }\r\n if (binDTO.manualRoutineSettings?.isManualRoutineSet && binDTO?.manualRoutineSettings?.targetLayer) {\r\n return ManualRoutineVariations.TargetLayer;\r\n }\r\n const manualRoutineType = binDTO.manualRoutineSettings?.type;\r\n if (binDTO.manualRoutineSettings?.isManualRoutineSet && [RoutineType.Core].includes(manualRoutineType!)) {\r\n return ManualRoutineVariations.Core;\r\n }\r\n if (binDTO?.routineEngine?.anyStaticValvePositions) {\r\n return ManualRoutineVariations.Individual;\r\n }\r\n return null;\r\n }\r\n const routineValue = currentValues(props.binDTO);\r\n\r\n useEffect(() => {\r\n if (form.isFieldsTouched()) {\r\n return;\r\n }\r\n const routineValue = currentValues(props.binDTO);\r\n const targetLayer = props.binDTO?.manualRoutineSettings?.targetLayer;\r\n form.setFields([{ name: 'routineSetting', touched: false, value: routineValue },\r\n { name: \"targetLayer\", touched: false, value: targetLayer },\r\n ]);\r\n setFormIteration(val => val + 1);\r\n }, [props.binDTO, form]);\r\n\r\n const manualRoutineSetting = useWatch([], form);\r\n\r\n const targetLayer = props.binDTO?.routineEngine?.targetLayer;\r\n const closeAllValvesMutation = useCloseAllValvesAndClearRoutinesMutation(props.deviceId);\r\n const runCoreManualRoutineMutation = useRunCoreManualRoutineMutation(props.deviceId);\r\n const runManaulLayerMutation = useRunManaulLayerMutation(props.deviceId);\r\n const turnOnCompressorMutation = useTurnOnCompressorMutation(props.deviceId);\r\n\r\n const inManualMode = props.binDTO?.operatingMode === OperatingMode.Manual;\r\n const isSeedBin = props.binDTO?.desiredProperties?.isSeedBin === true;\r\n\r\n\r\n const allowValveEdits = () => {\r\n if (!permissions.canWrite) {\r\n return false;\r\n }\r\n\r\n if (setValvesMutation.isLoading) {\r\n return false;\r\n }\r\n\r\n if (props.binDTO?.desiredProperties?.isSeedBin === true) {\r\n return true;\r\n }\r\n\r\n if (manualRoutineSetting?.routineSetting != ManualRoutineVariations.Individual) {\r\n return false;\r\n }\r\n return true;\r\n }\r\n\r\n const showDesiredValveSelection = (): boolean => {\r\n if (props.binDTO?.desiredProperties?.isSeedBin === true) {\r\n return true;\r\n }\r\n if (manualRoutineSetting?.routineSetting === ManualRoutineVariations.Individual) {\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n const showIndividualValveView = (): boolean => {\r\n const isAdmin = RoleUtil.currentUserHasAnyOfRoles([Role.ADMIN]);\r\n if (isAdmin) {\r\n return true;\r\n }\r\n if (!inManualMode) {\r\n return true;\r\n }\r\n if (manualRoutineSetting?.routineSetting != ManualRoutineVariations.Individual) {\r\n return false;\r\n }\r\n return true;\r\n }\r\n\r\n const submitTargetLayer = async () => {\r\n\r\n\r\n let targetLayer = Number.parseInt(form.getFieldValue('targetLayer'), 10);\r\n\r\n let compressorRequestSucceeded = false;\r\n try {\r\n const result = await turnOnCompressorMutation.mutateAsync();\r\n if (result.success === true) {\r\n compressorRequestSucceeded = true;\r\n }\r\n else {\r\n message.error(\"Problem turning on compressor\");\r\n }\r\n } catch (error) {\r\n console.log(\"error turning on compressor\", error);\r\n message.error(`Error turning on compressor`);\r\n }\r\n if (!compressorRequestSucceeded) {\r\n return;\r\n }\r\n\r\n runManaulLayerMutation.mutate({ targetLayer: targetLayer, },\r\n {\r\n onSuccess(data, variables, context) {\r\n setFormIteration(val => val + 1);\r\n if (data.success) {\r\n message.success(`Targeting layer ${variables.targetLayer}`);\r\n form.setFields([{ name: 'targetLayer', touched: false }]);\r\n }\r\n else {\r\n message.error(`Problem targeting layer ${variables.targetLayer}`);\r\n }\r\n },\r\n onError(error, variables, context) {\r\n console.error(\"error setting layer target\", error, { error, variables });\r\n message.error(`Error setting layer target to ${variables.targetLayer}`);\r\n },\r\n }\r\n );\r\n }\r\n\r\n const valveDisplayInstance = useValveDisplay();\r\n\r\n return (\r\n \r\n
\r\n {props.binDTO?.operatingMode === OperatingMode.Manual && \r\n \r\n \r\n {\r\n valveDisplayInstance.clearAllDesiredValves({ clearSelections: true, clearFailedValves: true });\r\n // close the popup that isn't part of the selection\r\n if (event.target.value !== ManualRoutineVariations.Closed) {\r\n setOpenCloseAllValvesConfirm(false);\r\n }\r\n if (event.target.value !== ManualRoutineVariations.Core) {\r\n setOpenTargetCoreConfirm(false);\r\n setOpenTargetCoreTooltip(false);\r\n }\r\n\r\n // close all tooltips\r\n }}>\r\n {\r\n // ignored to force pressing either cancel or close (clicking outside for some reason doesn't count as cancelling...)\r\n }}\r\n onCancel={() => {\r\n setFormIteration(count => count + 1);\r\n form.resetFields();\r\n setOpenCloseAllValvesConfirm(false);\r\n }}\r\n onConfirm={() => {\r\n setOpenCloseAllValvesConfirm(false);\r\n closeAllValvesMutation.mutate(undefined, {\r\n onSuccess(data, variables, context) {\r\n if (data.success) {\r\n message.info(\"Valves are set to close\");\r\n } else {\r\n message.error(`Problem requesting all valves to close`);\r\n }\r\n },\r\n onError(error, variables, context) {\r\n console.error(\"error requesting all valves and routine to close/clear\", error);\r\n const errorUserText = \"Error requesting all valves to close\";\r\n if (error instanceof ApiError) {\r\n console.error(\"Error requesting all valves to close\", error);\r\n }\r\n else {\r\n message.error(errorUserText);\r\n }\r\n\r\n },\r\n });\r\n }}\r\n >\r\n {\r\n // close the other popups\r\n setOpenTargetCoreConfirm(false);\r\n setOpenTargetCoreTooltip(false);\r\n\r\n setOpenCloseAllValvesConfirm(true);\r\n }} disabled={closeAllValvesMutation.isLoading || !permissions.canWrite} value={ManualRoutineVariations.Closed}>Closed\r\n \r\n \r\n Individual Valves\r\n \r\n {\r\n setOpenTargetCoreTooltip(newVisible);\r\n })} >\r\n {\r\n // ignored to force pressing either cancel or close (clicking outside for some reason doesn't count as cancelling...)\r\n // there has to be a way to track clicking outside the popup....\r\n }}\r\n onCancel={() => {\r\n setFormIteration(count => count + 1);\r\n form.resetFields();\r\n // force the tooltip closed if it was going to be open\r\n setOpenTargetCoreTooltip(false);\r\n setOpenTargetCoreConfirm(false);\r\n }}\r\n onConfirm={async () => {\r\n // force the tooltip closed if it was going to be open\r\n setOpenTargetCoreTooltip(false);\r\n setOpenTargetCoreConfirm(false);\r\n\r\n let compressorRequestSucceeded = false;\r\n try {\r\n const result = await turnOnCompressorMutation.mutateAsync();\r\n if (result.success === true) {\r\n compressorRequestSucceeded = true;\r\n }\r\n else {\r\n message.error(\"Problem turning on compressor\");\r\n }\r\n } catch (error) {\r\n console.log(\"error turning on compressor\", error);\r\n message.error(`Error turning on compressor`);\r\n }\r\n if (!compressorRequestSucceeded) {\r\n return;\r\n }\r\n\r\n runCoreManualRoutineMutation.mutate(undefined, {\r\n onSuccess(data, variables, context) {\r\n if (data.success) {\r\n message.info('Core routine set to run');\r\n } else {\r\n message.error(\"Problem requesting Core routine to run\");\r\n }\r\n },\r\n onError(error, variables, context) {\r\n console.error(\"Error requesting core routine to run\", error);\r\n message.error(\"Errir requesting Core routine to run\");\r\n },\r\n });\r\n }}\r\n >\r\n {\r\n // close the other popups\r\n setOpenCloseAllValvesConfirm(false);\r\n\r\n setOpenTargetCoreConfirm(true);\r\n }} disabled={runCoreManualRoutineMutation.isLoading || isSeedBin || turnOnCompressorMutation.isLoading || !permissions.canWrite} value={ManualRoutineVariations.Core}>Target the Core\r\n \r\n \r\n \r\n Target by Layer\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n }\r\n\r\n {manualRoutineSetting?.routineSetting === ManualRoutineVariations.Core && <>\r\n Valves are configured to direct airflow to the bin core.\r\n }\r\n\r\n {manualRoutineSetting?.routineSetting === ManualRoutineVariations.Individual && <>\r\n The compressor will automatically turn on to control the valves.\r\n }\r\n\r\n \r\n {manualRoutineSetting?.routineSetting == ManualRoutineVariations.TargetLayer &&
\r\n \r\n Layer Target Settings\r\n
\r\n \r\n
\r\n
\r\n Select a layer to target with airflow. The compressor will automatically turn on to control the valves.\r\n
\r\n {[...props.binDTO?.layers ?? []].reverse().map(layer => {\r\n\r\n const targetlayerSelected = manualRoutineSetting?.targetLayer === layer.number;\r\n const submitted = !form.isFieldTouched('targetLayer');\r\n\r\n return ( {\r\n if (!permissions.canWrite) {\r\n return;\r\n }\r\n form.setFields([{ name: 'targetLayer', touched: true, value: layer.number }])\r\n }}>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {targetlayerSelected && !submitted && permissions.canWrite && Click Apply}\r\n \r\n \r\n );\r\n })}\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n
\r\n }\r\n \r\n \r\n
\r\n\r\n {showIndividualValveView() && (\r\n \r\n \r\n \r\n Current Valve Positions {!allowValveEdits() ? \"(View only)\" : \"\"}\r\n {valveDisplayInstance.hasValvesToSubmit ? (\r\n (Not Applied)\r\n ) : null}\r\n \r\n \r\n {}\r\n Covered\r\n {}\r\n Not Covered\r\n \r\n \r\n \r\n \r\n )}\r\n\r\n \r\n );\r\n}","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\r\nimport { TooltipItem } from \"chart.js\";\r\nimport { AnnotationOptions } from \"chartjs-plugin-annotation\";\r\nimport { round, uniqueId } from \"lodash-es\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { useMemo } from \"react\";\r\nimport FanOffReason from \"src/consts/FanOffReason\";\r\nimport HeaterOffReason from \"src/consts/HeaterOffReason\";\r\nimport { Dayjs } from \"dayjs\";\r\nimport FanHeaterChangeTimeDatapointDTO from \"src/models/FanHeaterChangeTimeDatapointDTO\";\r\nimport { queryOptions } from \"src/queries/v4QueryOptions/queryOptions\";\r\n\r\nexport const compressorColor = \"#583381\";\r\nexport const fanStatusColor = compressorColor\r\nexport const heaterStatusColor = \"#8f51cc\";\r\n\r\n\r\n\r\nexport const tooltipItemRound = (tooltipItem: TooltipItem<'line'>) => {\r\n let label = tooltipItem.dataset.label ?? \"\";\r\n\r\n if (label) {\r\n label += ': ';\r\n }\r\n\r\n if (tooltipItem.dataset.label?.includes(\"Status\")) {\r\n\r\n if (typeof (tooltipItem.raw as any)?.y === 'string') {\r\n\r\n // assumed category\r\n label += (tooltipItem.raw as any)?.y;\r\n return label;\r\n }\r\n }\r\n\r\n label += round(tooltipItem.parsed.y, 1)?.toFixed(1);\r\n return label;\r\n }\r\n\r\n\r\n\r\n\r\n enum FanState {\r\n Off,\r\n On,\r\n Paused\r\n }\r\n \r\n enum HeaterState {\r\n Off,\r\n On,\r\n Paused,\r\n }\r\n\r\n export const fanOnBackgroundColor = 'rgb(196, 255, 191)';\r\n export const FanPausedBackgroundColor = 'rgb(245, 245, 245)';\r\n \r\n export const heaterOnBackgroundColor = '#fad9bb';\r\n \r\n interface FanHeaterAnnotationOptions {\r\n yAxes?: string[]\r\n }\r\n\r\n enum FanHeaterChangeType {\r\n Fan,\r\n Heater,\r\n }\r\n \r\n export enum FanHeaterStatus {\r\n Off,\r\n Paused,\r\n On,\r\n }\r\n\r\n export interface ChangeInfo {\r\n timetamp: string,\r\n fanStatus: FanHeaterStatus | null,\r\n prevFanStatus: FanHeaterStatus | null,\r\n heaterStatus: FanHeaterStatus | null,\r\n prevHeaterStatus: FanHeaterStatus | null,\r\n nextCaptureTimeUTC: string | null,\r\n }\r\n\r\n export function generateFanHeaterChangeAnnotationInfo(data: Array): Array {\r\n if (data == null) {\r\n return [];\r\n }\r\n\r\n const isFanOn = (fanOffReasonId: FanOffReason | null) => {\r\n if (fanOffReasonId == null) {\r\n return FanHeaterStatus.On;\r\n }\r\n if ([FanOffReason.FanTimer, FanOffReason.KeepFansOffOverride].includes(fanOffReasonId)) {\r\n return FanHeaterStatus.Off;\r\n }\r\n \r\n return FanHeaterStatus.Paused;\r\n };\r\n\r\n const isHeaterOn = (heaterOffReasonId: HeaterOffReason | null) => {\r\n if (heaterOffReasonId == null) {\r\n return FanHeaterStatus.On;\r\n }\r\n return FanHeaterStatus.Off;\r\n };\r\n\r\n const annotations = data.flatMap(datapoint => {\r\n const fanstate = isFanOn(datapoint.fanOffReasonId);\r\n const heaterState = isHeaterOn(datapoint.heaterOffReasonId);\r\n const prevFanStatus = isFanOn(datapoint.previousFanOffReasonId);\r\n const prevHeaterStatus = isHeaterOn(datapoint.previousHeaterOffReasonId);\r\n\r\n const result: ChangeInfo = {\r\n timetamp: datapoint.x,\r\n fanStatus: fanstate,\r\n prevHeaterStatus: prevHeaterStatus,\r\n heaterStatus: heaterState,\r\n prevFanStatus: prevFanStatus,\r\n nextCaptureTimeUTC: datapoint.nextCaptureTimeUTC,\r\n };\r\n return result;\r\n\r\n\r\n });\r\n return annotations;\r\n\r\n }\r\n\r\n export const useFanHeaterChangeHistoryQueryOptions = (binId: number, startDate: Dayjs | null | undefined, endDate: Dayjs | null | undefined) => {\r\n\r\n const queryClient = useQueryClient();\r\n return queryOptions({\r\n queryKey: [...binDBKeys.bin(binId), \"charts\", \"getFanHeaterChangeHistory\", { startDate: startDate, endDate: endDate }],\r\n queryFn: async (q) => {\r\n \r\n const result = await BinApiService.getFanHeaterChangeHistory(binId, startDate?.toISOString()!, endDate?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n return result;\r\n },\r\n refetchInterval: Infinity, retry: false, keepPreviousData: true,\r\n enabled: (binId ?? 0) > 0 && startDate != null && endDate != null,\r\n });\r\n }\r\n \r\n export const useFanHeaterChangeHistory = (binId: number, startDate: Dayjs | null | undefined, endDate: Dayjs | null | undefined, options?: FanHeaterAnnotationOptions) => {\r\n\r\n const queryClient = useQueryClient();\r\n\r\n const yAxes = useMemo(() => {\r\n return options?.yAxes ?? ['y'];\r\n }, [options?.yAxes])\r\n\r\n const changeQueryOptions = useFanHeaterChangeHistoryQueryOptions(binId, startDate, endDate);\r\n\r\n const chartQuery = useQuery({...changeQueryOptions});\r\n \r\n const annotations = useMemo(() => {\r\n\r\n const annotationsInfo = generateFanHeaterChangeAnnotationInfo(chartQuery.data ?? []);\r\n \r\n const fanHeaterAnnotations = annotationsInfo?.flatMap((datapoint, index) => {\r\n \r\n \r\n\r\n \r\n \r\n const statusAnnotations: AnnotationOptions[] = [];\r\n \r\n const fanStatus = datapoint.fanStatus;\r\n const heaterStatus = datapoint.heaterStatus;\r\n if (fanStatus == FanHeaterStatus.On) {\r\n const annotation: AnnotationOptions = {\r\n type: 'box',\r\n xScaleID: 'x',\r\n id: uniqueId('fanChange-On-'),\r\n xMin: datapoint.timetamp,\r\n xMax: datapoint.nextCaptureTimeUTC as string ?? undefined,\r\n backgroundColor: fanOnBackgroundColor,\r\n borderWidth: 0,\r\n z: -2,\r\n drawTime: 'beforeDatasetsDraw', \r\n };\r\n for (const yaxis of yAxes) {\r\n statusAnnotations.push({...annotation, yScaleID: yaxis});\r\n }\r\n }\r\n \r\n else if (fanStatus == FanHeaterStatus.Paused) {\r\n const annotation: AnnotationOptions = {\r\n type: 'box',\r\n xScaleID: 'x',\r\n id: uniqueId('fanChange-Paused-'),\r\n xMin: datapoint.timetamp as string,\r\n xMax: datapoint.nextCaptureTimeUTC as string ?? undefined,\r\n backgroundColor: FanPausedBackgroundColor,\r\n borderWidth: 0,\r\n z: -3,\r\n drawTime: 'beforeDatasetsDraw', \r\n };\r\n for (const yaxis of yAxes) {\r\n statusAnnotations.push({...annotation, yScaleID: yaxis});\r\n }\r\n }\r\n \r\n if (heaterStatus == FanHeaterStatus.On) {\r\n const annotation: AnnotationOptions & Record = {\r\n type: 'box',\r\n id: uniqueId('heaterChange-'),\r\n xMin: datapoint.timetamp as string,\r\n xMax: datapoint.nextCaptureTimeUTC as string ?? undefined, \r\n //backgroundColor: 'rgb(250, 217, 187)',\r\n backgroundColor: heaterOnBackgroundColor,\r\n borderWidth: 0,\r\n drawTime: 'beforeDatasetsDraw',\r\n z: -2,\r\n };\r\n for (const yaxis of yAxes) {\r\n statusAnnotations.push({...annotation, yScaleID: yaxis});\r\n }\r\n }\r\n \r\n return statusAnnotations;\r\n }) ?? [];\r\n return fanHeaterAnnotations;\r\n }, [chartQuery.data, yAxes]);\r\n \r\n return {data: chartQuery.data, annotations: annotations};\r\n }\r\n\r\n export const formatCompressorStatus = (value: number) => {\r\n if (value === 0) {\r\n return \"OFF\";\r\n }\r\n if (value === 1) {\r\n return \"ON\";\r\n }\r\n return '';\r\n }\r\n\r\n export interface FanControlMoisturePreferenceProps {\r\n useRHForFanControl: boolean,\r\n }","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum AutomationType {\r\n DriStack = 'DriStack',\r\n AutoBin = 'AutoBin',\r\n Special = 'Special',\r\n PremierPlus = 'PremierPlus',\r\n}\r\nexport default AutomationType;\r\n","import { queryOptions } from \"src/queries/v4QueryOptions/queryOptions\";\r\nimport ModeChangeDataPointDTO from \"src/models/ModeChangeDataPointDTO\";\r\nimport { ChangeInfo, FanHeaterStatus, generateFanHeaterChangeAnnotationInfo, useFanHeaterChangeHistoryQueryOptions } from \"./shared\";\r\nimport FanHeaterChangeTimeDatapointDTO from \"src/models/FanHeaterChangeTimeDatapointDTO\";\r\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\r\nimport { RangeValue } from \"./ChartContainer\";\r\nimport { AnnotationOptions } from \"chartjs-plugin-annotation\";\r\nimport { Dayjs } from \"dayjs\";\r\nimport { uniqueId } from \"lodash\";\r\nimport { useMemo } from \"react\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { useBinInfoContext } from \"src/queries/BinInfoContext\";\r\nimport AutomationType from \"src/consts/AutomationType\";\r\nimport { BaseHeaders } from \"./csv/types\";\r\n\r\nclass AnnotationAdvancer {\r\n public annotations: ModeChangeDataPointDTO[];\r\n public currentAnnotation: ModeChangeDataPointDTO;\r\n public passedTime: string | null;\r\n\r\n constructor(annotations: ModeChangeDataPointDTO[])\r\n {\r\n this.annotations = annotations;\r\n this.currentAnnotation = this.annotations[0];\r\n this.passedTime = null;\r\n }\r\n\r\n public currentMode(): string | null | undefined {\r\n return this.currentAnnotation?.operatingMode;\r\n }\r\n\r\n public time(time: string) {\r\n if (this.currentAnnotation == null || this.currentAnnotation.x >= time) {\r\n return;\r\n }\r\n const nextAnnotation = this.annotations.find(a => a.x >= time);\r\n if (nextAnnotation) {\r\n this.currentAnnotation = nextAnnotation;\r\n }\r\n }\r\n}\r\n\r\nclass AnnotationAdvancerFanHeaterChanges {\r\n public annotations: ChangeInfo[];\r\n public currentAnnotation: ChangeInfo;\r\n public passedTime: string | null;\r\n\r\n constructor(annotations: ChangeInfo[])\r\n {\r\n this.annotations = annotations;\r\n this.currentAnnotation = this.annotations[0];\r\n this.passedTime = null;\r\n }\r\n\r\n public fanStatus(): FanHeaterStatus | null {\r\n return this.currentAnnotation?.fanStatus;\r\n }\r\n\r\n public heaterStatus(): FanHeaterStatus | null {\r\n return this.currentAnnotation?.heaterStatus;\r\n }\r\n\r\n public time(time: string) {\r\n if (this.currentAnnotation == null || this.currentAnnotation.timetamp >= time) {\r\n return;\r\n }\r\n const nextAnnotation = this.annotations.find(a => a.timetamp >= time);\r\n if (nextAnnotation) {\r\n this.currentAnnotation = nextAnnotation;\r\n }\r\n }\r\n}\r\n\r\nexport type BaseCsv = {\r\n Timestamp: string\r\n [key: string]: any\r\n};\r\n\r\ntype useEnhancedWithFanChangeHistoryOptions = {\r\n data: Array & Array>,\r\n headers: BaseHeaders,\r\n} & BaseChartQueryOptions\r\n\r\nexport function useEnhancedWithFanChangeHistory(options: useEnhancedWithFanChangeHistoryOptions) {\r\n\r\n const value = options.range;\r\n const fanHistoryChangeQueryOptions = useFanHeaterChangeHistoryQueryOptions(options.binId, value?.[0], value?.[1]);\r\n const fanChangeHistoryQuery = useQuery({...fanHistoryChangeQueryOptions});\r\n\r\n const withFanChangeHistory = enhancedWithFanChangeHistory({\r\n data: options.data,\r\n headers: options.headers,\r\n fanHeaterChanges: fanChangeHistoryQuery.data,\r\n })\r\n return withFanChangeHistory;\r\n}\r\n\r\ninterface enhacementOptions {\r\n data: Array & Array>,\r\n headers: BaseHeaders,\r\n fanHeaterChanges: FanHeaterChangeTimeDatapointDTO[] | undefined,\r\n}\r\n\r\nexport function enhancedWithFanChangeHistory(options: enhacementOptions) {\r\n const binInfo = useBinInfoContext();\r\n const fanLabel = \"Fan\";\r\n const heaterLabel = \"Heater\";\r\n if (options.fanHeaterChanges == null || options.data == null) {\r\n return {headers: options.headers, data: options.data};\r\n }\r\n\r\n const annotations = generateFanHeaterChangeAnnotationInfo(options.fanHeaterChanges);\r\n const advancer = new AnnotationAdvancerFanHeaterChanges(annotations);\r\n\r\n const formatFanHeaterLabel = (status: FanHeaterStatus | null) => {\r\n if (status == null) {\r\n return null;\r\n }\r\n\r\n switch (status) {\r\n case FanHeaterStatus.Off: {\r\n return \"Off\";\r\n }\r\n case FanHeaterStatus.Paused: {\r\n return \"Paused\";\r\n }\r\n case FanHeaterStatus.On: {\r\n return \"On\";\r\n }\r\n }\r\n }\r\n\r\n const newData = options.data.map((row) => {\r\n advancer.time(row.Timestamp)\r\n \r\n const newRow = {...row, [fanLabel]: formatFanHeaterLabel(advancer.fanStatus()), [heaterLabel]: formatFanHeaterLabel(advancer.heaterStatus())};\r\n return newRow;\r\n });\r\n const newHeaders: BaseHeaders = [...options.headers];\r\n newHeaders.push(\"Fan\");\r\n if (binInfo.automationType === AutomationType.DriStack) {\r\n newHeaders.push(\"Heater\");\r\n }\r\n\r\n return {headers: newHeaders, data: newData};\r\n}\r\n\r\ntype enhacementOptionsModeHistory = {\r\n data: Array & Array>,\r\n headers: BaseHeaders,\r\n} & BaseChartQueryOptions\r\n\r\n\r\nexport function useEnhanceWithModeChanges(options: enhacementOptionsModeHistory) {\r\n const modeHistoryQuery = useQuery(useModeChangeHistoryQueryOptions({binId: options.binId, range: options.range}));\r\n\r\n\r\n const modeHeaderName = \"Mode\";\r\n const newHeaders: [...BaseHeaders, typeof modeHeaderName] = [...options.headers, modeHeaderName];\r\n \r\n const advancer = new AnnotationAdvancer(modeHistoryQuery.data?.originalData?.data ?? []);\r\n\r\n const newData = options.data?.map(row => {\r\n advancer.time(row.Timestamp);\r\n let mode = advancer.currentMode();\r\n return {...row, [modeHeaderName]: formatOperatingMode(mode!) };\r\n\r\n }) ?? [];\r\n\r\n return {headers: newHeaders, data: newData};\r\n}\r\nexport interface BaseChartQueryOptions {\r\n binId: number;\r\n range: RangeValue | null | undefined;\r\n}export const useModeChangeHistory = (binId: number, startDate: Dayjs | null | undefined, endDate: Dayjs | null | undefined, options?: ModeChangeHistoryOptions) => {\r\n\r\n const yAxes = useMemo(() => {\r\n return options?.yAxes ?? ['y'];\r\n }, [options?.yAxes]);\r\n\r\n const chartQuery = useQuery(useModeChangeHistoryQueryOptions({\r\n range: [startDate!, endDate!],\r\n binId: binId,\r\n }));\r\n\r\n const uniqueAnnotations = chartQuery.data?.annotationInfo;\r\n\r\n const annotations = useMemo(() => uniqueAnnotations?.flatMap((datapoint, index) => {\r\n const content = `${datapoint.content}`;\r\n const _annotations: AnnotationOptions[] = [];\r\n const annotation: AnnotationOptions = {\r\n id: uniqueId('modeChange-'),\r\n type: 'line',\r\n scaleID: 'x',\r\n borderDash: [6, 6],\r\n borderWidth: 1,\r\n borderColor: \"#aaa\",\r\n value: datapoint.timestamp,\r\n drawTime: 'beforeDatasetsDraw',\r\n label: {\r\n textStrokeColor: \"white\",\r\n textStrokeWidth: 1,\r\n display: true,\r\n content: content,\r\n backgroundColor: 'transparent',\r\n rotation: 0,\r\n color: \"black\",\r\n position: 'start',\r\n z: 10,\r\n xAdjust(ctx, options) {\r\n let value = ctx.chart.ctx.measureText(content);\r\n return value.width / 2;\r\n },\r\n font: {\r\n size: 12,\r\n }\r\n }\r\n };\r\n for (const yaxis of yAxes) {\r\n _annotations.push({ ...annotation, yScaleID: yaxis });\r\n }\r\n return _annotations;\r\n }) ?? [], [chartQuery.data, uniqueAnnotations, yAxes]);\r\n\r\n return useMemo(() => {\r\n return { chartQuery, annotations, uniqueAnnotations: uniqueAnnotations };\r\n }, [chartQuery, annotations, uniqueAnnotations]);\r\n};\r\n\r\n\r\n\r\nexport interface ModeChangeHistoryOptions {\r\n yAxes?: string[],\r\n }\r\n \r\n export const formatOperatingMode = (mode: string): string => {\r\n if (mode === \"Manual\") {\r\n return \"User Control\";\r\n }\r\n else if (mode === \"FillBin\") {\r\n return \"Fill & Aerate\"\r\n }\r\n else if (mode === \"PreDry\") {\r\n return \"Pre Dry\";\r\n }\r\n else if (mode === \"TopDry\") {\r\n return \"Top Dry\"\r\n }\r\n return mode;\r\n }\r\n const formatUniqueModeChangeHistoryChanges = (data: ModeChangeDataPointDTO[] | null | undefined) => {\r\n \r\n const annotations = data?.flatMap((datapoint, index) => {\r\n const mode = datapoint.operatingMode as string;\r\n const formattedMode = formatOperatingMode(mode);\r\n const content = `${formattedMode}`;\r\n const annotation = {\r\n timestamp: datapoint.x,\r\n content: content,\r\n };\r\n return annotation;\r\n });\r\n \r\n return {\r\n annotations: annotations,\r\n };\r\n }\r\n \r\n export interface ModeChangeHistoryQueryOptions {\r\n range: RangeValue | undefined,\r\n binId: number | null | undefined,\r\n }\r\n export const useModeChangeHistoryQueryOptions = (options: ModeChangeHistoryQueryOptions) => {\r\n const queryClient = useQueryClient();\r\n const binId = options.binId;\r\n const [startDate, endDate] = options.range ?? [];\r\n \r\n return queryOptions({\r\n queryKey: [...binDBKeys.bin(binId!), \"charts\", \"modeChangeChart\", { startDate, endDate }] as const,\r\n queryFn: async (q) => {\r\n \r\n const result = await BinApiService.getOperatingModeChangesDataset(binId!, q.queryKey[4].startDate?.toISOString()!, q.queryKey[4].endDate?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n \r\n const originalData = result;\r\n const enriched = formatUniqueModeChangeHistoryChanges(result.data);\r\n \r\n return {originalData: originalData, annotationInfo: enriched.annotations};\r\n },\r\n refetchInterval: Infinity, retry: false, keepPreviousData: true,\r\n enabled: (binId ?? 0) > 0 && startDate != null && endDate != null,\r\n }\r\n )\r\n }\r\n\r\n /**\r\n * Adds mode changes and fan changes to the base data supplied for use in a csv\r\n */\r\n export const useAddDefaultAnnotationsToCsv = (options: BaseChartQueryOptions & {baseData: BaseCsv[], baseHeaders: BaseHeaders}) => {\r\n\r\n\r\n const withModes = useEnhanceWithModeChanges({\r\n data: options.baseData,\r\n headers: options.baseHeaders,\r\n binId: options.binId,\r\n range: options.range,\r\n });\r\n\r\n const withFanChangeHistory = useEnhancedWithFanChangeHistory({\r\n data: withModes.data,\r\n headers: withModes.headers,\r\n binId: options.binId,\r\n range: options.range,\r\n });\r\n\r\n if (options.baseData == null) {\r\n return {data: options.baseData, headers: options.baseHeaders};\r\n };\r\n\r\n return {data: withFanChangeHistory.data, headers: withFanChangeHistory.headers};\r\n }\r\n\r\n","import { Line } from 'react-chartjs-2';\r\nimport React, { useCallback, useState } from 'react';\r\nimport dayjs, { Dayjs } from 'dayjs';\r\nimport { useQuery } from '@tanstack/react-query';\r\nimport { binDBKeys } from 'src/pages/binOverview/BinCommander';\r\nimport { useBinStateFromAzure } from 'src/queries/useBinStateFromAzure';\r\nimport { getUserTimezoneOffset } from '../../../components/BinDetails';\r\nimport { Button, DatePicker, Skeleton } from 'antd';\r\nimport BinApiService from 'src/api/BinApiService';\r\nimport { BaseChartProps } from './TemperatureCableChart';\r\nimport { RangePickerProps } from 'antd/es/date-picker';\r\nimport { queryClient } from 'src';\r\nimport { BaseChartQueryOptions, useModeChangeHistory } from \"./modehistoryAndFanChangeHistory\";\r\nimport { useFanHeaterChangeHistory } from './shared';\r\nimport { queryOptions } from 'src/queries/v4QueryOptions/queryOptions';\r\n\r\ntype RangeValue = Parameters['onChange']>>[0] | null;\r\n\r\nexport interface ChartDatePickerProps {\r\n startDate: Dayjs;\r\n endDate: Dayjs;\r\n onDateRangeChange: (val: RangeValue, formatString: [string, string]) => void;\r\n}\r\n\r\nexport const ChartDatePicker = (props: ChartDatePickerProps) => {\r\n\r\n const currentDate = props.endDate ?? dayjs();\r\n const priorDate = props.startDate ?? currentDate.subtract(60, 'days');\r\n\r\n const [dates, setDates] = useState([priorDate, currentDate]);\r\n const [value, setValue] = useState([priorDate, currentDate]);\r\n const [open, setOpen] = useState(false);\r\n\r\n const disabledDate = (current: Dayjs) => {\r\n if (!dates) {\r\n return false;\r\n }\r\n const tooLate = dates[0] && current.diff(dates[0], 'days') >= 366;\r\n const tooEarly = dates[1] && dates[1].diff(current, 'days') >= 366;\r\n return !!tooEarly || !!tooLate;\r\n };\r\n\r\n const onOpenChange = useCallback((open: boolean) => {\r\n if (open) {\r\n setDates([null, null]);\r\n } else {\r\n setDates(null);\r\n }\r\n setOpen(open);\r\n }, []);\r\n\r\n const rangePresets: {\r\n label: string;\r\n value: [Dayjs, Dayjs];\r\n }[] = [\r\n { label: 'Last 6 Hours', value: [dayjs().subtract(6, 'hours'), dayjs()] },\r\n { label: 'Last 12 Hours', value: [dayjs().subtract(12, 'hours'), dayjs()] },\r\n { label: 'Last 3 Days', value: [dayjs().add(-3, 'd'), dayjs()] },\r\n { label: 'Last 7 Days', value: [dayjs().add(-7, 'd'), dayjs()] },\r\n { label: 'Last 14 Days', value: [dayjs().add(-14, 'd'), dayjs()] },\r\n { label: 'Last 30 Days', value: [dayjs().add(-30, 'd'), dayjs()] },\r\n { label: 'Last 60 Days', value: [dayjs().add(-60, 'd'), dayjs()] },\r\n { label: 'Last 90 Days', value: [dayjs().add(-90, 'd'), dayjs()] },\r\n { label: '6 Months', value: [dayjs().add(-30 * 6, 'd'), dayjs()] },\r\n { label: '12 Months', value: [dayjs().add(-30 * 12, 'd'), dayjs()] },\r\n ];\r\n\r\n return {\r\n setDates(val);\r\n // Fix for Antd 5.12 so the datepicker closes after start and end date selection\r\n if (val?.[0] != null && val?.[1] != null) {\r\n setOpen(false);\r\n }\r\n }}\r\n //inputReadOnly\r\n allowClear={false}\r\n open={open}\r\n onChange={(val, formatString) => {\r\n const startDay = val?.[0]?.startOf('day') ?? null;\r\n const endDay = val?.[1]?.endOf('day') ?? null;\r\n const newRange: RangeValue = [startDay, endDay];\r\n props.onDateRangeChange(newRange, formatString);\r\n setValue(val);\r\n }}\r\n onOpenChange={onOpenChange}\r\n renderExtraFooter={(mode) => {\r\n return
\r\n {rangePresets.map(preset => {\r\n return \r\n })}\r\n
\r\n }}\r\n changeOnBlur />;\r\n}\r\n\r\nexport const co2ChartQueryOptions = (options: BaseChartQueryOptions) => {\r\n const value = options.range;\r\n\r\n return queryOptions(\r\n {\r\n queryKey: [...binDBKeys.bin(options.binId), \"charts\", \"CO2\", { startDate: value?.[0], endDate: value?.[1] }], staleTime: Infinity, keepPreviousData: true, retry: 0, queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n const results = await BinApiService.getCO2History(options.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n \r\n return results;\r\n }, enabled: (options.binId ?? 0) > 0,\r\n cacheTime: 5_000,\r\n }\r\n );\r\n}\r\n\r\nexport const Co2Chart = (props: BaseChartProps & {height?: number}) => {\r\n\r\n const value = props.value;\r\n\r\n const _co2ChartQueryOptions = co2ChartQueryOptions({binId: props.binId, range: value});\r\n\r\n const chartQuery = useQuery({..._co2ChartQueryOptions});\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n\r\n if (chartQuery.data == null) {\r\n return ;\r\n }\r\n\r\n return
\r\n\r\n
\r\n \r\n
\r\n
\r\n};\r\n","import { Plugin } from \"chart.js\";\r\nimport annotationPlugin from 'chartjs-plugin-annotation';\r\n\r\n// Adapted from https://github.com/chartjs/chartjs-plugin-annotation/discussions/880#discussioncomment-7254496\r\nexport const annotationFixOverlapPlugin: Plugin = {\r\n id: 'annotations-overlap-fix',\r\n afterUpdate: (chart) => {\r\n const usingAnnotations = chart.isPluginEnabled(annotationPlugin.id);\r\n if (!usingAnnotations) {\r\n return;\r\n }\r\n\r\n let ann = annotationPlugin as any;\r\n const elementList = ann\r\n // private variable. will break when the annotations ever gets around to exposing a public way to get the state\r\n ?._getState(chart)\r\n // adjust the line annotations with labels\r\n ?.elements.filter((i: any) => i?.options?.type == 'line' && i?.label != null);\r\n const map = new Map();\r\n let changed = false;\r\n\r\n for (var i = 0; i < elementList.length; i++) {\r\n const currentElement = elementList[i];\r\n\r\n const currentElementWidth =\r\n currentElement.label.x2 - currentElement.label.x;\r\n if (currentElement.label.x <= chart?.chartArea?.left) {\r\n currentElement.label.x = chart?.chartArea?.left;\r\n currentElement.label.x2 =\r\n currentElement.label.x + currentElementWidth;\r\n elementList[i] = currentElement;\r\n }\r\n if (currentElement?.label.x2 > chart?.chartArea?.right) {\r\n currentElement.label.x2 = chart?.chartArea?.right;\r\n currentElement.label.x =\r\n currentElement.label.x2 - currentElementWidth;\r\n elementList[i] = currentElement;\r\n }\r\n for (var j = i + 1; j < elementList.length; j++) {\r\n const nextElement = elementList[j];\r\n if (\r\n currentElement.label.x2 >= nextElement.label.x &&\r\n currentElement.label.y === nextElement.label.y\r\n ) {\r\n const elementHeight = nextElement.label.y2 - nextElement.label.y;\r\n if (map.has(currentElement.options.id)) {\r\n const currentElementList = map.get(\r\n currentElement.options.id\r\n );\r\n nextElement.label.y =\r\n currentElementList[\r\n currentElementList.length - 1\r\n ].label.y2;\r\n nextElement.label.y2 = nextElement.label.y + elementHeight;\r\n elementList[j] = nextElement;\r\n currentElementList.push(nextElement);\r\n map.set(\r\n currentElement.options.id,\r\n currentElementList\r\n );\r\n } else {\r\n nextElement.label.y = currentElement.label.y2;\r\n nextElement.label.y2 = nextElement.label.y + elementHeight;\r\n elementList[j] = nextElement;\r\n map.set(currentElement.options.id, [nextElement]);\r\n }\r\n elementList[j] = nextElement;\r\n changed = true;\r\n }\r\n }\r\n }\r\n\r\n if (changed) {\r\n chart.draw();\r\n }\r\n },\r\n}","import { Line} from 'react-chartjs-2';\r\nimport React, { useCallback, useState } from 'react';\r\nimport dayjs, { Dayjs } from 'dayjs';\r\nimport { useQuery, useQueryClient } from '@tanstack/react-query';\r\nimport { binDBKeys } from 'src/pages/binOverview/BinCommander';\r\nimport { useBinStateFromAzure } from 'src/queries/useBinStateFromAzure';\r\nimport { getUserTimezoneOffset } from '../../../components/BinDetails';\r\nimport { DatePicker, Divider, Skeleton } from 'antd';\r\nimport BinApiService from 'src/api/BinApiService';\r\nimport { BaseChartProps } from './TemperatureCableChart';\r\nimport { queryClient } from 'src';\r\nimport { ChartDatePicker } from './Co2Chart';\r\nimport { RangeValue } from './ChartContainer';\r\nimport { FanControlMoisturePreferenceProps, tooltipItemRound, useFanHeaterChangeHistory } from './shared';\r\nimport { ChartDataset, Chart as ChartJS} from 'chart.js';\r\n// @ts-ignore\r\nimport crosshair from '@adelamar/chartjs-plugin-crosshair';\r\nimport { useModeChangeHistory } from \"./modehistoryAndFanChangeHistory\";\r\nimport annotationPlugin from 'chartjs-plugin-annotation';\r\nimport {annotationFixOverlapPlugin} from 'src/pages/dashboard/charts/plugins/annotationOverlapFixPlugin';\r\nimport { queryOptions } from 'src/queries/v4QueryOptions/queryOptions';\r\nimport { BaseChartQueryOptions } from './modehistoryAndFanChangeHistory';\r\nChartJS.register(crosshair);\r\nChartJS.register(annotationPlugin);\r\nChartJS.register(annotationFixOverlapPlugin);\r\n// @ts-ignore\r\nChartJS.defaults.plugins.crosshair.zoom = {enabled: false};\r\n\r\nChartJS.defaults.animation = false;\r\nChartJS.defaults.responsive = true;\r\nChartJS.defaults.maintainAspectRatio = false;\r\nChartJS.defaults.datasets.line.pointRadius = 0;\r\n\r\nexport const ambientFanDatasetColor = \"rgb(235,124,52)\";\r\nexport const plenumFanDatasetColor = \"rgb(66,113,192)\";\r\n\r\n\r\nexport const plenumDewPointColor = 'rgba(254,106,138)';\r\n\r\nexport const useGetLowestLayerTemperatureHistoryQueryOptions = (options: BaseChartQueryOptions) => {\r\n const queryClient = useQueryClient();\r\n return queryOptions({\r\n queryKey: [...binDBKeys.bin(options.binId), \"charts\", \"LowestLayer\", { startDate: options.range?.[0], endDate: options.range?.[1] }], staleTime: Infinity, retry: 0, keepPreviousData: true,\r\n queryFn: async (q) => {\r\n const results = await BinApiService.getLowestLayerTemperatureHistory(options.binId, options.range?.[0]?.toISOString()!, options.range?.[1]?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({queryKey: q.queryKey.slice(0, -1), type: \"inactive\"});\r\n\r\n return results;\r\n }, enabled: (options.binId ?? 0) > 0 && options.range?.[0] != null && options.range?.[1] != null\r\n });\r\n}\r\n\r\nexport const LayerDewPointChart = (props: BaseChartProps & {showTitle?: boolean, yTitle?: string}) => {\r\n\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(14, 'days');\r\n\r\n\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n\r\n // Internally, we need to deal with some value. Depending on whether\r\n\t// the component is controlled or not, that value comes from its\r\n\t// props or from its internal state.\r\n\r\n\tconst value = isControlled ? props.value : internalValue;\r\n\r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n\r\n const getAmbientPlenumHistoryQueryOptions = useAmbientPlenumTemperatureQueryOptions({\r\n binId: props.binId,\r\n range: value,\r\n });\r\n\r\n const chartQuery = useQuery({...getAmbientPlenumHistoryQueryOptions});\r\n\r\n const getLowestLayerTemperatureHistoryQueryOptions = useGetLowestLayerTemperatureHistoryQueryOptions({\r\n binId: props.binId,\r\n range: value,\r\n })\r\n \r\n const chartLowestLayerQuery = useQuery({...getLowestLayerTemperatureHistoryQueryOptions});\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n\r\n\r\n return
\r\n\r\n{chartQuery.data && }\r\n {chartQuery.data == null && }\r\n
\r\n}\r\n\r\nexport const useAmbientPlenumTemperatureQueryOptions = (options: BaseChartQueryOptions) => {\r\n const queryClient = useQueryClient();\r\n return queryOptions({\r\n queryKey: [...binDBKeys.bin(options.binId), \"charts\", \"Ambient_Vs_Plenum\", { startDate: options.range?.[0], endDate: options.range?.[1] }], staleTime: Infinity, retry: 0, keepPreviousData: true, queryFn: async (q) => {\r\n const results = await BinApiService.getAmbientPlenumHistory(options.binId, options.range?.[0]?.toISOString()!, options.range?.[1]?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({queryKey: q.queryKey.slice(0, -1), type: \"inactive\"});\r\n\r\n return results;\r\n }, enabled: (options.binId ?? 0) > 0 && options.range?.[0] != null && options.range?.[1] != null\r\n });\r\n}\r\n\r\nexport const AmbientPlenumChart = (props: BaseChartProps & {showTitle?: boolean} & {showRecommendation?: boolean, showTemperatureChart?: boolean} & FanControlMoisturePreferenceProps) => {\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(14, 'days');\r\n\r\n\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n\r\n // Internally, we need to deal with some value. Depending on whether\r\n\t// the component is controlled or not, that value comes from its\r\n\t// props or from its internal state.\r\n\r\n\tconst value = isControlled ? props.value : internalValue;\r\n\r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n\r\n const ambientVsPlenumChartQueryOptions = useAmbientPlenumTemperatureQueryOptions({\r\n binId: props.binId,\r\n range: value,\r\n })\r\n\r\n const chartQuery = useQuery({...ambientVsPlenumChartQueryOptions});\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0]!, value?.[1]!);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n const formatMoistureTitle = () => {\r\n if (props.useRHForFanControl === false) {\r\n return \"Incoming Air EMC\";\r\n }\r\n else {\r\n return \"Incoming Air RH\";\r\n } \r\n }\r\n\r\n\r\n\r\n const datasets: Array = [];\r\n if (chartQuery.data) {\r\n datasets.push(\r\n// @ts-ignore\r\n{\r\n ...chartQuery.data[0],\r\n label: chartQuery.data[0]?.label!,\r\n borderColor: plenumFanDatasetColor,\r\n backgroundColor: plenumFanDatasetColor,\r\n });\r\n if (props.showRecommendation ?? true) {\r\n// @ts-ignore\r\ndatasets.push(\r\n// @ts-ignore\r\n{\r\n// @ts-ignore\r\n...chartQuery.data[1],\r\n label: chartQuery.data[1]?.label!,\r\n borderColor: \"rgb(165,164,164)\",\r\n backgroundColor: \"rgb(165,164,164)\",\r\n }\r\n ); \r\n }\r\ndatasets.push(\r\n// @ts-ignore\r\n{\r\n ...chartQuery.data[2],\r\n label: chartQuery.data[2]?.label!,\r\n borderColor: ambientFanDatasetColor,\r\n backgroundColor: ambientFanDatasetColor,\r\n }\r\n);\r\n }\r\nreturn (
\r\n\r\n {!isControlled && }\r\n
\r\n {chartQuery.data && }\r\n {chartQuery.data == null && }\r\n
\r\n\r\n{(props.showTemperatureChart ?? true) && <>\r\n\r\n
\r\n\r\n {chartQuery.data && }\r\n {chartQuery.data == null && }\r\n
\r\n }\r\n\r\n
);\r\n\r\n};\r\n","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\r\nimport dayjs from \"dayjs\";\r\nimport React, { useState, useCallback } from \"react\";\r\nimport { queryClient } from \"src\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { RangeValue } from \"./ChartContainer\";\r\nimport { FanControlMoisturePreferenceProps, tooltipItemRound, useFanHeaterChangeHistory } from \"./shared\";\r\nimport { BaseChartProps, htmlLegendPlugin } from \"./TemperatureCableChart\";\r\nimport _uniqueId from 'lodash/uniqueId';\r\nimport { Line } from \"react-chartjs-2\";\r\nimport { ChartDatePicker } from \"./Co2Chart\";\r\nimport { Skeleton } from \"antd\";\r\nimport { ambientFanDatasetColor, plenumFanDatasetColor } from \"./AmbientPlenumChart\";\r\nimport { LineAnnotationOptions } from \"chartjs-plugin-annotation\";\r\nimport FanOffReason from \"src/consts/FanOffReason\";\r\nimport HeaterOffReason from \"src/consts/HeaterOffReason\";\r\nimport ModeChangeDataPointDTO from \"src/models/ModeChangeDataPointDTO\";\r\nimport { queryOptions } from \"src/queries/v4QueryOptions/queryOptions\";\r\nimport { BaseChartQueryOptions, useModeChangeHistory } from \"./modehistoryAndFanChangeHistory\";\r\n\r\nexport const useLayerMcHistoryQueryOptions = (options: BaseChartQueryOptions) => {\r\n const queryClient = useQueryClient();\r\n const value = options.range;\r\n\r\n return queryOptions(\r\n {\r\n queryKey: [...binDBKeys.bin(options.binId), \"charts\", \"LayerSummary\", { startDate: value?.[0], endDate: value?.[1] }],\r\n queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n \r\n const result = await BinApiService.getLayerSummaryHistory(options.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n return result;\r\n },\r\n refetchInterval: Infinity, retry: false, keepPreviousData: true,\r\n enabled: (options.binId ?? 0) > 0 && value?.[0] != null && value?.[1] != null,\r\n }\r\n )\r\n}\r\n\r\nexport const LayerMCHistoryChart = (props: BaseChartProps & {yTitle?: string} & FanControlMoisturePreferenceProps) => {\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(14, 'days');\r\n \r\n const [legendId] = useState(_uniqueId('legend-'));\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n \r\n // Internally, we need to deal with some value. Depending on whether\r\n // the component is controlled or not, that value comes from its\r\n // props or from its internal state.\r\n \r\n const value = isControlled ? props.value : internalValue;\r\n \r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n \r\n const layerMcHistoryQueryOptions = useLayerMcHistoryQueryOptions({\r\n binId: props.binId,\r\n range: value,\r\n })\r\n\r\n\r\n const chartQuery = useQuery({...layerMcHistoryQueryOptions});\r\n\r\n const formatYTitle = useCallback(() => {\r\n if (props.useRHForFanControl === false) {\r\n return \"MC (%)\";\r\n } else {\r\n return \"RH %\";\r\n }\r\n }, [props.useRHForFanControl]);\r\n\r\n const getYSuggestedMinMax = () => {\r\n if (props.useRHForFanControl === false) {\r\n return {min: undefined, max: undefined}\r\n } else {\r\n return {min: 0, max: 100};\r\n }\r\n }\r\n \r\n \r\n return (
\r\n {!isControlled && }\r\n \r\n {chartQuery.data &&
\r\n {\r\n\r\n return {\r\n data: dataset.data,\r\n label: dataset.label!,\r\n parsing: {\r\n yAxisKey: props.useRHForFanControl === false ? 'mc' : 'relativeHumidity',\r\n }\r\n };\r\n }) ?? [],\r\n\r\n ] }} />\r\n
\r\n }\r\n
\r\n
\r\n
);\r\n }\r\n ","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\r\nimport dayjs from \"dayjs\";\r\nimport React, { useState, useCallback } from \"react\";\r\nimport { queryClient } from \"src\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { RangeValue } from \"./ChartContainer\";\r\nimport { FanControlMoisturePreferenceProps, tooltipItemRound, useFanHeaterChangeHistory } from \"./shared\";\r\nimport { BaseChartProps, htmlLegendPlugin } from \"./TemperatureCableChart\";\r\nimport _uniqueId from 'lodash/uniqueId';\r\nimport { Line } from \"react-chartjs-2\";\r\nimport { ChartDatePicker } from \"./Co2Chart\";\r\nimport { Skeleton } from \"antd\";\r\nimport { ambientFanDatasetColor, plenumFanDatasetColor, useAmbientPlenumTemperatureQueryOptions } from \"./AmbientPlenumChart\";\r\nimport { BaseChartQueryOptions, useModeChangeHistory } from \"./modehistoryAndFanChangeHistory\";\r\nimport TemperatureSensorEnum from \"src/consts/TemperatureSensorEnum\";\r\nimport { queryOptions } from \"src/queries/v4QueryOptions/queryOptions\";\r\n\r\nexport const useTemperatureLayerSummaryChartHistoryQueryOptions = (options: BaseChartQueryOptions) => {\r\n const queryClient = useQueryClient();\r\n const value = options.range;\r\n\r\n return queryOptions(\r\n {\r\n queryKey: [...binDBKeys.bin(options.binId), \"charts\", \"LayerSummary\", { startDate: value?.[0], endDate: value?.[1] }],\r\n queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n \r\n const result = await BinApiService.getLayerSummaryHistory(options.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n return result;\r\n },\r\n cacheTime: 5000,\r\n refetchInterval: Infinity, retry: false, keepPreviousData: true,\r\n enabled: (options.binId ?? 0) > 0 && value?.[0] != null && value?.[1] != null,\r\n }\r\n );\r\n}\r\n\r\nexport const LayerTemperatureHistoryChart = (props: BaseChartProps & {yTitle?: string} & {preferredSensorType: TemperatureSensorEnum} & FanControlMoisturePreferenceProps) => {\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(14, 'days');\r\n \r\n const [legendId] = useState(_uniqueId('legend-'));\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n const preferredSensorType = props.preferredSensorType ?? TemperatureSensorEnum.Thermocouple;\r\n const showHiddenTemp = props.useRHForFanControl === true;\r\n \r\n // Internally, we need to deal with some value. Depending on whether\r\n // the component is controlled or not, that value comes from its\r\n // props or from its internal state.\r\n \r\n const value = isControlled ? props.value : internalValue;\r\n \r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n\r\n const temperatureLayerSummaryChartHistoryQueryOptions = useTemperatureLayerSummaryChartHistoryQueryOptions({\r\n binId: props.binId,\r\n range: value,\r\n });\r\n\r\n const ambientPlenumTemperatureQueryOptions = useAmbientPlenumTemperatureQueryOptions({\r\n binId: props.binId,\r\n range: value,\r\n });\r\n \r\n \r\n const chartQuery = useQuery({...temperatureLayerSummaryChartHistoryQueryOptions});\r\n\r\n const chartAmbientPlenumFanQuery = useQuery({...ambientPlenumTemperatureQueryOptions});\r\n \r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanModeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n\r\n return (
\r\n {!isControlled && }\r\n \r\n {chartQuery.data && chartAmbientPlenumFanQuery.data &&
\r\n {\r\n\r\n return {\r\n data: dataset.data,\r\n label: dataset.label!,\r\n parsing: {\r\n yAxisKey: (showHiddenTemp || [TemperatureSensorEnum.Opi, TemperatureSensorEnum.PowerCast, TemperatureSensorEnum.BinSense].includes(preferredSensorType)) ? 'moistureCableTemperatureF' : 'temperatureF',\r\n }\r\n };\r\n }) ?? [],\r\n\r\n ] }} />\r\n
\r\n }\r\n
\r\n
\r\n
);\r\n }\r\n ","import { BaseChartProps } from \"./TemperatureCableChart\";\r\nimport { useQuery } from \"@tanstack/react-query\";\r\nimport { Skeleton } from \"antd\";\r\nimport dayjs from \"dayjs\";\r\nimport { round } from \"lodash-es\";\r\nimport React, { useState, useCallback, useMemo } from \"react\";\r\nimport { Line } from \"react-chartjs-2\";\r\nimport { queryClient } from \"src\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { RangeValue } from \"./ChartContainer\";\r\nimport { ChartDatePicker } from \"./Co2Chart\";\r\nimport { ChartData, ChartDataset } from \"chart.js\";\r\nimport FanHeaterDataPointDTO from \"src/models/FanHeaterDataPointDTO\";\r\nimport { any } from \"prop-types\";\r\nimport { fanStatusColor, heaterStatusColor, tooltipItemRound, useFanHeaterChangeHistory } from \"./shared\";\r\nimport _uniqueId from \"lodash/uniqueId\";\r\nimport { useModeChangeHistory } from \"./modehistoryAndFanChangeHistory\";\r\n\r\nenum FanStatusEnum {\r\n ON = \"ON\",\r\n OFF = \"OFF\",\r\n}\r\n\r\n// max 12 fans\r\nconst manualColors: Array = [\"#cd5051\", \"#813332\", \"#51cccc\", \"#328180\", \"#8ecc50\", \"#588133\", \"#8f51cc\", \"#583381\", \"#cdaf51\", \"#806c33\", \"#51cd71\", \"#338144\"];\r\n\r\n\r\nexport const PlenumPressureChart = (props: BaseChartProps & { hasHeater: boolean } & {showTitle?: boolean}) => {\r\n\r\n\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(7, 'days');\r\n\r\n const [legendId] = useState(_uniqueId('legend-'));\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n\r\n // Internally, we need to deal with some value. Depending on whether\r\n // the component is controlled or not, that value comes from its\r\n // props or from its internal state.\r\n\r\n const value = isControlled ? props.value : internalValue;\r\n\r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n const chartQuery = useQuery({\r\n queryKey: [...binDBKeys.bin(props.binId), \"charts\", \"Fan&Heater\", { startDate: value?.[0], endDate: value?.[1] }], staleTime: Infinity, keepPreviousData: true, retry: 0, queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n const results = await BinApiService.getFanHeaterChart(props.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n\r\n return results;\r\n }, enabled: (props.binId ?? 0) > 0,\r\n });\r\n\r\n \r\n // @ts-ignore string | dayJS not possible\r\n const plenumPressureDatasets: Array> = chartQuery.data?.fans?.flatMap((fan, index) => {\r\n\r\n return [{\r\n data: fan.data!, label: `Fan ${fan.label!} Pressure`, indexAxis: 'x', yAxisID: 'yPlenum', parsing: {\r\n yAxisKey: 'plenumPressureInH2O',\r\n },\r\n backgroundColor: manualColors[index],\r\n borderColor: manualColors[index],\r\n }];\r\n }) ?? [];\r\n\r\n\r\n\r\n const datasets: Array> = [];\r\n\r\n\r\n datasets.push(...plenumPressureDatasets);\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n\r\n return
\r\n\r\n {!isControlled && }\r\n \r\n {chartQuery.data != null &&
\r\n \r\n
}\r\n
\r\n
\r\n\r\n}\r\n","import { useQuery } from \"@tanstack/react-query\";\r\nimport { Skeleton } from \"antd\";\r\nimport dayjs from \"dayjs\";\r\nimport { floor, round } from \"lodash-es\";\r\nimport React, { useState, useCallback, useMemo } from \"react\";\r\nimport { Line } from \"react-chartjs-2\";\r\nimport { queryClient } from \"src\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { RangeValue } from \"./ChartContainer\";\r\nimport { ChartDatePicker } from \"./Co2Chart\";\r\nimport { compressorColor, tooltipItemRound, useFanHeaterChangeHistory } from \"./shared\";\r\nimport { BaseChartProps } from \"./TemperatureCableChart\";\r\nimport _uniqueId from 'lodash/uniqueId';\r\nimport { useModeChangeHistory } from \"./modehistoryAndFanChangeHistory\";\r\n\r\nexport const TargetLayerChart = (props: BaseChartProps & {shrinkHeight?: boolean}) => {\r\n\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(7, 'days');\r\n \r\n const [legendId] = useState(_uniqueId('legend-'));\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n \r\n // Internally, we need to deal with some value. Depending on whether\r\n // the component is controlled or not, that value comes from its\r\n // props or from its internal state.\r\n \r\n const value = isControlled ? props.value : internalValue;\r\n \r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n \r\n \r\n const chartQuery = useQuery({\r\n queryKey: [...binDBKeys.bin(props.binId), \"charts\", \"TargetLayer\", { startDate: value?.[0], endDate: value?.[1] }], staleTime: Infinity, keepPreviousData: true, retry: 0, queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n const results = await BinApiService.getTargetLayerChart(props.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n \r\n return results;\r\n }, enabled: (props.binId ?? 0) > 0,\r\n });\r\n \r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n\r\n return
\r\n \r\n {!isControlled && }\r\n \r\n
\r\n {chartQuery.data && '',\r\n footer(tooltipItems, ) {\r\n const tooltipItem = tooltipItems[0];\r\n const strings: Array = [];\r\n\r\n // @ts-ignore\r\n const routines = tooltipItem.raw.routines;\r\n strings.push(`Routine: ${routines}`)\r\n // @ts-ignore\r\n const steps = tooltipItem.raw.stepLabels;\r\n strings.push(` Steps: ${steps}`)\r\n\r\n // @ts-ignore I know startDate is here\r\n const startDate = dayjs(tooltipItem.raw.startDate);\r\n strings.push(`Start: ${startDate.format('LLL')}`);\r\n // @ts-ignore I know endDate is here\r\n const endDate = dayjs(tooltipItem.raw.endDate);\r\n strings.push(`End: ${endDate.format('LLL')}`);\r\n // @ts-ignore I know durationSeconds is here\r\n let targetDuration = dayjs.duration(tooltipItems[0]?.raw?.durationSeconds * 1000);\r\n strings.push(`Duration: ${floor(targetDuration.asHours())} hrs, ${roundTo((targetDuration).minutes(), 0)} min`);\r\n\r\n return strings.join('\\n');\r\n },\r\n }\r\n },\r\n },\r\n }} data={{ datasets: [\r\n\r\n {\r\n data: chartQuery.data?.data, label: \"Target Layer\", indexAxis: 'x', yAxisID: 'y',\r\n parsing: {yAxisKey: 'targetLayer',},\r\n spanGaps: false,\r\n borderWidth: 8,\r\n },\r\n ] }} />}\r\n
\r\n
\r\n
\r\n \r\n };","import 'chart.js/auto';\r\nimport { Chart } from 'react-chartjs-2';\r\nimport 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';\r\nimport { Line } from 'react-chartjs-2';\r\nimport _uniqueId from 'lodash/uniqueId';\r\n\r\n\r\nimport React, { useCallback, useMemo, useState } from 'react';\r\nimport {\r\n Chart as ChartJS,\r\n CategoryScale,\r\n LinearScale,\r\n PointElement,\r\n LineElement,\r\n Title,\r\n Tooltip,\r\n Legend,\r\n ChartData,\r\n ChartType,\r\n Plugin,\r\n LegendItem,\r\n ChartTypeRegistry,\r\n Point,\r\n BubbleDataPoint,\r\n} from 'chart.js';\r\nimport dayjs, { Dayjs } from 'dayjs';\r\n// @ts-ignore\r\nimport autocolors from 'chartjs-plugin-autocolors';\r\n// @ts-ignore\r\nimport { useQuery, useQueryClient } from '@tanstack/react-query';\r\nimport { binDBKeys } from 'src/pages/binOverview/BinCommander';\r\nimport { useBinStateFromAzure } from 'src/queries/useBinStateFromAzure';\r\nimport { useBinStateFromDevice } from 'src/queries/useBinStateFromDevice';\r\nimport { getUserTimezoneOffset } from '../../../components/BinDetails';\r\nimport { Card, Radio, Skeleton, Space, Switch, Typography } from 'antd';\r\nimport BinApiService from 'src/api/BinApiService';\r\nimport { queryClient } from 'src';\r\nimport SensorType from 'src/consts/SensorType';\r\nimport ReactDOM from 'react-dom';\r\nimport { ChartDatePicker } from './Co2Chart';\r\nimport { RangeValue } from './ChartContainer';\r\nimport { round } from 'lodash';\r\nimport { FanControlMoisturePreferenceProps, tooltipItemRound, useFanHeaterChangeHistory } from './shared';\r\nimport OpiCableDatapointDTO from 'src/models/OpiCableDatapointDTO';\r\nimport { BaseChartQueryOptions, useModeChangeHistory } from \"./modehistoryAndFanChangeHistory\";\r\nimport { queryOptions } from 'src/queries/v4QueryOptions/queryOptions';\r\n\r\n\r\nChartJS.register(\r\n CategoryScale,\r\n LinearScale,\r\n PointElement,\r\n LineElement,\r\n Title,\r\n Tooltip,\r\n Legend, autocolors,\r\n);\r\n\r\nenum LegendView {\r\n Cable = \"Cable\",\r\n Layer = \"Layer\",\r\n}\r\n\r\ninterface CustomLegendProps {\r\n chart: ChartJS;\r\n layerSections: Map>;\r\n cableSections: Map>;\r\n}\r\n\r\nconst CustomLegend = (props: CustomLegendProps) => {\r\n\r\n const chart = props.chart;\r\n const [toggle, setToggle] = useState(LegendView.Layer);\r\n\r\n const [showLegend, setShowLegend] = useState(false);\r\n\r\n const layerSections = props.layerSections;\r\n const cableSections = props.cableSections;\r\n\r\n return (<>\r\n \r\n {\r\n setToggle(e.target.value);\r\n }}>\r\n By Cable\r\n By Layer\r\n \r\n\r\n\r\n Show Legend\r\n setShowLegend(checked)} />\r\n \r\n \r\n\r\n{showLegend && <>\r\n{toggle === LegendView.Layer && [...layerSections.entries()].reverse().map(([layerId, items]) => {\r\n return (
\r\n

{\r\n // @ts-ignore\r\n const { type } = chart.config;\r\n const someVisible = items.some((item: any) => {\r\n return chart.isDatasetVisible(item.datasetIndex);\r\n });\r\n items.forEach((item: any) => {\r\n if (type === 'pie' || type === 'doughnut') {\r\n // Pie and doughnut charts only have a single dataset and visibility is per item\r\n chart.toggleDataVisibility(item.index);\r\n } else {\r\n chart.setDatasetVisibility(item.datasetIndex, !someVisible);\r\n }\r\n })\r\n chart.update();\r\n }}>Layer {layerId}

\r\n\r\n
\r\n {items.map(item => {\r\n return (\r\n
{\r\n // @ts-ignore\r\n const { type } = chart.config;\r\n if (type === 'pie' || type === 'doughnut') {\r\n // Pie and doughnut charts only have a single dataset and visibility is per item\r\n chart.toggleDataVisibility(item.index!);\r\n } else {\r\n chart.setDatasetVisibility(item.datasetIndex!, !chart.isDatasetVisible(item.datasetIndex!));\r\n }\r\n chart.update();\r\n }}\r\n >\r\n \r\n \r\n

\r\n {item.text}\r\n

\r\n
);\r\n })}\r\n
\r\n
);\r\n })}\r\n\r\n\r\n {toggle === LegendView.Cable && [...cableSections.entries()].map(([cableId, items]) => {\r\n return (
\r\n

{\r\n // @ts-ignore\r\n const { type } = chart.config;\r\n const someVisible = items.some((item: any) => {\r\n return chart.isDatasetVisible(item.datasetIndex);\r\n });\r\n items.forEach((item: any) => {\r\n if (type === 'pie' || type === 'doughnut') {\r\n // Pie and doughnut charts only have a single dataset and visibility is per item\r\n chart.toggleDataVisibility(item.index);\r\n } else {\r\n chart.setDatasetVisibility(item.datasetIndex, !someVisible);\r\n }\r\n })\r\n chart.update();\r\n }}>{cableId}

\r\n\r\n
\r\n {items.map(item => {\r\n return (\r\n
{\r\n // @ts-ignore\r\n const { type } = chart.config;\r\n if (type === 'pie' || type === 'doughnut') {\r\n // Pie and doughnut charts only have a single dataset and visibility is per item\r\n chart.toggleDataVisibility(item.index!);\r\n } else {\r\n chart.setDatasetVisibility(item.datasetIndex!, !chart.isDatasetVisible(item.datasetIndex!));\r\n }\r\n chart.update();\r\n }}\r\n >\r\n \r\n \r\n

\r\n {item.text}\r\n

\r\n
);\r\n })}\r\n
\r\n
);\r\n })}\r\n }\r\n );\r\n}\r\n\r\nexport const htmlLegendPlugin: Plugin = {\r\n id: 'htmlLegend',\r\n afterUpdate(chart, args, options) {\r\n if (chart == null) {\r\n return;\r\n }\r\n\r\n // const ul = getOrCreateLegendList(chart, options.containerID);\r\n\r\n // // Remove old legend items\r\n // while (ul.firstChild) {\r\n // ul.firstChild.remove();\r\n // }\r\n // Reuse the built-in legendItems generator\r\n const items = chart?.options?.plugins?.legend?.labels?.generateLabels?.(chart)!;\r\n\r\n\r\n const layerSections = new Map();\r\n\r\n items.forEach(item => {\r\n\r\n\r\n // @ts-ignore\r\n if (layerSections.has(chart.data.datasets[item.datasetIndex!].layerId)) {\r\n // @ts-ignore\r\n layerSections.get(chart.data.datasets[item.datasetIndex!].layerId).push(item);\r\n }\r\n else {\r\n // @ts-ignore\r\n layerSections.set(chart.data.datasets[item.datasetIndex!].layerId, [item]);\r\n }\r\n });\r\n\r\n const cableSections = new Map();\r\n items.forEach(item => {\r\n\r\n\r\n // @ts-ignore\r\n if (cableSections.has(chart.data.datasets[item.datasetIndex!].cableId)) {\r\n // @ts-ignore\r\n cableSections.get(chart.data.datasets[item.datasetIndex!].cableId).push(item);\r\n }\r\n else {\r\n // @ts-ignore\r\n cableSections.set(chart.data.datasets[item.datasetIndex!].cableId, [item]);\r\n }\r\n });\r\n\r\n const groups: Node[] = [];\r\n\r\n const legendSectionOverview = document.createElement(\"section\");\r\n const legendContainer = document.getElementById(options.containerID);\r\n //legendContainer?.replaceChildren(...groups);\r\n\r\n\r\n // React\r\n\r\n const toRender =
\r\n \r\n
;\r\n\r\n ReactDOM.render(toRender, legendContainer);\r\n\r\n\r\n //ul.appendChild(li);\r\n }\r\n};\r\n\r\nconst currentTime = dayjs();\r\n\r\nlet labels: string[] = [];\r\nfor (let i = 0; i < 7; i++) {\r\n labels.push(currentTime.add(5 * i, 'minutes').toISOString());\r\n}\r\n\r\nexport interface BaseChartProps {\r\n binId: number,\r\n deviceId: string,\r\n value?: RangeValue,\r\n onChange?: (value: RangeValue) => void;\r\n min?: number;\r\n max?: number;\r\n chartHeight?: number | string;\r\n // height of container containing chart if multiple\r\n containerMinHeight?: number | string;\r\n}\r\n\r\nexport const useTemperatureCableSensorsChartHistoryQueryOptions = (options: BaseChartQueryOptions) => {\r\n const queryClient = useQueryClient();\r\n const value = options.range;\r\n\r\n return queryOptions(\r\n {\r\n queryKey: [...binDBKeys.bin(options.binId), \"charts\", \"TemperatureCables\", { startDate: value?.[0], endDate: value?.[1] }],\r\n queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n \r\n const result = await BinApiService.getTemperatureCableHistoryDTO(options.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n return result;\r\n },\r\n refetchInterval: Infinity, retry: false, keepPreviousData: true,\r\n enabled: (options.binId ?? 0) > 0 && value?.[0] != null && value?.[1] != null,\r\n }\r\n );\r\n}\r\n\r\n\r\nexport const TemperatureCableChart = (props: BaseChartProps) => {\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(14, 'days');\r\n const [legendId] = useState(_uniqueId('legend-'));\r\n\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n\r\n // Internally, we need to deal with some value. Depending on whether\r\n\t// the component is controlled or not, that value comes from its\r\n\t// props or from its internal state.\r\n\r\n\tconst value = isControlled ? props.value : internalValue;\r\n\r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n\r\n const temperatureCableChartHistoryQueryOptions = useTemperatureCableSensorsChartHistoryQueryOptions({\r\n binId: props.binId,\r\n range: value,\r\n });\r\n const chartQuery = useQuery({...temperatureCableChartHistoryQueryOptions});\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n\r\n return (
\r\n {!isControlled && }\r\n \r\n {chartQuery.data && <>\r\n
\r\n \r\n
\r\n
\r\n \r\n }\r\n
\r\n\r\n
);\r\n}\r\n\r\nexport const TemperatureCableOpiChart = (props: BaseChartProps & FanControlMoisturePreferenceProps) => {\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(14, 'days');\r\n const showHiddenTemp = props.useRHForFanControl === true;\r\n \r\n const [legendId] = useState(_uniqueId('legend-'));\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n\r\n // Internally, we need to deal with some value. Depending on whether\r\n\t// the component is controlled or not, that value comes from its\r\n\t// props or from its internal state.\r\n\r\n\tconst value = isControlled ? props.value : internalValue;\r\n\r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n\r\n const chartQuery = useQuery({\r\n queryKey: [...binDBKeys.bin(props.binId), \"charts\", \"OPICables\", { startDate: value?.[0], endDate: value?.[1] }],\r\n queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n\r\n const result = await BinApiService.getOPICableHistory(props.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n return result;\r\n },\r\n refetchInterval: Infinity, retry: false, keepPreviousData: true,\r\n enabled: (props.binId ?? 0) > 0 && value?.[0] != null && value?.[1] != null,\r\n });\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n return (
\r\n {!isControlled && }\r\n \r\n {chartQuery.data && <>\r\n
\r\n {\r\n return {\r\n ...dataset,\r\n data: dataset.data,\r\n label: dataset.label!,\r\n parsing: {\r\n yAxisKey: 'temperatureF',\r\n }\r\n }\r\n })\r\n }} plugins={[htmlLegendPlugin]} />\r\n
\r\n
\r\n \r\n }\r\n
\r\n\r\n
);\r\n}\r\n\r\nexport const useBinSensePerSensorHistoryQueryOptions = (options: BaseChartQueryOptions) => {\r\n const queryClient = useQueryClient();\r\n const value = options.range;\r\n\r\n return queryOptions(\r\n {\r\n queryKey: [...binDBKeys.bin(options.binId), \"charts\", \"BinSenseCables\", { startDate: value?.[0], endDate: value?.[1] }],\r\n queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n \r\n const result = await BinApiService.getBinSenseCableHistory(options.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n return result;\r\n },\r\n refetchInterval: Infinity, retry: false, keepPreviousData: true,\r\n enabled: (options.binId ?? 0) > 0 && value?.[0] != null && value?.[1] != null,\r\n }\r\n );\r\n}\r\n\r\nexport const TemperatureCableBinSenseChart = (props: BaseChartProps & FanControlMoisturePreferenceProps) => {\r\n const showHiddenTemp = props.useRHForFanControl === true;\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(14, 'days');\r\n \r\n const [legendId] = useState(_uniqueId('legend-'));\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n\r\n // Internally, we need to deal with some value. Depending on whether\r\n\t// the component is controlled or not, that value comes from its\r\n\t// props or from its internal state.\r\n\r\n\tconst value = isControlled ? props.value : internalValue;\r\n\r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n\r\n const chartQuery = useQuery({...useBinSensePerSensorHistoryQueryOptions({\r\n binId: props.binId,\r\n range: value,\r\n })});\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n return (
\r\n {!isControlled && }\r\n \r\n {chartQuery.data && <>\r\n
\r\n {\r\n return {\r\n ...dataset,\r\n data: dataset.data,\r\n label: dataset.label!,\r\n parsing: {\r\n yAxisKey: 'temperatureF',\r\n }\r\n }\r\n })\r\n }} plugins={[htmlLegendPlugin]} />\r\n
\r\n
\r\n \r\n }\r\n
\r\n\r\n
);\r\n}\r\n\r\nexport const useOpiPerSensorHistoryQueryOptions = (options: BaseChartQueryOptions) => {\r\n const queryClient = useQueryClient();\r\n const value = options.range;\r\n return queryOptions({\r\n queryKey: [...binDBKeys.bin(options.binId), \"charts\", \"OPICables\", { startDate: value?.[0], endDate: value?.[1] }],\r\n queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n \r\n const result = await BinApiService.getOPICableHistory(options.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n return result;\r\n },\r\n refetchInterval: Infinity, retry: false, keepPreviousData: true,\r\n enabled: (options.binId ?? 0) > 0 && value?.[0] != null && value?.[1] != null,\r\n }\r\n )\r\n}\r\n\r\nexport const OPICableChart = (props: BaseChartProps & {yTitle?: string} & FanControlMoisturePreferenceProps) => {\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(14, 'days');\r\n \r\n const [legendId] = useState(_uniqueId('legend-'));\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n\r\n // Internally, we need to deal with some value. Depending on whether\r\n\t// the component is controlled or not, that value comes from its\r\n\t// props or from its internal state.\r\n\r\n\tconst value = isControlled ? props.value : internalValue;\r\n\r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n\r\n\r\n const chartQuery = useQuery({\r\n ...useOpiPerSensorHistoryQueryOptions({ binId: props.binId, range: value }),\r\n });\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n const formatYTitle = useCallback(() => {\r\n if (props.useRHForFanControl === false) {\r\n return \"MC (%)\";\r\n } else {\r\n return \"RH %\";\r\n }\r\n }, [props.useRHForFanControl]);\r\n\r\n const chartDataWithParsingOptions = useMemo(() => {\r\n return chartQuery.data?.map(dataset => {\r\n return {\r\n ...dataset,\r\n parsing: {\r\n yAxisKey: props.useRHForFanControl === false ? 'y' : 'relativeHumidity',\r\n }\r\n }\r\n }) ?? [];\r\n }, [chartQuery.data]);\r\n\r\n return (
\r\n {!isControlled && }\r\n \r\n {chartQuery.data &&
\r\n \r\n
\r\n }\r\n
\r\n
\r\n
);\r\n}\r\n\r\nexport const BinSenseCableChart = (props: BaseChartProps & {yTitle?: string} & FanControlMoisturePreferenceProps) => {\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(14, 'days');\r\n \r\n const [legendId] = useState(_uniqueId('legend-'));\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n\r\n // Internally, we need to deal with some value. Depending on whether\r\n\t// the component is controlled or not, that value comes from its\r\n\t// props or from its internal state.\r\n\r\n\tconst value = isControlled ? props.value : internalValue;\r\n\r\n const onDateChange = useCallback((values: RangeValue, formatter: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n\r\n\r\n const chartQuery = useQuery({\r\n queryKey: [...binDBKeys.bin(props.binId), \"charts\", \"BinSenseCables\", { startDate: value?.[0], endDate: value?.[1] }],\r\n queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n\r\n const result = await BinApiService.getBinSenseCableHistory(props.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n return result;\r\n },\r\n refetchInterval: Infinity, retry: false, keepPreviousData: true,\r\n enabled: (props.binId ?? 0) > 0 && value?.[0] != null && value?.[1] != null,\r\n });\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n const formatYTitle = useCallback(() => {\r\n if (props.useRHForFanControl === false) {\r\n return \"MC (%)\";\r\n } else {\r\n return \"RH %\";\r\n }\r\n }, [props.useRHForFanControl]);\r\n\r\n const chartDataWithParsingOptions = useMemo(() => {\r\n return chartQuery.data?.map(dataset => {\r\n return {\r\n ...dataset,\r\n parsing: {\r\n yAxisKey: props.useRHForFanControl === false ? 'y' : 'relativeHumidity',\r\n }\r\n }\r\n }) ?? [];\r\n }, [chartQuery.data]);\r\n\r\n return (
\r\n {!isControlled && }\r\n \r\n {chartQuery.data &&
\r\n \r\n
\r\n }\r\n
\r\n
\r\n
);\r\n}\r\n","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\r\nimport { Skeleton } from \"antd\";\r\nimport React from \"react\";\r\nimport { Line } from \"react-chartjs-2\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { plenumFanDatasetColor } from \"./AmbientPlenumChart\";\r\nimport { useModeChangeHistory } from \"./modehistoryAndFanChangeHistory\";\r\nimport { tooltipItemRound, useFanHeaterChangeHistory } from \"./shared\";\r\nimport { BaseChartProps } from \"./TemperatureCableChart\";\r\n\r\nexport const MoistureRemovalRHChart = (props: BaseChartProps & { shrinkHeight?: boolean }) => {\r\n const queryClient = useQueryClient();\r\n const value = props.value;\r\n\r\n const chartQuery = useQuery({\r\n queryKey: [...binDBKeys.bin(props.binId), \"charts\", \"MoistureRemoval\", { startDate: value?.[0], endDate: value?.[1] }], staleTime: Infinity, keepPreviousData: true, retry: 0, queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n const results = await BinApiService.getMoistureRemovalData(props.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n\r\n return results;\r\n }, enabled: (props.binId ?? 0) > 0,\r\n });\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n return <>\r\n\r\n \r\n
\r\n {chartQuery.data && }\r\n
\r\n
\r\n\r\n ;\r\n}","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\r\nimport { Skeleton } from \"antd\";\r\nimport React from \"react\";\r\nimport { Line } from \"react-chartjs-2\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { plenumFanDatasetColor } from \"./AmbientPlenumChart\";\r\nimport { useModeChangeHistory } from \"./modehistoryAndFanChangeHistory\";\r\nimport { tooltipItemRound, useFanHeaterChangeHistory } from \"./shared\";\r\nimport { BaseChartProps } from \"./TemperatureCableChart\";\r\n\r\nexport const MoistureRemovalAHChart = (props: BaseChartProps & { shrinkHeight?: boolean }) => {\r\n const queryClient = useQueryClient();\r\n const value = props.value;\r\n\r\n const chartQuery = useQuery({\r\n queryKey: [...binDBKeys.bin(props.binId), \"charts\", \"MoistureRemoval\", { startDate: value?.[0], endDate: value?.[1] }], staleTime: Infinity, keepPreviousData: true, retry: 0, queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n const results = await BinApiService.getMoistureRemovalData(props.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n\r\n return results;\r\n }, enabled: (props.binId ?? 0) > 0,\r\n });\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n return <>\r\n\r\n \r\n
\r\n {chartQuery.data && }\r\n
\r\n
\r\n\r\n ;\r\n}","import { useQuery, useQueryClient } from \"@tanstack/react-query\";\r\nimport { Skeleton } from \"antd\";\r\nimport React from \"react\";\r\nimport { Line } from \"react-chartjs-2\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { formatNumber } from \"../BinStatusPage/HeaterControls\";\r\nimport { ambientFanDatasetColor, plenumFanDatasetColor } from \"./AmbientPlenumChart\";\r\nimport { BaseChartQueryOptions, useModeChangeHistory } from \"./modehistoryAndFanChangeHistory\";\r\nimport { tooltipItemRound, useFanHeaterChangeHistory } from \"./shared\";\r\nimport { BaseChartProps } from \"./TemperatureCableChart\";\r\nimport { sortedIndex, sortedLastIndexBy, sortedLastIndex, sortedIndexBy } from \"lodash\";\r\nimport { queryOptions } from \"src/queries/v4QueryOptions/queryOptions\";\r\n\r\nexport const useGrainHeightHistoryQueryOptions = (options: BaseChartQueryOptions) => {\r\n\r\n const queryClient = useQueryClient();\r\n const value = options.range;\r\n\r\n return queryOptions({\r\n queryKey: [...binDBKeys.bin(options.binId), \"charts\", \"RangeFinderChart\", { startDate: value?.[0], endDate: value?.[1] }], staleTime: Infinity, keepPreviousData: true, retry: 0, queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n const results = await BinApiService.getGrainHeightsHistory(options.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n\r\n return results;\r\n }, enabled: (options.binId ?? 0) > 0,\r\n });\r\n}\r\n\r\nexport const useGrainHeightHistoryUserEnteredQueryOptions = (options: BaseChartQueryOptions) => {\r\n\r\n const queryClient = useQueryClient();\r\n const value = options.range;\r\n\r\n return queryOptions({\r\n queryKey: [...binDBKeys.bin(options.binId), \"charts\", \"RangeFinderChartUser\", { startDate: value?.[0], endDate: value?.[1] }], staleTime: Infinity, keepPreviousData: true, retry: 0, queryFn: async (q) => {\r\n const now = value?.[1];\r\n const priorMonth = value?.[0];\r\n const results = await BinApiService.getGrainHeightsHistoryUserEntered(options.binId, priorMonth?.toISOString()!, now?.toISOString()!, null, q.signal);\r\n\r\n queryClient.removeQueries({ queryKey: q.queryKey.slice(0, -1), type: \"inactive\" });\r\n\r\n return results;\r\n }, enabled: (options.binId ?? 0) > 0,\r\n });\r\n}\r\n\r\nexport const RangeFinderChart = (props: BaseChartProps & { shrinkHeight?: boolean }) => {\r\n const queryClient = useQueryClient();\r\n const value = props.value;\r\n\r\n const chartQuery = useQuery({\r\n ...useGrainHeightHistoryQueryOptions({\r\n binId: props.binId,\r\n range: value,\r\n }),\r\n });\r\n\r\n const chartQueryUser = useQuery({\r\n ...useGrainHeightHistoryUserEnteredQueryOptions({\r\n binId: props.binId,\r\n range: value,\r\n }),\r\n });\r\n\r\n const modeHistory = useModeChangeHistory(props.binId, value?.[0], value?.[1]);\r\n const fanHeaterChangeHistory = useFanHeaterChangeHistory(props.binId, value?.[0], value?.[1]);\r\n\r\n return <>\r\n\r\n \r\n
\r\n {chartQuery.data && '',\r\n footer: (context) => {\r\n const item = context[0];\r\n\r\n // @ts-ignore\r\n const percentFullCalculated = item?.raw?.percentFullCalculated;\r\n // @ts-ignore\r\n const bushelsFull = item?.raw?.bushelsFull;\r\n var bushelsFormatted = (new Intl.NumberFormat(undefined, { maximumFractionDigits: 0 })).format(bushelsFull);\r\n const strings = [];\r\n strings.push(`Headspace Sensor: ${formatNumber(percentFullCalculated, { suffix: \"%\", decimalPlaces: 0, filler: \"\" })} (${bushelsFormatted} bu.)`);\r\n\r\n // hack to get get access to datasets in tooltips that don't share the same x-value & frequency\r\n const loadsDataset = item.chart.data.datasets[1]?.data;\r\n const closestUserLoadIndex = sortedLastIndexBy(loadsDataset,\r\n item.raw,\r\n // @ts-ignore\r\n (load) => load.x);\r\n\r\n const getClosestLoadFromNearestIndex = () => {\r\n if (loadsDataset == null) {\r\n return undefined;\r\n }\r\n if (loadsDataset.length === 0) {\r\n return 0;\r\n }\r\n return loadsDataset?.[closestUserLoadIndex - 1];\r\n\r\n }\r\n const closestLoad = getClosestLoadFromNearestIndex();\r\n // @ts-ignore\r\n const percentFullUser = closestLoad?.percentFullUser;\r\n // @ts-ignore\r\n const bushelsFullUser = closestLoad?.bushelsFullUser;\r\n var bushelsFormattedUser = (new Intl.NumberFormat(undefined, { maximumFractionDigits: 0 })).format(bushelsFullUser);\r\n\r\n strings.push(`User Entered: ${formatNumber(percentFullUser, { suffix: \"%\", decimalPlaces: 0, filler: \"\" })} (${bushelsFormattedUser} bu.)`);\r\n\r\n return strings.join('\\n');\r\n }\r\n }\r\n },\r\n },\r\n }} data={{\r\n datasets: [\r\n\r\n {\r\n type: 'line',\r\n data: chartQuery.data ?? [], label: \"Headspace Sensor\", indexAxis: 'x',\r\n parsing: { yAxisKey: 'percentFullCalculated', },\r\n spanGaps: true,\r\n },\r\n {\r\n type: 'line',\r\n // @ts-ignore - different structure than 1st dataset\r\n data: chartQueryUser.data ?? [],\r\n label: \"User Entered\", indexAxis: 'x',\r\n parsing: {\r\n yAxisKey: 'percentFullUser',\r\n },\r\n spanGaps: true,\r\n stepped: true,\r\n backgroundColor: plenumFanDatasetColor,\r\n borderColor: plenumFanDatasetColor,\r\n }\r\n ]\r\n }} />}\r\n
\r\n
\r\n\r\n ;\r\n}","import { CloseSquareOutlined, FullscreenOutlined, RedoOutlined } from \"@ant-design/icons\";\r\nimport { useIsFetching } from \"@tanstack/react-query\";\r\nimport { Button, Col, Row, Select, SelectProps, Space } from \"antd\";\r\nimport dayjs from \"dayjs\";\r\nimport React, { useCallback, useMemo, useState } from \"react\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { AmbientPlenumChart, LayerDewPointChart } from \"./AmbientPlenumChart\";\r\nimport { RangeValue } from \"./ChartContainer\";\r\nimport { ChartDatePicker } from \"./Co2Chart\";\r\nimport { LayerMCHistoryChart } from \"./LayerMCHistoryChart\";\r\nimport { LayerTemperatureHistoryChart as LayerTemperatureHistoryChart } from \"./LayerTemperatureHistoryChart\";\r\nimport { PlenumPressureChart } from \"./PlenumPressureChart\";\r\nimport { TargetLayerChart } from \"./TargetLayerChart\";\r\nimport { BaseChartProps, OPICableChart, TemperatureCableOpiChart, BinSenseCableChart, TemperatureCableBinSenseChart } from \"./TemperatureCableChart\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport { MoistureRemovalRHChart } from \"./MoistureRemovalRHChart\";\r\nimport { MoistureRemovalAHChart } from \"./MoistureRemovalAHChart\";\r\nimport { RangeFinderChart } from \"./RangeFinderChart\";\r\nimport { getDefaultTemperatureSensorType } from \"src/pages/shared/binDTOUtils\";\r\n\r\nexport enum ExtraCharts {\r\n OPIMCSensors = 0,\r\n OPITemperatureSensors = 1,\r\n TemperatureCables = 2,\r\n BinSenseTemperatureSensors = 3,\r\n BinSenseMCSensors = 4,\r\n}\r\n\r\nexport const DryingDashboard = (props: BaseChartProps & {binDTO: BinDTO}) => {\r\n const currentDate = dayjs();\r\n const priorDate = currentDate.subtract(7, 'days');\r\n\r\n\r\n const isControlled = typeof props.value != 'undefined';\r\n const [internalValue, setIntervalValue] = useState([priorDate, currentDate]);\r\n const inprogress = useIsFetching([...binDBKeys.bin(props.binId), \"charts\"]);\r\n\r\n // Internally, we need to deal with some value. Depending on whether\r\n // the component is controlled or not, that value comes from its\r\n // props or from its internal state.\r\n\r\n const value = isControlled ? props.value : internalValue;\r\n\r\n const preferredTemperatureSensorType = props.binDTO?.temperatureSensorsType ?? getDefaultTemperatureSensorType(props.binDTO);\r\n const useRHForFanControl = props.binDTO?.useRHForFanControl!;\r\n\r\n const onDateChange = useCallback((values: RangeValue, formatter?: any) => {\r\n if (props.onChange) {\r\n props.onChange(values);\r\n }\r\n else {\r\n setIntervalValue(values);\r\n }\r\n }, [props.onChange]);\r\n\r\n const [extraCharts, setExtraCharts] = useState>([]);\r\n\r\n\r\n const extraChartSelectOptions = useMemo(() => {\r\n const extraChartSelectOptions: SelectProps['options'] = [];\r\n\r\n extraChartSelectOptions.push({\r\n label: \"OPI MC (per sensor)\",\r\n value: ExtraCharts.OPIMCSensors,\r\n });\r\n\r\n extraChartSelectOptions.push({\r\n label: \"OPI Temp (per sensor)\",\r\n value: ExtraCharts.OPITemperatureSensors,\r\n });\r\n\r\n extraChartSelectOptions.push({\r\n label: \"BinSense MC (per sensor)\",\r\n value: ExtraCharts.BinSenseMCSensors,\r\n });\r\n\r\n extraChartSelectOptions.push({\r\n label: \"BinSense Temp (per sensor)\",\r\n value: ExtraCharts.BinSenseTemperatureSensors,\r\n });\r\n return extraChartSelectOptions;\r\n }, []);\r\n\r\n\r\n return <>\r\n
\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n {\r\n startFillForm.setFieldsValue(populatefillFormInitialValues());\r\n }}\r\n title={\"Start Fill & Aerate Mode\"}\r\n onCancel={closeFillOrAddLoadDialog}\r\n footer={[\r\n ,\r\n \r\n ]}\r\n >\r\n
\r\n Batch settings\r\n
\r\n\r\n \r\n setState({ newBatchCheck: !state.newBatchCheck })}\r\n />\r\n \r\n {state.newBatchCheck && (\r\n \r\n \r\n \r\n )}\r\n\r\n \r\n\r\n \r\n Fan Settings\r\n
\r\n \r\n Fan will run when incoming air EMC is b/t {fillEmpty(startFillFormValues?.emcMin)} &{\" \"}\r\n {fillEmpty(startFillFormValues?.emcMax)}% & temp is b/t {fillEmpty(startFillFormValues?.tempMin)} &{\" \"}\r\n {fillEmpty(startFillFormValues?.tempMax)}℉\r\n \r\n
\r\n\r\n \r\n addonAfter=\"%\" step={0.5} min={0} max={100} />\r\n \r\n \r\n addonAfter=\"%\" step={0.5} min={0} max={100} />\r\n \r\n \r\n addonAfter=\"℉\" step={0.5} />\r\n \r\n\r\n \r\n addonAfter=\"℉\" step={0.5} />\r\n \r\n \r\n \r\n \r\n Cancel\r\n ,\r\n \r\n ]}\r\n >\r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n {\r\n startDryForm.setFieldsValue(populateDryFormInitialValues());\r\n }}\r\n okButtonProps={{ type: \"primary\", disabled: hasPendingLoadDeletes(), htmlType: \"submit\", form: \"startDryForm\" }}\r\n footer={[\r\n ,\r\n \r\n ]}\r\n bodyStyle={{ padding: 0 }}\r\n width=\"60%\"\r\n >\r\n \r\n \r\n \r\n Batch Settings\r\n
\r\n \r\n \r\n \r\n\r\n \r\n\r\n \r\n Fan Settings\r\n
\r\n \r\n Fan will run when incoming air EMC is b/t {fillEmpty(startDryFormValues?.emcMin)} &{\" \"}\r\n {fillEmpty(startDryFormValues?.emcMax)}% & temp is b/t {fillEmpty(startDryFormValues?.tempMin)} &{\" \"}\r\n {fillEmpty(startDryFormValues?.tempMax)}℉.\r\n \r\n
\r\n\r\n \r\n addonAfter=\"%\" step={0.5} min={0} max={100} />\r\n \r\n\r\n \r\n addonAfter=\"%\" step={0.5} min={0} max={100} />\r\n \r\n \r\n addonAfter=\"℉\" step={0.5} />\r\n \r\n\r\n \r\n addonAfter=\"℉\" step={0.5} />\r\n \r\n\r\n \r\n\r\n \r\n Heater Settings\r\n {/*
\r\n \r\n Heater will run when target layer's grain temp is below{\" \"}\r\n {fillEmpty(startDryFormValues?.maxLayerGrainTemperature)}.\r\n */}\r\n
\r\n\r\n \r\n \r\n \r\n{/* \r\n
\r\n }\r\n\r\n // {\r\n // \r\n // \r\n // setState({ addLoadVisible: true })}>\r\n // Add Load\r\n // \r\n // \r\n // \r\n // }\r\n >\r\n \r\n \r\n \r\n \r\n \r\n \r\n {\r\n storageForm.setFieldsValue(populateStorageFormInitialValues());\r\n }}\r\n title=\"Enter Storage\"\r\n okText=\"Enter Storage\"\r\n footer={[\r\n ,\r\n \r\n ]}\r\n bodyStyle={{ padding: 0 }}\r\n width=\"60%\"\r\n >\r\n \r\n \r\n \r\n \r\n Fan Settings\r\n
\r\n \r\n Fan will run when incoming air EMC is b/t {fillEmpty(storageFormValues?.emcMin)} &{\" \"}\r\n {fillEmpty(storageFormValues?.emcMax)}% & temp is b/t {fillEmpty(storageFormValues?.tempMin)} &{\" \"}\r\n {fillEmpty(storageFormValues?.tempMax)}℉\r\n \r\n
\r\n \r\n addonAfter=\"%\" step={0.5} min={0} max={100} />\r\n \r\n\r\n \r\n addonAfter=\"%\" step={0.5} min={0} max={100} />\r\n \r\n \r\n addonAfter=\"℉\" step={0.5} />\r\n \r\n\r\n \r\n addonAfter=\"℉\" step={0.5} />\r\n \r\n\r\n \r\n addonAfter=\" hours\" step={0.5} />\r\n \r\n \r\n \r\n
\r\n \r\n toggleManualStartModal(false)}\r\n destroyOnClose={true}\r\n title=\"Enter Manual\"\r\n okText=\"Enter Manual\"\r\n footer={[\r\n ,\r\n \r\n ]}\r\n bodyStyle={{ padding: 0 }}\r\n >\r\n \r\n \r\n \r\n \r\n addonAfter=\" hours\" step={0.5} />\r\n \r\n \r\n \r\n \r\n \r\n \r\n toggleChangeGrainTypeModal(false)}\r\n destroyOnClose={true}\r\n title=\"Change Grain Type\"\r\n bodyStyle={{ padding: 0 }}\r\n footer={[\r\n ,\r\n \r\n ]}\r\n >\r\n \r\n \r\n
\r\n \r\n \r\n \r\n
\r\n \r\n
\r\n \r\n {state.bin && (\r\n {\r\n const success = await handleFanSettingsSubmit(values);\r\n if (success) {\r\n setState({ showManualFanSettingsModal: false });\r\n }\r\n return success;\r\n }}\r\n onCancel={() => {\r\n setState({ showManualFanSettingsModal: false });\r\n }}\r\n />\r\n )}\r\n {/*//* progress bar */}\r\n \r\n {/* {automationType === AutomationType.DriStack && state.bin && \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n } */}\r\n \r\n \r\n \r\n \r\n \r\n {state.bin?.fans && (\r\n \r\n \r\n \r\n \r\n \r\n \r\n {hasFans && (\r\n \r\n \r\n \r\n )}\r\n \r\n )}\r\n {/* Something is wrong that it requires a manual div with padding to get padding between rows to work. Perhaps A Row within a Col? */}\r\n {hasHeater && selectAutomationType(state.bin) === AutomationType.DriStack && (\r\n
\r\n \r\n \r\n \r\n {state.bin && (\r\n \r\n )}\r\n \r\n \r\n \r\n
\r\n )}\r\n {/* Something is wrong that it requires a manual div with padding to get padding between rows to work. Perhaps A Row within a Col? */}\r\n {state.bin != null && selectAutomationType(state.bin) === AutomationType.DriStack && (\r\n
\r\n \r\n \r\n \r\n {state.bin && (\r\n \r\n )}\r\n \r\n \r\n\r\n {(isAdmin || inManualMode) && (\r\n \r\n \r\n \r\n {\r\n \r\n \r\n fetchBin(props.binInfo?.deviceId!)}\r\n />\r\n \r\n \r\n }\r\n \r\n }\r\n >\r\n {state.bin != null && (\r\n { }}\r\n />\r\n )}\r\n \r\n \r\n \r\n )}\r\n \r\n
\r\n )}\r\n\r\n
\r\n {state.bin?.desiredProperties?.isSeedBin && (\r\n \r\n \r\n \r\n {/* */}\r\n {/* */}\r\n {/* */}\r\n {/* */}\r\n \r\n \r\n )}\r\n
\r\n\r\n {/* Something is wrong that it requires a manual div with padding to get padding between rows to work. Perhaps A Row within a Col? */}\r\n {selectAutomationType(state.bin) === AutomationType.DriStack &&\r\n [OperatingMode.Dry, OperatingMode.TopDry, OperatingMode.PreDry].includes(state.bin?.operatingMode!) && (\r\n
\r\n \r\n
\r\n )}\r\n\r\n {/* {hasFans && binConfiguredWithHeaters(bin) && <>\r\n \r\n \r\n \r\n Heaters\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n Mode: {formatHeaterMode(bin?.grain?.heaterMode!)}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n Suggested Set Point: {heaterRecommendedTempFormatted(state.bin)}\r\n \r\n \r\n }\r\n\r\n {\r\n binConfiguredWithHeaters(bin) && (bin?.fans || []).map((fan) => (\r\n \r\n \r\n Heater {fan.id}\r\n \r\n {(fan?.isHeaterOn\r\n ? turnOffHeater(fan.id!)}>\r\n On\r\n \r\n : turnOnHeater(fan.id!)}>\r\n Off\r\n \r\n )}\r\n \r\n \r\n {(bin?.hardwareYear ?? 0) >= 2022 &&\r\n \r\n \r\n Current: {Number(fan.heaterAmps).toFixed(2)} Amps\r\n \r\n \r\n }\r\n \r\n ))\r\n }\r\n\r\n {selectAutomationType(state.bin) === AutomationType.DriStack &&
\r\n \r\n
} */}\r\n\r\n {automationType === AutomationType.DriStack && \r\n \r\n Fill Logs\r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n }\r\n >\r\n {state.bin != null && selectAutomationType(state.bin) === AutomationType.DriStack && (\r\n \r\n )}\r\n \r\n }\r\n \r\n \r\n {binInfo.id ? (\r\n \r\n ) : (\r\n <>\r\n )}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {operatingModeText}\r\n {bin?.isPaused === true && (\r\n \r\n  (PAUSED)\r\n \r\n )}\r\n \r\n\r\n {showCurrentRoutine && {renderRoutineInfo(pauseMessage)}}\r\n\r\n {(state.bin?.fans ?? []).map(fan => {\r\n return (\r\n \r\n {fan.isOn == null ? \"\" : fan.isOn ? \"On\" : \"Off\"} (\r\n {formatNumber(fan?.fan?.current?.current_mA! / 1000.0, {\r\n decimalPlaces: 2,\r\n filler: \"\",\r\n suffix: \" Amps\"\r\n })}\r\n )\r\n \r\n );\r\n })}\r\n {hasHeater &&\r\n (state.bin?.fans ?? []).map(fan => {\r\n return (\r\n \r\n {fan.isHeaterOn == null ? \"\" : fan.isHeaterOn ? \"On\" : \"Off\"} (\r\n {formatNumber(fan?.heaterAmps, { decimalPlaces: 2, filler: \"\", suffix: \" Amps\" })})\r\n \r\n );\r\n })}\r\n\r\n {automationType === AutomationType.DriStack && (\r\n \r\n {formatBool(state.bin?.compressorState?.isOn, {\r\n true: \"On\",\r\n false: \"Off\",\r\n null: \"\",\r\n bold: false\r\n })}{\" \"}\r\n (\r\n {formatNumber(state.bin?.compressorState?.inlinePressure?.psi, {\r\n decimalPlaces: 1,\r\n suffix: \" PSI\"\r\n })}\r\n )\r\n \r\n )}\r\n \r\n\r\n
\r\n \r\n {permissions.canWrite && }\r\n
\r\n
\r\n \r\n
\r\n {state.bin?.hasCamera && (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n )}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {state.bin != null && selectAutomationType(state.bin) === AutomationType.DriStack && (\r\n \r\n )}\r\n \r\n \r\n \r\n {state.bin != null && hasCableSensors && (\r\n \r\n )}\r\n \r\n \r\n {hasFans && (\r\n \r\n \r\n \r\n formatNumber(value, { decimalPlaces: 0, filler: \"\" })}\r\n />\r\n formatNumber(value, { decimalPlaces: 0, filler: \"\" })}\r\n />\r\n formatNumber(value, { decimalPlaces: 0, filler: \"\" })}\r\n />\r\n\r\n {state.bin?.useRHForFanControl === false && formatNumber(value, { decimalPlaces: 1, filler: \"\" })}\r\n />}\r\n
\r\n
\r\n )}\r\n \r\n
\r\n {/* \r\n \r\n \r\n {isAdmin && \r\n {!mcChartOffline ?\r\n \r\n\r\n \r\n : }\r\n }\r\n \r\n \r\n */}\r\n\r\n {/* \r\n \r\n \r\n {renderedCrit}\r\n \r\n \r\n\r\n \r\n \r\n {renderedWarning}\r\n \r\n \r\n */}\r\n\r\n {/* \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n \r\n
*/}\r\n \r\n {\" \"}\r\n \r\n ) : (\r\n <>\r\n {\r\n props.history.goBack();\r\n }}\r\n title={binInfo.name}\r\n />\r\n \r\n \r\n )}\r\n \r\n \r\n }>\r\n {state.bin != null && props.binInfo?.id != null && props.binInfo?.deviceId != null\r\n &&\r\n \r\n \r\n }\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n {state.binInfo.binLat && state.binInfo.binLng ? (\r\n \r\n ) : (\r\n \r\n \r\n \r\n )}\r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default withRouter(BinStatsPage);\r\n","import { useQuery } from \"@tanstack/react-query\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { deviceQueryKeys, getUserTimezoneOffset } from \"src/components/BinDetails\";\r\n\r\nexport const useBinStateFromAzure = (params: {\r\n azureDeviceID: string | null | undefined, binId: number, userTimeZoneOffset?: number\r\n},\r\n queryOptions: {\r\n enabled?: boolean,\r\n staleTime?: number,\r\n refetchInterval?: number,\r\n }\r\n) => {\r\n\r\n return useQuery(deviceQueryKeys.stateFromAzure(params.azureDeviceID!), async (q) => {\r\n if (params.azureDeviceID == null) {\r\n throw new Error(\"No Azure device supplied\");\r\n }\r\n const binDTO = await BinApiService.getBinDetailFromAzure(params.azureDeviceID!, params.binId, params.userTimeZoneOffset ?? getUserTimezoneOffset(), q.signal);\r\n if (binDTO == null) {\r\n throw new Error(\"Bin State from device is empty\");\r\n }\r\n return binDTO;\r\n }, {\r\n enabled: queryOptions.enabled,\r\n staleTime: queryOptions.refetchInterval,\r\n refetchInterval: queryOptions.refetchInterval,\r\n onError: (err) => console.error(\"problem getting binState from Azure\", err),\r\n });\r\n};","// Constants TypeScript files should be AUTO-GENERATED by the Typewriter Visual Studio plugin. Do not modify this file.\r\nenum HeaterOffReason {\r\n Requested = 'Requested',\r\n FanIsOff = 'FanIsOff',\r\n LayerTemperatureAboveLimit = 'LayerTemperatureAboveLimit',\r\n PlenumTempAboveLimit = 'PlenumTempAboveLimit',\r\n Unknown = 'Unknown',\r\n}\r\nexport default HeaterOffReason;\r\n","import { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\"\r\nimport { Button, Card, Col, ConfigProvider, Divider, Form, InputNumber, message, Modal, Popconfirm, Radio, RadioChangeEvent, Row, Segmented, Select, Skeleton, Space, Spin, Statistic, Tag, Tooltip, Typography } from \"antd\"\r\nimport dayjs, { Dayjs } from \"dayjs\"\r\nimport React, { useCallback, useState } from \"react\"\r\nimport BinDTO from \"src/models/BinDTO\"\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\"\r\nimport { deviceQueryKeys, getUserTimezoneOffset } from \"src/components/BinDetails\"\r\nimport TimeAgo from 'javascript-time-ago'\r\n\r\nimport en from 'javascript-time-ago/locale/en.json'\r\n\r\nTimeAgo.addDefaultLocale(en);\r\nimport ReactTimeAgo from 'react-time-ago'\r\nimport { useBinStateFromDevice } from \"src/queries/useBinStateFromDevice\"\r\nimport BinApiService from \"src/api/BinApiService\"\r\nimport { invalidateBinState } from \"./BinStatsPageParent\"\r\nimport { delay, layout2 } from \"./BinStatsPage\"\r\nimport FanOffReason from \"src/consts/FanOffReason\"\r\nimport { OperatingMode } from \"./BinVisualThree\"\r\nimport { EditOutlined, QuestionCircleFilled, QuestionCircleOutlined } from \"@ant-design/icons\"\r\nimport { useForm } from \"antd/es/form/Form\"\r\nimport { fillEmpty } from \"src/pages/features/WeatherMonitorForm\"\r\nimport HeaterOffReason from \"src/consts/HeaterOffReason\"\r\nimport { AmbientConditionsStatusIndicator, AmbientemcText, EmcRangeIndicator, isAnyFanOn, plenumEMCText, TemperatureRangeIndicator } from \"src/pages/features/WeatherMonitorAntdOnly\"\r\nimport { usePermissions } from \"src/pages/features/usePermissions\"\r\n\r\ninterface HeaterControlProps {\r\n azureDeviceId: string,\r\n binId: number,\r\n binDTO: BinDTO | null | undefined\r\n}\r\n\r\nexport const useSetHeaterMode = (deviceId: string) => {\r\n const queryClient = useQueryClient();\r\n\r\n return useMutation({\r\n mutationFn: async (params: { heaterDesiredOn: boolean }) => {\r\n const res = await BinApiService.setHeaterMode({ heaterDesiredOn: params.heaterDesiredOn, deviceId: deviceId })\r\n return res;\r\n },\r\n onSuccess: async (data) => {\r\n if (data.success === true) {\r\n message.info(\"Updated heater state\");\r\n }\r\n },\r\n // If the mutation fails,\r\n // use the context returned from onMutate to roll back\r\n onError: async (error, variables, context) => {\r\n if (error) {\r\n message.error(\"failed to update desired heater mode\");\r\n }\r\n },\r\n onSettled: async () => {\r\n //await queryClient.invalidateQueries(deviceQueryKeys.stateFromDevice(deviceId));\r\n // wait a few seconds for fans/heaters to get updated...\r\n await delay(3 * 1000);\r\n try {\r\n const res = await BinApiService.uploadBinStateToAzure(deviceId);\r\n if (res.success === true) {\r\n console.log(\"requested new binstate\");\r\n }\r\n } catch (error) {\r\n console.error(\"error requesting binstate invalidation\", error);\r\n }\r\n }\r\n });\r\n}\r\n\r\nexport const formatNumber = (num: number | null | undefined, options: { decimalPlaces?: number | true, filler?: string, suffix?: string, showSuffixIfNoValue?: boolean }) => {\r\n const decimals = options?.decimalPlaces ?? 1;\r\n const filler = options.filler ?? \"No Data\";\r\n const suffix = options.suffix ?? \"\";\r\n const showSuffixIfNoValue = options.showSuffixIfNoValue ?? false;\r\n if (Number.isFinite(num)) {\r\n let safeNumber = num as number;\r\n let rounded: number | string = safeNumber;\r\n if (decimals === true) {\r\n rounded = safeNumber;\r\n }\r\n else {\r\n rounded = Number(safeNumber)?.toFixed(decimals);\r\n }\r\n return `${rounded}${suffix}`;\r\n } else {\r\n\r\n return `${filler}${showSuffixIfNoValue ? suffix : ''}`;\r\n }\r\n}\r\n\r\nexport const formatHeaterStatus = (binDTO: BinDTO | null | undefined) => {\r\n if (binDTO == null) {\r\n return Status unknown;\r\n }\r\n\r\n const heaterIsPaused = [FanOffReason.WeatherConditions].includes(binDTO.fanOperations?.offReason!);\r\n if (heaterIsPaused) {\r\n return null;\r\n }\r\n\r\n const heaterStatus = binDTO.fans?.some(fan => fan.isHeaterOn === true);\r\n if (heaterStatus === true) {\r\n return Heater(s) are running\r\n }\r\n else if (heaterStatus === false) {\r\n return Heater(s) are OFF\r\n }\r\n else {\r\n return Unknown;\r\n }\r\n}\r\n\r\nexport const formatBool = (status: boolean | null | undefined, options?: { true?: string, false?: string, null?: string, bold?: boolean }) => {\r\n const trueText = options?.true ?? \"ON\";\r\n const falseText = options?.false ?? \"OFF\";\r\n const nullText = options?.null ?? \"No Data\";\r\n\r\n let text = nullText;\r\n if (status == null) {\r\n text = nullText;\r\n } else if (status === true) {\r\n text = trueText;\r\n } else if (status === false) {\r\n text = falseText;\r\n }\r\n return {text};\r\n}\r\n\r\nexport const HeaterMcReductionHelpText = () => {\r\n\r\n return <>\r\n When the fan & heater pauses due to weather, this offset temporarily increases the maximum EMC setpoint.\r\n \r\n
\r\n \r\n For example, if the EMC range is initially set between 5% - 15%, and there's a 5% \"Heater EMC Offset\", the upper EMC setpoint will be adjusted to 20%. This changes the EMC range to 5% - 20%, allowing the fan & heater to operate even when the air has higher moisture content. This adjustment accounts for the heater's capability to lower the EMC of incoming air.\r\n \r\n ;\r\n}\r\n\r\n\r\nexport enum DerivedHeaterMode {\r\n AUTO = \"Auto\",\r\n OFF = \"Off\",\r\n ON = \"On\",\r\n}\r\n\r\nexport const useUpdateLayerGrainTemperature = (deviceId: string) => {\r\n return useMutation({\r\n mutationFn: async (params: { maxLayerGrainTemperatureF: number }) => {\r\n return await BinApiService.updateMaxLayerGrainTemp({ deviceId: deviceId, temperatureF: params.maxLayerGrainTemperatureF });\r\n }\r\n });\r\n}\r\n\r\nexport const useUpdateHeaterMcOffsetMutation = (deviceId: string) => {\r\n return useMutation({\r\n mutationFn: async (params: { heaterMcOffset: number }) => {\r\n return await BinApiService.setHeaterPlenumMcOffset(params.heaterMcOffset, deviceId);\r\n }\r\n });\r\n}\r\n\r\nconst updateMaxGrainLayerTemperatureFormId = \"update-max-layer-grain-temperature-form-id\";\r\n\r\ninterface UpdateLayerTemperatureFormValues {\r\n maxLayerGrainTemperatureF: number;\r\n}\r\ninterface UpdateLayerTemperatureFormProps {\r\n maxGrainTempDefault: number,\r\n onFinish: (values: UpdateLayerTemperatureFormValues) => void,\r\n}\r\nconst UpdateLayerTemperatureForm = (props: UpdateLayerTemperatureFormProps) => {\r\n\r\n const [form] = useForm();\r\n\r\n return (\r\n
\r\n \r\n \r\n \r\n\r\n
\r\n );\r\n}\r\n\r\nconst FormatFanHeaterEMCText = (props: { binDTO: BinDTO }) => {\r\n\r\n if (props.binDTO?.weatherMonitorState == null) {\r\n return null;\r\n }\r\n\r\n const originalMinEMC = props.binDTO?.weatherMonitorState?.minMcLimit;\r\n const originalMaxEMC = props.binDTO?.weatherMonitorState?.maxMcLimit;\r\n\r\n let offsetMaxEMC = originalMaxEMC;\r\n if (offsetMaxEMC != null) {\r\n offsetMaxEMC += props.binDTO?.weatherMonitorState?.heaterPlenumMcOffset! ?? -5;\r\n }\r\n const showHeaterOffset = props.binDTO?.fanOperations?.desiredHeaterOn;\r\n return <>\r\n When paused, the fan and heater will run between {fillEmpty(originalMinEMC)}% and \r\n {fillEmpty(originalMaxEMC)}%\r\n {showHeaterOffset && <> {fillEmpty(offsetMaxEMC)}%. +{props.binDTO?.weatherMonitorState?.heaterPlenumMcOffset}% Heater EMC Offset}\r\n \r\n\r\n\r\n}\r\n\r\nexport const HeaterControls = (props: HeaterControlProps) => {\r\n\r\n const [changeGrainLayerTemperature, setChangeGrainLayerTemperature] = useState(false);\r\n\r\n const mutateHeaterMode = useSetHeaterMode(props.azureDeviceId);\r\n\r\n const mutateGrainLayerTemperature = useUpdateLayerGrainTemperature(props.azureDeviceId);\r\n const updateHeaterMcOffsetMutation = useUpdateHeaterMcOffsetMutation(props.azureDeviceId);\r\n\r\n const anyFanOn = isAnyFanOn(props.binDTO?.fans!);\r\n\r\n const onChangeClick = useCallback((option: RadioChangeEvent) => {\r\n let heaterMode: boolean = false;\r\n const value = option.target.value;\r\n if (value === DerivedHeaterMode.ON) {\r\n heaterMode = true;\r\n }\r\n console.log(\"heater change to send: \", heaterMode);\r\n mutateHeaterMode.mutate({ heaterDesiredOn: heaterMode });\r\n }, [mutateHeaterMode, props.azureDeviceId]);\r\n\r\n\r\n //const AmbientAvgTempF = 55.0;\r\n const AmbientAvgTempF = props.binDTO?.ambientAir?.temp;\r\n //const PlenumTempAvgF = 20.0;\r\n const PlenumTempAvgF = props.binDTO?.plenumAir?.temp;\r\n //const plenumTempLimitF = 105;\r\n const plenumTempLimitF = props.binDTO?.desiredProperties?.overrides?.plenumHighTempF ?? 105;\r\n\r\n\r\n const renderOffHeaterReason = (binDto: BinDTO | null | undefined) => {\r\n\r\n const reasons = [];\r\n\r\n const fanOffReason = binDto?.fanOperations?.offReason;\r\n if (fanOffReason === FanOffReason.WeatherConditions) {\r\n reasons.push(
Heater is PAUSED b/c incoming air conditions are not within the setpoints.\r\n The Heater will resume when conditions are within proper conditions for 10 minutes. \r\n
\r\n
);\r\n }\r\n else if (binDto?.fanOperations?.offReason) {\r\n reasons.push(Reason fan(s) are OFF: {binDto?.fanOperations?.offReason})\r\n }\r\n const getNextPossibleResumeTime = (binDTO: BinDTO): Dayjs | null => {\r\n let nextPossibleResume = binDTO?.fanControllerState?.timeTillNextPlenumTemperatureCheck;\r\n\r\n const fanheaterNextResume = binDTO?.fanControllerState?.timeTillFanHeaterPlenumPauseIsDone;\r\n if (fanheaterNextResume != null) {\r\n // the fans have been shut off\r\n // use this time instead\r\n nextPossibleResume = fanheaterNextResume;\r\n }\r\n\r\n if (nextPossibleResume == null) {\r\n return null;\r\n }\r\n return dayjs(nextPossibleResume);\r\n }\r\n\r\n if (binDto?.fanOperations?.heaterOffReason === HeaterOffReason.PlenumTempAboveLimit) {\r\n const resumeTime = getNextPossibleResumeTime(binDto);\r\n if (resumeTime) {\r\n\r\n const elem = \r\n Paused because the plenum temp exceeded safeguard limits\r\n Heater will attempt to resume \r\n \r\n \r\n reasons.push(elem);\r\n }\r\n }\r\n\r\n return reasons;\r\n }\r\n\r\n const getDerivedHeaterMode = (binDTO: BinDTO | null | undefined): DerivedHeaterMode | null => {\r\n let derivedHeaterMode: DerivedHeaterMode = DerivedHeaterMode.OFF;\r\n if (binDTO?.fanOperations?.desiredHeaterOn === true) {\r\n derivedHeaterMode = DerivedHeaterMode.ON;\r\n }\r\n\r\n return derivedHeaterMode;\r\n }\r\n const derivedHeaterMode = getDerivedHeaterMode(props.binDTO)\r\n\r\n const heaterOffReasons = renderOffHeaterReason(props.binDTO);\r\n const heaterControlAllowed = [OperatingMode.Manual, OperatingMode.Dry, OperatingMode.PreDry, OperatingMode.TopDry].includes(props.binDTO?.operatingMode!)\r\n const permissions = usePermissions();\r\n\r\n const getHeaterDisabledText = () => {\r\n if (!permissions.canWrite) {\r\n return \"View Only\";\r\n }\r\n if (!heaterControlAllowed) {\r\n return \"Controllable in Dry Mode or User Control mode\";\r\n }\r\n return '';\r\n }\r\n\r\n const heaterControlText = getHeaterDisabledText();\r\n\r\n return (<> \r\n\r\n \r\n\r\n \r\n \r\n
\r\n \r\n \r\n Off\r\n On\r\n {/*\r\n Auto\r\n */}\r\n \r\n \r\n {mutateHeaterMode.isLoading && }\r\n
\r\n\r\n \r\n \r\n Heater EMC Offset Settings\r\n Adjust Heater EMC Offset }>\r\n \r\n }\r\n okButtonProps={{\r\n form: 'test',\r\n htmlType: 'submit',\r\n }}\r\n okText=\"Apply\"\r\n description={<>\r\n
{\r\n updateHeaterMcOffsetMutation.mutate({ heaterMcOffset: vals.heaterMcOffset }, {\r\n onSuccess(data, variables, context) {\r\n if (data.success) {\r\n message.success(\"Heater EMC Offset applying...\");\r\n }\r\n else {\r\n message.error(\"Problem setting heater EMC Offset\");\r\n }\r\n },\r\n onError(error, variables, context) {\r\n console.error(\"Error applying heater EMC Offset\", error);\r\n message.error(\"Error applying heater EMC Offset\");\r\n },\r\n });\r\n }}>\r\n \r\n \r\n \r\n
\r\n }>\r\n ,\r\n ,\r\n ]\r\n }\r\n >\r\n \r\n \r\n {\r\n mutateGrainLayerTemperature.mutate({ maxLayerGrainTemperatureF: values.maxLayerGrainTemperatureF }, {\r\n onSuccess: (data, variables, context) => {\r\n if (data.success === true) {\r\n message.success(\"Updated max layer grain temperature\");\r\n setChangeGrainLayerTemperature(false);\r\n }\r\n else {\r\n message.error(\"Problem updating max layer grain temperature\");\r\n }\r\n },\r\n onError: (error) => {\r\n console.log(\"error while updating max layer grain temperature\", error);\r\n message.error(\"Error while updating max layer grain temperature\");\r\n },\r\n }\r\n );\r\n }} maxGrainTempDefault={props.binDTO?.fanOperations?.maxAllowedLayerTemperature ?? 75} />\r\n \r\n \r\n \r\n\r\n
);\r\n}","import { HistoryOutlined } from \"@ant-design/icons\";\r\nimport { useMutation } from \"@tanstack/react-query\";\r\nimport { message, Tooltip, Popconfirm, Button } from \"antd\";\r\nimport React from \"react\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { usePermissions } from \"src/pages/features/usePermissions\";\r\nimport { useBinInfoContext } from \"src/queries/BinInfoContext\";\r\n\r\nexport const ResetFanRuntime = (props: {}) => {\r\n const permissions = usePermissions();\r\n const binInfo = useBinInfoContext();\r\n\r\n const resetFanRuntimeMutation = useMutation({\r\n mutationFn: async () => {\r\n return await BinApiService.resetFanRuntime(binInfo.deviceId!);\r\n },\r\n onSuccess(data, variables, context) {\r\n if (data?.success) {\r\n message.success(\"Reset fan runtime\");\r\n }\r\n else {\r\n message.error(\"Problem resetting fan runtime\");\r\n }\r\n },\r\n onError(error, variables, context) {\r\n console.error(\"Error resetting fan runtime\", error);\r\n message.error(\"Error resetting fan runtime\");\r\n },\r\n });\r\n\r\n return (\r\n \r\n resetFanRuntimeMutation.mutate()}>\r\n \r\n \r\n \r\n );\r\n}","import React, { useState } from 'react';\r\nimport { Card, Descriptions, Typography, Space, Tooltip, Button, Modal, Row, Col, Form, Select, message, Popconfirm } from 'antd';\r\nimport {FormItem} from 'react-hook-form-antd';\r\nimport { EditOutlined, HistoryOutlined, InfoCircleOutlined } from '@ant-design/icons';\r\nimport { formatNumber } from './HeaterControls';\r\nimport { hasRangeFinder } from 'src/pages/features/shared';\r\nimport BinDTO from 'src/models/BinDTO';\r\nimport dayjs, { Dayjs } from 'dayjs';\r\nimport {useForm as useFormRHF} from 'react-hook-form';\r\nimport GrainType from 'src/consts/GrainType';\r\nimport BinApiService from 'src/api/BinApiService';\r\nimport BinInfoDTO from 'src/models/BinInfoDTO';\r\nimport { layout2 } from './BinStatsPage';\r\nimport { bind } from 'lodash';\r\nimport ThermocoupleDTO from 'src/models/ThermocoupleDTO';\r\nimport { getDefaultTemperatureSensorType } from 'src/pages/shared/binDTOUtils';\r\nimport TemperatureSensorEnum from 'src/consts/TemperatureSensorEnum';\r\nimport { formatBatchStartDate } from './BatchStartTag';\r\nimport AutomationType from 'src/consts/AutomationType';\r\nimport { useBinInfoContext } from 'src/queries/BinInfoContext';\r\nimport { usePermissions } from 'src/pages/features/usePermissions';\r\nimport { useMutation, useQuery } from '@tanstack/react-query';\r\nimport { fanDurationFormatted } from './BinVisualThree';\r\nimport { ResetFanRuntime } from './ResetFanRuntime';\r\nimport { useBinDTOContext } from 'src/queries/BinDTOContext';\r\n\r\nexport interface BinStatusCardProps{\r\n bin: BinDTO | undefined,\r\n binInfo: BinInfoDTO | undefined,\r\n inBinVisual: Boolean\r\n}\r\n\r\nexport interface ChangeGrainTypeFormValues {\r\n grainType: GrainType;\r\n}\r\n\r\nexport const BinStatusCard = (props: BinStatusCardProps) => {\r\n const [showChangeGrainType, setShowChangeGrainType] = useState(false);\r\n const binInfo = useBinInfoContext();\r\n const permissions = usePermissions();\r\n const bindtoContext = useBinDTOContext();\r\n const useRHForFanControl = bindtoContext?.binDTO?.useRHForFanControl!;\r\n\r\n const calculateEstimatedBu = (binDTO: BinDTO) : number | null => {\r\n\r\n const percentFull = binDTO?.grain?.percentFullCalculated;\r\n const calculatedBinCapacity = binDTO?.capacityBushels;\r\n\r\n if (binDTO.grain == null) {\r\n return null;\r\n }\r\n if (percentFull == null) {\r\n return null;\r\n }\r\n if (calculatedBinCapacity == null) {\r\n return null;\r\n }\r\n\r\n return calculatedBinCapacity * (percentFull / 100.0);\r\n }\r\n\r\n const changeGrainTypeDefaultValues = {grainType: props.bin?.grain?.grainType ?? GrainType.Corn};\r\n\r\n const changeGrainTypeFormMethods = useFormRHF({\r\n defaultValues: changeGrainTypeDefaultValues,\r\n values: changeGrainTypeDefaultValues,\r\n resetOptions: {\r\n keepDirtyValues: true,\r\n },\r\n });\r\n\r\n const calculateBinCapacity = (binDTO: BinDTO) : number | null => {\r\n const calculatedBinCapacity = binDTO?.capacityBushels;\r\n if (calculatedBinCapacity == null) {\r\n return null;\r\n }\r\n\r\n return calculatedBinCapacity;\r\n\r\n }\r\n\r\n const modeWaitMessage = (meessageContent?: string) => {\r\n let toShow = \"Applying mode change, site will update automatically in about 15 seconds\";\r\n if (meessageContent != null) {\r\n toShow = meessageContent;\r\n }\r\n message.info(toShow);\r\n }\r\n\r\n const changeGrainTypeHandleSubmit = async (vals: ChangeGrainTypeFormValues) => {\r\n const grainType = vals.grainType;\r\n\r\n // setFillValues({ grainType: grainType });\r\n try {\r\n const result = await BinApiService.setGrainType(props.bin?.deviceId!, {grainType: grainType});\r\n if (result.success) {\r\n modeWaitMessage();\r\n toggleChangeGrainTypeModal(false);\r\n //changeGrainTypeFormMethods.resetFields();\r\n } else {\r\n message.error(\"System did not change grain type\", 3);\r\n }\r\n } catch (error) {\r\n console.log(\"error changing grain type\", error);\r\n message.error('Problem encountered changing Grain Type', 3);\r\n } finally {\r\n // not called since the module should update\r\n //fetchBin(props.binInfo?.deviceId!);\r\n }\r\n }\r\n\r\n const formatEstimatedBuWithComma = (binDTO: BinDTO | undefined) : string | null => {\r\n if (binDTO == null) {\r\n return \"---\";\r\n }\r\n const calculatedBu = calculateEstimatedBu(binDTO);\r\n if (calculatedBu == null) {\r\n return \"--\";\r\n }else{\r\n const formattedNumber = Math.round(calculatedBu).toLocaleString(); // Format the number with commas\r\n return `${formattedNumber} bu.`;\r\n }\r\n }\r\n\r\n const formatEnteredBuWithComma = (binDTO: BinDTO | undefined) : string | null => {\r\n if (binDTO == null) {\r\n return \"---\";\r\n }\r\n const EnteredBu = binDTO.grain?.totalBushelsEntered;\r\n if (EnteredBu == null) {\r\n return \"--\";\r\n }else{\r\n const formattedNumber = Math.round(EnteredBu).toLocaleString(); // Format the number with commas\r\n return `${formattedNumber} bu.`;\r\n }\r\n }\r\n\r\n const formatBinCapacityBuWithComma = (binDTO: BinDTO | undefined) : string | null => {\r\n if (binDTO == null) {\r\n return \"---\";\r\n }\r\n const binCap = calculateBinCapacity(binDTO);\r\n if (binCap == null) {\r\n return \"--\";\r\n }else{\r\n const formattedNumber = Math.round(binCap).toLocaleString(); // Format the number with commas\r\n return `${formattedNumber} bu.`;\r\n }\r\n }\r\n\r\n // Method to toggle the change grain type modal\r\n const toggleChangeGrainTypeModal = (visible: boolean) => {\r\n // Implement your modal toggle logic here\r\n setShowChangeGrainType(visible);\r\n };\r\n\r\n const getAvgBinTemp = (binDTO: BinDTO) => {\r\n\r\n const preferredTemperatureSensorType = binDTO?.temperatureSensorsType ?? getDefaultTemperatureSensorType(binDTO);\r\n\r\n const useMoistureCables = [TemperatureSensorEnum.Opi, TemperatureSensorEnum.PowerCast, TemperatureSensorEnum.BinSense].includes(preferredTemperatureSensorType);\r\n const useThermocouples = [TemperatureSensorEnum.Thermocouple].includes(preferredTemperatureSensorType);\r\n\r\n if (useThermocouples && !binDTO?.temperatureCables?.length) return null;\r\n if (useMoistureCables && !(binDTO?.opiMoistureCables?.length || binDTO?.moistureCables_RF?.length)) return null;\r\n\r\n let avgBinTemp = 0;\r\n let numSensors = 0;\r\n\r\n binDTO.layerGrainStates?.forEach( (layer) => {\r\n if(useThermocouples && layer.thermocoupleTemperatureF != null){\r\n avgBinTemp += layer.thermocoupleTemperatureF;\r\n numSensors++;\r\n }\r\n else if (useMoistureCables && layer.temperatureF != null) {\r\n avgBinTemp += layer.temperatureF;\r\n numSensors++;\r\n }\r\n });\r\n\r\n return avgBinTemp / numSensors;\r\n }\r\n\r\n const getAvgBinMC = (binDTO: BinDTO) => {\r\n if (!binDTO?.opiMoistureCables?.length) return null;\r\n\r\n\r\n let avgBinMc = 0;\r\n let numSensors = 0;\r\n \r\n binDTO.layerGrainStates?.forEach( (layer) => {\r\n if(layer.moistureContent != null){\r\n avgBinMc += layer.moistureContent;\r\n numSensors++;\r\n }\r\n });\r\n\r\n return avgBinMc / numSensors;\r\n }\r\n\r\n \r\n const getAvgBinRH = (binDTO: BinDTO) => {\r\n if (!binDTO?.opiMoistureCables?.length) return null;\r\n\r\n\r\n let avg = 0;\r\n let numSensors = 0;\r\n \r\n binDTO.layerGrainStates?.forEach( (layer) => {\r\n if(layer.relitiveHumidity != null){\r\n avg += layer.relitiveHumidity;\r\n numSensors++;\r\n }\r\n });\r\n\r\n return avg / numSensors;\r\n }\r\n\r\n return (\r\n <>\r\n \r\n \r\n\r\n \r\n \r\n {props.bin?.grain?.grainType || '--'}\r\n \r\n \r\n \r\n \r\n \r\n {\r\n (props.bin?.innerCO2 != null || props.bin?.outerCO2 != null) &&\r\n \r\n {formatNumber(props.bin?.cO2Level, { decimalPlaces: 0 })}\r\n \r\n }\r\n \r\n {formatNumber(getAvgBinTemp(props.bin!), {decimalPlaces:1, suffix:\"°F\"})}\r\n \r\n {useRHForFanControl === true && \r\n {formatNumber(getAvgBinRH(props.bin!), {decimalPlaces:1, suffix:\"%\"})}\r\n }\r\n {useRHForFanControl === false && \r\n {formatNumber(getAvgBinMC(props.bin!), {decimalPlaces:1, suffix:\"%\"})}\r\n }\r\n\r\n \r\n {hasRangeFinder(props.bin) && (\r\n <>\r\n \r\n {formatNumber(props.bin?.grain?.percentFullCalculated, { decimalPlaces: 0, suffix: '%' })} (\r\n {formatEstimatedBuWithComma(props.bin)})\r\n \r\n {binInfo.automationType === AutomationType.DriStack && \r\n {formatNumber(props.bin?.grain?.percentFullEntered, { decimalPlaces: 0, suffix: '%' })} (\r\n {formatEnteredBuWithComma(props.bin)}\r\n {/* {formatNumber(props.bin?.grain?.totalBushelsEntered! / 1000.0, {\r\n decimalPlaces: 2,\r\n suffix: 'k bu.',\r\n })} */}\r\n )\r\n \r\n }\r\n \r\n )}\r\n {/* {formatBinCapacityBuWithComma(props.bin)} */}\r\n\r\n {binInfo?.automationType === AutomationType.DriStack && <>\r\n \r\n {formatNumber(props.bin?.grain?.weightedAverageMcIn, { decimalPlaces: 1, suffix: '%' })}\r\n \r\n \r\n {formatNumber(props.bin?.grain?.weightedAverageMcOut, { decimalPlaces: 1, suffix: '%' })}\r\n \r\n \r\n }\r\n\r\n {props.bin?.automationType === AutomationType.DriStack && \r\n {props.bin?.currentBatch?.startDate != null ? formatBatchStartDate(props.bin?.currentBatch?.startDate) : null}\r\n \r\n }\r\n\r\n \r\n Fan Runtime\r\n \r\n \r\n \r\n \r\n \r\n {/** Warning: expanding this to DriStack will clear loads. Either make this clear to the user or redo the ResetFanRuntime module command */}\r\n {binInfo.automationType === AutomationType.AutoBin && }\r\n \r\n )}\r\n >\r\n {dayjs\r\n .duration((props?.bin?.fanOperations?.fanRunTimeSeconds ?? 0) * 1000)\r\n .format('D [days] H [hours] & m [minutes]')}\r\n \r\n\r\n \r\n \r\n toggleChangeGrainTypeModal(false)}\r\n destroyOnClose={true}\r\n title=\"Change Grain Type\"\r\n bodyStyle={{ padding: 0 }}\r\n footer={[\r\n ,\r\n ,]}\r\n >\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};","import * as React from 'react';\r\nimport { useState, useEffect } from 'react';\r\nimport { Modal, Form, Switch, Input, InputNumber, Select, FormInstance, Spin, notification, Button, Checkbox } from 'antd';\r\nimport RhSensorEnum from 'src/consts/RhSensorEnum';\r\nimport TemperatureSensorEnum from 'src/consts/TemperatureSensorEnum';\r\nimport BinSettingsDTO from \"src/models/BinSettingsDTO\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport BinApiService from 'src/api/BinApiService';\r\n\r\nexport interface BinSettingsModalProps {\r\n\r\n settingsModalVisible: boolean;\r\n showModal(): void;\r\n closeModal(): void;\r\n bin: BinDTO | undefined;\r\n}\r\n\r\nconst PLENUM_HIGH_TEMP_DEFAULT = 105;\r\n\r\nexport const BinSettingsModal: React.FC = ({settingsModalVisible, showModal, closeModal, bin}) => {\r\n\r\n \r\n const [_formRef] = Form.useForm();\r\n const [loading, setLoading ] = useState(false);\r\n\r\n const getRhSensorTypes = () => {\r\n\r\n const types : {\r\n label: RhSensorEnum;\r\n value: RhSensorEnum;\r\n }[] = []\r\n\r\n if(bin?.opiMoistureCables?.length ?? 0 > 0){\r\n types.push({\r\n label: RhSensorEnum.Opi,\r\n value: RhSensorEnum.Opi,\r\n })\r\n }\r\n\r\n if(bin?.binSenseMoistureCables?.length ?? 0 > 0){\r\n types.push({\r\n label: RhSensorEnum.BinSense,\r\n value: RhSensorEnum.BinSense,\r\n })\r\n }\r\n\r\n if(bin?.moistureCables_RF?.length ?? 0 > 0){\r\n types.push({\r\n label: RhSensorEnum.PowerCast,\r\n value: RhSensorEnum.PowerCast,\r\n })\r\n }\r\n\r\n return types\r\n }\r\n\r\n const getTempSensorTypes = () => {\r\n\r\n const types : {\r\n label: TemperatureSensorEnum;\r\n value: TemperatureSensorEnum;\r\n }[] = []\r\n\r\n if(bin?.opiMoistureCables?.length ?? 0 > 0){\r\n types.push({\r\n label: TemperatureSensorEnum.Opi,\r\n value: TemperatureSensorEnum.Opi,\r\n })\r\n }\r\n\r\n if(bin?.binSenseMoistureCables?.length ?? 0 > 0){\r\n types.push({\r\n label: TemperatureSensorEnum.BinSense,\r\n value: TemperatureSensorEnum.BinSense,\r\n })\r\n }\r\n\r\n if(bin?.moistureCables_RF?.length ?? 0 > 0){\r\n types.push({\r\n label: TemperatureSensorEnum.PowerCast,\r\n value: TemperatureSensorEnum.PowerCast,\r\n })\r\n }\r\n\r\n if(bin?.temperatureCables?.length ?? 0 > 0){\r\n types.push({\r\n label: TemperatureSensorEnum.Thermocouple,\r\n value: TemperatureSensorEnum.Thermocouple,\r\n })\r\n }\r\n\r\n return types\r\n }\r\n\r\n const binRhSensorTypes = getRhSensorTypes();\r\n const binTempSensorTypes = getTempSensorTypes();\r\n\r\n useEffect(() => {\r\n if (bin && bin.binSettings && !_formRef.isFieldsTouched()) {\r\n\r\n _formRef.setFieldsValue(bin.binSettings as any);\r\n } else {\r\n console.log(\"bin or bin.binSettings is null\");\r\n }\r\n }, [bin]);\r\n\r\n\r\n const onFinish = async (values: BinSettingsDTO) =>{\r\n\r\n setLoading(true);\r\n try{\r\n\r\n values.binID = bin?.id!; \r\n const result = await BinApiService.setBinSettingsOverrides(bin?.deviceId ?? \"\", values);\r\n notification.success({ message:\"Bin settings uploaded\" })\r\n\r\n }catch(err){\r\n console.log(\"Error uploading bin settings\", err);\r\n notification.error({\r\n message: err.detail,\r\n description: err.errorDetails.detail,\r\n });\r\n }\r\n \r\n setLoading(false);\r\n closeModal();\r\n }\r\n\r\n return (\r\n\r\n {\r\n \r\n _formRef.setFieldsValue(bin?.binSettings as any);\r\n closeModal();\r\n }\r\n }\r\n okButtonProps={{ style: { display: 'none' } }} // Hide OK button\r\n width='35%'\r\n >\r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n name=\"useRHForFanControl\"\r\n label='Use RH and Temp for Fan Control automations'\r\n extra=\"Also uses RH and Temperature for all charts and tables\"\r\n valuePropName='checked'\r\n >\r\n \r\n \r\n\r\n \r\n \r\n Submit\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n );\r\n}","import * as React from \"react\";\r\nimport { useState, useEffect } from \"react\";\r\nimport { Modal, Row, Col, Form, Switch, Spin, Button, Skeleton, notification, Tabs, TabsProps, Tag, } from \"antd\";\r\nimport BinDTO from \"src/models/BinDTO\";\r\nimport InfiniteScroll from \"react-infinite-scroll-component\";\r\nimport SolenoidIgnoredStateDTO from \"src/models/SolenoidIgnoredStateDTO\";\r\nimport SetIgnoredSolenoidsRequestDTO from \"src/models/SetIgnoredSolenoidsRequestDTO\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport DesiredIgnoredSolenoidDTO from \"src/models/DesiredIgnoredSolenoidDTO\";\r\nimport SensorIgnoredStateDTO from \"src/models/SensorIgnoredStateDTO\";\r\nimport SetIgnoredSensorStateDTO from \"src/models/SetIgnoredSensorStateDTO\";\r\nimport SetIgnoredSensorRequestDTO from \"src/models/SetIgnoredSensorRequestDTO\";\r\n\r\n\r\nexport interface SensorActuatorControlModalProps {\r\n open: boolean;\r\n closeModal(): void;\r\n bin: BinDTO | undefined;\r\n}\r\n\r\nexport const SensorActuatorControlModal: React.FC = ({open, closeModal, bin}) => {\r\n\r\n const handleCloseModal = () => {\r\n closeModal();\r\n }\r\n\r\n const onTabChange = () => {\r\n\r\n }\r\n\r\n const ActuatorControl = () => {\r\n\r\n const [ignoredActuatorForm] = Form.useForm();\r\n const [allBinSolenoidIDs, setAllBinSolenoidIDs] = useState([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n const onActuatorControlFinish = async (values: SetIgnoredSolenoidsRequestDTO)=> {\r\n try {\r\n setLoading(true);\r\n values.deviceId = bin?.deviceId!;\r\n const result = await BinApiService.setIgnoredSolenoids(values);\r\n notification.success({ message: \"Successfully set solenoids to ignore\" });\r\n closeModal();\r\n } catch (error) {\r\n notification.error({\r\n message: \"Could not set solenoids to ignore\",\r\n description: error?.errorDetails?.detail ?? \"Contact support\"\r\n });\r\n } finally {\r\n setLoading(false);\r\n }\r\n }\r\n\r\n const resetData = () => {\r\n let ignoredSolenoids : string[] = bin?.desiredProperties?.ignoredSolenoids ?? [];\r\n\r\n let allSolenoids: SolenoidIgnoredStateDTO[] = [];\r\n bin?.stacks?.forEach(s => {\r\n s.valves?.forEach(v => {\r\n if (v?.spoolCylinderId != null) {\r\n let solenoidAvailable = ignoredSolenoids?.find(b => b === v.spoolCylinderId) != null ? false : true;\r\n allSolenoids.push({\r\n solenoidId: v.spoolCylinderId,\r\n available: solenoidAvailable\r\n });\r\n }\r\n if (v?.gateCylinderId != null) {\r\n let solenoidAvailable = ignoredSolenoids?.find(b => b === v.gateCylinderId) != null ? false : true;\r\n allSolenoids.push({\r\n solenoidId: v.gateCylinderId,\r\n available: solenoidAvailable\r\n });\r\n }\r\n });\r\n });\r\n\r\n let requestForm: SetIgnoredSolenoidsRequestDTO = {\r\n deviceId: bin?.deviceId!,\r\n solenoids: allSolenoids\r\n };\r\n\r\n ignoredActuatorForm.setFieldsValue(requestForm as any);\r\n setAllBinSolenoidIDs(allSolenoids);\r\n }\r\n\r\n useEffect(() => {\r\n if(bin && !ignoredActuatorForm.isFieldsTouched()){\r\n resetData();\r\n }\r\n }, [bin]);\r\n\r\n return (\r\n
\r\n \r\n \r\n {}}\r\n hasMore={false}\r\n loader={}\r\n scrollableTarget=\"scrollableDiv\"\r\n >\r\n \r\n {fields => (\r\n <>\r\n {fields.map(({ name }) => {\r\n const solenoid = allBinSolenoidIDs[name];\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {`${solenoid.solenoidId}`}\r\n \r\n \r\n );\r\n })}\r\n \r\n )}\r\n \r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n )\r\n }\r\n\r\n const SensorControl = () => {\r\n\r\n const [ignoredSensorForm] = Form.useForm();\r\n const [loading, setLoading] = useState(false);\r\n const [allBinSensorIds, setAllBinSensorIds] = useState([]);\r\n\r\n const onSensorControlFinish = async(values: any) => {\r\n try {\r\n setLoading(true);\r\n values.deviceId = bin?.deviceId!;\r\n const result = await BinApiService.setIgnoredSensors(values);\r\n notification.success({ message: \"Successfully set sensors to ignore\" });\r\n closeModal();\r\n } catch (error) {\r\n notification.error({\r\n message: \"Could not set sensors to ignore\",\r\n description: error?.errorDetails?.detail ?? \"Contact support\"\r\n });\r\n } finally {\r\n setLoading(false);\r\n }\r\n }\r\n\r\n\r\n const resetData = () => {\r\n let ignoredSensors : string[] = bin?.desiredProperties?.ignoredSensors ?? [];\r\n\r\n let allSensors: SensorIgnoredStateDTO[] = [];\r\n bin?.opiMoistureCables?.forEach( (opiMc) => {\r\n opiMc?.rhtStates?.forEach( (rht) => {\r\n if(rht?.id != null){\r\n let sensorAvailable = ignoredSensors?.find(b => b === rht.id) != null ? false : true;\r\n allSensors.push({\r\n sensorId: rht.id,\r\n available: sensorAvailable,\r\n currentMCReading: rht.mc,\r\n currentTempReading: rht.temperature\r\n })\r\n }\r\n })\r\n });\r\n\r\n bin?.temperatureCables?.forEach( (tempCable) => {\r\n tempCable?.thermocouples?.forEach( (tc) => {\r\n if(tc?.id != null){\r\n let sensorAvailable = ignoredSensors?.find(b => b === tc.id) != null ? false : true;\r\n allSensors.push({\r\n sensorId: tc.id,\r\n available: sensorAvailable,\r\n currentMCReading: null,\r\n currentTempReading: tc.temperature\r\n })\r\n }\r\n })\r\n });\r\n\r\n bin?.binSenseMoistureCables?.forEach( (binsenseCable) => {\r\n binsenseCable?.rhtStates?.forEach( (rht) => {\r\n if(rht?.id != null){\r\n let sensorAvailable = ignoredSensors?.find(b => b === rht.id) != null ? false : true;\r\n allSensors.push({\r\n sensorId: rht.id,\r\n available: sensorAvailable,\r\n currentMCReading: rht.mc,\r\n currentTempReading: rht.temperature\r\n })\r\n }\r\n })\r\n });\r\n\r\n bin?.moistureCables_RF?.forEach( (rfMc) => {\r\n rfMc?.wirelessStates?.forEach( (rht) => {\r\n if(rht?.id != null){\r\n let sensorAvailable = ignoredSensors?.find(b => b === rht.id) != null ? false : true;\r\n allSensors.push({\r\n sensorId: rht.id,\r\n available: sensorAvailable,\r\n currentMCReading: rht.mc,\r\n currentTempReading: rht.temperature\r\n })\r\n }\r\n })\r\n });\r\n\r\n let requestForm: SetIgnoredSensorRequestDTO = {\r\n deviceId: bin?.deviceId!,\r\n sensors: allSensors\r\n };\r\n\r\n ignoredSensorForm.setFieldsValue(requestForm as any);\r\n setAllBinSensorIds(allSensors);\r\n }\r\n\r\n useEffect(() => {\r\n if(bin && !ignoredSensorForm.isFieldsTouched()){\r\n resetData();\r\n }\r\n }, [bin]);\r\n\r\n return (\r\n
\r\n \r\n \r\n {}}\r\n hasMore={false}\r\n loader={}\r\n scrollableTarget=\"scrollableDiv\"\r\n >\r\n \r\n {fields => (\r\n <>\r\n {fields.map(({ name }) => {\r\n const sensor = allBinSensorIds[name];\r\n let readingsBadgeStr = \"\";\r\n if(sensor.currentMCReading != null){\r\n readingsBadgeStr += ` ${roundTo(sensor.currentMCReading, 1)}%MC`\r\n }\r\n if(sensor.currentTempReading != null){\r\n readingsBadgeStr += ` ${roundTo(sensor.currentTempReading, 1)}°F`\r\n }\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {`${sensor.sensorId}`}\r\n \r\n \r\n {`${readingsBadgeStr}`}\r\n \r\n \r\n );\r\n })}\r\n \r\n )}\r\n \r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n }\r\n\r\n const PlenumControl = () => {\r\n const [ignorePlenumTemperatureForm] = Form.useForm();\r\n const [loading, setLoading] = useState(false);\r\n\r\n const onIgnoreTemperatureControlFinish = async(values: any) => {\r\n try {\r\n setLoading(true);\r\n const ignoreTemperature = values?.ignoreTemperature ?? false;\r\n const result = await BinApiService.ignorePlenumTemperatureSensors(ignoreTemperature, bin?.deviceId!);\r\n notification.success({ message: \"Successfully set whether to ignore the plenum temperature\" });\r\n closeModal();\r\n } catch (error) {\r\n notification.error({\r\n message: \"Could not set whether to ignore the plenum temperature\",\r\n description: error?.errorDetails?.detail ?? \"Contact support\"\r\n });\r\n } finally {\r\n setLoading(false);\r\n }\r\n }\r\n\r\n const resetData = () => {\r\n ignorePlenumTemperatureForm.setFieldValue(\"ignoreTemperature\", bin?.ignorePlenumTemperatureSensor ?? false);\r\n }\r\n\r\n useEffect(() => {\r\n if(bin && !ignorePlenumTemperatureForm.isFieldsTouched()){\r\n resetData();\r\n }\r\n }, [bin]);\r\n\r\n return (\r\n
\r\n \r\n \r\n \r\n \r\n Ignore Plenum Temperature\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n )\r\n }\r\n\r\n const items: TabsProps['items'] = [\r\n {\r\n key: \"1\",\r\n label: \"Actuators\",\r\n children: \r\n },\r\n {\r\n key: \"2\",\r\n label: \"Sensors\",\r\n children: \r\n },\r\n {\r\n key: \"3\",\r\n label: \"Plenum\",\r\n children: \r\n }\r\n ]\r\n\r\n return (\r\n \r\n

\r\n Sensors/Actuators that are checked will be actuated by the system, whereas those that are not will be ignored.\r\n These lists are submitted seperately\r\n

\r\n \r\n \r\n );\r\n}\r\n\r\nexport default SensorActuatorControlModal;","import React from 'react';\r\nimport { Form, DatePicker, Button } from 'antd';\r\nimport { LogType } from 'src/pages/shared/downloadLogsByRange';\r\n\r\nconst { RangePicker } = DatePicker;\r\n\r\nconst formItemLayout = {\r\n labelCol: {\r\n xs: { span: 24 },\r\n sm: { span: 8 },\r\n },\r\n wrapperCol: {\r\n xs: { span: 24 },\r\n sm: { span: 16 },\r\n },\r\n};\r\n// const config = {\r\n// rules: [{ type: 'object' as const, required: true, message: 'Please select time!' }],\r\n// };\r\nconst rangeConfig = {\r\n rules: [{ type: 'array' as const, required: true, message: 'Select start and end date to download bin logs from!' }],\r\n};\r\n\r\ninterface DeviceLogRangeFormProps {\r\n onSubmit: (rangeStart: string, rangeEnd: string) => void;\r\n}\r\nexport const DeviceLogRangeForm = (props: DeviceLogRangeFormProps) => {\r\n const onFinish = (fieldsValue: any) => {\r\n // Should format date value before submit.\r\n const rangeValue = fieldsValue['range-picker'];\r\n const values = {\r\n ...fieldsValue,\r\n 'range-picker': [rangeValue[0].format('YYYY-MM-DD'), rangeValue[1].format('YYYY-MM-DD')],\r\n };\r\n console.log(values);\r\n console.log(rangeValue);\r\n const rangeStart = values['range-picker'][0];\r\n const rangeEnd = values['range-picker'][1];\r\n console.log('Received values of form: ', values[0], values[1]);\r\n props.onSubmit(rangeStart, rangeEnd);\r\n };\r\n\r\n return (\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};","import { DownloadOutlined } from \"@ant-design/icons\";\r\nimport { Button, InputNumber, message, Space } from \"antd\";\r\nimport Title from \"antd/lib/typography/Title\";\r\nimport React from \"react\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { RestartDriStackModuleButton } from \"src/pages/admin/RestartModuleButton\";\r\n\r\ninterface LogViewerProps {\r\n deviceId: string;\r\n}\r\n\r\nexport const LogViewer = (props: LogViewerProps) => {\r\n\r\n const [logContent, setLogContent] = React.useState('');\r\n const [fetchingLogs, setFetchingLogs] = React.useState(false);\r\n\r\n const [downloadLink, setDownloadLink] = React.useState('');\r\n const [numTail, setNumStail] = React.useState(100);\r\n\r\n const downloadLogs = () => {\r\n if (logContent == null) {\r\n return;\r\n }\r\n const data = new Blob([logContent], { type: 'text/plain;charset=utf-8' })\r\n\r\n // this part avoids memory leaks\r\n if (downloadLink !== '') window.URL.revokeObjectURL(downloadLink)\r\n \r\n // update the download link state\r\n setDownloadLink(window.URL.createObjectURL(data)) \r\n }\r\n\r\n const getModuleLogs = () => {\r\n setFetchingLogs(true);\r\n BinApiService.getLogsFromDristackmodule(props.deviceId, numTail)\r\n .then(res => {\r\n setLogContent(res.payloadText);\r\n }).catch(err => {\r\n message.error(\"Failed to fetch logs\", err);\r\n console.error(err);\r\n }).finally(() => {\r\n setFetchingLogs(false);\r\n })\r\n }\r\n\r\n React.useEffect(() => {\r\n downloadLogs();\r\n }, [logContent]);\r\n\r\n React.useEffect(() => {\r\n if (props.deviceId == null) {\r\n return;\r\n }\r\n setLogContent('');\r\n }, [props.deviceId])\r\n\r\n const handleInputChange = (input: number | null | undefined) => {\r\n if (input == undefined) {\r\n return;\r\n }\r\n \r\n try {\r\n setNumStail(input);\r\n console.debug(`Set numTails to ${input}`);\r\n }\r\n catch {\r\n console.error(\"Invalid number entered: \", input);\r\n }\r\n }\r\n\r\n const getLogButtonText = (): string => {\r\n if (logContent?.length === 0) {\r\n return \"Get Logs\";\r\n }\r\n else {\r\n return \"Refresh\";\r\n }\r\n }\r\n\r\n return (\r\n <>\r\n Get dristackmodule Logs\r\n \r\n \r\n min={1} max={9999} defaultValue={numTail} placeholder={\"Lines\"} onChange={handleInputChange} />\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n {logContent}\r\n
\r\n \r\n );\r\n}\r\n","import { useMutation } from \"@tanstack/react-query\"\r\nimport { Button, Card, Col, Collapse, Grid, message, Row, Select, Space, Typography } from \"antd\"\r\nimport React, { useCallback, useState } from \"react\"\r\nimport { ApiError } from \"src/api/ApiResultHandler\"\r\nimport BinApiService from \"src/api/BinApiService\"\r\nimport BoardGroup from \"src/consts/BoardGroup\"\r\nimport { LogType, viewBinStateLogsByRange, viewGrainStateLogsByRange, viewRunLogsByRange } from \"src/pages/shared/downloadLogsByRange\"\r\nimport { useBinDTOContext } from \"src/queries/BinDTOContext\"\r\nimport { useBinInfoContext } from \"src/queries/BinInfoContext\"\r\nimport { PopconfirmYesNo } from \"./BinStatsPage\"\r\nimport { DeviceLogRangeForm } from \"./DeviceLogsRangeForm\"\r\nimport { LogViewer } from \"./LogViewer\"\r\nconst { useBreakpoint } = Grid;\r\n\r\nconst usePowerCycleBoardsMutation = (deviceId: string) => {\r\n return useMutation({\r\n mutationFn: async (params: { boardGroup: BoardGroup }) => {\r\n if (deviceId == null) {\r\n message.error('Failed to powercycle board group. deviceId was empty');\r\n return;\r\n }\r\n return await BinApiService.powerCycleBoard({ deviceId, which: params.boardGroup })\r\n },\r\n onSuccess(data, variables, context) {\r\n message.success(`Powercycled ${variables.boardGroup} group`, 2);\r\n },\r\n onError(error, variables, context) {\r\n message.error(`Failed to powercycle ${variables.boardGroup} group. ${(error as ApiError)?.description!}`, 3);\r\n },\r\n });\r\n};\r\n\r\nexport const AdminControlsCard = (props: {}) => {\r\n const binInfo = useBinInfoContext();\r\n const [powerCycleSelection, setPowerCycleSelection] = useState(BoardGroup.All);\r\n\r\n const handlePowerCycleSelectChange = useCallback((value: BoardGroup) => {\r\n setPowerCycleSelection(value);\r\n }, []);\r\n\r\n const powercycleBoardsMutation = usePowerCycleBoardsMutation(binInfo.deviceId!);\r\n\r\n const screens = useBreakpoint();\r\n const isSm = screens.xl === false;\r\n\r\n return \r\n \r\n \r\n Select board group to powercycle\r\n \r\n \r\n powercycleBoardsMutation.mutate({boardGroup: powerCycleSelection})}>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n \r\n \r\n
\r\n}\r\n\r\nexport const LogFileDownloads = (props: any) => {\r\n const binInfo = useBinInfoContext();\r\n const bindtoContext = useBinDTOContext();\r\n\r\n const getHourlyForecast = useCallback(async () => {\r\n try {\r\n const forecast = await BinApiService.getHourlyForecast(binInfo.id);\r\n console.log(forecast);\r\n }\r\n catch (error) {\r\n console.log(error);\r\n };\r\n }, [binInfo]);\r\n\r\n const viewBinState = useCallback(async () => {\r\n var today = new Date();\r\n var utcToday = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()));\r\n var dateStart = utcToday.toISOString().substring(0, 10); // yyyy-mm-dd UTC\r\n var dateEnd = dateStart;\r\n await viewBinStateLogsByRange(binInfo.deviceId!, dateStart, dateEnd);\r\n // this.viewLogFile('BinState');\r\n }, [binInfo]);\r\n\r\n const handleLogClickSubmit = useCallback((type: LogType) => (dateStart: string, dateEnd: string) => {\r\n const clientFolder = binInfo.deviceId!;\r\n switch (type) {\r\n case \"BinState\":\r\n viewBinStateLogsByRange(clientFolder!, dateStart, dateEnd);\r\n break;\r\n case \"GrainState\":\r\n viewGrainStateLogsByRange(clientFolder!, dateStart, dateEnd);\r\n break;\r\n case \"Run\":\r\n viewRunLogsByRange(clientFolder!, dateStart, dateEnd);\r\n break;\r\n }\r\n }, [bindtoContext]);\r\n\r\n\r\n\r\n return <>\r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n}","import * as React from 'react';\r\nimport BinApiService from 'src/api/BinApiService';\r\nimport BinDTO from 'src/models/BinDTO';\r\nimport BinInfoDTO from 'src/models/BinInfoDTO';\r\nimport {\r\n Col,\r\n Row,\r\n Switch,\r\n Collapse,\r\n Select,\r\n Spin,\r\n Tag,\r\n Card,\r\n Popconfirm,\r\n message,\r\n Button,\r\n Form,\r\n Space,\r\n Modal,\r\n Typography,\r\n Popover,\r\n Divider,\r\n Skeleton,\r\n} from 'antd';\r\nimport { withIdleTimer, IIdleTimer} from 'react-idle-timer'\r\n\r\n// import { LikeOutlined } from '@ant-design/icons';\r\nimport { SpriteText2D, textAlign } from 'three-text2d';\r\nimport './BinVisual.less';\r\nimport * as THREE from 'three';\r\nimport { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';\r\nimport StackDTO from 'src/models/StackDTO';\r\nimport ValveDTO from 'src/models/ValveDTO';\r\nimport { EditFilled, InfoCircleOutlined, MinusCircleOutlined, RedoOutlined, SettingOutlined } from '@ant-design/icons';\r\nimport ThermocoupleDTO from 'src/models/ThermocoupleDTO';\r\nimport {\r\n Vector3,\r\n Group,\r\n Object3D,\r\n WebGLRenderer,\r\n Scene,\r\n Color,\r\n PerspectiveCamera,\r\n PointLight,\r\n AmbientLight,\r\n CylinderBufferGeometry,\r\n MeshStandardMaterial,\r\n Mesh,\r\n SphereBufferGeometry,\r\n MeshBasicMaterial,\r\n LineBasicMaterial,\r\n CatmullRomCurve3,\r\n BufferGeometry,\r\n Line,\r\n QuadraticBezierCurve3,\r\n BoxBufferGeometry,\r\n\r\n} from 'three';\r\nimport LayerDTO from 'src/models/LayerDTO';\r\nimport RoleUtil from 'src/utils/RoleUtil';\r\nimport Role from 'src/consts/Role';\r\nimport TemperatureCableDTO from 'src/models/TemperatureCableDTO';\r\nimport MoistureCableDTO from 'src/models/MoistureCableDTO';\r\nimport MoistureCableRFDTO from 'src/models/MoistureCableRFDTO';\r\n\r\n//import RHTStateDTO from 'src/models/RHTStateDTO';\r\n//import WirelessStateDTO from 'src/models/WirelessStateDTO';\r\nimport ValvePositionDTO from '../../../models/ValvePositionDTO';\r\nimport _, { floor } from 'lodash';\r\nimport { DeviceLogRangeForm } from './DeviceLogsRangeForm';\r\nimport { MappingErrorList, PANEL_KEYS_DEFAULT_EXPANDED, PopconfirmYesNo } from './BinStatsPage';\r\nimport ErrorPriority from 'src/consts/ErrorPriority';\r\nimport Title from 'antd/lib/typography/Title';\r\nimport BoardGroup from 'src/consts/BoardGroup';\r\nimport { LogViewer } from './LogViewer';\r\nimport { Component } from 'react'\r\nimport HeaterMode from 'src/consts/HeaterMode';\r\nimport { RequiredRule } from 'src/consts/FormConstants';\r\nimport ChangeHeaterModeDTO from 'src/models/ChangeHeaterModeDTO';\r\nimport { formatHeaterMode } from 'src/utils/formatting';\r\nimport BinVectorDTO from 'src/models/BinVectorDTO';\r\nimport { hasDyanmicLayers } from '../HomeScreen/Canvas';\r\nimport { getUserTimezoneOffset } from '../../../components/BinDetails';\r\nimport { WeatherMonitorBypassModal } from './WeatherMonitorBypassModal';\r\nimport produce from 'immer';\r\nimport { FanCard } from './FanCard';\r\nimport { LogType, viewBinStateLogsByRange, viewGrainStateLogsByRange, viewRunLogsByRange } from 'src/pages/shared/downloadLogsByRange';\r\nimport FormItem from 'antd/es/form/FormItem/index';\r\nimport dayjs, { Dayjs } from 'dayjs';\r\nimport { AutomationType, selectAutomationType } from 'src/utils/deriveAutomationType';\r\nimport { RestartDriStackModuleButton } from 'src/pages/admin/RestartModuleButton';\r\nimport { formatNumber } from './HeaterControls';\r\nimport { ManualRoutineControl } from './ManualRoutineControl';\r\nimport { BinStatusCard } from './BinStatusCard';\r\nimport { getDefaultTemperatureSensorType } from 'src/pages/shared/binDTOUtils';\r\nimport TemperatureSensorEnum from 'src/consts/TemperatureSensorEnum';\r\nimport { BinSettingsModal } from './BinSettingsModal';\r\nimport { SolenoidControlModal } from './SolenoidControlModal';\r\nimport SensorActuatorControlModal from './SensorActuatorControl';\r\nimport { AdminControlsCard } from './AdminControlsCard';\r\n\r\nconst { Option } = Select;\r\nconst { Panel } = Collapse;\r\n\r\nexport const PANEL_STYLE = {background: '#939393' };\r\n\r\nconst BINCOLOR = 0xE7E7E7;\r\n// const STACKCOLOR = 0x9dede1;\r\nconst STACKCOLOR = 0x32d000;\r\nconst TEMPCABLECOLOR = 0xff0000;\r\nconst RHTCABLECOLOR = 0x0000ff;\r\nconst RHT_WIRELESS_CABLE_COLOR = RHTCABLECOLOR;\r\nconst GATECOLOR = 0xf0ea2b;\r\nconst DEFUALTLAYERCOLOR = 0x138d75;\r\nexport const DISABLED_TAG_COLOR = \"default\";\r\nlet currentAscii: string | null;\r\n\r\nexport enum OperatingMode {\r\n Idle = 0,\r\n FillBin = 1,\r\n PreDry = 2,\r\n TopDry = 31,\r\n Dry = 3,\r\n Storage = 4,\r\n EmptyBin = 5,\r\n Manual = 20,\r\n FailSafe = 100,\r\n SelfCheck = 101,\r\n}\r\n\r\nexport const transformLayers = (binDTO: BinDTO): Array => {\r\n let layers: LayerMCInfo[] | null | undefined = binDTO?.layerGrainStates?.map((layer => ({...layer, mc: layer.moistureContent, moistureCableTemperatureF: layer.temperatureF, rh: layer.relitiveHumidity})));\r\n if (layers == null) {\r\n // use staticMC\r\n // not supported\r\n return [];\r\n }\r\n return layers ?? [];\r\n\r\n}\r\n\r\nexport const binConfiguredWithHeaters = (bin: BinDTO | null | undefined): boolean => {\r\n // const noHeaterOverride = bin?.desiredProperties?.overrides?.noHeater;\r\n const noHeaterOverride = !(bin?.hasHeaters)\r\n if (noHeaterOverride === null || noHeaterOverride === undefined) {\r\n return true;\r\n }\r\n if (noHeaterOverride === true) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\nconst ManualModeHelpText = (
\r\n

This control is only changeable in User Control Mode

\r\n
);\r\n\r\nexport const ManualModeInfoIcon = () => {\r\n return <>\r\n \r\n \r\n \r\n \r\n}\r\n\r\ninterface InactiveModalProps {\r\n userIsActive: boolean,\r\n onOk: () => void,\r\n}\r\n\r\ninterface cableTransformData {\r\n ringLength: number;\r\n distanceFromCenter: number,\r\n isCenter: boolean,\r\n bearing: number,\r\n cableHeight: number,\r\n}\r\n\r\ninterface RHTorSensorTextAddInputs {\r\n id: string | null,\r\n temperature: number | null,\r\n rh: number | null,\r\n mc: number | null,\r\n location: BinVectorDTO | null,\r\n}\r\n\r\n// const deviceLogsBaseURI = 'https://dristackdatalakeprod.blob.core.windows.net/device-logs/';\r\n\r\n const InactiveModal = (props: InactiveModalProps) => {\r\n return \r\n

Updating bin information was paused while you were away

\r\n\r\n ;\r\n };\r\n\r\n\r\n interface ConfirmMsgProps {\r\n heaterMode: HeaterMode;\r\n }\r\n \r\n const ConfirmMsg = (props: ConfirmMsgProps) => {\r\n return \r\n Do you still wish to change to {formatHeaterMode(props.heaterMode)} Mode? \r\n \r\n }\r\n const AmbientToOtherMode = (heaterMode: HeaterMode) => {\r\n return <>\r\n \r\n Changing to this mode causes the heater to turn on. However, the system will work to not increase the overall temperature of the grain in the bin. An increase in overall temperature could result in rewetting of some layers and over drying of others. \r\n \r\n \r\n ;\r\n }\r\n \r\n const OtherHeaterModeToAmbient = (heaterMode: HeaterMode) => {\r\n return <>\r\n \r\n Grain will cool when switching to ambient, limiting the ability to switch back to a heated drying mode which, if switched back to a heated mode, could result in rewetting of some layers and over drying of others.\r\n \r\n \r\n ;\r\n }\r\n \r\n enum VariationToShow {\r\n NoToSome = 0,\r\n SomeToNo = 1,\r\n Same = 2,\r\n }\r\n \r\n const variationToShow = (currentHeaterMode: HeaterMode, desiredHeaterMode: HeaterMode): VariationToShow => {\r\n const someVariations = [HeaterMode.EnergyEff, HeaterMode.Speed5, HeaterMode.Speed10];\r\n // const noHeatVariations = [HeaterMode.Ambient];\r\n if (currentHeaterMode === HeaterMode.Ambient && someVariations.includes(desiredHeaterMode)) {\r\n return VariationToShow.NoToSome;\r\n }\r\n else if (someVariations.includes(currentHeaterMode) && desiredHeaterMode === HeaterMode.Ambient) {\r\n return VariationToShow.SomeToNo;\r\n }\r\n else {\r\n return VariationToShow.Same;\r\n }\r\n };\r\n \r\n\r\ninterface ChangeHeaterModeModalProps {\r\n deviceId: string;\r\n currentHeaterMode: HeaterMode,\r\n open: boolean,\r\n onOk: (desiredHeaterMode: HeaterMode) => void,\r\n onCancel: () => void;\r\n}\r\n\r\nexport const ChangeHeaterModeModal = (props: ChangeHeaterModeModalProps) => {\r\n\r\n const [desiredMode, setDesiredMode] = React.useState(props.currentHeaterMode);\r\n const [pendingSubmit, setPendingSubmit] = React.useState(false);\r\n\r\n const handleSelectChange = (value: any) => {\r\n console.log(\"desired mode: \", value);\r\n setDesiredMode(value);\r\n }\r\n const onFinish = async (vals: any) => {\r\n try {\r\n setPendingSubmit(true);\r\n console.log(\"heater mode vals: \", vals);\r\n const heaterMode = vals.heaterMode;\r\n const changeHeaterModeDTO: ChangeHeaterModeDTO = {\r\n deviceId: props.deviceId,\r\n heaterMode: heaterMode,\r\n };\r\n if (await BinApiService.changeHeaterMode(changeHeaterModeDTO)) {\r\n message.success(`Heater mode updated to ${heaterMode}`);\r\n }\r\n else {\r\n message.error(\"Heater mode did not update\");\r\n }\r\n setPendingSubmit(false);\r\n props.onOk(heaterMode);\r\n\r\n } catch (err) {\r\n console.log(\"Updating heater mode failed\", err);\r\n message.error(\"An error occured while updating the heater mode\");\r\n\r\n setPendingSubmit(false);\r\n props.onOk(desiredMode);\r\n }\r\n }\r\n\r\n const handleOk = () => {\r\n props.onOk(desiredMode);\r\n }\r\n\r\n const showVariation = (variation: VariationToShow, desiredHeaterMode: HeaterMode) => {\r\n if (variation === VariationToShow.NoToSome) {\r\n return AmbientToOtherMode(desiredHeaterMode);\r\n }\r\n else if (variation === VariationToShow.SomeToNo) {\r\n return OtherHeaterModeToAmbient(desiredHeaterMode);\r\n }\r\n else {\r\n return null;\r\n // return \r\n }\r\n };\r\n\r\n const variation = variationToShow(props.currentHeaterMode, desiredMode);\r\n\r\n\r\n const ContentWarningVariation = React.useMemo(() => showVariation(variation, desiredMode), [variation, desiredMode]);\r\n\r\n\r\n\r\n return \r\n Cancel\r\n ,\r\n ,\r\n ]}\r\n \r\n >\r\n
\r\n\r\n\r\n \r\n\r\n\r\n\r\n
\r\n{ContentWarningVariation}\r\n\r\n ;\r\n\r\n}\r\n\r\nexport interface CompressorCardProps {\r\n isCompressorOn: boolean | null | undefined,\r\n turnOnCompressor: () => void,\r\n turnOffCompressor: () => void,\r\n operatingMode: number | null | undefined,\r\n hardwareYear: number,\r\n compressorAmps?: number | null | undefined,\r\n psi: number | null | undefined,\r\n}\r\n\r\nexport const formatPSI = (psi: number | null | undefined): string => {\r\n if (!Number.isFinite(psi)) {\r\n return \"\";\r\n }\r\n if (psi == null) {\r\n return \"\";\r\n }\r\n\r\n return psi.toFixed(2);\r\n}\r\n\r\nexport const formatCompressorAmps = (amps: number | null | undefined): string => {\r\n if (!Number.isFinite(amps)) {\r\n return \"\";\r\n }\r\n if (amps == null) {\r\n return \"\";\r\n }\r\n\r\n return amps.toFixed(2);\r\n}\r\n\r\nexport const CompressorCard = (props: CompressorCardProps) => {\r\n const inManualMode = props.operatingMode === OperatingMode.Manual;\r\n\r\n const compressorAmpsFormatted = formatCompressorAmps(props.compressorAmps);\r\n const psiFormatted = formatPSI(props.psi);\r\n\r\n return <>\r\n \r\n \r\n \r\n {`Compressor`}\r\n \r\n\r\n \r\n {/* {!inManualMode && } */}\r\n {props.isCompressorOn == null && No Data}\r\n {(props.isCompressorOn\r\n ? props.turnOffCompressor()}>\r\n On\r\n \r\n : props.turnOnCompressor()}>\r\n Off\r\n \r\n )}\r\n \r\n \r\n \r\n \r\n PSI: {psiFormatted}\r\n \r\n \r\n {props.hardwareYear >= 2022 && \r\n \r\n Current: {compressorAmpsFormatted} Amps\r\n \r\n \r\n }\r\n\r\n\r\n}\r\n\r\nexport const isMoniteringBin = (binData: BinDTO | null) => {\r\n return binData?.desiredProperties?.overrides?.monitoringBin === true;\r\n}\r\n\r\nexport const downloadAsciiBinState = (binState: BinDTO) => {\r\n\r\n var today = new Date();\r\n var dd = String(today.getUTCDate()).padStart(2, '0');\r\n var mm = String(today.getUTCMonth() + 1).padStart(2, '0'); // January is 0!\r\n var yyyy = today.getFullYear();\r\n var hh = today.getHours();\r\n var MM = today.getMinutes();\r\n var ss = today.getSeconds();\r\n var fileName = `BinState_${binState.name}_${yyyy}${mm}${dd}_${hh}${MM}${ss}.txt`;\r\n // var asciiText = currentAscii ?? '';\r\n const element = document.createElement('a');\r\n const file = new Blob([binState.ascii ?? \"\"], { type: 'text/plain;charset=utf-8'});\r\n element.href = URL.createObjectURL(file);\r\n element.download = fileName;\r\n document.body.appendChild(element);\r\n element.click();\r\n document.body.removeChild(element);\r\n}\r\n\r\nexport const fanDurationFormatted = (fanRuntimeSeconds: number | undefined): string => {\r\n const maybeRuntime = fanRuntimeSeconds;\r\n if (maybeRuntime == null) {\r\n return '--';\r\n }\r\n const totalSeconds = maybeRuntime;\r\n const days = Math.floor(totalSeconds / 3600 / 24);\r\n const hours = Math.floor(totalSeconds / 3600) % 24;\r\n const minutes = Math.floor(totalSeconds / 60) % 60;\r\n const seconds = Math.floor(totalSeconds) % 60;\r\n\r\n let dayFormatted = '';\r\n if (days >= 2) {\r\n dayFormatted = `${days} days, `;\r\n } else if (days === 1) {\r\n dayFormatted = `${days} day, `;\r\n }\r\n\r\n const hhmm = [hours, minutes]\r\n .map(v => v < 10 ? '0' + v : v)\r\n .join(':');\r\n return `${dayFormatted}${hhmm}`;\r\n\r\n}\r\n\r\n\r\ninterface State {\r\n userIsActive: boolean,\r\n binInfo: BinInfoDTO;\r\n selectedBinId: string;\r\n bin?: BinDTO;\r\n view: 'Stack' | 'Level' | 'Debug';\r\n loading: boolean;\r\n isOpenHeaterChange: boolean;\r\n openWeatherMonitorBypass: boolean;\r\n powerCycleSelection: BoardGroup;\r\n pendingPowerCycle: boolean;\r\n refreshDelay: boolean;\r\n refreshCounter: number;\r\n valvesDisabled: boolean;\r\n // if the model has been initialized already\r\n modelInitialized: boolean;\r\n settingsModalVisible: boolean;\r\n sensorActuatorControlModalVisible: boolean;\r\n}\r\n\r\nexport interface ObjectColorInfo {\r\n key: string;\r\n highlightColor: string;\r\n defaultColor: number;\r\n object: Mesh | SpriteText2D;\r\n type: string;\r\n}\r\ninterface StatProps {\r\n currentStep: number;\r\n bin: BinDTO | null | undefined;\r\n binInfo: BinInfoDTO;\r\n activeErrors: MappingErrorList;\r\n isLoading(value: boolean): void;\r\n updateBin(bin: BinDTO): void;\r\n fetchingFromDevice: boolean;\r\n}\r\n\r\n\r\n class IdleTimerComponent extends Component> {\r\n render () {\r\n return this.props.children;\r\n }\r\n }\r\n\r\ninterface SettingsButtonProps {\r\n showSettingsModal(): void;\r\n}\r\n\r\ninterface SensorActuatorControlButtonProps {\r\n showSensorActuatorControlModal(): void;\r\n}\r\n\r\nexport const SettingsButton = (props: SettingsButtonProps) => {\r\n return (\r\n \r\n );\r\n}\r\n\r\nexport const SensorActuatorControlButton = (props: SensorActuatorControlButtonProps) => {\r\n return (\r\n \r\n );\r\n}\r\n const IdleTimer = withIdleTimer(IdleTimerComponent);\r\n\r\nexport type LayerMCInfo = Omit & {mc: number | null, moistureCableTemperatureF: number | null, rh: number | null, hasGrain: boolean | null};\r\nclass BinVisualThreeUnwrapped extends React.Component {\r\n private components: any;\r\n // private static canvas = document.getElementById('player');\r\n private renderer: WebGLRenderer;\r\n private mount: HTMLElement | null;\r\n private scene: Scene;\r\n private camera: PerspectiveCamera;\r\n private controls: OrbitControls;\r\n\r\n // private ctx: CanvasRenderingContext2D | null;\r\n // for the bin model views\r\n private gateCoverArr: Object3D[] = [];\r\n private arrowCurveGroupArr: Object3D[] = [];\r\n private ringLengths: number[] = [];\r\n private stackArr: Object3D[] = [];\r\n private stackTextArr: Object3D[] = [];\r\n private segmentTextArr: Object3D[] = [];\r\n private layerMeshArr: Object3D[] = [];\r\n private highlightArr: ObjectColorInfo[] = [];\r\n private allGatesSpools: any;\r\n private interval: NodeJS.Timeout;\r\n private idleTimer: IIdleTimer | null;\r\n constructor(props: StatProps) {\r\n super(props);\r\n\r\n\r\n\r\n this.allGatesSpools = {\r\n isGatesOpen: false,\r\n isSpoolsOpen: false\r\n };\r\n\r\n this.state = {\r\n userIsActive: true,\r\n binInfo: props.binInfo,\r\n selectedBinId: props.binInfo.deviceId || 'Lange_test',\r\n view: 'Level',\r\n loading: true,\r\n isOpenHeaterChange: false,\r\n openWeatherMonitorBypass: false,\r\n powerCycleSelection: BoardGroup.All,\r\n pendingPowerCycle: false,\r\n refreshDelay: false,\r\n refreshCounter: 0,\r\n valvesDisabled: false,\r\n modelInitialized: false,\r\n settingsModalVisible:false,\r\n sensorActuatorControlModalVisible: false\r\n };\r\n\r\n this.idleTimer = null;\r\n\r\n this.onPrompt = this.onPrompt.bind(this);\r\n this.onIdle = this.onIdle.bind(this);\r\n this.onAction = this.onAction.bind(this);\r\n this.onActive = this.onActive.bind(this);\r\n this.isRefetchingFromDevice = this.isRefetchingFromDevice.bind(this);\r\n this.handleLogClickSubmit = this.handleLogClickSubmit.bind(this);\r\n this.openeWeatherMonitor = this.openeWeatherMonitor.bind(this);\r\n this.closeWeatherMonitor = this.closeWeatherMonitor.bind(this);\r\n this.closeWeatherMonitorAndRefresh = this.closeWeatherMonitorAndRefresh.bind(this);\r\n }\r\n\r\n showSensorActuatorControlModal = () =>{\r\n this.setState({\r\n sensorActuatorControlModalVisible: true\r\n })\r\n }\r\n\r\n closeSensorActuatorControlModal = () => {\r\n this.setState({\r\n sensorActuatorControlModalVisible: false\r\n });\r\n };\r\n\r\n showSettingsModal = () =>{\r\n this.setState({\r\n settingsModalVisible: true\r\n })\r\n }\r\n\r\n closeSettingsModal = () => {\r\n this.setState({\r\n settingsModalVisible: false\r\n });\r\n };\r\n\r\n\r\n onPrompt (): void {\r\n // Fire a Modal Prompt\r\n }\r\n \r\n onIdle (): void {\r\n this.setState({userIsActive: false});\r\n // Do some idle action like log out your user\r\n }\r\n \r\n onActive (event: Event): void {\r\n this.setState({userIsActive: true});\r\n this.refreshBinInfoAndErrors();\r\n\r\n // Close Modal Prompt\r\n // Do some active action\r\n }\r\n \r\n onAction (event: Event): void {\r\n // Do something when a user triggers a watched event\r\n }\r\n\r\n\r\n componentDidMount() {\r\n this.idleTimer!.start()\r\n this.fetchBin(this.state.selectedBinId, true, false);\r\n\r\n // Not a true fix bumping to 90 seconds, but should help prevent the buildup of pending requests for data if the device is slow\r\n this.interval = setInterval(this.updateNumbers, 90 * 1000);\r\n\r\n }\r\n\r\n componentWillUnmount() {\r\n clearInterval(this.interval);\r\n }\r\n\r\n refreshBinInfoAndErrors = () => {\r\n this.fetchBin(this.state.selectedBinId, false, true);\r\n //this.fetchErrors(false);\r\n }\r\n\r\n updateNumbers = (clearMessages: boolean = true) => {\r\n if (clearMessages) {\r\n message.destroy();\r\n }\r\n if (this.state.userIsActive) {\r\n this.fetchBin(this.state.selectedBinId, false, true);\r\n //this.fetchErrors(false);\r\n }\r\n }\r\n closeWeatherMonitorAndRefresh = async (bypassUntil: Dayjs | null): Promise => {\r\n this.closeWeatherMonitor();\r\n if (this.props.bin?.desiredProperties?.overrides != null) {\r\n const updated = produce(this.props.bin, draft => {\r\n\r\n // draft.desiredProperties!.overrides!.weatherMonitorBypassUntil = bypassUntil;\r\n });\r\n this.props.updateBin(updated);\r\n }\r\n //this.fetchBin(this.props.binInfo.deviceId!, false, false);\r\n };\r\n\r\n closeWeatherMonitor = () => {\r\n this.setState({openWeatherMonitorBypass: false});\r\n };\r\n\r\n openeWeatherMonitor = () => {\r\n this.setState({openWeatherMonitorBypass: true});\r\n };\r\n\r\n turnonFans = () => {\r\n message.info(`Fans turning on. Please wait.`, 4);\r\n BinApiService.turnOnFans(this.state.selectedBinId).then((succeeded) => {\r\n if (succeeded) {\r\n this.setState((prev) => {\r\n for (let fan of prev.bin!.fans!) {\r\n fan.isOn = true;\r\n var fanSprite = this.scene.getObjectByName(`Fan ${fan.id}`) as SpriteText2D;\r\n fanSprite.text = ' Fan On ';\r\n }\r\n return prev;\r\n });\r\n }\r\n else {\r\n message.destroy();\r\n message.error(`One or more Fans did not turn on`);\r\n }\r\n }).catch(err => {\r\n message.destroy();\r\n message.error(`Failure Turning on Fans`);\r\n }\r\n );\r\n\r\n }\r\n\r\n turnOffFans = () => {\r\n message.info(`Fans turning off. Please wait.`, 4);\r\n BinApiService.turnOffFans(this.state.selectedBinId).then((succeeded) => {\r\n if (succeeded) {\r\n this.setState((prev) => {\r\n for (let fan of prev.bin!.fans!) {\r\n fan.isOn = false;\r\n var fanSprite = this.scene.getObjectByName(`Fan ${fan.id}`) as SpriteText2D;\r\n fanSprite.text = ' Fan Off ';\r\n }\r\n return prev;\r\n });\r\n }\r\n else {\r\n message.destroy();\r\n message.error(`One or more Fans did not turn off`);\r\n }\r\n }).catch(err => {\r\n message.destroy();\r\n message.error(`Failure Turning off Fans`);\r\n }\r\n );\r\n\r\n }\r\n\r\n turnOnFan = (fanId: string, fanIndex: number) => {\r\n message.info(`Fan ${fanId} turning on. Please wait.`, 4);\r\n BinApiService.turnOnFan(fanId, this.state.selectedBinId).then((succeeded) => {\r\n if (succeeded) {\r\n this.setState((prev) => {\r\n prev.bin!.fans![fanIndex].isOn = true;\r\n return prev;\r\n });\r\n var fanSprite = this.scene.getObjectByName(`Fan ${fanId}`) as SpriteText2D;\r\n fanSprite.text = ' Fan On ';\r\n }\r\n else {\r\n message.destroy();\r\n message.error(`Fan ${fanId} did not turn on`);\r\n }\r\n }).catch(err => {\r\n message.destroy();\r\n message.error(`Failed to turn on fan ${fanId}`);\r\n }\r\n );\r\n }\r\n\r\n turnOffFan = (fanId: string, fanIndex: number) => {\r\n message.info(`Fan ${fanId} turning off. Please wait.`, 4);\r\n BinApiService.turnOffFan(fanId, this.state.selectedBinId).then((succeeded) => {\r\n if (succeeded) {\r\n this.setState((prev) => {\r\n prev.bin!.fans![fanIndex].isOn = false;\r\n return prev;\r\n });\r\n var fanSprite = this.scene.getObjectByName(`Fan ${fanId}`) as SpriteText2D;\r\n fanSprite.text = ' Fan Off ';\r\n } else {\r\n message.destroy();\r\n message.error(`Fan ${fanId} did not turn off`);\r\n\r\n }\r\n }).catch(err => {\r\n message.destroy();\r\n message.error(`Failed to turn off fan ${fanId} `);\r\n });\r\n }\r\n\r\n turnOnCompressor = () => {\r\n message.info('Compressor turning on. Please wait.', 4);\r\n BinApiService.turnOnCompressor(this.state.selectedBinId).then((successful) => {\r\n if (successful) {\r\n this.setState((prev) => {\r\n prev.bin!.isCompressorOn = true;\r\n return prev;\r\n });\r\n message.success('Compressor turned on.');\r\n } else {\r\n message.destroy();\r\n message.error('Compressor did not turn on');\r\n }\r\n }).catch(err => {\r\n message.destroy();\r\n message.error('Failed to turn on compressor');\r\n });\r\n }\r\n\r\n turnOffCompressor = () => {\r\n message.info('Compressor turning off. Please wait.', 4);\r\n BinApiService.turnOffCompressor(this.state.selectedBinId).then((successful) => {\r\n if (successful) {\r\n this.setState((prev) => {\r\n prev.bin!.isCompressorOn = false;\r\n return prev;\r\n });\r\n message.success('Compressor turned Off.');\r\n } else {\r\n message.destroy();\r\n message.error('Compressor did not turn off');\r\n }\r\n }).catch(err => {\r\n message.error('Failed to turn off compressor');\r\n });\r\n }\r\n\r\n turnOnHeater = (fanId: string) => {\r\n message.info(`Heater for Fan ${fanId} turning on. Please wait.`, 4);\r\n BinApiService.turnOnHeater(this.state.selectedBinId, fanId).then((succeeded) => {\r\n if (succeeded) {\r\n this.setState((prev) => {\r\n const fanIndex = prev.bin!.fans!.findIndex(fan => fan.id === fanId);\r\n if (fanIndex >= 0) {\r\n prev.bin!.fans![fanIndex].isHeaterOn = true;\r\n }\r\n return prev;\r\n });\r\n const heaterSprite = this.scene.getObjectByName(`Heater ${fanId}`) as SpriteText2D;\r\n heaterSprite.text = ' Heater On';\r\n message.success(`Heater for Fan ${fanId} turned on.`);\r\n } else {\r\n message.destroy();\r\n message.error(`Heater for Fan ${fanId} did not turn on`);\r\n }\r\n }).catch(err => {\r\n message.destroy();\r\n message.error(`Failed to turn on heater for Fan ${fanId}`);\r\n });\r\n }\r\n\r\n turnOnHeaters = () => {\r\n message.info('Heaters turning on. Please wait.', 4);\r\n let fanIds = this.state.bin!.fans!.map(x => x.id);\r\n BinApiService.turnOnHeaters(this.state.selectedBinId).then((succeeded) => {\r\n if (succeeded) {\r\n this.setState((prev) => {\r\n prev.bin!.fans!.forEach(x => { x.isHeaterOn = true; });\r\n return prev;\r\n });\r\n for (let fanId of fanIds) {\r\n var heaterSprite = this.scene.getObjectByName(`Heater ${fanId}`) as SpriteText2D;\r\n heaterSprite.text = ' Heater On';\r\n }\r\n message.success('Heaters turned on.');\r\n } else {\r\n message.destroy();\r\n message.error('Heaters did not turn on');\r\n }\r\n }).catch(err => {\r\n message.destroy();\r\n message.error('Failed to turn on heaters');\r\n });\r\n }\r\n\r\n turnOffHeater = (fanId: string) => {\r\n \r\n message.info(`Heater for Fan ${fanId} turning off. Please wait.`, 4);\r\n BinApiService.turnOffHeater(this.state.selectedBinId, fanId).then((succeeded) => {\r\n if (succeeded) {\r\n this.setState((prev) => {\r\n const fanIndex = prev.bin!.fans!.findIndex(fan => fan.id === fanId);\r\n if (fanIndex >= 0) {\r\n prev.bin!.fans![fanIndex].isHeaterOn = false;\r\n }\r\n return prev;\r\n });\r\n const heaterSprite = this.scene.getObjectByName(`Heater ${fanId}`) as SpriteText2D;\r\n \r\n heaterSprite.text = ' Heater Off';\r\n message.success(`Heater for Fan ${fanId} turned off.`);\r\n } else {\r\n message.destroy();\r\n message.error(`Heater for Fan ${fanId} did not turn off`);\r\n }\r\n }).catch(err => {\r\n message.destroy();\r\n message.error(`Failed to turn off heater for Fan ${fanId}`);\r\n });\r\n }\r\n\r\n\r\n turnOffHeaters = () => {\r\n message.info('Heater turning off. Please wait.', 4);\r\n let fanIds = this.state.bin!.fans!.map(x => x.id);\r\n BinApiService.turnOffHeaters(this.state.selectedBinId).then((succeeded) => {\r\n if (succeeded) {\r\n this.setState((prev) => {\r\n prev.bin!.fans!.forEach(x => { x.isHeaterOn = false; });\r\n return prev;\r\n });\r\n for (let fanId of fanIds) {\r\n\r\n var heaterSprite = this.scene.getObjectByName(`Heater ${fanId}`) as SpriteText2D;\r\n heaterSprite.text = ' Heater Off';\r\n }\r\n message.success('Heaters turned off.');\r\n } else {\r\n message.destroy();\r\n message.error('Heaters did not turn off');\r\n }\r\n }).catch(err => {\r\n message.destroy();\r\n message.error('Failed to turn off heaters');\r\n });\r\n }\r\n\r\n onHeaterModeChangeModalOk = (desiredHeaterMode: HeaterMode) => {\r\n this.setState({isOpenHeaterChange: false});\r\n console.log(\"outside: desired heater mode: \", desiredHeaterMode);\r\n // Todo: updating the bin values swallows messages\r\n this.updateNumbers(false);\r\n }\r\n\r\n onHeaterModeChangeModalCancel = () => {\r\n this.setState({isOpenHeaterChange: false});\r\n }\r\n\r\n onChangeHeaterModeClick = () => {\r\n this.setState({isOpenHeaterChange: true});\r\n }\r\n\r\n handlePowerCycleSelectChange = (value: BoardGroup) => {\r\n this.setState({powerCycleSelection: value});\r\n }\r\n\r\n powerCycleBoardGroup = () => {\r\n const deviceId = this.state.binInfo.deviceId;\r\n if (deviceId == null) {\r\n message.error('Failed to powercycle board group. deviceId was empty');\r\n return;\r\n }\r\n const boardGroup = this.state.powerCycleSelection;\r\n this.setState({pendingPowerCycle: true});\r\n BinApiService.powerCycleBoard({deviceId, which: boardGroup })\r\n .then(res => {\r\n message.success(`Powercycled ${boardGroup} group`, 2);\r\n })\r\n .catch(err => {\r\n message.error(`Failed to powercycle ${boardGroup} group. ${err.description}`, 3);\r\n })\r\n .finally(() => {\r\n this.setState({pendingPowerCycle: false});\r\n });\r\n }\r\n\r\n handleInactiveUserClick = (): void => {\r\n this.setState({userIsActive: true});\r\n }\r\n\r\n isRefetchingFromDevice = (): boolean => {\r\n return this.state.refreshDelay || this.props.fetchingFromDevice;\r\n }\r\n\r\n private bypassExpired = (bypassDate: Dayjs | null): boolean => {\r\n if (bypassDate == null) {\r\n // it was never set\r\n return true;\r\n }\r\n if (!bypassDate.isValid()) {\r\n return true;\r\n }\r\n\r\n\r\n\r\n const bypassOlderThanCurrentTIme = bypassDate.diff(new Date(), 'minute', true) < 0;\r\n if (bypassOlderThanCurrentTIme) {\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n renderRoutineInfo = (pauseMessage: string) => {\r\n const automationType = selectAutomationType(this.state.bin);\r\n if (automationType === AutomationType.DriStack) {\r\n\r\n if ( this.state.bin?.desiredProperties?.isSeedBin){\r\n return 'Current Routine: ' + `${this.state.bin?.currentRoutineName ?? \"None\"}` + ' Current Step: ' + `${this.state.bin?.routineEngine?.stepLabel ?? \"None\"}` + pauseMessage;\r\n }else{\r\n if (this.state.bin?.routineEngine?.routine == null) {\r\n return \"Current Routine: None\";\r\n }\r\n return 'Current Routine: ' + `${this.state.bin?.routineEngine?.routine ?? \"None\"}` + `: ${this.state.bin?.routineEngine?.stepLabel ?? \"None\"}` + pauseMessage\r\n }\r\n }\r\n else {\r\n return `${pauseMessage}`;\r\n }\r\n }\r\n\r\n render() {\r\n const { bin } = this.state;\r\n // const bypassDate = dayjs(this.props.bin?.desiredProperties?.overrides?.weatherMonitorBypassUntil);\r\n // full bin cntrol\r\n const isAtLeastPowerUser = RoleUtil.currentUserisAtLeastPowerUser();\r\n const isAdmin = RoleUtil.currentUserIsAdmin();\r\n const inManaulMode = this.props.currentStep === OperatingMode.Manual;\r\n let notifications = '';\r\n let pauseMessage = (this.props.bin?.isPaused ? ' - ' + this.props.bin.reasonForPause : '');\r\n\r\n return (\r\n <>\r\n { this.idleTimer = ref }}\r\n timeout={1000 * 60 * 10}\r\n onPrompt={this.onPrompt}\r\n onIdle={this.onIdle}\r\n onAction={this.onAction}\r\n onActive={this.onActive}\r\n >\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n {isAtLeastPowerUser && }\r\n \r\n\r\n \r\n {isAtLeastPowerUser && }\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n }>\r\n\r\n \r\n \r\n {notifications}\r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n {\r\n \r\n }\r\n {\r\n isAtLeastPowerUser && this.state.view === 'Debug' &&
\r\n {\r\n this.stackLabelVisibility(pos, bin?.desiredProperties?.ignoredSensors ?? []);\r\n }}\r\n defaultChecked={true}\r\n />\r\n Stack Text

\r\n {\r\n this.segmentLabelVisibility(pos);\r\n }}\r\n defaultChecked={false}\r\n />\r\n Segment Text

\r\n {\r\n this.arrowVisibility(pos);\r\n }}\r\n defaultChecked={false}\r\n />\r\n Arrows

\r\n {\r\n this.layerMeshVisibility(pos);\r\n }}\r\n defaultChecked={false}\r\n />\r\n Layers

\r\n {\r\n this.toggleAllGates(pos);\r\n this.allGatesSpools.isGatesOpen = pos;\r\n }}\r\n defaultChecked={false}\r\n />\r\n Gates

\r\n {\r\n this.toggleAllSpools(pos);\r\n this.allGatesSpools.isSpoolsOpen = pos;\r\n }}\r\n defaultChecked={false}\r\n />\r\n Spools

\r\n
\r\n }\r\n \r\n
\r\n \r\n \r\n
\r\n\r\n \r\n \r\n \r\n
\r\n {isAtLeastPowerUser && }\r\n \r\n \r\n {\r\n this.state.bin && \r\n \r\n }\r\n {\r\n this.state.bin && \r\n \r\n }\r\n \r\n \r\n );\r\n }\r\n\r\n heaterRecommendedTempFormatted(): string {\r\n const heaterMinRecomTemp = this.state.bin?.grain?.heaterMinRecomTemp;\r\n const heaterMaxRecomTemp = this.state.bin?.grain?.heaterMaxRecomTemp;\r\n\r\n if (heaterMinRecomTemp == null || heaterMaxRecomTemp == null) {\r\n return '-';\r\n }\r\n\r\n return `${heaterMinRecomTemp} - ${heaterMaxRecomTemp} °F`;\r\n }\r\n\r\n\r\n private populateLayer = (layer: LayerMCInfo | LayerMCInfo) => {\r\n var object = this.highlightArr.find(obj => obj.key === layer.number.toString());\r\n let index = this.highlightArr.findIndex(obj => obj.key === layer.number.toString());\r\n var calculatedColor = this.getBandColor(layer);\r\n if (object) {\r\n let binObject = object.object;\r\n let material = binObject.material;\r\n (material as MeshStandardMaterial).color.setHex(calculatedColor);\r\n this.highlightArr[index].defaultColor = calculatedColor;\r\n }\r\n let layerTxt = (this.scene.getObjectByName('Level_' + layer.number) as SpriteText2D);\r\n // hack for Horra demo - hide 0%\r\n if (this.state.bin?.name == \"Horra\") {\r\n layerTxt.text = ` L${layer.number}`;\r\n }\r\n else if (this.state.bin?.useRHForFanControl === true) {\r\n layerTxt.text = ` L${layer.number}: ${formatNumber(layer?.moistureCableTemperatureF, {decimalPlaces: 0, filler: \"\", suffix: \"°\"})} ${formatNumber(layer.rh, {decimalPlaces: 0, suffix: \"%RH\", filler: \"\"})}`;\r\n\r\n }\r\n else {\r\n layerTxt.text = ` L${layer.number}: ${formatNumber(layer?.mc, {decimalPlaces: 1, filler: \"\", suffix: \"%\"})}`;\r\n }\r\n // layer.segments!.forEach(segment => {\r\n // let segText = (this.scene.getObjectByName(segment.id!) as SpriteText2D);\r\n // segText.text = ` ${segment.moistureContent}% `;\r\n // });\r\n\r\n };\r\n\r\n private handleViewChange = (view: 'Stack' | 'Level' | 'Debug', ignoredIds: string[]) => {\r\n\r\n this.setState({ view: view });\r\n switch (view) {\r\n case 'Stack':\r\n this.stackLabelVisibility(true, ignoredIds);\r\n this.segmentLabelVisibility(false);\r\n this.layerMeshVisibility(false);\r\n break;\r\n case 'Level':\r\n this.stackLabelVisibility(true, ignoredIds);\r\n this.segmentLabelVisibility(false);\r\n this.layerMeshVisibility(true);\r\n break;\r\n case 'Debug':\r\n this.stackLabelVisibility(true, ignoredIds);\r\n this.segmentLabelVisibility(false);\r\n this.layerMeshVisibility(false);\r\n break;\r\n default:\r\n break;\r\n }\r\n }\r\n private initAll = () => {\r\n this.initModel();\r\n this.createTree();\r\n this.setState({ loading: false });\r\n }\r\n\r\n private createStack = (stack: StackDTO, stackIndex: number) => {\r\n const binData = this.state.bin;\r\n let stackClone = this.components.stackTemplate.clone();\r\n stackClone.material = this.components.stackTemplate.material.clone();\r\n stackClone.name = stack.id;\r\n var position;\r\n position = this.getStackTransformAmount(stackIndex, this.ringLengths[stack.ring], (stack.ring === 0), binData!, stack);\r\n var height = stack.topPoint!.heightFromFloor - .5;\r\n var radius = 5 / 48; // roughly .10416 this is 5 inch * .25\r\n stackClone.scale.set(radius, (height) * .25, radius);\r\n stackClone.position.set(position.x, position.y, position.z);\r\n this.scene.add(stackClone);\r\n this.addGatesToStacks(stack, stackClone, binData!);\r\n this.addStackText(stack, stackClone, binData!);\r\n\r\n // adding a little detail\r\n var binGeometry = new CylinderBufferGeometry(.2, .65, 1, 32, 1, true);\r\n var gateCoverMaterial = new MeshStandardMaterial({ color: GATECOLOR, side: THREE.FrontSide });\r\n var stackTopCover = new Mesh(binGeometry, gateCoverMaterial);\r\n\r\n var topGeometry = new SphereBufferGeometry(1, 24, 10);\r\n var topsphere = new Mesh(topGeometry, gateCoverMaterial);\r\n var sprite = new SpriteText2D(\r\n ` ${stack.id!} `,\r\n {\r\n align: textAlign.center,\r\n font: '52px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true\r\n }\r\n );\r\n sprite.scale.set(.034, .00075, .034);\r\n sprite.position.x = -2.9;\r\n sprite.position.y = .5;\r\n stackTopCover.scale.set(2.5, 0.04, 2.5);\r\n stackTopCover.position.y = .5;\r\n topsphere.scale.set(.2, .180, .2);\r\n topsphere.position.y = .5;\r\n stackTopCover.add(topsphere);\r\n stackClone.add(stackTopCover, sprite);\r\n this.stackArr.push(stackClone);\r\n var stackColor = `hsl(74, 16%, 76%)`; // hsl(0, 0%, 83%)\r\n this.highlightArr.push({ key: stack.id!, type: 'stack', highlightColor: stackColor, defaultColor: STACKCOLOR, object: stackClone });\r\n }\r\n\r\n private updateStacks = (bin: BinDTO) => {\r\n bin!.stacks?.forEach((stack: StackDTO, i: number) => {\r\n const curStack = this.scene.getObjectByName(stack.id!);\r\n if (curStack === undefined) {\r\n this.createStack(stack, i);\r\n }\r\n else {\r\n const topTemp = (curStack!.getObjectByName(`${stack.id} Temp`) as SpriteText2D);\r\n let topTempText = \"No Temp\"\r\n if (stack.topTemp != null) {\r\n topTempText = ` ${Math.ceil(stack.topTemp).toString()}°F `;\r\n }\r\n topTemp.text = topTempText;\r\n\r\n let topRHText = \"No RH\";\r\n if (stack.topRH != null) {\r\n topRHText = `${Math.ceil(stack.topRH).toString()}% RH`;\r\n }\r\n var topRH = (curStack!.getObjectByName(`${stack.id} RH`) as SpriteText2D);\r\n topRH.text = topRHText;\r\n \r\n // const { thermocouples, valves } = stack;\r\n // thermocouples?.forEach((thermocouple: ThermocoupleDTO) => {\r\n // var thermoText = (curStack!.getObjectByName(thermocouple.id!) as SpriteText2D);\r\n // thermoText.text = `${Math.ceil(thermocouple.temperature).toString()}°F`;\r\n // });\r\n stack.valves?.forEach((valve: ValveDTO) => {\r\n var gateCover = curStack!.getObjectByName(valve.id!);\r\n this.setGateSpoolVisuals(gateCover!, valve.isGateOpen, valve.isSpoolOpen);\r\n });\r\n }\r\n });\r\n }\r\n\r\n private updateLabels = () => {\r\n const { bin } = this.state;\r\n const hasFans = (this.state.bin?.fans?.length ?? 0) > 0;\r\n if (!isMoniteringBin(bin!)) {\r\n this.updateStacks(bin!);\r\n }\r\n\r\n bin!.temperatureCables!.forEach((cable: TemperatureCableDTO) => {\r\n var curCable = this.scene.getObjectByName(cable.id!);\r\n cable!.thermocouples?.forEach((thermocouple: ThermocoupleDTO) => {\r\n var thermoText = (curCable!.getObjectByName(thermocouple.id!) as SpriteText2D);\r\n var temperature = thermocouple.temperature != null ? thermocouple.temperature : 0.0;\r\n thermoText.text = `${Math.ceil(temperature).toString()}°F`;\r\n });\r\n });\r\n\r\n if (bin?.layerGrainStates) {\r\n bin?.layerGrainStates?.forEach(layer => {\r\n let convertedLayer: LayerMCInfo = {...layer,\r\n mc: layer.moistureContent, rh: layer.relitiveHumidity,\r\n moistureCableTemperatureF: layer.temperatureF };\r\n this.populateLayer(convertedLayer)\r\n })\r\n }\r\n else if (hasDyanmicLayers(bin!)) {\r\n // not supported\r\n // bin?.dynamicLayers?.forEach((layer) => {\r\n // this.populateLayer(layer);\r\n // });\r\n }\r\n else {\r\n // not supported\r\n // bin?.layers?.forEach((layer) => {\r\n // this.populateLayer(layer);\r\n // });\r\n }\r\n if (bin?.fans != null && bin?.fans.length > 0) {\r\n for (let fan of bin!.fans) {\r\n const heaterSprite = this.scene.getObjectByName(`Heater ${fan.id}`) as SpriteText2D;\r\n if (heaterSprite != null) {\r\n heaterSprite.text = ` Heater ${fan.isHeaterOn ? 'On' : 'Off'} `;\r\n }\r\n\r\n const fanSprite = this.scene.getObjectByName(`Fan ${fan.id}`) as SpriteText2D;\r\n if (fanSprite != null) {\r\n fanSprite.text = ` Fan ${fan.isOn ? 'On' : 'Off'} `;\r\n }\r\n }\r\n }\r\n\r\n if ((bin?.hardwareYear ?? 0) >= 2022) {\r\n let co2Sprite = this.scene.getObjectByName(`CO2`) as SpriteText2D | undefined;\r\n if (co2Sprite == null) {\r\n this.addCO2Text(this.state.bin!, this.components.eaveTop);\r\n }\r\n co2Sprite = this.scene.getObjectByName(`CO2`) as SpriteText2D;\r\n co2Sprite.text = ` CO2: ${bin!.cO2Level ?? \"___\"} PPM `;\r\n }\r\n\r\n if (hasFans) {\r\n const plenumSprite = this.scene.getObjectByName('Plenum') as SpriteText2D;\r\n if (plenumSprite != null) {\r\n plenumSprite.text = ` Plenum ${Math.round(bin!.plenumAir!.temp!)}°F ${Math.round(bin!.plenumAir!.rh!)}%RH`;\r\n }\r\n\r\n const ambientSprite = this.scene.getObjectByName('Ambient') as SpriteText2D;\r\n if (ambientSprite != null) {\r\n ambientSprite.text = ` Ambient ${Math.round(bin!.ambientAir!.temp!)}°F ${Math.round(bin!.ambientAir!.rh!)}%RH `;\r\n }\r\n }\r\n\r\n // (hack) used to force hiding of any newly added stack text if in grower view\r\n this.handleViewChange(this.state.view, bin?.desiredProperties?.ignoredSensors ?? []);\r\n }\r\n\r\n private logAsciiBinState = () => {\r\n console.log('manual push');\r\n console.log(currentAscii);\r\n }\r\n\r\n private getHourlyForecast = () => {\r\n BinApiService.getHourlyForecast(this.state.binInfo.id).then(forecast => {\r\n console.log(forecast);\r\n }).catch(error => {\r\n console.log(error);\r\n });\r\n }\r\n\r\n private getMoistureForecast = () => {\r\n BinApiService.getMoistureForecast(this.state.binInfo.id).then(forecast => {\r\n console.log(forecast);\r\n }).catch(error => {\r\n console.log(error);\r\n });\r\n }\r\n\r\n // private viewLogFile(logType: string) {\r\n\r\n // var deviceId = this.state.binInfo.deviceId;\r\n // var today = new Date();\r\n // var dd = String(today.getUTCDate()).padStart(2, '0');\r\n // var mm = String(today.getUTCMonth() + 1).padStart(2, '0'); // January is 0!\r\n // var yyyy = today.getFullYear();\r\n // var yy = yyyy % 1000;\r\n // var link: string;\r\n // switch (logType) {\r\n // case 'OperationLog':\r\n // link = `${deviceLogsBaseURI}${deviceId}/${yyyy}-${mm}-${dd}/${mm}${dd}%20OperationLog.txt`;\r\n // break;\r\n // case 'CanLog':\r\n // link = `${deviceLogsBaseURI}${deviceId}/${yyyy}-${mm}-${dd}/${yy}${mm}${dd}%20CanLog%20${deviceId}.txt`;\r\n // break;\r\n // case 'ErrorLog':\r\n // link = `${deviceLogsBaseURI}${deviceId}/${yyyy}-${mm}-${dd}/${yy}${mm}${dd}%20Log%20${deviceId}.txt`;\r\n // break;\r\n // default:\r\n // link = `${deviceLogsBaseURI}${deviceId}/${yyyy}-${mm}-${dd}/${mm}${dd}%20${logType}%20${deviceId}.txt`;\r\n // break;\r\n // }\r\n\r\n // console.log(link);\r\n // window.open(link);\r\n // }\r\n\r\n // private viewCanLog = () => {\r\n // this.viewLogFile('CanLog');\r\n // }\r\n // private viewOperationLog = () => {\r\n // this.viewLogFile('OperationLog');\r\n // }\r\n\r\n private viewBinState = async () => {\r\n var today = new Date();\r\n var utcToday = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()));\r\n var dateStart = utcToday.toISOString().substring(0, 10); // yyyy-mm-dd UTC\r\n var dateEnd = dateStart;\r\n await viewBinStateLogsByRange(this.state.binInfo.deviceId!, dateStart, dateEnd);\r\n // this.viewLogFile('BinState');\r\n }\r\n\r\n private handleLogClickSubmit = (type: LogType) => (dateStart: string, dateEnd: string) => {\r\n const clientFolder = this.state.bin?.deviceId!;\r\n switch (type) {\r\n case \"BinState\":\r\n viewBinStateLogsByRange(clientFolder!, dateStart, dateEnd);\r\n break;\r\n case \"GrainState\":\r\n viewGrainStateLogsByRange(clientFolder!, dateStart, dateEnd);\r\n break;\r\n case \"Run\":\r\n viewRunLogsByRange(clientFolder!, dateStart, dateEnd);\r\n break;\r\n }\r\n }\r\n\r\n // private viewGrainStateFile = () => {\r\n // this.viewLogFile('GrainState');\r\n // }\r\n\r\n // private viewErrorLog = () => {\r\n // this.viewLogFile('ErrorLog');\r\n // }\r\n\r\n private fetchBin = async (deviceId: string, initialLoad: boolean, manual: boolean) => {\r\n const { binInfo } = this.state;\r\n this.setState({ refreshDelay: true });\r\n if (initialLoad) {\r\n this.props.isLoading(true);\r\n const userTimeZoneOffset = getUserTimezoneOffset();\r\n try {\r\n const bin = await BinApiService.getBinDetailFromAzure(deviceId, binInfo.id, userTimeZoneOffset);\r\n this.setState(\r\n { bin, refreshDelay: false }, () => {\r\n this.props.updateBin(bin);\r\n this.initAll();\r\n });\r\n\r\n this.checkForWarnings();\r\n\r\n currentAscii = bin.ascii;\r\n console.log(currentAscii);\r\n } catch (error) {\r\n this.setState({ refreshDelay: false });\r\n\r\n console.log(error);\r\n }\r\n } else {\r\n const userTimeZoneOffset = getUserTimezoneOffset();\r\n try {\r\n const bin = await BinApiService.getBinDetailFromDevice(deviceId, binInfo.id, userTimeZoneOffset);\r\n this.setState(\r\n { bin, refreshDelay: false }, () => {\r\n if (manual) {\r\n message.success('Data Refreshed', 3);\r\n }\r\n this.props.updateBin(bin);\r\n this.updateLabels();\r\n\r\n });\r\n this.checkForWarnings();\r\n\r\n currentAscii = bin.ascii;\r\n } catch (error) {\r\n this.setState({ refreshDelay: false });\r\n console.log(error);\r\n }\r\n }\r\n console.log(this.state);\r\n }\r\n\r\n componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void {\r\n if (prevProps.bin != this.props.bin) {\r\n if (this.props.bin == null) {\r\n console.error(\"not updating bin visual state since supplied binDTO is null\", this.props.bin);\r\n return;\r\n }\r\n\r\n // prevent storing old BinDTO unless either captureTime is null\r\n if (this.state.bin?.captureTimeUtc != null && this.props.bin?.captureTimeUtc != null && this.state.bin?.captureTimeUtc > this.props.bin?.captureTimeUtc) {\r\n console.error(\"not updating bin visual state since supplied binDTO is older than current\", \"current: \", this.state.bin, \"passed bin\", this.props.bin);\r\n return;\r\n }\r\n\r\n this.setState(\r\n { bin: this.props.bin, refreshDelay: false }, () => {\r\n if (this.state.modelInitialized) {\r\n this.updateLabels();\r\n }\r\n else {\r\n this.initAll();\r\n }\r\n });\r\n\r\n //this.checkForWarnings();\r\n\r\n currentAscii = this.props.bin.ascii;\r\n }\r\n }\r\n\r\n\r\n private checkForWarnings = () => {\r\n // any warnings we would like to display based on bin State info\r\n\r\n const inModeCompressorPSIMatters: boolean = this.state.bin?.operatingMode !== OperatingMode.Idle && this.state.bin?.operatingMode !== OperatingMode.Storage && this.state.bin?.operatingMode !== OperatingMode.EmptyBin;\r\n const automationType = selectAutomationType(this.state.bin);\r\n if (automationType === AutomationType.DriStack && Number(this.state.bin?.pneumaticLinePressure) < 20 && this.state.bin?.compressorState?.isOn && inModeCompressorPSIMatters) {\r\n message.warning('Line Pressure low: ' + this.state.bin?.pneumaticLinePressure?.toFixed(1), 5);\r\n console.warn('Line Pressure low: ' + this.state.bin?.pneumaticLinePressure);\r\n }\r\n\r\n if (!(this.state.bin?.arePlenumAirSensorsResponsive ?? true)) {\r\n message.error('All of the plenum air sensors are NOT responsive');\r\n }\r\n\r\n if (!(this.state.bin?.areAmbientAirSensorsResponsive ?? true)) {\r\n message.error('All of the ambient air sensors are NOT responsive');\r\n }\r\n\r\n if (this.props.activeErrors == null) {\r\n return;\r\n }\r\n\r\n for (const err of Object.values(this.props.activeErrors)) {\r\n if (!err.isActive) {\r\n continue;\r\n }\r\n switch (err.priority) {\r\n case ErrorPriority.CriticalError:\r\n message.error(err.name);\r\n break;\r\n case ErrorPriority.Warning:\r\n message.warning(err.name);\r\n break;\r\n default: {\r\n console.warn(`unknown err priority: ${err.name}, priority: ${err.priority}`);\r\n message.error(err.name);\r\n }\r\n \r\n }\r\n\r\n }\r\n }\r\n\r\n private updateValves = (desiredValveChanges: StackDTO[]) => {\r\n this.setState({\r\n valvesDisabled: true\r\n });\r\n let valvePositionUpdates = _.flatten(desiredValveChanges!\r\n .map(x => x.valves!.map(y => ({ valveId: y.id, position: y.positionLabel })))) as ValvePositionDTO[];\r\n BinApiService.setValvePositions(this.state.binInfo.deviceId!, valvePositionUpdates).then((result: boolean) => {\r\n if (result === true) {\r\n message.success('Valve configuration changed. Watch Refresh button for update.');\r\n }\r\n else if (result === false) {\r\n message.error('Failed to update valve configuration in all/part. Make sure system is in User Control mode');\r\n }\r\n else {\r\n console.error(\"Unexpected valve response gotten: \", result);\r\n message.error(\"valve update failed with unknown response: contact support\");\r\n }\r\n }).catch(() => {\r\n message.error('Failed to update valve configuration.');\r\n }).finally(() => {\r\n\r\n this.fetchBin(this.state.selectedBinId, false, true);\r\n \r\n this.setState({\r\n valvesDisabled: false\r\n });\r\n });\r\n }\r\n\r\n\r\n\r\n private createTree = () => {\r\n // this.setState({ view: 'Stack' });\r\n this.handleViewChange('Level', []);\r\n }\r\n\r\n // @ts-ignore defined but never used. Unknown what intended to be used for\r\n private highlightLevel = (key: string) => {\r\n let layerId = key ? key : '';\r\n this.setHighlightColorGroup(layerId, true);\r\n\r\n }\r\n\r\n private highlightStack = (key: string) => {\r\n let stackId = key ? key : ''; // if false will do nothing but reset all colors\r\n\r\n this.setHighlightColorGroup(stackId, true);\r\n\r\n }\r\n\r\n // @ts-ignore defined but never used. Unknown what intended to be used for\r\n private highlightOne = (keys: string[], type: string) => {\r\n for (let object of this.highlightArr) {\r\n let binObject = object.object;\r\n let material = binObject.material;\r\n let hasKey = keys.find(key => key === object.key);\r\n if (hasKey !== undefined) {\r\n let color = new Color(object.highlightColor);\r\n if (type !== 'segment') {\r\n (material as MeshStandardMaterial).color.setHex(color.getHex());\r\n } else {\r\n let hex = color.getHexString();\r\n let hexString = `#${hex}`;\r\n (binObject as SpriteText2D).fillStyle = hexString;\r\n }\r\n\r\n } else if (object.type === type) {\r\n if (type !== 'segment') {\r\n (material as MeshStandardMaterial).color.setHex(object.defaultColor);\r\n } else {\r\n let color = new Color(object.defaultColor).getHexString();\r\n let colorString = `#${color}`;\r\n (binObject as SpriteText2D).fillStyle = colorString;\r\n }\r\n }\r\n }\r\n }\r\n\r\n private setHighlightColorGroup = (key: string, resetOthers: Boolean) => {\r\n\r\n for (let object of this.highlightArr) {\r\n let binObject = object.object;\r\n let material = binObject.material;\r\n if (object.key === key) {\r\n let color = new Color(object.highlightColor).getHex();\r\n (material as MeshStandardMaterial).color.setHex(color);\r\n } else if (resetOthers) {\r\n if (object.type !== 'segment') {\r\n (material as MeshStandardMaterial).color.setHex(object.defaultColor);\r\n } else {\r\n let color = new Color(object.defaultColor).getHexString();\r\n let colorString = `#${color}`;\r\n (binObject as SpriteText2D).fillStyle = colorString;\r\n }\r\n }\r\n }\r\n }\r\n\r\n private createFans() {\r\n if (!this.state.bin?.fans?.length) { return; }\r\n let cylinderLength = .8;\r\n let cylinderRadius = .3;\r\n let cubeLength = .6;\r\n let fanMaterial = new MeshStandardMaterial({ color: BINCOLOR, side: THREE.FrontSide });\r\n\r\n for (let fan of this.state.bin!.fans!) {\r\n let radians = THREE.MathUtils.degToRad(fan.location!.bearingAngle);\r\n\r\n var cylinderGeometry = new CylinderBufferGeometry(cylinderRadius, cylinderRadius, cylinderLength, 32, 1, false);\r\n var cylinderMesh = new Mesh(cylinderGeometry, fanMaterial);\r\n\r\n var cubeGeometry = new BoxBufferGeometry(cubeLength, cubeLength, cubeLength);\r\n var cubeMesh = new Mesh(cubeGeometry, fanMaterial);\r\n\r\n cylinderGeometry.translate(0, fan.location!.distanceFromCenter * 0.25 + cylinderLength / 2, 0);\r\n cubeGeometry.translate(0, fan.location!.distanceFromCenter * .25 + cylinderLength + cubeLength / 2, 0);\r\n\r\n cylinderMesh.rotateZ(-THREE.MathUtils.degToRad(90));\r\n cylinderMesh.rotateOnWorldAxis(new Vector3(0, 1, 0), radians);\r\n\r\n cubeMesh.rotateZ(-THREE.MathUtils.degToRad(90));\r\n cubeMesh.rotateOnWorldAxis(new Vector3(0, 1, 0), radians);\r\n\r\n cylinderMesh.position.y = -this.state.bin.eaveHeight * .5 * .25 + fan.location!.heightFromFloor * .25 + cylinderRadius / 2;\r\n cubeMesh.position.y = -this.state.bin.eaveHeight * .5 * .25 + fan.location!.heightFromFloor * .25 + cylinderRadius / 2;\r\n\r\n var fanLabel = new SpriteText2D(\r\n ` Fan ${fan.isOn ? 'On' : 'Off'} `,\r\n {\r\n align: textAlign.center,\r\n font: '60px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true\r\n });\r\n fanLabel.name = `Fan ${fan.id}`;\r\n fanLabel.scale.set(.0065, .0065, .0065);\r\n fanLabel.translateY(this.state.bin.diameter * .5 * .25 + cylinderLength + cubeLength + .8);\r\n \r\n var heaterLabel = new SpriteText2D(\r\n ` Heater ${fan.isHeaterOn ? 'On' : 'Off'} `,\r\n {\r\n align: textAlign.center,\r\n font: '60px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true\r\n });\r\n\r\n heaterLabel.scale.set(.0065, .0065, .0065);\r\n heaterLabel.name = `Heater ${fan.id}`;\r\n heaterLabel.translateY(this.state.bin.diameter * .5 * .25 + cylinderLength);\r\n heaterLabel.translateZ(1.4);\r\n\r\n this.scene.add(cylinderMesh);\r\n this.scene.add(cubeMesh);\r\n cubeMesh.add(fanLabel);\r\n if (binConfiguredWithHeaters(this.state.bin)) {\r\n cubeMesh.add(heaterLabel);\r\n }\r\n }\r\n }\r\n\r\n private initModel = () => {\r\n // Add renderer mount it onto the page\r\n this.renderer = new WebGLRenderer({ antialias: true });\r\n\r\n this.mount = document.getElementById('player');\r\n\r\n // this.renderer.setPixelRatio(window.devicePixelRatio);\r\n this.mount?.appendChild(this.renderer.domElement);\r\n var container = this.renderer.domElement.parentElement;\r\n let box = container!.getBoundingClientRect();\r\n this.renderer.setSize(box.width, box.height);\r\n this.renderer.setPixelRatio(window.devicePixelRatio);\r\n window.addEventListener('resize', this.onContainerResize);\r\n this.scene = new Scene();\r\n this.scene.background = new Color('#FFFFFF');\r\n this.camera = new PerspectiveCamera(60, 1, .1, 2000);\r\n this.camera.aspect = box.width / box.height;\r\n this.camera.updateProjectionMatrix();\r\n var light = new PointLight(0xfbf9e4, .6);\r\n light.position.set(300, 650, 1000);\r\n light.castShadow = true;\r\n this.scene.add(light);\r\n var light2 = new AmbientLight(0xFFFFFF, .5);\r\n this.scene.add(light2);\r\n this.controls = new OrbitControls(this.camera, this.mount!);\r\n this.controls.enablePan = false;\r\n this.controls.enableDamping = true;\r\n this.controls.dampingFactor = 0.25;\r\n this.controls.minPolarAngle = Math.PI / 4;\r\n this.controls.maxPolarAngle = 2 * Math.PI / 3;\r\n // build template models of the bin\r\n var eaveGeometry = new CylinderBufferGeometry(0.2, 1, 1, 32, 1, true);\r\n var binGeometry = new CylinderBufferGeometry(1, 1, 1, 32, 1, true);\r\n var cableGeometry = new CylinderBufferGeometry(0.2, 0.2, 1, 32, 1, false);\r\n var binNonOpenFacedGeometry = new CylinderBufferGeometry(1, 1, 1, 42, 1, false);\r\n var gateCoverGeometry = new CylinderBufferGeometry(0.4, 0.65, 1, 42, 1, true);\r\n var backMaterial = new MeshStandardMaterial({ color: BINCOLOR, side: THREE.BackSide });\r\n var frontMaterial = new MeshStandardMaterial({ color: BINCOLOR, side: THREE.FrontSide });\r\n var stackMaterial = new MeshStandardMaterial({ color: STACKCOLOR, side: THREE.FrontSide });\r\n var cableTMaterial = new MeshStandardMaterial({ color: TEMPCABLECOLOR, side: THREE.FrontSide });\r\n var cableRHTMaterial = new MeshStandardMaterial({ color: RHTCABLECOLOR, side: THREE.FrontSide });\r\n var cableRHTWirelessMaterial = new MeshStandardMaterial({ color: RHT_WIRELESS_CABLE_COLOR, side: THREE.FrontSide });\r\n var gateCoverMaterial = new MeshStandardMaterial({ color: GATECOLOR, side: THREE.FrontSide });\r\n\r\n var bin = new Mesh(binGeometry, backMaterial);\r\n var clearBin = bin.clone();\r\n // used to position always-visible sprite text\r\n var alwaysVisibleClearBin = bin.clone();\r\n\r\n var eave = new Mesh(eaveGeometry, backMaterial);\r\n var bottom = new Mesh(binNonOpenFacedGeometry, frontMaterial);\r\n var eaveTop = new Mesh(binNonOpenFacedGeometry, frontMaterial);\r\n var stackTemplate = new Mesh(binGeometry, stackMaterial);\r\n var cableTTemplate = new Mesh(cableGeometry, cableTMaterial);\r\n var cableRHTTemplate = new Mesh(cableGeometry, cableRHTMaterial);\r\n var cableRHTWirelessTemplate = new Mesh(cableGeometry, cableRHTWirelessMaterial);\r\n\r\n var gateCover = new Mesh(gateCoverGeometry, gateCoverMaterial);\r\n\r\n this.components = {\r\n bin: bin,\r\n clearBin: clearBin,\r\n alwaysVisibleClearBin: alwaysVisibleClearBin,\r\n eave: eave,\r\n bottom: bottom,\r\n eaveTop: eaveTop,\r\n stackTemplate: stackTemplate,\r\n cableTTemplate: cableTTemplate,\r\n cableRHTTemplate: cableRHTTemplate,\r\n cableRHTWirelessTemplate: cableRHTWirelessTemplate,\r\n gateCover: gateCover,\r\n };\r\n\r\n // scale and transform object to bin amount\r\n this.adjustBinModel();\r\n bin.add(bottom);\r\n bottom.scale.set(1, 0.05, 1);\r\n bottom.position.y = -0.5;\r\n eave.add(eaveTop);\r\n eaveTop.scale.set(0.204, 0.04, 0.204);\r\n eaveTop.position.y = 0.51;\r\n\r\n if ((this.state.bin?.hardwareYear ?? 0) >= 2022) {\r\n this.addCO2Text(this.state.bin!, this.components.eaveTop);\r\n }\r\n this.scene.add(bin, clearBin, alwaysVisibleClearBin, eave);\r\n this.createStacks();\r\n this.createLayers();\r\n if ((this.state.bin?.fans?.length ?? 0) > 0) {\r\n this.createFans();\r\n if(this.state.bin?.plenumAir != null){\r\n this.addPlenumLabel(this.state.bin!);\r\n }\r\n if(this.state.bin?.ambientAir != null){\r\n this.addAmbientTempLabel(this.state.bin!);\r\n }\r\n }\r\n\r\n this.camera.position.set(9, 0, 9);\r\n this.controls.update();\r\n this.renderScene();\r\n this.animate();\r\n this.setState({modelInitialized: true});\r\n this.props.isLoading(false);\r\n\r\n }\r\n private adjustBinModel = () => {\r\n var binData = this.state.bin;\r\n var { bin, clearBin, alwaysVisibleClearBin, eave } = this.components;\r\n var diameter = binData!.diameter;\r\n var radius = diameter / 2;\r\n var eaveHeight = binData!.eaveHeight;\r\n var eaveTipHeight = binData!.peakHeight - eaveHeight;\r\n // scaling everything down to 1/4 scale\r\n bin.scale.set(radius * .25, eaveHeight * .25, radius * .25);\r\n bin.position.y = 0; // (eaveHeight * .25) / 2;\r\n clearBin.scale.set(radius * .25, eaveHeight * .25, radius * .25);\r\n clearBin.position.y = 0;\r\n clearBin.material = bin.material.clone();\r\n clearBin.material.transparent = true;\r\n clearBin.material.opacity = 0;\r\n clearBin.visible = false;\r\n alwaysVisibleClearBin.scale.set(radius * .25, eaveHeight * .25, radius * .25);\r\n alwaysVisibleClearBin.position.y = 0;\r\n alwaysVisibleClearBin.material = bin.material.clone();\r\n alwaysVisibleClearBin.material.transparent = true;\r\n alwaysVisibleClearBin.material.opacity = 0;\r\n alwaysVisibleClearBin.visible = true;\r\n eave.scale.set(radius * .25, eaveTipHeight * .25, radius * .25);\r\n eave.position.y = ((eaveHeight * .25) / 2) + ((eaveTipHeight * .25) / 2);\r\n\r\n }\r\n\r\n \r\n private addMoistureCable = (cable: MoistureCableDTO, index: number, ringLengths: number[], isWireless: boolean = false) => {\r\n if ((cable?.rhtStates?.length ?? 0) <= 0) {\r\n // skip\r\n return;\r\n }\r\n\r\n let cableClone = this.components.cableRHTTemplate.clone();\r\n cableClone.material = this.components.cableRHTTemplate.material.clone();\r\n cableClone.name = cable.id;\r\n var position;\r\n position = this.getMoistureCableTransformAmount(index, ringLengths[cable.ring], (cable.ring === 0), this.state.bin!, cable);\r\n // var height = cable.topPoint!.heightFromFloor - .5;\r\n var height = cable.height - .5;\r\n var radius = 5 / 48; // roughly .10416 this is 5 inch * .25\r\n cableClone.scale.set(radius, (height) * .25, radius);\r\n cableClone.position.set(position.x, position.y, position.z);\r\n this.scene.add(cableClone);\r\n this.addMoistureCableText(cable, cableClone, this.state.bin!);\r\n }\r\n\r\n private addMoistureRFCable = (cable: MoistureCableRFDTO, index: number, ringLengths: number[]) => {\r\n if ((cable?.wirelessStates?.length ?? 0) <= 0) {\r\n // skip\r\n return;\r\n }\r\n\r\n let cableClone = this.components.cableRHTWirelessTemplate.clone();\r\n cableClone.material = this.components.cableRHTWirelessTemplate.material.clone();\r\n cableClone.name = cable.id;\r\n const position = this.getMoistureCableRFTransformAmount(index, ringLengths[cable.ring], (cable.ring === 0), this.state.bin!, cable);\r\n // var height = cable.topPoint!.heightFromFloor - .5;\r\n var height = cable.height - .5;\r\n var radius = 5 / 48; // roughly .10416 this is 5 inch * .25\r\n cableClone.scale.set(radius, (height) * .25, radius);\r\n cableClone.position.set(position.x, position.y, position.z);\r\n this.scene.add(cableClone);\r\n this.addMoistureCableRFText(cable, cableClone, this.state.bin!);\r\n }\r\n\r\n private createStacks = () => {\r\n var binData = this.state.bin;\r\n var stacks = binData!.stacks;\r\n var tempCables = binData!.temperatureCables;\r\n var moistureCables = binData!.moistureCables ?? [];\r\n const moistureCables_RF = binData!.moistureCables_RF ?? [];\r\n const opiMoistureCables = binData?.opiMoistureCables ?? [];\r\n const binSenseMoistureCables = binData?.binSenseMoistureCables ?? [];\r\n var quit = false;\r\n var currentRing = 0;\r\n\r\n while (!quit) {\r\n if(stacks == null || stacks.length == 0){\r\n const numberOfRings = this.state.bin?.desiredProperties?.layout?.rings?.length ?? 0;\r\n for(var j = 0; j< numberOfRings; j++){\r\n this.ringLengths.push(0);\r\n }\r\n quit = true;\r\n }else{\r\n const stackCount = stacks?.filter(s => s.ring === currentRing).length ?? 0;\r\n if (stackCount > 0) {\r\n this.ringLengths.push(stackCount);\r\n } else {\r\n quit = true;\r\n }\r\n currentRing++;\r\n }\r\n }\r\n\r\n tempCables?.forEach((cable: TemperatureCableDTO, i: number) => {\r\n let cableClone = this.components.cableTTemplate.clone();\r\n cableClone.material = this.components.cableTTemplate.material.clone();\r\n cableClone.name = cable.id;\r\n var position;\r\n position = this.getTempCableTransformAmount(i, this.ringLengths[cable.ring], (cable.ring === 0), binData!, cable);\r\n // var height = cable.topPoint!.heightFromFloor - .5;\r\n var height = cable.height - .5;\r\n var radius = 5 / 48; // roughly .10416 this is 5 inch * .25\r\n cableClone.scale.set(radius, (height) * .25, radius);\r\n cableClone.position.set(position.x, position.y, position.z);\r\n this.scene.add(cableClone);\r\n this.addTempCableText(cable, cableClone, binData!);\r\n });\r\n\r\n moistureCables?.forEach((cable: MoistureCableDTO, i: number) => {\r\n this.addMoistureCable(cable, i, this.ringLengths);\r\n });\r\n\r\n moistureCables_RF?.forEach((cable: MoistureCableRFDTO, i: number) => {\r\n this.addMoistureRFCable(cable, i, this.ringLengths);\r\n });\r\n\r\n opiMoistureCables?.forEach((cable: MoistureCableDTO, i: number) => {\r\n this.addMoistureCable(cable, i, this.ringLengths);\r\n });\r\n\r\n binSenseMoistureCables?.forEach((cable: MoistureCableDTO, i: number) => {\r\n this.addMoistureCable(cable, i, this.ringLengths);\r\n });\r\n\r\n if (!isMoniteringBin(binData!) && stacks != null && stacks?.length > 0) {\r\n stacks.forEach((stack: StackDTO, i: number) => {\r\n this.createStack(stack, i);\r\n });\r\n }\r\n }\r\n\r\n\r\n\r\n private createLayers = () => {\r\n var binData = this.state.bin;\r\n let layers: LayerMCInfo[] = transformLayers(binData!);\r\n console.log(\"layer info: \", layers);\r\n\r\n // if (hasDyanmicLayers(binData!)) {\r\n // layers = binData!.dynamicLayers!;\r\n // }\r\n // else {\r\n // layers = binData!.layers!;\r\n // }\r\n var eaveGeometry = new CylinderBufferGeometry(0.25, 1, 1, 32, 1, true);\r\n var layerGeometry = new CylinderBufferGeometry(1, 1, 1, 32, 1, true);\r\n var layerMaterial = new MeshStandardMaterial({ color: DEFUALTLAYERCOLOR, side: THREE.BackSide });\r\n\r\n layers?.forEach((layer: LayerMCInfo, i: any) => {\r\n let ifTop = layer.bottom!.heightFromFloor >= binData!.eaveHeight * .90;\r\n var calculatedColor = this.getBandColor(layer);\r\n var newMaterial = layerMaterial.clone();\r\n newMaterial.color.setHex(calculatedColor);\r\n var layerMesh = new Mesh(ifTop ? eaveGeometry : layerGeometry, newMaterial);\r\n var dimensions = this.getLayerTransformAmount(binData!, layer, ifTop);\r\n layerMesh.name = layer.number.toString();\r\n var noTopCone = !ifTop && (layer.top!.heightFromFloor >= binData!.eaveHeight);\r\n\r\n this.addLayerLabels(layer, binData!, layerMesh, noTopCone);\r\n \r\n layerMesh.scale.set(\r\n layer.bottom!.heightFromFloor >= binData!.eaveHeight ? .92 : (ifTop ? 1 : .98),\r\n dimensions.scaleY,\r\n layer.bottom!.heightFromFloor >= binData!.eaveHeight ? .92 : (ifTop ? 1 : .98));\r\n layerMesh.position.y = dimensions.y;\r\n this.components.bin.add(layerMesh);\r\n\r\n layerMesh.visible = false;\r\n this.layerMeshArr.push(layerMesh);\r\n this.highlightArr.push({\r\n key: layer.number.toString(),\r\n type: 'layer',\r\n highlightColor: 'hsl(168,60%,70%)',\r\n defaultColor: calculatedColor,\r\n object: layerMesh\r\n });\r\n });\r\n }\r\n\r\n private getBandColor = (layer: LayerMCInfo) => {\r\n const { bin } = this.state;\r\n const EMPTY_COLOR = 0xc7c7c7;\r\n const YELLOW_COLOR = 0xD1BE14;\r\n const LIGHT_BLUE = 0x15CAD1;\r\n const DARK_BLUE = 0x1595D1;\r\n\r\n if (!layer.hasGrain) {\r\n return EMPTY_COLOR;\r\n }\r\n\r\n var mc = layer.mc;\r\n if (mc == null) {\r\n return DEFUALTLAYERCOLOR;\r\n }\r\n if(mc < 16){\r\n return YELLOW_COLOR\r\n }\r\n if(mc >= 16 && mc <= 20){\r\n return LIGHT_BLUE;\r\n }\r\n if(mc >= 20){\r\n return DARK_BLUE;\r\n }\r\n return DEFUALTLAYERCOLOR;\r\n }\r\n\r\n private addLayerLabels = (layer: LayerMCInfo, binData: BinDTO, layerMesh: Object3D, noTopCone: boolean) => {\r\n \r\n\r\n\r\n // if(layer.mc === null){\r\n // return;\r\n // }\r\n\r\n let layerText = ` L${layer.number}: ${formatNumber(layer?.mc, {decimalPlaces: 1, filler: \"\", suffix: \"%\"})}`;\r\n \r\n var layerLevelLabel = new SpriteText2D(\r\n layerText,\r\n {\r\n align: textAlign.center,\r\n font: '60px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true,\r\n });\r\n\r\n layerLevelLabel.scale.set(.0014, .0011, .0014);\r\n let tempVector = new Vector3();\r\n layerLevelLabel.getWorldScale(tempVector);\r\n layerLevelLabel.name = 'Level_' + layer.number;\r\n let ifTopMovFront = 1;\r\n let ifTopMovSide = -.9;\r\n let layerMiddle = (((noTopCone ? binData!.eaveHeight : layer.top!.heightFromFloor) - layer.bottom!.heightFromFloor) / 2);\r\n let labelHeight = ((layerMiddle + layer.bottom!.heightFromFloor) / binData!.eaveHeight) - .5;\r\n layerLevelLabel.position.set(ifTopMovSide, labelHeight, ifTopMovFront);\r\n\r\n if((binData.moistureCables == null || binData.moistureCables.length === 0) \r\n && (binData.opiMoistureCables == null || binData.opiMoistureCables.length === 0)){\r\n layerLevelLabel.visible = false;\r\n }\r\n\r\n this.components.clearBin.add(layerLevelLabel);\r\n\r\n // layer.segments!.forEach((segment) => {\r\n // var segmentSprite = new SpriteText2D(\r\n // ` ${segment.moistureContent ? `${segment.moistureContent}%` : ''} `,\r\n // {\r\n // align: textAlign.center,\r\n // font: '42px Helvetica',\r\n // fillStyle: '#FFFFFF',\r\n // backgroundColor: '#000000',\r\n // antialias: true\r\n // });\r\n // var bearingAngle = segment.centerPoint!.bearingAngle;\r\n // segmentSprite.name = segment.id ? segment.id : '';\r\n // var distFromCenter = segment.centerPoint?.distanceFromCenter\r\n // ?segment.centerPoint.distanceFromCenter / (binData.diameter * 0.5)\r\n // : 0.05;\r\n // var x = distFromCenter * Math.cos(bearingAngle * Math.PI / 180);\r\n // var z = distFromCenter * Math.sin(bearingAngle * Math.PI / 180);\r\n // var y = ((segment.centerPoint!.heightFromFloor) / binData.eaveHeight) - 0.5;\r\n // segmentSprite.position.set(x, y, z);\r\n // segmentSprite.scale.set(.0015, .0011, .0015);\r\n\r\n // this.highlightArr.push({\r\n // key: segment.id!,\r\n // type: 'segment',\r\n // highlightColor: 'hsl(0, 100%, 50%)',\r\n // defaultColor: 0xFFFFFF,\r\n // object: segmentSprite\r\n // });\r\n // segmentSprite.visible = false;\r\n // this.components.bin.add(segmentSprite);\r\n // this.segmentTextArr.push(segmentSprite);\r\n // });\r\n }\r\n\r\n private addPlenumLabel = (binData: BinDTO) => {\r\n var plenumLabel = new SpriteText2D(\r\n ` Plenum ${Math.round(binData.plenumAir!.temp!)}°F ${Math.round(binData.plenumAir!.rh!)}%RH`,\r\n {\r\n align: textAlign.center,\r\n font: '60px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true\r\n });\r\n\r\n plenumLabel.scale.set(.0014, .0011, .0014);\r\n plenumLabel.name = 'Plenum';\r\n plenumLabel.position.set(0, -.5, 1.1);\r\n\r\n this.components.alwaysVisibleClearBin.add(plenumLabel);\r\n }\r\n\r\n private addAmbientTempLabel = (binData: BinDTO) => {\r\n var ambientTempLabel = new SpriteText2D(\r\n ` Ambient ${Math.round(binData.ambientAir!.temp!)}°F ${Math.round(binData.ambientAir!.rh!)}%RH `,\r\n {\r\n align: textAlign.center,\r\n font: '60px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true\r\n });\r\n\r\n ambientTempLabel.scale.set(.0014, .0011, .0014);\r\n ambientTempLabel.name = 'Ambient';\r\n ambientTempLabel.position.set(1, -.5, 1.1);\r\n\r\n this.components.alwaysVisibleClearBin.add(ambientTempLabel);\r\n }\r\n\r\n private getLayerTransformAmount = (binData: BinDTO, layer: LayerMCInfo, ifTop: boolean) => {\r\n var topLongNoCone = !ifTop && (layer.top!.heightFromFloor >= binData!.eaveHeight);\r\n var diff = (topLongNoCone ? binData!.eaveHeight : layer.top!.heightFromFloor) - layer.bottom!.heightFromFloor;\r\n var topLong = ifTop && (layer.bottom!.heightFromFloor <= binData!.eaveHeight);\r\n var scaleY = (diff / binData.eaveHeight) * .9;\r\n var position = ((layer.bottom!.heightFromFloor / binData.eaveHeight) - (topLong ? .465 : .5)) + (scaleY / 2);\r\n return { scaleY: scaleY, y: position };\r\n\r\n }\r\n\r\n private calculateCableTransformAmount = (transformData: cableTransformData): Vector3 => {\r\n const isCenter = transformData.isCenter;\r\n const ringLength = transformData.ringLength;\r\n const cableHeight = transformData.cableHeight;\r\n const bearing = transformData.bearing;\r\n const distanceFromCenter = transformData.distanceFromCenter;\r\n\r\n if (isCenter && ringLength === 1) {\r\n // var height = (((stack.topPoint!.heightFromFloor * 0.25) - (binData!.eaveHeight * 0.25)) / 2);\r\n var height = (((cableHeight * 0.25) - (this.state.bin!.eaveHeight * 0.25)) / 2);\r\n return new Vector3(0, height, 0);\r\n } else {\r\n\r\n // let x = 0, y = 0, z = 0, distanceFromCenter = stack.topPoint!.distanceFromCenter;\r\n //let x = 0, y = 0, z = 0;\r\n // let angle = 360 / ringLength;\r\n // let degrees = angle * i;\r\n\r\n // Make sure x value is never too close to zero (don't want a stack being blocked from view if we can't move the camera)\r\n let startingDegree = 0;\r\n switch (ringLength) {\r\n case 3:\r\n startingDegree = 20;\r\n break;\r\n case 4:\r\n startingDegree = 20;\r\n break;\r\n case 5:\r\n startingDegree = 22;\r\n break;\r\n case 6:\r\n startingDegree = 20;\r\n break;\r\n default:\r\n break;\r\n }\r\n let radians = (startingDegree + bearing) * Math.PI / 180;\r\n const x = (Math.cos(radians) * distanceFromCenter) * 0.25;\r\n const z = (Math.sin(radians) * distanceFromCenter) * 0.25;\r\n // y = (((stack.topPoint!.heightFromFloor * 0.25) - (binData!.eaveHeight * 0.25)) / 2);\r\n const y = (((cableHeight * 0.25) - (this.state.bin!.eaveHeight * 0.25)) / 2);\r\n\r\n return new Vector3(x, y, z);\r\n }\r\n }\r\n\r\n private getMoistureCableTransformAmount = (i: number, ringLength: number, isCenter: boolean, binData: BinDTO, cable: MoistureCableDTO): Vector3 => {\r\n if (isCenter && ringLength === 1) {\r\n const height = (((cable.height * 0.25) - (binData!.eaveHeight * 0.25)) / 2);\r\n return new Vector3(0, height, 0);\r\n }\r\n\r\n const transformInputData: cableTransformData = {\r\n bearing: cable.bearing,\r\n isCenter: isCenter,\r\n ringLength: ringLength,\r\n distanceFromCenter: cable.rhtStates![0].location!.distanceFromCenter,\r\n cableHeight: cable.height,\r\n };\r\n const transformAmount = this.calculateCableTransformAmount(transformInputData);\r\n return transformAmount;\r\n }\r\n\r\n private getMoistureCableRFTransformAmount = (i: number, ringLength: number, isCenter: boolean, binData: BinDTO, cable: MoistureCableRFDTO): Vector3 => {\r\n if (isCenter && ringLength === 1) {\r\n const height = (((cable.height * 0.25) - (binData!.eaveHeight * 0.25)) / 2);\r\n return new Vector3(0, height, 0);\r\n }\r\n\r\n const transformInputData: cableTransformData = {\r\n bearing: cable.bearing,\r\n isCenter: isCenter,\r\n ringLength: ringLength,\r\n distanceFromCenter: cable.wirelessStates![0].location!.distanceFromCenter,\r\n cableHeight: cable.height,\r\n };\r\n const transformAmount = this.calculateCableTransformAmount(transformInputData);\r\n return transformAmount;\r\n }\r\n\r\n private getTempCableTransformAmount = (i: number, ringLength: number, isCenter: boolean, binData: BinDTO, cable: TemperatureCableDTO) => {\r\n if (isCenter && ringLength === 1) {\r\n // var height = (((stack.topPoint!.heightFromFloor * 0.25) - (binData!.eaveHeight * 0.25)) / 2);\r\n var height = (((cable.height * 0.25) - (binData!.eaveHeight * 0.25)) / 2);\r\n return new Vector3(0, height, 0);\r\n } else {\r\n\r\n // let x = 0, y = 0, z = 0, distanceFromCenter = stack.topPoint!.distanceFromCenter;\r\n var firstThermocouple = cable?.thermocouples![0];\r\n let x = 0, y = 0, z = 0, distanceFromCenter = firstThermocouple.location!.distanceFromCenter;\r\n // let angle = 360 / ringLength;\r\n // let degrees = angle * i;\r\n\r\n // Make sure x value is never too close to zero (don't want a stack being blocked from view if we can't move the camera)\r\n let startingDegree = 0;\r\n switch (ringLength) {\r\n case 3:\r\n startingDegree = 20;\r\n break;\r\n case 4:\r\n startingDegree = 20;\r\n break;\r\n case 5:\r\n startingDegree = 22;\r\n break;\r\n case 6:\r\n startingDegree = 20;\r\n break;\r\n default:\r\n break;\r\n }\r\n let radians = (startingDegree + cable.bearing) * Math.PI / 180;\r\n x = (Math.cos(radians) * distanceFromCenter) * 0.25;\r\n z = (Math.sin(radians) * distanceFromCenter) * 0.25;\r\n // y = (((stack.topPoint!.heightFromFloor * 0.25) - (binData!.eaveHeight * 0.25)) / 2);\r\n y = (((cable.height * 0.25) - (binData!.eaveHeight * 0.25)) / 2);\r\n\r\n return new Vector3(x, y, z);\r\n }\r\n }\r\n\r\n private getStackTransformAmount = (stackIndex: number, ringLength: number, isCenter: boolean, binData: BinDTO, stack: StackDTO) => {\r\n\r\n if (isCenter && ringLength === 1) {\r\n var height = (((stack.topPoint!.heightFromFloor * 0.25) - (binData!.eaveHeight * 0.25)) / 2);\r\n return new Vector3(0, height, 0);\r\n } else {\r\n\r\n let x = 0, y = 0, z = 0, distanceFromCenter = stack.topPoint!.distanceFromCenter;\r\n // let angle = 360 / ringLength;\r\n // let degrees = angle * stackIndex;\r\n\r\n // Make sure x value is never too close to zero (don't want a stack being blocked from view if we can't move the camera)\r\n // let startingDegree = 0;\r\n\r\n // switch (ringLength) {\r\n // case 3:\r\n // startingDegree = 20;\r\n // break;\r\n // case 4:\r\n // startingDegree = 20;\r\n // break;\r\n // case 5:\r\n // startingDegree = 22;\r\n // break;\r\n // case 6:\r\n // startingDegree = 20;\r\n // break;\r\n // default:\r\n // break;\r\n // }\r\n\r\n\r\n let finalDegree = stack.bearing;\r\n // Make sure x value is never too close to zero (don't want a stack being blocked from view if we can't move the camera)\r\n const MIN_BEARING = 20;\r\n const MAX_BEARING = 340;\r\n if (stack.bearing >= MAX_BEARING) {\r\n finalDegree = MAX_BEARING;\r\n }\r\n else if (stack.bearing <= MIN_BEARING) {\r\n finalDegree = MIN_BEARING;\r\n }\r\n\r\n let radians = (finalDegree) * Math.PI / 180;\r\n x = (Math.cos(radians) * distanceFromCenter) * 0.25;\r\n z = (Math.sin(radians) * distanceFromCenter) * 0.25;\r\n y = (((stack.topPoint!.heightFromFloor * 0.25) - (binData!.eaveHeight * 0.25)) / 2);\r\n\r\n return new Vector3(x, y, z);\r\n }\r\n }\r\n private stackLabelVisibility = (show: boolean, ignoredIds: string[]) => {\r\n \r\n for (let label of this.stackTextArr) {\r\n label.visible = ignoredIds.find( b => b === label.name) == null;\r\n }\r\n }\r\n\r\n private segmentLabelVisibility = (show: boolean) => {\r\n for (let label of this.segmentTextArr) {\r\n label.visible = show;\r\n }\r\n }\r\n private layerMeshVisibility = (show: boolean) => {\r\n this.components.clearBin.visible = show;\r\n for (let layer of this.layerMeshArr) {\r\n layer.visible = show;\r\n }\r\n }\r\n private arrowVisibility = (show: boolean) => {\r\n for (let line of this.arrowCurveGroupArr) {\r\n line.visible = show;\r\n }\r\n }\r\n\r\n private toggleAllGates = (show: boolean) => {\r\n for (let gate of this.gateCoverArr) {\r\n this.setGateSpoolVisuals(gate, show, this.allGatesSpools.isSpoolsOpen);\r\n }\r\n }\r\n\r\n private toggleAllSpools = (show: boolean) => {\r\n for (let gate of this.gateCoverArr) {\r\n this.setGateSpoolVisuals(gate, this.allGatesSpools.isGatesOpen, show);\r\n }\r\n }\r\n\r\n private addGatesToStacks = (stackInfo: StackDTO, stackClone: Mesh, binData: BinDTO) => {\r\n let valves = stackInfo.valves;\r\n var BoxGeometry = new THREE.BoxGeometry();\r\n var BoxMaterial = new MeshBasicMaterial({ color: 0x000000 });\r\n\r\n valves!.forEach((valve, i) => {\r\n var closedBoxGrouping = new Group();\r\n let gateCoverClone = this.components.gateCover.clone();\r\n gateCoverClone.material = this.components.gateCover.material.clone();\r\n gateCoverClone.name = valve.id;\r\n gateCoverClone.scale.set(2.5, 0.04, 2.5);\r\n var y = this.getGateTranslationAmount(stackInfo, valve);\r\n gateCoverClone.position.y = y;\r\n var box = new Mesh(BoxGeometry, BoxMaterial);\r\n box.name = 'gate-open';\r\n box.scale.set(0.63, 0.5, 0.63);\r\n box.position.set(0, 0.75, 0);\r\n gateCoverClone.add(box);\r\n\r\n var closedBoxTop = new Mesh(BoxGeometry, BoxMaterial);\r\n closedBoxTop.scale.set(1.4, 0.140, 1.1);\r\n closedBoxTop.position.y = 0.546;\r\n\r\n var closedBoxSide = new Mesh(BoxGeometry, BoxMaterial);\r\n closedBoxSide.scale.set(1.4, 0.680, .1);\r\n closedBoxSide.position.set(0, 0.276, -0.598);\r\n var closedBoxSideClone = closedBoxSide.clone();\r\n closedBoxSideClone.position.z = 0.598;\r\n closedBoxGrouping.add(closedBoxTop, closedBoxSide, closedBoxSideClone);\r\n closedBoxGrouping.name = 'gate-closed';\r\n gateCoverClone.add(closedBoxGrouping);\r\n\r\n this.addArrowHelpers(valve, gateCoverClone);\r\n if (i + 1 !== valves?.length) {\r\n this.createArrowCurves(valve, valves![i + 1], gateCoverClone, stackClone, stackInfo);\r\n }\r\n this.setGateSpoolVisuals(gateCoverClone, valve.isGateOpen, valve.isSpoolOpen);\r\n this.gateCoverArr.push(gateCoverClone);\r\n stackClone.add(gateCoverClone);\r\n let highlightColor = `hsl(219, 44% , 80%)`;\r\n this.highlightArr.push({\r\n key: valve.id!,\r\n type: 'valve',\r\n defaultColor: GATECOLOR,\r\n highlightColor: highlightColor,\r\n object: gateCoverClone\r\n });\r\n\r\n });\r\n }\r\n\r\n private addRHTOrRFText = (moistureCable: MoistureCableDTO | MoistureCableRFDTO, cableClone: Mesh, RHT: RHTorSensorTextAddInputs) => {\r\n let id = RHT.id;\r\n let y = this.getRHTTranslationAmount(moistureCable, RHT); // 0.1;\r\n\r\n // this is a hack to get some spacing for temp + RH\r\n // there is probably a better way to do put some absolute left and right space between the two sprites\r\n let xTransformAmount = 0;\r\n if (this.state.bin?.useRHForFanControl === true) {\r\n xTransformAmount = 2.5;\r\n }\r\n\r\n const preferredSensorType = this.state.bin?.temperatureSensorsType ?? getDefaultTemperatureSensorType(this.state.bin!);\r\n if ([TemperatureSensorEnum.Opi, TemperatureSensorEnum.PowerCast, TemperatureSensorEnum.BinSense].includes(preferredSensorType)\r\n || this.state.bin?.useRHForFanControl === true\r\n ) {\r\n const temperature = RHT.temperature;\r\n let spriteTemp = new SpriteText2D(\r\n `${formatNumber(temperature, { decimalPlaces: 0, filler: \"\", suffix: \"°F\", showSuffixIfNoValue: true})}`,\r\n {\r\n align: textAlign.center,\r\n font: '54px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true,\r\n\r\n });\r\n spriteTemp.name = id!;\r\n spriteTemp.position.set(0 - xTransformAmount, y, 3);\r\n spriteTemp.scale.set(.044, .000754, .044);\r\n this.stackTextArr.push(spriteTemp);\r\n cableClone.add(spriteTemp);\r\n }\r\n\r\n\r\n\r\n\r\n\r\n\r\n y = this.getRHTTranslationAmount(moistureCable, RHT); // 0.1;\r\n\r\n\r\n if (this.state.bin?.useRHForFanControl === false) {\r\n var MC = RHT.mc;\r\n let spriteMC = new SpriteText2D(\r\n `${formatNumber(MC, {decimalPlaces: 1, filler: \"\", suffix: \"%\"})}`,\r\n {\r\n align: textAlign.center,\r\n font: '54px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true,\r\n \r\n });\r\n spriteMC.name = id!;\r\n spriteMC.position.set(0, y, -3);\r\n spriteMC.scale.set(.044, .000754, .044);\r\n this.stackTextArr.push(spriteMC);\r\n cableClone.add(spriteMC); \r\n }\r\n else if (this.state.bin?.useRHForFanControl === true) {\r\n let spriteRH = new SpriteText2D(\r\n `${formatNumber(RHT.rh, {decimalPlaces: 0, filler: \"NO VALUE\", suffix: \"%RH\"})}`,\r\n {\r\n align: textAlign.center,\r\n font: '54px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true,\r\n \r\n });\r\n spriteRH.position.set(0 + xTransformAmount + 1.5, y, -3);\r\n spriteRH.scale.set(.044, .000754, .044);\r\n this.stackTextArr.push(spriteRH);\r\n cableClone.add(spriteRH); \r\n }\r\n\r\n }\r\n\r\n private addMoistureCableText = (moistureCable: MoistureCableDTO , cableClone: Mesh, binData: BinDTO) => {\r\n let RHTS = moistureCable.rhtStates;\r\n const ignoredSensors = binData?.desiredProperties?.ignoredSensors ?? []\r\n RHTS!.forEach((RHT, i) => {\r\n this.addRHTOrRFText(moistureCable, cableClone, RHT);\r\n });\r\n }\r\n\r\n private addMoistureCableRFText = (moistureCableRF: MoistureCableRFDTO , cableClone: Mesh, binData: BinDTO) => {\r\n let wirelessRFs = moistureCableRF.wirelessStates;\r\n const ignoredSensors = binData?.desiredProperties?.ignoredSensors ?? []\r\n wirelessRFs!.forEach((rfSensor, i) => {\r\n this.addRHTOrRFText(moistureCableRF, cableClone, rfSensor);\r\n });\r\n }\r\n\r\n private addTempCableText = (tempCable: TemperatureCableDTO, cableClone: Mesh, binData: BinDTO) => {\r\n let thermocouples = tempCable.thermocouples;\r\n const ignoredSensors = binData?.desiredProperties?.ignoredSensors ?? [];\r\n thermocouples!.forEach((thermocouple, i) => {\r\n let id = thermocouple.id;\r\n var temperature = thermocouple.temperature != null ? thermocouple.temperature : 0.0;\r\n let sprite = new SpriteText2D(\r\n `${Math.ceil(temperature).toString()}°F`,\r\n {\r\n align: textAlign.center,\r\n font: '54px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true,\r\n });\r\n sprite.name = id!;\r\n let y = this.getThermoTranslationAmount(tempCable, thermocouple); // 0.1;\r\n sprite.position.set(0, y, 2);\r\n sprite.scale.set(.044, .000754, .044);\r\n sprite.visible = ignoredSensors.find( b => b === thermocouple.id) == null;\r\n this.stackTextArr.push(sprite);\r\n cableClone.add(sprite);\r\n });\r\n }\r\n\r\n private addStackText = (stack: StackDTO, stackClone: Mesh, binData: BinDTO) => {\r\n // let thermocouples = stack.thermocouples;\r\n const { topTemp, topRH } = stack; // thermocouples,\r\n // thermocouples!.forEach((thermocouple, i) => {\r\n // let id = thermocouple.id;\r\n // let sprite = new SpriteText2D(\r\n // `${Math.ceil(thermocouple.temperature).toString()}°F`,\r\n // {\r\n // align: textAlign.center,\r\n // font: '54px Helvetica',\r\n // fillStyle: '#FFFFFF',\r\n // backgroundColor: '#000000',\r\n // antialias: true,\r\n\r\n // });\r\n // sprite.name = id!;\r\n // let y = this.getThermoTranslationAmount(stack, thermocouple);\r\n // sprite.position.set(0, y, 2);\r\n // sprite.scale.set(.044, .000754, .044);\r\n // this.stackTextArr.push(sprite);\r\n // stackClone.add(sprite);\r\n\r\n // });\r\n\r\n let idTop = `${stack.id} Temp`;\r\n let topTempText = \"No temp\";\r\n if (topTemp != null) {\r\n topTempText = ` ${Math.ceil(topTemp).toString()}°F `;\r\n }\r\n let spriteTop = new SpriteText2D(\r\n topTempText,\r\n {\r\n align: textAlign.center,\r\n font: '54px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true,\r\n\r\n });\r\n spriteTop.name = idTop;\r\n let yTop = .5;\r\n spriteTop.position.set(4, yTop, 0);\r\n spriteTop.scale.set(.044, .000754, .044);\r\n this.stackTextArr.push(spriteTop);\r\n stackClone.add(spriteTop);\r\n\r\n let idTopRH = `${stack.id} RH`;\r\n\r\n let topRHText = \"\";\r\n if (topRH == null) {\r\n topRHText = \"No RH\";\r\n }\r\n else {\r\n topRHText = `${Math.ceil(topRH).toString()}% RH`;\r\n }\r\n let spriteRH = new SpriteText2D(\r\n topRHText,\r\n {\r\n align: textAlign.center,\r\n font: '54px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true,\r\n\r\n });\r\n spriteRH.name = idTopRH;\r\n spriteRH.position.set(0, .6, 0);\r\n spriteRH.scale.set(.044, .000754, .044);\r\n this.stackTextArr.push(spriteRH);\r\n stackClone.add(spriteRH);\r\n }\r\n\r\n private addCO2Text = (binData: BinDTO, eaveClone: Mesh) => {\r\n const { cO2Level } = binData;\r\n\r\n let idTopCO2 = `CO2`;\r\n let spriteCO2 = new SpriteText2D(\r\n ` CO2 ${cO2Level ?? \"___\"} PPM `,\r\n {\r\n align: textAlign.center,\r\n font: '54px Helvetica',\r\n fillStyle: '#FFFFFF',\r\n backgroundColor: '#000000',\r\n antialias: true,\r\n\r\n });\r\n spriteCO2.name = idTopCO2;\r\n spriteCO2.position.set(0, 5, 0);\r\n spriteCO2.scale.set(.0067, .067, .067);\r\n this.stackTextArr.push(spriteCO2.clone());\r\n this.layerMeshArr.push(spriteCO2.clone());\r\n if(cO2Level == null){\r\n spriteCO2.visible = false;\r\n }\r\n eaveClone.add(spriteCO2);\r\n }\r\n\r\n private getRHTTranslationAmount = (cable: MoistureCableDTO | MoistureCableRFDTO, rht: RHTorSensorTextAddInputs) => {\r\n\r\n let height = rht.location!.heightFromFloor;\r\n\r\n let heightPercent = height / cable.height;\r\n\r\n return heightPercent - 0.5;\r\n }\r\n\r\n private getThermoTranslationAmount = (cable: TemperatureCableDTO, thermocouple: ThermocoupleDTO) => {\r\n\r\n let height = thermocouple.location!.heightFromFloor;\r\n\r\n let heightPercent = height / cable.height;\r\n\r\n return heightPercent - 0.5;\r\n }\r\n\r\n private getGateTranslationAmount = (stackInfo: StackDTO, valve: ValveDTO) => {\r\n let heightPercent = valve.location!.heightFromFloor / stackInfo.topPoint!.heightFromFloor;\r\n\r\n return heightPercent - 0.5;\r\n }\r\n\r\n private addArrowHelpers = (valve: ValveDTO, gateCover: Mesh) => {\r\n\r\n // make the arrows for take-in group\r\n let takeInGrouping = new Group();\r\n takeInGrouping.name = 'take-in-arrows';\r\n let upArrowGrouping = new Group();\r\n upArrowGrouping.name = 'up-arrows';\r\n let exhaustArrowGrouping = new Group();\r\n exhaustArrowGrouping.name = 'exhaust-arrows';\r\n let material = new LineBasicMaterial({ color: 0xff0000, linewidth: 1 });\r\n\r\n // add exhaust arrow groupings\r\n let exhaustCurve = new QuadraticBezierCurve3(\r\n new Vector3(.45, -.2, 0),\r\n new Vector3(.45, -.8, 0),\r\n new Vector3(1, -1.3, 0)\r\n );\r\n let arrowPoints = [\r\n new Vector3(.8, -.8, 0),\r\n new Vector3(1, -1.3, 0),\r\n new Vector3(.6, - 1.2, 0)\r\n ];\r\n\r\n let exhaustCurveL = new QuadraticBezierCurve3(\r\n new Vector3(-.45, -.2, 0),\r\n new Vector3(-.45, -.8, 0),\r\n new Vector3(-1, -1.3, 0)\r\n\r\n );\r\n let arrowPointsL = [\r\n new Vector3(-.8, -.8, 0),\r\n new Vector3(-1, -1.3, 0),\r\n new Vector3(-.6, - 1.2, 0)\r\n ];\r\n let points = exhaustCurve.getPoints(50);\r\n let geometry = new BufferGeometry().setFromPoints(points);\r\n let geometry1 = new BufferGeometry().setFromPoints(arrowPoints);\r\n let exhaustLine = new Line(geometry, material);\r\n let exhaustLineArrow = new Line(geometry1, material);\r\n\r\n let pointsL = exhaustCurveL.getPoints(50);\r\n let geometryL = new BufferGeometry().setFromPoints(pointsL);\r\n var geometry1L = new BufferGeometry().setFromPoints(arrowPointsL);\r\n var exhaustLineL = new Line(geometryL, material);\r\n var exhaustLineArrowL = new Line(geometry1L, material);\r\n exhaustArrowGrouping.add(exhaustLine, exhaustLineArrow, exhaustLineL, exhaustLineArrowL);\r\n // add take in arrows\r\n var takeInCurve = new QuadraticBezierCurve3(\r\n new Vector3(.55, -.55, 0),\r\n new Vector3(.55, -1.2, 0),\r\n new Vector3(1.1, -1.7, 0)\r\n );\r\n\r\n var takeInCurveL = new QuadraticBezierCurve3(\r\n new Vector3(-.55, -.55, 0),\r\n new Vector3(-.55, -1.2, 0),\r\n new Vector3(-1.1, -1.7, 0)\r\n );\r\n\r\n var takeInArrow = [\r\n new Vector3(.45, -.85, 0),\r\n new Vector3(.55, -.55, 0),\r\n new Vector3(.7, -.8, 0)\r\n ];\r\n\r\n var takeInArrowL = [\r\n new Vector3(-.45, -.85, 0),\r\n new Vector3(-.55, -.55, 0),\r\n new Vector3(-.7, -.8, 0)\r\n ];\r\n\r\n var takeInPoints = takeInCurve.getPoints(50);\r\n var takeInGeometry = new BufferGeometry().setFromPoints(takeInPoints);\r\n var takeInArrowGeometry = new BufferGeometry().setFromPoints(takeInArrow);\r\n var takeInLine = new Line(takeInGeometry, material);\r\n var takeInLineArrow = new Line(takeInArrowGeometry, material);\r\n\r\n var takeInPointsL = takeInCurveL.getPoints(50);\r\n var takeInGeometryL = new BufferGeometry().setFromPoints(takeInPointsL);\r\n var takeInArrowGeometryL = new BufferGeometry().setFromPoints(takeInArrowL);\r\n var takeInLineL = new Line(takeInGeometryL, material);\r\n var takeInLineArrowL = new Line(takeInArrowGeometryL, material);\r\n\r\n takeInGrouping.add(takeInLine, takeInLineArrow, takeInLineL, takeInLineArrowL);\r\n // add up arrow\r\n var upLine = [\r\n new Vector3(0, .6, .7),\r\n new Vector3(0, 2, .7)\r\n ];\r\n var upArrow = [\r\n new Vector3(-.2, 1.6, .7),\r\n new Vector3(0, 2, .7),\r\n new Vector3(.2, 1.6, .7)\r\n ];\r\n var upLineGeometry = new BufferGeometry().setFromPoints(upLine);\r\n var upArrowGeometry = new BufferGeometry().setFromPoints(upArrow);\r\n var upLineObj = new Line(upLineGeometry, material);\r\n var upArrowObj = new Line(upArrowGeometry, material);\r\n upArrowGrouping.add(upLineObj, upArrowObj);\r\n\r\n gateCover.add(takeInGrouping, upArrowGrouping, exhaustArrowGrouping);\r\n\r\n }\r\n\r\n private createArrowCurves = (valve: ValveDTO, valveAbove: ValveDTO, gateCover: Mesh, stackClone: Mesh, stackInfo: StackDTO) => {\r\n var material = new LineBasicMaterial({ color: 0x0000ff, linewidth: 14 });\r\n var height = this.getGateTranslationAmount(stackInfo, valve);\r\n var heightAbove = this.getGateTranslationAmount(stackInfo, valveAbove);\r\n var difInHeight = heightAbove - height;\r\n var lineGroup = new Group();\r\n lineGroup.name = valve.id + ' ' + valveAbove.id;\r\n var curve = new CatmullRomCurve3([\r\n new Vector3(.5, height, 0),\r\n new Vector3(1.5, height - .05, 0),\r\n new Vector3(3.5, height, 0),\r\n new Vector3(2.5, heightAbove - difInHeight / 2, 0),\r\n new Vector3(2, heightAbove - difInHeight * (1 / 4), 0),\r\n new Vector3(1.5, heightAbove - difInHeight * (1 / 8), 0)\r\n ]);\r\n var curve2 = new CatmullRomCurve3([\r\n new Vector3(-.5, height, 0),\r\n new Vector3(-1.5, height - .05, 0),\r\n new Vector3(-3.5, height, 0),\r\n new Vector3(-2.5, heightAbove - difInHeight / 2, 0),\r\n new Vector3(-2, heightAbove - difInHeight * (1 / 4), 0),\r\n new Vector3(-1.5, heightAbove - difInHeight * (1 / 8), 0)\r\n ]);\r\n\r\n var points = curve.getPoints(50);\r\n var points2 = curve2.getPoints(50);\r\n\r\n var arrow = [\r\n new Vector3(2.5, heightAbove - difInHeight * (1 / 6), 0),\r\n new Vector3(1.5, heightAbove - difInHeight * (1 / 8), 0),\r\n new Vector3(1.3, heightAbove - difInHeight * (1 / 5), 0)\r\n ];\r\n\r\n var arrow2 = [\r\n new Vector3(-2.5, heightAbove - difInHeight * (1 / 6), 0),\r\n new Vector3(-1.5, heightAbove - difInHeight * (1 / 8), 0),\r\n new Vector3(-1.3, heightAbove - difInHeight * (1 / 5), 0)\r\n ];\r\n var geometry = new BufferGeometry().setFromPoints(points);\r\n var spline = new Line(geometry, material);\r\n var arrowGeometry = new BufferGeometry().setFromPoints(arrow);\r\n var lineArrow = new Line(arrowGeometry, material);\r\n\r\n var geometry2 = new BufferGeometry().setFromPoints(points2);\r\n var spline2 = new Line(geometry2, material);\r\n var arrowGeometry2 = new BufferGeometry().setFromPoints(arrow2);\r\n var lineArrow2 = new Line(arrowGeometry2, material);\r\n lineGroup.add(spline, lineArrow, spline2, lineArrow2);\r\n lineGroup.visible = false;\r\n stackClone.add(lineGroup);\r\n this.arrowCurveGroupArr.push(lineGroup);\r\n\r\n }\r\n\r\n private setGateSpoolVisuals = (gateCover: Object3D, isGateOpen: boolean, isSpoolOpen: boolean) => {\r\n var takeInArrow = gateCover.getObjectByName('take-in-arrows');\r\n var upArrow = gateCover.getObjectByName('up-arrows');\r\n var exhaustArrow = gateCover.getObjectByName('exhaust-arrows');\r\n var gateOpen = gateCover.getObjectByName('gate-open');\r\n var gateClosed = gateCover.getObjectByName('gate-closed');\r\n\r\n if (isGateOpen) {\r\n if (isSpoolOpen) {\r\n takeInArrow!.visible = true;\r\n upArrow!.visible = true;\r\n exhaustArrow!.visible = false;\r\n gateOpen!.visible = true;\r\n gateClosed!.visible = false;\r\n } else {\r\n takeInArrow!.visible = false;\r\n upArrow!.visible = true;\r\n exhaustArrow!.visible = false;\r\n gateOpen!.visible = true;\r\n gateClosed!.visible = false;\r\n }\r\n } else {\r\n if (isSpoolOpen) {\r\n takeInArrow!.visible = false;\r\n upArrow!.visible = false;\r\n exhaustArrow!.visible = true;\r\n gateOpen!.visible = false;\r\n gateClosed!.visible = true;\r\n } else {\r\n takeInArrow!.visible = false;\r\n upArrow!.visible = false;\r\n exhaustArrow!.visible = false;\r\n gateOpen!.visible = false;\r\n gateClosed!.visible = true;\r\n }\r\n }\r\n\r\n }\r\n\r\n private renderScene = () => {\r\n if (this.renderer) {\r\n this.renderer.render(this.scene, this.camera);\r\n }\r\n }\r\n\r\n private onContainerResize = () => {\r\n let container = this.renderer.domElement.parentElement;\r\n let box = container!.getBoundingClientRect();\r\n this.renderer.setSize(box.width, box.height);\r\n\r\n this.camera.aspect = box.width / box.height;\r\n this.camera.updateProjectionMatrix();\r\n\r\n }\r\n\r\n private animate = () => {\r\n\r\n requestAnimationFrame(this.animate);\r\n // required if controls.enableDamping or controls.autoRotate are set to true\r\n var cameraPos = this.camera.position;\r\n this.controls.update();\r\n\r\n for (let gate of this.gateCoverArr) {\r\n var gateClosed = gate.getObjectByName('gate-closed');\r\n let gatePos = new Vector3();\r\n gate.getWorldPosition(gatePos);\r\n gateClosed!.lookAt(new Vector3(cameraPos.x, gatePos.y, cameraPos.z));\r\n gateClosed!.rotateY(Math.PI / 2);\r\n\r\n }\r\n\r\n for (let stack of this.stackArr) {\r\n let stackPos = new Vector3();\r\n stack.getWorldPosition(stackPos);\r\n stack.lookAt(new Vector3(cameraPos.x, stackPos.y, cameraPos.z));\r\n }\r\n\r\n let worldPos = new Vector3();\r\n this.components.clearBin.getWorldPosition(worldPos);\r\n this.components.clearBin.lookAt(new Vector3(cameraPos.x, worldPos.y, cameraPos.z));\r\n this.components.alwaysVisibleClearBin.getWorldPosition(worldPos);\r\n this.components.alwaysVisibleClearBin.lookAt(new Vector3(cameraPos.x, worldPos.y, cameraPos.z));\r\n this.renderer.render(this.scene, this.camera);\r\n }\r\n}\r\n\r\n//export const BinVisualThree = withIdleTimer(BinVisualThreeUnwrapped);\r\nexport const BinVisualThree = BinVisualThreeUnwrapped;","import * as React from 'react';\r\nimport BinDTO from 'src/models/BinDTO';\r\n//import LayerDTO from 'src/models/LayerDTO';\r\nimport { LayerMCInfo, transformLayers } from '../BinStatusPage/BinVisualThree';\r\nimport { formatNumber } from '../BinStatusPage/HeaterControls';\r\n\r\ninterface CanvasProps {\r\n width: number;\r\n height: number;\r\n binData: BinDTO;\r\n}\r\ninterface Point2D {\r\n x: number;\r\n y: number;\r\n}\r\n\r\nexport const hasDyanmicLayers = (binData: BinDTO): boolean => {\r\n // const _hasDynamicLayers: boolean = (binData?.dynamicLayers?.length ?? 0) > 0;\r\n\r\n // disable showing dynamic layers until dynamicMC fixed\r\n const _hasDynamicLayers: boolean = false;\r\n return _hasDynamicLayers;\r\n}\r\n\r\nconst createLayers = (binData: BinDTO, ctx: CanvasRenderingContext2D, width: number, height: number) => {\r\n let layers: LayerMCInfo[] | null = [];\r\n if (binData?.layerGrainStates) {\r\n layers = transformLayers(binData);\r\n }\r\n else if (hasDyanmicLayers(binData)) {\r\n layers = [];\r\n // not supported\r\n //layers = binData?.dynamicLayers\r\n }\r\n else {\r\n // not supported\r\n //layers = binData?.layers;\r\n layers = [];\r\n }\r\n\r\n // let binHeight = binData.eaveHeight;\r\n let layerCount = layers?.length != null ? layers.length : 0;\r\n (layers || []).forEach((layer) => {\r\n let topPercent = (1 / layerCount) * layer.number;\r\n let bottomPercent = (1 / layerCount) * (layer.number - 1);\r\n let fillIn = true;\r\n\r\n // alex: skip using provided layer top and bottom for 2D visualization for now. Will fallback to equally spaced layers\r\n // Todo: Need to diagnose why layer.top and bottom are sometimes overlapping other layers when refreshing from device instead of Azure snapshot\r\n // if (layer.top !== null && layer.top!.heightFromFloor <= binHeight && layer.bottom!.heightFromFloor <= binHeight) {\r\n // topPercent = layer.top!.heightFromFloor >= binHeight ? 1 : layer.top!.heightFromFloor / binHeight;\r\n // bottomPercent = layer.bottom!.heightFromFloor !== 0 ? layer.bottom!.heightFromFloor / binHeight : 0;\r\n // }\r\n\r\n // let topPercent = tempArr[i].top >= binHeight ? 1 : tempArr[i].top / binHeight;\r\n // let bottomPercent = tempArr[i].bottom !== 0 ? tempArr[i].bottom / binHeight : 0;\r\n if (topPercent != null && bottomPercent != null) {\r\n let x = width * .1;\r\n let y = (((1 - (topPercent - .01)) * .6) + .3) * height;\r\n ctx.strokeStyle = '#C7C7C7';\r\n ctx.fillStyle = getFillColor(binData, layer, false);\r\n ctx.beginPath();\r\n ctx.moveTo(x, y);\r\n ctx.bezierCurveTo(x + (width * .05), y + (height * .05), width * .85, y + (height * 0.05), width * .9, y);\r\n x = width * .9;\r\n y = (((1 - (bottomPercent + .005)) * .6) + .3) * height;\r\n ctx.lineTo(x, y);\r\n ctx.bezierCurveTo(x - (width * .05), y + (height * .05), width * .15, y + (height * 0.05), width * .1, y);\r\n ctx.closePath();\r\n if (fillIn) {\r\n ctx.fill();\r\n }\r\n ctx.stroke();\r\n\r\n // add shadow to layer\r\n if (fillIn) {\r\n\r\n let t = .65;\r\n let p1 = { x: width * .1, y: (((1 - (topPercent - .01)) * .6) + .3) * height };\r\n let p2 = { x: p1.x + (width * .05), y: p1.y + (height * .05) };\r\n let p3 = { x: width * .85, y: p1.y + (height * 0.05) };\r\n let p4 = { x: width * .9, y: p1.y };\r\n let top = getCubicBezierAtPoint(p1, p2, p3, p4, t);\r\n ctx.fillStyle = getFillColor(binData, layer, true);\r\n ctx.strokeStyle = getFillColor(binData, layer, true);\r\n ctx.beginPath();\r\n ctx.moveTo(top.x, top.y + 1);\r\n ctx.bezierCurveTo(top.x, top.y, p3.x, p3.y - 3, p4.x, p4.y + 1);\r\n p1 = { x: width * .9, y: (((1 - (bottomPercent + .005)) * .6) + .3) * height };\r\n p2 = { x: p1.x - (width * .05), y: p1.y + (height * .05) };\r\n p3 = { x: width * .15, y: p1.y + (height * 0.05) };\r\n p4 = { x: width * .1, y: p1.y };\r\n ctx.lineTo(p1.x - 1, p1.y - 1);\r\n t = .35;\r\n let bottom1 = getCubicBezierAtPoint(p1, p2, p3, p4, t);\r\n let bottom2 = getCubicBezierAtPoint(p1, p2, p3, p4, .2);\r\n ctx.bezierCurveTo(p2.x - 5, p2.y - 9, bottom2.x, bottom2.y - 2, bottom1.x, bottom1.y - 1);\r\n ctx.closePath();\r\n ctx.fill();\r\n ctx.stroke();\r\n }\r\n\r\n // add loyer labels\r\n x = width * .5;\r\n y = (((1 - (((topPercent - (topPercent - bottomPercent) / 2)) - .01)) * .6) + .346) * height;\r\n ctx.font = '10px Helvetica';\r\n ctx.fillStyle = '#FFFFFF';\r\n ctx.textAlign = 'center';\r\n // if (layer.mc === 0) {\r\n // ctx.fillText(`L${layer.number}`, x, y);\r\n // } else {\r\n if (binData?.useRHForFanControl === false) { \r\n ctx.fillText(`L${layer.number}: ${formatNumber(layer?.mc, {decimalPlaces: 1, filler: \"\", suffix: \"%\"})}`, x, y);\r\n } else {\r\n ctx.fillText(`L${layer.number}: ${formatNumber(layer?.moistureCableTemperatureF, {decimalPlaces: 0, filler: \"\", suffix: \"°\"})} ${formatNumber(layer.rh, {decimalPlaces: 0, suffix: \"%RH\", filler: \"\"})}`, x, y);\r\n \r\n }\r\n // }\r\n }\r\n });\r\n\r\n};\r\nconst getFillColor = (binData: BinDTO, layer: LayerMCInfo, highlight: boolean) => {\r\n const YELLOW_COLOR = '#D1BE14';\r\n const LIGHT_BLUE = '#15CAD1';\r\n const DARK_BLUE = '#1595D1';\r\n const EMPTY_COLOR = \"#c7c7c7\";\r\n if (!layer.hasGrain) {\r\n return EMPTY_COLOR;\r\n }\r\n var targetMC = binData.currentBatch?.targetMC ? binData.currentBatch?.targetMC : 15.5;\r\n var mc = layer.mc;\r\n if ( mc === 0 ) {\r\n if (highlight) {\r\n return '#aaaaaa';\r\n }\r\n return '#c7c7c7';\r\n }\r\n\r\n if (mc == null) {\r\n return YELLOW_COLOR;\r\n }\r\n\r\n if(mc < 16){\r\n return YELLOW_COLOR\r\n }\r\n if(mc >= 16 && mc <= 20){\r\n return LIGHT_BLUE;\r\n }\r\n if(mc >= 20){\r\n return DARK_BLUE;\r\n }\r\n\r\n if (targetMC) {\r\n var interval = (mc - targetMC >= 0 ? mc - targetMC : 0) / (20 - targetMC);\r\n var piecewiseStart = 38 * (interval > 1 ? 1 : interval);\r\n var colorNum = 54 + (piecewiseStart <= 21 ? piecewiseStart : (107 + piecewiseStart));\r\n \r\n // var colorNum = 54 + (178 * (interval > 1 ? 1 : interval));\r\n if (mc <= targetMC) {\r\n colorNum = 54;\r\n }\r\n if (highlight) {\r\n return `hsl(${colorNum}, 80%, 40%)`;\r\n }\r\n return `hsl(${colorNum}, 82%, 45%)`;\r\n }\r\n if (highlight) {\r\n return '#12846e';\r\n }\r\n return '#138D75';\r\n};\r\nconst getCubicBezierAtPoint = (p1: Point2D, p2: Point2D, p3: Point2D, p4: Point2D, t: number) => {\r\n let retPoint = { x: 0, y: 0 };\r\n\r\n retPoint.x = (Math.pow((1 - t), 3) * p1.x) +\r\n (3 * Math.pow(1 - t, 2) * t * p2.x) +\r\n (3 * (1 - t) * Math.pow(t, 2) * p3.x) +\r\n (Math.pow(t, 3) * p4.x);\r\n\r\n retPoint.y = (Math.pow((1 - t), 3) * p1.y) +\r\n (3 * Math.pow(1 - t, 2) * t * p2.y) +\r\n (3 * (1 - t) * Math.pow(t, 2) * p3.y) +\r\n (Math.pow(t, 3) * p4.y);\r\n\r\n return retPoint;\r\n};\r\nconst Canvas = ({ width, height, binData }: CanvasProps) => {\r\n const canvasRef = React.useRef(null);\r\n\r\n React.useEffect(() => {\r\n if (canvasRef.current) {\r\n const canvas = canvasRef.current;\r\n const ctx = canvas.getContext('2d');\r\n if (ctx) {\r\n\r\n let x = width * .1, y = height * .3;\r\n // create the body of the bin\r\n ctx.fillStyle = '#AEAEAE';\r\n ctx.strokeStyle = '#C7C7C7';\r\n ctx.lineJoin = 'bevel';\r\n ctx.beginPath();\r\n ctx.moveTo(x, y);\r\n ctx.bezierCurveTo(x + (width * .05), y + (height * .05), width * .85, y + (height * 0.05), width * .9, y);\r\n x = width * .9;\r\n y = height * .9;\r\n ctx.lineTo(x, y);\r\n ctx.bezierCurveTo(x - (width * .05), y + (height * .05), width * .15, y + (height * 0.05), width * .1, y);\r\n ctx.closePath();\r\n ctx.fill();\r\n ctx.stroke();\r\n\r\n // create the eave top\r\n ctx.fillStyle = '#C7C7C7';\r\n ctx.beginPath();\r\n x = width * .1, y = height * .3;\r\n ctx.moveTo(x, y);\r\n ctx.bezierCurveTo(x + (width * .05), y + (height * .05), width * .85, y + (height * 0.05), width * .9, y);\r\n x = width * ((2 * 4 / 15) + .05), y = height * .15;\r\n ctx.lineTo(x, y);\r\n ctx.bezierCurveTo(x - (width * .01), y + (width * .01), width * ((4 / 15) + .16), y + (width * .01), width * ((4 / 15) + .15), y);\r\n ctx.closePath();\r\n ctx.fill();\r\n ctx.stroke();\r\n\r\n // create very tip\r\n ctx.fillStyle = '#D7D7D7';\r\n ctx.beginPath();\r\n ctx.moveTo(x, y);\r\n ctx.bezierCurveTo(\r\n x - (width * .01),\r\n y - (height * .01),\r\n width * ((4 / 15) + .16),\r\n y - (width * .01),\r\n width * ((4 / 15) + .15),\r\n y);\r\n\r\n x = width * ((4 / 15) + .15);\r\n ctx.bezierCurveTo(\r\n x + .01,\r\n y + (width * .01),\r\n width * ((2 * 4 / 15) + .04),\r\n y + (width * .01),\r\n x = width * ((2 * 4 / 15) + .05),\r\n y\r\n );\r\n ctx.fill();\r\n ctx.stroke();\r\n\r\n // add shadow detail\r\n let t = .3;\r\n let p1 = { x: width * ((2 * 4 / 15) + .05), y: height * .15 };\r\n let p2 = { x: p1.x - (width * .01), y: p1.y + (width * .01) };\r\n let p3 = { x: width * ((4 / 15) + .16), y: p1.y + (width * .01) };\r\n let p4 = { x: width * ((4 / 15) + .15), y: p1.y };\r\n let topPoint = getCubicBezierAtPoint(p1, p2, p3, p4, t);\r\n\r\n p1 = { x: width * .1, y: height * .3 };\r\n p2 = { x: p1.x + (width * .05), y: p1.y + (height * .05) };\r\n p3 = { x: width * .85, y: p1.y + (height * 0.05) };\r\n p4 = { x: width * .9, y: p1.y };\r\n t = .65;\r\n let bottomPoint = getCubicBezierAtPoint(p1, p2, p3, p4, t);\r\n\r\n ctx.strokeStyle = '#AAAAAA';\r\n ctx.fillStyle = '#AAAAAA';\r\n ctx.beginPath();\r\n ctx.moveTo(topPoint.x, topPoint.y);\r\n ctx.lineTo(bottomPoint.x, bottomPoint.y);\r\n ctx.bezierCurveTo(bottomPoint.x, bottomPoint.y, p3.x, p3.y, p4.x, p4.y);\r\n x = width * ((2 * 4 / 15) + .05), y = height * .15;\r\n ctx.lineTo(x, y);\r\n // ctx.moveTo(topPoint.x, topPoint.y);\r\n ctx.bezierCurveTo(\r\n width * ((2 * 4 / 15) + .05) - (width * .01),\r\n height * .15 + (height * .01),\r\n topPoint.x,\r\n topPoint.y,\r\n topPoint.x,\r\n topPoint.y);\r\n ctx.fill();\r\n ctx.stroke();\r\n\r\n // // bin shadow\r\n // ctx.beginPath();\r\n // ctx.moveTo(bottomPoint.x + 1, bottomPoint.y + 1);\r\n // ctx.bezierCurveTo(bottomPoint.x + 1, bottomPoint.y + 1, p3.x, p3.y, p4.x, p4.y);\r\n // x = width * .9;\r\n // y = height * .9;\r\n // ctx.lineTo(x, y);\r\n // p1 = {x: x, y: y};\r\n // p2 = { x: (x - (width * .05)), y: y + (height * .05) };\r\n // p3 = { x: width * .15, y: y + (height * .05) };\r\n // p4 = { x: width * .1, y: y };\r\n // t = .35;\r\n // let binBottomPoint = getCubicBezierAtPoint(p1, p2, p3, p4, t);\r\n // ctx.bezierCurveTo(p2.x, p2.y, binBottomPoint.x, binBottomPoint.y, binBottomPoint.x, binBottomPoint.y);\r\n // ctx.closePath();\r\n // ctx.fill();\r\n // ctx.stroke();\r\n\r\n createLayers(binData, ctx, width, height);\r\n\r\n }\r\n }\r\n });\r\n\r\n return ;\r\n};\r\n\r\nCanvas.defaultProps = {\r\n width: parent.innerWidth,\r\n height: parent.innerHeight\r\n};\r\n\r\nexport default Canvas;\r\n","export const DATETIME_FORMAT = \"MM/DD/YYYY h:mm a\";","import { CheckCircleFilled, CloseCircleFilled, RedoOutlined, WarningFilled } from \"@ant-design/icons\";\r\nimport { useQuery } from \"@tanstack/react-query\";\r\nimport { Card, Descriptions, DescriptionsProps, Divider, Flex, Grid, Row, Select, Space, Tag, Typography } from \"antd\";\r\nimport dayjs from \"dayjs\";\r\nimport React, { useState } from \"react\";\r\nimport { useWindowSize } from \"rooks\";\r\nimport ShivversService from \"src/api/ShivversService\";\r\nimport { binDBKeys } from \"src/pages/binOverview/BinCommander\";\r\nimport { formatNumber } from \"src/pages/dashboard/BinStatusPage/HeaterControls\";\r\nimport { useBinInfoContext } from \"src/queries/BinInfoContext\";\r\nimport { DATETIME_FORMAT } from \"./shared\";\r\n\r\nexport const CheckPassedIcon = () => {\r\n return \r\n}\r\n\r\nexport const CheckFailedIcon = () => {\r\n return \r\n}\r\n\r\nexport const HourSelector = (props: any) => {\r\n return <>\r\n \r\n \r\n \r\n\r\n {/* Shut Off Section */}\r\n \r\n Shut off with LGSO?\r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n Low Grain Shut Off\r\n \r\n\r\n \r\n \r\n \r\n\r\n\r\n \r\n \r\n {/* Time Inputs */}\r\n \r\n Time to Run\r\n \r\n\r\n \r\n \r\n Hours\r\n \r\n \r\n Minutes\r\n \r\n \r\n Seconds\r\n \r\n\r\n \r\n \r\n \r\n ))}\r\n\r\n {/* Machine Augur Status */}\r\n \r\n Machine Augur Status\r\n \r\n {[\"Auto\", \"OFF\", \"ON\", \"OFF\", \"OFF\", \"OFF\"].map((label, index) => (\r\n \r\n \r\n \r\n ))}\r\n\r\n {/* Start/Stop Buttons */}\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default MoveGrainControl;\r\n","import { notification } from \"antd\";\r\nimport React, { useCallback, useEffect, useState } from \"react\";\r\nimport { useHistory, useLocation } from \"react-router\";\r\nimport { usePermissions } from \"src/pages/features/usePermissions\";\r\nimport dayjs, { Dayjs } from \"dayjs\";\r\nimport FanControl from \"./FanControl\";\r\nimport MoveGrainControl from \"./MoveGrainControl\";\r\nimport { HubConnectionBuilder, HubConnectionState, HubConnection, LogLevel } from \"@microsoft/signalr\";\r\nimport { useGetPremierPlusBinDto } from \"src/queries/useGetPremierPlusBinDto\";\r\nimport { useQueryClient } from \"@tanstack/react-query\";\r\n\r\nconst ControlPanel = () => {\r\n\r\n const queryClient = useQueryClient();\r\n const history = useHistory();\r\n const location = useLocation();\r\n const permissions = usePermissions();\r\n const binInfo = {\r\n name: \"Test_PremierPlus_Physical_1\", \r\n id:1107\r\n };\r\n const lastTapestripTime = dayjs(\"2024-12-09T15:30:00\");\r\n\r\n const [activeTabKey, setActiveTabKey] = useState(\"1\");\r\n const [showAlertModal, setShowAlertModal] = useState(false);\r\n const [showTapestripLog, setShowTapestripLog] = useState(false);\r\n\r\n const [tapestripHistoryModalRange, setTapestripHistoryModalRange] = useState<[Dayjs | null, Dayjs | null]>([null, null]);\r\n const { getPremierPlusBinDtoQueryKey, getPremierPlusBinDtoQuery } = useGetPremierPlusBinDto(binInfo?.id, { enabled: true, refetchOnWindowFocus: false });\r\n\r\n useEffect( () => {\r\n\r\n if(getPremierPlusBinDtoQuery.data == null){\r\n notification.error({ message:'Could not fetch snapshot' });\r\n }else{\r\n notification.success({ message: 'New Message received' });\r\n }\r\n }, [getPremierPlusBinDtoQuery.dataUpdatedAt]);\r\n\r\n const startSignalRConnection = useCallback(async (connection: HubConnection) => {\r\n try {\r\n await connection.start();\r\n console.log(\"signalR connected, \", \"url: \", connection.baseUrl);\r\n } catch (err) {\r\n console.error(\"Error establishing signalr connection: \", err);\r\n setTimeout(() => startSignalRConnection(connection), 5000);\r\n }\r\n }, []);\r\n \r\n useEffect(() => {\r\n const binId = binInfo?.id;\r\n if (binId == null) {\r\n return;\r\n }\r\n let connection: HubConnection;\r\n let disconnectedDueToUnmount = false;\r\n \r\n const setUpSignalRConnection = async (binId: number) => {\r\n\r\n const connection = new HubConnectionBuilder()\r\n .withUrl(\"/signalr\")\r\n .withAutomaticReconnect()\r\n .configureLogging(LogLevel.Information)\r\n .build();\r\n\r\n connection.on(\"NewSnapshot\", async (message: any) => {\r\n queryClient.invalidateQueries({ queryKey:[getPremierPlusBinDtoQueryKey] });\r\n console.log('Requesting new snapshot');\r\n });\r\n\r\n // start the connection\r\n await startSignalRConnection(connection);\r\n // subscribe to bin\r\n\r\n if (connection.state === HubConnectionState.Connected) {\r\n try {\r\n await connection.invoke(\"SubscribeBin\", binId);\r\n console.log(\"signalr: subscribed to binId: \", binInfo?.id);\r\n } catch (err) {\r\n console.error(`signalr: Error subscribing to bin with Id ${binId}`, err.toString());\r\n }\r\n } else {\r\n console.log(\"hub not connected\");\r\n }\r\n \r\n // handle disconnect\r\n connection.onclose(async error => {\r\n console.log(\"disconnected from SignalR\", error);\r\n if (disconnectedDueToUnmount) {\r\n console.debug(\"skipping reconnect due to component cleanup/umount\");\r\n return;\r\n }\r\n await startSignalRConnection(connection);\r\n });\r\n \r\n //return the connection\r\n return connection;\r\n };\r\n \r\n if (binId) {\r\n setUpSignalRConnection(binId).then(con => {\r\n connection = con;\r\n });\r\n }\r\n \r\n const cleanUpSignalRConnection = async (binId: number, connection: HubConnection) => {\r\n if (connection.state === HubConnectionState.Connected) {\r\n connection;\r\n try {\r\n await connection.invoke(\"UnsubscribeBin\", binId);\r\n console.log(\"unsubscribing from bin\", binId);\r\n } catch (err) {\r\n return console.error(\"Error unsubscribing to bin\", binId, err.toString());\r\n }\r\n connection.off(\"SubscribeBin\");\r\n disconnectedDueToUnmount = true;\r\n connection.stop();\r\n } else {\r\n connection.off(\"SubscribeBin\");\r\n disconnectedDueToUnmount = true;\r\n connection.stop();\r\n }\r\n };\r\n \r\n return function cleanUp() {\r\n if (binInfo?.id) {\r\n const binId = binInfo?.id;\r\n cleanUpSignalRConnection(binId, connection).catch(err => console.log(\"failed to cleanup signalr\", err));\r\n }\r\n };\r\n }, [binInfo?.id]);\r\n\r\n return (\r\n <>\r\n {/* \r\n \r\n {\r\n // key unique to the location, only present if we came to this page from another first using React Router. ex. clicking details in BinDetails.\r\n // https://reactrouter.com/en/main/start/concepts#locations\r\n const hasHistory = !(location?.key == null);\r\n if (!hasHistory) {\r\n // going to the homepage is the best we can do currently (no knowledge of prior history or upper route/component to go back to)\r\n history.push(Routes.HOME_ROUTE);\r\n } else {\r\n history.goBack();\r\n }\r\n }}\r\n title={binInfo.name}\r\n tags={[]}\r\n />\r\n \r\n \r\n {`Last Tape Reading: ${\r\n lastTapestripTime != null ? lastTapestripTime?.format(\"MM/DD/YYYY h:mm a\") : \"----\"\r\n }`}\r\n \r\n \r\n \r\n {\r\n setShowAlertModal(true);\r\n }}\r\n >\r\n Alerts\r\n \r\n \r\n }\r\n style={{\r\n width: \"100%\"\r\n }}\r\n styles={{\r\n body: {\r\n padding: 0\r\n }\r\n }}\r\n >\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n */}\r\n\r\n {/* {permissions.canWrite && \r\n \r\n \r\n \r\n } */}\r\n \r\n
\r\n \r\n \r\n\r\n
\r\n \r\n \r\n\r\n
\r\n\r\n {/* \r\n \r\n \r\n \r\n */}\r\n \r\n );\r\n};\r\n\r\nexport default ControlPanel;","import { Layout, Tabs, TabsProps } from \"antd\";\r\nimport React, { useState } from \"react\";\r\nimport ControlPanel from \"./ControlPanel\";\r\n\r\nconst PremierPlusDashboard = () => {\r\n\r\n const [activeTabKey, setActiveTabKey] = useState(\"1\");\r\n const handleTabChange = (key: string) => {\r\n setActiveTabKey(key);\r\n };\r\n\r\n const items: TabsProps['items'] = [\r\n {\r\n key: '1',\r\n label: 'Dashboard',\r\n children: \r\n }\r\n ];\r\n \r\n return (\r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default PremierPlusDashboard;\r\n","import { Button, message, Row, Skeleton } from \"antd\";\r\nimport { StatusCodes } from \"http-status-codes/build/cjs/status-codes\";\r\nimport React, { useEffect } from \"react\";\r\nimport { Redirect, useHistory, useParams } from \"react-router\";\r\nimport { Link } from \"react-router-dom\";\r\nimport { useGetBinByIDQuery } from \"src/queries/useGetBinByIDQuery\";\r\nimport { deviceQueryKeys } from \"../../../components/BinDetails\";\r\nimport BinStatsPage, { HistoryState } from \"./BinStatsPage\";\r\nimport { queryClient } from \"src\";\r\nimport { connect } from \"react-redux\";\r\nimport { GrowerSlim, UserSessionActions } from \"src/redux/actions/UserSessionActions\";\r\nimport { BinInfoContext } from \"src/queries/BinInfoContext\";\r\nimport AutomationType from \"src/consts/AutomationType\";\r\nimport PremierPlusDashboard from \"src/pages/features/PremierPlus/PremierPlusDashboard\";\r\n\r\ninterface Props {\r\n growerChangeAction: (grower: GrowerSlim) => void;\r\n}\r\n\r\n\r\nexport const invalidateBinState = (deviceId: string): NodeJS.Timeout => {\r\n const timeoutId = setTimeout(async () => {\r\n console.log(\"invaliding binState at\", Date());\r\n await queryClient.invalidateQueries(deviceQueryKeys.stateFromDevice(deviceId));\r\n message.info('Data Refreshing...', 2);\r\n }, 8000);\r\n return timeoutId;\r\n};\r\n\r\n\r\n\r\nexport const strictParseInt = (maybeInt: string | null | undefined, radix: number): number => {\r\n if (maybeInt == null) {\r\n return NaN;\r\n }\r\n const initial = parseInt(maybeInt, radix);\r\n if (initial.toString() !== maybeInt) {\r\n return NaN;\r\n }\r\n return initial;\r\n}\r\n\r\nconst BinStatsPageParent = (props: Props) => {\r\n const params = useParams();\r\n const history = useHistory();\r\n const binIdParam = params.binId?.replaceAll(\"-\", \".\");\r\n console.log(\"params\", params);\r\n console.log(\"translated param\", binIdParam);\r\n\r\n const binId = strictParseInt(binIdParam, 10);\r\n // Nan, Ids < 0, non-numeric, all evaluate to false\r\n const validbinIdInUrl = binId >= 0;\r\n console.log(\"parsed binId param\", {binId, valid: validbinIdInUrl});\r\n\r\n\r\n if (!validbinIdInUrl) {\r\n return <>\r\n \r\n

{binIdParam} is not a valid bin identifer

\r\n
\r\n \r\n \r\n \r\n \r\n\r\n }\r\n\r\n let binInfoQuery = useGetBinByIDQuery(binId, {enabled: validbinIdInUrl});\r\n\r\n useEffect(() => {\r\n const growerId = binInfoQuery.data?.growerId;\r\n if (!(growerId)) {\r\n return;\r\n }\r\n const bininfo = binInfoQuery.data;\r\n const grower: GrowerSlim = {\r\n externalId: null,\r\n growerId: bininfo?.growerId!,\r\n growerName: bininfo?.growerName!,\r\n isExternal: false,\r\n };\r\n props.growerChangeAction(grower);\r\n }, [binInfoQuery.data?.growerId]);\r\n\r\n const unauthorizedBin = binInfoQuery.error?.status === StatusCodes.FORBIDDEN;\r\n const binNotFound = binInfoQuery.error?.status === StatusCodes.NOT_FOUND;\r\n const notFoundOrUnauthorized = unauthorizedBin || binNotFound;\r\n\r\n if (notFoundOrUnauthorized) {\r\n console.log(\"Redirecting to homepage due to unauthorized or notfound bin\");\r\n return ;\r\n }\r\n\r\n\r\n if (binInfoQuery.data) {\r\n return <>\r\n \r\n {binInfoQuery.data.automationType === AutomationType.PremierPlus && }\r\n {binInfoQuery.data.automationType !== AutomationType.PremierPlus && }\r\n \r\n ;\r\n }\r\n\r\n if (binInfoQuery.isLoading) {\r\n return <>\r\n \r\n \r\n }\r\n\r\n if (binInfoQuery.isError) {\r\n return <>\r\n

An unexpected server error occurred loading the bin

\r\n Back to Dashboard\r\n

Contact Support if this perists.

\r\n ;\r\n }\r\n\r\n return <>\r\n\r\n}\r\n\r\n\r\n\r\n\r\nfunction mapDispatchToProps(dispatch: any) {\r\n return {\r\n growerChangeAction: (grower: GrowerSlim) => dispatch(UserSessionActions.changeGrower(grower)),\r\n };\r\n}\r\n\r\nexport default connect(null, mapDispatchToProps)(BinStatsPageParent);\r\n","import { Col, Layout, notification, Select, Typography } from \"antd\";\r\nimport React, { useEffect, useState } from \"react\";\r\nimport { strictParseInt } from \"../BinStatusPage/BinStatsPageParent\";\r\nimport { useParams } from \"react-router\";\r\nimport { useGetBinByIDQuery } from \"src/queries/useGetBinByIDQuery\";\r\nimport RoleUtil from \"src/utils/RoleUtil\";\r\nimport Role from \"src/consts/Role\";\r\nimport { GrowerSlim, UserSessionActions } from \"src/redux/actions/UserSessionActions\";\r\nimport EnterpriseApiService from \"src/api/EnterpriseApiService\";\r\nimport { connect, useStore } from \"react-redux\";\r\nimport CurrentUser from \"src/utils/CurrentUser\";\r\nimport { StateStoreModel } from \"src/redux/state/StateStoreModel\";\r\nimport HistoryUtil from \"src/utils/HistoryUtil\";\r\nimport Routes from \"src/consts/Routes\"\r\n\r\ninterface GrowerSelectionProps {\r\n growerName: string | null;\r\n growerID: number | null;\r\n grower: GrowerSlim | null;\r\n growerChangeAction: (grower: GrowerSlim) => any;\r\n logoutAction: () => any;\r\n\r\n}\r\nconst GrowerSelection = (props: GrowerSelectionProps) => {\r\n\r\n const params = useParams();\r\n const binIdParam = params.binId;\r\n\r\n const binId = strictParseInt(binIdParam, 10);\r\n // Nan, Ids < 0, non-numeric, all evaluate to false\r\n const validbinIdInUrl = binId >= 0;\r\n\r\n const binInfoQuery = useGetBinByIDQuery(binId, { enabled: validbinIdInUrl, refetchOnReconnect: false, refetchOnWindowFocus: false });\r\n\r\n const [user, setUser] = useState(CurrentUser.Get());\r\n\r\n const [growerIdList, setGrowerIdList] = useState>([]);\r\n\r\n const currentGrower = growerIdList?.find(grower => {\r\n return grower.growerName === props.growerName;\r\n });\r\n\r\n const currentGrowerName = currentGrower?.growerName;\r\n\r\n\r\n useEffect(() => {\r\n if (validbinIdInUrl && binInfoQuery.isInitialLoading) {\r\n return;\r\n }\r\n getData();\r\n }, [user, binInfoQuery.isInitialLoading, validbinIdInUrl]);\r\n\r\n const getData = () => {\r\n if (user?.userId) {\r\n if (RoleUtil.CanViewBinFleet()) {\r\n EnterpriseApiService.getGrowerIDs(true, true).then((growerIDs) => {\r\n //let GrowerNameIDPairs: [number, string][] = growerIDs.map((grower) => [grower.growerID, grower.growerName ? grower.growerName : 'error: no name']);\r\n let GrowerNameIDPairs: GrowerSlim[] = growerIDs.map((grower) => {\r\n const growerSlim: GrowerSlim = {\r\n growerId: grower.growerID,\r\n externalId: grower.externalId,\r\n isExternal: grower.isExternal,\r\n growerName: grower.growerName as string,\r\n }\r\n return growerSlim;\r\n })\r\n\r\n const growerNameAlreadySet = !!props.growerName;\r\n if (!growerNameAlreadySet) {\r\n props.growerChangeAction(GrowerNameIDPairs[0]);\r\n }\r\n setGrowerIdList(GrowerNameIDPairs);\r\n }).catch(error => {\r\n notification.error({\r\n message: error.message,\r\n description: error.description\r\n });\r\n });\r\n } else {\r\n EnterpriseApiService.getGrowersOfUserByUsername(user.userName!).then((growerIDs) => {\r\n //let GrowerNameIDPairs: [number, string][] = growerIDs.map((grower) => [grower.growerID, grower.growerName ? grower.growerName : 'error: no name']);\r\n\r\n let GrowerNameIDPairs: GrowerSlim[] = growerIDs.map((grower) => {\r\n const growerSlim: GrowerSlim = {\r\n growerId: grower.growerID,\r\n externalId: grower.externalId,\r\n isExternal: grower.isExternal,\r\n growerName: grower.growerName as string,\r\n }\r\n return growerSlim;\r\n });\r\n const growerNameAlreadySet = !!props.growerName;\r\n if (!growerNameAlreadySet) {\r\n props.growerChangeAction(GrowerNameIDPairs[0]);\r\n }\r\n setGrowerIdList(GrowerNameIDPairs);\r\n }).catch(error => {\r\n notification.error({\r\n message: error.message,\r\n description: error.description\r\n });\r\n });\r\n }\r\n }\r\n }\r\n\r\n return <>\r\n {growerIdList.length > 0 &&\r\n \r\n {growerIdList.length > 1 ?\r\n \r\n : null\r\n // \r\n // {growerIdList[0].growerName}\r\n // \r\n }\r\n \r\n }\r\n ;\r\n\r\n};\r\n\r\nfunction mapDispatchToProps(dispatch: any) {\r\n return {\r\n growerChangeAction: (grower: GrowerSlim) => dispatch(UserSessionActions.changeGrower(grower)),\r\n logoutAction: () => dispatch(UserSessionActions.logout())\r\n };\r\n}\r\n\r\nfunction mapStateToProps(state: StateStoreModel) {\r\n return {\r\n growerID: state.UserSession.GrowerID,\r\n growerName: state.UserSession.GrowerName,\r\n grower: state.UserSession.grower,\r\n };\r\n}\r\n\r\nexport class GrowerSelectOptionParser {\r\n\r\n public growerName: string;\r\n public externalId: string | null;\r\n public growerId: number | null;\r\n constructor(selectValue: string) {\r\n const [name, externalId, growerId] = selectValue.split(\"####\", 3);\r\n this.growerName = name;\r\n this.externalId = externalId;\r\n this.growerId = growerId != null ? Number.parseInt(growerId, 10) : null;\r\n }\r\n\r\n public static ToSelectvalue(growerName: string, externalId: string | null, growerId: number | null): string {\r\n const string = `${growerName}####${externalId}####${growerId}`;\r\n return string;\r\n }\r\n\r\n public static FromGrower(grower: GrowerSlim): GrowerSelectOptionParser {\r\n return GrowerSelectOptionParser.FromParts(grower.growerName, grower.externalId, grower.growerId);\r\n }\r\n\r\n public static FromParts(growerName: string, externalId: string | null, growerId: number | null): GrowerSelectOptionParser {\r\n const string = GrowerSelectOptionParser.ToSelectvalue(growerName, externalId, growerId);\r\n return new GrowerSelectOptionParser(string);\r\n }\r\n public selectValue(): string {\r\n return GrowerSelectOptionParser.ToSelectvalue(this.growerName, this.externalId, this.growerId);\r\n }\r\n}\r\n\r\nexport default connect(mapStateToProps, mapDispatchToProps)(GrowerSelection);\r\n","export type OverlayGeometry =\r\n | google.maps.Marker\r\n | google.maps.Polygon\r\n | google.maps.Polyline\r\n | google.maps.Rectangle\r\n | google.maps.Circle;\r\n\r\nexport interface DrawResult {\r\n type: google.maps.drawing.OverlayType;\r\n overlay: OverlayGeometry;\r\n}\r\n\r\nexport interface Snapshot {\r\n radius?: number;\r\n center?: google.maps.LatLngLiteral;\r\n position?: google.maps.LatLngLiteral;\r\n path?: Array;\r\n bounds?: google.maps.LatLngBoundsLiteral;\r\n}\r\n\r\nexport interface Overlay {\r\n type: google.maps.drawing.OverlayType;\r\n geometry: OverlayGeometry;\r\n snapshot: Snapshot;\r\n}\r\n\r\nexport interface State {\r\n past: Array>;\r\n now: Array;\r\n future: Array>;\r\n}\r\n\r\nexport enum DrawingActionKind {\r\n SET_OVERLAY = 'SET_OVERLAY',\r\n UPDATE_OVERLAYS = 'UPDATE_OVERLAYS',\r\n UNDO = 'UNDO',\r\n REDO = 'REDO',\r\n RESET = 'RESET'\r\n}\r\n\r\nexport interface ActionWithTypeOnly {\r\n type: Exclude;\r\n}\r\n\r\nexport interface SetOverlayAction {\r\n type: DrawingActionKind.SET_OVERLAY;\r\n payload: DrawResult;\r\n}\r\n\r\nexport type Action = ActionWithTypeOnly | SetOverlayAction;\r\n\r\nexport function isCircle(\r\n overlay: OverlayGeometry\r\n): overlay is google.maps.Circle {\r\n return (overlay as google.maps.Circle).getCenter !== undefined;\r\n}\r\n\r\nexport function isMarker(\r\n overlay: OverlayGeometry\r\n): overlay is google.maps.Marker {\r\n return (overlay as google.maps.Marker).getPosition !== undefined;\r\n}\r\n\r\nexport function isPolygon(\r\n overlay: OverlayGeometry\r\n): overlay is google.maps.Polygon {\r\n return (overlay as google.maps.Polygon).getPath !== undefined;\r\n}\r\n\r\nexport function isPolyline(\r\n overlay: OverlayGeometry\r\n): overlay is google.maps.Polyline {\r\n return (overlay as google.maps.Polyline).getPath !== undefined;\r\n}\r\n\r\nexport function isRectangle(\r\n overlay: OverlayGeometry\r\n): overlay is google.maps.Rectangle {\r\n return (overlay as google.maps.Rectangle).getBounds !== undefined;\r\n}\r\n\r\n\r\nexport interface LatLng{\r\n lat: number;\r\n lng: number;\r\n}","import {Dispatch, MutableRefObject, useEffect} from 'react';\r\n\r\nimport {\r\n Action,\r\n DrawResult,\r\n DrawingActionKind,\r\n Overlay,\r\n Snapshot,\r\n State,\r\n isCircle,\r\n isMarker,\r\n isPolygon,\r\n isPolyline,\r\n isRectangle\r\n} from './types';\r\n\r\nexport default function reducer(state: State, action: Action) {\r\n switch (action.type) {\r\n // This action is called whenever anything changes on any overlay.\r\n // We then take a snapshot of the relevant values of each overlay and\r\n // save them as the new \"now\". The old \"now\" is added to the \"past\" stack\r\n case DrawingActionKind.UPDATE_OVERLAYS: {\r\n const overlays = state.now.map((overlay: Overlay) => {\r\n const snapshot: Snapshot = {};\r\n const {geometry} = overlay;\r\n\r\n if (isCircle(geometry)) {\r\n snapshot.center = geometry.getCenter()?.toJSON();\r\n snapshot.radius = geometry.getRadius();\r\n } else if (isMarker(geometry)) {\r\n snapshot.position = geometry.getPosition()?.toJSON();\r\n } else if (isPolygon(geometry) || isPolyline(geometry)) {\r\n snapshot.path = geometry.getPath()?.getArray();\r\n } else if (isRectangle(geometry)) {\r\n snapshot.bounds = geometry.getBounds()?.toJSON();\r\n }\r\n\r\n return {\r\n ...overlay,\r\n snapshot\r\n };\r\n });\r\n\r\n return {\r\n now: [...overlays],\r\n past: [...state.past, state.now],\r\n future: []\r\n };\r\n }\r\n\r\n // This action is called when a new overlay is added to the map.\r\n // We then take a snapshot of the relevant values of the new overlay and\r\n // add it to the \"now\" state. The old \"now\" is added to the \"past\" stack\r\n case DrawingActionKind.SET_OVERLAY: {\r\n const {overlay} = action.payload;\r\n\r\n const snapshot: Snapshot = {};\r\n\r\n if (isCircle(overlay)) {\r\n snapshot.center = overlay.getCenter()?.toJSON();\r\n snapshot.radius = overlay.getRadius();\r\n } else if (isMarker(overlay)) {\r\n snapshot.position = overlay.getPosition()?.toJSON();\r\n \r\n } else if (isPolygon(overlay) || isPolyline(overlay)) {\r\n snapshot.path = overlay.getPath()?.getArray();\r\n } else if (isRectangle(overlay)) {\r\n snapshot.bounds = overlay.getBounds()?.toJSON();\r\n }\r\n\r\n return {\r\n past: [...state.past, state.now],\r\n now: [\r\n ...state.now,\r\n {\r\n type: action.payload.type,\r\n geometry: action.payload.overlay,\r\n snapshot\r\n }\r\n ],\r\n future: []\r\n };\r\n }\r\n\r\n // This action is called when the undo button is clicked.\r\n // Get the top item from the \"past\" stack and set it as the new \"now\".\r\n // Add the old \"now\" to the \"future\" stack to enable redo functionality\r\n case DrawingActionKind.UNDO: {\r\n const last = state.past.slice(-1)[0];\r\n\r\n if (!last) return state;\r\n\r\n return {\r\n past: [...state.past].slice(0, -1),\r\n now: last,\r\n future: state.now ? [...state.future, state.now] : state.future\r\n };\r\n }\r\n\r\n // This action is called when the redo button is clicked.\r\n // Get the top item from the \"future\" stack and set it as the new \"now\".\r\n // Add the old \"now\" to the \"past\" stack to enable undo functionality\r\n case DrawingActionKind.REDO: {\r\n const next = state.future.slice(-1)[0];\r\n\r\n if (!next) return state;\r\n\r\n return {\r\n past: state.now ? [...state.past, state.now] : state.past,\r\n now: next,\r\n future: [...state.future].slice(0, -1)\r\n };\r\n }\r\n\r\n // This action is called when the undo button is clicked.\r\n // Get the top item from the \"past\" stack and set it as the new \"now\".\r\n // Add the old \"now\" to the \"future\" stack to enable redo functionality\r\n case DrawingActionKind.RESET: {\r\n return {\r\n past: [],\r\n now: [],\r\n future: []\r\n };\r\n }\r\n }\r\n}\r\n\r\n// Handle drawing manager events\r\nexport function useDrawingManagerEvents(\r\n drawingManager: google.maps.drawing.DrawingManager | null,\r\n overlaysShouldUpdateRef: MutableRefObject,\r\n dispatch: Dispatch\r\n) {\r\n useEffect(() => {\r\n if (!drawingManager) return;\r\n\r\n const eventListeners: Array = [];\r\n\r\n const addUpdateListener = (eventName: string, drawResult: DrawResult) => {\r\n const updateListener = google.maps.event.addListener(\r\n drawResult.overlay,\r\n eventName,\r\n () => {\r\n if (eventName === 'dragstart') {\r\n overlaysShouldUpdateRef.current = false;\r\n }\r\n\r\n if (eventName === 'dragend') {\r\n overlaysShouldUpdateRef.current = true;\r\n }\r\n\r\n if (overlaysShouldUpdateRef.current) {\r\n dispatch({type: DrawingActionKind.UPDATE_OVERLAYS});\r\n }\r\n }\r\n );\r\n\r\n eventListeners.push(updateListener);\r\n };\r\n\r\n const overlayCompleteListener = google.maps.event.addListener( drawingManager, 'overlaycomplete',\r\n (drawResult: DrawResult) => {\r\n switch (drawResult.type) {\r\n case google.maps.drawing.OverlayType.CIRCLE:\r\n ['center_changed', 'radius_changed'].forEach(eventName =>\r\n addUpdateListener(eventName, drawResult)\r\n );\r\n break;\r\n\r\n case google.maps.drawing.OverlayType.MARKER:\r\n ['dragend'].forEach(eventName =>\r\n addUpdateListener(eventName, drawResult)\r\n );\r\n break;\r\n case google.maps.drawing.OverlayType.POLYGON:\r\n case google.maps.drawing.OverlayType.POLYLINE:\r\n ['mouseup'].forEach(eventName =>\r\n addUpdateListener(eventName, drawResult)\r\n );\r\n break;\r\n case google.maps.drawing.OverlayType.RECTANGLE:\r\n ['bounds_changed', 'dragstart', 'dragend'].forEach(eventName =>\r\n addUpdateListener(eventName, drawResult)\r\n );\r\n\r\n break;\r\n }\r\n\r\n dispatch({type: DrawingActionKind.SET_OVERLAY, payload: drawResult});\r\n }\r\n );\r\n\r\n eventListeners.push(overlayCompleteListener);\r\n\r\n return () => {\r\n eventListeners.forEach(listener =>\r\n google.maps.event.removeListener(listener)\r\n );\r\n };\r\n }, [dispatch, drawingManager, overlaysShouldUpdateRef]);\r\n}\r\n\r\n// Update overlays with the current \"snapshot\" when the \"now\" state changes\r\nexport function useOverlaySnapshots(\r\n map: google.maps.Map | null,\r\n state: State,\r\n overlaysShouldUpdateRef: MutableRefObject\r\n) {\r\n useEffect(() => {\r\n if (!map || !state.now) return;\r\n\r\n for (const overlay of state.now) {\r\n overlaysShouldUpdateRef.current = false;\r\n\r\n overlay.geometry.setMap(map);\r\n\r\n const {radius, center, position, path, bounds} = overlay.snapshot;\r\n\r\n if (isCircle(overlay.geometry)) {\r\n overlay.geometry.setRadius(radius ?? 0);\r\n overlay.geometry.setCenter(center ?? null);\r\n } else if (isMarker(overlay.geometry)) {\r\n overlay.geometry.setPosition(position);\r\n } else if (isPolygon(overlay.geometry) || isPolyline(overlay.geometry)) {\r\n overlay.geometry.setPath(path ?? []);\r\n } else if (isRectangle(overlay.geometry)) {\r\n overlay.geometry.setBounds(bounds ?? null);\r\n }\r\n\r\n overlaysShouldUpdateRef.current = true;\r\n }\r\n\r\n return () => {\r\n for (const overlay of state.now) {\r\n overlay.geometry.setMap(null);\r\n }\r\n };\r\n }, [map, overlaysShouldUpdateRef, state.now]);\r\n}\r\n","import React, {useReducer, useRef} from 'react';\r\nimport {useMap} from '@vis.gl/react-google-maps';\r\n\r\nimport reducer, {\r\n useDrawingManagerEvents,\r\n useOverlaySnapshots\r\n} from './UndoRedo';\r\n\r\nimport { DrawingActionKind } from './types';\r\n\r\ninterface UndoAllControlProps {\r\n map: google.maps.Map | null;\r\n drawingManager: google.maps.drawing.DrawingManager | null;\r\n setSelectedMarkerPosition: () => void;\r\n}\r\n\r\nexport const UndoAllControl : React.FC = ({ map, drawingManager, setSelectedMarkerPosition }) => {\r\n\r\n const [state, dispatch] = useReducer(reducer, {\r\n past: [],\r\n now: [],\r\n future: []\r\n });\r\n\r\n const overlaysShouldUpdateRef = useRef(false); \r\n useDrawingManagerEvents(drawingManager, overlaysShouldUpdateRef, dispatch);\r\n useOverlaySnapshots(map, state, overlaysShouldUpdateRef);\r\n \r\n return(\r\n <>\r\n
\r\n {\r\n dispatch({ type: DrawingActionKind.RESET });\r\n drawingManager?.setOptions({\r\n drawingMode: google.maps.drawing.OverlayType.MARKER,\r\n drawingControlOptions:{\r\n position: google.maps.ControlPosition.TOP_CENTER,\r\n drawingModes: [\r\n google.maps.drawing.OverlayType.MARKER,\r\n ]\r\n }\r\n })\r\n setSelectedMarkerPosition();\r\n }}\r\n disabled={!state.past.length}>\r\n \r\n \r\n \r\n \r\n
\r\n \r\n )\r\n}","import { ControlPosition, MapControl } from \"@vis.gl/react-google-maps\";\r\nimport React from \"react\";\r\nimport { Dispatch, SetStateAction } from \"react\";\r\nimport { useDrawingManager } from \"./useDrawingManager\";\r\nimport { UndoAllControl } from \"./UndoAllControl\";\r\nimport { LatLng } from \"./types\";\r\n\r\n\r\n\r\ninterface MapControlWithDrawingProps{\r\n map: google.maps.Map | null;\r\n setSelectedMarkerPosition: (coords: LatLng | null) => void;\r\n }\r\n\r\n \r\n const MapControlWithDrawing : React.FC = ({map, setSelectedMarkerPosition}) => {\r\n\r\n const addListenerCallback = (newDrawingManager: google.maps.drawing.DrawingManager) => { \r\n newDrawingManager.addListener('overlaycomplete', (event: any) => {\r\n if(event.type === 'marker'){\r\n var position = event.overlay.getPosition();\r\n position = event.overlay.getPosition();\r\n const lat = position.lat(); \r\n const lng = position.lng(); \r\n setSelectedMarkerPosition({lat, lng});\r\n \r\n newDrawingManager?.setOptions({\r\n drawingMode: null,\r\n drawingControlOptions:{\r\n position: google.maps.ControlPosition.TOP_RIGHT,\r\n drawingModes: []\r\n }\r\n })\r\n }\r\n })\r\n }\r\n \r\n const drawingManager = useDrawingManager(map, null, addListenerCallback);\r\n \r\n return(\r\n \r\n setSelectedMarkerPosition(null)}/>\r\n \r\n )\r\n }\r\n \r\n export default MapControlWithDrawing;","import {useMap, useMapsLibrary} from '@vis.gl/react-google-maps';\r\nimport {useEffect, useState} from 'react';\r\n\r\nexport const useDrawingManager = ( \r\n map: google.maps.Map | null = null, \r\n initialValue: google.maps.drawing.DrawingManager | null = null,\r\n addListenerCallback: (drawingManager: google.maps.drawing.DrawingManager) => void\r\n\r\n) => {\r\n const drawing = useMapsLibrary('drawing');\r\n\r\n const [drawingManager, setDrawingManager] =\r\n useState(initialValue);\r\n\r\n useEffect(() => {\r\n if (!map || !drawing) return;\r\n\r\n // https://developers.google.com/maps/documentation/javascript/reference/drawing\r\n const newDrawingManager = new drawing.DrawingManager({\r\n map,\r\n drawingMode: google.maps.drawing.OverlayType.MARKER,\r\n drawingControl: true,\r\n drawingControlOptions: {\r\n position: google.maps.ControlPosition.TOP_RIGHT,\r\n drawingModes: [\r\n google.maps.drawing.OverlayType.MARKER,\r\n ]\r\n },\r\n markerOptions: {\r\n draggable: false\r\n },\r\n polylineOptions: {\r\n editable: true,\r\n draggable: true\r\n }\r\n });\r\n\r\n\r\n addListenerCallback(newDrawingManager);\r\n setDrawingManager(newDrawingManager);\r\n\r\n return () => {\r\n newDrawingManager.setMap(null);\r\n };\r\n }, [drawing, map]);\r\n\r\n return drawingManager;\r\n}\r\n","import { useMap } from \"@vis.gl/react-google-maps\";\r\nimport { useEffect } from \"react\";\r\n\r\nexport interface MapHandlerProps {\r\n place: google.maps.places.PlaceResult | null;\r\n map: google.maps.Map | null;\r\n}\r\n\r\nexport const MapHandler: React.FC = ({ map, place }) => {\r\n\r\n useEffect(() => {\r\n if (!map || !place) return;\r\n\r\n if (place.geometry?.viewport) {\r\n map.fitBounds(place.geometry?.viewport);\r\n }\r\n }, [map, place]);\r\n\r\n return null;\r\n};\r\n","import React, {useRef, useEffect, useState} from 'react';\r\nimport {useMapsLibrary} from '@vis.gl/react-google-maps';\r\n\r\ninterface Props {\r\n onPlaceSelect: (place: google.maps.places.PlaceResult | null) => void;\r\n}\r\n\r\nexport const PlaceAutocompleteClassic = ({onPlaceSelect}: Props) => {\r\n const [placeAutocomplete, setPlaceAutocomplete] = useState(null);\r\n const inputRef = useRef(null);\r\n const places = useMapsLibrary('places');\r\n\r\n useEffect(() => {\r\n if (!places || !inputRef.current) return;\r\n\r\n const options = {\r\n fields: ['geometry', 'name', 'formatted_address']\r\n };\r\n\r\n setPlaceAutocomplete(new places.Autocomplete(inputRef.current, options));\r\n }, [places]);\r\n\r\n useEffect(() => {\r\n if (!placeAutocomplete) return;\r\n\r\n placeAutocomplete.addListener('place_changed', () => {\r\n onPlaceSelect(placeAutocomplete.getPlace());\r\n });\r\n }, [onPlaceSelect, placeAutocomplete]);\r\n\r\n return (\r\n
\r\n \r\n
\r\n );\r\n};\r\n","import React, { useState, useCallback, useEffect } from \"react\";\r\nimport { APIProvider, Map, MapControl, useMap, } from \"@vis.gl/react-google-maps\";\r\nimport { LatLng } from \"./types\";\r\nimport MapControlWithDrawing from \"./MapControl\";\r\nimport { MapHandler } from \"./MapHandler\";\r\nimport { PlaceAutocompleteClassic } from \"./PlaceAutoComplete\";\r\n\r\ninterface GoogleMapCoreInterface {\r\n selectedMarkerPosition: LatLng | null;\r\n setSelectedMarkerPosition: (coords: LatLng) => void;\r\n}\r\n\r\nconst GoogleMapCore : React.FC = ({ selectedMarkerPosition, setSelectedMarkerPosition }) => {\r\n const map = useMap();\r\n const [selectedPlace, setSelectedPlace] = useState();\r\n\r\n const handleSelectedPlace = (place:any) => {\r\n setSelectedPlace(place);\r\n }\r\n\r\n return(\r\n <>\r\n \r\n
\r\n \r\n \r\n \r\n \r\n \r\n

Lat: {selectedMarkerPosition?.lat}, Long: {selectedMarkerPosition?.lng}

\r\n \r\n )\r\n}\r\n\r\nconst GoogleMapWithPinDrop : React.FC = ({ selectedMarkerPosition, setSelectedMarkerPosition }) => {\r\n\r\n \r\n return(\r\n \r\n \r\n \r\n )\r\n};\r\n\r\nexport default GoogleMapWithPinDrop;\r\n","import AdminApiService from \"src/api/AdminApiService\";\r\nimport { useQuery } from \"@tanstack/react-query\";\r\nimport { QueryTypes } from \"./shared\";\r\nimport { queryClient } from \"src\";\r\nimport EnterpriseApiService from \"src/api/EnterpriseApiService\";\r\nimport GrowerDTO from \"src/models/GrowerDTO\";\r\n\r\nexport const useGetAllGrowersQuery = (options: QueryTypes = {}) => {\r\n return useQuery([\"getAllGrowersQuery\"],\r\n async (q) => {\r\n return await EnterpriseApiService.getGrowerIDs();\r\n },\r\n {\r\n enabled: options?.enabled,\r\n refetchInterval: options?.refetchInterval,\r\n refetchOnWindowFocus: options?.refetchOnWindowFocus,\r\n initialData: () => {\r\n const data = queryClient.getQueryData([\"getAllGrowersQuery\"]);\r\n if(data != null){\r\n return data as GrowerDTO[];\r\n }\r\n return [];\r\n },\r\n \r\n });\r\n}","import { Button, Carousel, Form, Input, Modal, notification, Row, Select, Spin } from \"antd\"\r\nimport React, { useEffect, useState } from \"react\"\r\nimport GoogleMapWithPinDrop from \"./GoogleMaps/GoogleMapWithPinDrop\"\r\nimport { LatLng } from \"./GoogleMaps/types\";\r\nimport RegisterProductDTO from \"src/models/RegisterProductDTO\";\r\nimport { CarouselRef } from \"antd/es/carousel\";\r\nimport BinApiService from \"src/api/BinApiService\";\r\nimport { CheckCircleOutlined } from \"@ant-design/icons\";\r\nimport RoleUtil from \"src/utils/RoleUtil\";\r\nimport GrowerSelection from \"src/pages/dashboard/HomeScreen/GrowerSelection\";\r\nimport { GrowerSlim } from \"src/redux/actions/UserSessionActions\";\r\nimport { useGetAllGrowersQuery } from \"src/queries/useGetAllGrowersQuery\";\r\n\r\ninterface RegisterProductModalProps {\r\n modalOpen: boolean;\r\n closeModal: () => void;\r\n growerId: number;\r\n}\r\n\r\nconst RegisterProductModal : React.FC = ({ modalOpen, closeModal, growerId }) => {\r\n \r\n const [content, setContent]=useState(1);\r\n const [disableNextButton, setDisableNextButton] = useState(true);\r\n const [msg, setMsg] = useState(<>);\r\n const [msgLoading, setMsgLoading] = useState(false);\r\n const [selectedMarkerPosition, setSelectedMarkerPosition] = useState(null);\r\n const [loading, setLoading] = useState(false);\r\n const allGrowersQuery = useGetAllGrowersQuery({ enabled: true, refetchOnWindowFocus: false });\r\n\r\n const allGrowerSelectOptions = [\r\n { value: 0, label: 'Select a grower' },\r\n ...(allGrowersQuery.data ?? []).map( (g) => (\r\n { value: g.growerID, label: g.growerName }\r\n ))\r\n ]\r\n\r\n const [form] = Form.useForm();\r\n const values = Form.useWatch([], { form, preserve: true });\r\n const carouselRef = React.createRef();\r\n const isAdmin = RoleUtil.currentUserIsAdmin();\r\n\r\n const searchDeviceId = async () => {\r\n const deviceId = values?.deviceId;\r\n if(deviceId == '' || deviceId == null){\r\n setMsg(

\r\n Please type in your device ID\r\n

);\r\n return;\r\n }else{\r\n try{\r\n setMsgLoading(true);\r\n const result = await BinApiService.pingPremierPlusDevice(deviceId);\r\n if(result == true){\r\n setDisableNextButton(false);\r\n setMsg(\r\n \r\n \r\n Item found\r\n );\r\n }else{\r\n setMsg(

Could not reach device. Please make sure it's online and spelled correctly

);\r\n }\r\n \r\n }catch(err){\r\n setMsg(

Error reaching server, please reach out to contact support

);\r\n }finally{\r\n setMsgLoading(false);\r\n }\r\n }\r\n }\r\n\r\n const handleCloseModal = () =>{\r\n\r\n form.resetFields();\r\n setSelectedMarkerPosition(null);\r\n setMsg(<>);\r\n setDisableNextButton(true);\r\n handleResetCarousel();\r\n closeModal();\r\n }\r\n\r\n const onRegisterProductSubmit = async ( values: RegisterProductDTO ) => {\r\n\r\n try{\r\n setLoading(true);\r\n if(selectedMarkerPosition == null){\r\n notification.error({\r\n message:\"Please find your bin on the map\"\r\n })\r\n setLoading(false);\r\n return;\r\n }\r\n values.latitude = selectedMarkerPosition?.lat ?? 0;\r\n values.longitude = selectedMarkerPosition?.lng ?? 0;\r\n const result = await BinApiService.registerProduct(values);\r\n notification.success({ message: 'Successfully registered product' });\r\n setLoading(false);\r\n handleCloseModal();\r\n }catch(error){\r\n notification.error({\r\n message: \"Could not register product\",\r\n description: error?.errorDetails?.detail ?? \"Contact support\",\r\n });\r\n setLoading(false);\r\n }\r\n }\r\n\r\n const handleClickNext = () => {\r\n setContent((prev) => Math.max(prev + 1, 2));\r\n carouselRef?.current?.next();\r\n }\r\n\r\n const handleClickPrev = () => {\r\n setContent((prev) => Math.max(prev - 1, 1));\r\n carouselRef?.current?.prev();\r\n }\r\n\r\n const handleResetCarousel = () => {\r\n setContent(1);\r\n carouselRef?.current?.goTo(1);\r\n }\r\n\r\n return(\r\n \r\n \r\n
\r\n \r\n
\r\n

Register your product

\r\n \r\n \r\n \r\n \r\n\r\n {\r\n isAdmin && \r\n \r\n \r\n \r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n\r\n \r\n\r\n
\r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n\r\n \r\n \r\n \r\n \r\n\r\n \r\n DSM Regions\r\n {\r\n return {item}\r\n }}>\r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n\r\n {...checkLayout} label=\"Link Replace\" name=\"linkReplaced\" valuePropName=\"checked\">\r\n {\r\n if (e.target.checked) {\r\n form.setFieldsValue({\r\n linkReplaceAuthor: `${securityInfo?.firstName} ${securityInfo?.lastName}`,\r\n linkReplaceNotes: form.getFieldValue(\"linkReplaceNotes\"),\r\n linkReplaceDate: form.getFieldValue(\"linkReplaceDate\"),\r\n linkReplaced: true,\r\n });\r\n }\r\n else {\r\n form.setFieldsValue({\r\n linkReplaceAuthor: '',\r\n linkReplaceNotes: \"\",\r\n linkReplaceDate: \"\",\r\n linkReplaced: false,\r\n })\r\n }\r\n }} />\r\n \r\n\r\n\r\n\r\n \r\n \r\n {\r\n return { value: value ? dayjs(value).tz(\"America/Chicago\", false) : \"\" };\r\n }} hidden={!formValues.linkReplaced}>\r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n\r\n
\r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n {formValues.updateIp && }\r\n \r\n \r\n \r\n \r\n {errorText != null && {errorText}

} closable onClose={() => setErrorText(null)} />}\r\n \r\n
\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n Check if you are updating IP address\r\n \r\n

If this check box is checked only the IP address will update.\r\n Change the IP address in the account textbox and press save.\r\n You do not need to check edit user.

\r\n \r\n
\r\n\r\n\r\n \r\n \r\n \r\n\r\n \r\n}\r\n\r\n\r\ninterface ConfirmIpUpdateProps {\r\n oldIp: string | null | undefined,\r\n newIp: string | null | undefined,\r\n onConfirm: () => void;\r\n onCancel: () => void;\r\n}\r\nexport const ConfirmIpUpdate = (props: ConfirmIpUpdateProps) => {\r\n\r\n const queryClient = useQueryClient();\r\n const [firstSubmitDone, setFirstSubmitDone] = useState(false);\r\n const isMutating = queryClient.isMutating({ mutationKey: [\"updateIp\"] });\r\n\r\n const onYesClick = useCallback(() => {\r\n setFirstSubmitDone(true);\r\n }, [setFirstSubmitDone]);\r\n\r\n return 0}>\r\n \r\n Double check the old and new IP addresses to confirm the update\r\n
\r\n
\r\n The old IP address is {props.oldIp}, and {props.newIp} is the new IP address. If this is correct press submit.\r\n\r\n
\r\n \r\n \r\n \r\n
\r\n
\r\n\r\n {firstSubmitDone && <>Are you sure you want to update this user?\r\n \r\n \r\n \r\n }\r\n
\r\n\r\n }\r\n />
;\r\n}\r\n\r\nexport const usePremierUserSearchQuery = (params?: {enabled?: boolean, onlyCustomers?: boolean}) => {\r\n return useQuery({\r\n queryKey: [{purpose: \"userSearch\", ...userEditScope[0]}],\r\n enabled: params?.enabled,\r\n queryFn: async (q) => {\r\n return await ShivversService.searchUsers(undefined, params?.onlyCustomers, q.signal);\r\n }\r\n });\r\n}\r\n\r\nexport const Search = (props: {}) => {\r\n const query = usePremierUserSearchQuery();\r\n\r\n const history = useHistory();\r\n\r\n const options = query.data?.map(u => {\r\n return { ...u, value: u.externalId, label: `${u.firstName} ${u.lastName}` }\r\n }) ?? [];\r\n return \r\n \r\n }\r\n \r\n {this.state.chosenGrowerId && this.state.chosenGrowerName &&\r\n \r\n \r\n \r\n }\r\n \r\n \r\n {this.state.currentGrower &&\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n {\r\n !!this.state.chosenGrowerId && \r\n \r\n \r\n \r\n \r\n \r\n \r\n }\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n }\r\n \r\n );\r\n }\r\n\r\n private onFinish = (values: any) => {\r\n let formGrower = {\r\n growerID: this.state.chosenGrowerId ? this.state.chosenGrowerId : 0,\r\n growerName: values.growerName,\r\n isActive: values.isActive,\r\n notes: values.notes,\r\n externalBinIds: values.externalBinIds,\r\n } as GrowerDTO;\r\n if (!this.state.chosenGrowerId) {\r\n EnterpriseApiService.addGrower(formGrower).then(() => {\r\n notification.success({\r\n message: 'New Organization Added Succesfully'\r\n });\r\n this.getData();\r\n this.setState({\r\n chosenGrowerId: null,\r\n chosenGrowerName: '',\r\n currentGrower: null\r\n });\r\n }).catch(error => {\r\n notification.error({\r\n message: error.message,\r\n description: error.description\r\n });\r\n });\r\n } else {\r\n EnterpriseApiService.updateGrower(formGrower).then(() => {\r\n notification.success({\r\n message: 'Organization Updated Succesfully'\r\n });\r\n this.getData();\r\n if (RoleUtil.currentUserIsAdmin()) {\r\n this.setState({\r\n chosenGrowerId: null,\r\n chosenGrowerName: '',\r\n currentGrower: null\r\n });\r\n }\r\n }).catch(error => {\r\n notification.error({\r\n message: error.message,\r\n description: error.description\r\n });\r\n });\r\n }\r\n }\r\n\r\n private getData() {\r\n EnterpriseApiService.getGrowerIDs(false).then((growerIDs) => {\r\n let GrowerNameIDPairs: [number, string][] = growerIDs.map((grower) => [grower.growerID, grower.growerName ? grower.growerName : 'error: no name']);\r\n this.setState({\r\n growerIdList: GrowerNameIDPairs\r\n });\r\n }).catch(error => {\r\n notification.error({\r\n message: error.message,\r\n description: error.description\r\n });\r\n });\r\n }\r\n private handleGrowerIdClicked = async (id: number) => {\r\n if (id) {\r\n EnterpriseApiService.getGrower(id).then(grower => {\r\n this.setState({\r\n chosenGrowerId: grower.growerID,\r\n chosenGrowerName: grower.growerName ?? \"\",\r\n currentGrower: grower\r\n });\r\n this._formRef.current?.resetFields();\r\n }).catch(error => {\r\n notification.error({\r\n message: error.message,\r\n description: error.description\r\n });\r\n });\r\n } else {\r\n await this.setState({\r\n chosenGrowerId: null,\r\n chosenGrowerName: 'New Organization',\r\n currentGrower: GrowerDTO.create()\r\n });\r\n this._formRef.current?.resetFields();\r\n }\r\n }\r\n\r\n private binIdDropdownValues = () => {\r\n return (\r\n \r\n {this.state?.currentGrower?.bins?.map((binInfo, index) => (\r\n \r\n \r\n
\r\n {binInfo.name}\r\n
\r\n \r\n
\r\n ))}\r\n
\r\n );\r\n }\r\n\r\n}\r\n\r\n// requuirements for a custom form control https://ant.design/components/form#form-demo-customized-form-controls\r\ninterface SearchProps {\r\n id?: string;\r\n value?: number[];\r\n onChange?: (value: number[]) => void;\r\n}\r\nexport const Search = (props: SearchProps) => {\r\n const query = usePremierUserSearchQuery({onlyCustomers: true});\r\n\r\n const options = query.data?.map(u => {\r\n return { ...u, value: u.externalId, label: `${u.firstName} ${u.lastName}` }\r\n }) ?? [];\r\n return