/**
* 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.ByteBuffer;
import java.nio.FloatBuffer;
import loon.LSystem;
public class VertexBufferObjectSubData implements VertexData {
final VertexAttributes attributes;
final FloatBuffer buffer;
final ByteBuffer byteBuffer;
int bufferHandle;
final boolean isDirect;
final boolean isStatic;
final int usage;
boolean isDirty = false;
boolean isBound = false;
public VertexBufferObjectSubData(boolean isStatic, int numVertices,
VertexAttribute... attributes) {
this.isStatic = isStatic;
this.attributes = new VertexAttributes(attributes);
byteBuffer = LSystem.base().support().newByteBuffer(this.attributes.vertexSize
* numVertices);
isDirect = true;
usage = isStatic ? GL20.GL_STATIC_DRAW : GL20.GL_DYNAMIC_DRAW;
buffer = byteBuffer.asFloatBuffer();
bufferHandle = createBufferObject();
buffer.flip();
byteBuffer.flip();
}
private int createBufferObject() {
GL20 gl = LSystem.base().graphics().gl;
int result = gl.glGenBuffer();
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, result);
gl.glBufferData(GL20.GL_ARRAY_BUFFER, byteBuffer.capacity(), null,
usage);
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
return result;
}
@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;
}
private void bufferChanged() {
if (isBound) {
LSystem.base().graphics().gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0,
byteBuffer.limit(), byteBuffer);
isDirty = false;
}
}
@Override
public void setVertices(float[] vertices, int offset, int count) {
isDirty = true;
if (isDirect) {
LSystem.base().support().copy(vertices, byteBuffer, offset, count);
buffer.position(0);
buffer.limit(count);
} else {
buffer.clear();
buffer.put(vertices, offset, count);
buffer.flip();
byteBuffer.position(0);
byteBuffer.limit(buffer.limit() << 2);
}
bufferChanged();
}
@Override
public void updateVertices(int targetOffset, float[] vertices,
int sourceOffset, int count) {
isDirty = true;
if (isDirect) {
final int pos = byteBuffer.position();
byteBuffer.position(targetOffset * 4);
LSystem.base().support().copy(vertices, sourceOffset, byteBuffer, count);
byteBuffer.position(pos);
} else {
throw LSystem.runThrow("Buffer must be allocated direct."); // Should
// never
// happen
}
bufferChanged();
}
@Override
public void bind(final ShaderProgram shader) {
bind(shader, null);
}
@Override
public void bind(final ShaderProgram shader, final 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;
}
public void invalidate() {
bufferHandle = createBufferObject();
isDirty = true;
}
public int getBufferHandle() {
return bufferHandle;
}
@Override
public void close() {
GL20 gl = LSystem.base().graphics().gl;
gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
gl.glDeleteBuffer(bufferHandle);
bufferHandle = 0;
}
}