Comparing version 0.2.2 to 0.2.3
@@ -5,3 +5,8 @@ import { | ||
VectorKeyframeTrack, | ||
KeyframeClip | ||
KeyframeClip, | ||
StepInterpolant, | ||
LinearInterpolant, | ||
CubicSplineInterpolant, | ||
QuaternionLinearInterpolant, | ||
QuaternionCubicSplineInterpolant | ||
} from 't3d'; | ||
@@ -84,4 +89,4 @@ import { GLTFUtils } from '../GLTFUtils.js'; | ||
for (let j = 0, jl = targetNodes.length; j < jl; j++) { | ||
// TODO interpolation | ||
const track = new TypedKeyframeTrack(targetNodes[j], PATH_PROPERTIES[target.path], input, output); | ||
const interpolant = getInterpolant(gltfSampler.interpolation, TypedKeyframeTrack === QuaternionKeyframeTrack); | ||
const track = new TypedKeyframeTrack(targetNodes[j], PATH_PROPERTIES[target.path], input, output, interpolant); | ||
tracks.push(track); | ||
@@ -103,2 +108,14 @@ } | ||
} | ||
function getInterpolant(type, quaternion) { | ||
switch (type) { | ||
case 'STEP': | ||
return StepInterpolant; | ||
case 'CUBICSPLINE': | ||
return quaternion ? QuaternionCubicSplineInterpolant : CubicSplineInterpolant; | ||
case 'LINEAR': | ||
default: | ||
return quaternion ? QuaternionLinearInterpolant : LinearInterpolant; | ||
} | ||
} |
@@ -1,176 +0,132 @@ | ||
import { Vector3, Box3, Triangle } from 't3d'; | ||
import { Vector3, Box3 } from 't3d'; | ||
class Octree { | ||
static generateOctreeFromNode(node, maxDepth = 5) { | ||
const triangles = [], box = new Box3(); | ||
constructor(box = new Box3(), depth = 0) { | ||
this.box = box; | ||
this.depth = depth; | ||
function addTriangle(triangle) { | ||
box.min.x = Math.min(box.min.x, triangle.a.x, triangle.b.x, triangle.c.x); | ||
box.min.y = Math.min(box.min.y, triangle.a.y, triangle.b.y, triangle.c.y); | ||
box.min.z = Math.min(box.min.z, triangle.a.z, triangle.b.z, triangle.c.z); | ||
box.max.x = Math.max(box.max.x, triangle.a.x, triangle.b.x, triangle.c.x); | ||
box.max.y = Math.max(box.max.y, triangle.a.y, triangle.b.y, triangle.c.y); | ||
box.max.z = Math.max(box.max.z, triangle.a.z, triangle.b.z, triangle.c.z); | ||
this.subTrees = []; | ||
triangles.push(triangle); | ||
} | ||
this.elements = []; | ||
node.traverse(child => { | ||
if (child.isMesh) { | ||
const geometry = child.geometry; | ||
const isIndexed = !!geometry.index; | ||
this.elementTest = function(box, element) { | ||
return box.containsPoint(element); | ||
}; | ||
} | ||
const positionBuffer = geometry.getAttribute('a_Position').buffer; | ||
const positionArray = positionBuffer.array; | ||
isEmpty() { | ||
return this.elements.length === 0 && this.subTrees.length === 0; | ||
} | ||
if (isIndexed) { | ||
const indexArray = geometry.index.buffer.array; | ||
for (let i = 0; i < indexArray.length; i += 3) { | ||
const a = indexArray[i]; | ||
const b = indexArray[i + 1]; | ||
const c = indexArray[i + 2]; | ||
divideElements(maxDepth = 5, capacity = 8) { | ||
const { depth, subTrees, elements, elementTest } = this; | ||
const v1 = new Vector3().fromArray(positionArray, a * 3); | ||
const v2 = new Vector3().fromArray(positionArray, b * 3); | ||
const v3 = new Vector3().fromArray(positionArray, c * 3); | ||
if (depth >= maxDepth || elements.length <= capacity) return; | ||
v1.applyMatrix4(child.worldMatrix); | ||
v2.applyMatrix4(child.worldMatrix); | ||
v3.applyMatrix4(child.worldMatrix); | ||
this.subdivide(); | ||
const triangle = new Triangle(v1, v2, v3); | ||
triangle._belong = child; | ||
addTriangle(triangle); | ||
} | ||
} else { | ||
for (let i = 0; i < positionBuffer.count; i += 3) { | ||
const v1 = new Vector3().fromArray(positionArray, i * 3); | ||
const v2 = new Vector3().fromArray(positionArray, (i + 1) * 3); | ||
const v3 = new Vector3().fromArray(positionArray, (i + 2) * 3); | ||
v1.applyMatrix4(child.worldMatrix); | ||
v2.applyMatrix4(child.worldMatrix); | ||
v3.applyMatrix4(child.worldMatrix); | ||
const triangle = new Triangle(v1, v2, v3); | ||
triangle._belong = child; | ||
addTriangle(triangle); | ||
} | ||
// distribute elements to subTrees | ||
let element = this.elements.pop(); | ||
while (element) { | ||
for (let i = 0; i < subTrees.length; i++) { | ||
if (elementTest(subTrees[i].box, element)) { | ||
subTrees[i].elements.push(element); | ||
} | ||
} | ||
}); | ||
element = this.elements.pop(); | ||
} | ||
// offset small amount to account for regular grid | ||
box.min.x -= 0.01; | ||
box.min.y -= 0.01; | ||
box.min.z -= 0.01; | ||
const octree = new Octree(box, maxDepth); | ||
octree.triangles = triangles; | ||
return octree.split(0); | ||
// recursive call | ||
subTrees.forEach(subTree => subTree.divideElements(maxDepth, capacity)); | ||
} | ||
constructor(box, maxDepth = 5) { | ||
this.box = box; | ||
this.maxDepth = maxDepth; | ||
addElement(element, maxDepth = 5, capacity = 8) { | ||
const { box, depth, subTrees, elements, elementTest } = this; | ||
this.triangles = []; | ||
this.subTrees = []; | ||
} | ||
if (!elementTest(box, element)) { | ||
return false; | ||
} | ||
split(level) { | ||
const subTrees = []; | ||
const halfSize = vec_0.copy(this.box.max).sub(this.box.min).multiplyScalar(0.5); | ||
if (subTrees.length === 0) { | ||
elements.push(element); | ||
for (let x = 0; x < 2; x++) { | ||
for (let y = 0; y < 2; y++) { | ||
for (let z = 0; z < 2; z++) { | ||
const box = new Box3(); | ||
const v = vec_1.set(x, y, z); | ||
if (elements.length > capacity && depth < maxDepth) { | ||
this.divideElements(maxDepth, capacity); | ||
} | ||
box.min.copy(this.box.min).add(v.multiply(halfSize)); | ||
box.max.copy(box.min).add(halfSize); | ||
return true; | ||
} | ||
subTrees.push(new Octree(box, this.maxDepth)); | ||
} | ||
for (let i = 0; i < subTrees.length; i++) { | ||
if (subTrees[i].addElement(element, maxDepth, capacity)) { | ||
return true; | ||
} | ||
} | ||
} | ||
let triangle = this.triangles.pop(); | ||
removeElement(element) { | ||
const elements = this.elements; | ||
const index = elements.indexOf(element); | ||
while (triangle) { | ||
for (let i = 0; i < subTrees.length; i++) { | ||
if (subTrees[i].box.intersectsTriangle(triangle)) { | ||
subTrees[i].triangles.push(triangle); | ||
} | ||
} | ||
triangle = this.triangles.pop(); | ||
if (index !== -1) { | ||
elements.splice(index, 1); | ||
return true; | ||
} | ||
const subTrees = this.subTrees; | ||
for (let i = 0; i < subTrees.length; i++) { | ||
const len = subTrees[i].triangles.length; | ||
if (len > 8 && level < this.maxDepth) { | ||
subTrees[i].split(level + 1); | ||
if (subTrees[i].removeElement(element)) { | ||
return true; | ||
} | ||
if (len !== 0) { | ||
this.subTrees.push(subTrees[i]); | ||
} | ||
} | ||
return this; | ||
return false; | ||
} | ||
getRayTriangles(ray, triangles) { | ||
for (let i = 0; i < this.subTrees.length; i++) { | ||
const subTree = this.subTrees[i]; | ||
if (!ray.intersectsBox(subTree.box)) continue; | ||
subdivide() { | ||
const halfSize = _vec3_1.copy(this.box.max).sub(this.box.min).multiplyScalar(0.5); | ||
_subdivideArray.forEach((v, i) => { | ||
const box = new Box3(); | ||
if (subTree.triangles.length > 0) { | ||
for (let j = 0; j < subTree.triangles.length; j++) { | ||
if (triangles.indexOf(subTree.triangles[j]) === -1) triangles.push(subTree.triangles[j]); | ||
} | ||
} else { | ||
subTree.getRayTriangles(ray, triangles); | ||
} | ||
} | ||
box.min.copy(this.box.min).add(_vec3_2.copy(v).multiply(halfSize)); | ||
box.max.copy(box.min).add(halfSize); | ||
return triangles; | ||
this.subTrees[i] = new this.constructor(box, this.depth + 1); | ||
}); | ||
} | ||
rayIntersect(ray) { | ||
if (ray.direction.getLength() === 0) return; | ||
count() { | ||
let count = 1; | ||
const triangles = []; | ||
let triangle, position, distance = 1e100; | ||
this.getRayTriangles(ray, triangles); | ||
for (let i = 0; i < triangles.length; i++) { | ||
const result = ray.intersectTriangle(triangles[i].a, triangles[i].b, triangles[i].c, true, vec_1); | ||
if (result) { | ||
const tempDistnce = result.sub(ray.origin).getLength(); | ||
if (distance > tempDistnce) { | ||
position = result.clone().add(ray.origin); | ||
distance = tempDistnce; | ||
triangle = triangles[i]; | ||
} | ||
} | ||
for (let i = 0; i < this.subTrees.length; i++) { | ||
count += this.subTrees[i].count(); | ||
} | ||
return distance < 1e100 ? { distance: distance, triangle: triangle, position: position, target: triangle.belong } : null; | ||
return count; | ||
} | ||
dispose() { | ||
this.subTrees.forEach(subTree => subTree.dispose()); | ||
this.subTrees.length = 0; | ||
this.elements.length = 0; | ||
} | ||
} | ||
const vec_0 = new Vector3(); | ||
const vec_1 = new Vector3(); | ||
const _subdivideArray = [ | ||
new Vector3(0, 0, 0), | ||
new Vector3(0, 0, 1), | ||
new Vector3(0, 1, 0), | ||
new Vector3(0, 1, 1), | ||
new Vector3(1, 0, 0), | ||
new Vector3(1, 0, 1), | ||
new Vector3(1, 1, 0), | ||
new Vector3(1, 1, 1) | ||
]; | ||
const _vec3_1 = new Vector3(); | ||
const _vec3_2 = new Vector3(); | ||
export { Octree }; |
@@ -110,5 +110,3 @@ import { Vector3 } from 't3d'; | ||
// Reset portal | ||
portalLeft = portalApex; | ||
portalRight = portalApex; | ||
leftIndex = apexIndex; | ||
rightIndex = apexIndex; | ||
@@ -135,5 +133,3 @@ // Restart scan | ||
portalLeft = portalApex; | ||
portalRight = portalApex; | ||
leftIndex = apexIndex; | ||
rightIndex = apexIndex; | ||
// Restart scan | ||
@@ -140,0 +136,0 @@ i = apexIndex; |
@@ -7,5 +7,5 @@ import { | ||
Mesh, | ||
Box3, | ||
Matrix4 | ||
Box3 | ||
} from 't3d'; | ||
import { SceneUtils } from '../SceneUtils.js'; | ||
@@ -39,3 +39,3 @@ class BoxHelper extends Mesh { | ||
if (this.object !== undefined) { | ||
setFromObject(_box3_1, this.object); | ||
SceneUtils.setBox3FromObject(this.object, this.object, _box3_1); | ||
} | ||
@@ -99,52 +99,3 @@ | ||
const _box3_1 = new Box3(); | ||
const _box3_2 = new Box3(); | ||
const _mat4_1 = new Matrix4(); | ||
function setFromObject(box, object) { | ||
box.makeEmpty(); | ||
return expandByObject(box, object, object); | ||
} | ||
function expandByObject(box, object, root) { | ||
const geometry = object.geometry; | ||
if (geometry !== undefined) { | ||
geometry.computeBoundingBox(); | ||
getMatrixFromRoot(object, root, _mat4_1); | ||
_box3_2.copy(geometry.boundingBox); | ||
_box3_2.applyMatrix4(_mat4_1); | ||
box.expandByBox3(_box3_2); | ||
} | ||
const children = object.children; | ||
for (let i = 0, l = children.length; i < l; i++) { | ||
expandByObject(box, children[i], root); | ||
} | ||
return this; | ||
} | ||
function getMatrixFromRoot(node, root, target) { | ||
target.identity(); | ||
let tempNode = node; | ||
while (tempNode !== root && tempNode !== null) { | ||
if (tempNode.matrixAutoUpdate || tempNode.matrixNeedsUpdate) { | ||
tempNode.updateMatrix(); | ||
} | ||
target.premultiply(tempNode.matrix); | ||
tempNode = tempNode.parent; | ||
} | ||
return target; | ||
} | ||
export { BoxHelper }; |
@@ -10,5 +10,7 @@ import { Mesh, Geometry, BasicMaterial, DRAW_MODE, BLEND_TYPE, Attribute, Buffer } from 't3d'; | ||
material.drawMode = DRAW_MODE.LINES; | ||
material.envMap = undefined; | ||
material.transparent = true; | ||
material.blending = BLEND_TYPE.ADD; | ||
material.diffuse.setHex(color); | ||
material.envMap = undefined; | ||
material.fog = false; | ||
@@ -23,2 +25,4 @@ super(geometry, material); | ||
for (let i = 0; i < tree.length; i++) { | ||
if (tree[i].isEmpty()) continue; | ||
const min = tree[i].box.min; | ||
@@ -25,0 +29,0 @@ const max = tree[i].box.max; |
import { | ||
Attribute, | ||
Buffer, | ||
Color3, | ||
Geometry, | ||
LineMaterial, | ||
Matrix4, | ||
Mesh, | ||
VERTEX_COLOR, | ||
Object3D, | ||
SphereGeometry, | ||
ShaderMaterial, | ||
Vector3 | ||
} from 't3d'; | ||
class SkeletonHelper extends Mesh { | ||
class SkeletonHelper extends Object3D { | ||
constructor(object) { | ||
super(); | ||
const bones = getBoneList(object); | ||
const geometry = new Geometry(); | ||
this.root = object; | ||
this.bones = bones; | ||
const vertices = []; | ||
const colors = []; | ||
this._midStep = 0.25; | ||
const color1 = new Color3(0, 0, 1); | ||
const color2 = new Color3(0, 1, 0); | ||
this.midWidthScale = 0.1; | ||
this.ballScale = 0.4; | ||
// create bone spheres and connectors | ||
const material = new ShaderMaterial(skeletonShader); | ||
material.depthTest = false; | ||
material.depthWrite = false; | ||
this._material = material; | ||
this._sphereGeometry = new SphereGeometry(0.25, 16, 12); | ||
this._connectorGeometry = new ConnectorGeometry(); | ||
this._distanceMap = new WeakMap(); | ||
for (let i = 0; i < bones.length; i++) { | ||
@@ -30,33 +44,51 @@ const bone = bones[i]; | ||
if (bone.parent && bone.parent.isBone) { | ||
vertices.push(0, 0, 0); | ||
vertices.push(0, 0, 0); | ||
colors.push(color1.r, color1.g, color1.b, 1); | ||
colors.push(color2.r, color2.g, color2.b, 1); | ||
const boneSphere = new Mesh(this._sphereGeometry, this._material); | ||
boneSphere.receiveShadows = false; | ||
boneSphere.castShadows = false; | ||
boneSphere.renderOrder = 1; | ||
this.add(boneSphere); | ||
const boneConnector = new Mesh(this._connectorGeometry, this._material); | ||
boneConnector.receiveShadows = false; | ||
boneConnector.castShadows = false; | ||
this.add(boneConnector); | ||
this._distanceMap.set(bone.parent, 0); | ||
} | ||
} | ||
} | ||
geometry.addAttribute('a_Position', new Attribute(new Buffer(new Float32Array(vertices), 3))); | ||
geometry.addAttribute('a_Color', new Attribute(new Buffer(new Float32Array(colors), 4))); | ||
set midStep(value) { | ||
this._midStep = value; | ||
this._connectorGeometry.updateMidStep(value); | ||
} | ||
const material = new LineMaterial(); | ||
material.vertexColors = VERTEX_COLOR.RGBA; | ||
material.depthTest = false; | ||
material.depthWrite = false; | ||
material.transparent = true; | ||
get midStep() { | ||
return this._midStep; | ||
} | ||
super(geometry, material); | ||
set colorMin(value) { | ||
this._material.uniforms.u_colorMin = value; | ||
} | ||
this.frustumCulled = false; | ||
get colorMin() { | ||
return this._material.uniforms.u_colorMin; | ||
} | ||
this.root = object; | ||
this.bones = bones; | ||
set colorMax(value) { | ||
this._material.uniforms.u_colorMax = value; | ||
} | ||
get colorMax() { | ||
return this._material.uniforms.u_colorMax; | ||
} | ||
updateMatrix(force) { | ||
const bones = this.bones; | ||
const midWidthScale = this.midWidthScale; | ||
const ballScale = this.ballScale; | ||
const geometry = this.geometry; | ||
const position = geometry.getAttribute('a_Position'); | ||
const distanceMap = this._distanceMap; | ||
worldMatrixInv.getInverse(this.root.worldMatrix); | ||
_worldMatrixInv.getInverse(this.root.worldMatrix); | ||
@@ -67,16 +99,29 @@ for (let i = 0, j = 0; i < bones.length; i++) { | ||
if (bone.parent && bone.parent.isBone) { | ||
boneMatrix.multiplyMatrices(worldMatrixInv, bone.worldMatrix); | ||
vector.setFromMatrixPosition(boneMatrix); | ||
_boneEndMatrix.multiplyMatrices(_worldMatrixInv, bone.worldMatrix); | ||
_boneStartMatrix.multiplyMatrices(_worldMatrixInv, bone.parent.worldMatrix); | ||
_boneEnd.setFromMatrixPosition(_boneEndMatrix); | ||
_boneStart.setFromMatrixPosition(_boneStartMatrix); | ||
position.buffer.array[j * position.size + 0] = vector.x; | ||
position.buffer.array[j * position.size + 1] = vector.y; | ||
position.buffer.array[j * position.size + 2] = vector.z; | ||
_boneDirection.subVectors(_boneEnd, _boneStart); | ||
const boneDistance = _boneDirection.getLength(); | ||
_boneDirection.normalize(); | ||
boneMatrix.multiplyMatrices(worldMatrixInv, bone.parent.worldMatrix); | ||
vector.setFromMatrixPosition(boneMatrix); | ||
bone._distance = boneDistance; | ||
distanceMap.set(bone, boneDistance); | ||
position.buffer.array[(j + 1) * position.size + 0] = vector.x; | ||
position.buffer.array[(j + 1) * position.size + 1] = vector.y; | ||
position.buffer.array[(j + 1) * position.size + 2] = vector.z; | ||
let minDistance = boneDistance; | ||
const parentDistance = distanceMap.get(bone.parent); | ||
if (parentDistance) { | ||
minDistance = Math.min(minDistance, parentDistance); | ||
} | ||
const sphere = this.children[j]; | ||
sphere.position.copy(_boneStart); | ||
sphere.scale.set(1, 1, 1).multiplyScalar(minDistance * ballScale); | ||
const connector = this.children[j + 1]; | ||
connector.position.copy(_boneStart); | ||
connector.scale.set(midWidthScale, midWidthScale, 1).multiplyScalar(boneDistance); | ||
connector.quaternion.setFromUnitVectors(new Vector3(0, 0, -1), _boneDirection); | ||
j += 2; | ||
@@ -86,4 +131,2 @@ } | ||
position.buffer.version++; | ||
super.updateMatrix(force); | ||
@@ -94,5 +137,8 @@ } | ||
const vector = new Vector3(); | ||
const boneMatrix = new Matrix4(); | ||
const worldMatrixInv = new Matrix4(); | ||
const _worldMatrixInv = new Matrix4(); | ||
const _boneStartMatrix = new Matrix4(); | ||
const _boneEndMatrix = new Matrix4(); | ||
const _boneStart = new Vector3(); | ||
const _boneEnd = new Vector3(); | ||
const _boneDirection = new Vector3(); | ||
@@ -113,2 +159,105 @@ function getBoneList(object) { | ||
class ConnectorGeometry extends Geometry { | ||
constructor(midStep = 0.25) { | ||
super(); | ||
const vertexs = [ | ||
new Vector3(0, 0, 0), | ||
new Vector3(1, -1, -midStep), | ||
new Vector3(1, 1, -midStep), | ||
new Vector3(-1, 1, -midStep), | ||
new Vector3(-1, -1, -midStep), | ||
new Vector3(1, -1, -midStep), | ||
new Vector3(0, 0, -1) | ||
]; | ||
const normal = [ | ||
new Vector3(1, 0, 1), | ||
new Vector3(0, 1, 1), | ||
new Vector3(-1, 0, 1), | ||
new Vector3(0, -1, 1), | ||
new Vector3(1, 0, -1), | ||
new Vector3(0, 1, -1), | ||
new Vector3(-1, 0, -1), | ||
new Vector3(0, -1, -1) | ||
]; | ||
const positions = []; | ||
const normals = []; | ||
for (let i = 0; i < 4; i++) { | ||
positions.push(vertexs[0].x, vertexs[0].y, vertexs[0].z); | ||
positions.push(vertexs[i + 1].x, vertexs[i + 1].y, vertexs[i + 1].z); | ||
positions.push(vertexs[i + 2].x, vertexs[i + 2].y, vertexs[i + 2].z); | ||
normals.push(normal[i].x, normal[i].y, normal[i].z); | ||
normals.push(normal[i].x, normal[i].y, normal[i].z); | ||
normals.push(normal[i].x, normal[i].y, normal[i].z); | ||
positions.push(vertexs[6].x, vertexs[6].y, vertexs[6].z); | ||
positions.push(vertexs[i + 2].x, vertexs[i + 2].y, vertexs[i + 2].z); | ||
positions.push(vertexs[i + 1].x, vertexs[i + 1].y, vertexs[i + 1].z); | ||
normals.push(normal[i + 4].x, normal[i + 4].y, normal[i + 4].z); | ||
normals.push(normal[i + 4].x, normal[i + 4].y, normal[i + 4].z); | ||
normals.push(normal[i + 4].x, normal[i + 4].y, normal[i + 4].z); | ||
} | ||
this.addAttribute('a_Position', new Attribute(new Buffer(new Float32Array(positions), 3))); | ||
this.addAttribute('a_Normal', new Attribute(new Buffer(new Float32Array(normals), 3))); | ||
this.computeBoundingSphere(); | ||
this.computeBoundingBox(); | ||
} | ||
updateMidStep(midStep) { | ||
const positions = this.getAttribute('a_Position').buffer.array; | ||
for (let i = 0; i < 8; i++) { | ||
positions[i * 9 + 5] = -midStep; | ||
positions[i * 9 + 8] = -midStep; | ||
} | ||
this.getAttribute('a_Position').buffer.version++; | ||
// boundings are not needed to be updated | ||
} | ||
} | ||
const skeletonShader = { | ||
name: 'skeleton_shader', | ||
uniforms: { | ||
'u_colorMin': [0.35, 0.35, 0.35], | ||
'u_colorMax': [0.7, 0.7, 0.7] | ||
}, | ||
vertexShader: ` | ||
#include <common_vert> | ||
varying vec3 v_Normal; | ||
void main() { | ||
v_Normal = (transposeMat4(inverseMat4(u_Model)) * vec4(a_Normal, 0.0)).xyz; | ||
gl_Position = u_Projection * u_View * u_Model * vec4(a_Position, 1.0); | ||
} | ||
`, | ||
fragmentShader: ` | ||
#include <common_frag> | ||
uniform vec3 u_colorMin; | ||
uniform vec3 u_colorMax; | ||
varying vec3 v_Normal; | ||
void main() { | ||
vec3 N = normalize(v_Normal); | ||
float ndl = dot(N, vec3(0, 1, 0)) * 0.5 + 0.5; | ||
vec3 diffuse = mix(u_colorMin, u_colorMax, ndl); | ||
gl_FragColor = vec4(diffuse, 1.0); | ||
} | ||
` | ||
}; | ||
export { SkeletonHelper }; |
import { Texture2D, TEXTURE_FILTER } from 't3d'; | ||
// TODO Optimize: Avoid canvas creation, use Data buffer instead. | ||
class GradientTextureGenerator { | ||
constructor(width = 256, height = 1) { | ||
const canvas = document.createElement('canvas'); | ||
canvas.width = width; | ||
canvas.height = height; | ||
const context = canvas.getContext('2d'); | ||
constructor(width = 256) { | ||
const texture = new Texture2D(); | ||
texture.image = { data: null, width: width, height: 1 }; | ||
texture.magFilter = texture.minFilter = TEXTURE_FILTER.LINEAR; | ||
texture.generateMipmaps = false; | ||
texture.image = canvas; | ||
this._canvas = canvas; | ||
this._context = context; | ||
this._texture = texture; | ||
} | ||
gradient(gradientData) { | ||
const width = this._canvas.width; | ||
const height = this._canvas.height; | ||
const context = this._context; | ||
gradient(colorGradient) { | ||
const texture = this._texture; | ||
const gradient = context.createLinearGradient(0, 0, width, 0); | ||
for (const i in gradientData) { | ||
gradient.addColorStop(+i, gradientData[i]); | ||
} | ||
context.fillStyle = gradient; | ||
context.fillRect(0, 0, width, height); | ||
texture.image.data = colorGradient.getUint8Array(texture.image.width); | ||
texture.version++; | ||
return this; | ||
@@ -38,0 +18,0 @@ } |
{ | ||
"name": "t3d", | ||
"version": "0.2.2", | ||
"version": "0.2.3", | ||
"description": "t3d.js is a web-first, light weight, extendable 3D rendering library.", | ||
@@ -54,3 +54,3 @@ "type": "module", | ||
"jsdoc": "^4.0.2", | ||
"rollup": "^3.18.0", | ||
"rollup": "^4.9.1", | ||
"servez": "^2.1.1" | ||
@@ -57,0 +57,0 @@ }, |
@@ -0,1 +1,3 @@ | ||
import { LinearInterpolant, StepInterpolant } from './KeyframeInterpolants.js'; | ||
/** | ||
@@ -13,5 +15,5 @@ * Base class for property track. | ||
* @param {Array} values | ||
* @param {Boolean} [interpolant=true] | ||
* @param {t3d.KeyframeInterpolant.constructor} [interpolant=t3d.LinearInterpolant] | ||
*/ | ||
constructor(target, propertyPath, times, values, interpolant = true) { | ||
constructor(target, propertyPath, times, values, interpolant = LinearInterpolant) { | ||
this.target = target; | ||
@@ -25,18 +27,45 @@ this.propertyPath = propertyPath; | ||
this.valueSize = values.length / times.length; | ||
this.valueSize = 0; | ||
this.interpolant = null; | ||
// since 0.2.2, remove this after few versions later | ||
if (interpolant === true) { | ||
interpolant = LinearInterpolant; | ||
} else if (interpolant === false) { | ||
interpolant = StepInterpolant; | ||
} | ||
this.setInterpolant(interpolant); | ||
} | ||
/** | ||
* Set interpolant for this keyframe track. | ||
* @param {t3d.KeyframeInterpolant.constructor} interpolant | ||
* @return {t3d.KeyframeTrack} | ||
*/ | ||
setInterpolant(interpolant) { | ||
this.valueSize = interpolant.getValueSize.call(this); | ||
this.interpolant = interpolant; | ||
return this; | ||
} | ||
/** | ||
* Get value at time. | ||
* The value will be interpolated by interpolant if time is between keyframes. | ||
* @param {Number} t - time | ||
* @param {Array} outBuffer - output buffer | ||
* @return {Array} output buffer | ||
*/ | ||
getValue(t, outBuffer) { | ||
const times = this.times, | ||
const interpolant = this.interpolant, | ||
times = this.times, | ||
tl = times.length; | ||
if (t <= times[0]) { | ||
return this._copyValue(0, outBuffer); | ||
return interpolant.copyValue.call(this, 0, outBuffer); | ||
} else if (t >= times[tl - 1]) { | ||
return this._copyValue(tl - 1, outBuffer); | ||
return interpolant.copyValue.call(this, tl - 1, outBuffer); | ||
} | ||
// TODO optimize | ||
// TODO use index cache for better performance | ||
// https://github.com/mrdoob/three.js/blob/dev/src/math/Interpolant.js | ||
@@ -48,44 +77,9 @@ let i0 = tl - 1; | ||
const ratio = (t - times[i0]) / (times[i0 + 1] - times[i0]); | ||
return this._interpolate(i0, ratio, outBuffer); | ||
const duration = times[i0 + 1] - times[i0]; | ||
const ratio = (t - times[i0]) / duration; | ||
return interpolant.interpolate.call(this, i0, ratio, duration, outBuffer); | ||
} | ||
_interpolate(index0, ratio, outBuffer) { | ||
const values = this.values, | ||
valueSize = this.valueSize; | ||
let value1, value2; | ||
for (let i = 0; i < valueSize; i++) { | ||
value1 = values[index0 * valueSize + i]; | ||
value2 = values[(index0 + 1) * valueSize + i]; | ||
if (this.interpolant) { | ||
if (value1 !== undefined && value2 !== undefined) { | ||
outBuffer[i] = value1 * (1 - ratio) + value2 * ratio; | ||
} else { | ||
outBuffer[i] = value1; | ||
} | ||
} else { | ||
outBuffer[i] = value1; | ||
} | ||
} | ||
return outBuffer; | ||
} | ||
_copyValue(index, outBuffer) { | ||
const values = this.values, | ||
valueSize = this.valueSize, | ||
offset = valueSize * index; | ||
for (let i = 0; i < valueSize; i++) { | ||
outBuffer[i] = values[offset + i]; | ||
} | ||
return outBuffer; | ||
} | ||
} | ||
export { KeyframeTrack }; |
import { KeyframeTrack } from '../KeyframeTrack.js'; | ||
import { StepInterpolant } from '../KeyframeInterpolants.js'; | ||
@@ -15,13 +16,13 @@ /** | ||
* @param {Array} values | ||
* @param {Boolean} [interpolant=true] | ||
* @param {t3d.KeyframeInterpolant.constructor} [interpolant=t3d.StepInterpolant] | ||
*/ | ||
constructor(target, propertyPath, times, values, interpolant) { | ||
constructor(target, propertyPath, times, values, interpolant = StepInterpolant) { | ||
// since 0.2.2, remove this after few versions later | ||
if (interpolant === true) { | ||
interpolant = StepInterpolant; | ||
} | ||
super(target, propertyPath, times, values, interpolant); | ||
} | ||
_interpolate(index0, _ratio, outBuffer) { | ||
outBuffer[0] = this.values[index0]; | ||
return outBuffer; | ||
} | ||
} | ||
@@ -28,0 +29,0 @@ |
@@ -15,3 +15,3 @@ import { KeyframeTrack } from '../KeyframeTrack.js'; | ||
* @param {Array} values | ||
* @param {Boolean} [interpolant=true] | ||
* @param {t3d.KeyframeInterpolant.constructor} [interpolant=t3d.LinearInterpolant] | ||
*/ | ||
@@ -18,0 +18,0 @@ constructor(target, propertyPath, times, values, interpolant) { |
@@ -15,3 +15,3 @@ import { KeyframeTrack } from '../KeyframeTrack.js'; | ||
* @param {Array} values | ||
* @param {Boolean} [interpolant=true] | ||
* @param {t3d.KeyframeInterpolant.constructor} [interpolant=t3d.LinearInterpolant] | ||
*/ | ||
@@ -18,0 +18,0 @@ constructor(target, propertyPath, times, values, interpolant) { |
import { KeyframeTrack } from '../KeyframeTrack.js'; | ||
import { Quaternion } from '../../math/Quaternion.js'; | ||
import { QuaternionLinearInterpolant } from '../KeyframeInterpolants.js'; | ||
@@ -16,18 +16,11 @@ /** | ||
* @param {Array} values | ||
* @param {Boolean} [interpolant=true] | ||
* @param {t3d.KeyframeInterpolant.constructor} [interpolant=t3d.QuaternionLinearInterpolant] | ||
*/ | ||
constructor(target, propertyPath, times, values, interpolant) { | ||
super(target, propertyPath, times, values, interpolant); | ||
} | ||
_interpolate(index0, ratio, outBuffer) { | ||
const values = this.values; | ||
if (this.interpolant) { | ||
Quaternion.slerpFlat(outBuffer, 0, values, index0 * 4, values, (index0 + 1) * 4, ratio); | ||
} else { | ||
this._copyValue(index0, outBuffer); | ||
constructor(target, propertyPath, times, values, interpolant = QuaternionLinearInterpolant) { | ||
// since 0.2.2, remove this after few versions later | ||
if (interpolant === true) { | ||
interpolant = QuaternionLinearInterpolant; | ||
} | ||
return outBuffer; | ||
super(target, propertyPath, times, values, interpolant); | ||
} | ||
@@ -34,0 +27,0 @@ |
import { KeyframeTrack } from '../KeyframeTrack.js'; | ||
import { StepInterpolant } from '../KeyframeInterpolants.js'; | ||
@@ -15,13 +16,13 @@ /** | ||
* @param {Array} values | ||
* @param {Boolean} [interpolant=true] | ||
* @param {t3d.KeyframeInterpolant.constructor} [interpolant=t3d.StepInterpolant] | ||
*/ | ||
constructor(target, propertyPath, times, values, interpolant) { | ||
constructor(target, propertyPath, times, values, interpolant = StepInterpolant) { | ||
// since 0.2.2, remove this after few versions later | ||
if (interpolant === true) { | ||
interpolant = StepInterpolant; | ||
} | ||
super(target, propertyPath, times, values, interpolant); | ||
} | ||
_interpolate(index0, _ratio, outBuffer) { | ||
outBuffer[0] = this.values[index0]; | ||
return outBuffer; | ||
} | ||
} | ||
@@ -28,0 +29,0 @@ |
@@ -15,3 +15,3 @@ import { KeyframeTrack } from '../KeyframeTrack.js'; | ||
* @param {Array} values | ||
* @param {Boolean} [interpolant=true] | ||
* @param {t3d.KeyframeInterpolant.constructor} [interpolant=t3d.LinearInterpolant] | ||
*/ | ||
@@ -18,0 +18,0 @@ constructor(target, propertyPath, times, values, interpolant) { |
@@ -48,3 +48,6 @@ /** | ||
SUBTRACT: 101, | ||
REVERSE_SUBTRACT: 102 | ||
REVERSE_SUBTRACT: 102, | ||
/** Only webgl2 */ | ||
MIN: 103, | ||
MAX: 104 | ||
}; | ||
@@ -281,5 +284,3 @@ | ||
PCSS16_SOFT: 'pcss16_soft', | ||
/** Only webgl2 */ | ||
PCSS32_SOFT: 'pcss32_soft', | ||
/** Only webgl2 */ | ||
PCSS64_SOFT: 'pcss64_soft' | ||
@@ -286,0 +287,0 @@ }; |
@@ -15,2 +15,3 @@ /** | ||
export { KeyframeClip } from './animation/KeyframeClip.js'; | ||
export * from './animation/KeyframeInterpolants.js'; | ||
export { KeyframeTrack } from './animation/KeyframeTrack.js'; | ||
@@ -17,0 +18,0 @@ export { PropertyBindingMixer } from './animation/PropertyBindingMixer.js'; |
@@ -159,11 +159,31 @@ import { Vector3 } from './Vector3.js'; | ||
* Returns the center point of the box as a Vector3. | ||
* @param {t3d.Vector3} optionalTarget - the result will be copied into this Vector3. | ||
* @param {t3d.Vector3} target - the result will be copied into this Vector3. | ||
* @return {t3d.Vector3} | ||
*/ | ||
getCenter(optionalTarget) { | ||
const result = optionalTarget || new Vector3(); | ||
return this.isEmpty() ? result.set(0, 0, 0) : result.addVectors(this.min, this.max).multiplyScalar(0.5); | ||
getCenter(target = new Vector3()) { | ||
return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); | ||
} | ||
/** | ||
* Returns the width, height and depth of this box. | ||
* @param {t3d.Vector3} target - the result will be copied into this Vector3. | ||
* @return {t3d.Vector3} | ||
*/ | ||
getSize(target = new Vector3()) { | ||
return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); | ||
} | ||
/** | ||
* Computes the union of this box and box, | ||
* setting the upper bound of this box to the greater of the two boxes' upper bounds and the lower bound of this box to the lesser of the two boxes' lower bounds. | ||
* @param {t3d.Box3} box - Box that will be unioned with this box. | ||
* @return {t3d.Box3} | ||
*/ | ||
union(box) { | ||
this.min.min(box.min); | ||
this.max.max(box.max); | ||
return this; | ||
} | ||
/** | ||
* Transforms this Box3 with the supplied matrix. | ||
@@ -193,2 +213,13 @@ * @param {t3d.Matrix4} matrix - The Matrix4 to apply | ||
/** | ||
* Returns true if the specified point lies within or on the boundaries of this box. | ||
* @param {t3d.Vector3} point - Vector3 to check for inclusion. | ||
* @return {Boolean} | ||
*/ | ||
containsPoint(point) { | ||
return point.x < this.min.x || point.x > this.max.x || | ||
point.y < this.min.y || point.y > this.max.y || | ||
point.z < this.min.z || point.z > this.max.z ? false : true; | ||
} | ||
/** | ||
* Determines whether or not this box intersects triangle. | ||
@@ -195,0 +226,0 @@ * @param {t3d.Triangle} triangle - Triangle to check for intersection against. |
@@ -5,2 +5,3 @@ import { Vector3 } from './Vector3.js'; | ||
const _vec3_1 = new Vector3(); | ||
const _vec3_2 = new Vector3(); | ||
const _mat4_1 = new Matrix3(); | ||
@@ -64,2 +65,17 @@ | ||
/** | ||
* Defines the plane based on the 3 provided points. | ||
* The winding order is assumed to be counter-clockwise, and determines the direction of the normal. | ||
* @param {t3d.Vector3} a - first point on the plane. | ||
* @param {t3d.Vector3} b - second point on the plane. | ||
* @param {t3d.Vector3} c - third point on the plane. | ||
* @return {t3d.Plane} | ||
*/ | ||
setFromCoplanarPoints(a, b, c) { | ||
const normal = _vec3_1.subVectors(c, b).cross(_vec3_2.subVectors(a, b)).normalize(); | ||
// Q: should an error be thrown if normal is zero (e.g. degenerate plane)? | ||
this.setFromNormalAndCoplanarPoint(normal, a); | ||
return this; | ||
} | ||
/** | ||
* Normalizes the normal vector, and adjusts the constant value accordingly. | ||
@@ -66,0 +82,0 @@ * @return {t3d.Plane} |
@@ -91,2 +91,65 @@ import { Vector3 } from './Vector3.js'; | ||
/** | ||
* Get the distance of the closest approach between the Ray and the Plane. | ||
* @param {t3d.Plane} plane - the Plane to compute a distance to. | ||
* @return {Number} | ||
*/ | ||
distanceToPlane(plane) { | ||
const denominator = plane.normal.dot(this.direction); | ||
if (denominator === 0) { | ||
// line is coplanar, return origin | ||
if (plane.distanceToPoint(this.origin) === 0) { | ||
return 0; | ||
} | ||
// Null is preferable to undefined since undefined means.... it is undefined | ||
return null; | ||
} | ||
const t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; | ||
// Return if the ray never intersects the plane | ||
return t >= 0 ? t : null; | ||
} | ||
/** | ||
* Intersect this Ray with a Plane, returning the intersection point or null if there is no intersection. | ||
* @param {t3d.Plane} plane - the Plane to intersect with. | ||
* @param {t3d.Vector3} [optionalTarget=] - the result will be copied into this Vector3. | ||
* @return {t3d.Vector3} | ||
*/ | ||
intersectPlane(plane, optionalTarget = new Vector3()) { | ||
const t = this.distanceToPlane(plane); | ||
if (t === null) { | ||
return null; | ||
} | ||
return this.at(t, optionalTarget); | ||
} | ||
/** | ||
* Return true if this Ray intersects with the Plane. | ||
* @param {t3d.Plane} plane - the plane to intersect with. | ||
* @return {Boolean} | ||
*/ | ||
intersectsPlane(plane) { | ||
// check if the ray lies on the plane first | ||
const distToPoint = plane.distanceToPoint(this.origin); | ||
if (distToPoint === 0) { | ||
return true; | ||
} | ||
const denominator = plane.normal.dot(this.direction); | ||
if (denominator * distToPoint < 0) { | ||
return true; | ||
} | ||
// ray origin is behind the plane (and is pointing behind it) | ||
return false; | ||
} | ||
/** | ||
* Return true if this Ray intersects with the Box3. | ||
@@ -93,0 +156,0 @@ * @param {t3d.Box3} box - the Box3 to intersect with. |
@@ -84,4 +84,4 @@ import { Attribute } from './Attribute.js'; | ||
* Split the geometry into groups, each of which will be rendered in a separate WebGL draw call. This allows an array of materials to be used with the geometry. | ||
* Each group is an object of the form: | ||
* { start: Integer, count: Integer, materialIndex: Integer } | ||
* Each group is an object of the form: { start: Integer, count: Integer, materialIndex: Integer }, | ||
* or { multiDrawStarts: Integer[], multiDrawCounts: Integer[], multiDrawCount: Integer, materialIndex: Integer } if multiDraw is available. | ||
* @type {Array} | ||
@@ -88,0 +88,0 @@ * @default [] |
@@ -53,8 +53,6 @@ import { PropertyMap } from '../render/PropertyMap.js'; | ||
// If in pass rendering, skip the geometry if it has been set in this pass. | ||
if (passInfo.enabled) { | ||
if (geometryProperties.pass === passInfo.count) { | ||
return; | ||
} | ||
geometryProperties.pass = passInfo.count; | ||
if (geometryProperties.pass === passInfo.count) { | ||
return; | ||
} | ||
geometryProperties.pass = passInfo.count; | ||
@@ -61,0 +59,0 @@ if (!geometryProperties.created) { |
@@ -237,2 +237,7 @@ import { WebGLPrograms } from './WebGLPrograms.js'; | ||
if (!passInfo.enabled) { | ||
console.warn('WebGLRenderer: beginRender must be called before renderRenderableItem.'); | ||
return; | ||
} | ||
const object = renderable.object; | ||
@@ -355,12 +360,10 @@ const material = getMaterial.call(this, renderable); | ||
let refreshMaterial = true; | ||
if (passInfo.enabled) { | ||
if (!material.forceUpdateUniforms) { | ||
if (materialProperties.pass !== passInfo.count) { | ||
materialProperties.pass = passInfo.count; | ||
} else { | ||
refreshMaterial = this._currentMaterial !== material; | ||
} | ||
if (!material.forceUpdateUniforms) { | ||
if (materialProperties.pass !== passInfo.count) { | ||
materialProperties.pass = passInfo.count; | ||
} else { | ||
refreshMaterial = this._currentMaterial !== material; | ||
} | ||
this._currentMaterial = material; | ||
} | ||
this._currentMaterial = material; | ||
@@ -618,22 +621,28 @@ const uniforms = program.getUniforms(); | ||
const instanceCount = geometry.instanceCount; | ||
const useInstancing = instanceCount >= 0; | ||
const useGroup = !!group; | ||
const useMultiDraw = useGroup && group.multiDrawCount !== undefined; | ||
const useIndexBuffer = geometry.index !== null; | ||
const position = geometry.getAttribute('a_Position'); | ||
let drawStart = 0; | ||
let drawCount = Infinity; | ||
if (useIndexBuffer) { | ||
drawCount = geometry.index.buffer.count; | ||
} else if (position) { | ||
drawCount = position.buffer.count; | ||
} | ||
const groupStart = group ? group.start : 0; | ||
const groupCount = group ? group.count : Infinity; | ||
drawStart = Math.max(drawStart, groupStart); | ||
drawCount = Math.min(drawCount, groupCount); | ||
if (drawCount < 0 || drawCount === Infinity) return; | ||
if (!useMultiDraw) { | ||
const position = geometry.getAttribute('a_Position'); | ||
const instanceCount = geometry.instanceCount; | ||
const useInstancing = instanceCount >= 0; | ||
if (useIndexBuffer) { | ||
drawCount = geometry.index.buffer.count; | ||
} else if (position) { | ||
drawCount = position.buffer.count; | ||
} | ||
if (useGroup) { | ||
drawStart = Math.max(drawStart, group.start); | ||
drawCount = Math.min(drawCount, group.count); | ||
} | ||
if (drawCount < 0 || drawCount === Infinity) return; | ||
} | ||
if (useIndexBuffer) { | ||
@@ -646,3 +655,3 @@ const indexBufferProperties = buffers.get(geometry.index.buffer); | ||
if (capabilities.version < 2 && !capabilities.getExtension('OES_element_index_uint')) { | ||
console.warn('draw elements type not support UNSIGNED_INT!'); | ||
console.warn('WebGLRenderer: draw elements type not support UNSIGNED_INT!'); | ||
} | ||
@@ -652,12 +661,23 @@ } | ||
if (useInstancing) { | ||
if (instanceCount > 0) { | ||
if (capabilities.version >= 2) { | ||
gl.drawElementsInstanced(material.drawMode, drawCount, type, drawStart * bytesPerElement, instanceCount); | ||
} else if (capabilities.getExtension('ANGLE_instanced_arrays')) { | ||
capabilities.getExtension('ANGLE_instanced_arrays').drawElementsInstancedANGLE(material.drawMode, drawCount, type, drawStart * bytesPerElement, instanceCount); | ||
} else { | ||
console.warn('no support instanced draw.'); | ||
return; | ||
} | ||
if (instanceCount <= 0) return; | ||
if (capabilities.version >= 2) { | ||
gl.drawElementsInstanced(material.drawMode, drawCount, type, drawStart * bytesPerElement, instanceCount); | ||
} else if (capabilities.getExtension('ANGLE_instanced_arrays')) { | ||
capabilities.getExtension('ANGLE_instanced_arrays').drawElementsInstancedANGLE(material.drawMode, drawCount, type, drawStart * bytesPerElement, instanceCount); | ||
} else { | ||
console.warn('WebGLRenderer: using instanced draw but hardware does not support.'); | ||
return; | ||
} | ||
} else if (useMultiDraw) { | ||
if (group.multiDrawCount <= 0) return; | ||
const extension = capabilities.getExtension('WEBGL_multi_draw'); | ||
if (!extension) { | ||
console.warn('WebGLRenderer: using multi draw but hardware does not support extension WEBGL_multi_draw.'); | ||
return; | ||
} | ||
extension.multiDrawElementsWEBGL(material.drawMode, group.multiDrawCounts, 0, type, group.multiDrawStarts, 0, group.multiDrawCount); | ||
} else { | ||
@@ -668,12 +688,23 @@ gl.drawElements(material.drawMode, drawCount, type, drawStart * bytesPerElement); | ||
if (useInstancing) { | ||
if (instanceCount > 0) { | ||
if (capabilities.version >= 2) { | ||
gl.drawArraysInstanced(material.drawMode, drawStart, drawCount, instanceCount); | ||
} else if (capabilities.getExtension('ANGLE_instanced_arrays')) { | ||
capabilities.getExtension('ANGLE_instanced_arrays').drawArraysInstancedANGLE(material.drawMode, drawStart, drawCount, instanceCount); | ||
} else { | ||
console.warn('no support instanced draw.'); | ||
return; | ||
} | ||
if (instanceCount <= 0) return; | ||
if (capabilities.version >= 2) { | ||
gl.drawArraysInstanced(material.drawMode, drawStart, drawCount, instanceCount); | ||
} else if (capabilities.getExtension('ANGLE_instanced_arrays')) { | ||
capabilities.getExtension('ANGLE_instanced_arrays').drawArraysInstancedANGLE(material.drawMode, drawStart, drawCount, instanceCount); | ||
} else { | ||
console.warn('WebGLRenderer: using instanced draw but hardware does not support.'); | ||
return; | ||
} | ||
} else if (useMultiDraw) { | ||
if (group.multiDrawCount <= 0) return; | ||
const extension = capabilities.getExtension('WEBGL_multi_draw'); | ||
if (!extension) { | ||
console.warn('WebGLRenderer: using multi draw but hardware does not support extension WEBGL_multi_draw.'); | ||
return; | ||
} | ||
extension.multiDrawArraysWEBGL(material.drawMode, group.multiDrawStarts, 0, group.multiDrawCounts, 0, group.multiDrawCount); | ||
} else { | ||
@@ -685,2 +716,8 @@ gl.drawArrays(material.drawMode, drawStart, drawCount); | ||
if (renderInfo) { | ||
if (useMultiDraw) { | ||
drawCount = 0; | ||
for (let i = 0; i < group.multiDrawCount; i++) { | ||
drawCount += group.multiDrawCounts[i]; | ||
} | ||
} | ||
renderInfo.update(drawCount, material.drawMode, instanceCount < 0 ? 1 : instanceCount); | ||
@@ -687,0 +724,0 @@ } |
@@ -281,3 +281,5 @@ import { BLEND_TYPE, CULL_FACE_TYPE, DRAW_SIDE, COMPARE_FUNC, BLEND_EQUATION, BLEND_FACTOR } from '../const.js'; | ||
[BLEND_EQUATION.SUBTRACT]: gl.FUNC_SUBTRACT, | ||
[BLEND_EQUATION.REVERSE_SUBTRACT]: gl.FUNC_REVERSE_SUBTRACT | ||
[BLEND_EQUATION.REVERSE_SUBTRACT]: gl.FUNC_REVERSE_SUBTRACT, | ||
[BLEND_EQUATION.MIN]: gl.MIN, | ||
[BLEND_EQUATION.MAX]: gl.MAX | ||
}; | ||
@@ -284,0 +286,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2978904
372
72351