/**
* Copyright 2008 - 2015 The Loon Game Engine Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loon
* @author cping
* @email:javachenpeng@yahoo.com
* @version 0.5
*/
package loon.opengl;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import loon.LSystem;
public class VertexBufferObject implements VertexData {
private VertexAttributes attributes;
private FloatBuffer buffer;
private ByteBuffer byteBuffer;
private boolean ownsBuffer;
private int bufferHandle;
private int usage;
boolean isDirty = false;
boolean isBound = false;
public VertexBufferObject(boolean isStatic, int numVertices,
VertexAttribute... attributes) {
this(isStatic, numVertices, new VertexAttributes(attributes));
}
public VertexBufferObject(boolean isStatic, int numVertices,
VertexAttributes attributes) {
bufferHandle = LSystem.base().graphics().gl.glGenBuffer();
ByteBuffer data = LSystem.base().support()
.newUnsafeByteBuffer(attributes.vertexSize * numVertices);
data.limit(0);
setBuffer(data, true, attributes);
setUsage(isStatic ? GL20.GL_STATIC_DRAW : GL20.GL_DYNAMIC_DRAW);
}
protected VertexBufferObject(int usage, ByteBuffer data,
boolean ownsBuffer, VertexAttributes attributes) {
bufferHandle = LSystem.base().graphics().gl.glGenBuffer();
setBuffer(data, ownsBuffer, attributes);
setUsage(usage);
}
@Override
public VertexAttributes getAttributes() {
return attributes;
}
@Override
public int getNumVertices() {
return buffer.limit() * 4 / attributes.vertexSize;
}
@Override
public int getNumMaxVertices() {
return byteBuffer.capacity() / attributes.vertexSize;
}
@Override
public FloatBuffer getBuffer() {
isDirty = true;
return buffer;
}
protected void setBuffer(Buffer data, boolean ownsBuffer,
VertexAttributes value) {
if (isBound) {
throw LSystem.runThrow(
"Cannot change attributes while VBO is bound");
}
if (this.ownsBuffer && byteBuffer != null) {
LSystem.base().support().disposeUnsafeByteBuffer(byteBuffer);
}
attributes = value;
if (data instanceof ByteBuffer) {
byteBuffer = (ByteBuffer) data;
} else {
throw LSystem.runThrow("Only ByteBuffer is currently supported");
}
this.ownsBuffer = ownsBuffer;
final int l = byteBuffer.limit();
byteBuffer.limit(byteBuffer.capacity());
buffer = byteBuffer.asFloatBuffer();
byteBuffer.limit(l);
buffer.limit(l / 4);
}
private void bufferChanged() {
if (isBound) {
LSystem.base().graphics().gl.glBufferData(GL20.GL_ARRAY_BUFFER,
byteBuffer.limit(), byteBuffer, usage);
isDirty = false;
}
}
@Override
public void setVertices(float[] vertices, int offset, int count) {
isDirty = true;
if (LSystem.base().support().isNative()) {
LSystem.base().support().copy(vertices, byteBuffer, offset, count);
buffer.position(0);
buffer.limit(count);
} else {
buffer.clear();
buffer.put(vertices, offset, count).flip();
}
bufferChanged();
}
@Override
public void updateVertices(int targetOffset, float[] vertices,
int sourceOffset, int count) {
isDirty = true;
final int pos = byteBuffer.position();
byteBuffer.position(targetOffset * 4);
LSystem.base().support()
.copy(vertices, byteBuffer, sourceOffset, count);
byteBuffer.position(pos);
buffer.position(0);
bufferChanged();
}
protected int getUsage() {
return usage;
}
protected void setUsage(int value) {
if (isBound)
throw LSystem.runThrow("Cannot change usage while VBO is bound");
usage = value;
}
@Override
public void bind(ShaderProgram shader) {
bind(shader, null);
}
@Override
public void bind(ShaderProgram shader, int[] locations) {
final GL20 gl = LSystem.base().graphics().gl;
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, bufferHandle);
if (isDirty) {
byteBuffer.limit(buffer.limit() * 4);
gl.glBufferData(GL20.GL_ARRAY_BUFFER, byteBuffer.limit(),
byteBuffer, usage);
isDirty = false;
}
final int numAttributes = attributes.size();
if (locations == null) {
for (int i = 0; i < numAttributes; i++) {
final VertexAttribute attribute = attributes.get(i);
final int location = shader
.getAttributeLocation(attribute.alias);
if (location < 0){
continue;
}
shader.enableVertexAttribute(location);
shader.setVertexAttribute(location, attribute.numComponents,
attribute.type, attribute.normalized,
attributes.vertexSize, attribute.offset);
}
} else {
for (int i = 0; i < numAttributes; i++) {
final VertexAttribute attribute = attributes.get(i);
final int location = locations[i];
if (location < 0){
continue;
}
shader.enableVertexAttribute(location);
shader.setVertexAttribute(location, attribute.numComponents,
attribute.type, attribute.normalized,
attributes.vertexSize, attribute.offset);
}
}
isBound = true;
}
@Override
public void unbind(final ShaderProgram shader) {
unbind(shader, null);
}
@Override
public void unbind(final ShaderProgram shader, final int[] locations) {
final GL20 gl = LSystem.base().graphics().gl;
final int numAttributes = attributes.size();
if (locations == null) {
for (int i = 0; i < numAttributes; i++) {
shader.disableVertexAttribute(attributes.get(i).alias);
}
} else {
for (int i = 0; i < numAttributes; i++) {
final int location = locations[i];
if (location >= 0)
shader.disableVertexAttribute(location);
}
}
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
isBound = false;
}
@Override
public void invalidate() {
bufferHandle = LSystem.base().graphics().gl.glGenBuffer();
isDirty = true;
}
@Override
public void close() {
GL20 gl = LSystem.base().graphics().gl;
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
gl.glDeleteBuffer(bufferHandle);
bufferHandle = 0;
if (ownsBuffer) {
LSystem.base().support().disposeUnsafeByteBuffer(byteBuffer);
}
buffer = null;
byteBuffer = null;
}
}