Skip to content

Commit 62c3d28

Browse files
authored
Merge pull request #4121 from lasalvavida/snorm-range-max
Snorm range max
2 parents 171aa7f + fb3a0a7 commit 62c3d28

File tree

4 files changed

+138
-28
lines changed

4 files changed

+138
-28
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Change Log
99
* Fixed an issue causing entities to disappear when updating multiple entities simultaneously. [#4096](https://github.com/AnalyticalGraphicsInc/cesium/issues/4096)
1010
* Added support in CZML for expressing `BillboardGraphics.alignedAxis` as the velocity vector of an entity, using `velocityReference` syntax.
1111
* Normalizing the velocity vector produced by `VelocityVectorProperty` is now optional.
12+
* Added `AttributeCompression.octEncodeInRange`, `AttributeCompression.octDecodeInRange` and an optional `rangeMax` parameter to `Math.toSNorm` and `Math.fromSNorm`, to support oct-encoding with variable precision. [#4121](https://github.com/AnalyticalGraphicsInc/cesium/pull/4121)
1213

1314
### 1.23 - 2016-07-01
1415

Source/Core/AttributeCompression.js

+52-16
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
define([
33
'./Cartesian2',
44
'./Cartesian3',
5+
'./defaultValue',
56
'./defined',
67
'./DeveloperError',
78
'./Math'
89
], function(
910
Cartesian2,
1011
Cartesian3,
12+
defaultValue,
1113
defined,
1214
DeveloperError,
1315
CesiumMath) {
@@ -23,21 +25,22 @@ define([
2325
var AttributeCompression = {};
2426

2527
/**
26-
* Encodes a normalized vector into 2 SNORM values in the range of [0-255] following the 'oct' encoding.
28+
* Encodes a normalized vector into 2 SNORM values in the range of [0-rangeMax] following the 'oct' encoding.
2729
*
28-
* Oct encoding is a compact representation of unit length vectors. The encoding and decoding functions are low cost, and represent the normalized vector within 1 degree of error.
30+
* Oct encoding is a compact representation of unit length vectors.
2931
* The 'oct' encoding is described in "A Survey of Efficient Representations of Independent Unit Vectors",
3032
* Cigolle et al 2014: {@link http://jcgt.org/published/0003/02/01/}
3133
*
32-
* @param {Cartesian3} vector The normalized vector to be compressed into 2 byte 'oct' encoding.
33-
* @param {Cartesian2} result The 2 byte oct-encoded unit length vector.
34-
* @returns {Cartesian2} The 2 byte oct-encoded unit length vector.
34+
* @param {Cartesian3} vector The normalized vector to be compressed into 2 component 'oct' encoding.
35+
* @param {Cartesian2} result The 2 component oct-encoded unit length vector.
36+
* @param {Number} rangeMax The maximum value of the SNORM range. The encoded vector is stored in log2(rangeMax+1) bits.
37+
* @returns {Cartesian2} The 2 component oct-encoded unit length vector.
3538
*
3639
* @exception {DeveloperError} vector must be normalized.
3740
*
38-
* @see AttributeCompression.octDecode
41+
* @see AttributeCompression.octDecodeInRange
3942
*/
40-
AttributeCompression.octEncode = function(vector, result) {
43+
AttributeCompression.octEncodeInRange = function(vector, rangeMax, result) {
4144
//>>includeStart('debug', pragmas.debug);
4245
if (!defined(vector)) {
4346
throw new DeveloperError('vector is required.');
@@ -60,36 +63,53 @@ define([
6063
result.y = (1.0 - Math.abs(x)) * CesiumMath.signNotZero(y);
6164
}
6265

63-
result.x = CesiumMath.toSNorm(result.x);
64-
result.y = CesiumMath.toSNorm(result.y);
66+
result.x = CesiumMath.toSNorm(result.x, rangeMax);
67+
result.y = CesiumMath.toSNorm(result.y, rangeMax);
6568

6669
return result;
6770
};
6871

72+
/**
73+
* Encodes a normalized vector into 2 SNORM values in the range of [0-255] following the 'oct' encoding.
74+
*
75+
* @param {Cartesian3} vector The normalized vector to be compressed into 2 byte 'oct' encoding.
76+
* @param {Cartesian2} result The 2 byte oct-encoded unit length vector.
77+
* @returns {Cartesian2} The 2 byte oct-encoded unit length vector.
78+
*
79+
* @exception {DeveloperError} vector must be normalized.
80+
*
81+
* @see AttributeCompression.octEncodeInRange
82+
* @see AttributeCompression.octDecode
83+
*/
84+
AttributeCompression.octEncode = function(vector, result) {
85+
return AttributeCompression.octEncodeInRange(vector, 255, result);
86+
};
87+
6988
/**
7089
* Decodes a unit-length vector in 'oct' encoding to a normalized 3-component vector.
7190
*
7291
* @param {Number} x The x component of the oct-encoded unit length vector.
7392
* @param {Number} y The y component of the oct-encoded unit length vector.
93+
* @param {Number} rangeMax The maximum value of the SNORM range. The encoded vector is stored in log2(rangeMax+1) bits.
7494
* @param {Cartesian3} result The decoded and normalized vector
7595
* @returns {Cartesian3} The decoded and normalized vector.
7696
*
77-
* @exception {DeveloperError} x and y must be a signed normalized integer between 0 and 255.
97+
* @exception {DeveloperError} x and y must be an unsigned normalized integer between 0 and rangeMax.
7898
*
79-
* @see AttributeCompression.octEncode
99+
* @see AttributeCompression.octEncodeInRange
80100
*/
81-
AttributeCompression.octDecode = function(x, y, result) {
101+
AttributeCompression.octDecodeInRange = function(x, y, rangeMax, result) {
82102
//>>includeStart('debug', pragmas.debug);
83103
if (!defined(result)) {
84104
throw new DeveloperError('result is required.');
85105
}
86-
if (x < 0 || x > 255 || y < 0 || y > 255) {
87-
throw new DeveloperError('x and y must be a signed normalized integer between 0 and 255');
106+
if (x < 0 || x > rangeMax || y < 0 || y > rangeMax) {
107+
throw new DeveloperError('x and y must be a signed normalized integer between 0 and ' + rangeMax);
88108
}
89109
//>>includeEnd('debug');
90110

91-
result.x = CesiumMath.fromSNorm(x);
92-
result.y = CesiumMath.fromSNorm(y);
111+
result.x = CesiumMath.fromSNorm(x, rangeMax);
112+
result.y = CesiumMath.fromSNorm(y, rangeMax);
93113
result.z = 1.0 - (Math.abs(result.x) + Math.abs(result.y));
94114

95115
if (result.z < 0.0)
@@ -102,6 +122,22 @@ define([
102122
return Cartesian3.normalize(result, result);
103123
};
104124

125+
/**
126+
* Decodes a unit-length vector in 2 byte 'oct' encoding to a normalized 3-component vector.
127+
*
128+
* @param {Number} x The x component of the oct-encoded unit length vector.
129+
* @param {Number} y The y component of the oct-encoded unit length vector.
130+
* @param {Cartesian3} result The decoded and normalized vector.
131+
* @returns {Cartesian3} The decoded and normalized vector.
132+
*
133+
* @exception {DeveloperError} x and y must be an unsigned normalized integer between 0 and 255.
134+
*
135+
* @see AttributeCompression.octDecodeInRange
136+
*/
137+
AttributeCompression.octDecode = function(x, y, result) {
138+
return AttributeCompression.octDecodeInRange(x, y, 255, result);
139+
};
140+
105141
/**
106142
* Packs an oct encoded vector into a single floating-point number.
107143
*

Source/Core/Math.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -218,25 +218,29 @@ define([
218218
};
219219

220220
/**
221-
* Converts a scalar value in the range [-1.0, 1.0] to a 8-bit 2's complement number.
221+
* Converts a scalar value in the range [-1.0, 1.0] to a SNORM in the range [0, rangeMax]
222222
* @param {Number} value The scalar value in the range [-1.0, 1.0]
223-
* @returns {Number} The 8-bit 2's complement number, where 0 maps to -1.0 and 255 maps to 1.0.
223+
* @param {Number} [rangeMax=255] The maximum value in the mapped range, 255 by default.
224+
* @returns {Number} A SNORM value, where 0 maps to -1.0 and rangeMax maps to 1.0.
224225
*
225226
* @see CesiumMath.fromSNorm
226227
*/
227-
CesiumMath.toSNorm = function(value) {
228-
return Math.round((CesiumMath.clamp(value, -1.0, 1.0) * 0.5 + 0.5) * 255.0);
228+
CesiumMath.toSNorm = function(value, rangeMax) {
229+
rangeMax = defaultValue(rangeMax, 255);
230+
return Math.round((CesiumMath.clamp(value, -1.0, 1.0) * 0.5 + 0.5) * rangeMax);
229231
};
230232

231233
/**
232-
* Converts a SNORM value in the range [0, 255] to a scalar in the range [-1.0, 1.0].
234+
* Converts a SNORM value in the range [0, rangeMax] to a scalar in the range [-1.0, 1.0].
233235
* @param {Number} value SNORM value in the range [0, 255]
236+
* @param {Number} [rangeMax=255] The maximum value in the SNORM range, 255 by default.
234237
* @returns {Number} Scalar in the range [-1.0, 1.0].
235238
*
236239
* @see CesiumMath.toSNorm
237240
*/
238-
CesiumMath.fromSNorm = function(value) {
239-
return CesiumMath.clamp(value, 0.0, 255.0) / 255.0 * 2.0 - 1.0;
241+
CesiumMath.fromSNorm = function(value, rangeMax) {
242+
rangeMax = defaultValue(rangeMax, 255);
243+
return CesiumMath.clamp(value, 0.0, rangeMax) / rangeMax * 2.0 - 1.0;
240244
};
241245

242246
/**

Specs/Core/AttributeCompressionSpec.js

+74-5
Original file line numberDiff line numberDiff line change
@@ -79,23 +79,21 @@ defineSuite([
7979
it('throws oct decode result undefined', function() {
8080
var result;
8181
expect(function() {
82-
AttributeCompression.octDecode(Cartesian2.ZERO, result);
82+
AttributeCompression.octDecode(0, 0, result);
8383
}).toThrowDeveloperError();
8484
});
8585

8686
it('throws oct decode x out of bounds', function() {
8787
var result = new Cartesian3();
88-
var invalidSNorm = new Cartesian2(256, 0);
8988
expect(function() {
90-
AttributeCompression.octDecode(invalidSNorm, result);
89+
AttributeCompression.octDecode(256, 0, result);
9190
}).toThrowDeveloperError();
9291
});
9392

9493
it('throws oct decode y out of bounds', function() {
9594
var result = new Cartesian3();
96-
var invalidSNorm = new Cartesian2(0, 256);
9795
expect(function() {
98-
AttributeCompression.octDecode(invalidSNorm, result);
96+
AttributeCompression.octDecode(0, 256, result);
9997
}).toThrowDeveloperError();
10098
});
10199

@@ -169,6 +167,77 @@ defineSuite([
169167
expect(AttributeCompression.octDecode(encoded.x, encoded.y, result)).toEqualEpsilon(normal, epsilon);
170168
});
171169

170+
it('oct encoding high precision', function() {
171+
var rangeMax = 4294967295;
172+
var epsilon = CesiumMath.EPSILON8;
173+
174+
var encoded = new Cartesian2();
175+
var result = new Cartesian3();
176+
var normal = new Cartesian3(0.0, 0.0, 1.0);
177+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
178+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
179+
180+
normal = new Cartesian3(0.0, 0.0, -1.0);
181+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
182+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
183+
184+
normal = new Cartesian3(0.0, 1.0, 0.0);
185+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
186+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
187+
188+
normal = new Cartesian3(0.0, -1.0, 0.0);
189+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
190+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
191+
192+
normal = new Cartesian3(1.0, 0.0, 0.0);
193+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
194+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
195+
196+
normal = new Cartesian3(-1.0, 0.0, 0.0);
197+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
198+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
199+
200+
normal = new Cartesian3(1.0, 1.0, 1.0);
201+
Cartesian3.normalize(normal, normal);
202+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
203+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
204+
205+
normal = new Cartesian3(1.0, -1.0, 1.0);
206+
Cartesian3.normalize(normal, normal);
207+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
208+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
209+
210+
normal = new Cartesian3(-1.0, -1.0, 1.0);
211+
Cartesian3.normalize(normal, normal);
212+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
213+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
214+
215+
normal = new Cartesian3(-1.0, 1.0, 1.0);
216+
Cartesian3.normalize(normal, normal);
217+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
218+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
219+
220+
normal = new Cartesian3(1.0, 1.0, -1.0);
221+
Cartesian3.normalize(normal, normal);
222+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
223+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
224+
225+
normal = new Cartesian3(1.0, -1.0, -1.0);
226+
Cartesian3.normalize(normal, normal);
227+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
228+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
229+
230+
normal = new Cartesian3(-1.0, 1.0, -1.0);
231+
Cartesian3.normalize(normal, normal);
232+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
233+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
234+
235+
normal = new Cartesian3(-1.0, -1.0, -1.0);
236+
Cartesian3.normalize(normal, normal);
237+
AttributeCompression.octEncodeInRange(normal, rangeMax, encoded);
238+
expect(AttributeCompression.octDecodeInRange(encoded.x, encoded.y, rangeMax, result)).toEqualEpsilon(normal, epsilon);
239+
});
240+
172241
it('octFloat encoding', function() {
173242
var epsilon = CesiumMath.EPSILON1;
174243

0 commit comments

Comments
 (0)