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) Fix render signals caching between frames (not reseting global flags per usual)
Capture image from opengl to pixel-check Capture image from opengl to pixel-check
Add ui tests Add ui tests
Fix image panel displaying texture flipped
Fix drag event relative positions
Fix slider behavior
New character customizer component
# TODO # TODO

View File

@ -32,7 +32,6 @@ import electrosphere.renderer.ui.elements.Slider;
import electrosphere.renderer.ui.elements.VirtualScrollable; import electrosphere.renderer.ui.elements.VirtualScrollable;
import electrosphere.renderer.ui.elements.Window; import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.elementtypes.NavigableElement.NavigationEventCallback; 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.YogaAlignment;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection; import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification; import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification;
@ -393,39 +392,39 @@ public class MenuGeneratorsLevelEditor {
xDiv.setMaxHeight(50); xDiv.setMaxHeight(50);
xDiv.setFlexDirection(YogaFlexDirection.Row); xDiv.setFlexDirection(YogaFlexDirection.Row);
xDiv.addChild(Label.createLabel("X: ")); 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(); LightManager lightManager = Globals.renderingEngine.getLightManager();
DirectionalLight directionalLight = lightManager.getDirectionalLight(); DirectionalLight directionalLight = lightManager.getDirectionalLight();
Vector3f direction = directionalLight.getDirection(); Vector3f direction = directionalLight.getDirection();
direction.x = event.getAsFloat() * 2 - 1; direction.x = event.getAsFloat() * 2 - 1;
directionalLight.setDirection(direction); directionalLight.setDirection(direction);
}})); }));
scrollable.addChild(xDiv); scrollable.addChild(xDiv);
Div yDiv = Div.createDiv(); Div yDiv = Div.createDiv();
yDiv.setMaxHeight(50); yDiv.setMaxHeight(50);
yDiv.setFlexDirection(YogaFlexDirection.Row); yDiv.setFlexDirection(YogaFlexDirection.Row);
yDiv.addChild(Label.createLabel("Y: ")); 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(); LightManager lightManager = Globals.renderingEngine.getLightManager();
DirectionalLight directionalLight = lightManager.getDirectionalLight(); DirectionalLight directionalLight = lightManager.getDirectionalLight();
Vector3f direction = directionalLight.getDirection(); Vector3f direction = directionalLight.getDirection();
direction.y = event.getAsFloat() * 2 - 1; direction.y = event.getAsFloat() * 2 - 1;
directionalLight.setDirection(direction); directionalLight.setDirection(direction);
}})); }));
scrollable.addChild(yDiv); scrollable.addChild(yDiv);
Div zDiv = Div.createDiv(); Div zDiv = Div.createDiv();
zDiv.setMaxHeight(50); zDiv.setMaxHeight(50);
zDiv.setFlexDirection(YogaFlexDirection.Row); zDiv.setFlexDirection(YogaFlexDirection.Row);
zDiv.addChild(Label.createLabel("Z: ")); 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(); LightManager lightManager = Globals.renderingEngine.getLightManager();
DirectionalLight directionalLight = lightManager.getDirectionalLight(); DirectionalLight directionalLight = lightManager.getDirectionalLight();
Vector3f direction = directionalLight.getDirection(); Vector3f direction = directionalLight.getDirection();
direction.z = event.getAsFloat() * 2 - 1; direction.z = event.getAsFloat() * 2 - 1;
directionalLight.setDirection(direction); directionalLight.setDirection(direction);
}})); }));
scrollable.addChild(zDiv); scrollable.addChild(zDiv);

View File

