fixes + ssbo impl + instanced actor rearch
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
This commit is contained in:
parent
dd763b31a9
commit
f7d1bbed70
@ -730,6 +730,14 @@ DBody key refactor
|
||||
Audio for grass/leaves
|
||||
Re-enable UI test
|
||||
Fix foliage chunk-level radius being too low (causing grass cutoff)
|
||||
Turn on anti-aliasing for font generation
|
||||
|
||||
(09/12/2024)
|
||||
Alias fonts
|
||||
Fix audio handling bug
|
||||
SSBO implementation
|
||||
Rearch instanced actor
|
||||
ParticleService implementation
|
||||
|
||||
# TODO
|
||||
|
||||
|
||||
@ -13,8 +13,13 @@ import org.lwjgl.openal.AL10;
|
||||
*/
|
||||
public class AudioSource {
|
||||
|
||||
/**
|
||||
* An undefined source's id
|
||||
*/
|
||||
static final int UNDEFINED_ID = -1;
|
||||
|
||||
//The id for the source
|
||||
int sourceId;
|
||||
int sourceId = UNDEFINED_ID;
|
||||
|
||||
/**
|
||||
* Creates an audio source object
|
||||
@ -37,8 +42,10 @@ public class AudioSource {
|
||||
*/
|
||||
public void setBuffer(int bufferId) {
|
||||
stop();
|
||||
AL10.alSourcei(sourceId, AL10.AL_BUFFER, bufferId);
|
||||
Globals.audioEngine.checkError();
|
||||
if(isAllocated()){
|
||||
AL10.alSourcei(sourceId, AL10.AL_BUFFER, bufferId);
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,8 +53,10 @@ public class AudioSource {
|
||||
* @param position the position
|
||||
*/
|
||||
public void setPosition(Vector3f position) {
|
||||
AL10.alSource3f(sourceId, AL10.AL_POSITION, position.x, position.y, position.z);
|
||||
Globals.audioEngine.checkError();
|
||||
if(isAllocated()){
|
||||
AL10.alSource3f(sourceId, AL10.AL_POSITION, position.x, position.y, position.z);
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,8 +64,10 @@ public class AudioSource {
|
||||
* @param speed the speed
|
||||
*/
|
||||
public void setSpeed(Vector3f speed) {
|
||||
AL10.alSource3f(sourceId, AL10.AL_VELOCITY, speed.x, speed.y, speed.z);
|
||||
Globals.audioEngine.checkError();
|
||||
if(isAllocated()){
|
||||
AL10.alSource3f(sourceId, AL10.AL_VELOCITY, speed.x, speed.y, speed.z);
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,8 +75,10 @@ public class AudioSource {
|
||||
* @param time The time in seconds
|
||||
*/
|
||||
public void setOffset(float time){
|
||||
AL10.alSourcef(sourceId, AL11.AL_SEC_OFFSET, time);
|
||||
Globals.audioEngine.checkError();
|
||||
if(isAllocated()){
|
||||
AL10.alSourcef(sourceId, AL11.AL_SEC_OFFSET, time);
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,9 +86,11 @@ public class AudioSource {
|
||||
* @param gain the gain
|
||||
*/
|
||||
public void setGain(float gain) {
|
||||
LoggerInterface.loggerAudio.DEBUG("Set Gain: " + gain);
|
||||
AL10.alSourcef(sourceId, AL10.AL_GAIN, gain);
|
||||
Globals.audioEngine.checkError();
|
||||
if(isAllocated()){
|
||||
LoggerInterface.loggerAudio.DEBUG("Set Gain: " + gain);
|
||||
AL10.alSourcef(sourceId, AL10.AL_GAIN, gain);
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,16 +99,20 @@ public class AudioSource {
|
||||
* @param value The value to set the param to
|
||||
*/
|
||||
public void setProperty(int param, float value) {
|
||||
AL10.alSourcef(sourceId, param, value);
|
||||
Globals.audioEngine.checkError();
|
||||
if(isAllocated()){
|
||||
AL10.alSourcef(sourceId, param, value);
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the audio source
|
||||
*/
|
||||
public void play() {
|
||||
AL10.alSourcePlay(sourceId);
|
||||
Globals.audioEngine.checkError();
|
||||
if(isAllocated()){
|
||||
AL10.alSourcePlay(sourceId);
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,8 +120,11 @@ public class AudioSource {
|
||||
* @return True if it is playing, false otherwise
|
||||
*/
|
||||
public boolean isPlaying() {
|
||||
boolean isPlaying = AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING;
|
||||
Globals.audioEngine.checkError();
|
||||
boolean isPlaying = false;
|
||||
if(isAllocated()){
|
||||
isPlaying = AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING;
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
return isPlaying;
|
||||
}
|
||||
|
||||
@ -110,16 +132,20 @@ public class AudioSource {
|
||||
* Pauses the audio source
|
||||
*/
|
||||
public void pause() {
|
||||
AL10.alSourcePause(sourceId);
|
||||
Globals.audioEngine.checkError();
|
||||
if(isAllocated()){
|
||||
AL10.alSourcePause(sourceId);
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the audio source
|
||||
*/
|
||||
public void stop() {
|
||||
AL10.alSourceStop(sourceId);
|
||||
Globals.audioEngine.checkError();
|
||||
if(isAllocated()){
|
||||
AL10.alSourceStop(sourceId);
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,7 +153,16 @@ public class AudioSource {
|
||||
*/
|
||||
public void cleanup() {
|
||||
stop();
|
||||
sourceId = UNDEFINED_ID;
|
||||
AL10.alDeleteSources(sourceId);
|
||||
Globals.audioEngine.checkError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the audio source is allocated or not
|
||||
* @return true if allocated, false otherwise
|
||||
*/
|
||||
private boolean isAllocated(){
|
||||
return sourceId != UNDEFINED_ID;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
package electrosphere.client.entity.particle;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import electrosphere.engine.signal.Signal;
|
||||
import electrosphere.engine.signal.SignalServiceImpl;
|
||||
import electrosphere.renderer.actor.instance.StridedInstanceData;
|
||||
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
|
||||
|
||||
/**
|
||||
* The particle service
|
||||
*/
|
||||
public class ParticleService extends SignalServiceImpl {
|
||||
|
||||
/**
|
||||
* The maximum number of particles
|
||||
*/
|
||||
static final int MAX_PARTICLES = 1000;
|
||||
|
||||
/**
|
||||
* Path to the vertex shader
|
||||
*/
|
||||
static final String VERTEX_SHADER_PATH = "";
|
||||
|
||||
/**
|
||||
* Path to the fragment shader
|
||||
*/
|
||||
static final String FRAGMENT_SHADER_PATH = "";
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The instance data for the particles
|
||||
*/
|
||||
StridedInstanceData particleInstanceData;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ParticleService() {
|
||||
super("ParticleService");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
if(particleInstanceData != null){
|
||||
particleInstanceData.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Signal signal){
|
||||
boolean rVal = false;
|
||||
switch(signal.getType()){
|
||||
case RENDERING_ENGINE_READY: {
|
||||
List<HomogenousBufferTypes> types = Arrays.asList(new HomogenousBufferTypes[]{
|
||||
//position
|
||||
HomogenousBufferTypes.VEC3D,
|
||||
//rotation
|
||||
HomogenousBufferTypes.VEC4D,
|
||||
//scale
|
||||
HomogenousBufferTypes.VEC3D,
|
||||
//texture index
|
||||
HomogenousBufferTypes.INT,
|
||||
});
|
||||
this.particleInstanceData = new StridedInstanceData(MAX_PARTICLES,types,VERTEX_SHADER_PATH,FRAGMENT_SHADER_PATH);
|
||||
rVal = true;
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
}
|
||||
@ -12,6 +12,7 @@ import electrosphere.audio.VirtualAudioSourceManager;
|
||||
import electrosphere.audio.collision.HitboxAudioService;
|
||||
import electrosphere.audio.movement.MovementAudioService;
|
||||
import electrosphere.auth.AuthenticationManager;
|
||||
import electrosphere.client.entity.particle.ParticleService;
|
||||
import electrosphere.client.fluid.cells.FluidCellManager;
|
||||
import electrosphere.client.fluid.manager.ClientFluidManager;
|
||||
import electrosphere.client.foliagemanager.ClientFoliageManager;
|
||||
@ -292,6 +293,7 @@ public class Globals {
|
||||
//
|
||||
public static String particleBillboardModel;
|
||||
public static ParticleDefinition particleDefinition;
|
||||
public static ParticleService particleService;
|
||||
|
||||
public static ShaderOptionMap shaderOptionMap;
|
||||
|
||||
@ -505,6 +507,7 @@ public class Globals {
|
||||
//add services here
|
||||
Globals.signalSystem = (SignalSystem)serviceManager.registerService(new SignalSystem());
|
||||
Globals.elementService = (ElementService)serviceManager.registerService(new ElementService());
|
||||
Globals.particleService = (ParticleService)serviceManager.registerService(new ParticleService());
|
||||
serviceManager.instantiate();
|
||||
//
|
||||
//End service manager
|
||||
@ -515,6 +518,7 @@ public class Globals {
|
||||
Globals.signalSystem.registerService(SignalType.YOGA_APPLY, Globals.elementService);
|
||||
Globals.signalSystem.registerService(SignalType.YOGA_DESTROY, Globals.elementService);
|
||||
Globals.signalSystem.registerService(SignalType.UI_MODIFICATION, Globals.elementService);
|
||||
Globals.signalSystem.registerService(SignalType.RENDERING_ENGINE_READY, Globals.particleService);
|
||||
|
||||
|
||||
}
|
||||
@ -668,6 +672,7 @@ public class Globals {
|
||||
Globals.clientSynchronizationManager = null;
|
||||
Globals.server = null;
|
||||
Globals.serverSynchronizationManager = null;
|
||||
Globals.playerManager = null;
|
||||
Globals.javaPID = null;
|
||||
Globals.RENDER_FLAG_RENDER_SHADOW_MAP = true;
|
||||
Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = false;
|
||||
|
||||
@ -17,6 +17,11 @@ public class Signal {
|
||||
//
|
||||
ENGINE_SHUTDOWN,
|
||||
|
||||
//
|
||||
//RENDERING
|
||||
//
|
||||
RENDERING_ENGINE_READY,
|
||||
|
||||
//
|
||||
//UI
|
||||
//
|
||||
|
||||
@ -5,9 +5,11 @@ import java.util.Map;
|
||||
|
||||
import org.joml.Vector2i;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
import org.lwjgl.opengl.GL45;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.renderer.buffer.UniformBlockBinding;
|
||||
import electrosphere.renderer.shader.ShaderProgram;
|
||||
|
||||
/**
|
||||
@ -54,6 +56,11 @@ public class OpenGLState {
|
||||
//map of texture units and their corresponding texture pointers
|
||||
Map<Integer,Integer> unitToPointerMap;
|
||||
|
||||
/**
|
||||
* A map of index -> uniform buffer bound to that index
|
||||
*/
|
||||
Map<Integer,UniformBlockBinding> indexBlockMap;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the opengl state
|
||||
@ -69,7 +76,8 @@ public class OpenGLState {
|
||||
framebufferType = 0;
|
||||
framebufferPointer = 0;
|
||||
activeShader = null;
|
||||
unitToPointerMap = new HashMap<Integer,Integer>();
|
||||
this.unitToPointerMap = new HashMap<Integer,Integer>();
|
||||
this.indexBlockMap = new HashMap<Integer,UniformBlockBinding>();
|
||||
this.storeCurrentEnvironmentContraints();
|
||||
}
|
||||
|
||||
@ -309,4 +317,38 @@ public class OpenGLState {
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a buffer to a given uniform buffer block index
|
||||
* @param index the index
|
||||
* @param buffer the buffer
|
||||
*/
|
||||
public void glBindBufferBase(int index, UniformBlockBinding buffer){
|
||||
if(this.indexBlockMap.containsKey(index)){
|
||||
UniformBlockBinding currentBuffer = this.indexBlockMap.get(index);
|
||||
if(currentBuffer == null || currentBuffer != buffer){
|
||||
//does not already contain index, should bind
|
||||
this.indexBlockMap.put(index,buffer);
|
||||
GL45.glBindBufferBase(index, index, buffer.getId());
|
||||
Globals.renderingEngine.checkError();
|
||||
}
|
||||
} else {
|
||||
//does not already contain index, should bind
|
||||
this.indexBlockMap.put(index,buffer);
|
||||
GL45.glBindBufferBase(index, index, buffer.getId());
|
||||
Globals.renderingEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbinds a buffer from a given uniform buffer block index
|
||||
* @param index the index
|
||||
*/
|
||||
public void glUnbindBufferBase(int index){
|
||||
if(this.indexBlockMap.containsKey(index)){
|
||||
this.indexBlockMap.remove(index);
|
||||
GL45.glBindBufferBase(index, index, UniformBlockBinding.UNBIND_ADDRESS);
|
||||
Globals.renderingEngine.checkError();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -43,6 +43,7 @@ import org.lwjgl.opengl.GLDebugMessageCallback;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.engine.signal.Signal.SignalType;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.renderer.debug.DebugRendering;
|
||||
import electrosphere.renderer.framebuffer.Framebuffer;
|
||||
@ -462,6 +463,13 @@ public class RenderingEngine {
|
||||
float nearClip = 0.001f;
|
||||
Globals.projectionMatrix.setPerspective(verticalFOV, Globals.aspectRatio, nearClip, Globals.userSettings.getGraphicsViewDistance());
|
||||
Globals.viewMatrix.translation(new Vector3f(0.0f,0.0f,-3.0f));
|
||||
|
||||
/**
|
||||
* Alert everyone that the rendering engine is ready
|
||||
*/
|
||||
if(Globals.signalSystem != null){
|
||||
Globals.signalSystem.post(SignalType.RENDERING_ENGINE_READY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,399 @@
|
||||
package electrosphere.renderer.actor.instance;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.joml.Matrix4d;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4d;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.renderer.OpenGLState;
|
||||
import electrosphere.renderer.RenderPipelineState;
|
||||
import electrosphere.renderer.buffer.HomogenousInstancedArray;
|
||||
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
|
||||
import electrosphere.renderer.buffer.ShaderAttribute;
|
||||
|
||||
/**
|
||||
* An instance data that uses multiple homogenous buffers
|
||||
*/
|
||||
public class HomogenousInstanceData implements InstanceData {
|
||||
|
||||
//the capacity (number of instanced) of this data block. Defaults to 100
|
||||
int capacity = 1000;
|
||||
|
||||
//shader paths
|
||||
String vertexShaderPath;
|
||||
String fragmentShaderPath;
|
||||
|
||||
//the number of draw calls since the last clear operation
|
||||
int drawCalls = 0;
|
||||
|
||||
//The set of instanced actors to draw
|
||||
List<InstancedActor> actorQueue = null;
|
||||
//Map of actor to index in the buffers that are emitted
|
||||
Map<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>();
|
||||
//Map of index -> actor used for buffer evictions
|
||||
Map<Integer,InstancedActor> indexActorMap = new HashMap<Integer,InstancedActor>();
|
||||
|
||||
//list of all attribute indices in use by this instance data
|
||||
List<ShaderAttribute> attributeIndices = new LinkedList<ShaderAttribute>();
|
||||
//map of attribute -> buffer of attribute data
|
||||
Map<ShaderAttribute,Object> attributeCpuBufferMap = new HashMap<ShaderAttribute,Object>();
|
||||
//map of attribute -> gl HomogenousBuffer
|
||||
Map<ShaderAttribute,HomogenousInstancedArray> attributeGlBufferMap = new HashMap<ShaderAttribute,HomogenousInstancedArray>();
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param capacity Capacity of the buffer (number of elements) backing this data
|
||||
*/
|
||||
protected HomogenousInstanceData(int capacity, String vertexPath, String fragmentPath){
|
||||
this.capacity = capacity;
|
||||
this.vertexShaderPath = vertexPath;
|
||||
this.fragmentShaderPath = fragmentPath;
|
||||
actorQueue = new LinkedList<InstancedActor>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new type of attribute to this instance data block
|
||||
* @param shaderAttribute The shader attribute
|
||||
* @param type The type of the attribute
|
||||
*/
|
||||
protected void addDataType(ShaderAttribute shaderAttribute, HomogenousBufferTypes type){
|
||||
attributeIndices.add(shaderAttribute);
|
||||
switch(type){
|
||||
case VEC3F: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 3));
|
||||
} break;
|
||||
case VEC3D: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity * 3));
|
||||
} break;
|
||||
case VEC4F: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4));
|
||||
} break;
|
||||
case VEC4D: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity * 4));
|
||||
} break;
|
||||
case DOUBLE: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity));
|
||||
} break;
|
||||
case FLOAT: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity));
|
||||
} break;
|
||||
case INT: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createIntBuffer(capacity));
|
||||
} break;
|
||||
case MAT4F: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4 * 4));
|
||||
} break;
|
||||
case MAT4D: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4 * 4));
|
||||
} break;
|
||||
}
|
||||
if(shaderAttribute.isSingleIndex()){
|
||||
attributeGlBufferMap.put(shaderAttribute,HomogenousInstancedArray.createHomogenousInstancedArray(shaderAttribute.getIndex(), type, capacity));
|
||||
} else {
|
||||
attributeGlBufferMap.put(shaderAttribute,HomogenousInstancedArray.createHomogenousInstancedArray(shaderAttribute.getIndices(), type, capacity));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds an actor to be sorted in the queue
|
||||
* @param actor The actor to be sorted
|
||||
*/
|
||||
public void addInstance(InstancedActor actor){
|
||||
actorQueue.add(actor);
|
||||
drawCalls++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of entries that are to be drawn
|
||||
* @return The number of entries to be drawn
|
||||
*/
|
||||
public int getDrawCount(){
|
||||
return drawCalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the queue
|
||||
*/
|
||||
public void clearDrawQueue(){
|
||||
actorQueue.clear();
|
||||
drawCalls = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the buffers for the upcoming render call. The intention is to make this emberassingly parallel.
|
||||
*/
|
||||
public void fillBuffers(){
|
||||
int i = 0;
|
||||
//for some reason the limit is not being set correctly. This explicitly forces it for each buffer
|
||||
for(ShaderAttribute attribute : attributeIndices){
|
||||
switch(attributeGlBufferMap.get(attribute).getType()){
|
||||
case VEC3F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case VEC3D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case VEC4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case VEC4D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case MAT4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
// System.out.println(buffer.position() + " " + buffer.limit());
|
||||
} break;
|
||||
case MAT4D: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case DOUBLE: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case FLOAT: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case INT: {
|
||||
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
}
|
||||
}
|
||||
// actorQueue.sort(Comparator.naturalOrder());
|
||||
//buffer data
|
||||
for(InstancedActor actor : actorQueue){
|
||||
//push values to attribute buffers
|
||||
for(ShaderAttribute attribute : attributeIndices){
|
||||
switch(attributeGlBufferMap.get(attribute).getType()){
|
||||
case VEC3F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Vector3f vec = (Vector3f)actor.getAttributeValue(attribute);
|
||||
buffer.put(vec.x);
|
||||
buffer.put(vec.y);
|
||||
buffer.put(vec.z);
|
||||
} break;
|
||||
case VEC3D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Vector3d vec = (Vector3d)actor.getAttributeValue(attribute);
|
||||
buffer.put(vec.x);
|
||||
buffer.put(vec.y);
|
||||
buffer.put(vec.z);
|
||||
} break;
|
||||
case VEC4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Vector4f vec = (Vector4f)actor.getAttributeValue(attribute);
|
||||
buffer.put(vec.w);
|
||||
buffer.put(vec.x);
|
||||
buffer.put(vec.y);
|
||||
buffer.put(vec.z);
|
||||
} break;
|
||||
case VEC4D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Vector4d vec = (Vector4d)actor.getAttributeValue(attribute);
|
||||
buffer.put(vec.w);
|
||||
buffer.put(vec.x);
|
||||
buffer.put(vec.y);
|
||||
buffer.put(vec.z);
|
||||
} break;
|
||||
case MAT4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Matrix4f mat = (Matrix4f)actor.getAttributeValue(attribute);
|
||||
buffer.put(mat.m00());
|
||||
buffer.put(mat.m01());
|
||||
buffer.put(mat.m02());
|
||||
buffer.put(mat.m03());
|
||||
|
||||
buffer.put(mat.m10());
|
||||
buffer.put(mat.m11());
|
||||
buffer.put(mat.m12());
|
||||
buffer.put(mat.m13());
|
||||
|
||||
buffer.put(mat.m20());
|
||||
buffer.put(mat.m21());
|
||||
buffer.put(mat.m22());
|
||||
buffer.put(mat.m23());
|
||||
|
||||
buffer.put(mat.m30());
|
||||
buffer.put(mat.m31());
|
||||
buffer.put(mat.m32());
|
||||
buffer.put(mat.m33());
|
||||
} break;
|
||||
case MAT4D: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Matrix4d mat = (Matrix4d)actor.getAttributeValue(attribute);
|
||||
buffer.put((float)mat.m00());
|
||||
buffer.put((float)mat.m01());
|
||||
buffer.put((float)mat.m02());
|
||||
buffer.put((float)mat.m03());
|
||||
|
||||
buffer.put((float)mat.m10());
|
||||
buffer.put((float)mat.m11());
|
||||
buffer.put((float)mat.m12());
|
||||
buffer.put((float)mat.m13());
|
||||
|
||||
buffer.put((float)mat.m20());
|
||||
buffer.put((float)mat.m21());
|
||||
buffer.put((float)mat.m22());
|
||||
buffer.put((float)mat.m23());
|
||||
|
||||
buffer.put((float)mat.m30());
|
||||
buffer.put((float)mat.m31());
|
||||
buffer.put((float)mat.m32());
|
||||
buffer.put((float)mat.m33());
|
||||
} break;
|
||||
case DOUBLE: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.put((Double)actor.getAttributeValue(attribute));
|
||||
} break;
|
||||
case FLOAT: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.put((Float)actor.getAttributeValue(attribute));
|
||||
} break;
|
||||
case INT: {
|
||||
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.put((Integer)actor.getAttributeValue(attribute));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
//increment
|
||||
i++;
|
||||
if(i >= capacity){
|
||||
break;
|
||||
}
|
||||
}
|
||||
//reset all buffers
|
||||
flip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips the data buffer(s)
|
||||
*/
|
||||
public void flip(){
|
||||
//reset all buffers
|
||||
for(ShaderAttribute attribute : attributeIndices){
|
||||
switch(attributeGlBufferMap.get(attribute).getType()){
|
||||
case VEC3F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case VEC3D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case VEC4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case VEC4D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case MAT4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case MAT4D: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case DOUBLE: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case FLOAT: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case INT: {
|
||||
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a map of all attributes to the buffers of data for that attribute
|
||||
* @return The data buffers
|
||||
*/
|
||||
public Map<ShaderAttribute,Object> getCpuBufferMap(){
|
||||
return attributeCpuBufferMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a map of attribute name to gl homogenous buffer object
|
||||
* @return The map
|
||||
*/
|
||||
public Map<ShaderAttribute,HomogenousInstancedArray> getGlBufferMap(){
|
||||
return attributeGlBufferMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVertexShader(){
|
||||
return vertexShaderPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFragmentShader(){
|
||||
return fragmentShaderPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(OpenGLState openGLState, RenderPipelineState renderPipelineState){
|
||||
Map<ShaderAttribute,Object> buffers = this.getCpuBufferMap();
|
||||
Map<ShaderAttribute,HomogenousInstancedArray> glBufferMap = this.getGlBufferMap();
|
||||
for(ShaderAttribute attribute : buffers.keySet()){
|
||||
HomogenousInstancedArray buffer = glBufferMap.get(attribute);
|
||||
buffer.updateBuffer(buffers.get(attribute), 0);
|
||||
buffer.bind(renderPipelineState);
|
||||
}
|
||||
renderPipelineState.setInstanceCount(this.getDrawCount());
|
||||
Globals.renderingEngine.checkError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'destroy'");
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,366 +1,61 @@
|
||||
package electrosphere.renderer.actor.instance;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.joml.Matrix4d;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4d;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
import electrosphere.renderer.buffer.HomogenousInstancedArray;
|
||||
import electrosphere.renderer.buffer.ShaderAttribute;
|
||||
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
|
||||
import electrosphere.renderer.OpenGLState;
|
||||
import electrosphere.renderer.RenderPipelineState;
|
||||
|
||||
/**
|
||||
* Effectively controls the block of data that is passed to the gpu each time this instance is drawn.
|
||||
* Does priority management to draw the instanced of higher priority if there happens to be greater than the capacity number of items to draw.
|
||||
*/
|
||||
public class InstanceData {
|
||||
|
||||
//the capacity (number of instanced) of this data block. Defaults to 100
|
||||
int capacity = 1000;
|
||||
|
||||
//shader paths
|
||||
String vertexShaderPath;
|
||||
String fragmentShaderPath;
|
||||
|
||||
//the number of draw calls since the last clear operation
|
||||
int drawCalls = 0;
|
||||
|
||||
//The set of instanced actors to draw
|
||||
List<InstancedActor> actorQueue = null;
|
||||
//Map of actor to index in the buffers that are emitted
|
||||
Map<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>();
|
||||
//Map of index -> actor used for buffer evictions
|
||||
Map<Integer,InstancedActor> indexActorMap = new HashMap<Integer,InstancedActor>();
|
||||
|
||||
//list of all attribute indices in use by this instance data
|
||||
List<ShaderAttribute> attributeIndices = new LinkedList<ShaderAttribute>();
|
||||
//map of attribute -> buffer of attribute data
|
||||
Map<ShaderAttribute,Object> attributeCpuBufferMap = new HashMap<ShaderAttribute,Object>();
|
||||
//map of attribute -> gl HomogenousBuffer
|
||||
Map<ShaderAttribute,HomogenousInstancedArray> attributeGlBufferMap = new HashMap<ShaderAttribute,HomogenousInstancedArray>();
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param capacity Capacity of the buffer (number of elements) backing this data
|
||||
*/
|
||||
protected InstanceData(int capacity, String vertexPath, String fragmentPath){
|
||||
this.capacity = capacity;
|
||||
this.vertexShaderPath = vertexPath;
|
||||
this.fragmentShaderPath = fragmentPath;
|
||||
actorQueue = new LinkedList<InstancedActor>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new type of attribute to this instance data block
|
||||
* @param shaderAttribute The shader attribute
|
||||
* @param type The type of the attribute
|
||||
*/
|
||||
protected void addDataType(ShaderAttribute shaderAttribute, HomogenousBufferTypes type){
|
||||
attributeIndices.add(shaderAttribute);
|
||||
switch(type){
|
||||
case VEC3F: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 3));
|
||||
} break;
|
||||
case VEC3D: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity * 3));
|
||||
} break;
|
||||
case VEC4F: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4));
|
||||
} break;
|
||||
case VEC4D: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity * 4));
|
||||
} break;
|
||||
case DOUBLE: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity));
|
||||
} break;
|
||||
case FLOAT: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity));
|
||||
} break;
|
||||
case INT: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createIntBuffer(capacity));
|
||||
} break;
|
||||
case MAT4F: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4 * 4));
|
||||
} break;
|
||||
case MAT4D: {
|
||||
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4 * 4));
|
||||
} break;
|
||||
}
|
||||
if(shaderAttribute.isSingleIndex()){
|
||||
attributeGlBufferMap.put(shaderAttribute,HomogenousInstancedArray.createHomogenousInstancedArray(shaderAttribute.getIndex(), type, capacity));
|
||||
} else {
|
||||
attributeGlBufferMap.put(shaderAttribute,HomogenousInstancedArray.createHomogenousInstancedArray(shaderAttribute.getIndices(), type, capacity));
|
||||
}
|
||||
}
|
||||
|
||||
public interface InstanceData {
|
||||
|
||||
/**
|
||||
* Adds an actor to be sorted in the queue
|
||||
* @param actor The actor to be sorted
|
||||
*/
|
||||
protected void addInstance(InstancedActor actor){
|
||||
actorQueue.add(actor);
|
||||
drawCalls++;
|
||||
}
|
||||
public void addInstance(InstancedActor actor);
|
||||
|
||||
/**
|
||||
* Gets the number of entries that are to be drawn
|
||||
* @return The number of entries to be drawn
|
||||
*/
|
||||
public int getDrawCount(){
|
||||
return drawCalls;
|
||||
}
|
||||
public int getDrawCount();
|
||||
|
||||
/**
|
||||
* Clears the queue
|
||||
*/
|
||||
protected void clearDrawQueue(){
|
||||
actorQueue.clear();
|
||||
drawCalls = 0;
|
||||
}
|
||||
public void clearDrawQueue();
|
||||
|
||||
/**
|
||||
* Fills the buffers for the upcoming render call. The intention is to make this emberassingly parallel.
|
||||
*/
|
||||
protected void fillBuffers(){
|
||||
int i = 0;
|
||||
//for some reason the limit is not being set correctly. This explicitly forces it for each buffer
|
||||
for(ShaderAttribute attribute : attributeIndices){
|
||||
switch(attributeGlBufferMap.get(attribute).getType()){
|
||||
case VEC3F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case VEC3D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case VEC4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case VEC4D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case MAT4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
// System.out.println(buffer.position() + " " + buffer.limit());
|
||||
} break;
|
||||
case MAT4D: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case DOUBLE: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case FLOAT: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
case INT: {
|
||||
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.limit(buffer.capacity());
|
||||
} break;
|
||||
}
|
||||
}
|
||||
actorQueue.sort(Comparator.naturalOrder());
|
||||
//buffer data
|
||||
for(InstancedActor actor : actorQueue){
|
||||
//push values to attribute buffers
|
||||
for(ShaderAttribute attribute : attributeIndices){
|
||||
switch(attributeGlBufferMap.get(attribute).getType()){
|
||||
case VEC3F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Vector3f vec = (Vector3f)actor.getAttributeValue(attribute);
|
||||
buffer.put(vec.x);
|
||||
buffer.put(vec.y);
|
||||
buffer.put(vec.z);
|
||||
} break;
|
||||
case VEC3D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Vector3d vec = (Vector3d)actor.getAttributeValue(attribute);
|
||||
buffer.put(vec.x);
|
||||
buffer.put(vec.y);
|
||||
buffer.put(vec.z);
|
||||
} break;
|
||||
case VEC4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Vector4f vec = (Vector4f)actor.getAttributeValue(attribute);
|
||||
buffer.put(vec.w);
|
||||
buffer.put(vec.x);
|
||||
buffer.put(vec.y);
|
||||
buffer.put(vec.z);
|
||||
} break;
|
||||
case VEC4D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Vector4d vec = (Vector4d)actor.getAttributeValue(attribute);
|
||||
buffer.put(vec.w);
|
||||
buffer.put(vec.x);
|
||||
buffer.put(vec.y);
|
||||
buffer.put(vec.z);
|
||||
} break;
|
||||
case MAT4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Matrix4f mat = (Matrix4f)actor.getAttributeValue(attribute);
|
||||
buffer.put(mat.m00());
|
||||
buffer.put(mat.m01());
|
||||
buffer.put(mat.m02());
|
||||
buffer.put(mat.m03());
|
||||
|
||||
buffer.put(mat.m10());
|
||||
buffer.put(mat.m11());
|
||||
buffer.put(mat.m12());
|
||||
buffer.put(mat.m13());
|
||||
|
||||
buffer.put(mat.m20());
|
||||
buffer.put(mat.m21());
|
||||
buffer.put(mat.m22());
|
||||
buffer.put(mat.m23());
|
||||
|
||||
buffer.put(mat.m30());
|
||||
buffer.put(mat.m31());
|
||||
buffer.put(mat.m32());
|
||||
buffer.put(mat.m33());
|
||||
} break;
|
||||
case MAT4D: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
Matrix4d mat = (Matrix4d)actor.getAttributeValue(attribute);
|
||||
buffer.put((float)mat.m00());
|
||||
buffer.put((float)mat.m01());
|
||||
buffer.put((float)mat.m02());
|
||||
buffer.put((float)mat.m03());
|
||||
|
||||
buffer.put((float)mat.m10());
|
||||
buffer.put((float)mat.m11());
|
||||
buffer.put((float)mat.m12());
|
||||
buffer.put((float)mat.m13());
|
||||
|
||||
buffer.put((float)mat.m20());
|
||||
buffer.put((float)mat.m21());
|
||||
buffer.put((float)mat.m22());
|
||||
buffer.put((float)mat.m23());
|
||||
|
||||
buffer.put((float)mat.m30());
|
||||
buffer.put((float)mat.m31());
|
||||
buffer.put((float)mat.m32());
|
||||
buffer.put((float)mat.m33());
|
||||
} break;
|
||||
case DOUBLE: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.put((Double)actor.getAttributeValue(attribute));
|
||||
} break;
|
||||
case FLOAT: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.put((Float)actor.getAttributeValue(attribute));
|
||||
} break;
|
||||
case INT: {
|
||||
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
|
||||
buffer.put((Integer)actor.getAttributeValue(attribute));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
//increment
|
||||
i++;
|
||||
if(i >= capacity){
|
||||
break;
|
||||
}
|
||||
}
|
||||
//reset all buffers
|
||||
flip();
|
||||
}
|
||||
|
||||
protected void flip(){
|
||||
//reset all buffers
|
||||
for(ShaderAttribute attribute : attributeIndices){
|
||||
switch(attributeGlBufferMap.get(attribute).getType()){
|
||||
case VEC3F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case VEC3D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case VEC4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case VEC4D: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case MAT4F: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case MAT4D: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case DOUBLE: {
|
||||
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case FLOAT: {
|
||||
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
case INT: {
|
||||
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
|
||||
if(buffer.position() > 0){
|
||||
buffer.flip();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void fillBuffers();
|
||||
|
||||
/**
|
||||
* Gets a map of all attributes to the buffers of data for that attribute
|
||||
* @return The data buffers
|
||||
* Flips the buffer(s)
|
||||
*/
|
||||
public Map<ShaderAttribute,Object> getCpuBufferMap(){
|
||||
return attributeCpuBufferMap;
|
||||
}
|
||||
public void flip();
|
||||
|
||||
/**
|
||||
* Gets a map of attribute name to gl homogenous buffer object
|
||||
* @return The map
|
||||
* Gets the vertex shader associated with this data
|
||||
* @return The vertex shader
|
||||
*/
|
||||
public Map<ShaderAttribute,HomogenousInstancedArray> getGlBufferMap(){
|
||||
return attributeGlBufferMap;
|
||||
}
|
||||
public String getVertexShader();
|
||||
|
||||
/**
|
||||
* Gets the fragment shader associated with this data
|
||||
* @return The fragment shader
|
||||
*/
|
||||
public String getFragmentShader();
|
||||
|
||||
/**
|
||||
* Uploads the instance data
|
||||
*/
|
||||
public void upload(OpenGLState openGLState, RenderPipelineState renderPipelineState);
|
||||
|
||||
/**
|
||||
* Destroys the data
|
||||
*/
|
||||
public void destroy();
|
||||
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ public class InstanceManager {
|
||||
InstancedActor rVal = new InstancedActor(modelPath);
|
||||
if(!pathToInstanceData.containsKey(modelPath)){
|
||||
//create instance data
|
||||
InstanceData instanceData = new InstanceData(capacity,vertexShaderPath,fragmentShaderPath);
|
||||
HomogenousInstanceData instanceData = new HomogenousInstanceData(capacity,vertexShaderPath,fragmentShaderPath);
|
||||
//queue shader
|
||||
Globals.assetManager.addShaderToQueue(vertexShaderPath, fragmentShaderPath);
|
||||
//if asset manager doesn't have model, queue model
|
||||
@ -84,7 +84,7 @@ public class InstanceManager {
|
||||
data.fillBuffers();
|
||||
|
||||
//fetch model/shader and draw if both available
|
||||
ShaderProgram shader = Globals.assetManager.fetchShader(data.vertexShaderPath, null, data.fragmentShaderPath);
|
||||
ShaderProgram shader = Globals.assetManager.fetchShader(data.getVertexShader(), null, data.getFragmentShader());
|
||||
Model model = Globals.assetManager.fetchModel(modelPath);
|
||||
if(model != null && shader != null){
|
||||
openGLState.setActiveShader(renderPipelineState, shader);
|
||||
|
||||
@ -0,0 +1,237 @@
|
||||
package electrosphere.renderer.actor.instance;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.joml.Matrix4d;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4d;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
|
||||
import electrosphere.renderer.buffer.HomogenousUniformBuffer;
|
||||
import electrosphere.renderer.buffer.ShaderAttribute;
|
||||
import electrosphere.renderer.buffer.ShaderStorageBuffer;
|
||||
import electrosphere.renderer.OpenGLState;
|
||||
import electrosphere.renderer.RenderPipelineState;
|
||||
import electrosphere.renderer.buffer.BufferEnums.BufferAccess;
|
||||
import electrosphere.renderer.buffer.BufferEnums.BufferUsage;
|
||||
|
||||
/**
|
||||
* Instance data that uses a single buffer with strided input
|
||||
*/
|
||||
public class StridedInstanceData implements InstanceData {
|
||||
|
||||
//the capacity (number of instanced) of this data block. Defaults to 100
|
||||
int capacity = 1000;
|
||||
|
||||
//shader paths
|
||||
String vertexShaderPath;
|
||||
String fragmentShaderPath;
|
||||
|
||||
//the number of draw calls since the last clear operation
|
||||
int drawCalls = 0;
|
||||
|
||||
//The set of instanced actors to draw
|
||||
List<InstancedActor> actorQueue = null;
|
||||
//Map of actor to index in the buffers that are emitted
|
||||
Map<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>();
|
||||
//Map of index -> actor used for buffer evictions
|
||||
Map<Integer,InstancedActor> indexActorMap = new HashMap<Integer,InstancedActor>();
|
||||
|
||||
//list of all attribute indices in use by this instance data
|
||||
List<ShaderAttribute> attributeIndices = new LinkedList<ShaderAttribute>();
|
||||
|
||||
//the SSBO
|
||||
ShaderStorageBuffer buffer;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param capacity Capacity of the buffer (number of elements) backing this data
|
||||
*/
|
||||
public StridedInstanceData(int capacity, List<HomogenousBufferTypes> types, String vertexPath, String fragmentPath){
|
||||
int entrySize = 0;
|
||||
for(HomogenousBufferTypes type : types){
|
||||
entrySize = entrySize + HomogenousUniformBuffer.calculateTypeSize(type);
|
||||
}
|
||||
this.capacity = capacity;
|
||||
this.vertexShaderPath = vertexPath;
|
||||
this.fragmentShaderPath = fragmentPath;
|
||||
actorQueue = new LinkedList<InstancedActor>();
|
||||
this.buffer = new ShaderStorageBuffer(capacity * entrySize, BufferUsage.STREAM, BufferAccess.DRAW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new type of attribute to this instance data block
|
||||
* @param shaderAttribute The shader attribute
|
||||
* @param type The type of the attribute
|
||||
*/
|
||||
protected void addDataType(ShaderAttribute shaderAttribute, HomogenousBufferTypes type){
|
||||
attributeIndices.add(shaderAttribute);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds an actor to be sorted in the queue
|
||||
* @param actor The actor to be sorted
|
||||
*/
|
||||
public void addInstance(InstancedActor actor){
|
||||
actorQueue.add(actor);
|
||||
drawCalls++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of entries that are to be drawn
|
||||
* @return The number of entries to be drawn
|
||||
*/
|
||||
public int getDrawCount(){
|
||||
return drawCalls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the queue
|
||||
*/
|
||||
public void clearDrawQueue(){
|
||||
actorQueue.clear();
|
||||
drawCalls = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the buffers for the upcoming render call. The intention is to make this emberassingly parallel.
|
||||
*/
|
||||
public void fillBuffers(){
|
||||
int i = 0;
|
||||
ByteBuffer byteBuff = this.buffer.getBuffer();
|
||||
//buffer data
|
||||
for(InstancedActor actor : actorQueue){
|
||||
//push values to attribute buffers
|
||||
for(ShaderAttribute attribute : attributeIndices){
|
||||
switch(attribute.getType()){
|
||||
case VEC3F: {
|
||||
Vector3f vec = (Vector3f)actor.getAttributeValue(attribute);
|
||||
byteBuff.putFloat(vec.x);
|
||||
byteBuff.putFloat(vec.y);
|
||||
byteBuff.putFloat(vec.z);
|
||||
} break;
|
||||
case VEC3D: {
|
||||
Vector3d vec = (Vector3d)actor.getAttributeValue(attribute);
|
||||
byteBuff.putDouble(vec.x);
|
||||
byteBuff.putDouble(vec.y);
|
||||
byteBuff.putDouble(vec.z);
|
||||
} break;
|
||||
case VEC4F: {
|
||||
Vector4f vec = (Vector4f)actor.getAttributeValue(attribute);
|
||||
byteBuff.putFloat(vec.w);
|
||||
byteBuff.putFloat(vec.x);
|
||||
byteBuff.putFloat(vec.y);
|
||||
byteBuff.putFloat(vec.z);
|
||||
} break;
|
||||
case VEC4D: {
|
||||
Vector4d vec = (Vector4d)actor.getAttributeValue(attribute);
|
||||
byteBuff.putDouble(vec.w);
|
||||
byteBuff.putDouble(vec.x);
|
||||
byteBuff.putDouble(vec.y);
|
||||
byteBuff.putDouble(vec.z);
|
||||
} break;
|
||||
case MAT4F: {
|
||||
Matrix4f mat = (Matrix4f)actor.getAttributeValue(attribute);
|
||||
byteBuff.putFloat(mat.m00());
|
||||
byteBuff.putFloat(mat.m01());
|
||||
byteBuff.putFloat(mat.m02());
|
||||
byteBuff.putFloat(mat.m03());
|
||||
|
||||
byteBuff.putFloat(mat.m10());
|
||||
byteBuff.putFloat(mat.m11());
|
||||
byteBuff.putFloat(mat.m12());
|
||||
byteBuff.putFloat(mat.m13());
|
||||
|
||||
byteBuff.putFloat(mat.m20());
|
||||
byteBuff.putFloat(mat.m21());
|
||||
byteBuff.putFloat(mat.m22());
|
||||
byteBuff.putFloat(mat.m23());
|
||||
|
||||
byteBuff.putFloat(mat.m30());
|
||||
byteBuff.putFloat(mat.m31());
|
||||
byteBuff.putFloat(mat.m32());
|
||||
byteBuff.putFloat(mat.m33());
|
||||
} break;
|
||||
case MAT4D: {
|
||||
Matrix4d mat = (Matrix4d)actor.getAttributeValue(attribute);
|
||||
byteBuff.putDouble((float)mat.m00());
|
||||
byteBuff.putDouble((float)mat.m01());
|
||||
byteBuff.putDouble((float)mat.m02());
|
||||
byteBuff.putDouble((float)mat.m03());
|
||||
|
||||
byteBuff.putDouble((float)mat.m10());
|
||||
byteBuff.putDouble((float)mat.m11());
|
||||
byteBuff.putDouble((float)mat.m12());
|
||||
byteBuff.putDouble((float)mat.m13());
|
||||
|
||||
byteBuff.putDouble((float)mat.m20());
|
||||
byteBuff.putDouble((float)mat.m21());
|
||||
byteBuff.putDouble((float)mat.m22());
|
||||
byteBuff.putDouble((float)mat.m23());
|
||||
|
||||
byteBuff.putDouble((float)mat.m30());
|
||||
byteBuff.putDouble((float)mat.m31());
|
||||
byteBuff.putDouble((float)mat.m32());
|
||||
byteBuff.putDouble((float)mat.m33());
|
||||
} break;
|
||||
case DOUBLE: {
|
||||
byteBuff.putDouble((Double)actor.getAttributeValue(attribute));
|
||||
} break;
|
||||
case FLOAT: {
|
||||
byteBuff.putFloat((Float)actor.getAttributeValue(attribute));
|
||||
} break;
|
||||
case INT: {
|
||||
byteBuff.putInt((Integer)actor.getAttributeValue(attribute));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
//increment
|
||||
i++;
|
||||
if(i >= capacity){
|
||||
break;
|
||||
}
|
||||
}
|
||||
//reset all buffers
|
||||
flip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips the data buffer(s)
|
||||
*/
|
||||
public void flip(){
|
||||
this.buffer.getBuffer().flip();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVertexShader(){
|
||||
return vertexShaderPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFragmentShader(){
|
||||
return fragmentShaderPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(OpenGLState openGLState, RenderPipelineState renderPipelineState){
|
||||
this.buffer.upload();
|
||||
openGLState.glBindBufferBase(0, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if(this.buffer != null){
|
||||
this.buffer.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
97
src/main/java/electrosphere/renderer/buffer/BufferEnums.java
Normal file
97
src/main/java/electrosphere/renderer/buffer/BufferEnums.java
Normal file
@ -0,0 +1,97 @@
|
||||
package electrosphere.renderer.buffer;
|
||||
|
||||
import org.lwjgl.opengl.GL45;
|
||||
|
||||
/**
|
||||
* Enums for various buffer data
|
||||
*/
|
||||
public class BufferEnums {
|
||||
|
||||
/**
|
||||
* Buffer usage enum
|
||||
*/
|
||||
public static enum BufferUsage {
|
||||
|
||||
/**
|
||||
* The data store contents will be modified once and used many times.
|
||||
*/
|
||||
STATIC,
|
||||
|
||||
/**
|
||||
* The data store contents will be modified once and used at most a few times.
|
||||
*/
|
||||
STREAM,
|
||||
|
||||
/**
|
||||
* The data store contents will be modified repeatedly and used many times.
|
||||
*/
|
||||
DYNAMIC,
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer access enum
|
||||
*/
|
||||
public static enum BufferAccess {
|
||||
|
||||
/**
|
||||
* The data store contents are modified by the application, and used as the source for GL drawing and image specification commands.
|
||||
*/
|
||||
DRAW,
|
||||
|
||||
/**
|
||||
* The data store contents are modified by reading data from the GL, and used to return that data when queried by the application.
|
||||
*/
|
||||
READ,
|
||||
|
||||
/**
|
||||
* The data store contents are modified by reading data from the GL, and used as the source for GL drawing and image specification commands.
|
||||
*/
|
||||
COPY,
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the constant for the given usage and access combination
|
||||
* @param usage The usage value
|
||||
* @param access The access value
|
||||
* @return The constant
|
||||
*/
|
||||
public static int getBufferUsage(BufferUsage usage, BufferAccess access){
|
||||
if(usage == null || access == null){
|
||||
throw new IllegalArgumentException("Passed null value into getBufferUsage! " + usage + " " + access);
|
||||
}
|
||||
switch(usage){
|
||||
case STATIC: {
|
||||
switch(access){
|
||||
case DRAW:
|
||||
return GL45.GL_STATIC_DRAW;
|
||||
case READ:
|
||||
return GL45.GL_STATIC_READ;
|
||||
case COPY:
|
||||
return GL45.GL_STATIC_COPY;
|
||||
}
|
||||
} break;
|
||||
case STREAM: {
|
||||
switch(access){
|
||||
case DRAW:
|
||||
return GL45.GL_STREAM_DRAW;
|
||||
case READ:
|
||||
return GL45.GL_STREAM_READ;
|
||||
case COPY:
|
||||
return GL45.GL_STREAM_COPY;
|
||||
}
|
||||
} break;
|
||||
case DYNAMIC: {
|
||||
switch(access){
|
||||
case DRAW:
|
||||
return GL45.GL_DYNAMIC_DRAW;
|
||||
case READ:
|
||||
return GL45.GL_DYNAMIC_READ;
|
||||
case COPY:
|
||||
return GL45.GL_DYNAMIC_COPY;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
throw new IllegalStateException("Somehow hit unreachable code! " + access + " " + usage);
|
||||
}
|
||||
|
||||
}
|
||||
@ -119,6 +119,43 @@ public class HomogenousUniformBuffer {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of bytes required by this type
|
||||
* @return The number of bytes
|
||||
*/
|
||||
public static int calculateTypeSize(HomogenousBufferTypes type){
|
||||
switch(type){
|
||||
case VEC3F: {
|
||||
return 3 * 4;
|
||||
}
|
||||
case VEC3D: {
|
||||
return 3 * 8;
|
||||
}
|
||||
case VEC4F: {
|
||||
return 4 * 4;
|
||||
}
|
||||
case VEC4D: {
|
||||
return 4 * 8;
|
||||
}
|
||||
case DOUBLE: {
|
||||
return 1 * 8;
|
||||
}
|
||||
case FLOAT: {
|
||||
return 1 * 4;
|
||||
}
|
||||
case INT: {
|
||||
return 1 * 4;
|
||||
}
|
||||
case MAT4F: {
|
||||
return 4 * 4 * 4;
|
||||
}
|
||||
case MAT4D: {
|
||||
return 4 * 4 * 4;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this buffer is ready to stream data to or bind
|
||||
* @return True if ready, false otherwise
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
package electrosphere.renderer.buffer;
|
||||
|
||||
import org.lwjgl.opengl.GL45;
|
||||
|
||||
/**
|
||||
* An opengl buffer
|
||||
*/
|
||||
public interface OpenGLBuffer {
|
||||
|
||||
/**
|
||||
* The type of the buffer
|
||||
*/
|
||||
public static enum BufferType {
|
||||
/**
|
||||
* An array buffer
|
||||
*/
|
||||
GL_ARRAY_BUFFER,
|
||||
|
||||
/**
|
||||
* A storage buffer
|
||||
*/
|
||||
GL_SHADER_STORAGE_BUFFER,
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id of the buffer on opengl side
|
||||
* @return The id
|
||||
*/
|
||||
public int getId();
|
||||
|
||||
/**
|
||||
* Gets the type of the buffer
|
||||
* @return The type
|
||||
*/
|
||||
public BufferType getType();
|
||||
|
||||
/**
|
||||
* Gets the int representing the buffer type
|
||||
* @param buffer The buffer
|
||||
*/
|
||||
public static int getTypeInt(OpenGLBuffer buffer){
|
||||
if(buffer == null){
|
||||
throw new IllegalArgumentException("Passed null buffer into getTypeInt! " + buffer);
|
||||
}
|
||||
switch(buffer.getType()){
|
||||
case GL_ARRAY_BUFFER:
|
||||
return GL45.GL_ARRAY_BUFFER;
|
||||
case GL_SHADER_STORAGE_BUFFER:
|
||||
return GL45.GL_SHADER_STORAGE_BUFFER;
|
||||
}
|
||||
throw new IllegalStateException("Somehow reached unreachable code! " + buffer + " " + buffer.getType());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
package electrosphere.renderer.buffer;
|
||||
|
||||
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
|
||||
|
||||
/**
|
||||
* Represents an attribute of a shader.
|
||||
* When working with shader attributes, almost all data types are 1-1 where one vec4 can be stored in 1 attribute.
|
||||
@ -13,6 +15,11 @@ public class ShaderAttribute {
|
||||
//for multi-attribute index types (mat4f, mat4d, etc)
|
||||
int[] attributeIndices;
|
||||
|
||||
/**
|
||||
* The type of the attribute
|
||||
*/
|
||||
HomogenousBufferTypes type;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for 1-1 attribute
|
||||
@ -30,6 +37,15 @@ public class ShaderAttribute {
|
||||
this.attributeIndices = attributeIndices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for 1-1 attribute
|
||||
* @param attributeIndex The attribute index
|
||||
*/
|
||||
public ShaderAttribute(int attributeIndex, HomogenousBufferTypes type){
|
||||
this.attributeIndex = attributeIndex;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the attribute is a 1-1 relation
|
||||
* @return True if 1-1, false otherwise
|
||||
@ -54,4 +70,12 @@ public class ShaderAttribute {
|
||||
return attributeIndices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the attribute
|
||||
* @return The type
|
||||
*/
|
||||
public HomogenousBufferTypes getType(){
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,105 @@
|
||||
package electrosphere.renderer.buffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL45;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.renderer.buffer.BufferEnums.BufferAccess;
|
||||
import electrosphere.renderer.buffer.BufferEnums.BufferUsage;
|
||||
|
||||
/**
|
||||
* A shader storage buffer
|
||||
*/
|
||||
public class ShaderStorageBuffer implements UniformBlockBinding {
|
||||
|
||||
/**
|
||||
* The id for the buffer
|
||||
*/
|
||||
int id;
|
||||
|
||||
/**
|
||||
* The java buffer associated with the SSBO
|
||||
*/
|
||||
ByteBuffer buffer;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param capacity The capacity of the SSBO
|
||||
* @param usage The usage of the buffer
|
||||
* @param access The access of the buffer
|
||||
*/
|
||||
public ShaderStorageBuffer(int capacity, BufferUsage usage, BufferAccess access){
|
||||
//create the buffer java-side
|
||||
buffer = BufferUtils.createByteBuffer(capacity);
|
||||
|
||||
//create the buffer opengl-side
|
||||
id = GL45.glGenBuffers();
|
||||
Globals.renderingEngine.checkError();
|
||||
GL45.glBindBuffer(GL45.GL_SHADER_STORAGE_BUFFER, id);
|
||||
Globals.renderingEngine.checkError();
|
||||
GL45.glBufferData(GL45.GL_SHADER_STORAGE_BUFFER, MemoryUtil.NULL, BufferEnums.getBufferUsage(usage, access));
|
||||
Globals.renderingEngine.checkError();
|
||||
GL45.glBindBuffer(GL45.GL_SHADER_STORAGE_BUFFER,id);
|
||||
Globals.renderingEngine.checkError();
|
||||
GL45.glBindBuffer(GL45.GL_SHADER_STORAGE_BUFFER,UNBIND_ADDRESS);
|
||||
Globals.renderingEngine.checkError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the java side buffer to opengl
|
||||
*/
|
||||
public void upload(){
|
||||
long offset = 0;
|
||||
//bind
|
||||
GL45.glBindBuffer(GL45.GL_SHADER_STORAGE_BUFFER,id);
|
||||
Globals.renderingEngine.checkError();
|
||||
|
||||
//upload
|
||||
GL45.glBufferSubData(GL45.GL_SHADER_STORAGE_BUFFER, offset, buffer);
|
||||
Globals.renderingEngine.checkError();
|
||||
|
||||
//unbind
|
||||
GL45.glBindBuffer(GL45.GL_SHADER_STORAGE_BUFFER,UNBIND_ADDRESS);
|
||||
Globals.renderingEngine.checkError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the buffer
|
||||
*/
|
||||
public void destroy(){
|
||||
if(buffer != null){
|
||||
//destroy opengl-side buffer
|
||||
GL45.glDeleteBuffers(id);
|
||||
|
||||
//destroy java-side buffer
|
||||
MemoryUtil.memFree(buffer);
|
||||
|
||||
//set the java-side buffer to be null
|
||||
buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the java-side buffer
|
||||
* @return The java-side buffer
|
||||
*/
|
||||
public ByteBuffer getBuffer(){
|
||||
return this.buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferType getType() {
|
||||
return BufferType.GL_SHADER_STORAGE_BUFFER;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package electrosphere.renderer.buffer;
|
||||
|
||||
/**
|
||||
* A buffer that can be bound to a uniform block
|
||||
*/
|
||||
public interface UniformBlockBinding extends OpenGLBuffer {
|
||||
|
||||
/**
|
||||
* The address to use to unbind a buffer
|
||||
*/
|
||||
static final int UNBIND_ADDRESS = 0;
|
||||
|
||||
}
|
||||
@ -7,8 +7,6 @@ import electrosphere.renderer.RenderPipelineState;
|
||||
import electrosphere.renderer.RenderingEngine;
|
||||
import electrosphere.renderer.actor.ActorTextureMask;
|
||||
import electrosphere.renderer.actor.instance.InstanceData;
|
||||
import electrosphere.renderer.buffer.HomogenousInstancedArray;
|
||||
import electrosphere.renderer.buffer.ShaderAttribute;
|
||||
import electrosphere.renderer.light.LightManager;
|
||||
import electrosphere.renderer.shader.ShaderProgram;
|
||||
import electrosphere.renderer.texture.Texture;
|
||||
@ -18,7 +16,6 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.joml.Matrix4d;
|
||||
import org.joml.Matrix4f;
|
||||
@ -322,23 +319,6 @@ public class Mesh {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a buffer to the gpu
|
||||
* @param uniformTypeMap The type of the buffer
|
||||
* @param buffers The buffer
|
||||
*/
|
||||
void bufferInstanceData(
|
||||
RenderPipelineState renderPipelineState,
|
||||
Map<ShaderAttribute,Object> buffers,
|
||||
Map<ShaderAttribute,HomogenousInstancedArray> uniformGlBufferMap
|
||||
){
|
||||
for(ShaderAttribute attribute : buffers.keySet()){
|
||||
HomogenousInstancedArray buffer = uniformGlBufferMap.get(attribute);
|
||||
buffer.updateBuffer(buffers.get(attribute), 0);
|
||||
buffer.bind(renderPipelineState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -491,11 +471,7 @@ public class Mesh {
|
||||
if(renderPipelineState.getInstanced()){
|
||||
if(renderPipelineState.getInstanceData()!=null){
|
||||
InstanceData instanceData = renderPipelineState.getInstanceData();
|
||||
Map<ShaderAttribute,Object> buffers = instanceData.getCpuBufferMap();
|
||||
Map<ShaderAttribute,HomogenousInstancedArray> glBufferMap = instanceData.getGlBufferMap();
|
||||
bufferInstanceData(renderPipelineState, buffers, glBufferMap);
|
||||
renderPipelineState.setInstanceCount(instanceData.getDrawCount());
|
||||
Globals.renderingEngine.checkError();
|
||||
instanceData.upload(openGLState, renderPipelineState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ public class FontManager {
|
||||
font = new java.awt.Font(java.awt.Font.MONOSPACED, java.awt.Font.PLAIN, 16);
|
||||
}
|
||||
if(font!=null){
|
||||
defaultFont = FontUtils.loadFont(Globals.renderingEngine.getOpenGLState(), font, false);
|
||||
defaultFont = FontUtils.loadFont(Globals.renderingEngine.getOpenGLState(), font, true);
|
||||
fontMap.put("default",defaultFont);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
package electrosphere.renderer.buffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.renderer.OpenGLState;
|
||||
import electrosphere.renderer.buffer.BufferEnums.BufferAccess;
|
||||
import electrosphere.renderer.buffer.BufferEnums.BufferUsage;
|
||||
import electrosphere.test.annotations.UnitTest;
|
||||
import electrosphere.test.template.RenderingTestTemplate;
|
||||
|
||||
/**
|
||||
* Tests for the shader storage buffer
|
||||
*/
|
||||
public class ShaderStorageBufferTests extends RenderingTestTemplate {
|
||||
|
||||
@UnitTest
|
||||
public void test_Constructor_NoThrow(){
|
||||
assertDoesNotThrow(() -> {
|
||||
new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
|
||||
});
|
||||
}
|
||||
|
||||
@UnitTest
|
||||
public void test_Constructor_JavaSideCapacity_10(){
|
||||
ShaderStorageBuffer buffer = new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
|
||||
assertEquals(10, buffer.getBuffer().limit());
|
||||
}
|
||||
|
||||
@UnitTest
|
||||
public void test_destroy_NoThrow(){
|
||||
assertDoesNotThrow(() -> {
|
||||
ShaderStorageBuffer buffer = new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
|
||||
buffer.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
@UnitTest
|
||||
public void test_destroyTwice_NoThrow(){
|
||||
assertDoesNotThrow(() -> {
|
||||
ShaderStorageBuffer buffer = new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
|
||||
buffer.destroy();
|
||||
buffer.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
@UnitTest
|
||||
public void test_upload_NoThrow(){
|
||||
assertDoesNotThrow(() -> {
|
||||
ShaderStorageBuffer buffer = new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
|
||||
buffer.upload();
|
||||
});
|
||||
}
|
||||
|
||||
@UnitTest
|
||||
public void test_bind_NoThrow(){
|
||||
assertDoesNotThrow(() -> {
|
||||
ShaderStorageBuffer buffer = new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
|
||||
OpenGLState openGLState = Globals.renderingEngine.getOpenGLState();
|
||||
openGLState.glBindBufferBase(0, buffer);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,25 +2,19 @@ package electrosphere.renderer.framebuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.renderer.RenderingEngine;
|
||||
import electrosphere.renderer.texture.Texture;
|
||||
import electrosphere.test.template.extensions.StateCleanupCheckerExtension;
|
||||
import electrosphere.test.annotations.UnitTest;
|
||||
import electrosphere.test.template.RenderingTestTemplate;
|
||||
|
||||
/**
|
||||
* Tests for framebuffer creation utilities
|
||||
*/
|
||||
@ExtendWith(StateCleanupCheckerExtension.class)
|
||||
public class FramebufferUtilsTests {
|
||||
public class FramebufferUtilsTests extends RenderingTestTemplate {
|
||||
|
||||
@Test
|
||||
@UnitTest
|
||||
public void testCreateScreenFramebuffer(){
|
||||
Globals.initGlobals();
|
||||
Globals.renderingEngine = new RenderingEngine();
|
||||
Globals.renderingEngine.createOpenglContext();
|
||||
assertDoesNotThrow(() -> {
|
||||
Texture screenTextureColor = FramebufferUtils.generateScreenTextureColorAlpha(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
|
||||
RenderingEngine.screenTextureColor = screenTextureColor;
|
||||
@ -29,11 +23,9 @@ public class FramebufferUtilsTests {
|
||||
Framebuffer screenFramebuffer = FramebufferUtils.generateScreenTextureFramebuffer(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, screenTextureColor, screenTextureDepth);
|
||||
RenderingEngine.screenFramebuffer = screenFramebuffer;
|
||||
});
|
||||
Globals.renderingEngine.destroy();
|
||||
Globals.resetGlobals();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UnitTest
|
||||
public void testCreateScreenFramebufferRepeat(){
|
||||
assertDoesNotThrow(() -> {
|
||||
Globals.initGlobals();
|
||||
@ -51,8 +43,6 @@ public class FramebufferUtilsTests {
|
||||
screenTextureColor = FramebufferUtils.generateScreenTextureColorAlpha(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
|
||||
screenTextureDepth = FramebufferUtils.generateScreenTextureDepth(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
|
||||
FramebufferUtils.generateScreenTextureFramebuffer(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, screenTextureColor, screenTextureDepth);
|
||||
Globals.renderingEngine.destroy();
|
||||
Globals.resetGlobals();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -3,13 +3,13 @@ package electrosphere.renderer.ui.elements;
|
||||
import electrosphere.test.annotations.IntegrationTest;
|
||||
import electrosphere.menu.WindowUtils;
|
||||
import electrosphere.menu.mainmenu.MenuGeneratorsUITesting;
|
||||
import electrosphere.test.template.RenderingTestTemplate;
|
||||
import electrosphere.test.template.UITestTemplate;
|
||||
import electrosphere.test.testutils.TestEngineUtils;
|
||||
|
||||
/**
|
||||
* Tests for the window class
|
||||
*/
|
||||
public class WindowTest extends RenderingTestTemplate {
|
||||
public class WindowTest extends UITestTemplate {
|
||||
|
||||
/**
|
||||
* Tests creating a window
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
package electrosphere.test.template;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import electrosphere.test.template.extensions.StateCleanupCheckerExtension;
|
||||
import electrosphere.test.template.extensions.UIExtension;
|
||||
|
||||
import static electrosphere.test.testutils.Assertions.*;
|
||||
import electrosphere.test.testutils.TestRenderingUtils;
|
||||
|
||||
/**
|
||||
* A test class that involves testing renders
|
||||
*/
|
||||
@Tag("integration")
|
||||
@Tag("graphical")
|
||||
@ExtendWith(StateCleanupCheckerExtension.class)
|
||||
@ExtendWith(UIExtension.class)
|
||||
public class UITestTemplate {
|
||||
|
||||
/**
|
||||
* Checks the most recent render versus an existing image
|
||||
* @param renderName The name associated with the render
|
||||
* @param existingRenderPath The path to the existing image
|
||||
*/
|
||||
public void checkRender(String renderName, String existingRenderPath){
|
||||
//of the format "electrosphere.renderer.ui.elements.WindowTest"
|
||||
String canonicalName = this.getClass().getCanonicalName();
|
||||
|
||||
//check the render
|
||||
assertEqualsRender(existingRenderPath, () -> {
|
||||
|
||||
//on failure, save the failed render
|
||||
String failureSavePath = "./.testcache/" + canonicalName + "-" + renderName + ".png";
|
||||
File saveFile = new File(failureSavePath);
|
||||
System.err.println("[[ATTACHMENT|" + saveFile.getAbsolutePath() + "]]");
|
||||
TestRenderingUtils.saveTestRender(failureSavePath);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -5,8 +5,7 @@ import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.engine.Main;
|
||||
import electrosphere.test.testutils.EngineInit;
|
||||
import electrosphere.renderer.RenderingEngine;
|
||||
|
||||
/**
|
||||
* Spins up an tears down generic rendering environment
|
||||
@ -15,17 +14,15 @@ public class RenderingExtension implements BeforeEachCallback, AfterEachCallback
|
||||
|
||||
@Override
|
||||
public void beforeEach(ExtensionContext context) throws Exception {
|
||||
Globals.WINDOW_DECORATED = false;
|
||||
Globals.WINDOW_FULLSCREEN = true;
|
||||
Globals.RUN_AUDIO = false;
|
||||
Globals.WINDOW_WIDTH = 1920;
|
||||
Globals.WINDOW_HEIGHT = 1080;
|
||||
EngineInit.initGraphicalEngine();
|
||||
Globals.initGlobals();
|
||||
Globals.renderingEngine = new RenderingEngine();
|
||||
Globals.renderingEngine.createOpenglContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterEach(ExtensionContext context) throws Exception {
|
||||
Main.shutdown();
|
||||
Globals.renderingEngine.destroy();
|
||||
Globals.resetGlobals();
|
||||
}
|
||||
|
||||
}
|
||||
@ -26,6 +26,7 @@ public class StateCleanupCheckerExtension implements AfterEachCallback {
|
||||
Globals.clientSynchronizationManager,
|
||||
Globals.server,
|
||||
Globals.serverSynchronizationManager,
|
||||
Globals.playerManager,
|
||||
LoggerInterface.loggerEngine,
|
||||
RenderingEngine.screenFramebuffer,
|
||||
};
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
package electrosphere.test.template.extensions;
|
||||
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.engine.Main;
|
||||
import electrosphere.test.testutils.EngineInit;
|
||||
|
||||
/**
|
||||
* Spins up an tears down generic ui environment
|
||||
*/
|
||||
public class UIExtension implements BeforeEachCallback, AfterEachCallback {
|
||||
|
||||
@Override
|
||||
public void beforeEach(ExtensionContext context) throws Exception {
|
||||
Globals.WINDOW_DECORATED = false;
|
||||
Globals.WINDOW_FULLSCREEN = true;
|
||||
Globals.RUN_AUDIO = false;
|
||||
Globals.WINDOW_WIDTH = 1920;
|
||||
Globals.WINDOW_HEIGHT = 1080;
|
||||
EngineInit.initGraphicalEngine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterEach(ExtensionContext context) throws Exception {
|
||||
Main.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
Loading…
Reference in New Issue
Block a user