diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 81be5451..659aee4d 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -768,6 +768,10 @@ Fix gridded data cell manager saving attached items on realm save Fix render signals caching between frames (not reseting global flags per usual) Capture image from opengl to pixel-check Add ui tests +Fix image panel displaying texture flipped +Fix drag event relative positions +Fix slider behavior +New character customizer component # TODO diff --git a/src/main/java/electrosphere/menu/ingame/MenuGeneratorsLevelEditor.java b/src/main/java/electrosphere/menu/ingame/MenuGeneratorsLevelEditor.java index dd1b95af..160dd0ad 100644 --- a/src/main/java/electrosphere/menu/ingame/MenuGeneratorsLevelEditor.java +++ b/src/main/java/electrosphere/menu/ingame/MenuGeneratorsLevelEditor.java @@ -32,7 +32,6 @@ import electrosphere.renderer.ui.elements.Slider; import electrosphere.renderer.ui.elements.VirtualScrollable; import electrosphere.renderer.ui.elements.Window; import electrosphere.renderer.ui.elementtypes.NavigableElement.NavigationEventCallback; -import electrosphere.renderer.ui.elementtypes.ValueElement; import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment; import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection; import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification; @@ -393,39 +392,39 @@ public class MenuGeneratorsLevelEditor { xDiv.setMaxHeight(50); xDiv.setFlexDirection(YogaFlexDirection.Row); xDiv.addChild(Label.createLabel("X: ")); - xDiv.addChild(Slider.createSlider(new ValueElement.ValueChangeEventCallback() {public void execute(ValueChangeEvent event) { + xDiv.addChild(Slider.createSlider((ValueChangeEvent event) -> { LightManager lightManager = Globals.renderingEngine.getLightManager(); DirectionalLight directionalLight = lightManager.getDirectionalLight(); Vector3f direction = directionalLight.getDirection(); direction.x = event.getAsFloat() * 2 - 1; directionalLight.setDirection(direction); - }})); + })); scrollable.addChild(xDiv); Div yDiv = Div.createDiv(); yDiv.setMaxHeight(50); yDiv.setFlexDirection(YogaFlexDirection.Row); yDiv.addChild(Label.createLabel("Y: ")); - yDiv.addChild(Slider.createSlider(new ValueElement.ValueChangeEventCallback() {public void execute(ValueChangeEvent event) { + yDiv.addChild(Slider.createSlider((ValueChangeEvent event) -> { LightManager lightManager = Globals.renderingEngine.getLightManager(); DirectionalLight directionalLight = lightManager.getDirectionalLight(); Vector3f direction = directionalLight.getDirection(); direction.y = event.getAsFloat() * 2 - 1; directionalLight.setDirection(direction); - }})); + })); scrollable.addChild(yDiv); Div zDiv = Div.createDiv(); zDiv.setMaxHeight(50); zDiv.setFlexDirection(YogaFlexDirection.Row); zDiv.addChild(Label.createLabel("Z: ")); - zDiv.addChild(Slider.createSlider(new ValueElement.ValueChangeEventCallback() {public void execute(ValueChangeEvent event) { + zDiv.addChild(Slider.createSlider((ValueChangeEvent event) -> { LightManager lightManager = Globals.renderingEngine.getLightManager(); DirectionalLight directionalLight = lightManager.getDirectionalLight(); Vector3f direction = directionalLight.getDirection(); direction.z = event.getAsFloat() * 2 - 1; directionalLight.setDirection(direction); - }})); + })); scrollable.addChild(zDiv); diff --git a/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsUITesting.java b/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsUITesting.java index 853b358b..17d77fc9 100644 --- a/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsUITesting.java +++ b/src/main/java/electrosphere/menu/mainmenu/MenuGeneratorsUITesting.java @@ -6,6 +6,7 @@ import electrosphere.client.entity.camera.CameraEntityUtils; import electrosphere.engine.Globals; import electrosphere.menu.WindowUtils; import electrosphere.renderer.actor.ActorUtils; +import electrosphere.renderer.ui.components.CharacterCustomizer; import electrosphere.renderer.ui.elements.ActorPanel; import electrosphere.renderer.ui.elements.Button; import electrosphere.renderer.ui.elements.FormElement; @@ -13,7 +14,6 @@ import electrosphere.renderer.ui.elements.Label; import electrosphere.renderer.ui.elements.Slider; import electrosphere.renderer.ui.elements.VirtualScrollable; import electrosphere.renderer.ui.elementtypes.Element; -import electrosphere.renderer.ui.elementtypes.ValueElement; import electrosphere.renderer.ui.events.ValueChangeEvent; import electrosphere.renderer.ui.macros.InputMacros; @@ -59,9 +59,9 @@ public class MenuGeneratorsUITesting { } // slider test - Slider slider = Slider.createSlider(new ValueElement.ValueChangeEventCallback() {public void execute(ValueChangeEvent event) { - - }}); + Slider slider = Slider.createSlider((ValueChangeEvent event) -> { + + }); virtualScrollable.addChild(slider); rVal.addChild(virtualScrollable); diff --git a/src/main/java/electrosphere/renderer/framebuffer/FramebufferUtils.java b/src/main/java/electrosphere/renderer/framebuffer/FramebufferUtils.java index 30ebc28f..0dab34cc 100644 --- a/src/main/java/electrosphere/renderer/framebuffer/FramebufferUtils.java +++ b/src/main/java/electrosphere/renderer/framebuffer/FramebufferUtils.java @@ -157,6 +157,14 @@ public class FramebufferUtils { return buffer; } + /** + * Creates a texture framebuffer + * @param openGLState The opengl engine state + * @param width The width of the texture + * @param height The height of the texture + * @return The texture + * @throws Exception Thrown if the framebuffer fails to initialize + */ public static Framebuffer generateTextureFramebuffer(OpenGLState openGLState, int width, int height) throws Exception { Framebuffer buffer = new Framebuffer(); buffer.bind(openGLState); diff --git a/src/main/java/electrosphere/renderer/ui/ElementService.java b/src/main/java/electrosphere/renderer/ui/ElementService.java index a2c9d369..0dd5564e 100644 --- a/src/main/java/electrosphere/renderer/ui/ElementService.java +++ b/src/main/java/electrosphere/renderer/ui/ElementService.java @@ -233,6 +233,11 @@ public class ElementService extends SignalServiceImpl { Vector2i absPos = getAbsolutePosition(currentElement); clickEvent.setRelativeX(clickEvent.getCurrentX() - absPos.x); clickEvent.setRelativeY(clickEvent.getCurrentY() - absPos.y); + } else if(event instanceof DragEvent){ + DragEvent dragEvent = (DragEvent)event; + Vector2i absPos = getAbsolutePosition(currentElement); + dragEvent.setRelativeX(dragEvent.getCurrentX() - absPos.x); + dragEvent.setRelativeY(dragEvent.getCurrentY() - absPos.y); } propagate = currentElement.handleEvent(event); } @@ -429,11 +434,10 @@ public class ElementService extends SignalServiceImpl { */ public void drag(int x, int y, int lastX, int lastY, int deltaX, int deltaY){ if(currentDragElement != null){ - //need to calculate offset based on parent positions - //the positions for x, y, lastX, and lastY are all in absolute-to-screen coordinates - //the logic for things like slider depend on the - Vector2i absPos = getAbsolutePosition(currentDragElement); - DragEvent event = new DragEvent(x - absPos.x, y - absPos.y, lastX - absPos.x, lastY - absPos.y, deltaX, deltaY, DragEventType.DRAG, currentDragElement); + DragEvent event = new DragEvent(x, y, lastX, lastY, deltaX, deltaY, DragEventType.DRAG, currentDragElement); + Vector2i absPos = this.getAbsolutePosition(currentDragElement); + event.setRelativeX(x - absPos.x); + event.setRelativeY(y - absPos.y); currentDragElement.handleEvent(event); } // fireEvent(event,event.getCurrentX(),event.getCurrentY()); @@ -450,11 +454,10 @@ public class ElementService extends SignalServiceImpl { */ public void dragRelease(int x, int y, int lastX, int lastY, int deltaX, int deltaY){ if(currentDragElement != null){ - //need to calculate offset based on parent positions - //the positions for x, y, lastX, and lastY are all in absolute-to-screen coordinates - //the logic for things like slider depend on the - Vector2i absPos = getAbsolutePosition(currentDragElement); - DragEvent event = new DragEvent(x - absPos.x, y - absPos.y, lastX - absPos.x, lastY - absPos.y, deltaX, deltaY, DragEventType.RELEASE, currentDragElement); + DragEvent event = new DragEvent(x, y, lastX, lastY, deltaX, deltaY, DragEventType.RELEASE, currentDragElement); + Vector2i absPos = this.getAbsolutePosition(currentDragElement); + event.setRelativeX(x - absPos.x); + event.setRelativeY(y - absPos.y); currentDragElement.handleEvent(event); currentDragElement = null; } diff --git a/src/main/java/electrosphere/renderer/ui/components/CharacterCustomizer.java b/src/main/java/electrosphere/renderer/ui/components/CharacterCustomizer.java new file mode 100644 index 00000000..6a2c1e0e --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/components/CharacterCustomizer.java @@ -0,0 +1,160 @@ +package electrosphere.renderer.ui.components; + +import java.util.stream.Collectors; + +import org.joml.Vector3f; + +import electrosphere.client.entity.camera.CameraEntityUtils; +import electrosphere.engine.Globals; +import electrosphere.entity.types.creature.CreatureTemplate; +import electrosphere.game.data.creature.type.CreatureData; +import electrosphere.game.data.creature.type.visualattribute.AttributeVariant; +import electrosphere.game.data.creature.type.visualattribute.VisualAttribute; +import electrosphere.net.parser.net.message.CharacterMessage; +import electrosphere.renderer.actor.Actor; +import electrosphere.renderer.actor.ActorStaticMorph; +import electrosphere.renderer.actor.ActorUtils; +import electrosphere.renderer.ui.elements.ActorPanel; +import electrosphere.renderer.ui.elements.Button; +import electrosphere.renderer.ui.elements.Div; +import electrosphere.renderer.ui.elements.Label; +import electrosphere.renderer.ui.elements.ScrollableContainer; +import electrosphere.renderer.ui.elements.Slider; +import electrosphere.renderer.ui.elements.StringCarousel; +import electrosphere.renderer.ui.elementtypes.Element; +import electrosphere.renderer.ui.events.ValueChangeEvent; +import electrosphere.util.Utilities; + +/** + * Panel to customize a character + */ +public class CharacterCustomizer { + + /** + * Minimum width of the component + */ + public static final int MIN_WIDTH = 500; + + /** + * Minimum height of the component + */ + public static final int MIN_HEIGHT = 500; + + /** + * Creates a character customizer panel + * @param race The race of the character + * @return The panel component + */ + public static Element createCharacterCustomizerPanel(String race){ + //figure out race data + CreatureData selectedRaceType = Globals.gameConfigCurrent.getCreatureTypeLoader().getType(race); + + //spawn camera so renderer doesn't crash (once render pipeline is modularized this shouldn't be necessary) + Globals.playerCamera = CameraEntityUtils.spawnBasicCameraEntity(new Vector3f(0,0,0), new Vector3f(0,0.3f,1).normalize()); + Globals.viewMatrix = CameraEntityUtils.getCameraViewMatrix(Globals.playerCamera); + + //create actor panel + Actor characterActor = ActorUtils.createActorFromModelPath(selectedRaceType.getModelPath()); + ActorPanel actorPanel = ActorPanel.create(characterActor); + actorPanel.setAnimation(selectedRaceType.getIdleData().getAnimation().getNameThirdPerson()); + actorPanel.setPosition(new Vector3f(0,-0.5f,-0.6f)); + actorPanel.setScale(new Vector3f(1.0f)); + + //have to build static morph while looping through attributes + ActorStaticMorph staticMorph = new ActorStaticMorph(); + + //create creature template + CreatureTemplate template = CreatureTemplate.create(race); + + //create scrollable + ScrollableContainer scrollable = ScrollableContainer.createScrollable(); + scrollable.setMinWidth(MIN_WIDTH); + scrollable.setMinHeight(MIN_HEIGHT); + + //create edit controls here + for(VisualAttribute attribute : selectedRaceType.getVisualAttributes()){ + if(attribute.getType().equals(VisualAttribute.TYPE_BONE)){ + //add label for slider + Label sliderName = new Label(0.6f); + sliderName.setText(attribute.getAttributeId()); + sliderName.setMinWidth(200); + //add a slider + Slider boneSlider = Slider.createSlider((ValueChangeEvent event) -> { + if(characterActor.getStaticMorph() != null){ + staticMorph.updateValue(attribute.getSubtype(), attribute.getPrimaryBone(), event.getAsFloat()); + if(attribute.getMirrorBone() != null){ + staticMorph.updateValue(attribute.getSubtype(), attribute.getMirrorBone(), event.getAsFloat()); + } + template.getAttributeValue(attribute.getAttributeId()).setValue(event.getAsFloat()); + } + }); + float min = attribute.getMinValue(); + float max = attribute.getMaxValue(); + float defaultValue = min + (max - min)/2.0f; + boneSlider.setMinimum(min); + boneSlider.setMaximum(max); + boneSlider.setValue(defaultValue); + boneSlider.setMinWidth(200); + //actually add attributes to static morph + if(attribute.getPrimaryBone() != null && staticMorph.getBoneTransforms(attribute.getPrimaryBone()) == null){ + staticMorph.initBoneTransforms(attribute.getPrimaryBone()); + } + if(attribute.getMirrorBone() != null && staticMorph.getBoneTransforms(attribute.getMirrorBone()) == null){ + staticMorph.initBoneTransforms(attribute.getMirrorBone()); + } + //add attribute to creature template + template.putAttributeValue(attribute.getAttributeId(), defaultValue); + + scrollable.addChild(Div.createRow(sliderName,boneSlider)); + } else if(attribute.getType().equals(VisualAttribute.TYPE_REMESH)){ + //add label for carousel + Label scrollableName = new Label(0.6f); + scrollableName.setText(attribute.getAttributeId()); + scrollableName.setMinWidth(200); + //add a carousel + StringCarousel variantCarousel = StringCarousel.create( + // attribute.getVariants().stream().filter(variant -> ), + attribute.getVariants().stream().map(variant -> variant.getId()).collect(Collectors.toList()), + (ValueChangeEvent event) -> { + //TODO: implement updating visuals + template.getAttributeValue(attribute.getAttributeId()).setVariantId(event.getAsString()); + AttributeVariant variant = null; + for(AttributeVariant variantCurrent : attribute.getVariants()){ + if(variantCurrent.getId().equals(event.getAsString())){ + variant = variantCurrent; + break; + } + } + if(variant != null){ + Globals.assetManager.addModelPathToQueue(variant.getModel()); + for(String mesh : variant.getMeshes()){ + characterActor.getMeshMask().queueMesh(variant.getModel(), mesh); + } + } + } + ); + variantCarousel.setMinWidth(200); + //set the current attrib for the template + template.putAttributeValue(attribute.getAttributeId(), attribute.getVariants().get(0).getId()); + + scrollable.addChild(Div.createRow(scrollableName,variantCarousel)); + } + } + //finally set static morph + characterActor.setActorStaticMorph(staticMorph); + + //create layout + Div rVal = Div.createCol( + Div.createRow( + scrollable, + actorPanel + ), + Button.createButton("Create", () -> { + Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestCreateCharacterMessage(Utilities.stringify(template))); + }) + ); + + return rVal; + } + +} diff --git a/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java b/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java index ec8630ff..6bd52a68 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java +++ b/src/main/java/electrosphere/renderer/ui/elements/ActorPanel.java @@ -20,23 +20,26 @@ import electrosphere.renderer.RenderPipelineState; import electrosphere.renderer.RenderingEngine; import electrosphere.renderer.actor.Actor; import electrosphere.renderer.debug.DebugRendering; -import electrosphere.renderer.framebuffer.Framebuffer; -import electrosphere.renderer.framebuffer.FramebufferUtils; -import electrosphere.renderer.model.Material; import electrosphere.renderer.model.Model; import electrosphere.renderer.ui.elementtypes.DraggableElement; -import electrosphere.renderer.ui.elementtypes.DrawableElement; import electrosphere.renderer.ui.events.DragEvent; import electrosphere.renderer.ui.events.DragEvent.DragEventType; import electrosphere.renderer.ui.events.Event; -public class ActorPanel extends StandardElement implements DrawableElement, DraggableElement { +public class ActorPanel extends BufferedStandardDrawableContainerElement implements DraggableElement { + + /** + * The default width of an actor panel + */ + public static final int DEFAULT_WIDTH = 500; + + /** + * The default height of an actor panel + */ + public static final int DEFAULT_HEIGHT = 500; static Vector3f color = new Vector3f(1.0f); - Material customMat = new Material(); - Framebuffer elementBuffer; - Actor actor; Matrix4d modelMatrix = new Matrix4d(); String currentAnim; @@ -56,14 +59,16 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag static final Vector3f windowDrawDebugColor = new Vector3f(0.0f,0.0f,1.0f); + @Deprecated public ActorPanel(OpenGLState openGLState, int x, int y, int width, int height, Actor actor){ super(); - try { - elementBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height); - } catch(Exception e){ - LoggerInterface.loggerRenderer.ERROR(e); - } - customMat.setTexturePointer(elementBuffer.getTexture().getTexturePointer()); + + //must manually set because framebuffer is allocated on set + //these default to -1 until they are set with methods, but methods allocate on each set + //to break chicken-and-egg problem, manually set here + this.width = DEFAULT_WIDTH; + this.height = DEFAULT_HEIGHT; + this.actor = actor; this.internalPositionX = x; this.internalPositionY = y; @@ -72,6 +77,36 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag this.aspectRatio = (float)width / (float)height; recalculateModelMatrix(); } + + /** + * Constructor + * @param actor The actor + */ + private ActorPanel(Actor actor){ + super(); + + //must manually set because framebuffer is allocated on set + //these default to -1 until they are set with methods, but methods allocate on each set + //to break chicken-and-egg problem, manually set here + this.width = DEFAULT_WIDTH; + this.height = DEFAULT_HEIGHT; + + this.actor = actor; + this.setWidth(DEFAULT_WIDTH); + this.setHeight(DEFAULT_HEIGHT); + this.aspectRatio = (float)DEFAULT_WIDTH / (float)DEFAULT_HEIGHT; + this.recalculateModelMatrix(); + } + + /** + * Creates an actor panel + * @param actor The actor to put in the panel + * @return The actor panel + */ + public static ActorPanel create(Actor actor){ + ActorPanel rVal = new ActorPanel(actor); + return rVal; + } @@ -86,122 +121,113 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag int parentHeight ) { - elementBuffer.bind(openGLState); -// Globals.renderingEngine.setViewportSize(width, height); + if(this.elementBuffer != null){ + elementBuffer.bind(openGLState); + // Globals.renderingEngine.setViewportSize(width, height); - RenderingEngine.setFOV(FOV); - RenderingEngine.setAspectRatio(aspectRatio); - - openGLState.glDepthTest(true); - openGLState.glDepthFunc(GL_LESS); - glDepthMask(true); - openGLState.glViewport(width, height); + RenderingEngine.setFOV(FOV); + RenderingEngine.setAspectRatio(aspectRatio); + + openGLState.glDepthTest(true); + openGLState.glDepthFunc(GL_LESS); + glDepthMask(true); + openGLState.glViewport(width, height); - glClearColor(1.0f, 1.0f, 1.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - Model actorModel = Globals.assetManager.fetchModel(actor.getModelPath()); + Model actorModel = Globals.assetManager.fetchModel(actor.getModelPath()); - if(currentAnim != null){ - if((!actor.isPlayingAnimation() || !actor.isPlayingAnimation(currentAnim)) && - actorModel != null && - actorModel.getAnimation(currentAnim) != null - ){ - actor.playAnimation(currentAnim,3); - actor.incrementAnimationTime(0.0001); + if(currentAnim != null){ + if((!actor.isPlayingAnimation() || !actor.isPlayingAnimation(currentAnim)) && + actorModel != null && + actorModel.getAnimation(currentAnim) != null + ){ + actor.playAnimation(currentAnim,3); + actor.incrementAnimationTime(0.0001); + } + } + + // + // Set rendering engine state + // + renderPipelineState.setUseMeshShader(true); + renderPipelineState.setBufferStandardUniforms(true); + renderPipelineState.setBufferNonStandardUniforms(true); + renderPipelineState.setUseMaterial(true); + renderPipelineState.setUseShadowMap(true); + renderPipelineState.setUseBones(true); + renderPipelineState.setUseLight(true); + + + + + + + + actor.applySpatialData(modelMatrix,new Vector3d(actorPosition)); + actor.draw(renderPipelineState,openGLState); + + RenderingEngine.setFOV(Globals.verticalFOV); + RenderingEngine.setAspectRatio(Globals.aspectRatio); + + openGLState.glDepthTest(false); + + //this call binds the screen as the "texture" we're rendering to + //have to call before actually rendering + openGLState.glBindFramebuffer(GL_FRAMEBUFFER, parentFramebufferPointer); + + //set viewport + openGLState.glViewport(parentWidth, parentHeight); + + float ndcX = (float)getInternalX()/parentWidth; + float ndcY = (float)getInternalY()/parentHeight; + float ndcWidth = (float)getInternalWidth()/parentWidth; + float ndcHeight = (float)getInternalHeight()/parentHeight; + + Vector3f boxPosition = new Vector3f(ndcX,ndcY,0); + Vector3f boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); + + + + + + + // + // Set rendering engine state + // + renderPipelineState.setUseMeshShader(true); + renderPipelineState.setBufferStandardUniforms(false); + renderPipelineState.setBufferNonStandardUniforms(true); + renderPipelineState.setUseMaterial(true); + renderPipelineState.setUseShadowMap(false); + renderPipelineState.setUseBones(false); + renderPipelineState.setUseLight(false); + + + + + + Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID); + if(planeModel != null){ + planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); + planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); + planeModel.pushUniformToMesh("plane", "tPosition", texPosition); + planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); + planeModel.getMeshes().get(0).setMaterial(elementMat); + planeModel.draw(renderPipelineState,Globals.renderingEngine.getOpenGLState()); + } else { + LoggerInterface.loggerRenderer.ERROR("Actor Panel unable to find plane model!!", new Exception()); + } + + if(Globals.RENDER_FLAG_RENDER_UI_BOUNDS && DebugRendering.RENDER_DEBUG_OUTLINE_ACTOR_PANEL){ + DebugRendering.drawUIBounds(parentFramebufferPointer, boxPosition, boxDimensions, windowDrawDebugColor); } } - - // - // Set rendering engine state - // - renderPipelineState.setUseMeshShader(true); - renderPipelineState.setBufferStandardUniforms(true); - renderPipelineState.setBufferNonStandardUniforms(true); - renderPipelineState.setUseMaterial(true); - renderPipelineState.setUseShadowMap(true); - renderPipelineState.setUseBones(true); - renderPipelineState.setUseLight(true); - - - - - - - - actor.applySpatialData(modelMatrix,new Vector3d(actorPosition)); - actor.draw(renderPipelineState,openGLState); - - RenderingEngine.setFOV(Globals.verticalFOV); - RenderingEngine.setAspectRatio(Globals.aspectRatio); - - openGLState.glDepthTest(false); - - //this call binds the screen as the "texture" we're rendering to - //have to call before actually rendering - openGLState.glBindFramebuffer(GL_FRAMEBUFFER, parentFramebufferPointer); - - //set viewport - openGLState.glViewport(parentWidth, parentHeight); - - float ndcX = (float)getInternalX()/parentWidth; - float ndcY = (float)getInternalY()/parentHeight; - float ndcWidth = (float)getInternalWidth()/parentWidth; - float ndcHeight = (float)getInternalHeight()/parentHeight; - - Vector3f boxPosition = new Vector3f(ndcX,ndcY,0); - Vector3f boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); - - - - - - - // - // Set rendering engine state - // - renderPipelineState.setUseMeshShader(true); - renderPipelineState.setBufferStandardUniforms(false); - renderPipelineState.setBufferNonStandardUniforms(true); - renderPipelineState.setUseMaterial(true); - renderPipelineState.setUseShadowMap(false); - renderPipelineState.setUseBones(false); - renderPipelineState.setUseLight(false); - - - - - - Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID); - if(planeModel != null){ - planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); - planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); - planeModel.pushUniformToMesh("plane", "tPosition", texPosition); - planeModel.pushUniformToMesh("plane", "tDimension", texScale); - planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); - planeModel.getMeshes().get(0).setMaterial(customMat); - planeModel.draw(renderPipelineState,Globals.renderingEngine.getOpenGLState()); - } else { - LoggerInterface.loggerRenderer.ERROR("Actor Panel unable to find plane model!!", new Exception()); - } - - if(Globals.RENDER_FLAG_RENDER_UI_BOUNDS && DebugRendering.RENDER_DEBUG_OUTLINE_ACTOR_PANEL){ - DebugRendering.drawUIBounds(parentFramebufferPointer, boxPosition, boxDimensions, windowDrawDebugColor); - } } - public boolean visible = false; - - - public void setVisible(boolean draw) { - this.visible = draw; - } - - public boolean getVisible(){ - return this.visible; - } - public void setAnimation(String animation){ currentAnim = animation; } diff --git a/src/main/java/electrosphere/renderer/ui/elements/BufferedStandardDrawableContainerElement.java b/src/main/java/electrosphere/renderer/ui/elements/BufferedStandardDrawableContainerElement.java new file mode 100644 index 00000000..cd39faf2 --- /dev/null +++ b/src/main/java/electrosphere/renderer/ui/elements/BufferedStandardDrawableContainerElement.java @@ -0,0 +1,171 @@ +package electrosphere.renderer.ui.elements; + +import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.glClear; +import static org.lwjgl.opengl.GL11.glClearColor; + +import org.joml.Vector3f; +import org.lwjgl.opengl.GL45; + +import electrosphere.engine.Globals; +import electrosphere.logger.LoggerInterface; +import electrosphere.renderer.OpenGLState; +import electrosphere.renderer.RenderPipelineState; +import electrosphere.renderer.framebuffer.Framebuffer; +import electrosphere.renderer.framebuffer.FramebufferUtils; +import electrosphere.renderer.model.Material; +import electrosphere.renderer.model.Model; +import electrosphere.renderer.texture.Texture; +import electrosphere.renderer.ui.elementtypes.DrawableElement; +import electrosphere.renderer.ui.elementtypes.Element; + +/** + * A drawable container component that draws to an internal framebuffer before rendering that framebuffer on its own draw call** + */ +public class BufferedStandardDrawableContainerElement extends StandardDrawableContainerElement { + + /** + * The default width of an actor panel + */ + public static final int DEFAULT_WIDTH = 50; + + /** + * The default height of an actor panel + */ + public static final int DEFAULT_HEIGHT = 50; + + /** + * The material that contains the internal framebuffer's texture + */ + Material elementMat; + + /** + * The internal framebuffer for this component (all children are rendered to this, then this texture is rendered to a quad itself) + */ + Framebuffer elementBuffer; + + /** + * Constructor + */ + public BufferedStandardDrawableContainerElement(){ + super(); + elementMat = new Material(); + this.regenerateFramebuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT); + } + + + @Override + public void draw( + RenderPipelineState renderPipelineState, + OpenGLState openGLState, + int parentFramebufferPointer, + int parentPosX, + int parentPosY, + int parentWidth, + int parentHeight + ) { + + float ndcWidth = (float)getWidth()/parentWidth; + float ndcHeight = (float)getHeight()/parentHeight; + float ndcX = (float)(getInternalX() + parentPosX)/parentWidth; + float ndcY = (float)(getInternalY() + parentPosY)/parentHeight; + Vector3f boxPosition = new Vector3f(ndcX,ndcY,0); + Vector3f boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); + Vector3f texPosition = new Vector3f(0,0,0); + Vector3f texScale = new Vector3f(1,1,0); + + //grab assets required to render window + Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID); + Texture windowFrame = Globals.assetManager.fetchTexture("Textures/ui/uiFrame1.png"); + + + elementBuffer.bind(openGLState); + openGLState.glViewport(width, height); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + for(Element child : childList){ + if(child instanceof DrawableElement){ + DrawableElement drawableChild = (DrawableElement) child; + drawableChild.draw( + renderPipelineState, + openGLState, + parentFramebufferPointer, + parentPosX + this.internalPositionX, + parentPosY + this.internalPositionY, + parentWidth, + parentHeight + ); + } + } + //this call binds the screen as the "texture" we're rendering to + //have to call before actually rendering + openGLState.glBindFramebuffer(GL45.GL_FRAMEBUFFER, parentFramebufferPointer); + openGLState.glViewport(parentWidth, parentHeight); + + //render background of window + if(planeModel != null && windowFrame != null){ + planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); + planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); + planeModel.pushUniformToMesh("plane", "tPosition", texPosition); + planeModel.pushUniformToMesh("plane", "tDimension", texScale); + elementMat.setTexturePointer(windowFrame.getTexturePointer()); + planeModel.getMeshes().get(0).setMaterial(elementMat); + planeModel.drawUI(); + } + + if(planeModel != null){ + planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); + planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); + planeModel.pushUniformToMesh("plane", "tPosition", texPosition); + planeModel.pushUniformToMesh("plane", "tDimension", texScale); + elementMat.setTexturePointer(elementBuffer.getTexture().getTexturePointer()); + planeModel.getMeshes().get(0).setMaterial(elementMat); + planeModel.drawUI(); + } else { + LoggerInterface.loggerRenderer.ERROR("ScrollableContainer unable to find plane model!!", new Exception()); + } + } + + @Override + public void setWidth(int width) { + if(width <= 1){ + throw new Error("Provided invalid width! " + width); + } + super.setWidth(width); + this.regenerateFramebuffer(width, height); + } + + @Override + public void setHeight(int height) { + if(height <= 1){ + throw new Error("Provided invalid height! " + height); + } + super.setHeight(height); + this.regenerateFramebuffer(width, height); + } + + /** + * Regenerates the backing framebuffer + * @param width The width of the new buffer + * @param height The height of the new buffer + */ + protected void regenerateFramebuffer(int width, int height){ + if(width <= 1 || height <= 1){ + throw new Error("Invalid dimensions set! " + width + " " + height); + } + if(elementBuffer != null){ + elementBuffer.free(); + } + try { + elementBuffer = FramebufferUtils.generateTextureFramebuffer(Globals.renderingEngine.getOpenGLState(), width, height); + } catch(Exception e){ + LoggerInterface.loggerRenderer.ERROR(e); + } + elementMat.setTexturePointer(elementBuffer.getTexture().getTexturePointer()); + } + + +} diff --git a/src/main/java/electrosphere/renderer/ui/elements/Div.java b/src/main/java/electrosphere/renderer/ui/elements/Div.java index 32cc5d97..23a079fd 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/Div.java +++ b/src/main/java/electrosphere/renderer/ui/elements/Div.java @@ -72,6 +72,23 @@ public class Div extends StandardContainerElement implements ClickableElement,Dr return rVal; } + /** + * Creates a wrapper div + * @param child The child to wrap + * @param width The width of the wrapper + * @param height The height of the wrapper + * @return The wrapper element + */ + public static Div createWrapper(Element child, int width, int height){ + Div rVal = new Div(); + rVal.setWidth(width); + rVal.setHeight(height); + if(child != null){ + rVal.addChild(child); + } + return rVal; + } + /** * Constructor */ diff --git a/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java b/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java index bda194c3..79b82c1c 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java +++ b/src/main/java/electrosphere/renderer/ui/elements/ImagePanel.java @@ -39,7 +39,7 @@ public class ImagePanel extends StandardElement implements DrawableElement, Drag //rendering data for positioning the model - Vector3f texPosition = new Vector3f(0,0,0); + Vector3f texPosition = new Vector3f(1,1,0); Vector3f texScale = new Vector3f(1,1,0); Vector3f boxPosition = new Vector3f(); Vector3f boxDimensions = new Vector3f(); diff --git a/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java b/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java index a4ac508f..85f5201a 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java +++ b/src/main/java/electrosphere/renderer/ui/elements/ScrollableContainer.java @@ -6,9 +6,6 @@ import electrosphere.engine.Globals; import electrosphere.logger.LoggerInterface; import electrosphere.renderer.OpenGLState; import electrosphere.renderer.RenderPipelineState; -import electrosphere.renderer.framebuffer.Framebuffer; -import electrosphere.renderer.framebuffer.FramebufferUtils; -import electrosphere.renderer.model.Material; import electrosphere.renderer.model.Model; import electrosphere.renderer.texture.Texture; import electrosphere.renderer.ui.elementtypes.ContainerElement; @@ -19,32 +16,38 @@ import electrosphere.renderer.ui.events.Event; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL30.*; -public class ScrollableContainer extends StandardContainerElement implements DrawableElement { +public class ScrollableContainer extends BufferedStandardDrawableContainerElement { + /** + * The default width of a slider + */ + static final int DEFAULT_HEIGHT = 50; + + /** + * The default height of a slider + */ + static final int DEFAULT_WIDTH = 50; + + /** + * The color associated with the scrollable + */ Vector3f color = new Vector3f(1.0f); - boolean focused = false; - - public boolean visible = false; - - Framebuffer widgetBuffer; - Material customMat = new Material(); Vector3f boxPosition = new Vector3f(); Vector3f boxDimensions = new Vector3f(); Vector3f texPosition = new Vector3f(0,0,0); Vector3f texScale = new Vector3f(1,1,0); - + @Deprecated public ScrollableContainer(OpenGLState openGLState, int positionX, int positionY, int width, int height){ super(); - try { - widgetBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height); - } catch(Exception e){ - LoggerInterface.loggerRenderer.ERROR(e); - } -// widgetBuffer = FramebufferUtils.generateScreensizeTextureFramebuffer(); - customMat.setTexturePointer(widgetBuffer.getTexture().getTexturePointer()); -// customMat.setTexturePointer(Globals.assetManager.fetchTexture("Textures/Testing1.png").getTexturePointer()); + + //must manually set because framebuffer is allocated on set + //these default to -1 until they are set with methods, but methods allocate on each set + //to break chicken-and-egg problem, manually set here + this.width = DEFAULT_WIDTH; + this.height = DEFAULT_HEIGHT; + float ndcX = (float)positionX/Globals.WINDOW_WIDTH; float ndcY = (float)positionY/Globals.WINDOW_HEIGHT; float ndcWidth = (float)width/Globals.WINDOW_WIDTH; @@ -55,6 +58,29 @@ public class ScrollableContainer extends StandardContainerElement implements Dra boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); } + /** + * Constructor + */ + private ScrollableContainer(){ + super(); + + //must manually set because framebuffer is allocated on set + //these default to -1 until they are set with methods, but methods allocate on each set + //to break chicken-and-egg problem, manually set here + this.width = DEFAULT_WIDTH; + this.height = DEFAULT_HEIGHT; + + setWidth(DEFAULT_WIDTH); + setWidth(DEFAULT_HEIGHT); + } + + /** + * Creates a scrollable container element + * @return The scrollable + */ + public static ScrollableContainer createScrollable(){ + return new ScrollableContainer(); + } @Override @@ -62,16 +88,6 @@ public class ScrollableContainer extends StandardContainerElement implements Dra return false; } - @Override - public boolean getVisible() { - return visible; - } - - @Override - public void setVisible(boolean visible) { - this.visible = visible; - } - //recursively check if focused element is child of input element or is input element boolean containsFocusedElement(Element parent){ Element focusedElement = Globals.elementService.getFocusedElement(); @@ -98,103 +114,113 @@ public class ScrollableContainer extends StandardContainerElement implements Dra int parentWidth, int parentHeight ) { - //figure out if currently focused element is a child or subchild of this container - if(containsFocusedElement(this)){ - //if it is, if it is offscreen, calculate offset to put it onscreen - Element focused = Globals.elementService.getFocusedElement(); - if( - focused.getRelativeX() + focused.getWidth() > this.width || - focused.getRelativeY() + focused.getHeight() > this.height || - focused.getRelativeX() < 0 || - focused.getRelativeY() < 0 - ){ - int neededOffsetX = 0; - int neededOffsetY = 0; - //basically if we're offscreen negative, pull to positive - //if we're offscreen positive and we're not as large as the screen, pull from the positive into focus - //if we are larger than the screen, set position to 0 - if(focused.getRelativeX() < 0){ - neededOffsetX = -focused.getRelativeX(); - } else if(focused.getRelativeX() + focused.getWidth() > this.width){ - if(focused.getWidth() > this.width){ + if(this.elementBuffer != null){ + //figure out if currently focused element is a child or subchild of this container + if(containsFocusedElement(this)){ + //if it is, if it is offscreen, calculate offset to put it onscreen + Element focused = Globals.elementService.getFocusedElement(); + if( + focused.getRelativeX() + focused.getWidth() > this.width || + focused.getRelativeY() + focused.getHeight() > this.height || + focused.getRelativeX() < 0 || + focused.getRelativeY() < 0 + ){ + int neededOffsetX = 0; + int neededOffsetY = 0; + //basically if we're offscreen negative, pull to positive + //if we're offscreen positive and we're not as large as the screen, pull from the positive into focus + //if we are larger than the screen, set position to 0 + if(focused.getRelativeX() < 0){ neededOffsetX = -focused.getRelativeX(); - } else { - neededOffsetX = -((focused.getRelativeX() - this.width) + focused.getWidth()); + } else if(focused.getRelativeX() + focused.getWidth() > this.width){ + if(focused.getWidth() > this.width){ + neededOffsetX = -focused.getRelativeX(); + } else { + neededOffsetX = -((focused.getRelativeX() - this.width) + focused.getWidth()); + } } - } - if(focused.getRelativeY() < 0){ - neededOffsetY = -focused.getRelativeY(); - } else if(focused.getRelativeY() + focused.getHeight() > this.height){ - if(focused.getHeight() > this.height){ + if(focused.getRelativeY() < 0){ neededOffsetY = -focused.getRelativeY(); - } else { - neededOffsetY = -((focused.getRelativeY() - this.height) + focused.getHeight()); - // System.out.println(focused.getPositionY() + " " + this.height + " " + focused.getHeight()); + } else if(focused.getRelativeY() + focused.getHeight() > this.height){ + if(focused.getHeight() > this.height){ + neededOffsetY = -focused.getRelativeY(); + } else { + neededOffsetY = -((focused.getRelativeY() - this.height) + focused.getHeight()); + // System.out.println(focused.getPositionY() + " " + this.height + " " + focused.getHeight()); + } + } + //apply offset to all children + for(Element child : childList){ + int newX = child.getRelativeX() + neededOffsetX; + int newY = child.getRelativeY() + neededOffsetY; + child.setPositionX(newX); + child.setPositionY(newY); + // System.out.println(currentX + " " + currentY); } } - //apply offset to all children - for(Element child : childList){ - int newX = child.getRelativeX() + neededOffsetX; - int newY = child.getRelativeY() + neededOffsetY; - child.setPositionX(newX); - child.setPositionY(newY); - // System.out.println(currentX + " " + currentY); + } + + float ndcWidth = (float)getWidth()/parentWidth; + float ndcHeight = (float)getHeight()/parentHeight; + float ndcX = (float)(getInternalX() + parentPosX)/parentWidth; + float ndcY = (float)(getInternalY() + parentPosY)/parentHeight; + boxPosition = new Vector3f(ndcX,ndcY,0); + boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); + + //grab assets required to render window + Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID); + Texture windowFrame = Globals.assetManager.fetchTexture("Textures/ui/uiFrame1.png"); + + + elementBuffer.bind(openGLState); + openGLState.glViewport(width, height); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + for(Element child : childList){ + if(child instanceof DrawableElement){ + DrawableElement drawableChild = (DrawableElement) child; + drawableChild.draw( + renderPipelineState, + openGLState, + parentFramebufferPointer, + parentPosX + this.internalPositionX, + parentPosY + this.internalPositionY, + parentWidth, + parentHeight + ); } } - } + //this call binds the screen as the "texture" we're rendering to + //have to call before actually rendering + openGLState.glBindFramebuffer(GL_FRAMEBUFFER, parentFramebufferPointer); + openGLState.glViewport(parentWidth, parentHeight); - float ndcX = (float)internalPositionX/parentWidth; - float ndcY = (float)internalPositionY/parentHeight; - float ndcWidth = (float)internalWidth/parentWidth; - float ndcHeight = (float)internalHeight/parentHeight; - boxPosition = new Vector3f(ndcX,ndcY,0); - boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); - - //grab assets required to render window - Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID); - Texture windowFrame = Globals.assetManager.fetchTexture("Textures/ui/uiFrame1.png"); - - - widgetBuffer.bind(openGLState); - openGLState.glViewport(width, height); - - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - for(Element child : childList){ - if(child instanceof DrawableElement){ - DrawableElement drawableChild = (DrawableElement) child; - drawableChild.draw(renderPipelineState,openGLState,widgetBuffer.getFramebufferPointer(),0,0,width,height); + //render background of window + if(planeModel != null && windowFrame != null){ + planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); + planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); + planeModel.pushUniformToMesh("plane", "tPosition", texPosition); + planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); + elementMat.setTexturePointer(windowFrame.getTexturePointer()); + planeModel.getMeshes().get(0).setMaterial(elementMat); + planeModel.drawUI(); + } + + if(planeModel != null){ + planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); + planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); + planeModel.pushUniformToMesh("plane", "tPosition", texPosition); + planeModel.pushUniformToMesh("plane", "tDimension", texScale); + planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); + elementMat.setTexturePointer(elementBuffer.getTexture().getTexturePointer()); + planeModel.getMeshes().get(0).setMaterial(elementMat); + planeModel.drawUI(); + } else { + LoggerInterface.loggerRenderer.ERROR("ScrollableContainer unable to find plane model!!", new Exception()); } - } - //this call binds the screen as the "texture" we're rendering to - //have to call before actually rendering - openGLState.glBindFramebuffer(GL_FRAMEBUFFER, parentFramebufferPointer); - openGLState.glViewport(parentWidth, parentHeight); - - //render background of window - if(planeModel != null && windowFrame != null){ - planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); - planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); - planeModel.pushUniformToMesh("plane", "tPosition", texPosition); - planeModel.pushUniformToMesh("plane", "tDimension", texScale); - planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); - customMat.setTexturePointer(windowFrame.getTexturePointer()); - planeModel.getMeshes().get(0).setMaterial(customMat); - planeModel.drawUI(); - } - - if(planeModel != null){ - planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); - planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions); - planeModel.pushUniformToMesh("plane", "tPosition", texPosition); - planeModel.pushUniformToMesh("plane", "tDimension", texScale); - planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color); - customMat.setTexturePointer(widgetBuffer.getTexture().getTexturePointer()); - planeModel.getMeshes().get(0).setMaterial(customMat); - planeModel.drawUI(); - } else { - LoggerInterface.loggerRenderer.ERROR("ScrollableContainer unable to find plane model!!", new Exception()); } } diff --git a/src/main/java/electrosphere/renderer/ui/elements/Slider.java b/src/main/java/electrosphere/renderer/ui/elements/Slider.java index e795782e..5e77dbc6 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/Slider.java +++ b/src/main/java/electrosphere/renderer/ui/elements/Slider.java @@ -46,26 +46,25 @@ public class Slider extends StandardDrawableElement implements ClickableElement, static Material mat; + int drawMarginX = 5; + int drawMarginY = 5; + static final int idealMargin = 5; //5 pixels margin ideally static final Vector3f windowDrawDebugColor = new Vector3f(1.0f,1.0f,1.0f); + /** + * The default width of a slider + */ static final int DEFAULT_HEIGHT = 20; - static final int DEFAULT_WIDTH = 100; - /** - * Creates a slider element - * @param callback the Logic to fire when the slider changes value - * @return the slider element + * The default height of a slider */ - public static Slider createSlider(ValueChangeEventCallback callback){ - Slider slider = new Slider(); - slider.setOnValueChangeCallback(callback); - return slider; - } + static final int DEFAULT_WIDTH = 100; + /** * Creates a slider element @@ -126,17 +125,17 @@ public class Slider extends StandardDrawableElement implements ClickableElement, Globals.renderingEngine.bindFramebuffer(parentFramebufferPointer); openGLState.glViewport(parentWidth, parentHeight); - int marginX = Math.max(width - idealMargin * 2, 0); - if(marginX < idealMargin){ - marginX = 0; + int drawMarginX = Math.max(width - idealMargin * 2, 0); + if(drawMarginX < idealMargin){ + drawMarginX = 0; } else { - marginX = idealMargin; + drawMarginX = idealMargin; } - int marginY = Math.max(height - idealMargin * 2, 0); - if(marginY < idealMargin){ - marginY = 0; + int drawMarginY = Math.max(height - idealMargin * 2, 0); + if(drawMarginY < idealMargin){ + drawMarginY = 0; } else { - marginY = idealMargin; + drawMarginY = idealMargin; } @@ -158,10 +157,10 @@ public class Slider extends StandardDrawableElement implements ClickableElement, planeModel.drawUI(); //actual slider - ndcX = (float)(getInternalX() + marginX + parentPosX)/parentWidth; - ndcY = (float)(getInternalY() + marginY + parentPosY)/parentHeight; - ndcWidth = (float)((getInternalWidth() - marginX * 2) * getValueAsPercentage())/parentWidth; - ndcHeight = (float)(getInternalHeight() - marginY * 2)/parentHeight; + ndcWidth = (float)((getInternalWidth() - drawMarginX * 2) * getValueAsPercentage())/parentWidth; + ndcHeight = (float)(getInternalHeight() - drawMarginY * 2)/parentHeight; + ndcX = (float)(getInternalX() + drawMarginX + parentPosX)/parentWidth; + ndcY = (float)(getInternalY() + drawMarginY + parentPosY)/parentHeight; boxPosition = new Vector3f(ndcX,ndcY,0); boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); @@ -173,7 +172,7 @@ public class Slider extends StandardDrawableElement implements ClickableElement, LoggerInterface.loggerRenderer.ERROR("Window unable to find plane model!!", new Exception()); } if(Globals.RENDER_FLAG_RENDER_UI_BOUNDS && DebugRendering.RENDER_DEBUG_OUTLINE_SLIDER){ - boxDimensions.x = (float)((width - marginX * 2) * 1.0f)/parentWidth; + boxDimensions.x = (float)((width - drawMarginX * 2) * 1.0f)/parentWidth; DebugRendering.drawUIBounds(parentFramebufferPointer, boxPosition, boxDimensions, windowDrawDebugColor); } } @@ -248,6 +247,15 @@ public class Slider extends StandardDrawableElement implements ClickableElement, onValueChange = callback; } + /** + * Gets the current value from the percentage + * @param percentage The percentage + * @return The value + */ + private float valueFromPercentage(float percentage){ + return (percentage * (max - min) + min); + } + @Override public boolean handleEvent(Event event) { @@ -280,6 +288,7 @@ public class Slider extends StandardDrawableElement implements ClickableElement, switch(menuEvent.getType()){ case INCREMENT: value = Math.min(value + ((max - min) * 0.01f),max); + value = this.valueFromPercentage(value); if(onValueChange != null){ onValueChange.execute(new ValueChangeEvent(value)); } @@ -287,6 +296,7 @@ public class Slider extends StandardDrawableElement implements ClickableElement, break; case DECREMENT: value = Math.max(value - ((max - min) * 0.01f),min); + value = this.valueFromPercentage(value); if(onValueChange != null){ onValueChange.execute(new ValueChangeEvent(value)); } @@ -308,9 +318,10 @@ public class Slider extends StandardDrawableElement implements ClickableElement, propagate = onDrag.execute(dragEvent); } else { //default behavior - int percentage = dragEvent.getCurrentX() - getInternalX(); - int max = width; + int percentage = dragEvent.getRelativeX() - getInternalX(); + int max = getInternalWidth(); value = Math.max(Math.min((float)percentage/max,1.0f),0.0f); + value = this.valueFromPercentage(value); if(onValueChange != null){ onValueChange.execute(new ValueChangeEvent(value)); } @@ -321,9 +332,10 @@ public class Slider extends StandardDrawableElement implements ClickableElement, propagate = onDragRelease.execute(dragEvent); } else { //default behavior - int percentage = dragEvent.getCurrentX() - getInternalX(); - int max = width; + int percentage = dragEvent.getRelativeX() - getInternalX(); + int max = getInternalWidth(); value = Math.max(Math.min((float)percentage/max,1.0f),0.0f); + value = this.valueFromPercentage(value); if(onValueChange != null){ onValueChange.execute(new ValueChangeEvent(value)); } @@ -338,8 +350,9 @@ public class Slider extends StandardDrawableElement implements ClickableElement, } else { //default behavior int percentage = clickEvent.getRelativeX() - getInternalX(); - int max = width; + int max = getInternalWidth(); value = Math.max(Math.min((float)percentage/max,1.0f),0.0f); + value = this.valueFromPercentage(value); if(onValueChange != null){ onValueChange.execute(new ValueChangeEvent(value)); } diff --git a/src/main/java/electrosphere/renderer/ui/elements/StandardContainerElement.java b/src/main/java/electrosphere/renderer/ui/elements/StandardContainerElement.java index 0000df07..35a5b1a5 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/StandardContainerElement.java +++ b/src/main/java/electrosphere/renderer/ui/elements/StandardContainerElement.java @@ -137,16 +137,6 @@ public class StandardContainerElement extends StandardElement implements Contain } } - // @Override - // public void setWidth(int width) { - // this.width = width; - // } - - // @Override - // public void setHeight(int height) { - // this.height = height; - // } - @Override public void setPositionX(int posX) { internalPositionX = posX; diff --git a/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java index dec04f85..4ce4530c 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java +++ b/src/main/java/electrosphere/renderer/ui/elements/StringCarousel.java @@ -2,6 +2,7 @@ package electrosphere.renderer.ui.elements; import java.util.LinkedList; import java.util.List; +import java.util.function.Consumer; import org.joml.Vector3f; import org.lwjgl.util.yoga.Yoga; @@ -50,6 +51,27 @@ public class StringCarousel extends StandardContainerElement implements Drawable Yoga.YGNodeStyleSetMinWidth(this.yogaNode, 1); } + /** + * Constructor + */ + private StringCarousel(){ + super(); + this.font = Globals.fontManager.getFont("default"); + } + + /** + * Creates a string carousel element + * @param options The options for the carousel + * @return The carousel + */ + public static StringCarousel create(List options, Consumer callback){ + StringCarousel rVal = new StringCarousel(); + rVal.setOnValueChangeCallback(new ValueChangeEventCallback() {public void execute(ValueChangeEvent event) { + callback.accept(event); + }}); + return rVal; + } + public void addOption(String option){ options.add(option); if(currentOption == -1){ diff --git a/src/main/java/electrosphere/renderer/ui/elements/Window.java b/src/main/java/electrosphere/renderer/ui/elements/Window.java index daf54db8..6822ac57 100644 --- a/src/main/java/electrosphere/renderer/ui/elements/Window.java +++ b/src/main/java/electrosphere/renderer/ui/elements/Window.java @@ -97,6 +97,21 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme this.setHeight(height); } + /** + * Creates a window + * @param openGLState The opengl state + * @param posX The x position of the window + * @param posY The y position of the window + * @param width The width of the window + * @param height The height of the window + * @param showDecorations true to show window decorations, false otherwise + * @return The window element + */ + public static Window create(OpenGLState openGLState, int posX, int posY, int width, int height, boolean showDecorations){ + Window rVal = new Window(openGLState, posX, posY, width, height, showDecorations); + return rVal; + } + @Override public void draw( RenderPipelineState renderPipelineState, diff --git a/src/main/java/electrosphere/renderer/ui/events/DragEvent.java b/src/main/java/electrosphere/renderer/ui/events/DragEvent.java index 7b1b5d36..9352ddb4 100644 --- a/src/main/java/electrosphere/renderer/ui/events/DragEvent.java +++ b/src/main/java/electrosphere/renderer/ui/events/DragEvent.java @@ -14,6 +14,9 @@ public class DragEvent implements Event { int currentY; int previousX; int previousY; + //relative positions + int relativeX; + int relativeY; int deltaX; int deltaY; DragEventType type; @@ -66,4 +69,20 @@ public class DragEvent implements Event { this.target = target; } + public int getRelativeX(){ + return relativeX; + } + + public int getRelativeY(){ + return relativeY; + } + + public void setRelativeX(int relX){ + this.relativeX = relX; + } + + public void setRelativeY(int relY){ + this.relativeY = relY; + } + } diff --git a/src/test/java/electrosphere/renderer/ui/MainMenuTests.java b/src/test/java/electrosphere/renderer/ui/MainMenuTests.java new file mode 100644 index 00000000..1fa030cf --- /dev/null +++ b/src/test/java/electrosphere/renderer/ui/MainMenuTests.java @@ -0,0 +1,32 @@ +package electrosphere.renderer.ui; + +import electrosphere.menu.WindowUtils; +import electrosphere.menu.mainmenu.MenuGeneratorsUITesting; +import electrosphere.test.annotations.IntegrationTest; +import electrosphere.test.template.UITestTemplate; +import electrosphere.test.testutils.TestEngineUtils; + +/** + * Tests rendering the main menu + */ +public class MainMenuTests extends UITestTemplate { + + /** + * Tests creating a window + */ + @IntegrationTest + public void test_UITestWindow_Create(){ + //create ui testing window + TestEngineUtils.simulateFrames(1); + WindowUtils.replaceMainMenuContents(MenuGeneratorsUITesting.createUITestMenu()); + + //wait for ui updates + TestEngineUtils.flush(); + + TestEngineUtils.simulateFrames(1); + + // TestRenderingUtils.saveTestRender("./test/java/renderer/ui/elements/window.png"); + this.checkRender("Basic", "./test/java/renderer/ui/uitest.png"); + } + +} diff --git a/src/test/java/electrosphere/renderer/ui/elements/SliderTests.java b/src/test/java/electrosphere/renderer/ui/elements/SliderTests.java new file mode 100644 index 00000000..20335fa7 --- /dev/null +++ b/src/test/java/electrosphere/renderer/ui/elements/SliderTests.java @@ -0,0 +1,10 @@ +package electrosphere.renderer.ui.elements; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for a slider element + */ +public class SliderTests { + +} diff --git a/src/test/java/electrosphere/renderer/ui/elements/StringCarouselTests.java b/src/test/java/electrosphere/renderer/ui/elements/StringCarouselTests.java new file mode 100644 index 00000000..d8f1b6a4 --- /dev/null +++ b/src/test/java/electrosphere/renderer/ui/elements/StringCarouselTests.java @@ -0,0 +1,10 @@ +package electrosphere.renderer.ui.elements; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for a string carousel + */ +public class StringCarouselTests { + +} diff --git a/src/test/java/electrosphere/renderer/ui/elements/WindowTest.java b/src/test/java/electrosphere/renderer/ui/elements/WindowTest.java index 56a7b924..55a91311 100644 --- a/src/test/java/electrosphere/renderer/ui/elements/WindowTest.java +++ b/src/test/java/electrosphere/renderer/ui/elements/WindowTest.java @@ -1,9 +1,11 @@ package electrosphere.renderer.ui.elements; import electrosphere.test.annotations.IntegrationTest; - +import electrosphere.engine.Globals; +import electrosphere.menu.WindowStrings; import electrosphere.menu.WindowUtils; -import electrosphere.menu.mainmenu.MenuGeneratorsUITesting; +import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment; +import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification; import electrosphere.test.template.UITestTemplate; import electrosphere.test.testutils.TestEngineUtils; @@ -19,15 +21,17 @@ public class WindowTest extends UITestTemplate { public void testCreateWindow(){ //create ui testing window TestEngineUtils.simulateFrames(1); - WindowUtils.replaceMainMenuContents(MenuGeneratorsUITesting.createUITestMenu()); + Window window = Window.create(Globals.renderingEngine.getOpenGLState(),50,50,500,500,true); + window.setParentAlignItem(YogaAlignment.Center); + window.setParentJustifyContent(YogaJustification.Center); + WindowUtils.replaceWindow(WindowStrings.WINDOW_MENU_MAIN, window); //wait for ui updates TestEngineUtils.flush(); TestEngineUtils.simulateFrames(1); - // TestRenderingUtils.saveTestRender("./test/java/renderer/ui/elements/window.png"); - this.checkRender("Basic", "./test/java/renderer/ui/elements/ui-test.png"); + this.checkRender("Basic", "./test/java/renderer/ui/elements/window1.png"); } } diff --git a/test/java/renderer/ui/elements/imagepanel1.png b/test/java/renderer/ui/elements/imagepanel1.png index fa6802d5..26ffe70b 100644 Binary files a/test/java/renderer/ui/elements/imagepanel1.png and b/test/java/renderer/ui/elements/imagepanel1.png differ diff --git a/test/java/renderer/ui/elements/window1.png b/test/java/renderer/ui/elements/window1.png new file mode 100644 index 00000000..bd822dc0 Binary files /dev/null and b/test/java/renderer/ui/elements/window1.png differ diff --git a/test/java/renderer/ui/elements/ui-test.png b/test/java/renderer/ui/uitest.png similarity index 100% rename from test/java/renderer/ui/elements/ui-test.png rename to test/java/renderer/ui/uitest.png