wip(type): add type to more util methods

This commit is contained in:
pissang 2022-05-02 22:35:36 +08:00
parent b46e8c1159
commit cf4bf27158
9 changed files with 508 additions and 510 deletions

View File

@ -3,7 +3,6 @@ import * as mat4 from './glmatrix/mat4';
import BoundingBox from './math/BoundingBox';
import GeometryBase, { AttributeValue, GeometryAttribute, GeometryBaseOpts } from './GeometryBase';
import type Matrix4 from './math/Matrix4';
import type Renderer from './Renderer';
export interface GeometryOpts extends GeometryBaseOpts {}
@ -81,7 +80,6 @@ class Geometry extends GeometryBase {
/**
* Calculated bounding box of geometry.
*/
boundingBox?: BoundingBox;
attributes!: {
position: GeometryAttribute<3>;

View File

@ -8,6 +8,7 @@ import type Renderer from './Renderer';
import type Camera from './Camera';
import type Renderable from './Renderable';
import type Vector2 from './math/Vector2';
import type BoundingBox from './math/BoundingBox';
export type AttributeType = 'byte' | 'ubyte' | 'short' | 'ushort' | 'float';
export type AttributeSize = 1 | 2 | 3 | 4;
@ -295,6 +296,8 @@ interface GeometryBase extends GeometryBaseOpts {}
class GeometryBase {
readonly __uid__ = genGUID();
// TODO put in GeometryBase?
boundingBox?: BoundingBox;
/**
* Attributes of geometry.
*/

View File

@ -65,7 +65,7 @@ export interface Texture2DOpts extends TextureOpts, Texture2DData {
interface Texture2D extends Omit<Texture2DOpts, 'image'> {}
class Texture2D extends Texture {
readonly textureType: string = 'texture2D';
readonly textureType = 'texture2D';
private _image?: TextureImageSource;
private _potCanvas?: HTMLCanvasElement;

View File

@ -101,7 +101,6 @@ class EnvironmentMapPass {
return this._cameras[target];
}
render(renderer: Renderer, scene: Scene, notUpdateScene?: boolean) {
const _gl = renderer.gl;
const texture = this.texture;
if (!notUpdateScene) {
scene.update();

View File

@ -11,7 +11,7 @@ import Shader from '../Shader';
import Skybox from '../plugin/Skybox';
import Scene from '../Scene';
import EnvironmentMapPass from '../prePass/EnvironmentMap';
import textureUtil from './texture';
import { panoramaToCubeMap } from './texture';
import integrateBRDFShaderCode from './shader/integrateBRDF.glsl.js';
import prefilterFragCode from './shader/prefilter.glsl.js';
@ -81,7 +81,7 @@ export function prefilterEnvironmentMap(
// FIXME FLOAT type will cause GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error on iOS
type: textureType === Texture.FLOAT ? Texture.HALF_FLOAT : textureType
});
textureUtil.panoramaToCubeMap(renderer, envMap, envCubemap, {
panoramaToCubeMap(renderer, envMap, envCubemap, {
// PENDING encodeRGBM so it can be decoded as RGBM
encodeRGBM: textureOpts.decodeRGBM
});

View File

@ -4,13 +4,12 @@
const EPSILON = 1.0 / 1048576.0;
function supertriangle(vertices) {
let xmin = Number.POSITIVE_INFINITY;
let ymin = Number.POSITIVE_INFINITY;
let xmax = Number.NEGATIVE_INFINITY;
let ymax = Number.NEGATIVE_INFINITY;
let i, dx, dy, dmax, xmid, ymid;
let xmin = Infinity;
let ymin = Infinity;
let xmax = -Infinity;
let ymax = -Infinity;
for (i = vertices.length; i--; ) {
for (let i = vertices.length; i--; ) {
if (vertices[i][0] < xmin) {
xmin = vertices[i][0];
}
@ -25,11 +24,11 @@ function supertriangle(vertices) {
}
}
dx = xmax - xmin;
dy = ymax - ymin;
dmax = Math.max(dx, dy);
xmid = xmin + dx * 0.5;
ymid = ymin + dy * 0.5;
const dx = xmax - xmin;
const dy = ymax - ymin;
const dmax = Math.max(dx, dy);
const xmid = xmin + dx * 0.5;
const ymid = ymin + dy * 0.5;
return [
[xmid - 20 * dmax, ymid - dmax],
@ -126,7 +125,7 @@ const delaunay = {
/* Make an array of indices into the vertex array, sorted by the
* vertices' x-position. Force stable sorting by comparing indices if
* the x-positions are equal. */
indices = new Array(n);
indices = [];
for (i = n; i--; ) {
indices[i] = i;
@ -222,7 +221,7 @@ const delaunay = {
const b = tri[2][0] - tri[0][0];
const c = tri[1][1] - tri[0][1];
const d = tri[2][1] - tri[0][1];
let i = a * d - b * c;
const i = a * d - b * c;
/* Degenerate tri. */
if (i === 0.0) {

View File

@ -2,7 +2,7 @@
// TODO test
import Geometry from '../Geometry';
import Mesh from '../Mesh';
import Node from '../Node';
import ClayNode from '../Node';
import BoundingBox from '../math/BoundingBox';
import vec3 from '../glmatrix/vec3';
import mat4 from '../glmatrix/mat4';
@ -10,330 +10,326 @@ import mat4 from '../glmatrix/mat4';
/**
* @namespace clay.util.mesh
*/
const meshUtil = {
/**
* Merge multiple meshes to one.
* Note that these meshes must have the same material
*
* @param {Array.<clay.Mesh>} meshes
* @param {boolean} applyWorldTransform
* @return {clay.Mesh}
* @memberOf clay.util.mesh
*/
merge: function (meshes, applyWorldTransform) {
if (!meshes.length) {
return;
}
const templateMesh = meshes[0];
const templateGeo = templateMesh.geometry;
const material = templateMesh.material;
const geometry = new Geometry({
dynamic: false
});
geometry.boundingBox = new BoundingBox();
const attributeNames = templateGeo.getEnabledAttributes();
for (let i = 0; i < attributeNames.length; i++) {
const name = attributeNames[i];
const attr = templateGeo.attributes[name];
// Extend custom attributes
if (!geometry.attributes[name]) {
geometry.attributes[name] = attr.clone(false);
}
}
const inverseTransposeMatrix = mat4.create();
// Initialize the array data and merge bounding box
let nVertex = 0;
let nFace = 0;
for (let k = 0; k < meshes.length; k++) {
const currentGeo = meshes[k].geometry;
if (currentGeo.boundingBox) {
currentGeo.boundingBox.applyTransform(
applyWorldTransform ? meshes[k].worldTransform : meshes[k].localTransform
);
geometry.boundingBox.union(currentGeo.boundingBox);
}
nVertex += currentGeo.vertexCount;
nFace += currentGeo.triangleCount;
}
for (let n = 0; n < attributeNames.length; n++) {
const name = attributeNames[n];
const attrib = geometry.attributes[name];
attrib.init(nVertex);
}
if (nVertex >= 0xffff) {
geometry.indices = new Uint32Array(nFace * 3);
} else {
geometry.indices = new Uint16Array(nFace * 3);
}
let vertexOffset = 0;
let indicesOffset = 0;
const useIndices = templateGeo.isUseIndices();
for (let mm = 0; mm < meshes.length; mm++) {
const mesh = meshes[mm];
const currentGeo = mesh.geometry;
const nVertex = currentGeo.vertexCount;
const matrix = applyWorldTransform ? mesh.worldTransform.array : mesh.localTransform.array;
mat4.invert(inverseTransposeMatrix, matrix);
mat4.transpose(inverseTransposeMatrix, inverseTransposeMatrix);
for (let nn = 0; nn < attributeNames.length; nn++) {
const name = attributeNames[nn];
const currentAttr = currentGeo.attributes[name];
const targetAttr = geometry.attributes[name];
// Skip the unused attributes;
if (!currentAttr.value.length) {
continue;
}
const len = currentAttr.value.length;
const size = currentAttr.size;
const offset = vertexOffset * size;
const count = len / size;
for (let i = 0; i < len; i++) {
targetAttr.value[offset + i] = currentAttr.value[i];
}
// Transform position, normal and tangent
if (name === 'position') {
vec3.forEach(targetAttr.value, size, offset, count, vec3.transformMat4, matrix);
} else if (name === 'normal' || name === 'tangent') {
vec3.forEach(
targetAttr.value,
size,
offset,
count,
vec3.transformMat4,
inverseTransposeMatrix
);
}
}
if (useIndices) {
const len = currentGeo.indices.length;
for (let i = 0; i < len; i++) {
geometry.indices[i + indicesOffset] = currentGeo.indices[i] + vertexOffset;
}
indicesOffset += len;
}
vertexOffset += nVertex;
}
return new Mesh({
material: material,
geometry: geometry
});
},
/**
* Split mesh into sub meshes, each mesh will have maxJointNumber joints.
* @param {clay.Mesh} mesh
* @param {number} maxJointNumber
* @param {boolean} inPlace
* @return {clay.Node}
*
* @memberOf clay.util.mesh
*/
// FIXME, Have issues on some models
splitByJoints: function (mesh, maxJointNumber, inPlace) {
const geometry = mesh.geometry;
const skeleton = mesh.skeleton;
const material = mesh.material;
const joints = mesh.joints;
if (!geometry || !skeleton || !joints.length) {
return;
}
if (joints.length < maxJointNumber) {
return mesh;
}
const indices = geometry.indices;
const faceLen = geometry.triangleCount;
let rest = faceLen;
const isFaceAdded = [];
const jointValues = geometry.attributes.joint.value;
for (let i = 0; i < faceLen; i++) {
isFaceAdded[i] = false;
}
const addedJointIdxPerFace = [];
const buckets = [];
const getJointByIndex = function (idx) {
return joints[idx];
};
while (rest > 0) {
const bucketTriangles = [];
const bucketJointReverseMap = [];
const bucketJoints = [];
let subJointNumber = 0;
for (let i = 0; i < joints.length; i++) {
bucketJointReverseMap[i] = -1;
}
for (let f = 0; f < faceLen; f++) {
if (isFaceAdded[f]) {
continue;
}
let canAddToBucket = true;
let addedNumber = 0;
for (let i = 0; i < 3; i++) {
const idx = indices[f * 3 + i];
for (let j = 0; j < 4; j++) {
const jointIdx = jointValues[idx * 4 + j];
if (jointIdx >= 0) {
if (bucketJointReverseMap[jointIdx] === -1) {
if (subJointNumber < maxJointNumber) {
bucketJointReverseMap[jointIdx] = subJointNumber;
bucketJoints[subJointNumber++] = jointIdx;
addedJointIdxPerFace[addedNumber++] = jointIdx;
} else {
canAddToBucket = false;
}
}
}
}
}
if (!canAddToBucket) {
// Reverse operation
for (let i = 0; i < addedNumber; i++) {
bucketJointReverseMap[addedJointIdxPerFace[i]] = -1;
bucketJoints.pop();
subJointNumber--;
}
} else {
bucketTriangles.push(indices.subarray(f * 3, (f + 1) * 3));
isFaceAdded[f] = true;
rest--;
}
}
buckets.push({
triangles: bucketTriangles,
joints: bucketJoints.map(getJointByIndex),
jointReverseMap: bucketJointReverseMap
});
}
const root = new Node({
name: mesh.name
});
const attribNames = geometry.getEnabledAttributes();
attribNames.splice(attribNames.indexOf('joint'), 1);
// Map from old vertex index to new vertex index
const newIndices = [];
for (let b = 0; b < buckets.length; b++) {
const bucket = buckets[b];
const jointReverseMap = bucket.jointReverseMap;
const subGeo = new Geometry();
const subMesh = new Mesh({
name: [mesh.name, b].join('-'),
// DON'T clone material.
material: material,
geometry: subGeo,
skeleton: skeleton,
joints: bucket.joints.slice()
});
let nVertex = 0;
const nVertex2 = geometry.vertexCount;
for (let i = 0; i < nVertex2; i++) {
newIndices[i] = -1;
}
// Count sub geo number
for (let f = 0; f < bucket.triangles.length; f++) {
const face = bucket.triangles[f];
for (let i = 0; i < 3; i++) {
const idx = face[i];
if (newIndices[idx] === -1) {
newIndices[idx] = nVertex;
nVertex++;
}
}
}
for (let a = 0; a < attribNames.length; a++) {
const attribName = attribNames[a];
const subAttrib = subGeo.attributes[attribName];
subAttrib.init(nVertex);
}
subGeo.attributes.joint.value = new Float32Array(nVertex * 4);
if (nVertex > 0xffff) {
subGeo.indices = new Uint32Array(bucket.triangles.length * 3);
} else {
subGeo.indices = new Uint16Array(bucket.triangles.length * 3);
}
let indicesOffset = 0;
nVertex = 0;
for (let i = 0; i < nVertex2; i++) {
newIndices[i] = -1;
}
for (let f = 0; f < bucket.triangles.length; f++) {
const triangle = bucket.triangles[f];
for (let i = 0; i < 3; i++) {
const idx = triangle[i];
if (newIndices[idx] === -1) {
newIndices[idx] = nVertex;
for (let a = 0; a < attribNames.length; a++) {
const attribName = attribNames[a];
const attrib = geometry.attributes[attribName];
const subAttrib = subGeo.attributes[attribName];
const size = attrib.size;
for (let j = 0; j < size; j++) {
subAttrib.value[nVertex * size + j] = attrib.value[idx * size + j];
}
}
for (let j = 0; j < 4; j++) {
const jointIdx = geometry.attributes.joint.value[idx * 4 + j];
const offset = nVertex * 4 + j;
if (jointIdx >= 0) {
subGeo.attributes.joint.value[offset] = jointReverseMap[jointIdx];
} else {
subGeo.attributes.joint.value[offset] = -1;
}
}
nVertex++;
}
subGeo.indices[indicesOffset++] = newIndices[idx];
}
}
subGeo.updateBoundingBox();
root.add(subMesh);
}
const children = mesh.children();
for (let i = 0; i < children.length; i++) {
root.add(children[i]);
}
root.position.copy(mesh.position);
root.rotation.copy(mesh.rotation);
root.scale.copy(mesh.scale);
if (inPlace) {
if (mesh.getParent()) {
const parent = mesh.getParent();
parent.remove(mesh);
parent.add(root);
}
}
return root;
/**
* Merge multiple meshes to one.
* Note that these meshes must have the same material
*
* @param {Array.<clay.Mesh>} meshes
* @param {boolean} applyWorldTransform
* @return {clay.Mesh}
* @memberOf clay.util.mesh
*/
export function merge(meshes, applyWorldTransform) {
if (!meshes.length) {
return;
}
};
export default meshUtil;
const templateMesh = meshes[0];
const templateGeo = templateMesh.geometry;
const material = templateMesh.material;
const geometry = new Geometry({
dynamic: false
});
geometry.boundingBox = new BoundingBox();
const attributeNames = templateGeo.getEnabledAttributes();
for (let i = 0; i < attributeNames.length; i++) {
const name = attributeNames[i];
const attr = templateGeo.attributes[name];
// Extend custom attributes
if (!geometry.attributes[name]) {
geometry.attributes[name] = attr.clone(false);
}
}
const inverseTransposeMatrix = mat4.create();
// Initialize the array data and merge bounding box
let nVertex = 0;
let nFace = 0;
for (let k = 0; k < meshes.length; k++) {
const currentGeo = meshes[k].geometry;
if (currentGeo.boundingBox) {
currentGeo.boundingBox.applyTransform(
applyWorldTransform ? meshes[k].worldTransform : meshes[k].localTransform
);
geometry.boundingBox.union(currentGeo.boundingBox);
}
nVertex += currentGeo.vertexCount;
nFace += currentGeo.triangleCount;
}
for (let n = 0; n < attributeNames.length; n++) {
const name = attributeNames[n];
const attrib = geometry.attributes[name];
attrib.init(nVertex);
}
if (nVertex >= 0xffff) {
geometry.indices = new Uint32Array(nFace * 3);
} else {
geometry.indices = new Uint16Array(nFace * 3);
}
let vertexOffset = 0;
let indicesOffset = 0;
const useIndices = templateGeo.isUseIndices();
for (let mm = 0; mm < meshes.length; mm++) {
const mesh = meshes[mm];
const currentGeo = mesh.geometry;
const nVertex = currentGeo.vertexCount;
const matrix = applyWorldTransform ? mesh.worldTransform.array : mesh.localTransform.array;
mat4.invert(inverseTransposeMatrix, matrix);
mat4.transpose(inverseTransposeMatrix, inverseTransposeMatrix);
for (let nn = 0; nn < attributeNames.length; nn++) {
const name = attributeNames[nn];
const currentAttr = currentGeo.attributes[name];
const targetAttr = geometry.attributes[name];
// Skip the unused attributes;
if (!currentAttr.value.length) {
continue;
}
const len = currentAttr.value.length;
const size = currentAttr.size;
const offset = vertexOffset * size;
const count = len / size;
for (let i = 0; i < len; i++) {
targetAttr.value[offset + i] = currentAttr.value[i];
}
// Transform position, normal and tangent
if (name === 'position') {
vec3.forEach(targetAttr.value, size, offset, count, vec3.transformMat4, matrix);
} else if (name === 'normal' || name === 'tangent') {
vec3.forEach(
targetAttr.value,
size,
offset,
count,
vec3.transformMat4,
inverseTransposeMatrix
);
}
}
if (useIndices) {
const len = currentGeo.indices.length;
for (let i = 0; i < len; i++) {
geometry.indices[i + indicesOffset] = currentGeo.indices[i] + vertexOffset;
}
indicesOffset += len;
}
vertexOffset += nVertex;
}
return new Mesh({
material: material,
geometry: geometry
});
}
/**
* Split mesh into sub meshes, each mesh will have maxJointNumber joints.
* @param {clay.Mesh} mesh
* @param {number} maxJointNumber
* @param {boolean} inPlace
* @return {clay.Node}
*
* @memberOf clay.util.mesh
*/
// FIXME, Have issues on some models
export function splitByJoints(mesh, maxJointNumber, inPlace) {
const geometry = mesh.geometry;
const skeleton = mesh.skeleton;
const material = mesh.material;
const joints = mesh.joints;
if (!geometry || !skeleton || !joints.length) {
return;
}
if (joints.length < maxJointNumber) {
return mesh;
}
const indices = geometry.indices;
const faceLen = geometry.triangleCount;
let rest = faceLen;
const isFaceAdded = [];
const jointValues = geometry.attributes.joint.value;
for (let i = 0; i < faceLen; i++) {
isFaceAdded[i] = false;
}
const addedJointIdxPerFace = [];
const buckets = [];
const getJointByIndex = function (idx) {
return joints[idx];
};
while (rest > 0) {
const bucketTriangles = [];
const bucketJointReverseMap = [];
const bucketJoints = [];
let subJointNumber = 0;
for (let i = 0; i < joints.length; i++) {
bucketJointReverseMap[i] = -1;
}
for (let f = 0; f < faceLen; f++) {
if (isFaceAdded[f]) {
continue;
}
let canAddToBucket = true;
let addedNumber = 0;
for (let i = 0; i < 3; i++) {
const idx = indices[f * 3 + i];
for (let j = 0; j < 4; j++) {
const jointIdx = jointValues[idx * 4 + j];
if (jointIdx >= 0) {
if (bucketJointReverseMap[jointIdx] === -1) {
if (subJointNumber < maxJointNumber) {
bucketJointReverseMap[jointIdx] = subJointNumber;
bucketJoints[subJointNumber++] = jointIdx;
addedJointIdxPerFace[addedNumber++] = jointIdx;
} else {
canAddToBucket = false;
}
}
}
}
}
if (!canAddToBucket) {
// Reverse operation
for (let i = 0; i < addedNumber; i++) {
bucketJointReverseMap[addedJointIdxPerFace[i]] = -1;
bucketJoints.pop();
subJointNumber--;
}
} else {
bucketTriangles.push(indices.subarray(f * 3, (f + 1) * 3));
isFaceAdded[f] = true;
rest--;
}
}
buckets.push({
triangles: bucketTriangles,
joints: bucketJoints.map(getJointByIndex),
jointReverseMap: bucketJointReverseMap
});
}
const root = new ClayNode({
name: mesh.name
});
const attribNames = geometry.getEnabledAttributes();
attribNames.splice(attribNames.indexOf('joint'), 1);
// Map from old vertex index to new vertex index
const newIndices = [];
for (let b = 0; b < buckets.length; b++) {
const bucket = buckets[b];
const jointReverseMap = bucket.jointReverseMap;
const subGeo = new Geometry();
const subMesh = new Mesh({
name: [mesh.name, b].join('-'),
// DON'T clone material.
material: material,
geometry: subGeo,
skeleton: skeleton,
joints: bucket.joints.slice()
});
let nVertex = 0;
const nVertex2 = geometry.vertexCount;
for (let i = 0; i < nVertex2; i++) {
newIndices[i] = -1;
}
// Count sub geo number
for (let f = 0; f < bucket.triangles.length; f++) {
const face = bucket.triangles[f];
for (let i = 0; i < 3; i++) {
const idx = face[i];
if (newIndices[idx] === -1) {
newIndices[idx] = nVertex;
nVertex++;
}
}
}
for (let a = 0; a < attribNames.length; a++) {
const attribName = attribNames[a];
const subAttrib = subGeo.attributes[attribName];
subAttrib.init(nVertex);
}
subGeo.attributes.joint.value = new Float32Array(nVertex * 4);
if (nVertex > 0xffff) {
subGeo.indices = new Uint32Array(bucket.triangles.length * 3);
} else {
subGeo.indices = new Uint16Array(bucket.triangles.length * 3);
}
let indicesOffset = 0;
nVertex = 0;
for (let i = 0; i < nVertex2; i++) {
newIndices[i] = -1;
}
for (let f = 0; f < bucket.triangles.length; f++) {
const triangle = bucket.triangles[f];
for (let i = 0; i < 3; i++) {
const idx = triangle[i];
if (newIndices[idx] === -1) {
newIndices[idx] = nVertex;
for (let a = 0; a < attribNames.length; a++) {
const attribName = attribNames[a];
const attrib = geometry.attributes[attribName];
const subAttrib = subGeo.attributes[attribName];
const size = attrib.size;
for (let j = 0; j < size; j++) {
subAttrib.value[nVertex * size + j] = attrib.value[idx * size + j];
}
}
for (let j = 0; j < 4; j++) {
const jointIdx = geometry.attributes.joint.value[idx * 4 + j];
const offset = nVertex * 4 + j;
if (jointIdx >= 0) {
subGeo.attributes.joint.value[offset] = jointReverseMap[jointIdx];
} else {
subGeo.attributes.joint.value[offset] = -1;
}
}
nVertex++;
}
subGeo.indices[indicesOffset++] = newIndices[idx];
}
}
subGeo.updateBoundingBox();
root.add(subMesh);
}
const children = mesh.children();
for (let i = 0; i < children.length; i++) {
root.add(children[i]);
}
root.position.copy(mesh.position);
root.rotation.copy(mesh.rotation);
root.scale.copy(mesh.scale);
if (inPlace) {
if (mesh.getParent()) {
const parent = mesh.getParent();
parent.remove(mesh);
parent.add(root);
}
}
return root;
}

View File

@ -1,31 +1,26 @@
// @ts-nocheck
// Spherical Harmonic Helpers
import Texture from '../Texture';
import FrameBuffer from '../FrameBuffer';
import Texture2D from '../Texture2D';
import Pass from '../compositor/Pass';
import vendor from '../core/vendor';
import Skybox from '../plugin/Skybox';
import Skydome from '../plugin/Skydome';
import EnvironmentMapPass from '../prePass/EnvironmentMap';
import Scene from '../Scene';
import vec3 from '../glmatrix/vec3';
import * as vec3 from '../glmatrix/vec3';
const sh = {};
import projectEnvMapShaderCode from './shader/projectEnvMap.glsl.js';
const targets = ['px', 'nx', 'py', 'ny', 'pz', 'nz'];
import CompositorFullscreenQuadPass from '../compositor/Pass';
import type Renderer from '../Renderer';
import TextureCube, { CubeTarget, cubeTargets } from '../TextureCube';
// Project on gpu, but needs browser to support readPixels as Float32Array.
function projectEnvironmentMapGPU(renderer, envMap) {
function projectEnvironmentMapGPU(renderer: Renderer, envMap: TextureCube) {
const shTexture = new Texture2D({
width: 9,
height: 1,
type: Texture.FLOAT
});
const pass = new Pass({
fragment: projectEnvMapShaderCode
});
const pass = new CompositorFullscreenQuadPass(projectEnvMapShaderCode);
pass.material.define('fragment', 'TEXTURE_SIZE', envMap.width);
pass.setUniform('environmentMap', envMap);
@ -51,29 +46,30 @@ function projectEnvironmentMapGPU(renderer, envMap) {
return coeff;
}
function harmonics(normal, index) {
function harmonics(normal: vec3.Vec3Array, index: number) {
const x = normal[0];
const y = normal[1];
const z = normal[2];
if (index === 0) {
return 1.0;
} else if (index === 1) {
return x;
} else if (index === 2) {
return y;
} else if (index === 3) {
return z;
} else if (index === 4) {
return x * z;
} else if (index === 5) {
return y * z;
} else if (index === 6) {
return x * y;
} else if (index === 7) {
return 3.0 * z * z - 1.0;
} else {
return x * x - y * y;
switch (index) {
case 0:
return 1.0;
case 1:
return x;
case 2:
return y;
case 3:
return z;
case 4:
return x * z;
case 5:
return y * z;
case 6:
return x * y;
case 7:
return 3.0 * z * z - 1.0;
default:
return x * x - y * y;
}
}
@ -87,20 +83,25 @@ const normalTransform = {
};
// Project on cpu.
function projectEnvironmentMapCPU(renderer, cubePixels, width, height) {
function projectEnvironmentMapCPU(
renderer: Renderer,
cubePixels: Record<CubeTarget, Uint8Array>,
width: number,
height: number
) {
const coeff = new Float32Array(9 * 3);
const normal = vec3.create();
const texel = vec3.create();
const fetchNormal = vec3.create();
for (let m = 0; m < 9; m++) {
const result = vec3.create();
for (let k = 0; k < targets.length; k++) {
const pixels = cubePixels[targets[k]];
for (let k = 0; k < cubeTargets.length; k++) {
const pixels = cubePixels[cubeTargets[k]];
const sideResult = vec3.create();
let divider = 0;
let i = 0;
const transform = normalTransform[targets[k]];
const transform = normalTransform[cubeTargets[k]];
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
normal[0] = (x / (width - 1.0)) * 2.0 - 1.0;
@ -137,14 +138,14 @@ function projectEnvironmentMapCPU(renderer, cubePixels, width, height) {
return coeff;
}
/**
* @param {clay.Renderer} renderer
* @param {clay.Texture} envMap
* @param {Object} [textureOpts]
* @param {Object} [textureOpts.lod]
* @param {boolean} [textureOpts.decodeRGBM]
*/
sh.projectEnvironmentMap = function (renderer, envMap, opts) {
export function projectEnvironmentMap(
renderer: Renderer,
envMap: Texture2D | TextureCube,
opts?: {
decodeRGBM?: boolean;
lod?: number;
}
) {
// TODO sRGB
opts = opts || {};
@ -154,7 +155,7 @@ sh.projectEnvironmentMap = function (renderer, envMap, opts) {
const dummyScene = new Scene();
let size = 64;
if (envMap.textureType === 'texture2D') {
skybox = new Skydome({
skybox = new Skybox({
scene: dummyScene,
environmentMap: envMap
});
@ -178,13 +179,11 @@ sh.projectEnvironmentMap = function (renderer, envMap, opts) {
skybox.material.define('fragment', 'RGBM_DECODE');
}
skybox.material.set('lod', opts.lod);
const envMapPass = new EnvironmentMapPass({
texture: rgbmTexture
});
const cubePixels = {};
for (let i = 0; i < targets.length; i++) {
cubePixels[targets[i]] = new Uint8Array(width * height * 4);
const camera = envMapPass.getCamera(targets[i]);
const envMapPass = new EnvironmentMapPass();
const cubePixels = {} as Record<CubeTarget, Uint8Array>;
for (let i = 0; i < cubeTargets.length; i++) {
cubePixels[cubeTargets[i]] = new Uint8Array(width * height * 4);
const camera = envMapPass.getCamera(cubeTargets[i]);
camera.fov = 90;
framebuffer.attach(rgbmTexture);
framebuffer.bind(renderer);
@ -196,7 +195,7 @@ sh.projectEnvironmentMap = function (renderer, envMap, opts) {
height,
Texture.RGBA,
Texture.UNSIGNED_BYTE,
cubePixels[targets[i]]
cubePixels[cubeTargets[i]]
);
framebuffer.unbind(renderer);
}
@ -206,6 +205,4 @@ sh.projectEnvironmentMap = function (renderer, envMap, opts) {
rgbmTexture.dispose(renderer);
return projectEnvironmentMapCPU(renderer, cubePixels, width, height);
};
export default sh;
}

View File

@ -1,6 +1,11 @@
// @ts-nocheck
import * as util from '../core/util';
import Geometry from '../Geometry';
import GeometryBase, {
AttributeSize,
AttributeType,
AttributeValue,
GeometryAttribute
} from '../GeometryBase';
import BoundingBox from '../math/BoundingBox';
import Vector3 from '../math/Vector3';
@ -10,113 +15,120 @@ const META = {
generator: 'util.transferable.toObject'
};
interface DataObject {
meta: typeof META;
dynamic: boolean;
boundingBox: {
min: number[];
max: number[];
};
indices?: Uint16Array | Uint32Array;
attributes: Record<
string,
{
name: string;
type: AttributeType;
size: AttributeSize;
semantic?: string;
value: AttributeValue;
}
>;
}
/**
* @alias clay.util.transferable
* Convert geometry to a object containing transferable data
* @param {Geometry} geometry geometry
* @param {Boolean} shallow whether shallow copy
* @returns {Object} { data : data, buffers : buffers }, buffers is the transferable list
*/
const transferableUtil = {
/**
* Convert geometry to a object containing transferable data
* @param {Geometry} geometry geometry
* @param {Boolean} shallow whether shallow copy
* @returns {Object} { data : data, buffers : buffers }, buffers is the transferable list
*/
toObject: function (geometry, shallow) {
if (!geometry) {
return null;
}
const data = {
metadata: Object.assign({}, META)
export function toObject(geometry: GeometryBase, shallow?: boolean) {
const data = {
meta: Object.assign({}, META)
} as DataObject;
//transferable buffers
const buffers: ArrayBuffer[] = [];
//dynamic
data.dynamic = geometry.dynamic;
//bounding box
if (geometry.boundingBox) {
data.boundingBox = {
min: geometry.boundingBox.min.toArray(),
max: geometry.boundingBox.max.toArray()
};
//transferable buffers
const buffers = [];
//dynamic
data.dynamic = geometry.dynamic;
//bounding box
if (geometry.boundingBox) {
data.boundingBox = {
min: geometry.boundingBox.min.toArray(),
max: geometry.boundingBox.max.toArray()
};
}
//indices
if (geometry.indices && geometry.indices.length > 0) {
data.indices = copyIfNecessary(geometry.indices, shallow);
buffers.push(data.indices.buffer);
}
//attributes
data.attributes = {};
for (const p in geometry.attributes) {
if (util.hasOwn(geometry.attributes, p)) {
let attr = geometry.attributes[p];
//ignore empty attributes
if (attr && attr.value && attr.value.length > 0) {
attr = data.attributes[p] = copyAttribute(attr, shallow);
buffers.push(attr.value.buffer);
}
}
}
return {
data: data,
buffers: buffers
};
},
/**
* Reproduce a geometry from object generated by toObject
* @param {Object} object object generated by toObject
* @returns {Geometry} geometry
*/
toGeometry: function (object) {
if (!object) {
return null;
}
if (object.data && object.buffers) {
return transferableUtil.toGeometry(object.data);
}
if (!object.metadata || object.metadata.generator !== META.generator) {
throw new Error(
'[util.transferable.toGeometry] the object is not generated by util.transferable.'
);
}
//basic options
const options = {
dynamic: object.dynamic,
indices: object.indices
};
if (object.boundingBox) {
const min = new Vector3().setArray(object.boundingBox.min);
const max = new Vector3().setArray(object.boundingBox.max);
options.boundingBox = new BoundingBox(min, max);
}
const geometry = new Geometry(options);
//attributes
for (const p in object.attributes) {
if (util.hasOwn(object.attributes, p)) {
const attr = object.attributes[p];
geometry.attributes[p] = new Geometry.Attribute(
attr.name,
attr.type,
attr.size,
attr.semantic
);
geometry.attributes[p].value = attr.value;
}
}
return geometry;
}
};
function copyAttribute(attr, shallow) {
//indices
if (geometry.indices && geometry.indices.length > 0) {
data.indices = copyIfNecessary(geometry.indices, shallow);
buffers.push(data.indices!.buffer);
}
//attributes
const geoAttributes = geometry.attributes;
const dataAttributes = (data.attributes = {} as DataObject['attributes']);
for (const p in geoAttributes) {
if (util.hasOwn(geoAttributes, p)) {
const attr = geoAttributes[p];
//ignore empty attributes
if (attr && attr.value && attr.value.length > 0) {
dataAttributes[p] = copyAttribute(attr, shallow);
buffers.push((dataAttributes[p].value as Float32Array).buffer);
}
}
}
return {
data,
buffers
};
}
/**
* Reproduce a geometry from object generated by toObject
* @param {Object} object object generated by toObject
* @returns {Geometry} geometry
*/
export function toGeometry(object: DataObject) {
if (!object.meta || object.meta.generator !== META.generator) {
throw new Error(
'[util.transferable.toGeometry] the object is not generated by util.transferable.'
);
}
//basic options
const options = {
dynamic: object.dynamic,
indices: object.indices
};
const geometry = new Geometry(options);
const bbox = object.boundingBox;
if (bbox) {
const min = new Vector3().setArray(bbox.min);
const max = new Vector3().setArray(bbox.max);
geometry.boundingBox = new BoundingBox(min, max);
}
//attributes
for (const p in object.attributes) {
if (util.hasOwn(object.attributes, p)) {
const attrObj = object.attributes[p];
const attr = geometry.createAttribute(
attrObj.name,
attrObj.type,
attrObj.size,
attrObj.semantic
);
attr.value = attrObj.value;
}
}
return geometry;
}
function copyAttribute(attr: GeometryAttribute, shallow?: boolean) {
return {
name: attr.name,
type: attr.type,
@ -126,12 +138,6 @@ function copyAttribute(attr, shallow) {
};
}
function copyIfNecessary(arr, shallow) {
if (!shallow) {
return new arr.constructor(arr);
} else {
return arr;
}
function copyIfNecessary(arr: any, shallow?: boolean) {
return !shallow ? new arr.constructor(arr) : arr;
}
export default transferableUtil;