Skip to content

Commit

Permalink
Float16BufferAttribute: Unpack/pack float16's in in getters/setters. (#…
Browse files Browse the repository at this point in the history
…25519)

* .

* .

* .

* Fix some formatting issues.
  • Loading branch information
simondevyoutube committed Feb 24, 2023
1 parent c3c7caf commit 2b54088
Show file tree
Hide file tree
Showing 3 changed files with 257 additions and 0 deletions.
141 changes: 141 additions & 0 deletions src/core/BufferAttribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Vector3 } from '../math/Vector3.js';
import { Vector2 } from '../math/Vector2.js';
import { denormalize, normalize } from '../math/MathUtils.js';
import { StaticDrawUsage } from '../constants.js';
import { fromHalfFloat, toHalfFloat } from '../extras/DataUtils.js';

const _vector = /*@__PURE__*/ new Vector3();
const _vector2 = /*@__PURE__*/ new Vector2();
Expand Down Expand Up @@ -453,6 +454,146 @@ class Float16BufferAttribute extends BufferAttribute {

}

getX( index ) {

let x = fromHalfFloat( this.array[ index * this.itemSize ] );

if ( this.normalized ) x = denormalize( x, this.array );

return x;

}

setX( index, x ) {

if ( this.normalized ) x = normalize( x, this.array );

this.array[ index * this.itemSize ] = toHalfFloat( x );

return this;

}

getY( index ) {

let y = fromHalfFloat( this.array[ index * this.itemSize + 1 ] );

if ( this.normalized ) y = denormalize( y, this.array );

return y;

}

setY( index, y ) {

if ( this.normalized ) y = normalize( y, this.array );

this.array[ index * this.itemSize + 1 ] = toHalfFloat( y );

return this;

}

getZ( index ) {

let z = fromHalfFloat( this.array[ index * this.itemSize + 2 ] );

if ( this.normalized ) z = denormalize( z, this.array );

return z;

}

setZ( index, z ) {

if ( this.normalized ) z = normalize( z, this.array );

this.array[ index * this.itemSize + 2 ] = toHalfFloat( z );

return this;

}

getW( index ) {

let w = fromHalfFloat( this.array[ index * this.itemSize + 3 ] );

if ( this.normalized ) w = denormalize( w, this.array );

return w;

}

setW( index, w ) {

if ( this.normalized ) w = normalize( w, this.array );

this.array[ index * this.itemSize + 3 ] = toHalfFloat( w );

return this;

}

setXY( index, x, y ) {

index *= this.itemSize;

if ( this.normalized ) {

x = normalize( x, this.array );
y = normalize( y, this.array );

}

this.array[ index + 0 ] = toHalfFloat( x );
this.array[ index + 1 ] = toHalfFloat( y );

return this;

}

setXYZ( index, x, y, z ) {

index *= this.itemSize;

if ( this.normalized ) {

x = normalize( x, this.array );
y = normalize( y, this.array );
z = normalize( z, this.array );

}

this.array[ index + 0 ] = toHalfFloat( x );
this.array[ index + 1 ] = toHalfFloat( y );
this.array[ index + 2 ] = toHalfFloat( z );

return this;

}

setXYZW( index, x, y, z, w ) {

index *= this.itemSize;

if ( this.normalized ) {

x = normalize( x, this.array );
y = normalize( y, this.array );
z = normalize( z, this.array );
w = normalize( w, this.array );

}

this.array[ index + 0 ] = toHalfFloat( x );
this.array[ index + 1 ] = toHalfFloat( y );
this.array[ index + 2 ] = toHalfFloat( z );
this.array[ index + 3 ] = toHalfFloat( w );

return this;

}

}


Expand Down
67 changes: 67 additions & 0 deletions test/unit/src/core/BufferAttribute.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from '../../../../src/core/BufferAttribute.js';

import { DynamicDrawUsage } from '../../../../src/constants.js';
import { toHalfFloat, fromHalfFloat } from '../../../../src/extras/DataUtils.js';

