bone debug rendering

This commit is contained in:
austin 2024-08-01 21:26:55 -04:00
parent 41a145a5bd
commit abfafe13b9
15 changed files with 265 additions and 14 deletions

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file
#Thu Aug 01 17:36:27 EDT 2024
buildNumber=198
#Thu Aug 01 18:31:38 EDT 2024
buildNumber=199

View File

@ -15,5 +15,8 @@ Things that feel bad:
First person blocking angle doesn't line up
Can't instantly start/stop blocking
Attack animation feels slow
Hands don't align with blade
Need to be able to visualize bones
No audio
Short movement bursts feel jittery
Part of this may be cylinder collidable instead of capsule

View File

@ -178,6 +178,11 @@ public class ImGuiEntityMacros {
ImGui.text(bone.getFinalTransform() + "");
}
}
//Draws all the bones in the world
if(ImGui.button("Draw Bones")){
Globals.renderingEngine.getDebugContentPipeline().getDebugBonesPipeline().setEntity(detailViewEntity);
}
//Browsable list of all animations with their data
if(ImGui.collapsingHeader("Animation Channel Data")){

View File

@ -27,6 +27,15 @@ public class OpenGLState {
//the current depth function
int depthFunction = -1;
//whether to blend or nit
boolean blendTest = false;
//the current blend func
//map is (texture unit) -> [sfactor,dfactor]
Map<Integer,int[]> blendFuncMap = new HashMap<Integer,int[]>();
//the key that contains the value of glBlendFunc (which would affect all buffers)
static final int ALL_BUFFERS_KEY = -1;
//the currently bound texture
int boundTexturePointer = 0;
int boundTextureType = 0;
@ -204,4 +213,59 @@ public class OpenGLState {
return MAX_TEXTURE_WIDTH;
}
/**
*
* @param sfactor
* @param dfactor
*/
public void glBlendFunc(int sfactor, int dfactor){
GL40.glBlendFunc(sfactor, dfactor);
//set all other keys
for(int keyCurrent : this.blendFuncMap.keySet()){
if(keyCurrent != ALL_BUFFERS_KEY){
int[] funcs = this.blendFuncMap.get(keyCurrent);
funcs[0] = sfactor;
funcs[1] = dfactor;
}
}
}
/**
* Sets the blend function for opengl
* @param drawBufferIndex The draw buffer index that will have this function set
* @param sfactor The source factor
* @param dfactor The destination factor
*/
public void glBlendFunci(int drawBufferIndex, int sfactor, int dfactor){
if(this.blendFuncMap.containsKey(drawBufferIndex)){
int[] funcs = this.blendFuncMap.get(drawBufferIndex);
int sFactorCurr = funcs[0];
int dFactorCurr = funcs[1];
if(sfactor != sFactorCurr || dfactor != dFactorCurr){
funcs[0] = sfactor;
funcs[1] = dfactor;
this.blendFuncMap.put(drawBufferIndex,funcs);
GL40.glBlendFunci(drawBufferIndex, sfactor, dfactor);
}
} else {
int[] funcs = new int[]{
sfactor, dfactor
};
this.blendFuncMap.put(drawBufferIndex,funcs);
GL40.glBlendFunci(drawBufferIndex, sfactor, dfactor);
}
}
public void glBlend(boolean blend){
// if(this.blendTest != blend){
this.blendTest = blend;
if(this.blendTest){
GL40.glEnable(GL40.GL_BLEND);
} else {
GL40.glDisable(GL40.GL_BLEND);
}
// }
}
}

View File

@ -89,7 +89,6 @@ import electrosphere.renderer.framebuffer.Renderbuffer;
import electrosphere.renderer.light.LightManager;
import electrosphere.renderer.model.Model;
import electrosphere.renderer.pipelines.CompositePipeline;
import electrosphere.renderer.pipelines.DebugContentPipeline;
import electrosphere.renderer.pipelines.FirstPersonItemsPipeline;
import electrosphere.renderer.pipelines.ImGuiPipeline;
import electrosphere.renderer.pipelines.MainContentNoOITPipeline;
@ -100,6 +99,7 @@ import electrosphere.renderer.pipelines.RenderScreenPipeline;
import electrosphere.renderer.pipelines.ShadowMapPipeline;
import electrosphere.renderer.pipelines.UIPipeline;
import electrosphere.renderer.pipelines.VolumeBufferPipeline;
import electrosphere.renderer.pipelines.debug.DebugContentPipeline;
import electrosphere.renderer.shader.ShaderProgram;
import electrosphere.renderer.texture.Texture;
@ -303,8 +303,8 @@ public class RenderingEngine {
glEnable(GL_DEPTH_TEST);
// Support for transparency
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
openGLState.glBlend(true);
openGLState.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//this disables vsync to make game run faster
//https://stackoverflow.com/questions/55598376/glfwswapbuffers-is-slow
@ -597,6 +597,14 @@ public class RenderingEngine {
return this.imGuiPipeline;
}
/**
* Gets the debug content pipeline
* @return The debug content pipeline
*/
public DebugContentPipeline getDebugContentPipeline(){
return this.debugContentPipeline;
}
/**
* Gets the current render pipeline state
* @return The current render pipeline state

View File

@ -402,7 +402,7 @@ public class Actor {
}
/**
* Gets the position of a bone
* Gets the position of a bone in local space
* @param boneName The name of the bone
* @return The vector3f containing the position of the bone, or a vector of (0,0,0) if the model lookup fails
* //TODO: refactor to make failure more transparent (both for model not existing and bone not existing)
@ -431,6 +431,11 @@ public class Actor {
return rVal;
}
/**
* Gets the rotation of a bone in local space
* @param boneName The name of the bone
* @return The Quaterniond containing the rotation of the bone, or an identity Quaterniond if the lookup fails
*/
public Quaterniond getBoneRotation(String boneName){
Quaterniond rVal = new Quaterniond();
Model model = Globals.assetManager.fetchModel(modelPath);

View File

@ -1,8 +1,13 @@
package electrosphere.renderer.actor;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.util.MathUtils;
/**
* Utils for dealing with actors
@ -39,6 +44,46 @@ public class ActorUtils {
Actor entityActor = EntityUtils.getActor(actorEntity);
return entityActor.getStaticMorph();
}
/**
* Gets the world position of a bone
* @param actorEntity The entity that has a bone
* @param boneName The name of the bone
*/
public static Vector3d getBoneWorldPosition(Entity actorEntity, String boneName){
Actor actor = EntityUtils.getActor(actorEntity);
Vector3d localPos = new Vector3d(actor.getBonePosition(boneName));
//transform bone space
Vector3d position = new Vector3d(localPos);
position = position.mul(EntityUtils.getScale(actorEntity));
position = position.rotate(new Quaterniond(EntityUtils.getRotation(actorEntity)));
//transform worldspace
position.add(new Vector3d(EntityUtils.getPosition(actorEntity)));
return position;
}
/**
* Gets the global rotation of the bone
* @param actorEntity The entity with the bone
* @param boneName The name of the bone
* @return The global rotation of the bone
*/
public static Quaterniond getBoneWorldRotation(Entity actorEntity, String boneName){
Actor actor = EntityUtils.getActor(actorEntity);
Quaterniond localRot = actor.getBoneRotation(boneName);
Vector3d facingAngle = CreatureUtils.getFacingVector(actorEntity);
if(facingAngle == null){
facingAngle = MathUtils.getOriginVector();
}
//calculate rotation of model
return new Quaterniond()
.rotationTo(MathUtils.getOriginVector(), new Vector3d(facingAngle.x,facingAngle.y,facingAngle.z))
.mul(localRot)
.normalize();
}
}

View File

@ -17,8 +17,8 @@ public class CompositePipeline implements RenderPipeline {
//
openGLState.glDepthFunc(GL40.GL_ALWAYS);
// glDepthMask(false);
GL40.glEnable(GL40.GL_BLEND);
GL40.glBlendFunc(GL40.GL_SRC_ALPHA, GL40.GL_ONE_MINUS_SRC_ALPHA);
openGLState.glBlend(true);
openGLState.glBlendFunc(GL40.GL_SRC_ALPHA, GL40.GL_ONE_MINUS_SRC_ALPHA);
RenderingEngine.screenFramebuffer.bind(openGLState);

View File

@ -30,7 +30,9 @@ public class FirstPersonItemsPipeline implements RenderPipeline {
if(Globals.firstPersonEntity != null && !Globals.controlHandler.cameraIsThirdPerson()){
//update logic
updateFirstPersonModelPosition(Globals.firstPersonEntity);
if(Globals.cameraHandler.getTrackPlayerEntity()){
updateFirstPersonModelPosition(Globals.firstPersonEntity);
}
//setup opengl state
renderPipelineState.setUseBones(true);

View File

@ -31,7 +31,9 @@ public class MainContentNoOITPipeline implements RenderPipeline {
GL40.glDepthMask(true);
openGLState.glViewport(Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY());
GL40.glEnable(GL40.GL_BLEND);
openGLState.glBlend(true);
openGLState.glBlendFunci(0, GL40.GL_ONE, GL40.GL_ONE);
openGLState.glBlendFunci(1, GL40.GL_ZERO, GL40.GL_ONE_MINUS_SRC_COLOR);
GL40.glBlendFunci(0, GL40.GL_ONE, GL40.GL_ONE);
GL40.glBlendFunci(1, GL40.GL_ZERO, GL40.GL_ONE_MINUS_SRC_COLOR);
GL40.glBlendEquation(GL40.GL_FUNC_ADD);

View File

@ -125,7 +125,7 @@ public class MainContentPipeline implements RenderPipeline {
//
// glDisable(GL_DEPTH_TEST);
GL40.glDepthMask(false);
GL40.glEnable(GL40.GL_BLEND);
openGLState.glBlend(true);
GL40.glBlendFunci(0, GL40.GL_ONE, GL40.GL_ONE);
GL40.glBlendFunci(1, GL40.GL_ZERO, GL40.GL_ONE_MINUS_SRC_COLOR);
GL40.glBlendEquation(GL40.GL_FUNC_ADD);

View File

@ -30,8 +30,8 @@ public class NormalsForOutlinePipeline implements RenderPipeline {
//bind screen fbo
RenderingEngine.gameImageNormalsFramebuffer.bind(openGLState);
openGLState.glDepthTest(true);
GL40.glDisable(GL40.GL_BLEND);
GL40.glBlendFunc(GL40.GL_SRC_ALPHA, GL40.GL_ONE_MINUS_SRC_ALPHA);
openGLState.glBlend(false);
openGLState.glBlendFunc(GL40.GL_SRC_ALPHA, GL40.GL_ONE_MINUS_SRC_ALPHA);
openGLState.glDepthFunc(GL40.GL_LESS);
GL40.glDepthMask(true);

View File

@ -69,6 +69,7 @@ public class UIPipeline implements RenderPipeline {
//set opengl state
openGLState.glDepthTest(false);
openGLState.glBlend(true);
for(Element currentElement : Globals.elementManager.getWindowList()){
if(currentElement instanceof DrawableElement){

View File

@ -0,0 +1,102 @@
package electrosphere.renderer.pipelines.debug;
import org.joml.Matrix4d;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.lwjgl.opengl.GL40;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.actor.ActorUtils;
import electrosphere.renderer.model.Bone;
import electrosphere.renderer.model.Model;
import electrosphere.renderer.pipelines.RenderPipeline;
/**
* Renders the bones for a given mesh
*/
public class DebugBonesPipeline implements RenderPipeline {
//The scale vector
static final Vector3d scale = new Vector3d(1);
/**
* The entity to render bones for
*/
Entity targetEntity;
@Override
public void render(OpenGLState openGLState, RenderPipelineState renderPipelineState) {
Globals.profiler.beginCpuSample("DebugBonesPipeline.render");
if(targetEntity != null){
//bind screen fbo
RenderingEngine.screenFramebuffer.bind(openGLState);
openGLState.glDepthTest(true);
openGLState.glDepthFunc(GL40.GL_LESS);
GL40.glDepthMask(true);
openGLState.glBlend(false);
openGLState.glViewport(Globals.userSettings.getRenderResolutionX(), Globals.userSettings.getRenderResolutionY());
///
/// R E N D E R I N G S T U F F
///
//Sets the background color.
//
// Set render pipeline state
//
renderPipelineState.setUseMeshShader(false);
renderPipelineState.setBufferStandardUniforms(true);
renderPipelineState.setBufferNonStandardUniforms(false);
renderPipelineState.setUseMaterial(true);
renderPipelineState.setUseShadowMap(true);
renderPipelineState.setUseBones(true);
renderPipelineState.setUseLight(true);
Matrix4d modelTransformMatrix = new Matrix4d();
//
//Get target data
//
Actor targetActor = EntityUtils.getActor(targetEntity);
Model boneModel = Globals.assetManager.fetchModel("Models/basic/geometry/unitcylinder.fbx");
boneModel.getMaterials().get(0).set_diffuse(Globals.textureDiffuseDefault);
for(Bone bone : targetActor.getBoneValues()){
Vector3d bonePos = ActorUtils.getBoneWorldPosition(targetEntity, bone.boneID);
Quaterniond boneRot = ActorUtils.getBoneWorldRotation(targetEntity, bone.boneID);
//put pos + rot into model
Vector3f cameraModifiedPosition = new Vector3f((float)bonePos.x,(float)bonePos.y,(float)bonePos.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
modelTransformMatrix.identity();
modelTransformMatrix.translate(cameraModifiedPosition);
modelTransformMatrix.rotate(boneRot);
modelTransformMatrix.scale(scale);
boneModel.setModelMatrix(modelTransformMatrix);
//draw
boneModel.draw(renderPipelineState,openGLState);
}
}
Globals.profiler.endCpuSample();
}
/**
* Sets the entity that should be drawn in this pipeline
* @param entity The entity to draw bones for
*/
public void setEntity(Entity entity){
this.targetEntity = entity;
}
}

View File

@ -1,4 +1,4 @@
package electrosphere.renderer.pipelines;
package electrosphere.renderer.pipelines.debug;
import org.joml.Matrix4d;
import org.joml.Quaterniond;
@ -25,6 +25,7 @@ import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.model.Model;
import electrosphere.renderer.pipelines.RenderPipeline;
import electrosphere.renderer.texture.Texture;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.utils.EntityLookupUtils;
@ -37,6 +38,9 @@ import electrosphere.server.pathfinding.navmesh.NavShape;
*/
public class DebugContentPipeline implements RenderPipeline {
//The bone debugging pipeline
DebugBonesPipeline debugBonesPipeline = new DebugBonesPipeline();
@Override
public void render(OpenGLState openGLState, RenderPipelineState renderPipelineState) {
Globals.profiler.beginCpuSample("DebugContentPipeline.render");
@ -326,6 +330,8 @@ public class DebugContentPipeline implements RenderPipeline {
}
}
debugBonesPipeline.render(openGLState, renderPipelineState);
Globals.profiler.endCpuSample();
}
@ -366,5 +372,13 @@ public class DebugContentPipeline implements RenderPipeline {
}
return "Textures/transparent_grey.png";
}
/**
* Gets the bone debugging pipeline
* @return The bone debugging pipeline
*/
public DebugBonesPipeline getDebugBonesPipeline(){
return this.debugBonesPipeline;
}
}