UNPKG

cione-comp-lib

Version:

`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`

373 lines (372 loc) 12.1 kB
import Node3D from "./Node.mjs"; import Light from "./Light.mjs"; import Camera from "./Camera.mjs"; import BoundingBox from "./math/BoundingBox.mjs"; import util from "./core/util.mjs"; import mat4 from "./glmatrix/mat4.mjs"; import LRUCache from "./core/LRU.mjs"; import Matrix4 from "./math/Matrix4.mjs"; var IDENTITY = mat4.create(); var WORLDVIEW = mat4.create(); var programKeyCache = {}; function getProgramKey(lightNumbers) { var defineStr = []; var lightTypes = Object.keys(lightNumbers); lightTypes.sort(); for (var i = 0; i < lightTypes.length; i++) { var lightType = lightTypes[i]; defineStr.push(lightType + " " + lightNumbers[lightType]); } var key = defineStr.join("\n"); if (programKeyCache[key]) { return programKeyCache[key]; } var id = util.genGUID(); programKeyCache[key] = id; return id; } function RenderList() { this.opaque = []; this.transparent = []; this._opaqueCount = 0; this._transparentCount = 0; } RenderList.prototype.startCount = function() { this._opaqueCount = 0; this._transparentCount = 0; }; RenderList.prototype.add = function(object, isTransparent) { if (isTransparent) { this.transparent[this._transparentCount++] = object; } else { this.opaque[this._opaqueCount++] = object; } }; RenderList.prototype.endCount = function() { this.transparent.length = this._transparentCount; this.opaque.length = this._opaqueCount; }; var Scene = Node3D.extend( function() { return { material: null, lights: [], viewBoundingBoxLastFrame: new BoundingBox(), shadowUniforms: {}, _cameraList: [], _lightUniforms: {}, _previousLightNumber: {}, _lightNumber: {}, _lightProgramKeys: {}, _nodeRepository: {}, _renderLists: new LRUCache(20) }; }, function() { this._scene = this; }, { addToScene: function(node) { if (node instanceof Camera) { if (this._cameraList.length > 0) { console.warn("Found multiple camera in one scene. Use the fist one."); } this._cameraList.push(node); } else if (node instanceof Light) { this.lights.push(node); } if (node.name) { this._nodeRepository[node.name] = node; } }, removeFromScene: function(node) { var idx; if (node instanceof Camera) { idx = this._cameraList.indexOf(node); if (idx >= 0) { this._cameraList.splice(idx, 1); } } else if (node instanceof Light) { idx = this.lights.indexOf(node); if (idx >= 0) { this.lights.splice(idx, 1); } } if (node.name) { delete this._nodeRepository[node.name]; } }, getNode: function(name) { return this._nodeRepository[name]; }, setMainCamera: function(camera) { var idx = this._cameraList.indexOf(camera); if (idx >= 0) { this._cameraList.splice(idx, 1); } this._cameraList.unshift(camera); }, getMainCamera: function() { return this._cameraList[0]; }, getLights: function() { return this.lights; }, updateLights: function() { var lights = this.lights; this._previousLightNumber = this._lightNumber; var lightNumber = {}; for (var i = 0; i < lights.length; i++) { var light = lights[i]; if (light.invisible) { continue; } var group = light.group; if (!lightNumber[group]) { lightNumber[group] = {}; } lightNumber[group][light.type] = lightNumber[group][light.type] || 0; lightNumber[group][light.type]++; } this._lightNumber = lightNumber; for (var groupId in lightNumber) { this._lightProgramKeys[groupId] = getProgramKey(lightNumber[groupId]); } this._updateLightUniforms(); }, cloneNode: function(node) { var newNode = node.clone(); var clonedNodesMap = {}; function buildNodesMap(sNode, tNode) { clonedNodesMap[sNode.__uid__] = tNode; for (var i = 0; i < sNode._children.length; i++) { var sChild = sNode._children[i]; var tChild = tNode._children[i]; buildNodesMap(sChild, tChild); } } buildNodesMap(node, newNode); newNode.traverse(function(newChild) { if (newChild.skeleton) { newChild.skeleton = newChild.skeleton.clone(clonedNodesMap); } if (newChild.material) { newChild.material = newChild.material.clone(); } }); return newNode; }, updateRenderList: function(camera, updateSceneBoundingBox) { var id = camera.__uid__; var renderList = this._renderLists.get(id); if (!renderList) { renderList = new RenderList(); this._renderLists.put(id, renderList); } renderList.startCount(); if (updateSceneBoundingBox) { this.viewBoundingBoxLastFrame.min.set(Infinity, Infinity, Infinity); this.viewBoundingBoxLastFrame.max.set(-Infinity, -Infinity, -Infinity); } var sceneMaterialTransparent = this.material && this.material.transparent || false; this._doUpdateRenderList(this, camera, sceneMaterialTransparent, renderList, updateSceneBoundingBox); renderList.endCount(); return renderList; }, getRenderList: function(camera) { return this._renderLists.get(camera.__uid__); }, _doUpdateRenderList: function(parent, camera, sceneMaterialTransparent, renderList, updateSceneBoundingBox) { if (parent.invisible) { return; } for (var i = 0; i < parent._children.length; i++) { var child = parent._children[i]; if (child.isRenderable()) { var worldM = child.isSkinnedMesh() ? IDENTITY : child.worldTransform.array; var geometry = child.geometry; mat4.multiplyAffine(WORLDVIEW, camera.viewMatrix.array, worldM); if (updateSceneBoundingBox && !geometry.boundingBox || !this.isFrustumCulled(child, camera, WORLDVIEW)) { renderList.add(child, child.material.transparent || sceneMaterialTransparent); } } if (child._children.length > 0) { this._doUpdateRenderList(child, camera, sceneMaterialTransparent, renderList, updateSceneBoundingBox); } } }, isFrustumCulled: function() { var cullingBoundingBox = new BoundingBox(); var cullingMatrix = new Matrix4(); return function(object, camera, worldViewMat) { var geoBBox = object.boundingBox; if (!geoBBox) { if (object.skeleton && object.skeleton.boundingBox) { geoBBox = object.skeleton.boundingBox; } else { geoBBox = object.geometry.boundingBox; } } if (!geoBBox) { return false; } cullingMatrix.array = worldViewMat; cullingBoundingBox.transformFrom(geoBBox, cullingMatrix); if (object.castShadow) { this.viewBoundingBoxLastFrame.union(cullingBoundingBox); } if (object.frustumCulling) { if (!cullingBoundingBox.intersectBoundingBox(camera.frustum.boundingBox)) { return true; } cullingMatrix.array = camera.projectionMatrix.array; if (cullingBoundingBox.max.array[2] > 0 && cullingBoundingBox.min.array[2] < 0) { cullingBoundingBox.max.array[2] = -1e-20; } cullingBoundingBox.applyProjection(cullingMatrix); var min = cullingBoundingBox.min.array; var max = cullingBoundingBox.max.array; if (max[0] < -1 || min[0] > 1 || max[1] < -1 || min[1] > 1 || max[2] < -1 || min[2] > 1) { return true; } } return false; }; }(), _updateLightUniforms: function() { var lights = this.lights; lights.sort(lightSortFunc); var lightUniforms = this._lightUniforms; for (var group in lightUniforms) { for (var symbol in lightUniforms[group]) { lightUniforms[group][symbol].value.length = 0; } } for (var i = 0; i < lights.length; i++) { var light = lights[i]; if (light.invisible) { continue; } var group = light.group; for (var symbol in light.uniformTemplates) { var uniformTpl = light.uniformTemplates[symbol]; var value = uniformTpl.value(light); if (value == null) { continue; } if (!lightUniforms[group]) { lightUniforms[group] = {}; } if (!lightUniforms[group][symbol]) { lightUniforms[group][symbol] = { type: "", value: [] }; } var lu = lightUniforms[group][symbol]; lu.type = uniformTpl.type + "v"; switch (uniformTpl.type) { case "1i": case "1f": case "t": lu.value.push(value); break; case "2f": case "3f": case "4f": for (var j = 0; j < value.length; j++) { lu.value.push(value[j]); } break; default: console.error("Unkown light uniform type " + uniformTpl.type); } } } }, getLightGroups: function() { var lightGroups = []; for (var groupId in this._lightNumber) { lightGroups.push(groupId); } return lightGroups; }, getNumberChangedLightGroups: function() { var lightGroups = []; for (var groupId in this._lightNumber) { if (this.isLightNumberChanged(groupId)) { lightGroups.push(groupId); } } return lightGroups; }, isLightNumberChanged: function(lightGroup) { var prevLightNumber = this._previousLightNumber; var currentLightNumber = this._lightNumber; for (var type in currentLightNumber[lightGroup]) { if (!prevLightNumber[lightGroup]) { return true; } if (currentLightNumber[lightGroup][type] !== prevLightNumber[lightGroup][type]) { return true; } } for (var type in prevLightNumber[lightGroup]) { if (!currentLightNumber[lightGroup]) { return true; } if (currentLightNumber[lightGroup][type] !== prevLightNumber[lightGroup][type]) { return true; } } return false; }, getLightsNumbers: function(lightGroup) { return this._lightNumber[lightGroup]; }, getProgramKey: function(lightGroup) { return this._lightProgramKeys[lightGroup]; }, setLightUniforms: function() { function setUniforms(uniforms, program, renderer) { for (var symbol in uniforms) { var lu = uniforms[symbol]; if (lu.type === "tv") { if (!program.hasUniform(symbol)) { continue; } var texSlots = []; for (var i = 0; i < lu.value.length; i++) { var texture = lu.value[i]; var slot = program.takeCurrentTextureSlot(renderer, texture); texSlots.push(slot); } program.setUniform(renderer.gl, "1iv", symbol, texSlots); } else { program.setUniform(renderer.gl, lu.type, symbol, lu.value); } } } return function(program, lightGroup, renderer) { setUniforms(this._lightUniforms[lightGroup], program, renderer); setUniforms(this.shadowUniforms, program, renderer); }; }(), dispose: function() { this.material = null; this._opaqueList = []; this._transparentList = []; this.lights = []; this._lightUniforms = {}; this._lightNumber = {}; this._nodeRepository = {}; } } ); function lightSortFunc(a, b) { if (b.castShadow && !a.castShadow) { return true; } } var Scene$1 = Scene; export { Scene$1 as default };