diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 230b2732..bc5ace9e 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1966,6 +1966,7 @@ Performance improvements - LOD skipping in realm simulation - Shadow map pipeline only considers entities that are nearby - Character services references set of already-loaded characters when simulating macro data + - Normal outline pipeline use draw accumulator diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index 25ec27b1..6ac979e2 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -730,6 +730,14 @@ public class RenderingEngine { return this.shadowMapPipeline; } + /** + * Gets the normals-for-outline pipeline + * @return The normals-for-outline pipeline + */ + public NormalsForOutlinePipeline getNormalsForOutlinePipeline(){ + return this.normalsForOutlinePipeline; + } + /** * Gets the main content pipeline * @return The main content pipeline diff --git a/src/main/java/electrosphere/renderer/pipelines/NormalsForOutlinePipeline.java b/src/main/java/electrosphere/renderer/pipelines/NormalsForOutlinePipeline.java index 385d0208..9b3199ef 100644 --- a/src/main/java/electrosphere/renderer/pipelines/NormalsForOutlinePipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/NormalsForOutlinePipeline.java @@ -1,5 +1,8 @@ package electrosphere.renderer.pipelines; +import java.util.LinkedList; +import java.util.List; + import org.joml.Matrix4d; import org.joml.Vector3d; import org.lwjgl.opengl.GL40; @@ -8,16 +11,36 @@ import electrosphere.client.entity.camera.CameraEntityUtils; import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; -import electrosphere.entity.EntityTags; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.attach.AttachUtils; import electrosphere.renderer.OpenGLState; import electrosphere.renderer.RenderPipelineState; import electrosphere.renderer.RenderingEngine; import electrosphere.renderer.actor.Actor; +import electrosphere.renderer.model.Model; +import electrosphere.renderer.target.DrawTargetAccumulator; +import electrosphere.renderer.target.DrawTargetAccumulator.ModelAccumulatorData; +/** + * Draws normals for generating character outlines + */ public class NormalsForOutlinePipeline implements RenderPipeline { + /** + * Cutoff for adding to shadow map pipeline draw accumulator + */ + public static final double DRAW_CUTOFF_DIST = 30f; + + /** + * The draw target accumulator + */ + private DrawTargetAccumulator drawTargetAccumulator = new DrawTargetAccumulator(); + + /** + * The queue for non-static entities to draw + */ + private List standardDrawCall = new LinkedList(); + @Override public void render(OpenGLState openGLState, RenderPipelineState renderPipelineState) { Globals.profiler.beginCpuSample("NormalsForOutlinePipeline.render"); @@ -63,23 +86,48 @@ public class NormalsForOutlinePipeline implements RenderPipeline { openGLState.setActiveShader(renderPipelineState, RenderingEngine.renderNormalsShader); - for(Entity currentEntity : Globals.clientState.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE)){ + modelTransformMatrix = new Matrix4d(); + Vector3d posVec = new Vector3d(); + Vector3d scaleVec = new Vector3d(); + for(Entity currentEntity : this.standardDrawCall){ Vector3d position = EntityUtils.getPosition(currentEntity); - if(shouldDraw(currentEntity)){ + if( + currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW)!=null + ){ //fetch actor Actor currentActor = EntityUtils.getActor(currentEntity); //calculate camera-modified vector3d - Vector3d cameraModifiedPosition = new Vector3d(position).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); + Vector3d cameraCenter = scaleVec.set(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); + Vector3d cameraModifiedPosition = posVec.set(position).sub(cameraCenter); //calculate and apply model transform - modelTransformMatrix.identity(); + modelTransformMatrix = modelTransformMatrix.identity(); modelTransformMatrix.translate(cameraModifiedPosition); modelTransformMatrix.rotate(EntityUtils.getRotation(currentEntity)); - modelTransformMatrix.scale(new Vector3d(EntityUtils.getScale(currentEntity))); - currentActor.applySpatialData(modelTransformMatrix,position); + modelTransformMatrix.scale(scaleVec.set(EntityUtils.getScale(currentEntity))); + currentActor.applySpatialData(new Matrix4d(modelTransformMatrix),new Vector3d(position)); //draw currentActor.draw(renderPipelineState,openGLState); } } + renderPipelineState.setUseBones(false); + for(ModelAccumulatorData accumulator : this.drawTargetAccumulator.getCalls()){ + Model model = Globals.assetManager.fetchModel(accumulator.getModelPath()); + if(model != null){ + int count = accumulator.getCount(); + List transforms = accumulator.getTransforms(); + List positions = accumulator.getPositions(); + model.setMeshMask(null); + for(int meshIndex = 0; meshIndex < model.getMeshCount(); meshIndex++){ + for(int i = 0; i < count; i++){ + Vector3d position = positions.get(i); + Matrix4d transform = transforms.get(i); + model.setWorldPos(position); + model.setModelMatrix(transform); + model.drawMesh(renderPipelineState, openGLState, meshIndex); + } + } + } + } Globals.renderingEngine.defaultFramebuffer.bind(openGLState); @@ -91,7 +139,7 @@ public class NormalsForOutlinePipeline implements RenderPipeline { * @param entity The entity * @return true if should draw, false otherwise */ - static boolean shouldDraw(Entity entity){ + public static boolean shouldDraw(Entity entity){ return ( (boolean)entity.getData(EntityDataStrings.DATA_STRING_DRAW) && @@ -134,5 +182,21 @@ public class NormalsForOutlinePipeline implements RenderPipeline { ) ; } + + /** + * Gets the draw target accumulator + * @return The draw target accumulator + */ + public DrawTargetAccumulator getDrawTargetAccumulator(){ + return drawTargetAccumulator; + } + + /** + * Gets the queue of standard entities to draw + * @return The queue of standard entites + */ + public List getStandardEntityQueue(){ + return standardDrawCall; + } } diff --git a/src/main/java/electrosphere/renderer/target/DrawTargetEvaluator.java b/src/main/java/electrosphere/renderer/target/DrawTargetEvaluator.java index 56453f35..65506208 100644 --- a/src/main/java/electrosphere/renderer/target/DrawTargetEvaluator.java +++ b/src/main/java/electrosphere/renderer/target/DrawTargetEvaluator.java @@ -14,6 +14,7 @@ import electrosphere.entity.EntityTags; import electrosphere.entity.EntityUtils; import electrosphere.renderer.actor.Actor; import electrosphere.renderer.pipelines.MainContentPipeline; +import electrosphere.renderer.pipelines.NormalsForOutlinePipeline; import electrosphere.renderer.pipelines.ShadowMapPipeline; /** @@ -43,6 +44,12 @@ public class DrawTargetEvaluator { List shadowQueue = Globals.renderingEngine.getShadowMapPipeline().getStandardEntityQueue(); shadowAccumulator.clearCalls(); shadowQueue.clear(); + + //normals pipeline structures + DrawTargetAccumulator normalAccumulator = Globals.renderingEngine.getNormalsForOutlinePipeline().getDrawTargetAccumulator(); + List normalQueue = Globals.renderingEngine.getNormalsForOutlinePipeline().getStandardEntityQueue(); + normalAccumulator.clearCalls(); + normalQueue.clear(); //reused objects Vector3d posVec = new Vector3d(); @@ -94,6 +101,9 @@ public class DrawTargetEvaluator { if(dist < ShadowMapPipeline.DRAW_CUTOFF_DIST && shadowList.contains(currentEntity)){ shadowAccumulator.addCall(currentActor.getModelPath(), position, modelTransformMatrix); } + if(dist < NormalsForOutlinePipeline.DRAW_CUTOFF_DIST && NormalsForOutlinePipeline.shouldDraw(currentEntity)){ + normalAccumulator.addCall(currentActor.getModelPath(), position, modelTransformMatrix); + } } } else { if(MainContentPipeline.shouldDrawSolidPass(currentEntity)){ @@ -102,6 +112,9 @@ public class DrawTargetEvaluator { if(dist < ShadowMapPipeline.DRAW_CUTOFF_DIST && shadowList.contains(currentEntity)){ shadowQueue.add(currentEntity); } + if(dist < NormalsForOutlinePipeline.DRAW_CUTOFF_DIST && NormalsForOutlinePipeline.shouldDraw(currentEntity)){ + normalQueue.add(currentEntity); + } } }