Skip to content

Commit

Permalink
improvement: textures scale to size (#551)
Browse files Browse the repository at this point in the history
* fix: textures scale to size

* fix: added EmptyComponent to whitelist

* improvement: graphics are more closely aligned with meshes

* docs: added documentation to the alignment code

* refactor: made Invisibility a field in Renderable component

* docs: cleaned up and added more documentation
  • Loading branch information
IsaacLic authored Aug 25, 2020
1 parent 82aefe4 commit f43d4b1
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 43 deletions.
13 changes: 7 additions & 6 deletions engine/src/main/java/org/destinationsol/SolApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -260,23 +260,24 @@ private void draw() {

//TODO remove this block - it is for debugging purposes
if (!entityCreated) {

Size size = new Size();
size.size = 2;

RenderableElement element = new RenderableElement();
element.texture = SolRandom.randomElement(Assets.listTexturesMatching("engine:asteroid_.*"));
element.relativePosition = new Vector2(0, 0);
element.relativePosition = new Vector2();
element.drawableLevel = DrawableLevel.BODIES;
element.width = 2;
element.height = 2;
element.tint = Color.YELLOW;
element.setSize(size.size);
element.graphicsOffset = new Vector2();
Renderable graphicsComponent = new Renderable();
graphicsComponent.elements.add(element);

Position position = new Position();
position.position = solGame.getHero().getShip().getPosition().cpy();
position.position.y += 3;

Size size = new Size();
size.size = 2;

Health health = new Health();
health.currentHealth = 1;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public class AsteroidBodyCreationSystem implements EventReceiver {
@ReceiveEvent(components = {AsteroidMesh.class, Size.class, Position.class, Angle.class, Renderable.class})
public EventResult onGenerateBody(GenerateBodyEvent event, EntityRef entity) {


float size = entity.getComponent(Size.class).get().size;
Vector2 position = entity.getComponent(Position.class).get().position;
float angle = entity.getComponent(Angle.class).get().getAngle();
Expand All @@ -90,9 +89,29 @@ public EventResult onGenerateBody(GenerateBodyEvent event, EntityRef entity) {
fixtureDef.density = DENSITY;
fixtureDef.friction = Const.FRICTION;
collisionMeshLoader.attachFixture(body, element.texture.name, fixtureDef, size);

calculateGraphicsOffset(element);
}

entitySystemManager.sendEvent(new BodyCreatedEvent(body), entity);
return EventResult.CONTINUE;
}

/**
* This calculates the offset of the renderable element from "the origin" (as defined in the JSON that the
* CollisionMeshLoader reads from).
* The origin is where the center of the object should be, which is relevant for physics handling. The
* CollisionMeshLoader creates Fixtures (collision meshes) using that information, so the sprites need to be
* adjusted to overlay the mesh properly.
* LibGDX draws sprites from the bottom left corner. Since the position information is from the center, it
* needs to be adjusted to be at the bottom left of the sprite. To do so, (.5, .5) is subtracted from the origin.
* (The coordinates are scaled to range from zero to one, so (.5, .5) represents the center.)
* The originInformation is the information that was read from the JSON, which is used to calculate the graphics
* offset information.
*/
//TODO separate this method into a separate system once CollisionMeshLoader is modular
private void calculateGraphicsOffset(RenderableElement element) {
Vector2 originInformation = collisionMeshLoader.getOrigin(element.texture.name, 1);
element.graphicsOffset = new Vector2(originInformation.x - .5f, originInformation.y - .5f);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ public class ModuleManager {
java.io.PipedOutputStream.class,
/* Gestalt classes */
org.terasology.gestalt.entitysystem.component.Component.class,
org.terasology.gestalt.entitysystem.component.EmptyComponent.class,
org.terasology.gestalt.entitysystem.event.Event.class,
org.terasology.gestalt.entitysystem.entity.EntityRef.class,
org.terasology.gestalt.entitysystem.entity.EntityIterator.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.math.Vector2;
import org.destinationsol.common.SolMath;
import org.destinationsol.game.drawables.DrawableLevel;
import org.destinationsol.size.components.Size;

/**
* Contains a {@link TextureAtlas.AtlasRegion} (an image), along with information about how it should be drawn.
Expand All @@ -29,10 +31,10 @@ public class RenderableElement {
public TextureAtlas.AtlasRegion texture;

/** The width of the texture when drawn. */
public float width;
private float width;

/** The height of the texture when drawn. */
public float height;
private float height;

/** Represents the depth at which this element renders, as well as its logical grouping. */
public DrawableLevel drawableLevel;
Expand All @@ -46,14 +48,52 @@ public class RenderableElement {
/** The tint that the texture should be given. */
public Color tint;

/**
* The amount that the sprite should be moved to line up accurately with the mesh. This should be scaled according
* to the {@link Size}. This is different from the relativePosition, because this is a practical adjustment to align
* the mesh with the sprite, as opposed to moving the sprite relative to the base entity. To draw the sprite
* accurately, it needs to be drawn from the bottom-left of the actual image, not the bottom left of the .png file,
* so this contains the information for calculating the actual start of the sprite.
* <p>
* Modification of this can create mesh misalignments, so only change this if you know what you're doing.
*/
public Vector2 graphicsOffset;


//TODO this should be automatically called when the Size component is changed, e.g. the entity shrinks or grows
/**
* Resizes the renderable element to the given size. The larger dimension of the texture is set to the size, and the
* smaller one is scaled down proportionally.
*/
public void setSize(float size) {
size /= drawableLevel.depth; // Scales the texture size for objects that are in the background
float dimensionsRatio = (float) texture.getRegionWidth() / texture.getRegionHeight();
if (dimensionsRatio > 1) {
width = size;
height = size / dimensionsRatio;
} else {
width = size / dimensionsRatio;
height = size;
}
}

public void copy(RenderableElement other) {
this.texture = new TextureAtlas.AtlasRegion(other.texture);
this.drawableLevel = other.drawableLevel;
this.relativePosition = other.relativePosition.cpy();
this.relativeAngle = other.relativeAngle;
this.width = other.width;
this.height = other.height;
this.width = other.getWidth();
this.height = other.getHeight();
this.tint = other.tint.cpy();
this.graphicsOffset = other.graphicsOffset.cpy();
}

public float getWidth() {
return width;
}

public float getHeight() {
return height;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public final class Renderable implements Component<Renderable> {

public ArrayList<RenderableElement> elements = new ArrayList<>();

public boolean isInvisible;

@Override
public void copy(Renderable other) {
ArrayList<RenderableElement> newElements = new ArrayList<>();
Expand All @@ -37,5 +39,6 @@ public void copy(Renderable other) {
}

this.elements = newElements;
this.isInvisible = other.isInvisible;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
import org.destinationsol.common.In;
import org.destinationsol.rendering.RenderableElement;
import org.destinationsol.rendering.components.Renderable;
import org.destinationsol.rendering.components.Invisible;
import org.destinationsol.rendering.events.RenderEvent;
import org.destinationsol.entitysystem.EntitySystemManager;
import org.destinationsol.entitysystem.EventReceiver;
import org.destinationsol.game.GameDrawer;
import org.destinationsol.location.components.Angle;
import org.destinationsol.location.components.Position;
import org.destinationsol.size.components.Size;
import org.terasology.gestalt.entitysystem.entity.EntityRef;
import org.terasology.gestalt.entitysystem.event.EventResult;
import org.terasology.gestalt.entitysystem.event.ReceiveEvent;
Expand All @@ -41,13 +41,14 @@ public class RenderingSystem implements EventReceiver {
@In
private GameDrawer drawer;

@ReceiveEvent(components = {Renderable.class, Position.class})
@ReceiveEvent(components = {Renderable.class, Position.class, Size.class})
public EventResult onRender(RenderEvent event, EntityRef entity) {

if (!entity.hasComponent(Invisible.class)) {
Renderable renderable = entity.getComponent(Renderable.class).get();
if (!renderable.isInvisible) {

Renderable renderable = entity.getComponent(Renderable.class).get();
Vector2 basePosition = entity.getComponent(Position.class).get().position;
float size = entity.getComponent(Size.class).get().size;

float baseAngle = 0;
if (entity.hasComponent(Position.class)) {
Expand All @@ -56,10 +57,20 @@ public EventResult onRender(RenderEvent event, EntityRef entity) {

for (RenderableElement renderableElement : renderable.elements) {
float angle = renderableElement.relativeAngle + baseAngle;
Vector2 renderableElementPosition = basePosition.add(renderableElement.relativePosition);

drawer.draw(renderableElement.texture, renderableElement.width,
renderableElement.height, renderableElement.width / 2, renderableElement.height / 2,
basePosition.x, basePosition.y, angle, renderableElement.tint);
/*
This calculates how much the position of the drawable should be shifted horizontally or vertically to
line up with the collision mesh.
*/
float horizontalShift = renderableElement.getWidth() / 2;
float verticalShift = renderableElement.getHeight() / 2;
horizontalShift += renderableElement.graphicsOffset.x * size;
verticalShift += renderableElement.graphicsOffset.y * size;

drawer.draw(renderableElement.texture, renderableElement.getWidth(), renderableElement.getHeight(),
horizontalShift, verticalShift, renderableElementPosition.x, renderableElementPosition.y, angle,
renderableElement.tint);
}
}

Expand Down

0 comments on commit f43d4b1

Please sign in to comment.