@ -6,6 +6,7 @@ import electrosphere.client.entity.camera.CameraEntityUtils;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.menu.WindowUtils; import electrosphere.menu.WindowUtils;
import electrosphere.renderer.actor.ActorUtils; import electrosphere.renderer.actor.ActorUtils;
import electrosphere.renderer.ui.components.CharacterCustomizer;
import electrosphere.renderer.ui.elements.ActorPanel; import electrosphere.renderer.ui.elements.ActorPanel;
import electrosphere.renderer.ui.elements.Button; import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.FormElement; 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.Slider;
import electrosphere.renderer.ui.elements.VirtualScrollable; import electrosphere.renderer.ui.elements.VirtualScrollable;
import electrosphere.renderer.ui.elementtypes.Element; import electrosphere.renderer.ui.elementtypes.Element;
import electrosphere.renderer.ui.elementtypes.ValueElement;
import electrosphere.renderer.ui.events.ValueChangeEvent; import electrosphere.renderer.ui.events.ValueChangeEvent;
import electrosphere.renderer.ui.macros.InputMacros; import electrosphere.renderer.ui.macros.InputMacros;
@ -59,9 +59,9 @@ public class MenuGeneratorsUITesting {
} }
// slider test // slider test
Slider slider = Slider.createSlider(new ValueElement.ValueChangeEventCallback() {public void execute(ValueChangeEvent event) { Slider slider = Slider.createSlider((ValueChangeEvent event) -> {
}}); });
virtualScrollable.addChild(slider); virtualScrollable.addChild(slider);
rVal.addChild(virtualScrollable); rVal.addChild(virtualScrollable);

View File

