Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvement: textures scale to size #551

Merged
merged 6 commits into from
Aug 25, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -259,23 +259,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,6 +89,20 @@ public EventResult onGenerateBody(GenerateBodyEvent event, EntityRef entity) {
fixtureDef.density = DENSITY;
fixtureDef.friction = Const.FRICTION;
collisionMeshLoader.attachFixture(body, element.texture.name, fixtureDef, size);

/*This calculates the offset of the renderable element from "the origin" (as defined in the JSON that the
IsaacLic marked this conversation as resolved.
Show resolved Hide resolved
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.)
This is different from the relativePosition, because this is a practical adjustment to align the mesh with the sprite.
*/
//TODO separate this from Body creation once CollisionMeshLoader is modular
Vector2 originInformation = collisionMeshLoader.getOrigin(element.texture.name, 1);
element.graphicsOffset = new Vector2(originInformation.x - .5f, originInformation.y - .5f);
}

entitySystemManager.sendEvent(new BodyCreatedEvent(body), entity);
Expand Down
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,49 @@ 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
IsaacLic marked this conversation as resolved.
Show resolved Hide resolved
* 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. 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 modified when the Size component is changed
IsaacLic marked this conversation as resolved.
Show resolved Hide resolved
/**
* 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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a stray thought than important here, but we had talked about this on Discord recently, with the move from an Invisible component to a boolean inside Renderable - one thing I forgot to mention as a nice benefit to having more components is being able to filter on them in the @ReceiveEvent which is a nice way to avoid if statements and keep the handlers simple.

However, in this case being invisible would be the less likely case, it would seem, and we can't test for "Renderable, Position, and Size, but not Invisible" in the annotation anyway 🤔 Hmm, wonder if that would be useful to have. That then might make one think about whether there should be a a Visible component instead but ... then that's essentially Renderable. ECS: A different bicycle than OOP, but both still fall over at times ;-)

Copy link
Contributor Author

@IsaacLic IsaacLic Aug 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely hear the benefits of booleans in a component and of having separate components. Maybe you can talk about it with @immortius :)


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