cione-comp-lib
Version:
`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`
373 lines (372 loc) • 12.1 kB
JavaScript
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 };