Explorar el Código

Merge pull request #1110 from nockawa/master

Fixed Vector2.Transform and DynamicFloatArray.pack
David Catuhe hace 9 años
padre
commit
47ceb02f4f
Se han modificado 4 ficheros con 3322 adiciones y 3179 borrados
  1. 2965 2964
      src/Math/babylon.math.js
  2. 2 2
      src/Math/babylon.math.ts
  3. 215 142
      src/Tools/babylon.dynamicFloatArray.js
  4. 140 71
      src/Tools/babylon.dynamicFloatArray.ts

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 2965 - 2964
src/Math/babylon.math.js


+ 2 - 2
src/Math/babylon.math.ts

@@ -605,8 +605,8 @@
         }
         }
 
 
         public static Transform(vector: Vector2, transformation: Matrix): Vector2 {
         public static Transform(vector: Vector2, transformation: Matrix): Vector2 {
-            var x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]);
-            var y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]);
+            var x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + transformation.m[12];
+            var y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + transformation.m[13];
 
 
             return new Vector2(x, y);
             return new Vector2(x, y);
         }
         }

+ 215 - 142
src/Tools/babylon.dynamicFloatArray.js

@@ -1,142 +1,215 @@
-var BABYLON;
-(function (BABYLON) {
-    /**
-    * The purpose of this class is to store float32 based elements of a given size (defined by the stride argument) in a dynamic fashion, that is, you can add/free elements. You can then access to a defragmented/packed version of the underlying Float32Array by calling the pack() method.
-    * The intent is to maintain through time data that will be bound to a WebGlBuffer with the ability to change add/remove elements.
-    * It was first built to effiently maintain the WebGlBuffer that contain instancing based data.
-    * Allocating an Element will return a instance of DynamicFloatArrayEntry which contains the offset into the Float32Array of where the element starts, you are then responsible to copy your data using this offset.
-    * Beware, calling pack() may change the offset of some Entries because this method will defrag the Float32Array to replace empty elements by moving allocated ones at their location.
-     * This method will retrun an ArrayBufferView on the existing Float32Array that describes the occupied elements. Use this View to update the WebGLBuffer and NOT the "buffer" field of the class. The pack() method won't shrink/reallocate the buffer to keep it GC friendly, all the empty space will be put at the end of the buffer, the method just ensure there're no "free holes".
-    */
-    var DynamicFloatArray = (function () {
-        /**
-         * Construct an instance of the dynamic float array
-         * @param stride size of one entry in float (i.e. not bytes!)
-         * @param initialEntryCount the number of available entries at construction
-         */
-        function DynamicFloatArray(stride, initialEntryCount) {
-            this.stride = stride;
-            this.entryCount = initialEntryCount;
-            this.buffer = new Float32Array(stride * initialEntryCount);
-            this.allEntries = new Array(initialEntryCount);
-            this.freeEntries = new Array(initialEntryCount);
-            for (var i = 0; i < initialEntryCount; i++) {
-                var entry = new DynamicFloatArrayEntry();
-                entry.offset = i * stride;
-                this.allEntries[i] = entry;
-                this.freeEntries[initialEntryCount - i - 1] = entry;
-            }
-        }
-        DynamicFloatArray.prototype.allocElement = function () {
-            if (this.freeEntries.length === 0) {
-                this._growBuffer();
-            }
-            var entry = this.freeEntries.pop();
-            return entry;
-        };
-        DynamicFloatArray.prototype.freeElement = function (entry) {
-            this.freeEntries.push(entry);
-        };
-        /**
-         * This method will pack all the occupied elements into a linear sequence and free the rest.
-         * Instances of DynamicFloatArrayEntry may have their 'offset' member changed as data could be copied from one location to another, so be sure to read/write your data based on the value inside this member after you called pack().
-         */
-        DynamicFloatArray.prototype.pack = function () {
-            // no free slot? no need to pack
-            if (this.freeEntries.length === 0) {
-                return this.buffer;
-            }
-            var s = this.stride;
-            var sortedFree = this.freeEntries.sort(function (a, b) { return a.offset - b.offset; });
-            var sortedAll = this.allEntries.sort(function (a, b) { return a.offset - b.offset; });
-            // Make sure there's a free element at the very end, we need it to create a range where we'll move the occupied elements that may appear before
-            var lastFree = new DynamicFloatArrayEntry();
-            lastFree.offset = this.entryCount * s;
-            sortedFree.push(lastFree);
-            var firstFreeSlotOffset = sortedFree[0].offset;
-            var freeZoneSize = 1;
-            var prevOffset = sortedFree[0].offset;
-            for (var i = 1; i < sortedFree.length; i++) {
-                var curFree = sortedFree[i];
-                var curOffset = curFree.offset;
-                // Compute the distance between this offset and the previous
-                var distance = curOffset - prevOffset;
-                // If the distance is the stride size, they are adjacents, it good, move to the next
-                if (distance === s) {
-                    // Free zone is one element bigger
-                    ++freeZoneSize;
-                    // as we're about to iterate to the next, the cur becomes the prev...
-                    prevOffset = curOffset;
-                    continue;
-                }
-                // Distance is bigger, which means there's x element between the previous free and this one
-                var occupiedRange = (distance / s) - 1;
-                // Two cases the free zone is smaller than the data to move or bigger
-                // Copy what can fit in the free zone
-                var curMoveOffset = curOffset - s;
-                var copyCount = Math.min(freeZoneSize, occupiedRange);
-                for (var j = 0; j < copyCount; j++) {
-                    var freeI = firstFreeSlotOffset / s;
-                    var curI = curMoveOffset / s;
-                    var moveEntry = sortedAll[curI];
-                    this._moveEntry(moveEntry, firstFreeSlotOffset);
-                    var replacedEntry = sortedAll[freeI];
-                    // set the offset of the element entry we replace with a value that will make it discard at the end of the method
-                    replacedEntry.offset = curMoveOffset;
-                    // Swap the entry we moved and the one it replaced in the sorted array to reflect the action we've made
-                    sortedAll[freeI] = moveEntry;
-                    sortedAll[curI] = replacedEntry;
-                    curMoveOffset -= s;
-                    firstFreeSlotOffset += s;
-                }
-                // Free Zone is smaller or equal so it's no longer a free zone, set the new one to the current location
-                if (freeZoneSize <= occupiedRange) {
-                    firstFreeSlotOffset = curOffset;
-                    freeZoneSize = 1;
-                }
-                else {
-                    freeZoneSize = ((curOffset - firstFreeSlotOffset) / s) + 1;
-                }
-                // as we're about to iterate to the next, the cur becomes the prev...
-                prevOffset = curOffset;
-            }
-            // Allocate a new buffer with the perfect size, copy the content, update free data
-            this.entryCount = firstFreeSlotOffset / s;
-            var optimizedBuffer = this.buffer.subarray(0, firstFreeSlotOffset);
-            this.freeEntries.splice(0);
-            this.allEntries = sortedAll.slice(0, this.entryCount);
-            return optimizedBuffer;
-        };
-        DynamicFloatArray.prototype._moveEntry = function (entry, destOffset) {
-            for (var i = 0; i < this.stride; i++) {
-                this.buffer[destOffset + i] = this.buffer[entry.offset + i];
-            }
-            entry.offset = destOffset;
-        };
-        DynamicFloatArray.prototype._growBuffer = function () {
-            // Allocate the new buffer with 50% more entries, copy the content of the current one
-            var newEntryCount = this.entryCount * 1.5;
-            var newBuffer = new Float32Array(newEntryCount * this.stride);
-            newBuffer.set(this.buffer);
-            var addedCount = newEntryCount - this.entryCount;
-            this.allEntries.length += addedCount;
-            this.freeEntries.length += addedCount;
-            for (var i = this.entryCount; i < newEntryCount; i++) {
-                var entry = new DynamicFloatArrayEntry();
-                entry.offset = i * this.stride;
-                this.allEntries[i] = entry;
-                this.freeEntries[i] = entry;
-            }
-            this.buffer = newBuffer;
-            this.entryCount = newEntryCount;
-        };
-        return DynamicFloatArray;
-    })();
-    BABYLON.DynamicFloatArray = DynamicFloatArray;
-    var DynamicFloatArrayEntry = (function () {
-        function DynamicFloatArrayEntry() {
-        }
-        return DynamicFloatArrayEntry;
-    })();
-    BABYLON.DynamicFloatArrayEntry = DynamicFloatArrayEntry;
-})(BABYLON || (BABYLON = {}));
+var BABYLON;
+(function (BABYLON) {
+    /**
+    * The purpose of this class is to store float32 based elements of a given size (defined by the stride argument) in a dynamic fashion, that is, you can add/free elements. You can then access to a defragmented/packed version of the underlying Float32Array by calling the pack() method.
+    * The intent is to maintain through time data that will be bound to a WebGlBuffer with the ability to change add/remove elements.
+    * It was first built to effiently maintain the WebGlBuffer that contain instancing based data.
+    * Allocating an Element will return a instance of DynamicFloatArrayElement which contains the offset into the Float32Array of where the element starts, you are then responsible to copy your data using this offset.
+    * Beware, calling pack() may change the offset of some Entries because this method will defrag the Float32Array to replace empty elements by moving allocated ones at their location.
+     * This method will return an ArrayBufferView on the existing Float32Array that describes the used elements. Use this View to update the WebGLBuffer and NOT the "buffer" field of the class. The pack() method won't shrink/reallocate the buffer to keep it GC friendly, all the empty space will be put at the end of the buffer, the method just ensure there're no "free holes".
+    */
+    var DynamicFloatArray = (function () {
+        /**
+         * Construct an instance of the dynamic float array
+         * @param stride size of one element in float (i.e. not bytes!)
+         * @param initialElementCount the number of available entries at construction
+         */
+        function DynamicFloatArray(stride, initialElementCount) {
+            this._stride = stride;
+            this.buffer = new Float32Array(stride * initialElementCount);
+            this._lastUsed = 0;
+            this._firstFree = 0;
+            this._allEntries = new Array(initialElementCount);
+            this._freeEntries = new Array(initialElementCount);
+            for (var i = 0; i < initialElementCount; i++) {
+                var element = new DynamicFloatArrayElementInfo();
+                element.offset = i * stride;
+                this._allEntries[i] = element;
+                this._freeEntries[initialElementCount - i - 1] = element;
+            }
+        }
+        /**
+         * Allocate an element in the array.
+         * @return the element info instance that contains the offset into the main buffer of the element's location.
+         * Beware, this offset may change when you call pack()
+         */
+        DynamicFloatArray.prototype.allocElement = function () {
+            if (this._freeEntries.length === 0) {
+                this._growBuffer();
+            }
+            var el = this._freeEntries.pop();
+            this._lastUsed = Math.max(el.offset, this._lastUsed);
+            if (el.offset === this._firstFree) {
+                if (this._freeEntries.length > 0) {
+                    this._firstFree = this._freeEntries[this._freeEntries.length - 1].offset;
+                }
+                else {
+                    this._firstFree += this._stride;
+                }
+            }
+            return el;
+        };
+        /**
+         * Free the element corresponding to the given element info
+         * @param elInfo the element that describe the allocated element
+         */
+        DynamicFloatArray.prototype.freeElement = function (elInfo) {
+            this._firstFree = Math.min(elInfo.offset, this._firstFree);
+            this._freeEntries.push(elInfo);
+        };
+        /**
+         * This method will pack all the used elements into a linear sequence and put all the free space at the end.
+         * Instances of DynamicFloatArrayElement may have their 'offset' member changed as data could be copied from one location to another, so be sure to read/write your data based on the value inside this member after you called pack().
+         * @return the subarray that is the view of the used elements area, you can use it as a source to update a WebGLBuffer
+         */
+        DynamicFloatArray.prototype.pack = function () {
+            // no free slot? no need to pack
+            if (this._freeEntries.length === 0) {
+                return this.buffer;
+            }
+            // If the buffer is already packed the last used will always be lower than the first free
+            if (this._lastUsed < this._firstFree) {
+                var elementsBuffer_1 = this.buffer.subarray(0, this._lastUsed + this._stride);
+                return elementsBuffer_1;
+            }
+            var s = this._stride;
+            // Make sure there's a free element at the very end, we need it to create a range where we'll move the used elements that may appear before
+            var lastFree = new DynamicFloatArrayElementInfo();
+            lastFree.offset = this.totalElementCount * s;
+            this._freeEntries.push(lastFree);
+            var sortedFree = this._freeEntries.sort(function (a, b) { return a.offset - b.offset; });
+            var sortedAll = this._allEntries.sort(function (a, b) { return a.offset - b.offset; });
+            var firstFreeSlotOffset = sortedFree[0].offset;
+            var freeZoneSize = 1;
+            // The sortedFree array is sorted in reverse, first free at the end, last free at the beginning, so we loop from the end to beginning
+            var prevOffset = sortedFree[0].offset;
+            for (var i = 1; i < sortedFree.length; i++) {
+                var curFree = sortedFree[i];
+                var curOffset = curFree.offset;
+                // Compute the distance between this offset and the previous
+                var distance = curOffset - prevOffset;
+                // If the distance is the stride size, they are adjacents, it good, move to the next
+                if (distance === s) {
+                    // Free zone is one element bigger
+                    ++freeZoneSize;
+                    // as we're about to iterate to the next, the cur becomes the prev...
+                    prevOffset = curOffset;
+                    continue;
+                }
+                // Distance is bigger, which means there's x element between the previous free and this one
+                var usedRange = (distance / s) - 1;
+                // Two cases the free zone is smaller than the data to move or bigger
+                // Copy what can fit in the free zone
+                var curMoveOffset = curOffset - s;
+                var copyCount = Math.min(freeZoneSize, usedRange);
+                for (var j = 0; j < copyCount; j++) {
+                    var freeI = firstFreeSlotOffset / s;
+                    var curI = curMoveOffset / s;
+                    var moveEl = sortedAll[curI];
+                    this._moveElement(moveEl, firstFreeSlotOffset);
+                    var replacedEl = sortedAll[freeI];
+                    // set the offset of the element element we replace with a value that will make it discard at the end of the method
+                    replacedEl.offset = curMoveOffset;
+                    // Swap the element we moved and the one it replaced in the sorted array to reflect the action we've made
+                    sortedAll[freeI] = moveEl;
+                    sortedAll[curI] = replacedEl;
+                    curMoveOffset -= s;
+                    firstFreeSlotOffset += s;
+                }
+                // Free Zone is smaller or equal so it's no longer a free zone, set the new one to the current location
+                if (freeZoneSize <= usedRange) {
+                    firstFreeSlotOffset = curOffset;
+                    freeZoneSize = 1;
+                }
+                else {
+                    freeZoneSize = ((curOffset - firstFreeSlotOffset) / s) + 1;
+                }
+                // as we're about to iterate to the next, the cur becomes the prev...
+                prevOffset = curOffset;
+            }
+            var elementsBuffer = this.buffer.subarray(0, firstFreeSlotOffset);
+            this._lastUsed = firstFreeSlotOffset - s;
+            this._firstFree = firstFreeSlotOffset;
+            sortedFree.pop(); // Remove the last free because that's the one we added at the start of the method
+            this._freeEntries = sortedFree.sort(function (a, b) { return b.offset - a.offset; });
+            this._allEntries = sortedAll;
+            return elementsBuffer;
+        };
+        DynamicFloatArray.prototype._moveElement = function (element, destOffset) {
+            for (var i = 0; i < this._stride; i++) {
+                this.buffer[destOffset + i] = this.buffer[element.offset + i];
+            }
+            element.offset = destOffset;
+        };
+        DynamicFloatArray.prototype._growBuffer = function () {
+            // Allocate the new buffer with 50% more entries, copy the content of the current one
+            var newElCount = this.totalElementCount * 1.5;
+            var newBuffer = new Float32Array(newElCount * this._stride);
+            newBuffer.set(this.buffer);
+            var addedCount = newElCount - this.totalElementCount;
+            this._allEntries.length += addedCount;
+            this._freeEntries.length += addedCount;
+            for (var i = this.totalElementCount; i < newElCount; i++) {
+                var el = new DynamicFloatArrayElementInfo();
+                el.offset = i * this._stride;
+                this._allEntries[i] = el;
+                this._freeEntries[i] = el;
+            }
+            this.buffer = newBuffer;
+            this.totalElementCount = newElCount;
+        };
+        Object.defineProperty(DynamicFloatArray.prototype, "totalElementCount", {
+            /**
+             * Get the total count of entries that can fit in the current buffer
+             * @returns the elements count
+             */
+            get: function () {
+                return this._allEntries.length;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(DynamicFloatArray.prototype, "freeElementCount", {
+            /**
+             * Get the count of free entries that can still be allocated without resizing the buffer
+             * @returns the free elements count
+             */
+            get: function () {
+                return this._freeEntries.length;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(DynamicFloatArray.prototype, "usedElementCount", {
+            /**
+             * Get the count of allocated elements
+             * @returns the allocated elements count
+             */
+            get: function () {
+                return this._allEntries.length - this._freeEntries.length;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(DynamicFloatArray.prototype, "stride", {
+            /**
+             * Return the size of one element in float
+             * @returns the size in float
+             */
+            get: function () {
+                return this._stride;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        return DynamicFloatArray;
+    }());
+    BABYLON.DynamicFloatArray = DynamicFloatArray;
+    var DynamicFloatArrayElementInfo = (function () {
+        function DynamicFloatArrayElementInfo() {
+        }
+        return DynamicFloatArrayElementInfo;
+    }());
+    BABYLON.DynamicFloatArrayElementInfo = DynamicFloatArrayElementInfo;
+})(BABYLON || (BABYLON = {}));
+//# sourceMappingURL=babylon.dynamicFloatArray.js.map

+ 140 - 71
src/Tools/babylon.dynamicFloatArray.ts

@@ -3,68 +3,97 @@
     * The purpose of this class is to store float32 based elements of a given size (defined by the stride argument) in a dynamic fashion, that is, you can add/free elements. You can then access to a defragmented/packed version of the underlying Float32Array by calling the pack() method.
     * The purpose of this class is to store float32 based elements of a given size (defined by the stride argument) in a dynamic fashion, that is, you can add/free elements. You can then access to a defragmented/packed version of the underlying Float32Array by calling the pack() method.
     * The intent is to maintain through time data that will be bound to a WebGlBuffer with the ability to change add/remove elements.
     * The intent is to maintain through time data that will be bound to a WebGlBuffer with the ability to change add/remove elements.
     * It was first built to effiently maintain the WebGlBuffer that contain instancing based data.
     * It was first built to effiently maintain the WebGlBuffer that contain instancing based data.
-    * Allocating an Element will return a instance of DynamicFloatArrayEntry which contains the offset into the Float32Array of where the element starts, you are then responsible to copy your data using this offset.
+    * Allocating an Element will return a instance of DynamicFloatArrayElement which contains the offset into the Float32Array of where the element starts, you are then responsible to copy your data using this offset.
     * Beware, calling pack() may change the offset of some Entries because this method will defrag the Float32Array to replace empty elements by moving allocated ones at their location.
     * Beware, calling pack() may change the offset of some Entries because this method will defrag the Float32Array to replace empty elements by moving allocated ones at their location.
-     * This method will retrun an ArrayBufferView on the existing Float32Array that describes the occupied elements. Use this View to update the WebGLBuffer and NOT the "buffer" field of the class. The pack() method won't shrink/reallocate the buffer to keep it GC friendly, all the empty space will be put at the end of the buffer, the method just ensure there're no "free holes". 
+     * This method will return an ArrayBufferView on the existing Float32Array that describes the used elements. Use this View to update the WebGLBuffer and NOT the "buffer" field of the class. The pack() method won't shrink/reallocate the buffer to keep it GC friendly, all the empty space will be put at the end of the buffer, the method just ensure there're no "free holes". 
     */
     */
     export class DynamicFloatArray {
     export class DynamicFloatArray {
         /**
         /**
          * Construct an instance of the dynamic float array
          * Construct an instance of the dynamic float array
-         * @param stride size of one entry in float (i.e. not bytes!)
-         * @param initialEntryCount the number of available entries at construction
+         * @param stride size of one element in float (i.e. not bytes!)
+         * @param initialElementCount the number of available entries at construction
          */
          */
-        constructor(stride: number, initialEntryCount: number) {
-            this.stride = stride;
-            this.entryCount = initialEntryCount;
-            this.buffer = new Float32Array(stride * initialEntryCount);
-
-            this.allEntries = new Array<DynamicFloatArrayEntry>(initialEntryCount);
-            this.freeEntries = new Array<DynamicFloatArrayEntry>(initialEntryCount);
-
-            for (let i = 0; i < initialEntryCount; i++) {
-                let entry = new DynamicFloatArrayEntry();
-                entry.offset = i * stride;
-
-                this.allEntries[i] = entry;
-                this.freeEntries[initialEntryCount - i - 1] = entry;
+        constructor(stride: number, initialElementCount: number) {
+            this._stride = stride;
+            this.buffer = new Float32Array(stride * initialElementCount);
+            this._lastUsed = 0;
+            this._firstFree = 0;
+            this._allEntries = new Array<DynamicFloatArrayElementInfo>(initialElementCount);
+            this._freeEntries = new Array<DynamicFloatArrayElementInfo>(initialElementCount);
+
+            for (let i = 0; i < initialElementCount; i++) {
+                let element = new DynamicFloatArrayElementInfo();
+                element.offset = i * stride;
+
+                this._allEntries[i] = element;
+                this._freeEntries[initialElementCount - i - 1] = element;
             }
             }
         }
         }
 
 
-        allocElement(): DynamicFloatArrayEntry {
-            if (this.freeEntries.length === 0) {
+        /**
+         * Allocate an element in the array.
+         * @return the element info instance that contains the offset into the main buffer of the element's location.
+         * Beware, this offset may change when you call pack()
+         */
+        allocElement(): DynamicFloatArrayElementInfo {
+            if (this._freeEntries.length === 0) {
                 this._growBuffer();
                 this._growBuffer();
             }
             }
 
 
-            var entry = this.freeEntries.pop();
-            return entry;
+            let el = this._freeEntries.pop();
+            this._lastUsed = Math.max(el.offset, this._lastUsed);
+
+            if (el.offset === this._firstFree) {
+                if (this._freeEntries.length > 0) {
+                    this._firstFree = this._freeEntries[this._freeEntries.length - 1].offset;
+                } else {
+                    this._firstFree += this._stride;
+                }
+            }
+            return el;
         }
         }
 
 
-        freeElement(entry: DynamicFloatArrayEntry) {
-            this.freeEntries.push(entry);
+        /**
+         * Free the element corresponding to the given element info
+         * @param elInfo the element that describe the allocated element
+         */
+        freeElement(elInfo: DynamicFloatArrayElementInfo) {
+            this._firstFree = Math.min(elInfo.offset, this._firstFree);
+            this._freeEntries.push(elInfo);
         }
         }
 
 
         /**
         /**
-         * This method will pack all the occupied elements into a linear sequence and free the rest.
-         * Instances of DynamicFloatArrayEntry may have their 'offset' member changed as data could be copied from one location to another, so be sure to read/write your data based on the value inside this member after you called pack().
+         * This method will pack all the used elements into a linear sequence and put all the free space at the end.
+         * Instances of DynamicFloatArrayElement may have their 'offset' member changed as data could be copied from one location to another, so be sure to read/write your data based on the value inside this member after you called pack().
+         * @return the subarray that is the view of the used elements area, you can use it as a source to update a WebGLBuffer
          */
          */
         pack(): Float32Array {
         pack(): Float32Array {
+
             // no free slot? no need to pack
             // no free slot? no need to pack
-            if (this.freeEntries.length === 0) {
+            if (this._freeEntries.length === 0) {
                 return this.buffer;
                 return this.buffer;
             }
             }
 
 
-            let s = this.stride;
-            let sortedFree = this.freeEntries.sort((a, b) => a.offset - b.offset);
-            let sortedAll = this.allEntries.sort((a, b) => a.offset - b.offset);
+            // If the buffer is already packed the last used will always be lower than the first free
+            if (this._lastUsed < this._firstFree) {
+                let elementsBuffer = this.buffer.subarray(0, this._lastUsed + this._stride);
+                return elementsBuffer;
+            }
+
+            let s = this._stride;
 
 
-            // Make sure there's a free element at the very end, we need it to create a range where we'll move the occupied elements that may appear before
-            let lastFree = new DynamicFloatArrayEntry();
-            lastFree.offset = this.entryCount * s;
-            sortedFree.push(lastFree);
+            // Make sure there's a free element at the very end, we need it to create a range where we'll move the used elements that may appear before
+            let lastFree = new DynamicFloatArrayElementInfo();
+            lastFree.offset = this.totalElementCount * s;
+            this._freeEntries.push(lastFree);
+
+            let sortedFree = this._freeEntries.sort((a, b) => a.offset - b.offset);
+            let sortedAll = this._allEntries.sort((a, b) => a.offset - b.offset);
 
 
             let firstFreeSlotOffset = sortedFree[0].offset;
             let firstFreeSlotOffset = sortedFree[0].offset;
             let freeZoneSize = 1;
             let freeZoneSize = 1;
 
 
+            // The sortedFree array is sorted in reverse, first free at the end, last free at the beginning, so we loop from the end to beginning
             let prevOffset = sortedFree[0].offset;
             let prevOffset = sortedFree[0].offset;
             for (let i = 1; i < sortedFree.length; i++) {
             for (let i = 1; i < sortedFree.length; i++) {
                 let curFree = sortedFree[i];
                 let curFree = sortedFree[i];
@@ -85,34 +114,34 @@
                 }
                 }
 
 
                 // Distance is bigger, which means there's x element between the previous free and this one
                 // Distance is bigger, which means there's x element between the previous free and this one
-                let occupiedRange = (distance / s) - 1;
+                let usedRange = (distance / s) - 1;
 
 
                 // Two cases the free zone is smaller than the data to move or bigger
                 // Two cases the free zone is smaller than the data to move or bigger
 
 
                 // Copy what can fit in the free zone
                 // Copy what can fit in the free zone
                 let curMoveOffset = curOffset - s;
                 let curMoveOffset = curOffset - s;
-                let copyCount = Math.min(freeZoneSize, occupiedRange);
+                let copyCount = Math.min(freeZoneSize, usedRange);
                 for (let j = 0; j < copyCount; j++) {
                 for (let j = 0; j < copyCount; j++) {
                     let freeI = firstFreeSlotOffset / s;
                     let freeI = firstFreeSlotOffset / s;
                     let curI = curMoveOffset / s;
                     let curI = curMoveOffset / s;
 
 
-                    let moveEntry = sortedAll[curI];
-                    this._moveEntry(moveEntry, firstFreeSlotOffset);
-                    let replacedEntry = sortedAll[freeI];
+                    let moveEl = sortedAll[curI];
+                    this._moveElement(moveEl, firstFreeSlotOffset);
+                    let replacedEl = sortedAll[freeI];
 
 
-                    // set the offset of the element entry we replace with a value that will make it discard at the end of the method
-                    replacedEntry.offset = curMoveOffset;
+                    // set the offset of the element element we replace with a value that will make it discard at the end of the method
+                    replacedEl.offset = curMoveOffset;
 
 
-                    // Swap the entry we moved and the one it replaced in the sorted array to reflect the action we've made
-                    sortedAll[freeI] = moveEntry;
-                    sortedAll[curI] = replacedEntry;
+                    // Swap the element we moved and the one it replaced in the sorted array to reflect the action we've made
+                    sortedAll[freeI] = moveEl;
+                    sortedAll[curI] = replacedEl;
 
 
                     curMoveOffset -= s;
                     curMoveOffset -= s;
                     firstFreeSlotOffset += s;
                     firstFreeSlotOffset += s;
                 }
                 }
 
 
                 // Free Zone is smaller or equal so it's no longer a free zone, set the new one to the current location
                 // Free Zone is smaller or equal so it's no longer a free zone, set the new one to the current location
-                if (freeZoneSize <= occupiedRange) {
+                if (freeZoneSize <= usedRange) {
                     firstFreeSlotOffset = curOffset;
                     firstFreeSlotOffset = curOffset;
                     freeZoneSize = 1;
                     freeZoneSize = 1;
                 }
                 }
@@ -126,52 +155,92 @@
                 prevOffset = curOffset;
                 prevOffset = curOffset;
             }
             }
 
 
-            // Allocate a new buffer with the perfect size, copy the content, update free data
-            this.entryCount = firstFreeSlotOffset / s;
-            let optimizedBuffer = this.buffer.subarray(0, firstFreeSlotOffset);
-            this.freeEntries.splice(0);
-            this.allEntries = sortedAll.slice(0, this.entryCount);
-            return optimizedBuffer;
+            let elementsBuffer = this.buffer.subarray(0, firstFreeSlotOffset);
+            this._lastUsed = firstFreeSlotOffset - s;
+            this._firstFree = firstFreeSlotOffset;
+            sortedFree.pop();             // Remove the last free because that's the one we added at the start of the method
+            this._freeEntries = sortedFree.sort((a, b) => b.offset - a.offset);
+            this._allEntries = sortedAll;
+
+            return elementsBuffer;
         }
         }
 
 
-        private _moveEntry(entry: DynamicFloatArrayEntry, destOffset: number) {
-            for (let i = 0; i < this.stride; i++) {
-                this.buffer[destOffset + i] = this.buffer[entry.offset + i];
+        private _moveElement(element: DynamicFloatArrayElementInfo, destOffset: number) {
+            for (let i = 0; i < this._stride; i++) {
+                this.buffer[destOffset + i] = this.buffer[element.offset + i];
             }
             }
 
 
-            entry.offset = destOffset;
+            element.offset = destOffset;
         }
         }
 
 
         private _growBuffer() {
         private _growBuffer() {
             // Allocate the new buffer with 50% more entries, copy the content of the current one
             // Allocate the new buffer with 50% more entries, copy the content of the current one
-            let newEntryCount = this.entryCount * 1.5;
-            let newBuffer = new Float32Array(newEntryCount * this.stride);
+            let newElCount = this.totalElementCount * 1.5;
+            let newBuffer = new Float32Array(newElCount * this._stride);
             newBuffer.set(this.buffer);
             newBuffer.set(this.buffer);
 
 
-            let addedCount = newEntryCount - this.entryCount;
-            this.allEntries.length += addedCount;
-            this.freeEntries.length += addedCount;
+            let addedCount = newElCount - this.totalElementCount;
+            this._allEntries.length += addedCount;
+            this._freeEntries.length += addedCount;
 
 
-            for (let i = this.entryCount; i < newEntryCount; i++) {
-                let entry = new DynamicFloatArrayEntry();
-                entry.offset = i * this.stride;
+            for (let i = this.totalElementCount; i < newElCount; i++) {
+                let el = new DynamicFloatArrayElementInfo();
+                el.offset = i * this._stride;
 
 
-                this.allEntries[i] = entry;
-                this.freeEntries[i] = entry;
+                this._allEntries[i] = el;
+                this._freeEntries[i] = el;
             }
             }
 
 
             this.buffer = newBuffer;
             this.buffer = newBuffer;
-            this.entryCount = newEntryCount;
+            this.totalElementCount = newElCount;
         }
         }
 
 
+        /**
+         * This is the main buffer, all elements are stored inside, you use the DynamicFloatArrayElement instance of a given element to know its location into this buffer, then you have the responsability to perform write operations in this buffer at the right location!
+         * Don't use this buffer for a WebGL bufferSubData() operation, but use the one returned by the pack() method.
+         */
         buffer: Float32Array;
         buffer: Float32Array;
-        entryCount: number;
-        stride: number;
-        allEntries: Array<DynamicFloatArrayEntry>;
-        freeEntries: Array<DynamicFloatArrayEntry>;
+
+        /**
+         * Get the total count of entries that can fit in the current buffer
+         * @returns the elements count
+         */
+        get totalElementCount(): number {
+            return this._allEntries.length;
+        }
+
+        /**
+         * Get the count of free entries that can still be allocated without resizing the buffer
+         * @returns the free elements count
+         */
+        get freeElementCount(): number {
+            return this._freeEntries.length;
+        }
+
+        /**
+         * Get the count of allocated elements
+         * @returns the allocated elements count
+         */
+        get usedElementCount(): number {
+            return this._allEntries.length - this._freeEntries.length;
+        }
+
+        /**
+         * Return the size of one element in float
+         * @returns the size in float
+         */
+        get stride(): number {
+            return this._stride;
+        }
+
+        private _allEntries: Array<DynamicFloatArrayElementInfo>;
+        private _freeEntries: Array<DynamicFloatArrayElementInfo>;
+        private _stride: number;
+        private _lastUsed: number;
+        private _firstFree: number;
     }
     }
 
 
-    export class DynamicFloatArrayEntry {
+    export class DynamicFloatArrayElementInfo {
         offset: number;
         offset: number;
     }
     }
 }
 }