Skip to content

Commit

Permalink
Move textureUV adjustments for sprites to the shader
Browse files Browse the repository at this point in the history
  • Loading branch information
ultraq committed Jun 28, 2024
1 parent 6947ef5 commit 0862da9
Show file tree
Hide file tree
Showing 17 changed files with 152 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,14 @@ class Map extends Node<Map> {
},
tileSetSpriteSheetFuture.thenApplyAsync { spriteSheet ->
material.texture = spriteSheet.texture
material.with {
texture = spriteSheet.texture
framesHorizontal = spriteSheet.framesHorizontal
framesVertical = spriteSheet.framesVertical
frameStepX = spriteSheet.frameStepX
frameStepY = spriteSheet.frameStepY
frame = 0
}
return spriteSheet
}
)
Expand Down Expand Up @@ -469,7 +477,7 @@ class Map extends Node<Map> {

var overlay = new PalettedSprite(tileFile)
overlay.name = "${tile.name} - Variant ${imageVariant}"
overlay.initialFrame = imageVariant
overlay.frame = imageVariant
overlay.position = new Vector2f(tilePos).asWorldCoords(1)
addChild(overlay)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package nz.net.ultraq.redhorizon.classic.nodes

import nz.net.ultraq.redhorizon.classic.Faction
import nz.net.ultraq.redhorizon.classic.shaders.Shaders
import nz.net.ultraq.redhorizon.engine.graphics.GraphicsRenderer
import nz.net.ultraq.redhorizon.engine.graphics.GraphicsRequests.ShaderRequest
import nz.net.ultraq.redhorizon.engine.graphics.GraphicsRequests.SpriteMeshRequest
import nz.net.ultraq.redhorizon.engine.scenegraph.Scene
Expand Down Expand Up @@ -92,8 +91,19 @@ class PalettedSprite extends Sprite implements FactionColours {
CompletableFuture<Void> onSceneAddedAsync(Scene scene) {

return CompletableFuture.allOf(
scene
.requestCreateOrGet(new SpriteMeshRequest(bounds, region))
spriteSheetGenerator.generate(scene)
.thenComposeAsync { newSpriteSheet ->
spriteSheet = newSpriteSheet
material.with {
texture = spriteSheet.texture
framesHorizontal = spriteSheet.framesHorizontal
framesVertical = spriteSheet.framesVertical
frameStepX = spriteSheet.frameStepX
frameStepY = spriteSheet.frameStepY
frame = this.frame
}
return scene.requestCreateOrGet(new SpriteMeshRequest(bounds, spriteSheet.textureRegion.scale(repeatX, repeatY)))
}
.thenAcceptAsync { newMesh ->
mesh = newMesh
},
Expand All @@ -102,37 +112,12 @@ class PalettedSprite extends Sprite implements FactionColours {
.thenAcceptAsync { requestedShader ->
shader = requestedShader
},
spriteSheetGenerator.generate(scene)
.thenApplyAsync { newSpriteSheet ->
spriteSheet = newSpriteSheet
material.texture = spriteSheet.texture

// TODO: Some uses are a repeating tile, others aren't. There should be a unified way of doing this 🤔
if (repeatX != 1f || repeatY != 1f) {
region.setMax(repeatX, repeatY)
}
else {
region.set(spriteSheet[initialFrame])
}
},
CompletableFuture.runAsync { ->
material.adjustmentMap = buildAdjustmentMap(faction)
}
)
}

@Override
void render(GraphicsRenderer renderer) {

if (material.adjustmentMap) {
if (factionChanged) {
material.adjustmentMap = buildAdjustmentMap(faction)
factionChanged = false
}
super.render(renderer)
}
}

/**
* Update the faction being applied to this sprite.
*/
Expand All @@ -142,4 +127,14 @@ class PalettedSprite extends Sprite implements FactionColours {
FactionColours.super.setFaction(faction)
factionChanged = true
}

@Override
void update() {

if (material.adjustmentMap && factionChanged) {
material.adjustmentMap = buildAdjustmentMap(faction)
factionChanged = false
}
super.update()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ layout (std140) uniform Camera {
mat4 view;
};
uniform mat4 model;
uniform int framesHorizontal;
uniform int framesVertical;
uniform float frameStepX;
uniform float frameStepY;
uniform int frame;

/**
* Vertex shader main function, mostly passes geometry information along to the
Expand All @@ -21,5 +26,9 @@ void main() {

gl_Position = projection * view * model * position;
v_vertexColour = colour;
v_textureUVs = textureUVs;

// Adjust textureUVs to the location of the selected frame in the spritesheet
float textureU = (frame % framesHorizontal) * frameStepX;
float textureV = floor(frame / framesHorizontal) * frameStepY;
v_textureUVs = textureUVs + vec2(textureU, textureV);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package nz.net.ultraq.redhorizon.classic.shaders

import nz.net.ultraq.redhorizon.engine.graphics.Attribute
import nz.net.ultraq.redhorizon.engine.graphics.Material
import nz.net.ultraq.redhorizon.engine.graphics.ShaderConfig
import nz.net.ultraq.redhorizon.engine.graphics.Uniform

Expand All @@ -37,6 +38,11 @@ class PalettedSpriteShader extends ShaderConfig {
},
{ shader, material, window ->
shader.setUniform('adjustmentMap', material.adjustmentMap)
},
{ shader, material, window ->
Material.KEYS_SPRITES.each { key ->
shader.setUniformGeneric(key, material.attributes[key])
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class Unit extends Node<Unit> implements FactionColours, Rotatable, Temporal {
var closestHeading = Math.round(heading / degreesPerHeading)
var rotationFrame = closestHeading ? (headings - closestHeading) * frames as int : 0
var animationFrame = frames ? Math.floor((currentTimeMs - animationStartTime) / 1000 * FRAMERATE) % frames as int : 0
region.set(spriteSheet.getFrame(unitData.shpFile.getStateFramesOffset(currentState) + rotationFrame + animationFrame))
frame = unitData.shpFile.getStateFramesOffset(currentState) + rotationFrame + animationFrame
}

super.update()
Expand All @@ -233,7 +233,7 @@ class Unit extends Node<Unit> implements FactionColours, Rotatable, Temporal {
var turretHeadings = unitData.shpFile.parts.turret.headings
var closestTurretHeading = Math.round(heading / degreesPerHeading)
var turretRotationFrame = closestTurretHeading ? turretHeadings - closestTurretHeading as int : 0
region.set(spriteSheet.getFrame(unitData.shpFile.parts.body.headings + turretRotationFrame))
frame = unitData.shpFile.parts.body.headings + turretRotationFrame
}

super.update()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,7 @@ interface GraphicsRenderer extends AutoCloseable, EventTarget {
* Create a mesh to represent a surface onto which a texture will go. This is
* a convenience method for {@link #createMesh}.
*/
default Mesh createSpriteMesh(Rectanglef surface) {

return createSpriteMesh(surface, new Rectanglef(0, 0, 1, 1))
}
Mesh createSpriteMesh(Rectanglef surface)

/**
* Create a mesh to represent a surface onto which a texture will go. This is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ interface GraphicsRequests {
@ImmutableOptions(knownImmutables = ['surface', 'textureUVs'])
static record SpriteMeshRequest(Rectanglef surface, Rectanglef textureUVs) implements Request<Mesh> {
SpriteMeshRequest(Rectanglef surface) {
this(surface, null)
this(surface, new Rectanglef(0, 0, 1, 1))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,7 @@ class GraphicsSystem extends EngineSystem implements GraphicsRequests {
case ShaderRequest -> renderer.createShader(request.shaderConfig())
case MeshRequest -> renderer.createMesh(request.type(), request.layout(), request.vertices(), request.colour(),
request.textureUVs(), request.indices(), request.dynamic())
case SpriteMeshRequest -> {
if (request.textureUVs() != null) {
yield renderer.createSpriteMesh(request.surface(), request.textureUVs())
}
else {
yield renderer.createSpriteMesh(request.surface())
}
}
case SpriteMeshRequest -> renderer.createSpriteMesh(request.surface(), request.textureUVs())
case TextureRequest -> renderer.createTexture(request.width(), request.height(), request.format(), request.data())
case SpriteSheetRequest -> renderer.createSpriteSheet(request.width(), request.height(), request.format(), request.data())
case UniformBufferRequest -> renderer.createUniformBuffer(request.name(), request.data())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,36 @@ package nz.net.ultraq.redhorizon.engine.graphics
*/
class Material {

private static final String KEY_FRAME = 'frame'
private static final String KEY_FRAMES_HORIZONTAL = 'framesHorizontal'
private static final String KEY_FRAMES_VERTICAL = 'framesVertical'
private static final String KEY_FRAME_STEP_X = 'frameStepX'
private static final String KEY_FRAME_STEP_Y = 'frameStepY'

static final String[] KEYS_SPRITES = [
KEY_FRAME, KEY_FRAMES_HORIZONTAL, KEY_FRAMES_VERTICAL, KEY_FRAME_STEP_X, KEY_FRAME_STEP_Y
]

final Map<String, Object> attributes = [:]
Texture texture

void setFrame(int frame) {
attributes[KEY_FRAME] = frame
}

void setFrameStepX(float frameStepX) {
attributes[KEY_FRAME_STEP_X] = frameStepX
}

void setFrameStepY(float frameStepY) {
attributes[KEY_FRAME_STEP_Y] = frameStepY
}

void setFramesHorizontal(int framesHorizontal) {
attributes[KEY_FRAMES_HORIZONTAL] = framesHorizontal
}

void setFramesVertical(int framesVertical) {
attributes[KEY_FRAMES_VERTICAL] = framesVertical
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,38 +45,31 @@ abstract class Shader implements GraphicsResource {
uniforms*.apply(this, material, window)
}

/**
* Apply a data uniform to the shader. If {@code data} is an array, it can be
* used to determine the shader type (eg: 2 floats = vec2).
*/
abstract void setUniformGeneric(String name, Object data)

/**
* Apply a data uniform to the shader. The type of data is determined by the
* size of the data array.
*
* @param name
* @param data
*/
abstract void setUniform(String name, float[] data)

/**
* Apply a data uniform to the shader. The type of data is determined by the
* size of the data array.
*
* @param name
* @param data
*/
abstract void setUniform(String name, int[] data)

/**
* Apply a matrix uniform to the shader.
*
* @param name
* @param matrix
*/
abstract void setUniform(String name, Matrix4f matrix)

/**
* Apply a texture uniform using the given texture ID.
*
* @param name
* @param textureUnit
* @param textureId
*/
abstract void setUniformTexture(String name, int textureUnit, Texture texture)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class SpriteSheet implements GraphicsResource {
/**
* Return coordinates on the sprite sheet that would locate the sprite with
* the corresponding index from the raw data.
* <p>
* Note that with texture adjustments calculated in the shader, there
* shouldn't be a need for this any more.
*/
Rectanglef getFrame(int index) {

Expand All @@ -65,4 +68,12 @@ class SpriteSheet implements GraphicsResource {
textureV + frameStepY as float
)
}

/**
* Return a texture region for use with this spritesheet.
*/
Rectanglef getTextureRegion() {

return getFrame(0)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ class OpenGLRenderer implements GraphicsRenderer {
}

@Override
Mesh createSpriteMesh(Rectanglef surface, Rectanglef textureUVs) {
Mesh createSpriteMesh(Rectanglef surface, Rectanglef textureUVs = new Rectanglef(0, 0, 1, 1)) {

return createMesh(
MeshType.TRIANGLES,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,30 @@ class OpenGLShader extends Shader {

/**
* Cached function for looking up a uniform location in a shader program.
*
* @param name
* @return
*/
@Memoized
private int getUniformLocation(String name) {

return glGetUniformLocation(programId, name)
}

@Override
void setUniformGeneric(String name, Object data) {

if (data == null) {
throw new IllegalArgumentException("Data value for key ${name} was null")
}

switch (data) {
case float, Float -> setUniform(name, (float)data)
case float[], Float[] -> setUniform(name, (float[])data)
case int, Integer -> setUniform(name, (int)data)
case int[], Integer[] -> setUniform(name, (int[])data)
case Matrix4f -> setUniform(name, data)
default -> throw new UnsupportedOperationException("Data type of ${data.class.simpleName} not supported")
}
}

@Override
void setUniform(String name, float[] data) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ layout (std140) uniform Camera {
mat4 view;
};
uniform mat4 model;
uniform int framesHorizontal;
uniform int framesVertical;
uniform float frameStepX;
uniform float frameStepY;
uniform int frame;

/**
* Vertex shader main function, mostly passes geometry information along to the
Expand All @@ -21,5 +26,9 @@ void main() {

gl_Position = projection * view * model * position;
v_vertexColour = colour;
v_textureUVs = textureUVs;

// Adjust textureUVs to the location of the selected frame in the spritesheet
float textureU = (frame % framesHorizontal) * frameStepX;
float textureV = floor(frame / framesHorizontal) * frameStepY;
v_textureUVs = textureUVs + vec2(textureU, textureV);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package nz.net.ultraq.redhorizon.engine.graphics.opengl

import nz.net.ultraq.redhorizon.engine.graphics.Attribute
import nz.net.ultraq.redhorizon.engine.graphics.Material
import nz.net.ultraq.redhorizon.engine.graphics.ShaderConfig
import nz.net.ultraq.redhorizon.engine.graphics.Uniform

Expand All @@ -34,6 +35,11 @@ class SpriteShader extends ShaderConfig {
final Uniform[] uniforms = [
{ shader, material, window ->
shader.setUniformTexture('mainTexture', 0, material.texture)
},
{ shader, material, window ->
Material.KEYS_SPRITES.each { key ->
shader.setUniformGeneric(key, material[key])
}
}
]
}
Loading

0 comments on commit 0862da9

Please sign in to comment.