From abfafe13b935c3449e4ae34c140fb3219a1e53dc Mon Sep 17 00:00:00 2001 From: austin Date: Thu, 1 Aug 2024 21:26:55 -0400 Subject: [PATCH] bone debug rendering --- buildNumber.properties | 4 +- docs/src/progress/currenttarget.md | 3 + .../menu/debug/ImGuiEntityMacros.java | 5 + .../electrosphere/renderer/OpenGLState.java | 64 +++++++++++ .../renderer/RenderingEngine.java | 14 ++- .../electrosphere/renderer/actor/Actor.java | 7 +- .../renderer/actor/ActorUtils.java | 45 ++++++++ .../renderer/pipelines/CompositePipeline.java | 4 +- .../pipelines/FirstPersonItemsPipeline.java | 4 +- .../pipelines/MainContentNoOITPipeline.java | 4 +- .../pipelines/MainContentPipeline.java | 2 +- .../pipelines/NormalsForOutlinePipeline.java | 4 +- .../renderer/pipelines/UIPipeline.java | 1 + .../pipelines/debug/DebugBonesPipeline.java | 102 ++++++++++++++++++ .../{ => debug}/DebugContentPipeline.java | 16 ++- 15 files changed, 265 insertions(+), 14 deletions(-) create mode 100644 src/main/java/electrosphere/renderer/pipelines/debug/DebugBonesPipeline.java rename src/main/java/electrosphere/renderer/pipelines/{ => debug}/DebugContentPipeline.java (98%) diff --git a/buildNumber.properties b/buildNumber.properties index 58b84370..a3c10098 100644 --- a/buildNumber.properties +++ b/buildNumber.properties @@ -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 diff --git a/docs/src/progress/currenttarget.md b/docs/src/progress/currenttarget.md index bbd43079..71e4db5e 100644 --- a/docs/src/progress/currenttarget.md +++ b/docs/src/progress/currenttarget.md @@ -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 diff --git a/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java b/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java index 75a7736d..0bb64adf 100644 --- a/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java +++ b/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java @@ -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")){ diff --git a/src/main/java/electrosphere/renderer/OpenGLState.java b/src/main/java/electrosphere/renderer/OpenGLState.java index 25f96ad1..0ed07d35 100644 --- a/src/main/java/electrosphere/renderer/OpenGLState.java +++ b/src/main/java/electrosphere/renderer/OpenGLState.java @@ -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 blendFuncMap = new HashMap(); + //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); + } + // } + } + } diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index aced8193..247bba3f 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -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 diff --git a/src/main/java/electrosphere/renderer/actor/Actor.java b/src/main/java/electrosphere/renderer/actor/Actor.java index 10dc6552..f155c280 100644 --- a/src/main/java/electrosphere/renderer/actor/Actor.java +++ b/src/main/java/electrosphere/renderer/actor/Actor.java @@ -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); diff --git a/src/main/java/electrosphere/renderer/actor/ActorUtils.java b/src/main/java/electrosphere/renderer/actor/ActorUtils.java index 63cff187..3568ac79 100644 --- a/src/main/java/electrosphere/renderer/actor/ActorUtils.java +++ b/src/main/java/electrosphere/renderer/actor/ActorUtils.java @@ -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(); + } } diff --git a/src/main/java/electrosphere/renderer/pipelines/CompositePipeline.java b/src/main/java/electrosphere/renderer/pipelines/CompositePipeline.java index 71254b91..70d00445 100644 --- a/src/main/java/electrosphere/renderer/pipelines/CompositePipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/CompositePipeline.java @@ -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); diff --git a/src/main/java/electrosphere/renderer/pipelines/FirstPersonItemsPipeline.java b/src/main/java/electrosphere/renderer/pipelines/FirstPersonItemsPipeline.java index e884fb40..1d63c53c 100644 --- a/src/main/java/electrosphere/renderer/pipelines/FirstPersonItemsPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/FirstPersonItemsPipeline.java @@ -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); diff --git a/src/main/java/electrosphere/renderer/pipelines/MainContentNoOITPipeline.java b/src/main/java/electrosphere/renderer/pipelines/MainContentNoOITPipeline.java index 0e893c10..5ac07f82 100644 --- a/src/main/java/electrosphere/renderer/pipelines/MainContentNoOITPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/MainContentNoOITPipeline.java @@ -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); diff --git a/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java b/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java index 68a73136..59f49663 100644 --- a/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/MainContentPipeline.java @@ -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); diff --git a/src/main/java/electrosphere/renderer/pipelines/NormalsForOutlinePipeline.java b/src/main/java/electrosphere/renderer/pipelines/NormalsForOutlinePipeline.java index 5ab78630..df777f27 100644 --- a/src/main/java/electrosphere/renderer/pipelines/NormalsForOutlinePipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/NormalsForOutlinePipeline.java @@ -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); diff --git a/src/main/java/electrosphere/renderer/pipelines/UIPipeline.java b/src/main/java/electrosphere/renderer/pipelines/UIPipeline.java index fbf558ec..96f2d4ab 100644 --- a/src/main/java/electrosphere/renderer/pipelines/UIPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/UIPipeline.java @@ -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){ diff --git a/src/main/java/electrosphere/renderer/pipelines/debug/DebugBonesPipeline.java b/src/main/java/electrosphere/renderer/pipelines/debug/DebugBonesPipeline.java new file mode 100644 index 00000000..94ba8d0e --- /dev/null +++ b/src/main/java/electrosphere/renderer/pipelines/debug/DebugBonesPipeline.java @@ -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; + } + +} diff --git a/src/main/java/electrosphere/renderer/pipelines/DebugContentPipeline.java b/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java similarity index 98% rename from src/main/java/electrosphere/renderer/pipelines/DebugContentPipeline.java rename to src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java index 5ca9f041..38ec501b 100644 --- a/src/main/java/electrosphere/renderer/pipelines/DebugContentPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java @@ -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; + } }