slider fixes, dragevent fixes, new component
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-09-15 22:49:28 -04:00
parent 963e8b6585
commit 63feb835cd
23 changed files with 832 additions and 303 deletions

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
@ -73,6 +78,36 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag
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;
}
@Override
@ -86,8 +121,9 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag
int parentHeight
) {
if(this.elementBuffer != null){
elementBuffer.bind(openGLState);
// Globals.renderingEngine.setViewportSize(width, height);
// Globals.renderingEngine.setViewportSize(width, height);
RenderingEngine.setFOV(FOV);
RenderingEngine.setAspectRatio(aspectRatio);
@ -179,7 +215,7 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag
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.getMeshes().get(0).setMaterial(elementMat);
planeModel.draw(renderPipelineState,Globals.renderingEngine.getOpenGLState());
} else {
LoggerInterface.loggerRenderer.ERROR("Actor Panel unable to find plane model!!", new Exception());
@ -189,18 +225,8 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag
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;

View File

@ -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());
}
}

View File

@ -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
*/

View File

@ -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();

View File

@ -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,6 +114,7 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
int parentWidth,
int parentHeight
) {
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
@ -143,10 +160,10 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
}
}
float ndcX = (float)internalPositionX/parentWidth;
float ndcY = (float)internalPositionY/parentHeight;
float ndcWidth = (float)internalWidth/parentWidth;
float ndcHeight = (float)internalHeight/parentHeight;
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);
@ -155,7 +172,7 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
Texture windowFrame = Globals.assetManager.fetchTexture("Textures/ui/uiFrame1.png");
widgetBuffer.bind(openGLState);
elementBuffer.bind(openGLState);
openGLState.glViewport(width, height);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@ -164,7 +181,15 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
for(Element child : childList){
if(child instanceof DrawableElement){
DrawableElement drawableChild = (DrawableElement) child;
drawableChild.draw(renderPipelineState,openGLState,widgetBuffer.getFramebufferPointer(),0,0,width,height);
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
@ -179,8 +204,8 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
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);
elementMat.setTexturePointer(windowFrame.getTexturePointer());
planeModel.getMeshes().get(0).setMaterial(elementMat);
planeModel.drawUI();
}
@ -190,12 +215,13 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
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);
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());
}
}
}
}

View File

@ -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));
}

View File

@ -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;

View File

@ -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<String> options, Consumer<ValueChangeEvent> 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){

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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");
}
}

View File

@ -0,0 +1,10 @@
package electrosphere.renderer.ui.elements;
import static org.junit.jupiter.api.Assertions.*;
/**
* Tests for a slider element
*/
public class SliderTests {
}

View File

@ -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 {
}

View File

@ -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");
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB