Skip to content

Commit 752bb58

Browse files
committed
ref: texture api
1 parent 083a62e commit 752bb58

File tree

1 file changed

+136
-29
lines changed
  • common/src/main/kotlin/com/lambda/graphics/texture

1 file changed

+136
-29
lines changed

common/src/main/kotlin/com/lambda/graphics/texture/Texture.kt

Lines changed: 136 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,69 @@
1717

1818
package com.lambda.graphics.texture
1919

20+
import com.lambda.graphics.renderer.gui.TextureRenderer
2021
import com.lambda.graphics.texture.TextureUtils.bindTexture
2122
import com.lambda.graphics.texture.TextureUtils.readImage
2223
import com.lambda.graphics.texture.TextureUtils.setupTexture
24+
import com.lambda.util.math.Rect.Companion.basedOn
25+
import com.lambda.util.math.Vec2d
2326
import org.lwjgl.opengl.GL45C.*
2427
import java.awt.image.BufferedImage
2528
import java.lang.IllegalStateException
29+
import java.nio.ByteBuffer
2630

2731
/**
2832
* Represents a texture that can be uploaded and bound to the graphics pipeline
2933
* Supports mipmap generation and LOD (Level of Detail) configuration
30-
*
31-
* @param image Optional initial image to upload to the texture
32-
* @param format The format of the image passed in
33-
* @param levels Number of mipmap levels to generate for the texture
34-
* @param forceConsistency Flag to enforce consistency when updating the texture. If true, attempts to update
35-
* the texture after initialization will throw an exception
3634
*/
37-
open class Texture(
38-
image: BufferedImage?,
39-
val format: Int = GL_RGBA,
40-
private val levels: Int = 4,
41-
private val forceConsistency: Boolean = false,
42-
) {
35+
open class Texture{
36+
val format: Int
37+
private val levels: Int
38+
private val forceConsistency: Boolean
39+
40+
/**
41+
* @param image Optional initial image to upload to the texture
42+
* @param format The format of the image passed in
43+
* @param levels Number of mipmap levels to generate for the texture
44+
* @param forceConsistency Flag to enforce consistency when updating the texture. If true, attempts to update
45+
* the texture after initialization will throw an exception
46+
*/
47+
constructor(image: BufferedImage?,
48+
format: Int = GL_RGBA,
49+
levels: Int = 4,
50+
forceConsistency: Boolean = false)
51+
{
52+
this.format = format
53+
this.levels = levels
54+
this.forceConsistency = forceConsistency
55+
56+
image?.let { bindTexture(id); upload(it) }
57+
}
58+
59+
/**
60+
* @param buffer The image buffer
61+
* @param width The width of the image
62+
* @param height The height of the image
63+
* @param format The format of the image passed in
64+
* @param levels Number of mipmap levels to generate for the texture
65+
* @param forceConsistency Flag to enforce consistency when updating the texture. If true, attempts to update
66+
* the texture after initialization will throw an exception
67+
*/
68+
constructor(buffer: ByteBuffer,
69+
width: Int,
70+
height: Int,
71+
format: Int = GL_RGBA,
72+
levels: Int = 4,
73+
forceConsistency: Boolean = false)
74+
{
75+
this.format = format
76+
this.levels = levels
77+
this.forceConsistency = forceConsistency
78+
79+
bindTexture(id)
80+
upload(buffer, width, height)
81+
}
82+
4383
/**
4484
* Indicates whether there is an initial texture or not
4585
*/
@@ -57,12 +97,20 @@ open class Texture(
5797
}
5898

5999
/**
60-
* Uploads an image to the texture and generates mipmaps for the texture if applicable.
100+
* Unbinds the currently bound texture
101+
*/
102+
open fun unbind(slot: Int = 0) {
103+
bindTexture(0, slot)
104+
}
105+
106+
/**
107+
* Uploads an image to the texture and generates mipmaps for the texture if applicable
108+
* This function does not bind the texture
61109
*
62-
* @param image The image to upload to the texture
63-
* @param offset The mipmap level to upload the image to
110+
* @param image The image to upload to the texture
111+
* @param offset The mipmap level to upload the image to
64112
*/
65-
open fun upload(image: BufferedImage, offset: Int = 0) {
113+
fun upload(image: BufferedImage, offset: Int = 0) {
66114
if (forceConsistency && initialized) throw IllegalStateException("Client tried to update a texture, but the enforce consistency flag was present")
67115

68116
// Store level_base +1 through `level` images and generate
@@ -75,23 +123,90 @@ open class Texture(
75123

76124
// Set this mipmap to `offset` to define the original texture
77125
setupTexture(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR)
78-
glTexImage2D(GL_TEXTURE_2D, offset, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, readImage(image))
126+
glTexImage2D(GL_TEXTURE_2D, offset, GL_RGBA, width, height, 0, format, GL_UNSIGNED_BYTE, readImage(image))
79127
if (levels > 1) glGenerateMipmap(GL_TEXTURE_2D) // This take the derived values GL_TEXTURE_BASE_LEVEL and GL_TEXTURE_MAX_LEVEL to generate the stack
80128
}
81129

82-
open fun update(image: BufferedImage, offset: Int = 0) {
130+
/**
131+
* Uploads an image to the texture and generates mipmaps for the texture if applicable
132+
* This function does not bind the texture
133+
*
134+
* @param buffer The image buffer to upload to the texture
135+
* @param width The width of the texture
136+
* @param height The height of the texture
137+
* @param offset The mipmap level to upload the image to
138+
*/
139+
fun upload(buffer: ByteBuffer, width: Int, height: Int, offset: Int = 0) {
140+
if (forceConsistency && initialized) throw IllegalStateException("Client tried to update a texture, but the enforce consistency flag was present")
141+
142+
// Store level_base +1 through `level` images and generate
143+
// mipmaps from them
144+
setupLOD(levels = levels)
145+
146+
this.width = width
147+
this.height = height
148+
initialized = true
149+
150+
// Set this mipmap to `offset` to define the original texture
151+
setupTexture(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR)
152+
glTexImage2D(GL_TEXTURE_2D, offset, GL_RGBA, width, height, 0, format, GL_UNSIGNED_BYTE, buffer)
153+
if (levels > 1) glGenerateMipmap(GL_TEXTURE_2D) // This take the derived values GL_TEXTURE_BASE_LEVEL and GL_TEXTURE_MAX_LEVEL to generate the stack
154+
}
155+
156+
/**
157+
* Updates the data of a texture
158+
* This function does not bind the texture
159+
*
160+
* @param image The image to upload to the texture
161+
* @param offset The mipmap level to upload the image to
162+
*
163+
* @throws IllegalStateException If the texture has the consistency flag and is already initialized
164+
*/
165+
fun update(image: BufferedImage, offset: Int = 0) {
83166
if (!initialized) return upload(image, offset)
84167
if (forceConsistency && initialized) throw IllegalStateException("Client tried to update a texture, but the enforce consistency flag was present")
85168

86-
check(image.width + image.height > this.width + this.height && initialized) {
169+
check(image.width + image.height <= this.width + this.height && initialized) {
87170
"Client tried to update a texture with more data than allowed" +
88171
"Expected ${this.width + this.height} bytes but got ${image.width + image.height}"
89172
}
90173

91-
// Can we rebuild LOD ?
92-
glTexSubImage2D(GL_TEXTURE_2D, offset, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, readImage(image))
174+
glTexSubImage2D(GL_TEXTURE_2D, offset, 0, 0, width, height, format, GL_UNSIGNED_BYTE, readImage(image))
175+
}
176+
177+
/**
178+
* Updates the data of a texture
179+
* This function does not bind the texture
180+
*
181+
* @param buffer The image buffer to upload to the texture
182+
* @param width The width of the texture
183+
* @param height The height of the texture
184+
* @param offset The mipmap level to upload the image to
185+
*
186+
* @throws IllegalStateException If the texture has the consistency flag and is already initialized
187+
*/
188+
fun update(buffer: ByteBuffer, width: Int, height: Int, offset: Int = 0) {
189+
if (!initialized) return upload(buffer, width, height, offset)
190+
if (forceConsistency && initialized) throw IllegalStateException("Client tried to update a texture, but the enforce consistency flag was present")
191+
192+
check(width + height <= this.width + this.height && initialized) {
193+
"Client tried to update a texture with more data than allowed\n" +
194+
"Expected ${this.width + this.height} bytes but got ${width + height}"
195+
}
196+
197+
glTexSubImage2D(GL_TEXTURE_2D, offset, 0, 0, width, height, format, GL_UNSIGNED_BYTE, buffer)
93198
}
94199

200+
/**
201+
* Draws the texture
202+
* This function binds the texture
203+
*
204+
* @param coord The top left coordinate to draw at
205+
* @param scale The width and height multiplier
206+
*/
207+
fun draw(coord: Vec2d, scale: Double = 0.0) =
208+
TextureRenderer.drawTexture(this, basedOn(coord, width * scale, height * scale))
209+
95210
private fun setupLOD(levels: Int) {
96211
// When you call glTextureStorage, you're specifying the total number of levels, including level 0
97212
// This is a 0-based index system, which means that the maximum mipmap level is n-1
@@ -103,12 +218,4 @@ open class Texture(
103218
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0)
104219
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels)
105220
}
106-
107-
init {
108-
image?.let {
109-
// Don't use bind() because if a child class overrides the function we're screwed
110-
bindTexture(id)
111-
upload(it)
112-
}
113-
}
114221
}

0 commit comments

Comments
 (0)