From a34a3c036a2b4afecf15947254981c63c770d436 Mon Sep 17 00:00:00 2001 From: Guzul Date: Mon, 25 Sep 2023 18:10:02 +0300 Subject: [PATCH 1/3] [#33] Fix limitation of 3D path length The main reason the Path has limited number of points to be drawn is that vertexBuffer indices are UNSIGNED_SHORT type and thus unique index values are limited due to "Max Short" value. Add IntBufferObject, make Path use it as drawState.elementBuffer. Change interiorElements, outlineElements and verticalElements type to Int. modified: worldwind/src/androidMain/kotlin/earth/worldwind/util/kgl/AndroidKgl.kt modified: worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawShapeState.kt new file: worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/IntBufferObject.kt modified: worldwind/src/commonMain/kotlin/earth/worldwind/shape/Path.kt modified: worldwind/src/commonMain/kotlin/earth/worldwind/util/kgl/Kgl.kt --- .../earth/worldwind/util/kgl/AndroidKgl.kt | 4 ++- .../earth/worldwind/draw/DrawShapeState.kt | 4 +-- .../render/buffer/IntBufferObject.kt | 22 +++++++++++++++ .../kotlin/earth/worldwind/shape/Path.kt | 28 +++++++++---------- .../kotlin/earth/worldwind/util/kgl/Kgl.kt | 1 + 5 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/IntBufferObject.kt diff --git a/worldwind/src/androidMain/kotlin/earth/worldwind/util/kgl/AndroidKgl.kt b/worldwind/src/androidMain/kotlin/earth/worldwind/util/kgl/AndroidKgl.kt index 28904ae64..015eb2caf 100644 --- a/worldwind/src/androidMain/kotlin/earth/worldwind/util/kgl/AndroidKgl.kt +++ b/worldwind/src/androidMain/kotlin/earth/worldwind/util/kgl/AndroidKgl.kt @@ -3,6 +3,7 @@ package earth.worldwind.util.kgl import android.opengl.GLES20 import java.nio.ByteBuffer import java.nio.FloatBuffer +import java.nio.IntBuffer import java.nio.ShortBuffer class AndroidKgl : Kgl { @@ -80,7 +81,8 @@ class AndroidKgl : Kgl { override fun bufferData(target: Int, size: Int, sourceData: ShortArray, usage: Int, offset: Int) = GLES20.glBufferData(target, size, ShortBuffer.wrap(sourceData, offset, size / 2), usage) - + override fun bufferData(target: Int, size: Int, sourceData: IntArray, usage: Int, offset: Int) = + GLES20.glBufferData(target, size, IntBuffer.wrap(sourceData, offset, size / 4), usage) override fun bufferData(target: Int, size: Int, sourceData: FloatArray, usage: Int, offset: Int) = GLES20.glBufferData(target, size, FloatBuffer.wrap(sourceData, offset, size / 4), usage) diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawShapeState.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawShapeState.kt index 3661356ca..86c291b9d 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawShapeState.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/draw/DrawShapeState.kt @@ -4,8 +4,8 @@ import earth.worldwind.geom.Matrix3 import earth.worldwind.geom.Vec3 import earth.worldwind.render.Color import earth.worldwind.render.Texture +import earth.worldwind.render.buffer.AbstractBufferObject import earth.worldwind.render.buffer.FloatBufferObject -import earth.worldwind.render.buffer.ShortBufferObject import earth.worldwind.render.program.BasicShaderProgram open class DrawShapeState internal constructor() { @@ -15,7 +15,7 @@ open class DrawShapeState internal constructor() { var program: BasicShaderProgram? = null var vertexBuffer: FloatBufferObject? = null - var elementBuffer: ShortBufferObject? = null + var elementBuffer: AbstractBufferObject? = null val vertexOrigin = Vec3() var vertexStride = 0 var enableCullFace = true diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/IntBufferObject.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/IntBufferObject.kt new file mode 100644 index 000000000..9358ec211 --- /dev/null +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/IntBufferObject.kt @@ -0,0 +1,22 @@ +package earth.worldwind.render.buffer + +import earth.worldwind.draw.DrawContext +import earth.worldwind.util.kgl.GL_STATIC_DRAW + +open class IntBufferObject(target: Int, array: IntArray, size: Int = array.size) : AbstractBufferObject(target, size * 4) { + protected var array: IntArray? = array + + override fun release(dc: DrawContext) { + super.release(dc) + array = null // array can be non-null if the object has not been bound + } + + override fun bindBuffer(dc: DrawContext): Boolean { + array?.let{ loadBuffer(dc) }.also { array = null } + return super.bindBuffer(dc) + } + + override fun loadBufferObjectData(dc: DrawContext) { + array?.let { dc.gl.bufferData(target, byteCount, it, GL_STATIC_DRAW) } + } +} \ No newline at end of file diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Path.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Path.kt index beb40d994..ad4679397 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Path.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Path.kt @@ -7,7 +7,7 @@ import earth.worldwind.draw.DrawableSurfaceShape import earth.worldwind.geom.* import earth.worldwind.render.* import earth.worldwind.render.buffer.FloatBufferObject -import earth.worldwind.render.buffer.ShortBufferObject +import earth.worldwind.render.buffer.IntBufferObject import earth.worldwind.render.image.ImageOptions import earth.worldwind.render.image.ResamplingMode import earth.worldwind.render.image.WrapMode @@ -27,9 +27,9 @@ open class Path @JvmOverloads constructor( protected var vertexArray = FloatArray(0) protected var vertexIndex = 0 // TODO Use ShortArray instead of mutableListOf to avoid unnecessary memory re-allocations - protected val interiorElements = mutableListOf() - protected val outlineElements = mutableListOf() - protected val verticalElements = mutableListOf() + protected val interiorElements = mutableListOf() + protected val outlineElements = mutableListOf() + protected val verticalElements = mutableListOf() protected lateinit var vertexBufferKey: Any protected lateinit var elementBufferKey: Any protected val vertexOrigin = Vec3() @@ -93,7 +93,7 @@ open class Path @JvmOverloads constructor( // Assemble the drawable's OpenGL element buffer object. drawState.elementBuffer = rc.getBufferObject(elementBufferKey) { - ShortBufferObject(GL_ELEMENT_ARRAY_BUFFER, (interiorElements + outlineElements + verticalElements).toShortArray()) + IntBufferObject(GL_ELEMENT_ARRAY_BUFFER, (interiorElements + outlineElements + verticalElements).toIntArray()) } // Configure the drawable's vertex texture coordinate attribute. @@ -118,7 +118,7 @@ open class Path @JvmOverloads constructor( drawState.lineWidth(activeAttributes.outlineWidth + if (isSurfaceShape) 0.5f else 0f) drawState.drawElements( GL_LINE_STRIP, outlineElements.size, - GL_UNSIGNED_SHORT, interiorElements.size * 2 + GL_UNSIGNED_INT, interiorElements.size * Int.SIZE_BYTES ) } @@ -131,7 +131,7 @@ open class Path @JvmOverloads constructor( drawState.lineWidth(activeAttributes.outlineWidth) drawState.drawElements( GL_LINES, verticalElements.size, - GL_UNSIGNED_SHORT, interiorElements.size * 2 + outlineElements.size * 2 + GL_UNSIGNED_INT, (interiorElements.size + outlineElements.size) * Int.SIZE_BYTES ) } @@ -140,7 +140,7 @@ open class Path @JvmOverloads constructor( drawState.color(if (rc.isPickMode) pickColor else activeAttributes.interiorColor) drawState.drawElements( GL_TRIANGLE_STRIP, interiorElements.size, - GL_UNSIGNED_SHORT, 0 + GL_UNSIGNED_INT, 0 ) } @@ -252,25 +252,25 @@ open class Path @JvmOverloads constructor( vertexArray[vertexIndex++] = (latitude.inDegrees - vertexOrigin.y).toFloat() vertexArray[vertexIndex++] = (altitude - vertexOrigin.z).toFloat() vertexArray[vertexIndex++] = texCoord1d.toFloat() - outlineElements.add(vertex.toShort()) + outlineElements.add(vertex) } else { vertexArray[vertexIndex++] = (point.x - vertexOrigin.x).toFloat() vertexArray[vertexIndex++] = (point.y - vertexOrigin.y).toFloat() vertexArray[vertexIndex++] = (point.z - vertexOrigin.z).toFloat() vertexArray[vertexIndex++] = texCoord1d.toFloat() - outlineElements.add(vertex.toShort()) + outlineElements.add(vertex) if (isExtrude) { point = rc.geographicToCartesian(latitude, longitude, 0.0, altitudeMode, this.point) vertexArray[vertexIndex++] = (point.x - vertexOrigin.x).toFloat() vertexArray[vertexIndex++] = (point.y - vertexOrigin.y).toFloat() vertexArray[vertexIndex++] = (point.z - vertexOrigin.z).toFloat() vertexArray[vertexIndex++] = 0f /*unused*/ - interiorElements.add(vertex.toShort()) - interiorElements.add(vertex.inc().toShort()) + interiorElements.add(vertex) + interiorElements.add(vertex.inc()) } if (isExtrude && !intermediate) { - verticalElements.add(vertex.toShort()) - verticalElements.add(vertex.inc().toShort()) + verticalElements.add(vertex) + verticalElements.add(vertex.inc()) } } return vertex diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/util/kgl/Kgl.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/util/kgl/Kgl.kt index d6fef09d3..01dff4e5d 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/util/kgl/Kgl.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/util/kgl/Kgl.kt @@ -412,6 +412,7 @@ interface Kgl { fun createBuffer(): KglBuffer fun bindBuffer(target: Int, buffer: KglBuffer) fun bufferData(target: Int, size: Int, sourceData: ShortArray, usage: Int, offset: Int = 0) + fun bufferData(target: Int, size: Int, sourceData: IntArray, usage: Int, offset: Int = 0) fun bufferData(target: Int, size: Int, sourceData: FloatArray, usage: Int, offset: Int = 0) fun deleteBuffer(buffer: KglBuffer) From ed21431ee34956f87f22dc70c31521f3e48d4eaa Mon Sep 17 00:00:00 2001 From: Guzul Date: Mon, 25 Sep 2023 21:28:39 +0300 Subject: [PATCH 2/3] Add bufferData implementatation to JoglKgl and LwjglKgl --- .../src/jvmMain/kotlin/earth/worldwind/util/kgl/JoglKgl.kt | 3 +++ .../src/jvmMain/kotlin/earth/worldwind/util/kgl/LwjglKgl.kt | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/worldwind/src/jvmMain/kotlin/earth/worldwind/util/kgl/JoglKgl.kt b/worldwind/src/jvmMain/kotlin/earth/worldwind/util/kgl/JoglKgl.kt index 3e7748191..adad4fc18 100644 --- a/worldwind/src/jvmMain/kotlin/earth/worldwind/util/kgl/JoglKgl.kt +++ b/worldwind/src/jvmMain/kotlin/earth/worldwind/util/kgl/JoglKgl.kt @@ -93,6 +93,9 @@ class JoglKgl(private val gl: GL3ES3) : Kgl { override fun bufferData(target: Int, size: Int, sourceData: ShortArray, usage: Int, offset: Int) = gl.glBufferData(target, size.toLong(), ShortBuffer.wrap(sourceData, offset, sourceData.size - offset), usage) + override fun bufferData(target: Int, size: Int, sourceData: IntArray, usage: Int, offset: Int) = + gl.glBufferData(target, size.toLong(), IntBuffer.wrap(sourceData, offset, sourceData.size - offset), usage) + override fun bufferData(target: Int, size: Int, sourceData: FloatArray, usage: Int, offset: Int) = gl.glBufferData(target, size.toLong(), FloatBuffer.wrap(sourceData, offset, sourceData.size - offset), usage) diff --git a/worldwind/src/jvmMain/kotlin/earth/worldwind/util/kgl/LwjglKgl.kt b/worldwind/src/jvmMain/kotlin/earth/worldwind/util/kgl/LwjglKgl.kt index 010b2a40f..2a7e1c06c 100644 --- a/worldwind/src/jvmMain/kotlin/earth/worldwind/util/kgl/LwjglKgl.kt +++ b/worldwind/src/jvmMain/kotlin/earth/worldwind/util/kgl/LwjglKgl.kt @@ -3,6 +3,7 @@ package earth.worldwind.util.kgl import org.lwjgl.opengl.GL33 import java.nio.ByteBuffer import java.nio.FloatBuffer +import java.nio.IntBuffer import java.nio.ShortBuffer class LwjglKgl : Kgl { @@ -76,6 +77,9 @@ class LwjglKgl : Kgl { override fun bufferData(target: Int, size: Int, sourceData: ShortArray, usage: Int, offset: Int) = GL33.glBufferData(target, ShortBuffer.wrap(sourceData, offset, sourceData.size - offset), usage) + override fun bufferData(target: Int, size: Int, sourceData: IntArray, usage: Int, offset: Int) = + GL33.glBufferData(target, IntBuffer.wrap(sourceData, offset, sourceData.size - offset), usage) + override fun bufferData(target: Int, size: Int, sourceData: FloatArray, usage: Int, offset: Int) = GL33.glBufferData(target, FloatBuffer.wrap(sourceData, offset, sourceData.size - offset), usage) From 86ea9a4ece9d89ef1d0276534961c73a874f5775 Mon Sep 17 00:00:00 2001 From: Guzul Date: Thu, 28 Sep 2023 00:10:51 +0300 Subject: [PATCH 3/3] Make also polygons use IntBufferObject. Add basic `bufferData` implementation to AnddroidKgl, JoglKgl, LwjglKgl and WebKgl. modified: worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/FloatBufferObject.kt modified: worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/IntBufferObject.kt modified: worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/ShortBufferObject.kt modified: worldwind/src/commonMain/kotlin/earth/worldwind/shape/Polygon.kt modified: worldwind/src/jsMain/kotlin/earth/worldwind/util/kgl/WebKgl.kt --- .../render/buffer/FloatBufferObject.kt | 3 +- .../render/buffer/IntBufferObject.kt | 3 +- .../render/buffer/ShortBufferObject.kt | 3 +- .../kotlin/earth/worldwind/shape/Polygon.kt | 32 +++++++++---------- .../kotlin/earth/worldwind/util/kgl/WebKgl.kt | 6 ++++ 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/FloatBufferObject.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/FloatBufferObject.kt index e2908f9a0..3d4d6fb91 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/FloatBufferObject.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/FloatBufferObject.kt @@ -3,7 +3,8 @@ package earth.worldwind.render.buffer import earth.worldwind.draw.DrawContext import earth.worldwind.util.kgl.GL_STATIC_DRAW -open class FloatBufferObject(target: Int, array: FloatArray, size: Int = array.size) : AbstractBufferObject(target, size * 4) { +open class FloatBufferObject(target: Int, array: FloatArray, size: Int = array.size) + : AbstractBufferObject(target, size * Float.SIZE_BYTES) { protected var array: FloatArray? = array override fun release(dc: DrawContext) { diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/IntBufferObject.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/IntBufferObject.kt index 9358ec211..a95dcb6d5 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/IntBufferObject.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/IntBufferObject.kt @@ -3,7 +3,8 @@ package earth.worldwind.render.buffer import earth.worldwind.draw.DrawContext import earth.worldwind.util.kgl.GL_STATIC_DRAW -open class IntBufferObject(target: Int, array: IntArray, size: Int = array.size) : AbstractBufferObject(target, size * 4) { +open class IntBufferObject(target: Int, array: IntArray, size: Int = array.size) + : AbstractBufferObject(target, size * Int.SIZE_BYTES) { protected var array: IntArray? = array override fun release(dc: DrawContext) { diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/ShortBufferObject.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/ShortBufferObject.kt index ca85e590a..a9371142d 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/ShortBufferObject.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/render/buffer/ShortBufferObject.kt @@ -3,7 +3,8 @@ package earth.worldwind.render.buffer import earth.worldwind.draw.DrawContext import earth.worldwind.util.kgl.GL_STATIC_DRAW -open class ShortBufferObject(target: Int, array: ShortArray, size: Int = array.size) : AbstractBufferObject(target, size * 2) { +open class ShortBufferObject(target: Int, array: ShortArray, size: Int = array.size) + : AbstractBufferObject(target, size * Short.SIZE_BYTES) { protected var array: ShortArray? = array override fun release(dc: DrawContext) { diff --git a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Polygon.kt b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Polygon.kt index 7c30402e5..172bd08fb 100644 --- a/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Polygon.kt +++ b/worldwind/src/commonMain/kotlin/earth/worldwind/shape/Polygon.kt @@ -8,7 +8,7 @@ import earth.worldwind.geom.* import earth.worldwind.geom.Angle.Companion.degrees import earth.worldwind.render.* import earth.worldwind.render.buffer.FloatBufferObject -import earth.worldwind.render.buffer.ShortBufferObject +import earth.worldwind.render.buffer.IntBufferObject import earth.worldwind.render.image.ImageOptions import earth.worldwind.render.image.ResamplingMode import earth.worldwind.render.image.WrapMode @@ -30,10 +30,10 @@ open class Polygon @JvmOverloads constructor( protected var vertexArray = FloatArray(0) protected var vertexIndex = 0 // TODO Use ShortArray instead of mutableListOf to avoid unnecessary memory re-allocations - protected val topElements = mutableListOf() - protected val sideElements = mutableListOf() - protected val outlineElements = mutableListOf() - protected val verticalElements = mutableListOf() + protected val topElements = mutableListOf() + protected val sideElements = mutableListOf() + protected val outlineElements = mutableListOf() + protected val verticalElements = mutableListOf() protected var vertexBufferKey = nextCacheKey() protected var elementBufferKey = nextCacheKey() protected val vertexOrigin = Vec3() @@ -163,8 +163,8 @@ open class Polygon @JvmOverloads constructor( // Assemble the drawable's OpenGL element buffer object. drawState.elementBuffer = rc.getBufferObject(elementBufferKey) { - ShortBufferObject( - GL_ELEMENT_ARRAY_BUFFER, (topElements + sideElements + outlineElements + verticalElements).toShortArray() + IntBufferObject( + GL_ELEMENT_ARRAY_BUFFER, (topElements + sideElements + outlineElements + verticalElements).toIntArray() ) } if (isSurfaceShape || activeAttributes.interiorColor.alpha >= 1.0) { @@ -204,12 +204,12 @@ open class Polygon @JvmOverloads constructor( // Configure the drawable to display the shape's interior top. drawState.color(if (rc.isPickMode) pickColor else activeAttributes.interiorColor) drawState.texCoordAttrib(2 /*size*/, 12 /*offset in bytes*/) - drawState.drawElements(GL_TRIANGLES, topElements.size, GL_UNSIGNED_SHORT, 0 /*offset*/) + drawState.drawElements(GL_TRIANGLES, topElements.size, GL_UNSIGNED_INT, 0 /*offset*/) // Configure the drawable to display the shape's interior sides. if (isExtrude) { drawState.texture(null) - drawState.drawElements(GL_TRIANGLES, sideElements.size, GL_UNSIGNED_SHORT, topElements.size * 2 /*offset*/) + drawState.drawElements(GL_TRIANGLES, sideElements.size, GL_UNSIGNED_INT, topElements.size * Int.SIZE_BYTES /*offset*/) } } @@ -232,7 +232,7 @@ open class Polygon @JvmOverloads constructor( drawState.texCoordAttrib(1 /*size*/, 20 /*offset in bytes*/) drawState.drawElements( GL_LINES, outlineElements.size, - GL_UNSIGNED_SHORT, topElements.size * 2 + sideElements.size * 2 /*offset*/ + GL_UNSIGNED_INT, (topElements.size + sideElements.size) * Int.SIZE_BYTES /*offset*/ ) // Configure the drawable to display the shape's extruded verticals. @@ -242,7 +242,7 @@ open class Polygon @JvmOverloads constructor( drawState.texture(null) drawState.drawElements( GL_LINES, verticalElements.size, - GL_UNSIGNED_SHORT, topElements.size * 2 + sideElements.size * 2 + outlineElements.size * 2 /*offset*/ + GL_UNSIGNED_INT, (topElements.size + sideElements.size + outlineElements.size) * Int.SIZE_BYTES /*offset*/ ) } } @@ -396,8 +396,8 @@ open class Polygon @JvmOverloads constructor( vertexArray[vertexIndex++] = 0f /*unused*/ } if (isExtrude && type == VERTEX_ORIGINAL) { - verticalElements.add(vertex.toShort()) - verticalElements.add(vertex.inc().toShort()) + verticalElements.add(vertex) + verticalElements.add(vertex.inc()) } } return vertex @@ -440,9 +440,9 @@ open class Polygon @JvmOverloads constructor( } else { tessVertexCount = 0 // reset the vertex count and process one triangle } - val v0 = tessVertices[0].toShort() - val v1 = tessVertices[1].toShort() - val v2 = tessVertices[2].toShort() + val v0 = tessVertices[0] + val v1 = tessVertices[1] + val v2 = tessVertices[2] topElements.add(v0) topElements.add(v1) topElements.add(v2) diff --git a/worldwind/src/jsMain/kotlin/earth/worldwind/util/kgl/WebKgl.kt b/worldwind/src/jsMain/kotlin/earth/worldwind/util/kgl/WebKgl.kt index ce84e9272..f59967a6d 100644 --- a/worldwind/src/jsMain/kotlin/earth/worldwind/util/kgl/WebKgl.kt +++ b/worldwind/src/jsMain/kotlin/earth/worldwind/util/kgl/WebKgl.kt @@ -92,6 +92,12 @@ class WebKgl(val gl: WebGLRenderingContext) : Kgl { gl.bufferData(target, if (offset == 0 && len == arr.length) arr else arr.subarray(offset, offset + len), usage) } + override fun bufferData(target: Int, size: Int, sourceData: IntArray, usage: Int, offset: Int) { + val arr = sourceData.unsafeCast() + val len = size / 4 + gl.bufferData(target, if (offset == 0 && len == arr.length) arr else arr.subarray(offset, offset + len), usage) + } + override fun bufferData(target: Int, size: Int, sourceData: FloatArray, usage: Int, offset: Int) { val arr = sourceData.unsafeCast() val len = size / 4