slider fixes, dragevent fixes, new component
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
This commit is contained in:
parent
963e8b6585
commit
63feb835cd
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,23 +20,26 @@ import electrosphere.renderer.RenderPipelineState;
|
||||
import electrosphere.renderer.RenderingEngine;
|
||||
import electrosphere.renderer.actor.Actor;
|
||||
import electrosphere.renderer.debug.DebugRendering;
|
||||
import electrosphere.renderer.framebuffer.Framebuffer;
|
||||
import electrosphere.renderer.framebuffer.FramebufferUtils;
|
||||
import electrosphere.renderer.model.Material;
|
||||
import electrosphere.renderer.model.Model;
|
||||
import electrosphere.renderer.ui.elementtypes.DraggableElement;
|
||||
import electrosphere.renderer.ui.elementtypes.DrawableElement;
|
||||
import electrosphere.renderer.ui.events.DragEvent;
|
||||
import electrosphere.renderer.ui.events.DragEvent.DragEventType;
|
||||
import electrosphere.renderer.ui.events.Event;
|
||||
|
||||
public class ActorPanel extends StandardElement implements DrawableElement, DraggableElement {
|
||||
public class ActorPanel extends BufferedStandardDrawableContainerElement implements DraggableElement {
|
||||
|
||||
/**
|
||||
* The default width of an actor panel
|
||||
*/
|
||||
public static final int DEFAULT_WIDTH = 500;
|
||||
|
||||
/**
|
||||
* The default height of an actor panel
|
||||
*/
|
||||
public static final int DEFAULT_HEIGHT = 500;
|
||||
|
||||
static Vector3f color = new Vector3f(1.0f);
|
||||
|
||||
Material customMat = new Material();
|
||||
Framebuffer elementBuffer;
|
||||
|
||||
Actor actor;
|
||||
Matrix4d modelMatrix = new Matrix4d();
|
||||
String currentAnim;
|
||||
@ -56,14 +59,16 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag
|
||||
|
||||
static final Vector3f windowDrawDebugColor = new Vector3f(0.0f,0.0f,1.0f);
|
||||
|
||||
@Deprecated
|
||||
public ActorPanel(OpenGLState openGLState, int x, int y, int width, int height, Actor actor){
|
||||
super();
|
||||
try {
|
||||
elementBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height);
|
||||
} catch(Exception e){
|
||||
LoggerInterface.loggerRenderer.ERROR(e);
|
||||
}
|
||||
customMat.setTexturePointer(elementBuffer.getTexture().getTexturePointer());
|
||||
|
||||
//must manually set because framebuffer is allocated on set
|
||||
//these default to -1 until they are set with methods, but methods allocate on each set
|
||||
//to break chicken-and-egg problem, manually set here
|
||||
this.width = DEFAULT_WIDTH;
|
||||
this.height = DEFAULT_HEIGHT;
|
||||
|
||||
this.actor = actor;
|
||||
this.internalPositionX = x;
|
||||
this.internalPositionY = y;
|
||||
@ -72,6 +77,36 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag
|
||||
this.aspectRatio = (float)width / (float)height;
|
||||
recalculateModelMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param actor The actor
|
||||
*/
|
||||
private ActorPanel(Actor actor){
|
||||
super();
|
||||
|
||||
//must manually set because framebuffer is allocated on set
|
||||
//these default to -1 until they are set with methods, but methods allocate on each set
|
||||
//to break chicken-and-egg problem, manually set here
|
||||
this.width = DEFAULT_WIDTH;
|
||||
this.height = DEFAULT_HEIGHT;
|
||||
|
||||
this.actor = actor;
|
||||
this.setWidth(DEFAULT_WIDTH);
|
||||
this.setHeight(DEFAULT_HEIGHT);
|
||||
this.aspectRatio = (float)DEFAULT_WIDTH / (float)DEFAULT_HEIGHT;
|
||||
this.recalculateModelMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an actor panel
|
||||
* @param actor The actor to put in the panel
|
||||
* @return The actor panel
|
||||
*/
|
||||
public static ActorPanel create(Actor actor){
|
||||
ActorPanel rVal = new ActorPanel(actor);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -86,122 +121,113 @@ public class ActorPanel extends StandardElement implements DrawableElement, Drag
|
||||
int parentHeight
|
||||
) {
|
||||
|
||||
elementBuffer.bind(openGLState);
|
||||
// Globals.renderingEngine.setViewportSize(width, height);
|
||||
if(this.elementBuffer != null){
|
||||
elementBuffer.bind(openGLState);
|
||||
// Globals.renderingEngine.setViewportSize(width, height);
|
||||
|
||||
RenderingEngine.setFOV(FOV);
|
||||
RenderingEngine.setAspectRatio(aspectRatio);
|
||||
|
||||
openGLState.glDepthTest(true);
|
||||
openGLState.glDepthFunc(GL_LESS);
|
||||
glDepthMask(true);
|
||||
openGLState.glViewport(width, height);
|
||||
RenderingEngine.setFOV(FOV);
|
||||
RenderingEngine.setAspectRatio(aspectRatio);
|
||||
|
||||
openGLState.glDepthTest(true);
|
||||
openGLState.glDepthFunc(GL_LESS);
|
||||
glDepthMask(true);
|
||||
openGLState.glViewport(width, height);
|
||||
|
||||
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
Model actorModel = Globals.assetManager.fetchModel(actor.getModelPath());
|
||||
Model actorModel = Globals.assetManager.fetchModel(actor.getModelPath());
|
||||
|
||||
if(currentAnim != null){
|
||||
if((!actor.isPlayingAnimation() || !actor.isPlayingAnimation(currentAnim)) &&
|
||||
actorModel != null &&
|
||||
actorModel.getAnimation(currentAnim) != null
|
||||
){
|
||||
actor.playAnimation(currentAnim,3);
|
||||
actor.incrementAnimationTime(0.0001);
|
||||
if(currentAnim != null){
|
||||
if((!actor.isPlayingAnimation() || !actor.isPlayingAnimation(currentAnim)) &&
|
||||
actorModel != null &&
|
||||
actorModel.getAnimation(currentAnim) != null
|
||||
){
|
||||
actor.playAnimation(currentAnim,3);
|
||||
actor.incrementAnimationTime(0.0001);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Set rendering engine state
|
||||
//
|
||||
renderPipelineState.setUseMeshShader(true);
|
||||
renderPipelineState.setBufferStandardUniforms(true);
|
||||
renderPipelineState.setBufferNonStandardUniforms(true);
|
||||
renderPipelineState.setUseMaterial(true);
|
||||
renderPipelineState.setUseShadowMap(true);
|
||||
renderPipelineState.setUseBones(true);
|
||||
renderPipelineState.setUseLight(true);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
actor.applySpatialData(modelMatrix,new Vector3d(actorPosition));
|
||||
actor.draw(renderPipelineState,openGLState);
|
||||
|
||||
RenderingEngine.setFOV(Globals.verticalFOV);
|
||||
RenderingEngine.setAspectRatio(Globals.aspectRatio);
|
||||
|
||||
openGLState.glDepthTest(false);
|
||||
|
||||
//this call binds the screen as the "texture" we're rendering to
|
||||
//have to call before actually rendering
|
||||
openGLState.glBindFramebuffer(GL_FRAMEBUFFER, parentFramebufferPointer);
|
||||
|
||||
//set viewport
|
||||
openGLState.glViewport(parentWidth, parentHeight);
|
||||
|
||||
float ndcX = (float)getInternalX()/parentWidth;
|
||||
float ndcY = (float)getInternalY()/parentHeight;
|
||||
float ndcWidth = (float)getInternalWidth()/parentWidth;
|
||||
float ndcHeight = (float)getInternalHeight()/parentHeight;
|
||||
|
||||
Vector3f boxPosition = new Vector3f(ndcX,ndcY,0);
|
||||
Vector3f boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Set rendering engine state
|
||||
//
|
||||
renderPipelineState.setUseMeshShader(true);
|
||||
renderPipelineState.setBufferStandardUniforms(false);
|
||||
renderPipelineState.setBufferNonStandardUniforms(true);
|
||||
renderPipelineState.setUseMaterial(true);
|
||||
renderPipelineState.setUseShadowMap(false);
|
||||
renderPipelineState.setUseBones(false);
|
||||
renderPipelineState.setUseLight(false);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID);
|
||||
if(planeModel != null){
|
||||
planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
|
||||
planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);
|
||||
planeModel.pushUniformToMesh("plane", "tPosition", texPosition);
|
||||
planeModel.pushUniformToMesh("plane", "tDimension", texScale);
|
||||
planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color);
|
||||
planeModel.getMeshes().get(0).setMaterial(elementMat);
|
||||
planeModel.draw(renderPipelineState,Globals.renderingEngine.getOpenGLState());
|
||||
} else {
|
||||
LoggerInterface.loggerRenderer.ERROR("Actor Panel unable to find plane model!!", new Exception());
|
||||
}
|
||||
|
||||
if(Globals.RENDER_FLAG_RENDER_UI_BOUNDS && DebugRendering.RENDER_DEBUG_OUTLINE_ACTOR_PANEL){
|
||||
DebugRendering.drawUIBounds(parentFramebufferPointer, boxPosition, boxDimensions, windowDrawDebugColor);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Set rendering engine state
|
||||
//
|
||||
renderPipelineState.setUseMeshShader(true);
|
||||
renderPipelineState.setBufferStandardUniforms(true);
|
||||
renderPipelineState.setBufferNonStandardUniforms(true);
|
||||
renderPipelineState.setUseMaterial(true);
|
||||
renderPipelineState.setUseShadowMap(true);
|
||||
renderPipelineState.setUseBones(true);
|
||||
renderPipelineState.setUseLight(true);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
actor.applySpatialData(modelMatrix,new Vector3d(actorPosition));
|
||||
actor.draw(renderPipelineState,openGLState);
|
||||
|
||||
RenderingEngine.setFOV(Globals.verticalFOV);
|
||||
RenderingEngine.setAspectRatio(Globals.aspectRatio);
|
||||
|
||||
openGLState.glDepthTest(false);
|
||||
|
||||
//this call binds the screen as the "texture" we're rendering to
|
||||
//have to call before actually rendering
|
||||
openGLState.glBindFramebuffer(GL_FRAMEBUFFER, parentFramebufferPointer);
|
||||
|
||||
//set viewport
|
||||
openGLState.glViewport(parentWidth, parentHeight);
|
||||
|
||||
float ndcX = (float)getInternalX()/parentWidth;
|
||||
float ndcY = (float)getInternalY()/parentHeight;
|
||||
float ndcWidth = (float)getInternalWidth()/parentWidth;
|
||||
float ndcHeight = (float)getInternalHeight()/parentHeight;
|
||||
|
||||
Vector3f boxPosition = new Vector3f(ndcX,ndcY,0);
|
||||
Vector3f boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Set rendering engine state
|
||||
//
|
||||
renderPipelineState.setUseMeshShader(true);
|
||||
renderPipelineState.setBufferStandardUniforms(false);
|
||||
renderPipelineState.setBufferNonStandardUniforms(true);
|
||||
renderPipelineState.setUseMaterial(true);
|
||||
renderPipelineState.setUseShadowMap(false);
|
||||
renderPipelineState.setUseBones(false);
|
||||
renderPipelineState.setUseLight(false);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID);
|
||||
if(planeModel != null){
|
||||
planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
|
||||
planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);
|
||||
planeModel.pushUniformToMesh("plane", "tPosition", texPosition);
|
||||
planeModel.pushUniformToMesh("plane", "tDimension", texScale);
|
||||
planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color);
|
||||
planeModel.getMeshes().get(0).setMaterial(customMat);
|
||||
planeModel.draw(renderPipelineState,Globals.renderingEngine.getOpenGLState());
|
||||
} else {
|
||||
LoggerInterface.loggerRenderer.ERROR("Actor Panel unable to find plane model!!", new Exception());
|
||||
}
|
||||
|
||||
if(Globals.RENDER_FLAG_RENDER_UI_BOUNDS && DebugRendering.RENDER_DEBUG_OUTLINE_ACTOR_PANEL){
|
||||
DebugRendering.drawUIBounds(parentFramebufferPointer, boxPosition, boxDimensions, windowDrawDebugColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean visible = false;
|
||||
|
||||
|
||||
public void setVisible(boolean draw) {
|
||||
this.visible = draw;
|
||||
}
|
||||
|
||||
public boolean getVisible(){
|
||||
return this.visible;
|
||||
}
|
||||
|
||||
public void setAnimation(String animation){
|
||||
currentAnim = animation;
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -6,9 +6,6 @@ import electrosphere.engine.Globals;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.renderer.OpenGLState;
|
||||
import electrosphere.renderer.RenderPipelineState;
|
||||
import electrosphere.renderer.framebuffer.Framebuffer;
|
||||
import electrosphere.renderer.framebuffer.FramebufferUtils;
|
||||
import electrosphere.renderer.model.Material;
|
||||
import electrosphere.renderer.model.Model;
|
||||
import electrosphere.renderer.texture.Texture;
|
||||
import electrosphere.renderer.ui.elementtypes.ContainerElement;
|
||||
@ -19,32 +16,38 @@ import electrosphere.renderer.ui.events.Event;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL30.*;
|
||||
|
||||
public class ScrollableContainer extends StandardContainerElement implements DrawableElement {
|
||||
public class ScrollableContainer extends BufferedStandardDrawableContainerElement {
|
||||
|
||||
/**
|
||||
* The default width of a slider
|
||||
*/
|
||||
static final int DEFAULT_HEIGHT = 50;
|
||||
|
||||
/**
|
||||
* The default height of a slider
|
||||
*/
|
||||
static final int DEFAULT_WIDTH = 50;
|
||||
|
||||
/**
|
||||
* The color associated with the scrollable
|
||||
*/
|
||||
Vector3f color = new Vector3f(1.0f);
|
||||
|
||||
boolean focused = false;
|
||||
|
||||
public boolean visible = false;
|
||||
|
||||
Framebuffer widgetBuffer;
|
||||
Material customMat = new Material();
|
||||
Vector3f boxPosition = new Vector3f();
|
||||
Vector3f boxDimensions = new Vector3f();
|
||||
Vector3f texPosition = new Vector3f(0,0,0);
|
||||
Vector3f texScale = new Vector3f(1,1,0);
|
||||
|
||||
|
||||
@Deprecated
|
||||
public ScrollableContainer(OpenGLState openGLState, int positionX, int positionY, int width, int height){
|
||||
super();
|
||||
try {
|
||||
widgetBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height);
|
||||
} catch(Exception e){
|
||||
LoggerInterface.loggerRenderer.ERROR(e);
|
||||
}
|
||||
// widgetBuffer = FramebufferUtils.generateScreensizeTextureFramebuffer();
|
||||
customMat.setTexturePointer(widgetBuffer.getTexture().getTexturePointer());
|
||||
// customMat.setTexturePointer(Globals.assetManager.fetchTexture("Textures/Testing1.png").getTexturePointer());
|
||||
|
||||
//must manually set because framebuffer is allocated on set
|
||||
//these default to -1 until they are set with methods, but methods allocate on each set
|
||||
//to break chicken-and-egg problem, manually set here
|
||||
this.width = DEFAULT_WIDTH;
|
||||
this.height = DEFAULT_HEIGHT;
|
||||
|
||||
float ndcX = (float)positionX/Globals.WINDOW_WIDTH;
|
||||
float ndcY = (float)positionY/Globals.WINDOW_HEIGHT;
|
||||
float ndcWidth = (float)width/Globals.WINDOW_WIDTH;
|
||||
@ -55,6 +58,29 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
|
||||
boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private ScrollableContainer(){
|
||||
super();
|
||||
|
||||
//must manually set because framebuffer is allocated on set
|
||||
//these default to -1 until they are set with methods, but methods allocate on each set
|
||||
//to break chicken-and-egg problem, manually set here
|
||||
this.width = DEFAULT_WIDTH;
|
||||
this.height = DEFAULT_HEIGHT;
|
||||
|
||||
setWidth(DEFAULT_WIDTH);
|
||||
setWidth(DEFAULT_HEIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scrollable container element
|
||||
* @return The scrollable
|
||||
*/
|
||||
public static ScrollableContainer createScrollable(){
|
||||
return new ScrollableContainer();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@ -62,16 +88,6 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean visible) {
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
//recursively check if focused element is child of input element or is input element
|
||||
boolean containsFocusedElement(Element parent){
|
||||
Element focusedElement = Globals.elementService.getFocusedElement();
|
||||
@ -98,103 +114,113 @@ public class ScrollableContainer extends StandardContainerElement implements Dra
|
||||
int parentWidth,
|
||||
int parentHeight
|
||||
) {
|
||||
//figure out if currently focused element is a child or subchild of this container
|
||||
if(containsFocusedElement(this)){
|
||||
//if it is, if it is offscreen, calculate offset to put it onscreen
|
||||
Element focused = Globals.elementService.getFocusedElement();
|
||||
if(
|
||||
focused.getRelativeX() + focused.getWidth() > this.width ||
|
||||
focused.getRelativeY() + focused.getHeight() > this.height ||
|
||||
focused.getRelativeX() < 0 ||
|
||||
focused.getRelativeY() < 0
|
||||
){
|
||||
int neededOffsetX = 0;
|
||||
int neededOffsetY = 0;
|
||||
//basically if we're offscreen negative, pull to positive
|
||||
//if we're offscreen positive and we're not as large as the screen, pull from the positive into focus
|
||||
//if we are larger than the screen, set position to 0
|
||||
if(focused.getRelativeX() < 0){
|
||||
neededOffsetX = -focused.getRelativeX();
|
||||
} else if(focused.getRelativeX() + focused.getWidth() > this.width){
|
||||
if(focused.getWidth() > this.width){
|
||||
if(this.elementBuffer != null){
|
||||
//figure out if currently focused element is a child or subchild of this container
|
||||
if(containsFocusedElement(this)){
|
||||
//if it is, if it is offscreen, calculate offset to put it onscreen
|
||||
Element focused = Globals.elementService.getFocusedElement();
|
||||
if(
|
||||
focused.getRelativeX() + focused.getWidth() > this.width ||
|
||||
focused.getRelativeY() + focused.getHeight() > this.height ||
|
||||
focused.getRelativeX() < 0 ||
|
||||
focused.getRelativeY() < 0
|
||||
){
|
||||
int neededOffsetX = 0;
|
||||
int neededOffsetY = 0;
|
||||
//basically if we're offscreen negative, pull to positive
|
||||
//if we're offscreen positive and we're not as large as the screen, pull from the positive into focus
|
||||
//if we are larger than the screen, set position to 0
|
||||
if(focused.getRelativeX() < 0){
|
||||
neededOffsetX = -focused.getRelativeX();
|
||||
} else {
|
||||
neededOffsetX = -((focused.getRelativeX() - this.width) + focused.getWidth());
|
||||
} else if(focused.getRelativeX() + focused.getWidth() > this.width){
|
||||
if(focused.getWidth() > this.width){
|
||||
neededOffsetX = -focused.getRelativeX();
|
||||
} else {
|
||||
neededOffsetX = -((focused.getRelativeX() - this.width) + focused.getWidth());
|
||||
}
|
||||
}
|
||||
}
|
||||
if(focused.getRelativeY() < 0){
|
||||
neededOffsetY = -focused.getRelativeY();
|
||||
} else if(focused.getRelativeY() + focused.getHeight() > this.height){
|
||||
if(focused.getHeight() > this.height){
|
||||
if(focused.getRelativeY() < 0){
|
||||
neededOffsetY = -focused.getRelativeY();
|
||||
} else {
|
||||
neededOffsetY = -((focused.getRelativeY() - this.height) + focused.getHeight());
|
||||
// System.out.println(focused.getPositionY() + " " + this.height + " " + focused.getHeight());
|
||||
} else if(focused.getRelativeY() + focused.getHeight() > this.height){
|
||||
if(focused.getHeight() > this.height){
|
||||
neededOffsetY = -focused.getRelativeY();
|
||||
} else {
|
||||
neededOffsetY = -((focused.getRelativeY() - this.height) + focused.getHeight());
|
||||
// System.out.println(focused.getPositionY() + " " + this.height + " " + focused.getHeight());
|
||||
}
|
||||
}
|
||||
//apply offset to all children
|
||||
for(Element child : childList){
|
||||
int newX = child.getRelativeX() + neededOffsetX;
|
||||
int newY = child.getRelativeY() + neededOffsetY;
|
||||
child.setPositionX(newX);
|
||||
child.setPositionY(newY);
|
||||
// System.out.println(currentX + " " + currentY);
|
||||
}
|
||||
}
|
||||
//apply offset to all children
|
||||
for(Element child : childList){
|
||||
int newX = child.getRelativeX() + neededOffsetX;
|
||||
int newY = child.getRelativeY() + neededOffsetY;
|
||||
child.setPositionX(newX);
|
||||
child.setPositionY(newY);
|
||||
// System.out.println(currentX + " " + currentY);
|
||||
}
|
||||
|
||||
float ndcWidth = (float)getWidth()/parentWidth;
|
||||
float ndcHeight = (float)getHeight()/parentHeight;
|
||||
float ndcX = (float)(getInternalX() + parentPosX)/parentWidth;
|
||||
float ndcY = (float)(getInternalY() + parentPosY)/parentHeight;
|
||||
boxPosition = new Vector3f(ndcX,ndcY,0);
|
||||
boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
|
||||
|
||||
//grab assets required to render window
|
||||
Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID);
|
||||
Texture windowFrame = Globals.assetManager.fetchTexture("Textures/ui/uiFrame1.png");
|
||||
|
||||
|
||||
elementBuffer.bind(openGLState);
|
||||
openGLState.glViewport(width, height);
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
for(Element child : childList){
|
||||
if(child instanceof DrawableElement){
|
||||
DrawableElement drawableChild = (DrawableElement) child;
|
||||
drawableChild.draw(
|
||||
renderPipelineState,
|
||||
openGLState,
|
||||
parentFramebufferPointer,
|
||||
parentPosX + this.internalPositionX,
|
||||
parentPosY + this.internalPositionY,
|
||||
parentWidth,
|
||||
parentHeight
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
//this call binds the screen as the "texture" we're rendering to
|
||||
//have to call before actually rendering
|
||||
openGLState.glBindFramebuffer(GL_FRAMEBUFFER, parentFramebufferPointer);
|
||||
openGLState.glViewport(parentWidth, parentHeight);
|
||||
|
||||
float ndcX = (float)internalPositionX/parentWidth;
|
||||
float ndcY = (float)internalPositionY/parentHeight;
|
||||
float ndcWidth = (float)internalWidth/parentWidth;
|
||||
float ndcHeight = (float)internalHeight/parentHeight;
|
||||
boxPosition = new Vector3f(ndcX,ndcY,0);
|
||||
boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
|
||||
|
||||
//grab assets required to render window
|
||||
Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID);
|
||||
Texture windowFrame = Globals.assetManager.fetchTexture("Textures/ui/uiFrame1.png");
|
||||
|
||||
|
||||
widgetBuffer.bind(openGLState);
|
||||
openGLState.glViewport(width, height);
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
for(Element child : childList){
|
||||
if(child instanceof DrawableElement){
|
||||
DrawableElement drawableChild = (DrawableElement) child;
|
||||
drawableChild.draw(renderPipelineState,openGLState,widgetBuffer.getFramebufferPointer(),0,0,width,height);
|
||||
//render background of window
|
||||
if(planeModel != null && windowFrame != null){
|
||||
planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
|
||||
planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);
|
||||
planeModel.pushUniformToMesh("plane", "tPosition", texPosition);
|
||||
planeModel.pushUniformToMesh("plane", "tDimension", texScale);
|
||||
planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color);
|
||||
elementMat.setTexturePointer(windowFrame.getTexturePointer());
|
||||
planeModel.getMeshes().get(0).setMaterial(elementMat);
|
||||
planeModel.drawUI();
|
||||
}
|
||||
|
||||
if(planeModel != null){
|
||||
planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
|
||||
planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);
|
||||
planeModel.pushUniformToMesh("plane", "tPosition", texPosition);
|
||||
planeModel.pushUniformToMesh("plane", "tDimension", texScale);
|
||||
planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color);
|
||||
elementMat.setTexturePointer(elementBuffer.getTexture().getTexturePointer());
|
||||
planeModel.getMeshes().get(0).setMaterial(elementMat);
|
||||
planeModel.drawUI();
|
||||
} else {
|
||||
LoggerInterface.loggerRenderer.ERROR("ScrollableContainer unable to find plane model!!", new Exception());
|
||||
}
|
||||
}
|
||||
//this call binds the screen as the "texture" we're rendering to
|
||||
//have to call before actually rendering
|
||||
openGLState.glBindFramebuffer(GL_FRAMEBUFFER, parentFramebufferPointer);
|
||||
openGLState.glViewport(parentWidth, parentHeight);
|
||||
|
||||
//render background of window
|
||||
if(planeModel != null && windowFrame != null){
|
||||
planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
|
||||
planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);
|
||||
planeModel.pushUniformToMesh("plane", "tPosition", texPosition);
|
||||
planeModel.pushUniformToMesh("plane", "tDimension", texScale);
|
||||
planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color);
|
||||
customMat.setTexturePointer(windowFrame.getTexturePointer());
|
||||
planeModel.getMeshes().get(0).setMaterial(customMat);
|
||||
planeModel.drawUI();
|
||||
}
|
||||
|
||||
if(planeModel != null){
|
||||
planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
|
||||
planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);
|
||||
planeModel.pushUniformToMesh("plane", "tPosition", texPosition);
|
||||
planeModel.pushUniformToMesh("plane", "tDimension", texScale);
|
||||
planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color);
|
||||
customMat.setTexturePointer(widgetBuffer.getTexture().getTexturePointer());
|
||||
planeModel.getMeshes().get(0).setMaterial(customMat);
|
||||
planeModel.drawUI();
|
||||
} else {
|
||||
LoggerInterface.loggerRenderer.ERROR("ScrollableContainer unable to find plane model!!", new Exception());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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){
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
32
src/test/java/electrosphere/renderer/ui/MainMenuTests.java
Normal file
32
src/test/java/electrosphere/renderer/ui/MainMenuTests.java
Normal 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");
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package electrosphere.renderer.ui.elements;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests for a slider element
|
||||
*/
|
||||
public class SliderTests {
|
||||
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
}
|
||||
@ -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 |
BIN
test/java/renderer/ui/elements/window1.png
Normal file
BIN
test/java/renderer/ui/elements/window1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Loading…
Reference in New Issue
Block a user