@ -157,6 +157,14 @@ public class FramebufferUtils {
return buffer; 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 { public static Framebuffer generateTextureFramebuffer(OpenGLState openGLState, int width, int height) throws Exception {
Framebuffer buffer = new Framebuffer(); Framebuffer buffer = new Framebuffer();
buffer.bind(openGLState); buffer.bind(openGLState);

View File

@ -233,6 +233,11 @@ public class ElementService extends SignalServiceImpl {
Vector2i absPos = getAbsolutePosition(currentElement); Vector2i absPos = getAbsolutePosition(currentElement);
clickEvent.setRelativeX(clickEvent.getCurrentX() - absPos.x); clickEvent.setRelativeX(clickEvent.getCurrentX() - absPos.x);
clickEvent.setRelativeY(clickEvent.getCurrentY() - absPos.y); 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); 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){ public void drag(int x, int y, int lastX, int lastY, int deltaX, int deltaY){
if(currentDragElement != null){ if(currentDragElement != null){
//need to calculate offset based on parent positions DragEvent event = new DragEvent(x, y, lastX, lastY, deltaX, deltaY, DragEventType.DRAG, currentDragElement);
//the positions for x, y, lastX, and lastY are all in absolute-to-screen coordinates Vector2i absPos = this.getAbsolutePosition(currentDragElement);
//the logic for things like slider depend on the event.setRelativeX(x - absPos.x);
Vector2i absPos = getAbsolutePosition(currentDragElement); event.setRelativeY(y - absPos.y);
DragEvent event = new DragEvent(x - absPos.x, y - absPos.y, lastX - absPos.x, lastY - absPos.y, deltaX, deltaY, DragEventType.DRAG, currentDragElement);
currentDragElement.handleEvent(event); currentDragElement.handleEvent(event);
} }
// fireEvent(event,event.getCurrentX(),event.getCurrentY()); // 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){ public void dragRelease(int x, int y, int lastX, int lastY, int deltaX, int deltaY){
if(currentDragElement != null){ if(currentDragElement != null){
//need to calculate offset based on parent positions DragEvent event = new DragEvent(x, y, lastX, lastY, deltaX, deltaY, DragEventType.RELEASE, currentDragElement);
//the positions for x, y, lastX, and lastY are all in absolute-to-screen coordinates Vector2i absPos = this.getAbsolutePosition(currentDragElement);
//the logic for things like slider depend on the event.setRelativeX(x - absPos.x);
Vector2i absPos = getAbsolutePosition(currentDragElement); event.setRelativeY(y - absPos.y);
DragEvent event = new DragEvent(x - absPos.x, y - absPos.y, lastX - absPos.x, lastY - absPos.y, deltaX, deltaY, DragEventType.RELEASE, currentDragElement);
currentDragElement.handleEvent(event); currentDragElement.handleEvent(event);
currentDragElement = null; 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.RenderingEngine;
import electrosphere.renderer.actor.Actor; import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.debug.DebugRendering; 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.model.Model;
import electrosphere.renderer.ui.elementtypes.DraggableElement; import electrosphere.renderer.ui.elementtypes.DraggableElement;
import electrosphere.renderer.ui.elementtypes.DrawableElement;
import electrosphere.renderer.ui.events.DragEvent; import electrosphere.renderer.ui.events.DragEvent;
import electrosphere.renderer.ui.events.DragEvent.DragEventType; import electrosphere.renderer.ui.events.DragEvent.DragEventType;
import electrosphere.renderer.ui.events.Event; 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); static Vector3f color = new Vector3f(1.0f);
Material customMat = new Material();
Framebuffer elementBuffer;
Actor actor; Actor actor;
Matrix4d modelMatrix = new Matrix4d(); Matrix4d modelMatrix = new Matrix4d();
String currentAnim; 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); 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){ public ActorPanel(OpenGLState openGLState, int x, int y, int width, int height, Actor actor){
super(); super();
try {
elementBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height); //must manually set because framebuffer is allocated on set
} catch(Exception e){ //these default to -1 until they are set with methods, but methods allocate on each set
LoggerInterface.loggerRenderer.ERROR(e); //to break chicken-and-egg problem, manually set here
} this.width = DEFAULT_WIDTH;
customMat.setTexturePointer(elementBuffer.getTexture().getTexturePointer()); this.height = DEFAULT_HEIGHT;
this.actor = actor; this.actor = actor;
this.internalPositionX = x; this.internalPositionX = x;
this.internalPositionY = y; this.internalPositionY = y;
@ -72,6 +77,36 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag
this.aspectRatio = (float)width / (float)height; this.aspectRatio = (float)width / (float)height;
recalculateModelMatrix(); 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 int parentHeight
) { ) {
elementBuffer.bind(openGLState); if(this.elementBuffer != null){
// Globals.renderingEngine.setViewportSize(width, height); elementBuffer.bind(openGLState);
// Globals.renderingEngine.setViewportSize(width, height);
RenderingEngine.setFOV(FOV); RenderingEngine.setFOV(FOV);
RenderingEngine.setAspectRatio(aspectRatio); RenderingEngine.setAspectRatio(aspectRatio);
openGLState.glDepthTest(true); openGLState.glDepthTest(true);
openGLState.glDepthFunc(GL_LESS); openGLState.glDepthFunc(GL_LESS);
glDepthMask(true); glDepthMask(true);
openGLState.glViewport(width, height); openGLState.glViewport(width, height);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 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(currentAnim != null){
if((!actor.isPlayingAnimation() || !actor.isPlayingAnimation(currentAnim)) && if((!actor.isPlayingAnimation() || !actor.isPlayingAnimation(currentAnim)) &&
actorModel != null && actorModel != null &&
actorModel.getAnimation(currentAnim) != null actorModel.getAnimation(currentAnim) != null
){ ){
actor.playAnimation(currentAnim,3); actor.playAnimation(currentAnim,3);
actor.incrementAnimationTime(0.0001); 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){ public void setAnimation(String animation){
currentAnim = 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; 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 * Constructor
*/ */

View File

@ -39,7 +39,7 @@ public class ImagePanel extends StandardElement implements DrawableElement, Drag
//rendering data for positioning the model //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 texScale = new Vector3f(1,1,0);
Vector3f boxPosition = new Vector3f(); Vector3f boxPosition = new Vector3f();
Vector3f boxDimensions = new Vector3f(); Vector3f boxDimensions = new Vector3f();

View File

@ -6,9 +6,6 @@ import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.OpenGLState; import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState; 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.model.Model;
import electrosphere.renderer.texture.Texture; import electrosphere.renderer.texture.Texture;
import electrosphere.renderer.ui.elementtypes.ContainerElement; 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.GL11.*;
import static org.lwjgl.opengl.GL30.*; 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); Vector3f color = new Vector3f(1.0f);
boolean focused = false;
public boolean visible = false;
Framebuffer widgetBuffer;
Material customMat = new Material();
Vector3f boxPosition = new Vector3f(); Vector3f boxPosition = new Vector3f();
Vector3f boxDimensions = new Vector3f(); Vector3f boxDimensions = new Vector3f();
Vector3f texPosition = new Vector3f(0,0,0); Vector3f texPosition = new Vector3f(0,0,0);
Vector3f texScale = new Vector3f(1,1,0); Vector3f texScale = new Vector3f(1,1,0);
@Deprecated
public ScrollableContainer(OpenGLState openGLState, int positionX, int positionY, int width, int height){ public ScrollableContainer(OpenGLState openGLState, int positionX, int positionY, int width, int height){
super(); super();
try {
widgetBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height); //must manually set because framebuffer is allocated on set
} catch(Exception e){ //these default to -1 until they are set with methods, but methods allocate on each set
LoggerInterface.loggerRenderer.ERROR(e); //to break chicken-and-egg problem, manually set here
} this.width = DEFAULT_WIDTH;
// widgetBuffer = FramebufferUtils.generateScreensizeTextureFramebuffer(); this.height = DEFAULT_HEIGHT;
customMat.setTexturePointer(widgetBuffer.getTexture().getTexturePointer());
// customMat.setTexturePointer(Globals.assetManager.fetchTexture("Textures/Testing1.png").getTexturePointer());
float ndcX = (float)positionX/Globals.WINDOW_WIDTH; float ndcX = (float)positionX/Globals.WINDOW_WIDTH;
float ndcY = (float)positionY/Globals.WINDOW_HEIGHT; float ndcY = (float)positionY/Globals.WINDOW_HEIGHT;
float ndcWidth = (float)width/Globals.WINDOW_WIDTH; float ndcWidth = (float)width/Globals.WINDOW_WIDTH;
@ -55,6 +58,29 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); 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 @Override
@ -62,16 +88,6 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
return false; 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 //recursively check if focused element is child of input element or is input element
boolean containsFocusedElement(Element parent){ boolean containsFocusedElement(Element parent){
Element focusedElement = Globals.elementService.getFocusedElement(); Element focusedElement = Globals.elementService.getFocusedElement();
@ -98,103 +114,113 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
int parentWidth, int parentWidth,
int parentHeight int parentHeight
) { ) {
//figure out if currently focused element is a child or subchild of this container if(this.elementBuffer != null){
if(containsFocusedElement(this)){ //figure out if currently focused element is a child or subchild of this container
//if it is, if it is offscreen, calculate offset to put it onscreen if(containsFocusedElement(this)){
Element focused = Globals.elementService.getFocusedElement(); //if it is, if it is offscreen, calculate offset to put it onscreen
if( Element focused = Globals.elementService.getFocusedElement();
focused.getRelativeX() + focused.getWidth() > this.width || if(
focused.getRelativeY() + focused.getHeight() > this.height || focused.getRelativeX() + focused.getWidth() > this.width ||
focused.getRelativeX() < 0 || focused.getRelativeY() + focused.getHeight() > this.height ||
focused.getRelativeY() < 0 focused.getRelativeX() < 0 ||
){ focused.getRelativeY() < 0
int neededOffsetX = 0; ){
int neededOffsetY = 0; int neededOffsetX = 0;
//basically if we're offscreen negative, pull to positive int neededOffsetY = 0;
//if we're offscreen positive and we're not as large as the screen, pull from the positive into focus //basically if we're offscreen negative, pull to positive
//if we are larger than the screen, set position to 0 //if we're offscreen positive and we're not as large as the screen, pull from the positive into focus
if(focused.getRelativeX() < 0){ //if we are larger than the screen, set position to 0
neededOffsetX = -focused.getRelativeX(); if(focused.getRelativeX() < 0){
} else if(focused.getRelativeX() + focused.getWidth() > this.width){
if(focused.getWidth() > this.width){
neededOffsetX = -focused.getRelativeX(); neededOffsetX = -focused.getRelativeX();
} else { } else if(focused.getRelativeX() + focused.getWidth() > this.width){
neededOffsetX = -((focused.getRelativeX() - this.width) + focused.getWidth()); if(focused.getWidth() > this.width){
neededOffsetX = -focused.getRelativeX();
} else {
neededOffsetX = -((focused.getRelativeX() - this.width) + focused.getWidth());
}
} }
} if(focused.getRelativeY() < 0){
if(focused.getRelativeY() < 0){
neededOffsetY = -focused.getRelativeY();
} else if(focused.getRelativeY() + focused.getHeight() > this.height){
if(focused.getHeight() > this.height){
neededOffsetY = -focused.getRelativeY(); neededOffsetY = -focused.getRelativeY();
} else { } else if(focused.getRelativeY() + focused.getHeight() > this.height){
neededOffsetY = -((focused.getRelativeY() - this.height) + focused.getHeight()); if(focused.getHeight() > this.height){
// System.out.println(focused.getPositionY() + " " + this.height + " " + focused.getHeight()); 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; float ndcWidth = (float)getWidth()/parentWidth;
int newY = child.getRelativeY() + neededOffsetY; float ndcHeight = (float)getHeight()/parentHeight;
child.setPositionX(newX); float ndcX = (float)(getInternalX() + parentPosX)/parentWidth;
child.setPositionY(newY); float ndcY = (float)(getInternalY() + parentPosY)/parentHeight;
// System.out.println(currentX + " " + currentY); 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; //render background of window
float ndcY = (float)internalPositionY/parentHeight; if(planeModel != null && windowFrame != null){
float ndcWidth = (float)internalWidth/parentWidth; planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
float ndcHeight = (float)internalHeight/parentHeight; planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);
boxPosition = new Vector3f(ndcX,ndcY,0); planeModel.pushUniformToMesh("plane", "tPosition", texPosition);
boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); planeModel.pushUniformToMesh("plane", "tDimension", texScale);
planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color);
//grab assets required to render window elementMat.setTexturePointer(windowFrame.getTexturePointer());
Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID); planeModel.getMeshes().get(0).setMaterial(elementMat);
Texture windowFrame = Globals.assetManager.fetchTexture("Textures/ui/uiFrame1.png"); planeModel.drawUI();
}
widgetBuffer.bind(openGLState); if(planeModel != null){
openGLState.glViewport(width, height); planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); planeModel.pushUniformToMesh("plane", "tPosition", texPosition);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); planeModel.pushUniformToMesh("plane", "tDimension", texScale);
planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color);
for(Element child : childList){ elementMat.setTexturePointer(elementBuffer.getTexture().getTexturePointer());
if(child instanceof DrawableElement){ planeModel.getMeshes().get(0).setMaterial(elementMat);
DrawableElement drawableChild = (DrawableElement) child; planeModel.drawUI();
drawableChild.draw(renderPipelineState,openGLState,widgetBuffer.getFramebufferPointer(),0,0,width,height); } 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());
} }
} }

View File

@ -46,26 +46,25 @@ public class Slider extends StandardDrawableElement implements ClickableElement,
static Material mat; static Material mat;
int drawMarginX = 5;
int drawMarginY = 5;
static final int idealMargin = 5; //5 pixels margin ideally static final int idealMargin = 5; //5 pixels margin ideally
static final Vector3f windowDrawDebugColor = new Vector3f(1.0f,1.0f,1.0f); 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_HEIGHT = 20;
static final int DEFAULT_WIDTH = 100;
/** /**
* Creates a slider element * The default height of a slider
* @param callback the Logic to fire when the slider changes value
* @return the slider element
*/ */
public static Slider createSlider(ValueChangeEventCallback callback){ static final int DEFAULT_WIDTH = 100;
Slider slider = new Slider();
slider.setOnValueChangeCallback(callback);
return slider;
}
/** /**
* Creates a slider element * Creates a slider element
@ -126,17 +125,17 @@ public class Slider extends StandardDrawableElement implements ClickableElement,
Globals.renderingEngine.bindFramebuffer(parentFramebufferPointer); Globals.renderingEngine.bindFramebuffer(parentFramebufferPointer);
openGLState.glViewport(parentWidth, parentHeight); openGLState.glViewport(parentWidth, parentHeight);
int marginX = Math.max(width - idealMargin * 2, 0); int drawMarginX = Math.max(width - idealMargin * 2, 0);
if(marginX < idealMargin){ if(drawMarginX < idealMargin){
marginX = 0; drawMarginX = 0;
} else { } else {
marginX = idealMargin; drawMarginX = idealMargin;
} }
int marginY = Math.max(height - idealMargin * 2, 0); int drawMarginY = Math.max(height - idealMargin * 2, 0);
if(marginY < idealMargin){ if(drawMarginY < idealMargin){
marginY = 0; drawMarginY = 0;
} else { } else {
marginY = idealMargin; drawMarginY = idealMargin;
} }
@ -158,10 +157,10 @@ public class Slider extends StandardDrawableElement implements ClickableElement,
planeModel.drawUI(); planeModel.drawUI();
//actual slider //actual slider
ndcX = (float)(getInternalX() + marginX + parentPosX)/parentWidth; ndcWidth = (float)((getInternalWidth() - drawMarginX * 2) * getValueAsPercentage())/parentWidth;
ndcY = (float)(getInternalY() + marginY + parentPosY)/parentHeight; ndcHeight = (float)(getInternalHeight() - drawMarginY * 2)/parentHeight;
ndcWidth = (float)((getInternalWidth() - marginX * 2) * getValueAsPercentage())/parentWidth; ndcX = (float)(getInternalX() + drawMarginX + parentPosX)/parentWidth;
ndcHeight = (float)(getInternalHeight() - marginY * 2)/parentHeight; ndcY = (float)(getInternalY() + drawMarginY + parentPosY)/parentHeight;
boxPosition = new Vector3f(ndcX,ndcY,0); boxPosition = new Vector3f(ndcX,ndcY,0);
boxDimensions = new Vector3f(ndcWidth,ndcHeight,0); boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
planeModel.pushUniformToMesh("plane", "mPosition", boxPosition); 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()); LoggerInterface.loggerRenderer.ERROR("Window unable to find plane model!!", new Exception());
} }
if(Globals.RENDER_FLAG_RENDER_UI_BOUNDS && DebugRendering.RENDER_DEBUG_OUTLINE_SLIDER){ 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); DebugRendering.drawUIBounds(parentFramebufferPointer, boxPosition, boxDimensions, windowDrawDebugColor);
} }
} }
@ -248,6 +247,15 @@ public class Slider extends StandardDrawableElement implements ClickableElement,
onValueChange = callback; 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 @Override
public boolean handleEvent(Event event) { public boolean handleEvent(Event event) {
@ -280,6 +288,7 @@ public class Slider extends StandardDrawableElement implements ClickableElement,
switch(menuEvent.getType()){ switch(menuEvent.getType()){
case INCREMENT: case INCREMENT:
value = Math.min(value + ((max - min) * 0.01f),max); value = Math.min(value + ((max - min) * 0.01f),max);
value = this.valueFromPercentage(value);
if(onValueChange != null){ if(onValueChange != null){
onValueChange.execute(new ValueChangeEvent(value)); onValueChange.execute(new ValueChangeEvent(value));
} }
@ -287,6 +296,7 @@ public class Slider extends StandardDrawableElement implements ClickableElement,
break; break;
case DECREMENT: case DECREMENT:
value = Math.max(value - ((max - min) * 0.01f),min); value = Math.max(value - ((max - min) * 0.01f),min);
value = this.valueFromPercentage(value);
if(onValueChange != null){ if(onValueChange != null){
onValueChange.execute(new ValueChangeEvent(value)); onValueChange.execute(new ValueChangeEvent(value));
} }
@ -308,9 +318,10 @@ public class Slider extends StandardDrawableElement implements ClickableElement,
propagate = onDrag.execute(dragEvent); propagate = onDrag.execute(dragEvent);
} else { } else {
//default behavior //default behavior
int percentage = dragEvent.getCurrentX() - getInternalX(); int percentage = dragEvent.getRelativeX() - getInternalX();
int max = width; int max = getInternalWidth();
value = Math.max(Math.min((float)percentage/max,1.0f),0.0f); value = Math.max(Math.min((float)percentage/max,1.0f),0.0f);
value = this.valueFromPercentage(value);
if(onValueChange != null){ if(onValueChange != null){
onValueChange.execute(new ValueChangeEvent(value)); onValueChange.execute(new ValueChangeEvent(value));
} }
@ -321,9 +332,10 @@ public class Slider extends StandardDrawableElement implements ClickableElement,
propagate = onDragRelease.execute(dragEvent); propagate = onDragRelease.execute(dragEvent);
} else { } else {
//default behavior //default behavior
int percentage = dragEvent.getCurrentX() - getInternalX(); int percentage = dragEvent.getRelativeX() - getInternalX();
int max = width; int max = getInternalWidth();
value = Math.max(Math.min((float)percentage/max,1.0f),0.0f); value = Math.max(Math.min((float)percentage/max,1.0f),0.0f);
value = this.valueFromPercentage(value);
if(onValueChange != null){ if(onValueChange != null){
onValueChange.execute(new ValueChangeEvent(value)); onValueChange.execute(new ValueChangeEvent(value));
} }
@ -338,8 +350,9 @@ public class Slider extends StandardDrawableElement implements ClickableElement,
} else { } else {
//default behavior //default behavior
int percentage = clickEvent.getRelativeX() - getInternalX(); int percentage = clickEvent.getRelativeX() - getInternalX();
int max = width; int max = getInternalWidth();
value = Math.max(Math.min((float)percentage/max,1.0f),0.0f); value = Math.max(Math.min((float)percentage/max,1.0f),0.0f);
value = this.valueFromPercentage(value);
if(onValueChange != null){ if(onValueChange != null){
onValueChange.execute(new ValueChangeEvent(value)); 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 @Override
public void setPositionX(int posX) { public void setPositionX(int posX) {
internalPositionX = posX; internalPositionX = posX;

View File

@ -2,6 +2,7 @@ package electrosphere.renderer.ui.elements;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.lwjgl.util.yoga.Yoga; import org.lwjgl.util.yoga.Yoga;
@ -50,6 +51,27 @@ public class StringCarousel extends StandardContainerElement implements Drawable
Yoga.YGNodeStyleSetMinWidth(this.yogaNode, 1); 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){ public void addOption(String option){
options.add(option); options.add(option);
if(currentOption == -1){ if(currentOption == -1){

View File

@ -97,6 +97,21 @@ public class Window implements DrawableElement, ContainerElement, NavigableEleme
this.setHeight(height); 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 @Override
public void draw( public void draw(
RenderPipelineState renderPipelineState, RenderPipelineState renderPipelineState,

View File

@ -14,6 +14,9 @@ public class DragEvent implements Event {
int currentY; int currentY;
int previousX; int previousX;
int previousY; int previousY;
//relative positions
int relativeX;
int relativeY;
int deltaX; int deltaX;
int deltaY; int deltaY;
DragEventType type; DragEventType type;
@ -66,4 +69,20 @@ public class DragEvent implements Event {
this.target = target; 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; package electrosphere.renderer.ui.elements;
import electrosphere.test.annotations.IntegrationTest; import electrosphere.test.annotations.IntegrationTest;
import electrosphere.engine.Globals;
import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils; 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.template.UITestTemplate;
import electrosphere.test.testutils.TestEngineUtils; import electrosphere.test.testutils.TestEngineUtils;
@ -19,15 +21,17 @@ public class WindowTest extends UITestTemplate {
public void testCreateWindow(){ public void testCreateWindow(){
//create ui testing window //create ui testing window
TestEngineUtils.simulateFrames(1); 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 //wait for ui updates
TestEngineUtils.flush(); TestEngineUtils.flush();
TestEngineUtils.simulateFrames(1); TestEngineUtils.simulateFrames(1);
// TestRenderingUtils.saveTestRender("./test/java/renderer/ui/elements/window.png"); this.checkRender("Basic", "./test/java/renderer/ui/elements/window1.png");
this.checkRender("Basic", "./test/java/renderer/ui/elements/ui-test.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