export default QUnit.module( 'Core', () => {

Expand Down Expand Up @@ -495,6 +496,72 @@ export default QUnit.module( 'Core', () => {

} );

const toHalfFloatArray = ( f32Array ) => {
const f16Array = new Uint16Array( f32Array.length );
for ( let i = 0, n = f32Array.length; i < n; ++i ) {
f16Array[ i ] = toHalfFloat( f32Array[ i ] );
}
return f16Array;
};

const fromHalfFloatArray = ( f16Array ) => {
const f32Array = new Float32Array( f16Array.length );
for ( let i = 0, n = f16Array.length; i < n; ++i ) {
f32Array[ i ] = fromHalfFloat( f16Array[ i ] );
}
return f32Array;
};

QUnit.test( 'set[X, Y, Z, W, XYZ, XYZW]/get[X, Y, Z, W]', ( assert ) => {

const f32a = new Float32Array( [ 1, 2, 3, 4, 5, 6, 7, 8 ] );
const a = new Float16BufferAttribute( toHalfFloatArray( f32a ), 4, false );
const expected = new Float32Array( [ 1, 2, - 3, - 4, - 5, - 6, 7, 8 ] );

a.setX( 1, a.getX( 1 ) * - 1 );
a.setY( 1, a.getY( 1 ) * - 1 );
a.setZ( 0, a.getZ( 0 ) * - 1 );
a.setW( 0, a.getW( 0 ) * - 1 );

assert.deepEqual( fromHalfFloatArray( a.array ), expected, 'Check all set* calls set the correct values' );

} );

QUnit.test( 'setXY', ( assert ) => {

const f32a = new Float32Array( [ 1, 2, 3, 4 ] );
const a = new Float16BufferAttribute( toHalfFloatArray( f32a ), 2, false );
const expected = new Float32Array( [ - 1, - 2, 3, 4 ] );

a.setXY( 0, - 1, - 2 );

assert.deepEqual( fromHalfFloatArray( a.array ), expected, 'Check for the correct values' );

} );

QUnit.test( 'setXYZ', ( assert ) => {

const f32a = new Float32Array( [ 1, 2, 3, 4, 5, 6 ] );
const a = new Float16BufferAttribute( toHalfFloatArray( f32a ), 3, false );
const expected = new Float32Array( [ 1, 2, 3, - 4, - 5, - 6 ] );

a.setXYZ( 1, - 4, - 5, - 6 );

assert.deepEqual( fromHalfFloatArray( a.array ), expected, 'Check for the correct values' );

} );

QUnit.test( 'setXYZW', ( assert ) => {

const f32a = new Float32Array( [ 1, 2, 3, 4 ] );
const a = new Float16BufferAttribute( toHalfFloatArray( f32a ), 4, false );
const expected = new Float32Array( [ - 1, - 2, - 3, - 4 ] );

a.setXYZW( 0, - 1, - 2, - 3, - 4 );

assert.deepEqual( fromHalfFloatArray( a.array ), expected, 'Check for the correct values' );

} );
} );

QUnit.module( 'Float32BufferAttribute', () => {
Expand Down
49 changes: 49 additions & 0 deletions test/unit/src/core/BufferGeometry.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BufferGeometry } from '../../../../src/core/BufferGeometry.js';

import {
BufferAttribute,
Float16BufferAttribute,
Uint16BufferAttribute,
Uint32BufferAttribute
} from '../../../../src/core/BufferAttribute.js';
Expand All @@ -13,6 +14,7 @@ import { Quaternion } from '../../../../src/math/Quaternion.js';
import { Sphere } from '../../../../src/math/Sphere.js';
import { x, y, z } from '../../utils/math-constants.js';
import { EventDispatcher } from '../../../../src/core/EventDispatcher.js';
import { toHalfFloat } from '../../../../src/extras/DataUtils.js';

const DegToRad = Math.PI / 180;

Expand Down Expand Up @@ -459,6 +461,53 @@ export default QUnit.module( 'Core', () => {

} );

const toHalfFloatArray = ( f32Array ) => {
const f16Array = new Uint16Array( f32Array.length );
for ( let i = 0, n = f32Array.length; i < n; ++i ) {
f16Array[ i ] = toHalfFloat( f32Array[ i ] );
}
return f16Array;
};

QUnit.test( 'computeBoundingBox - Float16', ( assert ) => {
const vertices = [ - 1, - 2, - 3, 13, - 2, - 3.5, - 1, - 20, 0, - 4, 5, 6 ];
const geometry = new BufferGeometry();

geometry.setAttribute( 'position', new Float16BufferAttribute( toHalfFloatArray( vertices ), 3 ) );
geometry.computeBoundingBox();

let bb = geometry.boundingBox;

assert.ok( bb.min.x === - 4 && bb.min.y === - 20 && bb.min.z === - 3.5, 'min values are set correctly' );
assert.ok( bb.max.x === 13 && bb.max.y === 5 && bb.max.z === 6, 'max values are set correctly' );

bb = getBBForVertices( [ - 1, - 1, - 1 ] );

assert.ok( bb.min.x === bb.max.x && bb.min.y === bb.max.y && bb.min.z === bb.max.z, 'since there is only one vertex, max and min are equal' );
assert.ok( bb.min.x === - 1 && bb.min.y === - 1 && bb.min.z === - 1, 'since there is only one vertex, min and max are this vertex' );

} );

QUnit.test( 'computeBoundingSphere - Float16', ( assert ) => {
const vertices = [ - 10, 0, 0, 10, 0, 0 ];
const geometry = new BufferGeometry();

geometry.setAttribute( 'position', new Float16BufferAttribute( toHalfFloatArray( vertices ), 3 ) );
geometry.computeBoundingSphere();

let bs = geometry.boundingSphere;

assert.ok( bs.radius === 10, 'radius is equal to deltaMinMax / 2' );
assert.ok( bs.center.x === 0 && bs.center.y === 0 && bs.center.y === 0, 'bounding sphere is at ( 0, 0, 0 )' );

bs = getBSForVertices( [ - 5, 11, - 3, 5, - 11, 3 ] );
const radius = new Vector3( 5, 11, 3 ).length();

assert.ok( bs.radius === radius, 'radius is equal to directionLength' );
assert.ok( bs.center.x === 0 && bs.center.y === 0 && bs.center.y === 0, 'bounding sphere is at ( 0, 0, 0 )' );

} );

QUnit.todo( 'computeTangents', ( assert ) => {

assert.ok( false, 'everything\'s gonna be alright' );
Expand Down

0 comments on commit 2b54088

Please sign in to comment.