mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f78cfdd5fb | ||
|
|
183bcfbf41 | ||
|
|
502f30c0f9 | ||
|
|
ceff04adba | ||
|
|
b4dfdb8cc5 | ||
|
|
3064818477 | ||
|
|
27524e6a89 | ||
|
|
5d5e910fc0 | ||
|
|
f325fd7fc3 | ||
|
|
ec278d3d7a | ||
|
|
eef3d2f50a | ||
|
|
3da597ab18 | ||
|
|
0e79380618 | ||
|
|
b639d5c646 | ||
|
|
0d209d426d | ||
|
|
bf24084033 | ||
|
|
9267ecc735 | ||
|
|
dcc8d8deed | ||
|
|
5dc4f1fab8 | ||
|
|
d3cea1bfe4 | ||
|
|
5db2b248d7 | ||
|
|
21ab526284 | ||
|
|
4c8ea679ef | ||
|
|
2317379df2 | ||
|
|
24dae49c7f | ||
|
|
865de3cbab | ||
|
|
9c45549e35 | ||
|
|
2a20198292 | ||
|
|
f0b68f93af | ||
|
|
dd1d3aa518 | ||
|
|
c9633fd745 | ||
|
|
e2e94b2958 | ||
|
|
3046b043a0 | ||
|
|
8a6c8035db | ||
|
|
2b15370c00 | ||
|
|
3ca9e32191 | ||
|
|
efbca29381 | ||
|
|
9c1b6c75ff | ||
|
|
fa91b70528 | ||
|
|
61de8385ca | ||
|
|
962e7527a4 | ||
|
|
2ab2713fe9 | ||
|
|
7cf652cd4e | ||
|
|
48eba2fd96 | ||
|
|
356ba376fe | ||
|
|
ef6b0742a9 | ||
|
|
787929d0bd |
No files matched your search
@@ -50,6 +50,7 @@ Targets GL 3.3+ core profile
|
||||
* CodeChicken[Core](https://github.com/GTNewHorizons/CodeChickenCore/releases)/[Lib](https://github.com/GTNewHorizons/CodeChickenLib/releases) <1.2.0 - Threading issues, use 1.3.0+
|
||||
* Neodymium - Untested, but unlikely to be compatible. (Chunk Meshing is definitely incompatible, other features might work depending on config)
|
||||
* SmoothFont - Equivalent functionality added
|
||||
* ClientFixer - Use a resource pack like [GTNH-FTI-Standard-Font](https://github.com/Eldrinn-Elantey/GTNH-FTI-Standard-Font)
|
||||
|
||||
# Credits
|
||||
* ShadersMod
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import xyz.wagyourtail.jvmdg.gradle.flags.DowngradeFlags
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.DowngradeJar
|
||||
import xyz.wagyourtail.jvmdg.gradle.task.files.DowngradeFiles
|
||||
|
||||
plugins {
|
||||
id("com.gtnewhorizons.gtnhconvention")
|
||||
}
|
||||
|
||||
tasks.withType<DowngradeJar>().configureEach { logLevel.set("FATAL") }
|
||||
tasks.withType<DowngradeFiles>().configureEach { logLevel.set("FATAL") }
|
||||
|
||||
minecraft {
|
||||
extraRunJvmArguments.add("-Dangelica.enableTestBlocks=true")
|
||||
extraRunJvmArguments.add("-Dangelica.dumpClass=true")
|
||||
|
||||
+21
-3
@@ -66,9 +66,9 @@ configurations.all {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.14:dev")
|
||||
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.158:dev")
|
||||
|
||||
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.0.7") { transitive = false }
|
||||
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.1.0") { transitive = false }
|
||||
// runtimeOnlyNonPublishable("com.github.GTNewHorizons:lwjgl3ify:3.0.99")
|
||||
|
||||
compileOnly("org.projectlombok:lombok:1.18.42") { transitive = false }
|
||||
@@ -76,7 +76,7 @@ dependencies {
|
||||
|
||||
// Iris Shaders
|
||||
compileOnly('org.jetbrains:annotations:26.0.2')
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.9.52:dev")
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.11.4:dev")
|
||||
|
||||
shadowImplementation(project(":glsm")) { transitive = false }
|
||||
shadowImplementation(project(":lwjgl3-backend")) { transitive = false }
|
||||
@@ -97,6 +97,24 @@ dependencies {
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.35-GTNH:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
|
||||
|
||||
// Display List Testing
|
||||
/*
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-749-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ae2stuff:0.10.14-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BuildCraft:7.1.54:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:IguanaTweaksTConstruct:2.7.0:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenBlocks:1.12.4-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenComputers:1.12.12-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:TinkersConstruct:1.14.12-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ServerUtilities:2.2.13:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:ironchest:6.1.6:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.9.7:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Botania:1.13.15-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Mobs-Info:0.5.10-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Draconic-Evolution:1.5.18-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:HoloInventory:2.5.8-GTNH:dev")
|
||||
*/
|
||||
|
||||
//devOnlyNonPublishable('com.github.GTNewHorizons:worldedit-gtnh:v0.0.9')
|
||||
|
||||
// Notfine Deps
|
||||
|
||||
@@ -66,6 +66,7 @@ repositories {
|
||||
content { includeGroup("xyz.wagyourtail.jvmdowngrader") }
|
||||
}
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -123,6 +124,7 @@ tasks.withType<DowngradeFiles>().configureEach {
|
||||
downgradeTo.set(JavaVersion.VERSION_1_8)
|
||||
multiReleaseOriginal.set(false)
|
||||
multiReleaseVersions.set(emptySet())
|
||||
logLevel.set("FATAL")
|
||||
}
|
||||
|
||||
val downgradeDepsForTest by tasks.registering(DowngradeFiles::class) {
|
||||
@@ -138,6 +140,7 @@ val downgradeMainClasses by tasks.registering(DowngradeFiles::class) {
|
||||
val downgradeTestClasses by tasks.registering(DowngradeFiles::class) {
|
||||
inputCollection = sourceSets["test"].output.classesDirs
|
||||
classpath = sourceSets["test"].compileClasspath
|
||||
sourceSets["test"].output.classesDirs.files.forEach { outputs.dir(temporaryDir.resolve(it.name)) }
|
||||
dependsOn(tasks.named("testClasses"))
|
||||
}
|
||||
|
||||
@@ -173,6 +176,19 @@ tasks.test {
|
||||
jvmArgs("-Djava.library.path=${extractNatives.get().property("destinationFolder").let { (it as DirectoryProperty).asFile.get().path }}")
|
||||
}
|
||||
|
||||
val verifyTestsRan by tasks.registering {
|
||||
val resultsDir = layout.buildDirectory.dir("test-results/test")
|
||||
dependsOn(tasks.test)
|
||||
doLast {
|
||||
val dir = resultsDir.get().asFile
|
||||
val xmls = dir.listFiles { f -> f.name.startsWith("TEST-") && f.name.endsWith(".xml") } ?: emptyArray()
|
||||
check(xmls.isNotEmpty()) {
|
||||
":glsm:test produced no TEST-*.xml in $dir - test task likely went NO-SOURCE"
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.check { dependsOn(verifyTestsRan) }
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("mavenJava") {
|
||||
|
||||
@@ -4,7 +4,7 @@ celeritasVersion = 2.5.3-GTNH
|
||||
lombokVersion = 1.18.42
|
||||
log4jVersion = 2.0-beta9
|
||||
guavaVersion = 18.0
|
||||
gtnhlibVersion = 0.9.36
|
||||
gtnhlibVersion = 0.10.0
|
||||
glslTransformLibVersion = 0.2.0-26.g5ee7d96-GTNH
|
||||
antlr4RuntimeVersion = 4.13.2
|
||||
jcppVersion = 1.4.14
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.recording.AccumulatedDraw;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandBufferProcessor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builds final command buffers from raw command buffers and compiled VBO data.
|
||||
* Bridges the gap between VBO compilation (glsm types) and buffer processing (recording types).
|
||||
*/
|
||||
public final class CommandBufferBuilder {
|
||||
|
||||
private CommandBufferBuilder() {}
|
||||
|
||||
/**
|
||||
* Build commands with transform collapsing optimization.
|
||||
* Accumulates MODELVIEW transforms and emits MultMatrix at barriers (draws, CallList).
|
||||
* Uses mergedRanges where consecutive same-transform draws are combined.
|
||||
*/
|
||||
public static void buildFromRawBuffer(
|
||||
CommandBuffer rawBuffer,
|
||||
List<AccumulatedDraw> accumulatedDraws,
|
||||
CommandBuffer finalBuffer) {
|
||||
|
||||
rawBuffer.resetRead();
|
||||
int cmdIndex = 0;
|
||||
int rangeIndex = 0;
|
||||
|
||||
AccumulatedDraw draw;
|
||||
while (rawBuffer.hasRemaining()) {
|
||||
// Read and process the command
|
||||
CommandBufferProcessor.copyCommand(rawBuffer, finalBuffer);
|
||||
|
||||
// Emit draw ranges at this command position
|
||||
while (rangeIndex < accumulatedDraws.size() && (draw = accumulatedDraws.get(rangeIndex)).commandIndex == cmdIndex) {
|
||||
emitDrawRangeToBuffer(draw, finalBuffer, rangeIndex);
|
||||
rangeIndex++;
|
||||
}
|
||||
|
||||
cmdIndex++;
|
||||
}
|
||||
|
||||
while (rangeIndex < accumulatedDraws.size()) {
|
||||
emitDrawRangeToBuffer(accumulatedDraws.get(rangeIndex), finalBuffer, rangeIndex);
|
||||
rangeIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a draw range to the output buffer, with transform if needed.
|
||||
*/
|
||||
private static void emitDrawRangeToBuffer(
|
||||
AccumulatedDraw draw,
|
||||
CommandBuffer out,
|
||||
int vboIdx) {
|
||||
|
||||
if (draw.restoreData != null) {
|
||||
out.writeDrawRangeRestore(vboIdx, draw.restoreData);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write the draw range command
|
||||
out.writeDrawRange(vboIdx);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import org.taumc.glsl.ShaderParser;
|
||||
import org.taumc.glsl.Transformer;
|
||||
|
||||
@@ -114,13 +113,17 @@ public class CompatShaderTransformer {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean needsTransformation(String source) {
|
||||
// Shaders already at 330+ core don't need compat transformation
|
||||
public static boolean isCoreShader(String source) {
|
||||
final Matcher vm = VERSION_PATTERN.matcher(source);
|
||||
if (vm.find()) {
|
||||
final int version = Integer.parseInt(vm.group(1));
|
||||
if (version >= 330 && "core".equals(vm.group(2))) return false;
|
||||
return version >= 330 && "core".equals(vm.group(2));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean needsTransformation(String source) {
|
||||
if (isCoreShader(source)) return false;
|
||||
|
||||
return NEEDS_TRANSFORM_PATTERN.matcher(source).find();
|
||||
}
|
||||
|
||||
@@ -7,15 +7,16 @@ import com.gtnewhorizons.angelica.glsm.states.LightModelState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Per-program compat uniform location cache and upload manager.
|
||||
*
|
||||
@@ -102,8 +103,15 @@ public class CompatUniformManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Per-program cached uniform locations. Maps program ID → int[LOC_COUNT]. */
|
||||
private static final Int2ObjectOpenHashMap<int[]> programLocations = new Int2ObjectOpenHashMap<>();
|
||||
/** Per-program cached uniform locations + last-uploaded generations. Maps program ID -> state. */
|
||||
private static final Int2ObjectOpenHashMap<ProgramUniforms> programUniforms = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private static final class ProgramUniforms {
|
||||
final int[] locs;
|
||||
int lastMvGen = -1, lastMvLinearGen = -1, lastProjGen = -1, lastTexMatGen = -1;
|
||||
int lastFragmentGen = -1, lastLightingGen = -1, lastClipPlaneGen = -1;
|
||||
ProgramUniforms(int[] locs) { this.locs = locs; }
|
||||
}
|
||||
|
||||
// Reusable NIO buffers for upload
|
||||
private static final FloatBuffer mat4Buf = BufferUtils.createFloatBuffer(16);
|
||||
@@ -112,6 +120,7 @@ public class CompatUniformManager {
|
||||
private static final FloatBuffer clipPlaneBuf = BufferUtils.createFloatBuffer(32); // 8 planes * vec4
|
||||
private static final Matrix3f normalMatrix = new Matrix3f();
|
||||
private static final Matrix4f scratchMatrix = new Matrix4f();
|
||||
private static final Vector3f scratchCol = new Vector3f();
|
||||
|
||||
private static final float LIGHTMAP_SCALE = 1.0f / 256.0f;
|
||||
private static final FloatBuffer lightmapMatrixBuf;
|
||||
@@ -121,17 +130,18 @@ public class CompatUniformManager {
|
||||
new Matrix4f().scale(LIGHTMAP_SCALE).translate(8.0f, 8.0f, 8.0f).get(lightmapMatrixBuf);
|
||||
}
|
||||
|
||||
// Dirty tracking: skip uploads when state hasn't changed and program is the same
|
||||
private static int lastProgram = -1;
|
||||
private static int lastMvGen = -1;
|
||||
private static int lastProjGen = -1;
|
||||
private static int lastTexMatGen = -1;
|
||||
private static int lastFragmentGen = -1;
|
||||
private static int lastLightingGen = -1;
|
||||
private static int lastClipPlaneGen = -1;
|
||||
|
||||
private CompatUniformManager() {}
|
||||
|
||||
/**
|
||||
* Normalize each column of a 3x3 matrix in place. Used to strip the residual 1/scale that
|
||||
* inverse-transpose introduces.
|
||||
*/
|
||||
private static void normalizeColumnsInPlace(Matrix3f m) {
|
||||
m.setColumn(0, m.getColumn(0, scratchCol).normalize());
|
||||
m.setColumn(1, m.getColumn(1, scratchCol).normalize());
|
||||
m.setColumn(2, m.getColumn(2, scratchCol).normalize());
|
||||
}
|
||||
|
||||
public static void onLinkProgram(int program) {
|
||||
int[] locs = new int[LOC_COUNT];
|
||||
boolean hasAny = false;
|
||||
@@ -142,45 +152,49 @@ public class CompatUniformManager {
|
||||
}
|
||||
|
||||
if (hasAny) {
|
||||
programLocations.put(program, locs);
|
||||
programUniforms.put(program, new ProgramUniforms(locs));
|
||||
GLStateManager.LOGGER.debug("CompatUniformManager: program {} has compat uniforms", program);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onUseProgram(int program) {
|
||||
if (program == 0) return;
|
||||
/**
|
||||
* Uploads the emulated uniforms.
|
||||
*
|
||||
* @return Whether the program needs emulation (FFP or Iris Program)
|
||||
*/
|
||||
public static boolean refreshCompatUniforms(int program) {
|
||||
final ProgramUniforms pu = programUniforms.get(program);
|
||||
if (pu == null) return false;
|
||||
final int[] locs = pu.locs;
|
||||
|
||||
int[] locs = programLocations.get(program);
|
||||
if (locs == null) return;
|
||||
|
||||
final boolean programChanged = program != lastProgram;
|
||||
lastProgram = program;
|
||||
|
||||
// Matrix uniforms — skip if generation unchanged and same program
|
||||
// Matrix uniforms — skip if this program's storage already holds the current generation
|
||||
final int mvGen = GLStateManager.mvGeneration;
|
||||
final int mvLinearGen = GLStateManager.mvLinearGeneration;
|
||||
final int projGen = GLStateManager.projGeneration;
|
||||
final int texMatGen = GLStateManager.texMatrixGeneration;
|
||||
final boolean mvChanged = programChanged || mvGen != lastMvGen;
|
||||
final boolean projChanged = programChanged || projGen != lastProjGen;
|
||||
final boolean texMatChanged = programChanged || texMatGen != lastTexMatGen;
|
||||
final boolean mvChanged = mvGen != pu.lastMvGen;
|
||||
final boolean mvLinearChanged = mvLinearGen != pu.lastMvLinearGen;
|
||||
final boolean projChanged = projGen != pu.lastProjGen;
|
||||
final boolean texMatChanged = texMatGen != pu.lastTexMatGen;
|
||||
if (mvChanged || projChanged || texMatChanged) {
|
||||
uploadMatrices(locs, mvChanged, projChanged, texMatChanged);
|
||||
lastMvGen = mvGen;
|
||||
lastProjGen = projGen;
|
||||
lastTexMatGen = texMatGen;
|
||||
uploadMatrices(locs, mvChanged, mvLinearChanged, projChanged, texMatChanged);
|
||||
pu.lastMvGen = mvGen;
|
||||
pu.lastMvLinearGen = mvLinearGen;
|
||||
pu.lastProjGen = projGen;
|
||||
pu.lastTexMatGen = texMatGen;
|
||||
}
|
||||
|
||||
// Fragment-category uniforms (fog, alpha) — skip if generation unchanged
|
||||
final int fragGen = GLStateManager.fragmentGeneration;
|
||||
if (programChanged || fragGen != lastFragmentGen) {
|
||||
lastFragmentGen = fragGen;
|
||||
if (fragGen != pu.lastFragmentGen) {
|
||||
pu.lastFragmentGen = fragGen;
|
||||
uploadFragmentUniforms(locs);
|
||||
}
|
||||
|
||||
// Lighting-derived uniforms (scene color, light sources, material)
|
||||
final int litGen = GLStateManager.lightingGeneration;
|
||||
if (programChanged || litGen != lastLightingGen) {
|
||||
lastLightingGen = litGen;
|
||||
if (litGen != pu.lastLightingGen) {
|
||||
pu.lastLightingGen = litGen;
|
||||
if (locs[LOC_SCENE_COLOR] != -1) uploadSceneColor(locs);
|
||||
uploadLightSources(locs);
|
||||
uploadMaterial(locs);
|
||||
@@ -189,14 +203,16 @@ public class CompatUniformManager {
|
||||
// Clip plane equations + enabled bool — uploaded when enable state or equations change
|
||||
if (locs[LOC_CLIP_PLANES] != -1 || locs[LOC_CLIP_PLANES_ENABLED] != -1) {
|
||||
final int cpGen = GLStateManager.clipPlaneGeneration;
|
||||
if (programChanged || cpGen != lastClipPlaneGen) {
|
||||
lastClipPlaneGen = cpGen;
|
||||
if (cpGen != pu.lastClipPlaneGen) {
|
||||
pu.lastClipPlaneGen = cpGen;
|
||||
uploadClipPlanes(locs);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void uploadMatrices(int[] locs, boolean mvChanged, boolean projChanged, boolean texMatChanged) {
|
||||
private static void uploadMatrices(int[] locs, boolean mvChanged, boolean mvLinearChanged, boolean projChanged, boolean texMatChanged) {
|
||||
final Matrix4f mv = GLStateManager.getModelViewMatrix();
|
||||
final Matrix4f proj = GLStateManager.getProjectionMatrix();
|
||||
|
||||
@@ -218,15 +234,20 @@ public class CompatUniformManager {
|
||||
scratchMatrix.get(mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_MODELVIEW_INVERSE], false, mat4Buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (mvLinearChanged) {
|
||||
// Normal Matrix (inverse-transpose of upper-left 3x3 of ModelView)
|
||||
if (locs[LOC_NORMAL] != -1 || locs[LOC_IRIS_NORMAL] != -1) {
|
||||
mv.normal(normalMatrix);
|
||||
normalMatrix.get(mat3Buf);
|
||||
if (locs[LOC_NORMAL] != -1) {
|
||||
normalMatrix.get(mat3Buf);
|
||||
RENDER_BACKEND.uniformMatrix3(locs[LOC_NORMAL], false, mat3Buf);
|
||||
}
|
||||
// Column-normalized to unit magnitude
|
||||
if (locs[LOC_IRIS_NORMAL] != -1) {
|
||||
normalizeColumnsInPlace(normalMatrix);
|
||||
normalMatrix.get(mat3Buf);
|
||||
RENDER_BACKEND.uniformMatrix3(locs[LOC_IRIS_NORMAL], false, mat3Buf);
|
||||
}
|
||||
}
|
||||
@@ -398,14 +419,15 @@ public class CompatUniformManager {
|
||||
}
|
||||
|
||||
public static void onDeleteProgram(int program) {
|
||||
programLocations.remove(program);
|
||||
programUniforms.remove(program);
|
||||
}
|
||||
|
||||
public static boolean hasProgram(int program) {
|
||||
return programLocations.containsKey(program);
|
||||
return programUniforms.containsKey(program);
|
||||
}
|
||||
|
||||
public static int[] getLocations(int program) {
|
||||
return programLocations.get(program);
|
||||
final ProgramUniforms pu = programUniforms.get(program);
|
||||
return pu == null ? null : pu.locs;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -142,7 +142,7 @@ public class FeedbackManager {
|
||||
final int byteCount = count * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, (long) first * stride + posOffset, readbackBuf);
|
||||
readPositionBuffer((long) first * stride + posOffset, readbackBuf);
|
||||
|
||||
emitAllPrimitives(mode, count, i -> i, stride);
|
||||
}
|
||||
@@ -170,7 +170,7 @@ public class FeedbackManager {
|
||||
final int byteCount = vertexCount * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, (long) minIdx * stride + posOffset, readbackBuf);
|
||||
readPositionBuffer((long) minIdx * stride + posOffset, readbackBuf);
|
||||
|
||||
final int base = minIdx;
|
||||
emitAllPrimitives(mode, indexCount, i -> ImmediateModeRecorder.readIndex(indexBuf, type, 0, i) - base, stride);
|
||||
@@ -251,7 +251,23 @@ public class FeedbackManager {
|
||||
final int byteCount = vertexCount * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, (long) minIdx * stride + posOffset, readbackBuf);
|
||||
readPositionBuffer((long) minIdx * stride + posOffset, readbackBuf);
|
||||
}
|
||||
|
||||
private static void readPositionBuffer(long offset, ByteBuffer destination) {
|
||||
final int positionVbo = VertexAttribState.get(0).vboId;
|
||||
final int previousVbo = GLStateManager.getBoundVBO();
|
||||
|
||||
if (positionVbo != 0 && positionVbo != previousVbo) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, positionVbo);
|
||||
}
|
||||
try {
|
||||
RENDER_BACKEND.getBufferSubData(GL15.GL_ARRAY_BUFFER, offset, destination);
|
||||
} finally {
|
||||
if (positionVbo != 0 && positionVbo != previousVbo) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, previousVbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void emitVertex(int vertexIndex, int stride) {
|
||||
|
||||
@@ -144,32 +144,66 @@ public class GLStateManager {
|
||||
|
||||
// Generation counters for FFP uniform dirty tracking. Bumped when the corresponding GLSM state changes.
|
||||
// Per-matrix-mode generation counters — avoids re-uploading all matrices when only one mode changed
|
||||
public static int mvGeneration; // modelview matrix changes
|
||||
public static int mvGeneration; // modelview matrix changes (any: translation, rotation, scale)
|
||||
public static int mvLinearGeneration;
|
||||
public static int projGeneration; // projection matrix changes
|
||||
public static int texMatrixGeneration; // texture matrix changes
|
||||
public static int lightingGeneration;
|
||||
public static int fragmentGeneration; // fog + alpha ref
|
||||
public static int fragmentGeneration; // fog + alpha ref + overlay color
|
||||
public static int colorGeneration; // current vertex color
|
||||
public static int clipPlaneGeneration; // clip plane equation changes
|
||||
|
||||
@Getter private static float overlayR = 0.0f;
|
||||
@Getter private static float overlayG = 0.0f;
|
||||
@Getter private static float overlayB = 0.0f;
|
||||
@Getter private static float overlayA = 0.0f;
|
||||
|
||||
public static void setOverlayColor(float r, float g, float b, float a) {
|
||||
if (r == overlayR && g == overlayG && b == overlayB && a == overlayA) return;
|
||||
overlayR = r; overlayG = g; overlayB = b; overlayA = a;
|
||||
fragmentGeneration++;
|
||||
}
|
||||
|
||||
@Getter private static float shaderColorR = 1.0f;
|
||||
@Getter private static float shaderColorG = 1.0f;
|
||||
@Getter private static float shaderColorB = 1.0f;
|
||||
@Getter private static float shaderColorA = 1.0f;
|
||||
|
||||
public static void setShaderColor(float r, float g, float b, float a) {
|
||||
if (r == shaderColorR && g == shaderColorG && b == shaderColorB && a == shaderColorA) return;
|
||||
shaderColorR = r; shaderColorG = g; shaderColorB = b; shaderColorA = a;
|
||||
if (GLSMHooks.SHADER_COLOR_CHANGE.hasListeners()) {
|
||||
GLSMHooks.shaderColorChangeEvent.red = r;
|
||||
GLSMHooks.shaderColorChangeEvent.green = g;
|
||||
GLSMHooks.shaderColorChangeEvent.blue = b;
|
||||
GLSMHooks.shaderColorChangeEvent.alpha = a;
|
||||
GLSMHooks.SHADER_COLOR_CHANGE.post(GLSMHooks.shaderColorChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// Deferred vertex attribute upload flags — set when state changes, flushed before draw
|
||||
private static boolean dirtyColorAttrib;
|
||||
private static boolean dirtyColorAttrib = true;
|
||||
private static boolean dirtyNormalAttrib;
|
||||
private static boolean dirtyTexCoordAttrib;
|
||||
private static boolean dirtyLightmapAttrib;
|
||||
private static boolean dirtyLightmapAttrib = true;
|
||||
|
||||
/** Flush deferred vertex attribute uploads. Called before draw to ensure default attrib values are current. */
|
||||
public static void flushDeferredVertexAttribs() {
|
||||
RENDER_BACKEND.vertexAttrib4f(Usage.COLOR.getAttributeLocation(), color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
|
||||
dirtyColorAttrib = false;
|
||||
RENDER_BACKEND.vertexAttrib4f(Usage.SECONDARY_UV.getAttributeLocation(), GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY, 0.0f, 1.0f);
|
||||
dirtyLightmapAttrib = false;
|
||||
if (dirtyNormalAttrib) {
|
||||
// vertexFlags bits mark attribs the VBO already supplies; FFP supplies the rest via u_Current* uniforms.
|
||||
// Skip the backend vertexAttrib call in either case.
|
||||
public static void flushDeferredVertexAttribs(int vertexFlags) {
|
||||
if (dirtyColorAttrib && (vertexFlags & VertexFlags.COLOR_BIT) == 0) {
|
||||
RENDER_BACKEND.vertexAttrib4f(Usage.COLOR.getAttributeLocation(), color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
|
||||
dirtyColorAttrib = false;
|
||||
}
|
||||
if (dirtyLightmapAttrib && (vertexFlags & VertexFlags.BRIGHTNESS_BIT) == 0) {
|
||||
RENDER_BACKEND.vertexAttrib4f(Usage.SECONDARY_UV.getAttributeLocation(), GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY, 0.0f, 1.0f);
|
||||
dirtyLightmapAttrib = false;
|
||||
}
|
||||
if (dirtyNormalAttrib && (vertexFlags & VertexFlags.NORMAL_BIT) == 0) {
|
||||
final var n = ShaderManager.getCurrentNormal();
|
||||
RENDER_BACKEND.vertexAttrib3f(Usage.NORMAL.getAttributeLocation(), n.x, n.y, n.z);
|
||||
dirtyNormalAttrib = false;
|
||||
}
|
||||
if (dirtyTexCoordAttrib) {
|
||||
if (dirtyTexCoordAttrib && (vertexFlags & VertexFlags.TEXTURE_BIT) == 0) {
|
||||
final var tc = ShaderManager.getCurrentTexCoord();
|
||||
RENDER_BACKEND.vertexAttrib4f(Usage.PRIMARY_UV.getAttributeLocation(), tc.x, tc.y, tc.z, tc.w);
|
||||
dirtyTexCoordAttrib = false;
|
||||
@@ -269,6 +303,7 @@ public class GLStateManager {
|
||||
|
||||
// Saved generation counters at push time — used to detect whether state actually changed during push/pop scope
|
||||
private static final int[] savedMvGen = new int[MAX_ATTRIB_STACK_DEPTH];
|
||||
private static final int[] savedMvLinearGen = new int[MAX_ATTRIB_STACK_DEPTH];
|
||||
private static final int[] savedProjGen = new int[MAX_ATTRIB_STACK_DEPTH];
|
||||
private static final int[] savedTexMatGen = new int[MAX_ATTRIB_STACK_DEPTH];
|
||||
private static final int[] savedLightingGen = new int[MAX_ATTRIB_STACK_DEPTH];
|
||||
@@ -1966,7 +2001,14 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Shrink texture to 1x1 to free GPU memory, unbind from all units, but keep the name valid. */
|
||||
/**
|
||||
* Defer deleting textures until it has been reclaimed.
|
||||
* Why you might ask? In compat profile if you bind a texture that doesn't exist, ie a deleted texture,
|
||||
* mesa will silently create it for you, in core this is an error. Why would you be binding a deleted texture?
|
||||
* Damned if I know, but vanilla and mods do it... they create one, delete it, and then bind it....
|
||||
*
|
||||
* So... we shrink the texture to 1x1 to free GPU memory, unbind from all units, but keep the name valid.
|
||||
* */
|
||||
private static void deferDeleteTexture(int id) {
|
||||
if (id == 0) return;
|
||||
|
||||
@@ -1991,7 +2033,9 @@ public class GLStateManager {
|
||||
}
|
||||
RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
RENDER_BACKEND.texParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0);
|
||||
suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage2D(GL11.GL_TEXTURE_2D, 0, GL30.GL_R8, 1, 1, 0, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
restorePixelUnpackBuffer();
|
||||
|
||||
// Restore previous binding on active unit
|
||||
RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, savedBinding == id ? 0 : savedBinding);
|
||||
@@ -2097,22 +2141,20 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glEnd() {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
final DirectTessellator result = ImmediateModeRecorder.end();
|
||||
if (result != null) {
|
||||
final DirectTessellator result = ImmediateModeRecorder.end();
|
||||
if (result != null) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
if (DisplayListManager.isCompileAndExecute() && initConfig != null && initConfig.getDirectDrawer() != null) {
|
||||
DisplayListManager.flushMatrix();
|
||||
final var recorder = DisplayListManager.pauseRecording();
|
||||
final CommandRecorder recorder = DisplayListManager.pauseRecording();
|
||||
initConfig.getDirectDrawer().accept(result);
|
||||
DisplayListManager.resumeRecording(recorder);
|
||||
}
|
||||
DisplayListManager.addImmediateModeDraw(result);
|
||||
return;
|
||||
}
|
||||
if (initConfig != null && initConfig.getDirectDrawer() != null) {
|
||||
initConfig.getDirectDrawer().accept(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final DirectTessellator result = ImmediateModeRecorder.end();
|
||||
if (result != null && initConfig != null && initConfig.getDirectDrawer() != null) {
|
||||
initConfig.getDirectDrawer().accept(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2227,7 +2269,6 @@ public class GLStateManager {
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
// Core profile: a default VAO is generated at init and glBindVertexArray(0)
|
||||
// is redirected to it, so a VAO is always bound here — no fallback branches.
|
||||
DisplayListManager.flushMatrix();
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.create(mode, indices_count, type, indices_buffer_offset, boundEBO);
|
||||
if (capture != null) {
|
||||
DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
@@ -2838,6 +2879,22 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
colorMaterial.enable();
|
||||
bakeCurrentColorIntoTrackedMaterial();
|
||||
}
|
||||
|
||||
public static void disableColorMaterial() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_COLOR_MATERIAL);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
bakeCurrentColorIntoTrackedMaterial();
|
||||
colorMaterial.disable();
|
||||
}
|
||||
|
||||
private static void bakeCurrentColorIntoTrackedMaterial() {
|
||||
final float r = getColor().getRed();
|
||||
final float g = getColor().getGreen();
|
||||
final float b = getColor().getBlue();
|
||||
@@ -2868,17 +2925,6 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void disableColorMaterial() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordDisable(GL11.GL_COLOR_MATERIAL);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
colorMaterial.disable();
|
||||
}
|
||||
|
||||
public static void disableLighting() {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
@@ -3009,16 +3055,19 @@ public class GLStateManager {
|
||||
}
|
||||
// Update cached state (FFP shader reads from cache)
|
||||
if (isCachingEnabled()) {
|
||||
boolean changed = true;
|
||||
boolean changed = false;
|
||||
switch (pname) {
|
||||
case GL11.GL_FOG_DENSITY -> { fogState.setDensity(param); fragmentGeneration++; }
|
||||
case GL11.GL_FOG_START -> { fogState.setStart(param); fragmentGeneration++; }
|
||||
case GL11.GL_FOG_END -> { fogState.setEnd(param); fragmentGeneration++; }
|
||||
case GL11.GL_FOG_MODE -> { fogState.setFogMode((int) param); fragmentGeneration++; }
|
||||
default -> changed = false;
|
||||
case GL11.GL_FOG_DENSITY -> { if (BYPASS_CACHE || fogState.getDensity() != param) { fogState.setDensity(param); changed = true; } }
|
||||
case GL11.GL_FOG_START -> { if (BYPASS_CACHE || fogState.getStart() != param) { fogState.setStart(param); changed = true; } }
|
||||
case GL11.GL_FOG_END -> { if (BYPASS_CACHE || fogState.getEnd() != param) { fogState.setEnd(param); changed = true; } }
|
||||
case GL11.GL_FOG_MODE -> { if (BYPASS_CACHE || fogState.getFogMode() != (int) param) { fogState.setFogMode((int) param); changed = true; } }
|
||||
default -> {}
|
||||
}
|
||||
if (changed && GLSMHooks.FOG_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.FOG_STATE_CHANGE.post(GLSMHooks.fogStateChangeEvent);
|
||||
if (changed) {
|
||||
fragmentGeneration++;
|
||||
if (GLSMHooks.FOG_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.FOG_STATE_CHANGE.post(GLSMHooks.fogStateChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3032,7 +3081,7 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
// Update cached state (FFP shader reads from cache)
|
||||
if (isCachingEnabled() && pname == GL11.GL_FOG_MODE) {
|
||||
if (isCachingEnabled() && pname == GL11.GL_FOG_MODE && (BYPASS_CACHE || fogState.getFogMode() != param)) {
|
||||
fogState.setFogMode(param);
|
||||
fragmentGeneration++;
|
||||
if (GLSMHooks.FOG_STATE_CHANGE.hasListeners()) {
|
||||
@@ -3183,6 +3232,7 @@ public class GLStateManager {
|
||||
|
||||
// Snapshot generation counters so we can detect actual changes at pop time
|
||||
savedMvGen[attribDepth] = mvGeneration;
|
||||
savedMvLinearGen[attribDepth] = mvLinearGeneration;
|
||||
savedProjGen[attribDepth] = projGeneration;
|
||||
savedTexMatGen[attribDepth] = texMatrixGeneration;
|
||||
savedLightingGen[attribDepth] = lightingGeneration;
|
||||
@@ -3291,13 +3341,23 @@ public class GLStateManager {
|
||||
if ((mask & GL11.GL_TEXTURE_BIT) != 0 && fragmentGeneration != savedFragmentGen[depth]) fragmentGeneration++; // texenv state
|
||||
if ((mask & GL11.GL_TRANSFORM_BIT) != 0) {
|
||||
if (mvGeneration != savedMvGen[depth]) mvGeneration++;
|
||||
if (mvLinearGeneration != savedMvLinearGen[depth]) mvLinearGeneration++;
|
||||
if (projGeneration != savedProjGen[depth]) projGeneration++;
|
||||
if (texMatrixGeneration != savedTexMatGen[depth]) texMatrixGeneration++;
|
||||
}
|
||||
if ((mask & GL11.GL_CURRENT_BIT) != 0) {
|
||||
if (colorGeneration != savedColorGen[depth]) colorGeneration++;
|
||||
if (ShaderManager.getNormalGeneration() != savedNormalGen[depth]) ShaderManager.bumpNormalGeneration();
|
||||
if (ShaderManager.getTexCoordGeneration() != savedTexCoordGen[depth]) ShaderManager.bumpTexCoordGeneration();
|
||||
if (colorGeneration != savedColorGen[depth]) {
|
||||
colorGeneration++;
|
||||
dirtyColorAttrib = true;
|
||||
}
|
||||
if (ShaderManager.getNormalGeneration() != savedNormalGen[depth]) {
|
||||
ShaderManager.bumpNormalGeneration();
|
||||
dirtyNormalAttrib = true;
|
||||
}
|
||||
if (ShaderManager.getTexCoordGeneration() != savedTexCoordGen[depth]) {
|
||||
ShaderManager.bumpTexCoordGeneration();
|
||||
dirtyTexCoordAttrib = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3348,7 +3408,6 @@ public class GLStateManager {
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
DisplayListManager.recordMatrixMode(mode);
|
||||
DisplayListManager.resetRelativeTransform(); // Mode switch is a barrier - reset delta tracking
|
||||
if (recordMode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
@@ -3362,8 +3421,6 @@ public class GLStateManager {
|
||||
final Matrix4f matrix = new Matrix4f().set(m);
|
||||
m.rewind();
|
||||
DisplayListManager.recordLoadMatrix(matrix);
|
||||
// Reset relative transform - subsequent transforms are relative to loaded matrix
|
||||
DisplayListManager.resetRelativeTransform();
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
@@ -3383,8 +3440,6 @@ public class GLStateManager {
|
||||
final Matrix4f floatMatrix = new Matrix4f();
|
||||
floatMatrix.set(conversionMatrix4d);
|
||||
DisplayListManager.recordLoadMatrix(floatMatrix);
|
||||
// Reset relative transform - subsequent transforms are relative to loaded matrix
|
||||
DisplayListManager.resetRelativeTransform();
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
@@ -3413,8 +3468,15 @@ public class GLStateManager {
|
||||
|
||||
/** Bump the generation counter for the currently active matrix mode. */
|
||||
private static void bumpMatrixGeneration() {
|
||||
bumpMatrixGeneration(true);
|
||||
}
|
||||
|
||||
private static void bumpMatrixGeneration(boolean linearChanged) {
|
||||
switch (matrixMode.getMode()) {
|
||||
case GL11.GL_MODELVIEW -> mvGeneration++;
|
||||
case GL11.GL_MODELVIEW -> {
|
||||
mvGeneration++;
|
||||
if (linearChanged) mvLinearGeneration++;
|
||||
}
|
||||
case GL11.GL_PROJECTION -> projGeneration++;
|
||||
case GL11.GL_TEXTURE -> texMatrixGeneration++;
|
||||
}
|
||||
@@ -3424,8 +3486,6 @@ public class GLStateManager {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLoadIdentity();
|
||||
// Reset relative transform - accumulated transforms are discarded (overwritten by load)
|
||||
DisplayListManager.resetRelativeTransform();
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
@@ -3437,34 +3497,31 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glTranslatef(float x, float y, float z) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.updateRelativeTransform(x, y, z, DisplayListManager.TransformOp.TRANSLATE, null);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixTranslation(x, y, z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
getMatrixStack().translate(x, y, z);
|
||||
bumpMatrixGeneration();
|
||||
bumpMatrixGeneration(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glTranslated(double x, double y, double z) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.updateRelativeTransform((float) x, (float) y, (float) z, DisplayListManager.TransformOp.TRANSLATE, null);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixTranslation((float) x, (float) y, (float) z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
getMatrixStack().translate((float) x, (float) y, (float) z);
|
||||
bumpMatrixGeneration();
|
||||
bumpMatrixGeneration(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glScalef(float x, float y, float z) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.updateRelativeTransform(x, y, z, DisplayListManager.TransformOp.SCALE, null);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixScale(x, y, z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
getMatrixStack().scale(x, y, z);
|
||||
@@ -3473,10 +3530,9 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glScaled(double x, double y, double z) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.updateRelativeTransform((float) x, (float) y, (float) z, DisplayListManager.TransformOp.SCALE, null);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixScale((float) x, (float) y, (float) z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
getMatrixStack().scale((float) x, (float) y, (float) z);
|
||||
@@ -3490,10 +3546,9 @@ public class GLStateManager {
|
||||
multMatrix.set(floatBuffer);
|
||||
final int currentMode = matrixMode.getMode();
|
||||
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.updateRelativeTransform(multMatrix);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
getMatrixStack().mul(multMatrix);
|
||||
@@ -3501,6 +3556,13 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyMultMatrix(Matrix4f multMatrix) {
|
||||
if (isCachingEnabled()) {
|
||||
getMatrixStack().mul(multMatrix);
|
||||
bumpMatrixGeneration();
|
||||
}
|
||||
}
|
||||
|
||||
public static final Matrix4d conversionMatrix4d = new Matrix4d();
|
||||
public static final Matrix4f conversionMatrix4f = new Matrix4f();
|
||||
|
||||
@@ -3508,10 +3570,9 @@ public class GLStateManager {
|
||||
conversionMatrix4d.set(matrix);
|
||||
conversionMatrix4f.set(conversionMatrix4d);
|
||||
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.updateRelativeTransform(conversionMatrix4f);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
getMatrixStack().mul(conversionMatrix4f);
|
||||
@@ -3519,20 +3580,24 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Vector3f rotation = new Vector3f();
|
||||
|
||||
public static void glRotatef(float angle, float x, float y, float z) {
|
||||
final float lenSq = x * x + y * y + z * z;
|
||||
if (lenSq == 0.0f) return;
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
rotation.set(x, y, z).normalize();
|
||||
DisplayListManager.updateRelativeTransform(angle, 0, 0, DisplayListManager.TransformOp.ROTATE, rotation);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
|
||||
// Fast way to normalize rotation
|
||||
final float scalar = 1.0f / (float) Math.sqrt(lenSq);
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
// Convert deg to rad
|
||||
angle = (float) Math.toRadians(angle);
|
||||
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixRotation(angle, x, y, z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
rotation.set(x, y, z).normalize();
|
||||
getMatrixStack().rotate((float) Math.toRadians(angle), rotation);
|
||||
getMatrixStack().rotate(angle, x, y, z);
|
||||
bumpMatrixGeneration();
|
||||
}
|
||||
}
|
||||
@@ -3540,24 +3605,29 @@ public class GLStateManager {
|
||||
public static void glRotated(double angle, double x, double y, double z) {
|
||||
final double lenSq = x * x + y * y + z * z;
|
||||
if (lenSq == 0.0) return;
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
rotation.set((float) x, (float) y, (float) z).normalize();
|
||||
DisplayListManager.updateRelativeTransform((float) angle, 0, 0, DisplayListManager.TransformOp.ROTATE, rotation);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
|
||||
// Fast way to normalize rotation
|
||||
final double scalar = 1.0 / Math.sqrt(lenSq);
|
||||
x *= scalar;
|
||||
y *= scalar;
|
||||
z *= scalar;
|
||||
// Convert deg to rad
|
||||
angle = Math.toRadians(angle);
|
||||
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixRotation((float) angle, (float) x, (float) y, (float) z);
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
rotation.set((float) x, (float) y, (float) z).normalize();
|
||||
getMatrixStack().rotate((float) Math.toRadians(angle), rotation);
|
||||
getMatrixStack().rotate((float) angle, (float) x, (float) y, (float) z);
|
||||
bumpMatrixGeneration();
|
||||
}
|
||||
}
|
||||
|
||||
public static void glOrtho(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.updateRelativeTransformOrtho(left, right, bottom, top, zNear, zFar);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
getMatrixStack().ortho((float) left, (float) right, (float) bottom, (float) top, (float) zNear, (float) zFar);
|
||||
@@ -3566,10 +3636,9 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glFrustum(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.updateRelativeTransformFrustum(left, right, bottom, top, zNear, zFar);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
return;
|
||||
}
|
||||
if (isCachingEnabled()) {
|
||||
getMatrixStack().frustum((float) left, (float) right, (float) bottom, (float) top, (float) zNear, (float) zFar);
|
||||
@@ -3864,7 +3933,12 @@ public class GLStateManager {
|
||||
return switch (pname) {
|
||||
case GL11.GL_TEXTURE_WIDTH -> info.getWidth();
|
||||
case GL11.GL_TEXTURE_HEIGHT -> info.getHeight();
|
||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> info.getInternalFormat();
|
||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> {
|
||||
if (info.needsInternalFormatResolve() && isRecordingDisplayList()) {
|
||||
throw new IllegalStateException(String.format("glGetTexLevelParameteri(GL_TEXTURE_INTERNAL_FORMAT) needs to resolve a generic compressed format for texture %d during display list recording", getBoundTextureForServerState()));
|
||||
}
|
||||
yield info.getResolvedInternalFormat();
|
||||
}
|
||||
default -> {
|
||||
if (isRecordingDisplayList()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
@@ -3911,64 +3985,72 @@ public class GLStateManager {
|
||||
public static void glSamplerParameteri(int sampler, int pname, int param) { RENDER_BACKEND.samplerParameteri(sampler, pname, param); }
|
||||
public static void glSamplerParameterf(int sampler, int pname, float param) { RENDER_BACKEND.samplerParameterf(sampler, pname, param); }
|
||||
|
||||
private static void glMaterialFront(int pname, FloatBuffer params) {
|
||||
switch (pname) {
|
||||
private static boolean glMaterialFront(int pname, FloatBuffer params) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> frontMaterial.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> frontMaterial.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> frontMaterial.setSpecular(params);
|
||||
case GL11.GL_EMISSION -> frontMaterial.setEmission(params);
|
||||
case GL11.GL_SHININESS -> frontMaterial.setShininess(params);
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> {
|
||||
frontMaterial.setAmbient(params);
|
||||
frontMaterial.setDiffuse(params);
|
||||
final boolean a = frontMaterial.setAmbient(params);
|
||||
final boolean d = frontMaterial.setDiffuse(params);
|
||||
yield a || d;
|
||||
}
|
||||
case GL11.GL_COLOR_INDEXES -> frontMaterial.setColorIndexes(params);
|
||||
}
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static void glMaterialBack(int pname, FloatBuffer params) {
|
||||
switch (pname) {
|
||||
private static boolean glMaterialBack(int pname, FloatBuffer params) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> backMaterial.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> backMaterial.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> backMaterial.setSpecular(params);
|
||||
case GL11.GL_EMISSION -> backMaterial.setEmission(params);
|
||||
case GL11.GL_SHININESS -> backMaterial.setShininess(params);
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> {
|
||||
backMaterial.setAmbient(params);
|
||||
backMaterial.setDiffuse(params);
|
||||
final boolean a = backMaterial.setAmbient(params);
|
||||
final boolean d = backMaterial.setDiffuse(params);
|
||||
yield a || d;
|
||||
}
|
||||
case GL11.GL_COLOR_INDEXES -> backMaterial.setColorIndexes(params);
|
||||
}
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static void glMaterialFront(int pname, IntBuffer params) {
|
||||
switch (pname) {
|
||||
private static boolean glMaterialFront(int pname, IntBuffer params) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> frontMaterial.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> frontMaterial.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> frontMaterial.setSpecular(params);
|
||||
case GL11.GL_EMISSION -> frontMaterial.setEmission(params);
|
||||
case GL11.GL_SHININESS -> frontMaterial.setShininess(params);
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> {
|
||||
frontMaterial.setAmbient(params);
|
||||
frontMaterial.setDiffuse(params);
|
||||
final boolean a = frontMaterial.setAmbient(params);
|
||||
final boolean d = frontMaterial.setDiffuse(params);
|
||||
yield a || d;
|
||||
}
|
||||
case GL11.GL_COLOR_INDEXES -> frontMaterial.setColorIndexes(params);
|
||||
}
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static void glMaterialBack(int pname, IntBuffer params) {
|
||||
switch (pname) {
|
||||
private static boolean glMaterialBack(int pname, IntBuffer params) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_AMBIENT -> backMaterial.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> backMaterial.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> backMaterial.setSpecular(params);
|
||||
case GL11.GL_EMISSION -> backMaterial.setEmission(params);
|
||||
case GL11.GL_SHININESS -> backMaterial.setShininess(params);
|
||||
case GL11.GL_AMBIENT_AND_DIFFUSE -> {
|
||||
backMaterial.setAmbient(params);
|
||||
backMaterial.setDiffuse(params);
|
||||
final boolean a = backMaterial.setAmbient(params);
|
||||
final boolean d = backMaterial.setDiffuse(params);
|
||||
yield a || d;
|
||||
}
|
||||
case GL11.GL_COLOR_INDEXES -> backMaterial.setColorIndexes(params);
|
||||
}
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
public static void glMaterial(int face, int pname, FloatBuffer params) {
|
||||
@@ -3979,17 +4061,19 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
boolean changed = false;
|
||||
if (face == GL11.GL_FRONT) {
|
||||
glMaterialFront(pname, params);
|
||||
changed = glMaterialFront(pname, params);
|
||||
} else if (face == GL11.GL_BACK) {
|
||||
glMaterialBack(pname, params);
|
||||
changed = glMaterialBack(pname, params);
|
||||
} else if (face == GL11.GL_FRONT_AND_BACK) {
|
||||
glMaterialFront(pname, params);
|
||||
glMaterialBack(pname, params);
|
||||
final boolean f = glMaterialFront(pname, params);
|
||||
final boolean b = glMaterialBack(pname, params);
|
||||
changed = f || b;
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported face value for glMaterial: " + face);
|
||||
}
|
||||
lightingGeneration++;
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glMaterial(int face, int pname, IntBuffer params) {
|
||||
@@ -4008,17 +4092,19 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
boolean changed = false;
|
||||
if (face == GL11.GL_FRONT) {
|
||||
glMaterialFront(pname, params);
|
||||
changed = glMaterialFront(pname, params);
|
||||
} else if (face == GL11.GL_BACK) {
|
||||
glMaterialBack(pname, params);
|
||||
changed = glMaterialBack(pname, params);
|
||||
} else if (face == GL11.GL_FRONT_AND_BACK) {
|
||||
glMaterialFront(pname, params);
|
||||
glMaterialBack(pname, params);
|
||||
final boolean f = glMaterialFront(pname, params);
|
||||
final boolean b = glMaterialBack(pname, params);
|
||||
changed = f || b;
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported face value for glMaterial: " + face);
|
||||
}
|
||||
lightingGeneration++;
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glMaterialf(int face, int pname, float val) {
|
||||
@@ -4034,17 +4120,19 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean changed = false;
|
||||
if (face == GL11.GL_FRONT) {
|
||||
frontMaterial.setShininess(val);
|
||||
changed = frontMaterial.setShininess(val);
|
||||
} else if (face == GL11.GL_BACK) {
|
||||
backMaterial.setShininess(val);
|
||||
changed = backMaterial.setShininess(val);
|
||||
} else if (face == GL11.GL_FRONT_AND_BACK) {
|
||||
frontMaterial.setShininess(val);
|
||||
backMaterial.setShininess(val);
|
||||
final boolean f = frontMaterial.setShininess(val);
|
||||
final boolean b = backMaterial.setShininess(val);
|
||||
changed = f || b;
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported face value for glMaterial: " + face);
|
||||
}
|
||||
lightingGeneration++;
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glMateriali(int face, int pname, int val) {
|
||||
@@ -4062,7 +4150,7 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
final LightStateStack lightState = lightDataStates[light - GL11.GL_LIGHT0];
|
||||
switch (pname) {
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_AMBIENT -> lightState.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> lightState.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> lightState.setSpecular(params);
|
||||
@@ -4073,9 +4161,9 @@ public class GLStateManager {
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> lightState.setConstantAttenuation(params);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> lightState.setLinearAttenuation(params);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> lightState.setQuadraticAttenuation(params);
|
||||
default -> {}
|
||||
}
|
||||
lightingGeneration++;
|
||||
default -> false;
|
||||
};
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLight(int light, int pname, IntBuffer params) {
|
||||
@@ -4095,7 +4183,7 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
final LightStateStack lightState = lightDataStates[light - GL11.GL_LIGHT0];
|
||||
switch (pname) {
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_AMBIENT -> lightState.setAmbient(params);
|
||||
case GL11.GL_DIFFUSE -> lightState.setDiffuse(params);
|
||||
case GL11.GL_SPECULAR -> lightState.setSpecular(params);
|
||||
@@ -4106,9 +4194,9 @@ public class GLStateManager {
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> lightState.setConstantAttenuation(params);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> lightState.setLinearAttenuation(params);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> lightState.setQuadraticAttenuation(params);
|
||||
default -> {}
|
||||
}
|
||||
lightingGeneration++;
|
||||
default -> false;
|
||||
};
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLightf(int light, int pname, float param) {
|
||||
@@ -4120,15 +4208,15 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
final LightStateStack lightState = lightDataStates[light - GL11.GL_LIGHT0];
|
||||
switch (pname) {
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_SPOT_EXPONENT -> lightState.setSpotExponent(param);
|
||||
case GL11.GL_SPOT_CUTOFF -> lightState.setSpotCutoff(param);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> lightState.setConstantAttenuation(param);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> lightState.setLinearAttenuation(param);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> lightState.setQuadraticAttenuation(param);
|
||||
default -> {}
|
||||
}
|
||||
lightingGeneration++;
|
||||
default -> false;
|
||||
};
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLighti(int light, int pname, int param) {
|
||||
@@ -4140,16 +4228,15 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
final LightStateStack lightState = lightDataStates[light - GL11.GL_LIGHT0];
|
||||
switch (pname) {
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_SPOT_EXPONENT -> lightState.setSpotExponent(param);
|
||||
case GL11.GL_SPOT_CUTOFF -> lightState.setSpotCutoff(param);
|
||||
case GL11.GL_CONSTANT_ATTENUATION -> lightState.setConstantAttenuation(param);
|
||||
case GL11.GL_LINEAR_ATTENUATION -> lightState.setLinearAttenuation(param);
|
||||
case GL11.GL_QUADRATIC_ATTENUATION -> lightState.setQuadraticAttenuation(param);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
lightingGeneration++;
|
||||
default -> false;
|
||||
};
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLightModel(int pname, FloatBuffer params) {
|
||||
@@ -4160,14 +4247,13 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch (pname) {
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_LIGHT_MODEL_AMBIENT -> lightModel.setAmbient(params);
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> lightModel.setLocalViewer(params);
|
||||
case GL11.GL_LIGHT_MODEL_TWO_SIDE -> lightModel.setTwoSide(params);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
lightingGeneration++;
|
||||
default -> false;
|
||||
};
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLightModel(int pname, IntBuffer params) {
|
||||
@@ -4186,15 +4272,14 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch (pname) {
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_LIGHT_MODEL_AMBIENT -> lightModel.setAmbient(params);
|
||||
case GL12.GL_LIGHT_MODEL_COLOR_CONTROL -> lightModel.setColorControl(params);
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> lightModel.setLocalViewer(params);
|
||||
case GL11.GL_LIGHT_MODEL_TWO_SIDE -> lightModel.setTwoSide(params);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
lightingGeneration++;
|
||||
default -> false;
|
||||
};
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
|
||||
public static void glLightModelf(int pname, float param) {
|
||||
@@ -4207,13 +4292,12 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
if (isCachingEnabled()) {
|
||||
switch (pname) {
|
||||
final boolean changed = switch (pname) {
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> lightModel.setLocalViewer(param);
|
||||
case GL11.GL_LIGHT_MODEL_TWO_SIDE -> lightModel.setTwoSide(param);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
lightingGeneration++;
|
||||
default -> false;
|
||||
};
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4227,14 +4311,13 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
if (isCachingEnabled()) {
|
||||
switch (pname) {
|
||||
final boolean changed = switch (pname) {
|
||||
case GL12.GL_LIGHT_MODEL_COLOR_CONTROL -> lightModel.setColorControl(param);
|
||||
case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> lightModel.setLocalViewer(param);
|
||||
case GL11.GL_LIGHT_MODEL_TWO_SIDE -> lightModel.setTwoSide(param);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
lightingGeneration++;
|
||||
default -> false;
|
||||
};
|
||||
if (changed) lightingGeneration++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4309,7 +4392,6 @@ public class GLStateManager {
|
||||
GLDebug.debugMessage("Activating Program - " + program + ":" + programName);
|
||||
}
|
||||
RENDER_BACKEND.useProgram(program);
|
||||
CompatUniformManager.onUseProgram(program);
|
||||
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
|
||||
GLSMHooks.programChangeEvent.previousProgram = prev;
|
||||
GLSMHooks.programChangeEvent.newProgram = program;
|
||||
@@ -4391,8 +4473,8 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static int glGetError() {
|
||||
if (!RENDER_BACKEND.hasContext()) return 0;
|
||||
if (initConfig.noErrorChecks()) return 0;
|
||||
if (!RENDER_BACKEND.hasContext()) return 0;
|
||||
return RENDER_BACKEND.getError();
|
||||
}
|
||||
|
||||
@@ -4964,25 +5046,25 @@ public class GLStateManager {
|
||||
RENDER_BACKEND.bindBuffer(target, buffer);
|
||||
}
|
||||
|
||||
private static void suspendPixelUnpackBuffer() {
|
||||
static void suspendPixelUnpackBuffer() {
|
||||
if (boundPixelUnpackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void restorePixelUnpackBuffer() {
|
||||
static void restorePixelUnpackBuffer() {
|
||||
if (boundPixelUnpackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, boundPixelUnpackBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void suspendPixelPackBuffer() {
|
||||
static void suspendPixelPackBuffer() {
|
||||
if (boundPixelPackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void restorePixelPackBuffer() {
|
||||
static void restorePixelPackBuffer() {
|
||||
if (boundPixelPackBuffer != 0) {
|
||||
RENDER_BACKEND.bindBuffer(GL21.GL_PIXEL_PACK_BUFFER, boundPixelPackBuffer);
|
||||
}
|
||||
@@ -5146,8 +5228,12 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
setLightmapTextureCoords(target, s, t);
|
||||
} else {
|
||||
final int unit = target - GL13.GL_TEXTURE0;
|
||||
if (unit >= 2 && unit < 4) {
|
||||
ShaderManager.setCurrentTexCoord(unit, s, t, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
// Units 2+ silently ignored — shader uses v_TexCoord0 for all non-lightmap units
|
||||
}
|
||||
|
||||
public static void glMultiTexCoord2d(int target, double s, double t) {
|
||||
@@ -5915,4 +6001,14 @@ public class GLStateManager {
|
||||
public static void glGetTexImage(int target, int level, int format, int type, long pixels) {
|
||||
GL11.glGetTexImage(target, level, format, type, pixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw modes that rely on the previous vertex data cannot be merged currently.
|
||||
* It is possible to merge them using Index Buffers, but currently unimplemented.
|
||||
*/
|
||||
public static boolean isContinuousDraw(int drawMode) {
|
||||
return drawMode == GL11.GL_TRIANGLE_STRIP || drawMode == GL11.GL_TRIANGLE_FAN
|
||||
|| drawMode == GL11.GL_LINE_STRIP || drawMode == GL11.GL_LINE_LOOP
|
||||
|| drawMode == GL11.GL_QUAD_STRIP || drawMode == GL11.GL_POLYGON;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
/**
|
||||
* Tessellator data needed by {@link com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer}.
|
||||
*/
|
||||
public interface ITessellatorData {
|
||||
|
||||
boolean isDrawing();
|
||||
void setDrawing(boolean drawing);
|
||||
int getVertexCount();
|
||||
int[] getRawBuffer();
|
||||
int getRawBufferIndex();
|
||||
int getRawBufferSize();
|
||||
void setRawBufferSize(int size);
|
||||
void setRawBuffer(int[] buffer);
|
||||
int getDrawMode();
|
||||
boolean hasTexture();
|
||||
boolean hasColor();
|
||||
boolean hasNormals();
|
||||
boolean hasBrightness();
|
||||
void angelica$reset();
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.gtnewhorizons.angelica.glsm.dsa.DSAUnsupported;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3i;
|
||||
@@ -48,6 +49,9 @@ public class RenderSystem {
|
||||
private static int maxSSBOBindings;
|
||||
private static int maxGlslVersion;
|
||||
private static boolean supportsGpuShader4;
|
||||
@Getter private static int glMajor;
|
||||
@Getter private static int glMinor;
|
||||
@Getter private static boolean coreProfile;
|
||||
|
||||
// Sampler object state tracking (null if unsupported)
|
||||
private static int[] samplers;
|
||||
@@ -140,10 +144,17 @@ public class RenderSystem {
|
||||
if (GLStateManager.capabilities.OpenGL32) {
|
||||
final int profileMask = RENDER_BACKEND.getInteger(GL32.GL_CONTEXT_PROFILE_MASK);
|
||||
if ((profileMask & GL32.GL_CONTEXT_CORE_PROFILE_BIT) != 0) {
|
||||
coreProfile = true;
|
||||
GLStateManager.LOGGER.info("GL 3.3 core profile detected, enabling FFP shader emulation.");
|
||||
ShaderManager.getInstance().enable();
|
||||
}
|
||||
}
|
||||
|
||||
final Matcher glVerMatcher = SEMVER_PATTERN.matcher(RENDER_BACKEND.getString(GL11.GL_VERSION));
|
||||
if (glVerMatcher.matches()) {
|
||||
glMajor = Integer.parseInt(glVerMatcher.group("major"));
|
||||
glMinor = Integer.parseInt(glVerMatcher.group("minor"));
|
||||
}
|
||||
}
|
||||
|
||||
public static void generateMipmaps(int texture, int mipmapTarget) {
|
||||
@@ -156,7 +167,9 @@ public class RenderSystem {
|
||||
|
||||
public static void texImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels) {
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
if (target == GL11.GL_TEXTURE_2D && level == 0) {
|
||||
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
|
||||
}
|
||||
@@ -225,29 +238,41 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void texImage1D(int texture, int target, int level, int internalformat, int width, int border, int format, int type, ByteBuffer pixels) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage1D(target, level, internalformat, width, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void texImage3D(int texture, int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static String getProgramInfoLog(int program) {
|
||||
@@ -271,7 +296,9 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void readPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels) {
|
||||
GLStateManager.suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelPackBuffer();
|
||||
}
|
||||
|
||||
public static void bufferData(int target, FloatBuffer data, int usage) {
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.MultMatrixCmd;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class for tracking and collapsing matrix transforms during optimization.
|
||||
* Implements the transform accumulation and emission strategy:
|
||||
* <ul>
|
||||
* <li>accumulated: total relative transform (what GL matrix should be)</li>
|
||||
* <li>lastEmitted: what we've emitted (what GL matrix is)</li>
|
||||
* <li>stack: saved accumulated values at Push points</li>
|
||||
* </ul>
|
||||
*/
|
||||
class TransformOptimizer {
|
||||
private final Matrix4f accumulated = new Matrix4f(); // Current accumulated transform
|
||||
private final Matrix4f lastEmitted = new Matrix4f(); // Last emitted transform
|
||||
private final Deque<Matrix4f> stack = new ArrayDeque<>(); // For Push/Pop
|
||||
private final int listId; // For logging
|
||||
private boolean absoluteMatrixFlag = false; // True after LoadMatrix, cleared by LoadIdentity emit
|
||||
|
||||
TransformOptimizer(int listId) {
|
||||
this.listId = listId;
|
||||
accumulated.identity();
|
||||
lastEmitted.identity();
|
||||
}
|
||||
|
||||
Matrix4f getAccumulated() {
|
||||
return accumulated;
|
||||
}
|
||||
|
||||
void loadIdentity() {
|
||||
accumulated.identity();
|
||||
}
|
||||
|
||||
void pushTransform() {
|
||||
stack.push(new Matrix4f(accumulated));
|
||||
}
|
||||
|
||||
void popTransform() {
|
||||
if (!stack.isEmpty()) {
|
||||
final Matrix4f popped = stack.pop();
|
||||
accumulated.set(popped);
|
||||
// After Pop, GL state is restored to the pushed value so lastEmitted should also reflect this
|
||||
lastEmitted.set(accumulated);
|
||||
} else {
|
||||
GLStateManager.LOGGER.warn("[TransformOptimizer] list={} Pop with empty stack - resetting to identity", listId);
|
||||
accumulated.identity();
|
||||
lastEmitted.identity();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isIdentity() {
|
||||
return DisplayListManager.isIdentity(accumulated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there's a pending transform that hasn't been emitted yet.
|
||||
*/
|
||||
boolean hasPendingTransform() {
|
||||
return !accumulated.equals(lastEmitted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a MultMatrix command if accumulated differs from lastEmitted.
|
||||
* Updates lastEmitted to match accumulated after emission.
|
||||
*/
|
||||
void emitPendingTransform(List<DisplayListCommand> output) {
|
||||
if (!accumulated.equals(lastEmitted)) {
|
||||
emitTransformTo(output, accumulated);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a MultMatrix command to make GL state match a target transform.
|
||||
* Used when a draw expects a specific transform that may differ from accumulated.
|
||||
* Updates lastEmitted to match target after emission.
|
||||
*
|
||||
* @param output The command list to emit to
|
||||
* @param target The target transform to reach
|
||||
*/
|
||||
void emitTransformTo(List<DisplayListCommand> output, Matrix4f target) {
|
||||
if (target.equals(lastEmitted)) {
|
||||
// Already at target - nothing to emit
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate delta: what we need to multiply to go from lastEmitted to target
|
||||
// delta = inverse(lastEmitted) * target
|
||||
// But for simplicity, if lastEmitted is identity, just emit target
|
||||
if (DisplayListManager.isIdentity(lastEmitted)) {
|
||||
// Simple case: just emit target
|
||||
output.add(MultMatrixCmd.create(target));
|
||||
} else {
|
||||
// Need to emit delta: inv(lastEmitted) * target
|
||||
final Matrix4f delta = new Matrix4f(lastEmitted).invert().mul(target);
|
||||
output.add(MultMatrixCmd.create(delta));
|
||||
}
|
||||
lastEmitted.set(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a draw's expected transform differs from what we've emitted.
|
||||
*/
|
||||
boolean needsTransformForDraw(Matrix4f drawTransform) {
|
||||
return !drawTransform.equals(lastEmitted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark that the GL matrix is at an absolute value (after LoadMatrix).
|
||||
* Subsequent LoadIdentity commands must be emitted to actually reset GL to identity.
|
||||
*/
|
||||
void markAbsoluteMatrix() {
|
||||
absoluteMatrixFlag = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the GL matrix is at an absolute value and clear the flag.
|
||||
* Used to determine if LoadIdentity needs to emit.
|
||||
* @return true if LoadMatrix was called and not yet reset by LoadIdentity
|
||||
*/
|
||||
boolean checkAndClearAbsoluteMatrix() {
|
||||
boolean was = absoluteMatrixFlag;
|
||||
absoluteMatrixFlag = false;
|
||||
return was;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Abstract rendering backend.
|
||||
@@ -25,6 +27,14 @@ public abstract class RenderBackend {
|
||||
/** Higher priority backends are preferred. */
|
||||
public int getPriority() { return 0; }
|
||||
|
||||
public boolean supportsFileDrop() { return false; }
|
||||
|
||||
public void startFileDrop() {}
|
||||
|
||||
public void stopFileDrop() {}
|
||||
|
||||
public List<String> pollDroppedFiles() { return Collections.emptyList(); }
|
||||
|
||||
public abstract int getMinGLSLVersion();
|
||||
|
||||
public abstract void flush();
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.lwjgl.opengl.GL13;
|
||||
* Packed-long fragment shader permutation key for FFP emulation.
|
||||
*
|
||||
* Layout:
|
||||
* long[0]: global bits (10) + unit 0 (47 bits at offset 10)
|
||||
* long[0]: global bits (11) + unit 0 (47 bits at offset 11)
|
||||
* long[1..3]: unit 1..3 (47 bits each, low-aligned)
|
||||
* Only max(1, nrEnabledUnits) longs are significant.
|
||||
*
|
||||
@@ -63,12 +63,13 @@ public final class FragmentKey {
|
||||
public static final int FOG_EXP = 2;
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
private static final int GLOBAL_BITS = 10;
|
||||
private static final int GLOBAL_BITS = 11;
|
||||
private static final int BIT_FOG_MODE = 0; // 2 bits
|
||||
private static final int BIT_ALPHA_TEST = 2; // 1 bit
|
||||
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
|
||||
private static final int BIT_SEPARATE_SPECULAR = 6; // 1 bit
|
||||
private static final int BIT_NR_ENABLED_UNITS = 7; // 3 bits
|
||||
private static final int BIT_OVERLAY_ENABLED = 10; // 1 bit
|
||||
|
||||
private static final int U_ENABLED = 0;
|
||||
private static final int U_MODE = 1; // 3 bits
|
||||
@@ -119,6 +120,11 @@ public final class FragmentKey {
|
||||
global |= ((long) (encodeAlphaFunc(GLStateManager.getAlphaState().getFunction()) & 0x7)) << BIT_ALPHA_FUNC;
|
||||
}
|
||||
|
||||
// Damage overlay
|
||||
if (GLStateManager.getOverlayA() != 0.0f) {
|
||||
global |= (1L << BIT_OVERLAY_ENABLED);
|
||||
}
|
||||
|
||||
// Separate specular
|
||||
if (GLStateManager.getLightingState().isEnabled()
|
||||
&& GLStateManager.getLightModel().colorControl == GL12.GL_SEPARATE_SPECULAR_COLOR) {
|
||||
@@ -227,6 +233,7 @@ public final class FragmentKey {
|
||||
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
|
||||
public boolean separateSpecular() { return ((packed[0] >> BIT_SEPARATE_SPECULAR) & 1) != 0; }
|
||||
public int nrEnabledUnits() { return (int) ((packed[0] >> BIT_NR_ENABLED_UNITS) & 0x7); }
|
||||
public boolean overlayEnabled() { return ((packed[0] >> BIT_OVERLAY_ENABLED) & 1) != 0; }
|
||||
|
||||
private long unitBits(int i) {
|
||||
return (i == 0) ? (packed[0] >>> GLOBAL_BITS) : packed[i];
|
||||
@@ -252,6 +259,28 @@ public final class FragmentKey {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Per-unit enable bits as a 4-bit mask (bit i = unit i enabled). */
|
||||
public int enabledUnitMask() {
|
||||
int mask = 0;
|
||||
final int n = Math.min(nrEnabledUnits(), MAX_UNITS);
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (unitEnabled(i)) mask |= (1 << i);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
/** Per-unit enable bits from a packed scratch as a 4-bit mask (bit i = unit i enabled). */
|
||||
public static int unitMaskFromPacked(long[] packed, int len) {
|
||||
int mask = 0;
|
||||
if (len < 1) return 0;
|
||||
if (((packed[0] >>> GLOBAL_BITS) & 1L) != 0) mask |= 1;
|
||||
final int n = Math.min(len, MAX_UNITS);
|
||||
for (int i = 1; i < n; i++) {
|
||||
if ((packed[i] & 1L) != 0) mask |= (1 << i);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
public boolean lightmapEnabled() {
|
||||
return nrEnabledUnits() > 1 && unitEnabled(1);
|
||||
}
|
||||
@@ -338,10 +367,10 @@ public final class FragmentKey {
|
||||
default -> "?";
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b units=%d",
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b overlay=%b units=%d",
|
||||
fogName, alphaTestEnabled(),
|
||||
alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-",
|
||||
separateSpecular(), nrEnabledUnits()));
|
||||
separateSpecular(), overlayEnabled(), nrEnabledUnits()));
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (!unitEnabled(i)) {
|
||||
sb.append(String.format(" u%d=OFF", i));
|
||||
|
||||
+27
-11
@@ -37,11 +37,11 @@ public final class FragmentShaderGenerator {
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("in vec3 v_SpecularColor;\n");
|
||||
}
|
||||
if (key.textureEnabled()) {
|
||||
sb.append("in vec4 v_TexCoord0;\n");
|
||||
}
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append("in vec4 v_TexCoord1;\n");
|
||||
// Per-unit texcoord inputs. Each enabled unit sources from its own varying
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (key.unitEnabled(i)) {
|
||||
sb.append("in vec4 v_TexCoord").append(i).append(";\n");
|
||||
}
|
||||
}
|
||||
if (key.fogMode() != FragmentKey.FOG_NONE) {
|
||||
sb.append("in float v_FogCoord;\n");
|
||||
@@ -65,6 +65,9 @@ public final class FragmentShaderGenerator {
|
||||
sb.append("uniform vec4 u_TexEnvColor").append(i).append(";\n");
|
||||
}
|
||||
}
|
||||
if (key.overlayEnabled()) {
|
||||
sb.append("uniform vec4 u_OverlayColor;\n");
|
||||
}
|
||||
if (key.fogMode() != FragmentKey.FOG_NONE) {
|
||||
sb.append("uniform vec4 u_FogParams;\n");
|
||||
sb.append("uniform vec4 u_FogColor;\n");
|
||||
@@ -75,11 +78,7 @@ public final class FragmentShaderGenerator {
|
||||
private static void emitTextureSampling(StringBuilder sb, FragmentKey key) {
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (!key.unitEnabled(i)) continue;
|
||||
// Unit 1 is the lightmap — uses v_TexCoord1.
|
||||
// Units 2-3 intentionally share unit 0's texture coordinates since
|
||||
// the vertex shader only provides 2 tex coord varyings (unit 0 and unit 1/lightmap).
|
||||
final String texCoord = (i == 1) ? "v_TexCoord1.st" : "v_TexCoord0.st";
|
||||
sb.append(" vec4 tex").append(i).append("Color = texture(u_Sampler").append(i).append(", ").append(texCoord).append(");\n");
|
||||
sb.append(" vec4 tex").append(i).append("Color = texture(u_Sampler").append(i).append(", v_TexCoord").append(i).append(".st);\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,14 +86,21 @@ public final class FragmentShaderGenerator {
|
||||
if (!key.textureEnabled()) {
|
||||
sb.append(" // No texture -- vertex color only\n");
|
||||
sb.append(" vec4 color = v_Color;\n");
|
||||
emitOverlay(sb, key);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process units in order; "previous" starts as vertex color
|
||||
// Process units in order; "previous" starts as vertex color.
|
||||
boolean firstUnit = true;
|
||||
boolean overlayInjected = false;
|
||||
for (int i = 0; i < key.nrEnabledUnits(); i++) {
|
||||
if (!key.unitEnabled(i)) continue;
|
||||
|
||||
if (!firstUnit && !overlayInjected) {
|
||||
emitOverlay(sb, key);
|
||||
overlayInjected = true;
|
||||
}
|
||||
|
||||
final String texVar = "tex" + i + "Color";
|
||||
final String envColorVar = "u_TexEnvColor" + i;
|
||||
final String prevVar = firstUnit ? "v_Color" : "color";
|
||||
@@ -107,6 +113,16 @@ public final class FragmentShaderGenerator {
|
||||
}
|
||||
firstUnit = false;
|
||||
}
|
||||
|
||||
if (!overlayInjected) emitOverlay(sb, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the modern-style damage overlay mix.
|
||||
*/
|
||||
private static void emitOverlay(StringBuilder sb, FragmentKey key) {
|
||||
if (!key.overlayEnabled()) return;
|
||||
sb.append(" color.rgb = mix(color.rgb, u_OverlayColor.rgb, u_OverlayColor.a);\n");
|
||||
}
|
||||
|
||||
private static void emitSimpleUnit(StringBuilder sb, FragmentKey key, int unit, String texVar, String envColorVar, String prevVar, String assign) {
|
||||
|
||||
+12
-18
@@ -22,12 +22,10 @@ public final class GeometryShaderGenerator {
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("in vec3 v_SpecularColor_gs[];\n");
|
||||
}
|
||||
if (key.textureEnabled() || key.hasVertexTexCoord() || key.texGenEnabled()) {
|
||||
sb.append("in vec4 v_TexCoord0_gs[];\n");
|
||||
}
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append("in vec4 v_TexCoord1_gs[];\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append("in vec4 v_TexCoord0_gs[];\n");
|
||||
if (key.lightmapEnabled()) sb.append("in vec4 v_TexCoord1_gs[];\n");
|
||||
if (key.unitTexCoordEnabled(2)) sb.append("in vec4 v_TexCoord2_gs[];\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append("in vec4 v_TexCoord3_gs[];\n");
|
||||
if (key.fogEnabled()) {
|
||||
sb.append("in float v_FogCoord_gs[];\n");
|
||||
}
|
||||
@@ -37,12 +35,10 @@ public final class GeometryShaderGenerator {
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("out vec3 v_SpecularColor;\n");
|
||||
}
|
||||
if (key.textureEnabled() || key.hasVertexTexCoord() || key.texGenEnabled()) {
|
||||
sb.append("out vec4 v_TexCoord0;\n");
|
||||
}
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append("out vec4 v_TexCoord1;\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append("out vec4 v_TexCoord0;\n");
|
||||
if (key.lightmapEnabled()) sb.append("out vec4 v_TexCoord1;\n");
|
||||
if (key.unitTexCoordEnabled(2)) sb.append("out vec4 v_TexCoord2;\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append("out vec4 v_TexCoord3;\n");
|
||||
if (key.fogEnabled()) {
|
||||
sb.append("out float v_FogCoord;\n");
|
||||
}
|
||||
@@ -82,12 +78,10 @@ public final class GeometryShaderGenerator {
|
||||
if (key.separateSpecular()) {
|
||||
sb.append(" v_SpecularColor = v_SpecularColor_gs").append(idx).append(";\n");
|
||||
}
|
||||
if (key.textureEnabled() || key.hasVertexTexCoord() || key.texGenEnabled()) {
|
||||
sb.append(" v_TexCoord0 = v_TexCoord0_gs").append(idx).append(";\n");
|
||||
}
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append(" v_TexCoord1 = v_TexCoord1_gs").append(idx).append(";\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append(" v_TexCoord0 = v_TexCoord0_gs").append(idx).append(";\n");
|
||||
if (key.lightmapEnabled()) sb.append(" v_TexCoord1 = v_TexCoord1_gs").append(idx).append(";\n");
|
||||
if (key.unitTexCoordEnabled(2)) sb.append(" v_TexCoord2 = v_TexCoord2_gs").append(idx).append(";\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append(" v_TexCoord3 = v_TexCoord3_gs").append(idx).append(";\n");
|
||||
if (key.fogEnabled()) {
|
||||
sb.append(" v_FogCoord = v_FogCoord_gs").append(idx).append(";\n");
|
||||
}
|
||||
|
||||
@@ -26,18 +26,28 @@ public class Program {
|
||||
@Getter private final VertexKey vertexKey;
|
||||
@Getter private final FragmentKey fragmentKey;
|
||||
|
||||
// Per-program uniform dirty-tracking
|
||||
static final class UploadState {
|
||||
int mvGen = -1, mvLinearGen = -1, projGen = -1, texMatGen = -1;
|
||||
int lightingGen = -1, fragmentGen = -1, colorGen = -1, normalGen = -1, texCoordGen = -1;
|
||||
int texGenGen = -1, clipPlaneGen = -1;
|
||||
float lightmapX = Float.NaN, lightmapY = Float.NaN, lineWidth = Float.NaN;
|
||||
int viewportWidth = -1, viewportHeight = -1;
|
||||
}
|
||||
final UploadState uploadState = new UploadState();
|
||||
|
||||
// Uniform locations (-1 = not present in this variant)
|
||||
// Matrices
|
||||
public int locModelViewMatrix = -1;
|
||||
public int locProjectionMatrix = -1;
|
||||
public int locMVPMatrix = -1;
|
||||
public int locNormalMatrix = -1;
|
||||
public int locTextureMatrix0 = -1;
|
||||
public final int[] locTextureMatrix = { -1, -1, -1, -1 };
|
||||
|
||||
// Current state defaults
|
||||
public int locCurrentNormal = -1;
|
||||
public int locCurrentColor = -1;
|
||||
public int locCurrentTexCoord = -1;
|
||||
public final int[] locCurrentTexCoord = { -1, -1, -1, -1 };
|
||||
public int locCurrentLightmapCoord = -1;
|
||||
public int locLightmapTextureMatrix = -1;
|
||||
|
||||
@@ -92,6 +102,7 @@ public class Program {
|
||||
public final int[] locSampler = { -1, -1, -1, -1 };
|
||||
public int locAlphaRef = -1;
|
||||
public final int[] locTexEnvColor = { -1, -1, -1, -1 };
|
||||
public int locOverlayColor = -1;
|
||||
public int locFogParams = -1;
|
||||
public int locFogColor = -1;
|
||||
|
||||
@@ -107,11 +118,13 @@ public class Program {
|
||||
locProjectionMatrix = loc("u_ProjectionMatrix");
|
||||
locMVPMatrix = loc("u_MVPMatrix");
|
||||
locNormalMatrix = loc("u_NormalMatrix");
|
||||
locTextureMatrix0 = loc("u_TextureMatrix0");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
locTextureMatrix[i] = loc("u_TextureMatrix" + i);
|
||||
locCurrentTexCoord[i] = loc("u_CurrentTexCoord" + i);
|
||||
}
|
||||
|
||||
locCurrentNormal = loc("u_CurrentNormal");
|
||||
locCurrentColor = loc("u_CurrentColor");
|
||||
locCurrentTexCoord = loc("u_CurrentTexCoord");
|
||||
locCurrentLightmapCoord = loc("u_CurrentLightmapCoord");
|
||||
locLightmapTextureMatrix = loc("u_LightmapTextureMatrix");
|
||||
|
||||
@@ -169,6 +182,7 @@ public class Program {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
locTexEnvColor[i] = loc("u_TexEnvColor" + i);
|
||||
}
|
||||
locOverlayColor = loc("u_OverlayColor");
|
||||
locFogParams = loc("u_FogParams");
|
||||
locFogColor = loc("u_FogColor");
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACK
|
||||
/**
|
||||
* FFP shader manager
|
||||
*/
|
||||
public class ShaderManager {
|
||||
public final class ShaderManager {
|
||||
|
||||
private static final class Holder {
|
||||
|
||||
@@ -36,17 +36,26 @@ public class ShaderManager {
|
||||
@Getter
|
||||
private boolean active = false;
|
||||
private Program currentProgram = null;
|
||||
private int lastBoundProgramId = -1;
|
||||
private long currentVertexKeyPacked = Long.MIN_VALUE;
|
||||
private final long[] currentFKScratch = new long[FragmentKey.MAX_UNITS];
|
||||
private final long[] currentFKPacked = new long[FragmentKey.MAX_UNITS];
|
||||
private int currentFKLen = 0;
|
||||
|
||||
@Getter private static final Vector3f currentNormal = new Vector3f(0.0f, 0.0f, 1.0f);
|
||||
@Getter private static final Vector4f currentTexCoord = new Vector4f(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
private static final Vector4f[] currentTexCoords = {
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
};
|
||||
@Getter private static final Vec3fStack normalStack = new Vec3fStack(currentNormal);
|
||||
@Getter private static final Vec4fStack texCoordStack = new Vec4fStack(currentTexCoord);
|
||||
@Getter private static final Vec4fStack texCoordStack = new Vec4fStack(currentTexCoords[0]);
|
||||
@Getter private static int normalGeneration;
|
||||
@Getter private static int texCoordGeneration;
|
||||
|
||||
public static Vector4f getCurrentTexCoord() { return currentTexCoords[0]; }
|
||||
public static Vector4f getCurrentTexCoord(int unit) { return currentTexCoords[unit]; }
|
||||
@Getter private boolean enabled = false;
|
||||
private int currentVertexFlags = VertexFlags.TEXTURE_BIT | VertexFlags.COLOR_BIT | VertexFlags.NORMAL_BIT;
|
||||
|
||||
@@ -79,37 +88,37 @@ public class ShaderManager {
|
||||
|
||||
public void activate() {
|
||||
active = true;
|
||||
updateVariant(true, true, true, true);
|
||||
uploadUniforms();
|
||||
lastBoundProgramId = GLStateManager.getActiveProgram();
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
active = false;
|
||||
currentProgram = null;
|
||||
lastBoundProgramId = -1;
|
||||
currentVertexKeyPacked = Long.MIN_VALUE;
|
||||
currentFKLen = 0;
|
||||
}
|
||||
|
||||
public void preDraw(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
|
||||
GLStateManager.flushDeferredVertexAttribs();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null) bh.flushDeferredBlend();
|
||||
|
||||
if (!active) {
|
||||
if (enabled) {
|
||||
final int currentProgramId = GLStateManager.getActiveProgram();
|
||||
if (currentProgramId != 0) {
|
||||
CompatUniformManager.onUseProgram(currentProgramId);
|
||||
return;
|
||||
}
|
||||
active = true;
|
||||
} else {
|
||||
return;
|
||||
// Handle FFP & Iris uniforms
|
||||
final int currentProgramId = GLStateManager.getActiveProgram();
|
||||
if (currentProgramId != 0) {
|
||||
if (!CompatUniformManager.refreshCompatUniforms(currentProgramId)) {
|
||||
return; // Don't emulate FFP on non-iris core shaders
|
||||
}
|
||||
}
|
||||
|
||||
final long vkPacked = VertexKey.packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap);
|
||||
GLStateManager.flushDeferredVertexAttribs(currentVertexFlags);
|
||||
|
||||
if (!active) return;
|
||||
|
||||
|
||||
final int fkLen = FragmentKey.packFromState(currentFKScratch);
|
||||
final int fragMask = FragmentKey.unitMaskFromPacked(currentFKScratch, fkLen);
|
||||
final long vkPacked = VertexKey.packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragMask);
|
||||
|
||||
if (vkPacked != currentVertexKeyPacked || !Arrays.equals(currentFKScratch, 0, fkLen, currentFKPacked, 0, currentFKLen)) {
|
||||
commitVariant(vkPacked, fkLen);
|
||||
@@ -132,18 +141,16 @@ public class ShaderManager {
|
||||
preDraw(currentVertexFlags);
|
||||
}
|
||||
|
||||
private void updateVariant(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
|
||||
final long vkPacked = VertexKey.packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap);
|
||||
final int fkLen = FragmentKey.packFromState(currentFKScratch);
|
||||
commitVariant(vkPacked, fkLen);
|
||||
}
|
||||
|
||||
private void commitVariant(long vkPacked, int fkLen) {
|
||||
currentVertexKeyPacked = vkPacked;
|
||||
System.arraycopy(currentFKScratch, 0, currentFKPacked, 0, fkLen);
|
||||
currentFKLen = fkLen;
|
||||
currentProgram = cache.getOrCreate(vkPacked, currentFKPacked, currentFKLen);
|
||||
RENDER_BACKEND.useProgram(currentProgram.getProgramId());
|
||||
final int programId = currentProgram.getProgramId();
|
||||
if (programId != lastBoundProgramId) {
|
||||
RENDER_BACKEND.useProgram(programId);
|
||||
lastBoundProgramId = programId;
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadUniforms() {
|
||||
@@ -158,7 +165,12 @@ public class ShaderManager {
|
||||
}
|
||||
|
||||
public static void setCurrentTexCoord(float s, float t, float r, float q) {
|
||||
currentTexCoord.set(s, t, r, q);
|
||||
currentTexCoords[0].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
}
|
||||
|
||||
public static void setCurrentTexCoord(int unit, float s, float t, float r, float q) {
|
||||
currentTexCoords[unit].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexGenState;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import org.joml.Math;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector4f;
|
||||
@@ -42,119 +43,102 @@ public class Uniforms {
|
||||
private final org.joml.Vector3f tempVec3 = new org.joml.Vector3f();
|
||||
|
||||
// Pre-computed constants for fog params (Mesa STATE_FOG_PARAMS_OPTIMIZED)
|
||||
private static final double LN2 = Math.log(2.0);
|
||||
private static final double SQRT_LN2 = Math.sqrt(LN2);
|
||||
|
||||
// Dirty tracking: last-uploaded generation per category + program ID.
|
||||
// Program change forces full re-upload since uniform locations differ.
|
||||
private int lastProgramId = -1;
|
||||
private int lastMvGen = -1;
|
||||
private int lastProjGen = -1;
|
||||
private int lastTexMatGen = -1;
|
||||
private int lastLightingGen = -1;
|
||||
private int lastFragmentGen = -1;
|
||||
private int lastColorGen = -1;
|
||||
private int lastNormalGen = -1;
|
||||
private int lastTexCoordGen = -1;
|
||||
private float lastLightmapX = Float.NaN;
|
||||
private float lastLightmapY = Float.NaN;
|
||||
private int lastTexGenGen = -1;
|
||||
private int lastClipPlaneGen = -1;
|
||||
private float lastLineWidth = Float.NaN;
|
||||
private int lastViewportWidth = -1;
|
||||
private int lastViewportHeight = -1;
|
||||
private static final double LN2 = java.lang.Math.log(2.0);
|
||||
private static final double SQRT_LN2 = java.lang.Math.sqrt(LN2);
|
||||
|
||||
/**
|
||||
* Upload all relevant uniforms to the given FFP program based on current GLSM state.
|
||||
* Uses generation counters to skip categories that haven't changed since last upload.
|
||||
*/
|
||||
public void upload(Program program) {
|
||||
final boolean programChanged = program.getProgramId() != lastProgramId;
|
||||
lastProgramId = program.getProgramId();
|
||||
final Program.UploadState st = program.uploadState;
|
||||
|
||||
final int mvGen = GLStateManager.mvGeneration;
|
||||
final int mvLinearGen = GLStateManager.mvLinearGeneration;
|
||||
final int projGen = GLStateManager.projGeneration;
|
||||
final int texMatGen = GLStateManager.texMatrixGeneration;
|
||||
final boolean mvChanged = programChanged || mvGen != lastMvGen;
|
||||
final boolean projChanged = programChanged || projGen != lastProjGen;
|
||||
final boolean texMatChanged = programChanged || texMatGen != lastTexMatGen;
|
||||
final boolean mvChanged = mvGen != st.mvGen;
|
||||
final boolean mvLinearChanged = mvLinearGen != st.mvLinearGen;
|
||||
final boolean projChanged = projGen != st.projGen;
|
||||
final boolean texMatChanged = texMatGen != st.texMatGen;
|
||||
if (mvChanged || projChanged || texMatChanged) {
|
||||
uploadMatrices(program, mvChanged, projChanged, texMatChanged);
|
||||
lastMvGen = mvGen;
|
||||
lastProjGen = projGen;
|
||||
lastTexMatGen = texMatGen;
|
||||
uploadMatrices(program, mvChanged, mvLinearChanged, projChanged, texMatChanged);
|
||||
st.mvGen = mvGen;
|
||||
st.mvLinearGen = mvLinearGen;
|
||||
st.projGen = projGen;
|
||||
st.texMatGen = texMatGen;
|
||||
}
|
||||
|
||||
if (program.getVertexKey().lightingEnabled()) {
|
||||
final int litGen = GLStateManager.lightingGeneration;
|
||||
if (programChanged || litGen != lastLightingGen) {
|
||||
if (litGen != st.lightingGen) {
|
||||
uploadLighting(program);
|
||||
lastLightingGen = litGen;
|
||||
st.lightingGen = litGen;
|
||||
}
|
||||
}
|
||||
|
||||
// Current color/normal/texcoord — skip if generation unchanged
|
||||
if (!program.getVertexKey().hasVertexColor()) {
|
||||
final int colorGen = GLStateManager.colorGeneration;
|
||||
if (programChanged || colorGen != lastColorGen) {
|
||||
if (colorGen != st.colorGen) {
|
||||
uploadCurrentColor(program);
|
||||
lastColorGen = colorGen;
|
||||
st.colorGen = colorGen;
|
||||
}
|
||||
}
|
||||
|
||||
if (!program.getVertexKey().hasVertexNormal() && program.getVertexKey().lightingEnabled()) {
|
||||
final int normalGen = ShaderManager.getNormalGeneration();
|
||||
if (programChanged || normalGen != lastNormalGen) {
|
||||
if (normalGen != st.normalGen) {
|
||||
uploadCurrentNormal(program);
|
||||
lastNormalGen = normalGen;
|
||||
st.normalGen = normalGen;
|
||||
}
|
||||
}
|
||||
|
||||
if (!program.getVertexKey().hasVertexTexCoord() && program.getVertexKey().textureEnabled()) {
|
||||
if (program.getVertexKey().anyUnitTexCoordEnabled()) {
|
||||
final int texGen = ShaderManager.getTexCoordGeneration();
|
||||
if (programChanged || texGen != lastTexCoordGen) {
|
||||
uploadCurrentTexCoord(program);
|
||||
lastTexCoordGen = texGen;
|
||||
if (texGen != st.texCoordGen) {
|
||||
uploadCurrentTexCoords(program);
|
||||
st.texCoordGen = texGen;
|
||||
}
|
||||
}
|
||||
|
||||
if (program.getVertexKey().lightmapEnabled() && !program.getVertexKey().hasVertexLightmap()) {
|
||||
if (programChanged || GLSMConfig.lastBrightnessX != lastLightmapX || GLSMConfig.lastBrightnessY != lastLightmapY) {
|
||||
if (GLSMConfig.lastBrightnessX != st.lightmapX || GLSMConfig.lastBrightnessY != st.lightmapY) {
|
||||
uploadCurrentLightmapCoord(program);
|
||||
lastLightmapX = GLSMConfig.lastBrightnessX;
|
||||
lastLightmapY = GLSMConfig.lastBrightnessY;
|
||||
st.lightmapX = GLSMConfig.lastBrightnessX;
|
||||
st.lightmapY = GLSMConfig.lastBrightnessY;
|
||||
}
|
||||
}
|
||||
|
||||
if (program.getVertexKey().texGenEnabled()) {
|
||||
final int tgGen = GLStateManager.texGenGeneration;
|
||||
if (programChanged || tgGen != lastTexGenGen) {
|
||||
if (tgGen != st.texGenGen) {
|
||||
uploadTexGen(program);
|
||||
lastTexGenGen = tgGen;
|
||||
st.texGenGen = tgGen;
|
||||
}
|
||||
}
|
||||
|
||||
if (program.getVertexKey().clipPlanesEnabled()) {
|
||||
final int cpGen = GLStateManager.clipPlaneGeneration;
|
||||
if (programChanged || cpGen != lastClipPlaneGen) {
|
||||
if (cpGen != st.clipPlaneGen) {
|
||||
uploadClipPlanes(program);
|
||||
lastClipPlaneGen = cpGen;
|
||||
st.clipPlaneGen = cpGen;
|
||||
}
|
||||
}
|
||||
|
||||
final int fragGen = GLStateManager.fragmentGeneration;
|
||||
if (programChanged || fragGen != lastFragmentGen) {
|
||||
if (fragGen != st.fragmentGen) {
|
||||
uploadFragmentUniforms(program);
|
||||
lastFragmentGen = fragGen;
|
||||
st.fragmentGen = fragGen;
|
||||
}
|
||||
|
||||
// Wide line emulation uniforms
|
||||
if (program.locLineWidth != -1 && program.locViewportSize != -1) {
|
||||
uploadWideLineUniforms(program, programChanged);
|
||||
uploadWideLineUniforms(program);
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadMatrices(Program program, boolean mvChanged, boolean projChanged, boolean texMatChanged) {
|
||||
private void uploadMatrices(Program program, boolean mvChanged, boolean mvLinearChanged, boolean projChanged, boolean texMatChanged) {
|
||||
final Matrix4f mv = GLStateManager.getModelViewMatrix();
|
||||
final Matrix4f proj = GLStateManager.getProjectionMatrix();
|
||||
|
||||
@@ -164,7 +148,9 @@ public class Uniforms {
|
||||
mv.get(mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(program.locModelViewMatrix, false, mat4Buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (mvLinearChanged) {
|
||||
// Normal matrix = inverse transpose of upper-left 3x3 of ModelView
|
||||
if (program.locNormalMatrix != -1 || program.locNormalScale != -1) {
|
||||
mv.normal(normalMatrix);
|
||||
@@ -197,16 +183,15 @@ public class Uniforms {
|
||||
RENDER_BACKEND.uniformMatrix4(program.locMVPMatrix, false, mat4Buf);
|
||||
}
|
||||
|
||||
// Texture matrices
|
||||
// Texture matrices (per-unit).
|
||||
if (texMatChanged) {
|
||||
// Texture matrix unit 0
|
||||
if (program.locTextureMatrix0 != -1) {
|
||||
final Matrix4f texMat = GLStateManager.getTextures().getTextureUnitMatrix(0);
|
||||
texMat.get(mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(program.locTextureMatrix0, false, mat4Buf);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (program.locTextureMatrix[i] != -1) {
|
||||
final Matrix4f texMat = GLStateManager.getTextures().getTextureUnitMatrix(i);
|
||||
texMat.get(mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(program.locTextureMatrix[i], false, mat4Buf);
|
||||
}
|
||||
}
|
||||
|
||||
// Texture matrix unit 1 (lightmap)
|
||||
if (program.locLightmapTextureMatrix != -1) {
|
||||
final Matrix4f lmTexMat = GLStateManager.getTextures().getTextureUnitMatrix(1);
|
||||
lmTexMat.get(mat4Buf);
|
||||
@@ -314,19 +299,22 @@ public class Uniforms {
|
||||
if (program.locCurrentColor == -1) return;
|
||||
// Upload the current color from GLSM
|
||||
final var color = GLStateManager.getColor();
|
||||
tempVec4.set(
|
||||
Math.clamp(0f, 1f, color.getRed()),
|
||||
Math.clamp(0f, 1f, color.getGreen()),
|
||||
Math.clamp(0f, 1f, color.getBlue()),
|
||||
Math.clamp(0f, 1f, color.getAlpha()));
|
||||
vec4Buf.clear();
|
||||
vec4Buf.put(color.getRed());
|
||||
vec4Buf.put(color.getGreen());
|
||||
vec4Buf.put(color.getBlue());
|
||||
vec4Buf.put(color.getAlpha());
|
||||
vec4Buf.flip();
|
||||
tempVec4.get(vec4Buf);
|
||||
RENDER_BACKEND.uniform4(program.locCurrentColor, vec4Buf);
|
||||
}
|
||||
|
||||
private void uploadCurrentTexCoord(Program program) {
|
||||
if (program.locCurrentTexCoord == -1) return;
|
||||
final var tc = ShaderManager.getCurrentTexCoord();
|
||||
uploadVec4(program.locCurrentTexCoord, tc);
|
||||
private void uploadCurrentTexCoords(Program program) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (program.locCurrentTexCoord[i] != -1) {
|
||||
uploadVec4(program.locCurrentTexCoord[i], ShaderManager.getCurrentTexCoord(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadCurrentLightmapCoord(Program program) {
|
||||
@@ -340,10 +328,7 @@ public class Uniforms {
|
||||
// Upload current normal from FFPShaderManager's tracked normal
|
||||
final var normal = ShaderManager.getCurrentNormal();
|
||||
vec3Buf.clear();
|
||||
vec3Buf.put(normal.x);
|
||||
vec3Buf.put(normal.y);
|
||||
vec3Buf.put(normal.z);
|
||||
vec3Buf.flip();
|
||||
normal.get(vec3Buf);
|
||||
RENDER_BACKEND.uniform3(program.locCurrentNormal, vec3Buf);
|
||||
}
|
||||
|
||||
@@ -379,10 +364,7 @@ public class Uniforms {
|
||||
private void uploadPlane(int loc, float[] plane) {
|
||||
if (loc == -1) return;
|
||||
vec4Buf.clear();
|
||||
vec4Buf.put(plane[0]);
|
||||
vec4Buf.put(plane[1]);
|
||||
vec4Buf.put(plane[2]);
|
||||
vec4Buf.put(plane[3]);
|
||||
vec4Buf.put(plane, 0, 4);
|
||||
vec4Buf.flip();
|
||||
RENDER_BACKEND.uniform4(loc, vec4Buf);
|
||||
}
|
||||
@@ -420,6 +402,16 @@ public class Uniforms {
|
||||
}
|
||||
}
|
||||
|
||||
if (fk.overlayEnabled() && program.locOverlayColor != -1) {
|
||||
vec4Buf.clear();
|
||||
vec4Buf.put(GLStateManager.getOverlayR());
|
||||
vec4Buf.put(GLStateManager.getOverlayG());
|
||||
vec4Buf.put(GLStateManager.getOverlayB());
|
||||
vec4Buf.put(GLStateManager.getOverlayA());
|
||||
vec4Buf.flip();
|
||||
RENDER_BACKEND.uniform4(program.locOverlayColor, vec4Buf);
|
||||
}
|
||||
|
||||
// Fog
|
||||
if (fk.fogMode() != FragmentKey.FOG_NONE) {
|
||||
uploadFog(program);
|
||||
@@ -458,29 +450,26 @@ public class Uniforms {
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadWideLineUniforms(Program program, boolean programChanged) {
|
||||
private void uploadWideLineUniforms(Program program) {
|
||||
final Program.UploadState st = program.uploadState;
|
||||
final float lineWidth = GLStateManager.getLineState().getWidth();
|
||||
if (programChanged || lineWidth != lastLineWidth) {
|
||||
if (lineWidth != st.lineWidth) {
|
||||
RENDER_BACKEND.uniform1f(program.locLineWidth, lineWidth);
|
||||
lastLineWidth = lineWidth;
|
||||
st.lineWidth = lineWidth;
|
||||
}
|
||||
final int vw = GLStateManager.getViewportState().width;
|
||||
final int vh = GLStateManager.getViewportState().height;
|
||||
if (programChanged || vw != lastViewportWidth || vh != lastViewportHeight) {
|
||||
if (vw != st.viewportWidth || vh != st.viewportHeight) {
|
||||
RENDER_BACKEND.uniform2f(program.locViewportSize, vw, vh);
|
||||
lastViewportWidth = vw;
|
||||
lastViewportHeight = vh;
|
||||
st.viewportWidth = vw;
|
||||
st.viewportHeight = vh;
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadVec4(int loc, Vector4f v) {
|
||||
if (loc == -1) return;
|
||||
vec4Buf.clear();
|
||||
vec4Buf.put(v.x);
|
||||
vec4Buf.put(v.y);
|
||||
vec4Buf.put(v.z);
|
||||
vec4Buf.put(v.w);
|
||||
vec4Buf.flip();
|
||||
v.get(vec4Buf);
|
||||
RENDER_BACKEND.uniform4(loc, vec4Buf);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.MatrixHelper;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TexGenState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
@@ -10,9 +12,14 @@ import org.lwjgl.opengl.GL12;
|
||||
/**
|
||||
* Compact key capturing the vertex shader permutation axes for FFP emulation.
|
||||
* Packed into a single long for fast hash/compare.
|
||||
*
|
||||
* Per-unit fields are 4-bit base-indexed: bit (BASE + i) for unit i in [0..3].
|
||||
* Unit 1 is the lightmap; texcoord enable for unit 1 also gates the lightmap-specific code paths.
|
||||
*/
|
||||
public final class VertexKey {
|
||||
|
||||
public static final int MAX_UNITS = 4;
|
||||
|
||||
private final long packed;
|
||||
|
||||
private static final int BIT_LIGHTING = 0;
|
||||
@@ -23,26 +30,25 @@ public final class VertexKey {
|
||||
private static final int BIT_COLOR_MATERIAL = 5;
|
||||
private static final int BIT_SEPARATE_SPECULAR = 6;
|
||||
private static final int BIT_FOG = 7;
|
||||
private static final int BIT_FOG_DIST_MODE = 8;
|
||||
private static final int BIT_FOG_DIST_MODE = 8; // 2 bits: 8-9
|
||||
private static final int BIT_NORMALIZE = 10;
|
||||
private static final int BIT_RESCALE_NORMAL = 11;
|
||||
private static final int BIT_TEXTURE = 12;
|
||||
private static final int BIT_LIGHTMAP = 13;
|
||||
private static final int BIT_TEX_MATRIX = 14;
|
||||
private static final int BIT_HAS_VERTEX_COLOR = 15;
|
||||
private static final int BIT_HAS_VERTEX_NORMAL = 16;
|
||||
private static final int BIT_HAS_VERTEX_TEX = 17;
|
||||
private static final int BIT_HAS_VERTEX_LIGHTMAP = 18;
|
||||
private static final int BIT_COLOR_MAT_MODE = 19;
|
||||
// bits 19-21: colorMaterialMode (3 bits)
|
||||
// TexGen modes: 3 bits each (0=NONE, 1=OBJ_LINEAR, 2=EYE_LINEAR)
|
||||
private static final int BIT_TEXGEN_S = 22;
|
||||
private static final int BIT_TEXGEN_T = 25;
|
||||
private static final int BIT_TEXGEN_R = 28;
|
||||
private static final int BIT_TEXGEN_Q = 31;
|
||||
// bits 31-33: texGenModeQ (3 bits)
|
||||
private static final int BIT_CLIP_PLANES = 34;
|
||||
private static final int BIT_WIDE_LINE = 35;
|
||||
// Per-unit texcoord enable: bits 12-15 (unit 0..3). Unit 1 doubles as the lightmap flag.
|
||||
private static final int BIT_UNIT_TEX_BASE = 12;
|
||||
private static final int BIT_HAS_VERTEX_COLOR = 16;
|
||||
private static final int BIT_HAS_VERTEX_NORMAL = 17;
|
||||
private static final int BIT_HAS_VERTEX_TEX = 18;
|
||||
private static final int BIT_HAS_VERTEX_LIGHTMAP = 19;
|
||||
private static final int BIT_COLOR_MAT_MODE = 20; // 3 bits: 20-22
|
||||
// TexGen modes (unit 0 only, for now): 3 bits each (0=NONE, 1=OBJ_LINEAR, 2=EYE_LINEAR).
|
||||
private static final int BIT_TEXGEN_S = 23; // 3 bits: 23-25
|
||||
private static final int BIT_TEXGEN_T = 26; // 3 bits: 26-28
|
||||
private static final int BIT_TEXGEN_R = 29; // 3 bits: 29-31
|
||||
private static final int BIT_TEXGEN_Q = 32; // 3 bits: 32-34
|
||||
private static final int BIT_CLIP_PLANES = 35;
|
||||
private static final int BIT_WIDE_LINE = 36;
|
||||
// Per-unit texmat enable: bits 37-40 (unit 0..3). Unit 1 is the lightmap matrix.
|
||||
private static final int BIT_UNIT_TEXMAT_BASE = 37;
|
||||
|
||||
public static final int TG_NONE = 0;
|
||||
public static final int TG_OBJ_LINEAR = 1;
|
||||
@@ -71,9 +77,10 @@ public final class VertexKey {
|
||||
public int fogDistanceMode() { return (int)((packed >> BIT_FOG_DIST_MODE) & 0x3); }
|
||||
public boolean normalizeEnabled() { return bit(BIT_NORMALIZE); }
|
||||
public boolean rescaleNormalsEnabled() { return bit(BIT_RESCALE_NORMAL); }
|
||||
public boolean textureEnabled() { return bit(BIT_TEXTURE); }
|
||||
public boolean lightmapEnabled() { return bit(BIT_LIGHTMAP); }
|
||||
public boolean textureMatrixEnabled() { return bit(BIT_TEX_MATRIX); }
|
||||
public boolean unitTexCoordEnabled(int unit) { return bit(BIT_UNIT_TEX_BASE + unit); }
|
||||
public boolean unitTexMatEnabled(int unit) { return bit(BIT_UNIT_TEXMAT_BASE + unit); }
|
||||
public boolean anyUnitTexCoordEnabled() { return ((packed >> BIT_UNIT_TEX_BASE) & 0xF) != 0; }
|
||||
public boolean lightmapEnabled() { return unitTexCoordEnabled(1); }
|
||||
public boolean hasVertexColor() { return bit(BIT_HAS_VERTEX_COLOR); }
|
||||
public boolean hasVertexNormal() { return bit(BIT_HAS_VERTEX_NORMAL); }
|
||||
public boolean hasVertexTexCoord() { return bit(BIT_HAS_VERTEX_TEX); }
|
||||
@@ -81,28 +88,18 @@ public final class VertexKey {
|
||||
/** Color material mode (CM_AMBIENT, CM_DIFFUSE, CM_SPECULAR, CM_EMISSION, CM_AMBIENT_AND_DIFFUSE). Only meaningful when colorMaterialEnabled(). */
|
||||
public int colorMaterialMode() { return (int)((packed >> BIT_COLOR_MAT_MODE) & 0x7); }
|
||||
|
||||
/** TexGen mode for S coordinate (TG_NONE, TG_OBJ_LINEAR, TG_EYE_LINEAR). */
|
||||
/** TexGen mode for S coordinate (TG_NONE, TG_OBJ_LINEAR, TG_EYE_LINEAR). Unit 0 only; see TODO in packFromState. */
|
||||
public int texGenModeS() { return (int)((packed >> BIT_TEXGEN_S) & 0x7); }
|
||||
/** TexGen mode for T coordinate. */
|
||||
public int texGenModeT() { return (int)((packed >> BIT_TEXGEN_T) & 0x7); }
|
||||
/** TexGen mode for R coordinate. */
|
||||
public int texGenModeR() { return (int)((packed >> BIT_TEXGEN_R) & 0x7); }
|
||||
/** TexGen mode for Q coordinate. */
|
||||
public int texGenModeQ() { return (int)((packed >> BIT_TEXGEN_Q) & 0x7); }
|
||||
/** Whether any texgen coordinate is enabled. */
|
||||
public boolean texGenEnabled() { return texGenModeS() != TG_NONE || texGenModeT() != TG_NONE || texGenModeR() != TG_NONE || texGenModeQ() != TG_NONE; }
|
||||
/** Whether any clip plane is enabled. */
|
||||
public boolean clipPlanesEnabled() { return bit(BIT_CLIP_PLANES); }
|
||||
/** Whether wide line GS emulation is active for this variant. */
|
||||
public boolean wideLineEmulation() { return bit(BIT_WIDE_LINE); }
|
||||
|
||||
/** Whether vertex color replaces material ambient in this variant. */
|
||||
public boolean cmReplacesAmbient() { final int m = colorMaterialMode(); return m == CM_AMBIENT || m == CM_AMBIENT_AND_DIFFUSE; }
|
||||
/** Whether vertex color replaces material diffuse in this variant. */
|
||||
public boolean cmReplacesDiffuse() { final int m = colorMaterialMode(); return m == CM_DIFFUSE || m == CM_AMBIENT_AND_DIFFUSE; }
|
||||
/** Whether vertex color replaces material specular in this variant. */
|
||||
public boolean cmReplacesSpecular() { return colorMaterialMode() == CM_SPECULAR; }
|
||||
/** Whether vertex color replaces material emission in this variant. */
|
||||
public boolean cmReplacesEmission() { return colorMaterialMode() == CM_EMISSION; }
|
||||
|
||||
private boolean bit(int pos) { return ((packed >> pos) & 1) != 0; }
|
||||
@@ -126,7 +123,7 @@ public final class VertexKey {
|
||||
};
|
||||
}
|
||||
|
||||
public static long packFromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
|
||||
public static long packFromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap, int fragUnitMask) {
|
||||
long bits = 0;
|
||||
|
||||
final boolean lighting = GLStateManager.getLightingState().isEnabled();
|
||||
@@ -171,23 +168,18 @@ public final class VertexKey {
|
||||
bits |= (1L << BIT_RESCALE_NORMAL);
|
||||
}
|
||||
|
||||
// Texture unit 0 (base texture)
|
||||
if (GLStateManager.getTextures().getTextureUnitStates(0).isEnabled()) {
|
||||
bits |= (1L << BIT_TEXTURE);
|
||||
}
|
||||
// Texture unit 1 (lightmap)
|
||||
if (GLStateManager.getTextures().getTextureUnitStates(1).isEnabled()) {
|
||||
bits |= (1L << BIT_LIGHTMAP);
|
||||
final TextureUnitArray texUnit = GLStateManager.getTextures();
|
||||
for (int i = 0; i < MAX_UNITS; i++) {
|
||||
if ((fragUnitMask & (1 << i)) != 0) {
|
||||
bits |= (1L << (BIT_UNIT_TEX_BASE + i));
|
||||
}
|
||||
if (!MatrixHelper.isIdentity(texUnit.getTextureUnitMatrix(i))) {
|
||||
bits |= (1L << (BIT_UNIT_TEXMAT_BASE + i));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if texture matrix unit 0 is non-identity
|
||||
final Matrix4f texMat = GLStateManager.getTextures().getTextureUnitMatrix(0);
|
||||
if (!DisplayListManager.isIdentity(texMat)) {
|
||||
bits |= (1L << BIT_TEX_MATRIX);
|
||||
}
|
||||
|
||||
// TexGen (unit 0 only) — encode per-coordinate mode if enabled
|
||||
final var texUnit = GLStateManager.getTextures();
|
||||
// TexGen (unit 0 only) - per-coordinate mode if enabled.
|
||||
// TODO: per-unit texgen for units 2/3. Deferred - no MC code path uses glTexGen on those.
|
||||
final TexGenState tg = texUnit.getTexGenState(0);
|
||||
boolean anyTexGen = false;
|
||||
if (texUnit.getTexGenSStates(0).isEnabled() && tg.getMode(GL11.GL_S) != 0) {
|
||||
@@ -210,9 +202,9 @@ public final class VertexKey {
|
||||
bits |= ((long) m & 0x7) << BIT_TEXGEN_Q;
|
||||
if (m != TG_NONE) anyTexGen = true;
|
||||
}
|
||||
// Force texture matrix on when texgen is active (end portal pattern always uses it)
|
||||
// Force unit-0 texture matrix on when texgen is active (end portal pattern always uses it).
|
||||
if (anyTexGen) {
|
||||
bits |= (1L << BIT_TEX_MATRIX);
|
||||
bits |= (1L << BIT_UNIT_TEXMAT_BASE);
|
||||
}
|
||||
|
||||
// Vertex format flags
|
||||
@@ -221,12 +213,10 @@ public final class VertexKey {
|
||||
if (hasTexCoord) bits |= (1L << BIT_HAS_VERTEX_TEX);
|
||||
if (hasLightmap) bits |= (1L << BIT_HAS_VERTEX_LIGHTMAP);
|
||||
|
||||
// Clip planes
|
||||
if (GLStateManager.anyClipPlaneEnabled()) {
|
||||
bits |= (1L << BIT_CLIP_PLANES);
|
||||
}
|
||||
|
||||
// Wide line GS emulation
|
||||
if (GLStateManager.wideLineEmulationActive) {
|
||||
bits |= (1L << BIT_WIDE_LINE);
|
||||
}
|
||||
@@ -234,8 +224,8 @@ public final class VertexKey {
|
||||
return bits;
|
||||
}
|
||||
|
||||
public static VertexKey fromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap) {
|
||||
return new VertexKey(packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap));
|
||||
public static VertexKey fromState(boolean hasColor, boolean hasNormal, boolean hasTexCoord, boolean hasLightmap, int fragUnitMask) {
|
||||
return new VertexKey(packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragUnitMask));
|
||||
}
|
||||
|
||||
static VertexKey fromPacked(long packed) {
|
||||
@@ -256,9 +246,11 @@ public final class VertexKey {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("FFPVertexKey[0x%09X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%b lm=%b col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b wline=%b]",
|
||||
return String.format("FFPVertexKey[0x%011X: lit=%b l0=%b l1=%b cm=%b fog=%b tex=%d%d%d%d texmat=%d%d%d%d col=%b nrm=%b vtex=%b vlm=%b tg=%d/%d/%d/%d clip=%b wline=%b]",
|
||||
packed, lightingEnabled(), light0Enabled(), light1Enabled(),
|
||||
colorMaterialEnabled(), fogEnabled(), textureEnabled(), lightmapEnabled(),
|
||||
colorMaterialEnabled(), fogEnabled(),
|
||||
unitTexCoordEnabled(0)?1:0, unitTexCoordEnabled(1)?1:0, unitTexCoordEnabled(2)?1:0, unitTexCoordEnabled(3)?1:0,
|
||||
unitTexMatEnabled(0)?1:0, unitTexMatEnabled(1)?1:0, unitTexMatEnabled(2)?1:0, unitTexMatEnabled(3)?1:0,
|
||||
hasVertexColor(), hasVertexNormal(), hasVertexTexCoord(), hasVertexLightmap(),
|
||||
texGenModeS(), texGenModeT(), texGenModeR(), texGenModeQ(), clipPlanesEnabled(), wideLineEmulation());
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ public final class VertexShaderGenerator {
|
||||
source = source.replace("v_SpecularColor", "v_SpecularColor_gs")
|
||||
.replace("v_TexCoord0", "v_TexCoord0_gs")
|
||||
.replace("v_TexCoord1", "v_TexCoord1_gs")
|
||||
.replace("v_TexCoord2", "v_TexCoord2_gs")
|
||||
.replace("v_TexCoord3", "v_TexCoord3_gs")
|
||||
.replace("v_FogCoord", "v_FogCoord_gs")
|
||||
.replace("v_Color", "v_Color_gs");
|
||||
}
|
||||
@@ -76,11 +78,14 @@ public final class VertexShaderGenerator {
|
||||
sb.append("uniform mat3 u_NormalMatrix;\n");
|
||||
}
|
||||
|
||||
if (key.textureMatrixEnabled()) {
|
||||
sb.append("uniform mat4 u_TextureMatrix0;\n");
|
||||
for (int i = 0; i < VertexKey.MAX_UNITS; i++) {
|
||||
if (i == 1) continue;
|
||||
if (key.unitTexMatEnabled(i)) {
|
||||
sb.append("uniform mat4 u_TextureMatrix").append(i).append(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
// TexGen plane uniforms
|
||||
// TexGen plane uniforms (unit 0 only - TODO: per-unit)
|
||||
if (key.texGenModeS() == VertexKey.TG_OBJ_LINEAR) sb.append("uniform vec4 u_TexGenObjPlaneS;\n");
|
||||
if (key.texGenModeS() == VertexKey.TG_EYE_LINEAR) sb.append("uniform vec4 u_TexGenEyePlaneS;\n");
|
||||
if (key.texGenModeT() == VertexKey.TG_OBJ_LINEAR) sb.append("uniform vec4 u_TexGenObjPlaneT;\n");
|
||||
@@ -98,9 +103,11 @@ public final class VertexShaderGenerator {
|
||||
sb.append("uniform vec4 u_CurrentColor;\n");
|
||||
}
|
||||
|
||||
if (!key.hasVertexTexCoord() && key.textureEnabled()) {
|
||||
sb.append("uniform vec4 u_CurrentTexCoord;\n");
|
||||
if (key.unitTexCoordEnabled(0) && !key.hasVertexTexCoord()) {
|
||||
sb.append("uniform vec4 u_CurrentTexCoord0;\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(2)) sb.append("uniform vec4 u_CurrentTexCoord2;\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append("uniform vec4 u_CurrentTexCoord3;\n");
|
||||
|
||||
if (key.lightmapEnabled() && !key.hasVertexLightmap()) {
|
||||
sb.append("uniform vec2 u_CurrentLightmapCoord;\n");
|
||||
@@ -178,12 +185,15 @@ public final class VertexShaderGenerator {
|
||||
if (key.separateSpecular()) {
|
||||
sb.append("out vec3 v_SpecularColor;\n");
|
||||
}
|
||||
if (key.textureEnabled() || key.hasVertexTexCoord() || key.texGenEnabled()) {
|
||||
// Per-unit varying outputs. Each enabled texture unit (and texgen, which targets unit 0) gets its own v_TexCoord<i>.
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) {
|
||||
sb.append("out vec4 v_TexCoord0;\n");
|
||||
}
|
||||
if (key.lightmapEnabled()) {
|
||||
sb.append("out vec4 v_TexCoord1;\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(2)) sb.append("out vec4 v_TexCoord2;\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append("out vec4 v_TexCoord3;\n");
|
||||
if (key.fogEnabled()) {
|
||||
sb.append("out float v_FogCoord;\n");
|
||||
}
|
||||
@@ -331,22 +341,18 @@ public final class VertexShaderGenerator {
|
||||
private static void emitTexCoordPassthrough(StringBuilder sb, VertexKey key) {
|
||||
if (key.texGenEnabled()) {
|
||||
emitTexGenCoordGeneration(sb, key);
|
||||
} else if (key.textureEnabled() || key.hasVertexTexCoord()) {
|
||||
sb.append(" // Texture coordinates\n");
|
||||
if (key.hasVertexTexCoord()) {
|
||||
if (key.textureMatrixEnabled()) {
|
||||
sb.append(" v_TexCoord0 = u_TextureMatrix0 * vec4(a_TexCoord0, 0.0, 1.0);\n");
|
||||
} else {
|
||||
sb.append(" v_TexCoord0 = vec4(a_TexCoord0, 0.0, 1.0);\n");
|
||||
}
|
||||
} else if (key.unitTexCoordEnabled(0)) {
|
||||
sb.append(" // Texture coordinates - unit 0\n");
|
||||
final String src = key.hasVertexTexCoord()
|
||||
? "vec4(a_TexCoord0, 0.0, 1.0)"
|
||||
: "u_CurrentTexCoord0";
|
||||
if (key.unitTexMatEnabled(0)) {
|
||||
sb.append(" v_TexCoord0 = u_TextureMatrix0 * ").append(src).append(";\n");
|
||||
} else {
|
||||
if (key.textureMatrixEnabled()) {
|
||||
sb.append(" v_TexCoord0 = u_TextureMatrix0 * u_CurrentTexCoord;\n");
|
||||
} else {
|
||||
sb.append(" v_TexCoord0 = u_CurrentTexCoord;\n");
|
||||
}
|
||||
sb.append(" v_TexCoord0 = ").append(src).append(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (key.lightmapEnabled()) {
|
||||
if (key.hasVertexLightmap()) {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(a_TexCoord1, 0.0, 1.0);\n");
|
||||
@@ -354,6 +360,16 @@ public final class VertexShaderGenerator {
|
||||
sb.append(" v_TexCoord1 = u_LightmapTextureMatrix * vec4(u_CurrentLightmapCoord, 0.0, 1.0);\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 2; i < VertexKey.MAX_UNITS; i++) {
|
||||
if (!key.unitTexCoordEnabled(i)) continue;
|
||||
final String src = "u_CurrentTexCoord" + i;
|
||||
if (key.unitTexMatEnabled(i)) {
|
||||
sb.append(" v_TexCoord").append(i).append(" = u_TextureMatrix").append(i).append(" * ").append(src).append(";\n");
|
||||
} else {
|
||||
sb.append(" v_TexCoord").append(i).append(" = ").append(src).append(";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,6 +18,7 @@ public final class GLSMHooks {
|
||||
public static final EventBus<FogStateChangeEvent> FOG_STATE_CHANGE = EventBus.create(FogStateChangeEvent.class);
|
||||
public static final EventBus<LightmapCoordsEvent> LIGHTMAP_COORDS = EventBus.create(LightmapCoordsEvent.class);
|
||||
public static final EventBus<AlphaStateChangeEvent> ALPHA_STATE_CHANGE = EventBus.create(AlphaStateChangeEvent.class);
|
||||
public static final EventBus<ShaderColorChangeEvent> SHADER_COLOR_CHANGE = EventBus.create(ShaderColorChangeEvent.class);
|
||||
|
||||
// Reusable event instances
|
||||
public static final TextureBindEvent textureBindEvent = new TextureBindEvent();
|
||||
@@ -28,6 +29,7 @@ public final class GLSMHooks {
|
||||
public static final FogStateChangeEvent fogStateChangeEvent = new FogStateChangeEvent();
|
||||
public static final LightmapCoordsEvent lightmapCoordsEvent = new LightmapCoordsEvent();
|
||||
public static final AlphaStateChangeEvent alphaStateChangeEvent = new AlphaStateChangeEvent();
|
||||
public static final ShaderColorChangeEvent shaderColorChangeEvent = new ShaderColorChangeEvent();
|
||||
|
||||
private GLSMHooks() {}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class ShaderColorChangeEvent extends MutableEvent {
|
||||
public float red;
|
||||
public float green;
|
||||
public float blue;
|
||||
public float alpha;
|
||||
}
|
||||
@@ -35,11 +35,30 @@ public final class EcosystemNarrowRules {
|
||||
"com.fiskmods.lightsabers.asm.transformers"
|
||||
)),
|
||||
|
||||
new NarrowRule("FiskHeroes", "com.fiskmods.heroes.asm", List.of(
|
||||
"com.fiskmods.heroes.asm.SHLoadingPlugin",
|
||||
"com.fiskmods.heroes.asm.transformers"
|
||||
)),
|
||||
|
||||
new NarrowRule("Ears", "com.unascribed.ears", List.of(
|
||||
"com.unascribed.ears.asm",
|
||||
"com.unascribed.ears.common.agent"
|
||||
)),
|
||||
|
||||
new NarrowRule("FoamFix", "pl.asie.foamfix", List.of(
|
||||
"pl.asie.foamfix.bugfixmod",
|
||||
"pl.asie.foamfix.coremod",
|
||||
"pl.asie.foamfix.forkage",
|
||||
"pl.asie.foamfix.ghostbuster",
|
||||
"pl.asie.foamfix.FoamFixMod",
|
||||
"pl.asie.foamfix.HackyMessageFactoryWrapper",
|
||||
"pl.asie.foamfix.LaunchWrapperRuntimeFix",
|
||||
"pl.asie.foamfix.ProxyClient",
|
||||
"pl.asie.foamfix.ProxyCommon",
|
||||
"pl.asie.foamfix.repack.com.unascribed.ears.asm",
|
||||
"pl.asie.foamfix.repack.com.unascribed.ears.common.agent"
|
||||
)),
|
||||
|
||||
new NarrowRule("Alfheim", "alfheim.common.core.asm.hook", List.of(
|
||||
"alfheim.common.core.asm.hook.AlfheimFieldHookHandler",
|
||||
"alfheim.common.core.asm.hook.AlfheimHPHooks",
|
||||
|
||||
@@ -16,17 +16,13 @@ public final class AccumulatedDraw {
|
||||
public final VertexFormat format;
|
||||
public final List<ByteBuffer> drawBuffers;
|
||||
public final int drawMode;
|
||||
public final int commandIndex; // Position in command list for state tracking
|
||||
public final int stateGeneration; // Position in command list for state tracking
|
||||
public RestoreData restoreData;
|
||||
|
||||
public AccumulatedDraw(DirectTessellator tessellator, int commandIndex, int stateGeneration, boolean copyLast) {
|
||||
public AccumulatedDraw(DirectTessellator tessellator, boolean copyLast) {
|
||||
this.format = tessellator.getVertexFormat();
|
||||
this.drawMode = tessellator.drawMode;
|
||||
this.drawBuffers = new ArrayList<>();
|
||||
this.drawBuffers.add(tessellator.allocateBufferCopy());
|
||||
this.commandIndex = commandIndex;
|
||||
this.stateGeneration = stateGeneration;
|
||||
|
||||
if (copyLast) {
|
||||
this.restoreData = new RestoreData(tessellator);
|
||||
@@ -36,7 +32,6 @@ public final class AccumulatedDraw {
|
||||
|
||||
public void mergeDraw(DirectTessellator tessellator, boolean copyLast) {
|
||||
this.drawBuffers.add(tessellator.allocateBufferCopy());
|
||||
|
||||
if (copyLast) {
|
||||
this.restoreData = new RestoreData(tessellator);
|
||||
}
|
||||
|
||||
@@ -1,767 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
|
||||
public final class CommandBuffer {
|
||||
private static final int DEFAULT_CAPACITY = 4096; // Guess at typical display list size
|
||||
|
||||
private ByteBuffer buffer;
|
||||
private long basePointer;
|
||||
private long writePointer;
|
||||
private final List<Object> complexObjects = new ArrayList<>();
|
||||
|
||||
public CommandBuffer() {
|
||||
this(DEFAULT_CAPACITY);
|
||||
}
|
||||
|
||||
public CommandBuffer(int initialCapacity) {
|
||||
this.buffer = memAlloc(initialCapacity);
|
||||
this.basePointer = memAddress(buffer);
|
||||
this.writePointer = basePointer;
|
||||
}
|
||||
|
||||
// === Low-level write operations ===
|
||||
|
||||
private void writeInt(int value) {
|
||||
memPutInt(writePointer, value);
|
||||
writePointer += 4;
|
||||
}
|
||||
|
||||
private void writeLong(long value) {
|
||||
memPutLong(writePointer, value);
|
||||
writePointer += 8;
|
||||
}
|
||||
|
||||
private void writeFloat(float value) {
|
||||
memPutFloat(writePointer, value);
|
||||
writePointer += 4;
|
||||
}
|
||||
|
||||
private void writeDouble(double value) {
|
||||
memPutDouble(writePointer, value);
|
||||
writePointer += 8;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int needed) {
|
||||
final int size = size();
|
||||
final int remaining = buffer.capacity() - size;
|
||||
if (remaining < needed) {
|
||||
final int newCapacity = Math.max(buffer.capacity() * 2, buffer.capacity() + needed);
|
||||
buffer = memRealloc(buffer, newCapacity);
|
||||
basePointer = memAddress(buffer);
|
||||
writePointer = basePointer + size;
|
||||
}
|
||||
}
|
||||
|
||||
// === Single int commands ===
|
||||
|
||||
public void writeEnable(int cap) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.ENABLE);
|
||||
writeInt(cap);
|
||||
}
|
||||
|
||||
public void writeDisable(int cap) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DISABLE);
|
||||
writeInt(cap);
|
||||
}
|
||||
|
||||
public void writeClear(int mask) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CLEAR);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writeClearStencil(int s) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CLEAR_STENCIL);
|
||||
writeInt(s);
|
||||
}
|
||||
|
||||
public void writeCullFace(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CULL_FACE);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void writeDepthFunc(int func) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DEPTH_FUNC);
|
||||
writeInt(func);
|
||||
}
|
||||
|
||||
public void writeShadeModel(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.SHADE_MODEL);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void writeLogicOp(int opcode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.LOGIC_OP);
|
||||
writeInt(opcode);
|
||||
}
|
||||
|
||||
public void writeMatrixMode(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.MATRIX_MODE);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void writeActiveTexture(int texture) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.ACTIVE_TEXTURE);
|
||||
writeInt(texture);
|
||||
}
|
||||
|
||||
public void writeUseProgram(int program) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.USE_PROGRAM);
|
||||
writeInt(program);
|
||||
}
|
||||
|
||||
public void writePushAttrib(int mask) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.PUSH_ATTRIB);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writePopAttrib() {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.POP_ATTRIB);
|
||||
writeInt(0); // unused padding
|
||||
}
|
||||
|
||||
public void writeLoadIdentity() {
|
||||
ensureCapacity(4);
|
||||
writeInt(GLCommand.LOAD_IDENTITY);
|
||||
}
|
||||
|
||||
public void writePushMatrix() {
|
||||
ensureCapacity(4);
|
||||
writeInt(GLCommand.PUSH_MATRIX);
|
||||
}
|
||||
|
||||
public void writePopMatrix() {
|
||||
ensureCapacity(4);
|
||||
writeInt(GLCommand.POP_MATRIX);
|
||||
}
|
||||
|
||||
public void writeStencilMask(int mask) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.STENCIL_MASK);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writeDepthMask(boolean flag) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DEPTH_MASK);
|
||||
writeInt(flag ? 1 : 0);
|
||||
}
|
||||
|
||||
public void writeFrontFace(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.FRONT_FACE);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
// === Two int commands ===
|
||||
|
||||
public void writeBindTexture(int target, int texture) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.BIND_TEXTURE);
|
||||
writeInt(target);
|
||||
writeInt(texture);
|
||||
}
|
||||
|
||||
public void writePolygonMode(int face, int mode) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.POLYGON_MODE);
|
||||
writeInt(face);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void writeColorMaterial(int face, int mode) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.COLOR_MATERIAL);
|
||||
writeInt(face);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void writeLineStipple(int factor, int pattern) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.LINE_STIPPLE);
|
||||
writeInt(factor);
|
||||
writeInt(pattern);
|
||||
}
|
||||
|
||||
public void writeStencilMaskSeparate(int face, int mask) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.STENCIL_MASK_SEPARATE);
|
||||
writeInt(face);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writeFogi(int pname, int param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.FOGI);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
public void writeHint(int target, int mode) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.HINT);
|
||||
writeInt(target);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
// === Three int commands ===
|
||||
|
||||
public void writeStencilFunc(int func, int ref, int mask) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.STENCIL_FUNC);
|
||||
writeInt(func);
|
||||
writeInt(ref);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writeStencilOp(int fail, int zfail, int zpass) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.STENCIL_OP);
|
||||
writeInt(fail);
|
||||
writeInt(zfail);
|
||||
writeInt(zpass);
|
||||
}
|
||||
|
||||
public void writeTexParameteri(int target, int pname, int param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.TEX_PARAMETERI);
|
||||
writeInt(target);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
// === Four int commands ===
|
||||
|
||||
public void writeViewport(int x, int y, int width, int height) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.VIEWPORT);
|
||||
writeInt(x);
|
||||
writeInt(y);
|
||||
writeInt(width);
|
||||
writeInt(height);
|
||||
}
|
||||
|
||||
public void writeScissor(int x, int y, int width, int height) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.SCISSOR);
|
||||
writeInt(x);
|
||||
writeInt(y);
|
||||
writeInt(width);
|
||||
writeInt(height);
|
||||
}
|
||||
|
||||
public void writeBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.BLEND_FUNC);
|
||||
writeInt(srcRgb);
|
||||
writeInt(dstRgb);
|
||||
writeInt(srcAlpha);
|
||||
writeInt(dstAlpha);
|
||||
}
|
||||
|
||||
public void writeColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.COLOR_MASK);
|
||||
writeInt(red ? 1 : 0);
|
||||
writeInt(green ? 1 : 0);
|
||||
writeInt(blue ? 1 : 0);
|
||||
writeInt(alpha ? 1 : 0);
|
||||
}
|
||||
|
||||
public void writeStencilFuncSeparate(int face, int func, int ref, int mask) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.STENCIL_FUNC_SEPARATE);
|
||||
writeInt(face);
|
||||
writeInt(func);
|
||||
writeInt(ref);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void writeStencilOpSeparate(int face, int sfail, int dpfail, int dppass) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.STENCIL_OP_SEPARATE);
|
||||
writeInt(face);
|
||||
writeInt(sfail);
|
||||
writeInt(dpfail);
|
||||
writeInt(dppass);
|
||||
}
|
||||
|
||||
// === Float commands ===
|
||||
|
||||
public void writePointSize(float size) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.POINT_SIZE);
|
||||
writeFloat(size);
|
||||
}
|
||||
|
||||
public void writeLineWidth(float width) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.LINE_WIDTH);
|
||||
writeFloat(width);
|
||||
}
|
||||
|
||||
public void writePolygonOffset(float factor, float units) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.POLYGON_OFFSET);
|
||||
writeFloat(factor);
|
||||
writeFloat(units);
|
||||
}
|
||||
|
||||
public void writeColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
public void writeClearColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.CLEAR_COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
public void writeClearDepth(double depth) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.CLEAR_DEPTH);
|
||||
writeDouble(depth);
|
||||
}
|
||||
|
||||
public void writeBlendColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.BLEND_COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
// === Mixed int+float commands ===
|
||||
|
||||
public void writeAlphaFunc(int func, float ref) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.ALPHA_FUNC);
|
||||
writeInt(func);
|
||||
writeFloat(ref);
|
||||
}
|
||||
|
||||
public void writeFogf(int pname, float param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.FOGF);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void writeLightf(int light, int pname, float param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.LIGHTF);
|
||||
writeInt(light);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void writeLightModelf(int pname, float param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.LIGHT_MODELF);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void writeLighti(int light, int pname, int param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.LIGHTI);
|
||||
writeInt(light);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
public void writeLightModeli(int pname, int param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.LIGHT_MODELI);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
public void writeMaterialf(int face, int pname, float param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.MATERIALF);
|
||||
writeInt(face);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void writeTexParameterf(int target, int pname, float param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.TEX_PARAMETERF);
|
||||
writeInt(target);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
// === Double commands ===
|
||||
|
||||
public void writeTranslate(double x, double y, double z) {
|
||||
ensureCapacity(28);
|
||||
writeInt(GLCommand.TRANSLATE);
|
||||
writeDouble(x);
|
||||
writeDouble(y);
|
||||
writeDouble(z);
|
||||
}
|
||||
|
||||
public void writeRotate(double angle, double x, double y, double z) {
|
||||
ensureCapacity(36);
|
||||
writeInt(GLCommand.ROTATE);
|
||||
writeDouble(angle);
|
||||
writeDouble(x);
|
||||
writeDouble(y);
|
||||
writeDouble(z);
|
||||
}
|
||||
|
||||
public void writeScale(double x, double y, double z) {
|
||||
ensureCapacity(28);
|
||||
writeInt(GLCommand.SCALE);
|
||||
writeDouble(x);
|
||||
writeDouble(y);
|
||||
writeDouble(z);
|
||||
}
|
||||
|
||||
public void writeOrtho(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
ensureCapacity(52);
|
||||
writeInt(GLCommand.ORTHO);
|
||||
writeDouble(left);
|
||||
writeDouble(right);
|
||||
writeDouble(bottom);
|
||||
writeDouble(top);
|
||||
writeDouble(zNear);
|
||||
writeDouble(zFar);
|
||||
}
|
||||
|
||||
public void writeFrustum(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
ensureCapacity(52);
|
||||
writeInt(GLCommand.FRUSTUM);
|
||||
writeDouble(left);
|
||||
writeDouble(right);
|
||||
writeDouble(bottom);
|
||||
writeDouble(top);
|
||||
writeDouble(zNear);
|
||||
writeDouble(zFar);
|
||||
}
|
||||
|
||||
// === Matrix commands ===
|
||||
|
||||
public void writeMultMatrix(Matrix4f matrix) {
|
||||
ensureCapacity(68);
|
||||
writeInt(GLCommand.MULT_MATRIX);
|
||||
writeMatrix4f(matrix);
|
||||
}
|
||||
|
||||
public void writeLoadMatrix(Matrix4f matrix) {
|
||||
ensureCapacity(68);
|
||||
writeInt(GLCommand.LOAD_MATRIX);
|
||||
writeMatrix4f(matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a Matrix4f to the buffer in column-major order (OpenGL convention).
|
||||
* Uses accessor methods instead of getToAddress() to avoid JOML MemUtil issues.
|
||||
*/
|
||||
private void writeMatrix4f(Matrix4f m) {
|
||||
// Column 0
|
||||
writeFloat(m.m00()); writeFloat(m.m01()); writeFloat(m.m02()); writeFloat(m.m03());
|
||||
// Column 1
|
||||
writeFloat(m.m10()); writeFloat(m.m11()); writeFloat(m.m12()); writeFloat(m.m13());
|
||||
// Column 2
|
||||
writeFloat(m.m20()); writeFloat(m.m21()); writeFloat(m.m22()); writeFloat(m.m23());
|
||||
// Column 3
|
||||
writeFloat(m.m30()); writeFloat(m.m31()); writeFloat(m.m32()); writeFloat(m.m33());
|
||||
}
|
||||
|
||||
// === Buffer commands (inline 4 floats) ===
|
||||
|
||||
public void writeFog(int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(12 + count * 4);
|
||||
writeInt(GLCommand.FOG);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeLight(int light, int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(16 + count * 4);
|
||||
writeInt(GLCommand.LIGHT);
|
||||
writeInt(light);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeLightModel(int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(12 + count * 4);
|
||||
writeInt(GLCommand.LIGHT_MODEL);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMaterial(int face, int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(16 + count * 4);
|
||||
writeInt(GLCommand.MATERIAL);
|
||||
writeInt(face);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeClipPlane(int plane, double a, double b, double c, double d) {
|
||||
ensureCapacity(40);
|
||||
writeInt(GLCommand.CLIP_PLANE);
|
||||
writeInt(plane);
|
||||
writeDouble(a);
|
||||
writeDouble(b);
|
||||
writeDouble(c);
|
||||
writeDouble(d);
|
||||
}
|
||||
|
||||
// === Draw commands ===
|
||||
|
||||
public void writeDrawRange(int vboIndex) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DRAW_RANGE);
|
||||
writeInt(vboIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a DRAW_RANGE_RESTORE command that draws a VBO range and restores GL current state after.
|
||||
* Used for immediate mode VBOs to restore GL_CURRENT_COLOR, GL_CURRENT_NORMAL, GL_CURRENT_TEXTURE_COORDS.
|
||||
*
|
||||
* @param vboIndex Index into ownedVbos array
|
||||
*/
|
||||
public void writeDrawRangeRestore(
|
||||
int vboIndex, AccumulatedDraw.RestoreData restoreData
|
||||
) {
|
||||
ensureCapacity(28);
|
||||
writeInt(GLCommand.DRAW_RANGE_RESTORE);
|
||||
writeInt(vboIndex);
|
||||
writeInt(restoreData.flags);
|
||||
// Last color (4 bytes)
|
||||
writeInt(restoreData.lastColor);
|
||||
// Last normal (4 bytes)
|
||||
writeInt(restoreData.lastNormal);
|
||||
// Last texcoord (2 floats = 8 bytes)
|
||||
writeFloat(restoreData.lastTexCoordU);
|
||||
writeFloat(restoreData.lastTexCoordV);
|
||||
}
|
||||
|
||||
public void writeCallList(int listId) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CALL_LIST);
|
||||
writeInt(listId);
|
||||
}
|
||||
|
||||
public void writeBindVBO(int vbo) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.BIND_VBO);
|
||||
writeInt(vbo);
|
||||
}
|
||||
|
||||
public void writeBindVAO(int vao) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.BIND_VAO);
|
||||
writeInt(vao);
|
||||
}
|
||||
|
||||
public void writeDrawBuffer(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DRAW_BUFFER);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
private static final int MAX_DRAW_BUFFERS = 8;
|
||||
// cmd (4) + count (4) + buffers (4 * MAX_DRAW_BUFFERS)
|
||||
private static final int DRAW_BUFFERS_SIZE = 4 + 4 + 4 * MAX_DRAW_BUFFERS;
|
||||
|
||||
public void writeDrawBuffers(int count, int buffer) {
|
||||
ensureCapacity(DRAW_BUFFERS_SIZE);
|
||||
writeInt(GLCommand.DRAW_BUFFERS);
|
||||
writeInt(count);
|
||||
writeInt(buffer);
|
||||
for (int i = 1; i < MAX_DRAW_BUFFERS; i++) {
|
||||
writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeDrawBuffers(int count, IntBuffer bufs) {
|
||||
ensureCapacity(DRAW_BUFFERS_SIZE);
|
||||
writeInt(GLCommand.DRAW_BUFFERS);
|
||||
writeInt(count);
|
||||
final int pos = bufs.position();
|
||||
for (int i = 0; i < count && i < MAX_DRAW_BUFFERS; i++) {
|
||||
writeInt(bufs.get(pos + i));
|
||||
}
|
||||
for (int i = count; i < MAX_DRAW_BUFFERS; i++) {
|
||||
writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// === Complex object reference ===
|
||||
|
||||
public void writeComplexRef(DisplayListCommand cmd) {
|
||||
ensureCapacity(8);
|
||||
final int index = complexObjects.size();
|
||||
complexObjects.add(cmd);
|
||||
writeInt(GLCommand.COMPLEX_REF);
|
||||
writeInt(index);
|
||||
}
|
||||
|
||||
// === Reading methods for optimization pass ===
|
||||
|
||||
private long readPointer = 0;
|
||||
|
||||
/**
|
||||
* Reset read position to the beginning of the buffer.
|
||||
*/
|
||||
public void resetRead() {
|
||||
readPointer = basePointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there's more data to read.
|
||||
*/
|
||||
public boolean hasRemaining() {
|
||||
return readPointer < writePointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an int from the current position.
|
||||
*/
|
||||
public int readInt() {
|
||||
final int value = memGetInt(readPointer);
|
||||
readPointer += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
public long readLong() {
|
||||
final long value = memGetLong(readPointer);
|
||||
readPointer += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a float from the current position.
|
||||
*/
|
||||
public float readFloat() {
|
||||
final float value = memGetFloat(readPointer);
|
||||
readPointer += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a double from the current position.
|
||||
*/
|
||||
public double readDouble() {
|
||||
final double value = memGetDouble(readPointer);
|
||||
readPointer += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a Matrix4f from the current position (16 floats in column-major order).
|
||||
*/
|
||||
public Matrix4f readMatrix4f(Matrix4f dest) {
|
||||
dest.m00(readFloat()); dest.m01(readFloat()); dest.m02(readFloat()); dest.m03(readFloat());
|
||||
dest.m10(readFloat()); dest.m11(readFloat()); dest.m12(readFloat()); dest.m13(readFloat());
|
||||
dest.m20(readFloat()); dest.m21(readFloat()); dest.m22(readFloat()); dest.m23(readFloat());
|
||||
dest.m30(readFloat()); dest.m31(readFloat()); dest.m32(readFloat()); dest.m33(readFloat());
|
||||
return dest;
|
||||
}
|
||||
|
||||
// === Lifecycle ===
|
||||
|
||||
public int size() {
|
||||
return (int) (writePointer - basePointer);
|
||||
}
|
||||
|
||||
public long address() {
|
||||
return basePointer;
|
||||
}
|
||||
|
||||
public ByteBuffer toBuffer() {
|
||||
buffer.limit(size());
|
||||
buffer.position(0);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public Object[] getComplexObjects() {
|
||||
return complexObjects.toArray();
|
||||
}
|
||||
|
||||
public Object getComplexObject(int index) {
|
||||
return complexObjects.get(index);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size() == 0 && complexObjects.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the buffer for reuse. Does NOT free memory.
|
||||
*/
|
||||
public void clear() {
|
||||
writePointer = basePointer;
|
||||
complexObjects.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the underlying buffer. Must be called when done.
|
||||
*/
|
||||
public void free() {
|
||||
if (buffer != null) {
|
||||
memFree(buffer);
|
||||
buffer = null;
|
||||
basePointer = 0;
|
||||
writePointer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-50
@@ -1,12 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -14,7 +12,10 @@ import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetDouble;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
|
||||
/**
|
||||
* Executes commands from a CommandBuffer
|
||||
@@ -39,7 +40,13 @@ public final class CommandBufferExecutor {
|
||||
* @param complexObjects Array of complex objects (TexImage2DCmd, etc.)
|
||||
* @param ownedVbos Array of VBOs owned by the display list (indexed by DrawRange commands)
|
||||
*/
|
||||
public static void execute(ByteBuffer buffer, Object[] complexObjects, DisplayListVBO ownedVbos) {
|
||||
public static void execute(
|
||||
ByteBuffer buffer,
|
||||
DisplayListCommand[] complexObjects,
|
||||
DisplayListVBO ownedVbos,
|
||||
int list,
|
||||
CompiledDisplayList compiledDisplayList
|
||||
) {
|
||||
long ptr = memAddress(buffer);
|
||||
final long end = ptr + buffer.limit();
|
||||
|
||||
@@ -343,50 +350,6 @@ public final class CommandBufferExecutor {
|
||||
GLStateManager.glTexParameterf(target, pname, param);
|
||||
}
|
||||
|
||||
// === Double commands ===
|
||||
case GLCommand.TRANSLATE -> {
|
||||
final double x = memGetDouble(ptr);
|
||||
final double y = memGetDouble(ptr + 8);
|
||||
final double z = memGetDouble(ptr + 16);
|
||||
ptr += 24;
|
||||
GLStateManager.glTranslated(x, y, z);
|
||||
}
|
||||
case GLCommand.ROTATE -> {
|
||||
final double angle = memGetDouble(ptr);
|
||||
final double x = memGetDouble(ptr + 8);
|
||||
final double y = memGetDouble(ptr + 16);
|
||||
final double z = memGetDouble(ptr + 24);
|
||||
ptr += 32;
|
||||
GLStateManager.glRotated(angle, x, y, z);
|
||||
}
|
||||
case GLCommand.SCALE -> {
|
||||
final double x = memGetDouble(ptr);
|
||||
final double y = memGetDouble(ptr + 8);
|
||||
final double z = memGetDouble(ptr + 16);
|
||||
ptr += 24;
|
||||
GLStateManager.glScaled(x, y, z);
|
||||
}
|
||||
case GLCommand.ORTHO -> {
|
||||
final double left = memGetDouble(ptr);
|
||||
final double right = memGetDouble(ptr + 8);
|
||||
final double bottom = memGetDouble(ptr + 16);
|
||||
final double top = memGetDouble(ptr + 24);
|
||||
final double zNear = memGetDouble(ptr + 32);
|
||||
final double zFar = memGetDouble(ptr + 40);
|
||||
ptr += 48;
|
||||
GLStateManager.glOrtho(left, right, bottom, top, zNear, zFar);
|
||||
}
|
||||
case GLCommand.FRUSTUM -> {
|
||||
final double left = memGetDouble(ptr);
|
||||
final double right = memGetDouble(ptr + 8);
|
||||
final double bottom = memGetDouble(ptr + 16);
|
||||
final double top = memGetDouble(ptr + 24);
|
||||
final double zNear = memGetDouble(ptr + 32);
|
||||
final double zFar = memGetDouble(ptr + 40);
|
||||
ptr += 48;
|
||||
GLStateManager.glFrustum(left, right, bottom, top, zNear, zFar);
|
||||
}
|
||||
|
||||
// === Matrix commands ===
|
||||
case GLCommand.MULT_MATRIX -> {
|
||||
// Mode-agnostic: just multiply current matrix
|
||||
@@ -398,6 +361,20 @@ public final class CommandBufferExecutor {
|
||||
MATRIX_BUFFER.flip();
|
||||
GLStateManager.glMultMatrix(MATRIX_BUFFER);
|
||||
}
|
||||
case GLCommand.TRANSLATE -> {
|
||||
final float x = memGetFloat(ptr);
|
||||
final float y = memGetFloat(ptr + 4);
|
||||
final float z = memGetFloat(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glTranslatef(x, y, z);
|
||||
}
|
||||
case GLCommand.SCALE -> {
|
||||
final float x = memGetFloat(ptr);
|
||||
final float y = memGetFloat(ptr + 4);
|
||||
final float z = memGetFloat(ptr + 8);
|
||||
ptr += 12;
|
||||
GLStateManager.glScalef(x, y, z);
|
||||
}
|
||||
case GLCommand.LOAD_MATRIX -> {
|
||||
MATRIX_BUFFER.clear();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
@@ -543,10 +520,10 @@ public final class CommandBufferExecutor {
|
||||
case GLCommand.COMPLEX_REF -> {
|
||||
final int index = memGetInt(ptr);
|
||||
ptr += 4;
|
||||
((DisplayListCommand) complexObjects[index]).execute();
|
||||
complexObjects[index].execute();
|
||||
}
|
||||
|
||||
default -> throw new IllegalStateException("Unknown command opcode: " + cmd);
|
||||
default -> throw new IllegalStateException("Unknown command opcode: " + cmd + " in display list with ID " + list + "\n" + DisplayListManager.getCompiledDisplayListString(list, compiledDisplayList, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
public final class CommandBufferProcessor {
|
||||
private static final Matrix4f tempMatrix = new Matrix4f();
|
||||
|
||||
private CommandBufferProcessor() {}
|
||||
|
||||
/**
|
||||
* Copy a complete command (opcode already read) from raw to final buffer.
|
||||
*/
|
||||
public static void copyCommand(CommandBuffer raw, CommandBuffer out) {
|
||||
final int opcode = raw.readInt();
|
||||
copyCommandData(opcode, raw, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy command data based on opcode (opcode already consumed from raw).
|
||||
*/
|
||||
public static void copyCommandData(int opcode, CommandBuffer raw, CommandBuffer out) {
|
||||
switch (opcode) {
|
||||
// Single int commands
|
||||
case GLCommand.ENABLE -> out.writeEnable(raw.readInt());
|
||||
case GLCommand.DISABLE -> out.writeDisable(raw.readInt());
|
||||
case GLCommand.CLEAR -> out.writeClear(raw.readInt());
|
||||
case GLCommand.CLEAR_STENCIL -> out.writeClearStencil(raw.readInt());
|
||||
case GLCommand.CULL_FACE -> out.writeCullFace(raw.readInt());
|
||||
case GLCommand.DEPTH_FUNC -> out.writeDepthFunc(raw.readInt());
|
||||
case GLCommand.SHADE_MODEL -> out.writeShadeModel(raw.readInt());
|
||||
case GLCommand.LOGIC_OP -> out.writeLogicOp(raw.readInt());
|
||||
case GLCommand.MATRIX_MODE -> out.writeMatrixMode(raw.readInt());
|
||||
case GLCommand.ACTIVE_TEXTURE -> out.writeActiveTexture(raw.readInt());
|
||||
case GLCommand.USE_PROGRAM -> out.writeUseProgram(raw.readInt());
|
||||
case GLCommand.PUSH_ATTRIB -> out.writePushAttrib(raw.readInt());
|
||||
case GLCommand.POP_ATTRIB -> { raw.readInt(); out.writePopAttrib(); }
|
||||
case GLCommand.LOAD_IDENTITY -> out.writeLoadIdentity();
|
||||
case GLCommand.PUSH_MATRIX -> out.writePushMatrix();
|
||||
case GLCommand.POP_MATRIX -> out.writePopMatrix();
|
||||
case GLCommand.STENCIL_MASK -> out.writeStencilMask(raw.readInt());
|
||||
case GLCommand.DEPTH_MASK -> out.writeDepthMask(raw.readInt() != 0);
|
||||
case GLCommand.FRONT_FACE -> out.writeFrontFace(raw.readInt());
|
||||
|
||||
// Two int commands
|
||||
case GLCommand.BIND_TEXTURE -> out.writeBindTexture(raw.readInt(), raw.readInt());
|
||||
case GLCommand.POLYGON_MODE -> out.writePolygonMode(raw.readInt(), raw.readInt());
|
||||
case GLCommand.COLOR_MATERIAL -> out.writeColorMaterial(raw.readInt(), raw.readInt());
|
||||
case GLCommand.LINE_STIPPLE -> out.writeLineStipple(raw.readInt(), raw.readInt());
|
||||
case GLCommand.STENCIL_MASK_SEPARATE -> out.writeStencilMaskSeparate(raw.readInt(), raw.readInt());
|
||||
case GLCommand.FOGI -> out.writeFogi(raw.readInt(), raw.readInt());
|
||||
case GLCommand.HINT -> out.writeHint(raw.readInt(), raw.readInt());
|
||||
|
||||
// Three int commands
|
||||
case GLCommand.STENCIL_FUNC -> out.writeStencilFunc(raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.STENCIL_OP -> out.writeStencilOp(raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.TEX_PARAMETERI -> out.writeTexParameteri(raw.readInt(), raw.readInt(), raw.readInt());
|
||||
|
||||
// Four int commands
|
||||
case GLCommand.VIEWPORT -> out.writeViewport(raw.readInt(), raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.SCISSOR -> out.writeScissor(raw.readInt(), raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.BLEND_FUNC -> out.writeBlendFunc(raw.readInt(), raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.COLOR_MASK -> out.writeColorMask(raw.readInt() != 0, raw.readInt() != 0, raw.readInt() != 0, raw.readInt() != 0);
|
||||
case GLCommand.STENCIL_FUNC_SEPARATE -> out.writeStencilFuncSeparate(raw.readInt(), raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.STENCIL_OP_SEPARATE -> out.writeStencilOpSeparate(raw.readInt(), raw.readInt(), raw.readInt(), raw.readInt());
|
||||
|
||||
// Float commands
|
||||
case GLCommand.POINT_SIZE -> out.writePointSize(raw.readFloat());
|
||||
case GLCommand.LINE_WIDTH -> out.writeLineWidth(raw.readFloat());
|
||||
case GLCommand.POLYGON_OFFSET -> out.writePolygonOffset(raw.readFloat(), raw.readFloat());
|
||||
case GLCommand.COLOR -> out.writeColor(raw.readFloat(), raw.readFloat(), raw.readFloat(), raw.readFloat());
|
||||
case GLCommand.CLEAR_COLOR -> out.writeClearColor(raw.readFloat(), raw.readFloat(), raw.readFloat(), raw.readFloat());
|
||||
|
||||
// Single double commands
|
||||
case GLCommand.CLEAR_DEPTH -> out.writeClearDepth(raw.readDouble());
|
||||
|
||||
// Four float commands
|
||||
case GLCommand.BLEND_COLOR -> out.writeBlendColor(raw.readFloat(), raw.readFloat(), raw.readFloat(), raw.readFloat());
|
||||
|
||||
// Mixed int+float commands
|
||||
case GLCommand.ALPHA_FUNC -> out.writeAlphaFunc(raw.readInt(), raw.readFloat());
|
||||
case GLCommand.FOGF -> out.writeFogf(raw.readInt(), raw.readFloat());
|
||||
case GLCommand.LIGHTF -> out.writeLightf(raw.readInt(), raw.readInt(), raw.readFloat());
|
||||
case GLCommand.LIGHT_MODELF -> out.writeLightModelf(raw.readInt(), raw.readFloat());
|
||||
case GLCommand.LIGHTI -> out.writeLighti(raw.readInt(), raw.readInt(), raw.readInt());
|
||||
case GLCommand.LIGHT_MODELI -> out.writeLightModeli(raw.readInt(), raw.readInt());
|
||||
case GLCommand.MATERIALF -> out.writeMaterialf(raw.readInt(), raw.readInt(), raw.readFloat());
|
||||
case GLCommand.TEX_PARAMETERF -> out.writeTexParameterf(raw.readInt(), raw.readInt(), raw.readFloat());
|
||||
|
||||
// Double commands
|
||||
case GLCommand.TRANSLATE -> out.writeTranslate(raw.readDouble(), raw.readDouble(), raw.readDouble());
|
||||
case GLCommand.ROTATE -> out.writeRotate(raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble());
|
||||
case GLCommand.SCALE -> out.writeScale(raw.readDouble(), raw.readDouble(), raw.readDouble());
|
||||
case GLCommand.ORTHO -> out.writeOrtho(raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble());
|
||||
case GLCommand.FRUSTUM -> out.writeFrustum(raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble(), raw.readDouble());
|
||||
|
||||
// Matrix commands
|
||||
case GLCommand.MULT_MATRIX -> { out.writeMultMatrix(raw.readMatrix4f(tempMatrix)); }
|
||||
case GLCommand.LOAD_MATRIX -> { out.writeLoadMatrix(raw.readMatrix4f(tempMatrix)); }
|
||||
|
||||
// FloatBuffer commands (inline 4 floats max)
|
||||
case GLCommand.FOG -> {
|
||||
final int pname = raw.readInt();
|
||||
final int count = raw.readInt();
|
||||
final FloatBuffer fb = FloatBuffer.allocate(count);
|
||||
for (int i = 0; i < count; i++) fb.put(raw.readFloat());
|
||||
fb.flip();
|
||||
out.writeFog(pname, fb);
|
||||
}
|
||||
case GLCommand.LIGHT -> {
|
||||
final int light = raw.readInt();
|
||||
final int pname = raw.readInt();
|
||||
final int count = raw.readInt();
|
||||
final FloatBuffer fb = FloatBuffer.allocate(count);
|
||||
for (int i = 0; i < count; i++) fb.put(raw.readFloat());
|
||||
fb.flip();
|
||||
out.writeLight(light, pname, fb);
|
||||
}
|
||||
case GLCommand.LIGHT_MODEL -> {
|
||||
final int pname = raw.readInt();
|
||||
final int count = raw.readInt();
|
||||
final FloatBuffer fb = FloatBuffer.allocate(count);
|
||||
for (int i = 0; i < count; i++) fb.put(raw.readFloat());
|
||||
fb.flip();
|
||||
out.writeLightModel(pname, fb);
|
||||
}
|
||||
case GLCommand.MATERIAL -> {
|
||||
final int face = raw.readInt();
|
||||
final int pname = raw.readInt();
|
||||
final int count = raw.readInt();
|
||||
final FloatBuffer fb = FloatBuffer.allocate(count);
|
||||
for (int i = 0; i < count; i++) fb.put(raw.readFloat());
|
||||
fb.flip();
|
||||
out.writeMaterial(face, pname, fb);
|
||||
}
|
||||
|
||||
// Clip plane command (4 doubles)
|
||||
case GLCommand.CLIP_PLANE -> {
|
||||
final int plane = raw.readInt();
|
||||
final double a = raw.readDouble();
|
||||
final double b = raw.readDouble();
|
||||
final double c = raw.readDouble();
|
||||
final double d = raw.readDouble();
|
||||
out.writeClipPlane(plane, a, b, c, d);
|
||||
}
|
||||
|
||||
// Draw/call commands
|
||||
case GLCommand.DRAW_RANGE -> out.writeDrawRange(raw.readInt());
|
||||
case GLCommand.CALL_LIST -> out.writeCallList(raw.readInt());
|
||||
case GLCommand.BIND_VBO -> out.writeBindVBO(raw.readInt());
|
||||
case GLCommand.BIND_VAO -> out.writeBindVAO(raw.readInt());
|
||||
|
||||
// Complex object reference - transfer the object to output buffer
|
||||
case GLCommand.COMPLEX_REF -> {
|
||||
final int index = raw.readInt();
|
||||
final Object obj = raw.getComplexObject(index);
|
||||
out.writeComplexRef((DisplayListCommand) obj);
|
||||
}
|
||||
|
||||
default -> GLStateManager.LOGGER.warn("[CommandBufferProcessor] Unknown opcode in buffer copy: {}", opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
+535
-223
@@ -4,376 +4,688 @@ import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatchBuilder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawCapture;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
|
||||
/**
|
||||
* Records GL commands to a CommandBuffer during display list compilation.
|
||||
*/
|
||||
public final class CommandRecorder {
|
||||
private final CommandBuffer buffer;
|
||||
private static final int DEFAULT_CAPACITY = 2048; // Guess at typical display list size
|
||||
|
||||
private ByteBuffer buffer;
|
||||
private long basePointer;
|
||||
private long writePointer;
|
||||
private final ArrayList<DisplayListCommand> complexObjects = new ArrayList<>();
|
||||
private final IndexedDrawBatchBuilder indexedDraws = new IndexedDrawBatchBuilder();
|
||||
private int commandCount;
|
||||
|
||||
public CommandRecorder() {
|
||||
this.buffer = new CommandBuffer();
|
||||
this.commandCount = 0;
|
||||
this(DEFAULT_CAPACITY);
|
||||
}
|
||||
|
||||
public CommandBuffer getBuffer() {
|
||||
return buffer;
|
||||
public CommandRecorder(int initialCapacity) {
|
||||
this.buffer = memAlloc(initialCapacity);
|
||||
this.basePointer = memAddress0(buffer);
|
||||
this.writePointer = basePointer;
|
||||
}
|
||||
|
||||
public int getCommandCount() {
|
||||
return commandCount;
|
||||
// === Low-level write operations ===
|
||||
|
||||
private void writeInt(int value) {
|
||||
memPutInt(writePointer, value);
|
||||
writePointer += 4;
|
||||
}
|
||||
|
||||
private void writeLong(long value) {
|
||||
memPutLong(writePointer, value);
|
||||
writePointer += 8;
|
||||
}
|
||||
|
||||
private void writeFloat(float value) {
|
||||
memPutFloat(writePointer, value);
|
||||
writePointer += 4;
|
||||
}
|
||||
|
||||
private void writeDouble(double value) {
|
||||
memPutDouble(writePointer, value);
|
||||
writePointer += 8;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int needed) {
|
||||
final int size = size();
|
||||
final int remaining = buffer.capacity() - size;
|
||||
if (remaining < needed) {
|
||||
final int newCapacity = Math.max(buffer.capacity() * 2, buffer.capacity() + needed);
|
||||
buffer = memRealloc(buffer, newCapacity);
|
||||
basePointer = memAddress0(buffer);
|
||||
writePointer = basePointer + size;
|
||||
}
|
||||
}
|
||||
|
||||
public IndexedDrawBatchBuilder getIndexedDraws() {
|
||||
return indexedDraws;
|
||||
}
|
||||
|
||||
public void recordIndexedDrawCapture(IndexedDrawCapture capture) {
|
||||
public void writeIndexedDrawCapture(IndexedDrawCapture capture) {
|
||||
indexedDraws.add(capture);
|
||||
recordComplexCommand(capture.placeholder);
|
||||
writeComplexCommand(capture.placeholder);
|
||||
}
|
||||
|
||||
public void free() {
|
||||
for (IndexedDrawCapture c : indexedDraws.getCaptures()) {
|
||||
c.freeBuffers();
|
||||
}
|
||||
buffer.free();
|
||||
}
|
||||
|
||||
// ==================== Recording Methods ====================
|
||||
// === Single int commands ===
|
||||
|
||||
public void recordEnable(int cap) {
|
||||
buffer.writeEnable(cap);
|
||||
commandCount++;
|
||||
public void writeEnable(int cap) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.ENABLE);
|
||||
writeInt(cap);
|
||||
}
|
||||
|
||||
public void recordDisable(int cap) {
|
||||
buffer.writeDisable(cap);
|
||||
commandCount++;
|
||||
public void writeDisable(int cap) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DISABLE);
|
||||
writeInt(cap);
|
||||
}
|
||||
|
||||
public void recordClear(int mask) {
|
||||
buffer.writeClear(mask);
|
||||
commandCount++;
|
||||
public void writeClear(int mask) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CLEAR);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void recordClearColor(float r, float g, float b, float a) {
|
||||
buffer.writeClearColor(r, g, b, a);
|
||||
commandCount++;
|
||||
public void writeClearStencil(int s) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CLEAR_STENCIL);
|
||||
writeInt(s);
|
||||
}
|
||||
|
||||
public void recordClearDepth(double depth) {
|
||||
buffer.writeClearDepth(depth);
|
||||
commandCount++;
|
||||
public void writeCullFace(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CULL_FACE);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void recordBlendColor(float r, float g, float b, float a) {
|
||||
buffer.writeBlendColor(r, g, b, a);
|
||||
commandCount++;
|
||||
public void writeDepthFunc(int func) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DEPTH_FUNC);
|
||||
writeInt(func);
|
||||
}
|
||||
|
||||
public void recordClearStencil(int s) {
|
||||
buffer.writeClearStencil(s);
|
||||
commandCount++;
|
||||
public void writeShadeModel(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.SHADE_MODEL);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void recordColor(float r, float g, float b, float a) {
|
||||
buffer.writeColor(r, g, b, a);
|
||||
commandCount++;
|
||||
public void writeLogicOp(int opcode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.LOGIC_OP);
|
||||
writeInt(opcode);
|
||||
}
|
||||
|
||||
public void recordColorMask(boolean r, boolean g, boolean b, boolean a) {
|
||||
buffer.writeColorMask(r, g, b, a);
|
||||
commandCount++;
|
||||
public void writeMatrixMode(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.MATRIX_MODE);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void recordDepthMask(boolean flag) {
|
||||
buffer.writeDepthMask(flag);
|
||||
commandCount++;
|
||||
public void writeActiveTexture(int texture) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.ACTIVE_TEXTURE);
|
||||
writeInt(texture);
|
||||
}
|
||||
|
||||
public void recordFrontFace(int mode) {
|
||||
buffer.writeFrontFace(mode);
|
||||
commandCount++;
|
||||
public void writeUseProgram(int program) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.USE_PROGRAM);
|
||||
writeInt(program);
|
||||
}
|
||||
|
||||
public void recordDepthFunc(int func) {
|
||||
buffer.writeDepthFunc(func);
|
||||
commandCount++;
|
||||
public void writePushAttrib(int mask) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.PUSH_ATTRIB);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void recordBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
buffer.writeBlendFunc(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
commandCount++;
|
||||
public void writePopAttrib() {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.POP_ATTRIB);
|
||||
writeInt(0); // unused padding
|
||||
}
|
||||
|
||||
public void recordAlphaFunc(int func, float ref) {
|
||||
buffer.writeAlphaFunc(func, ref);
|
||||
commandCount++;
|
||||
public void writeLoadIdentity() {
|
||||
ensureCapacity(4);
|
||||
writeInt(GLCommand.LOAD_IDENTITY);
|
||||
}
|
||||
|
||||
public void recordCullFace(int mode) {
|
||||
buffer.writeCullFace(mode);
|
||||
commandCount++;
|
||||
public void writePushMatrix() {
|
||||
ensureCapacity(4);
|
||||
writeInt(GLCommand.PUSH_MATRIX);
|
||||
}
|
||||
|
||||
public void recordShadeModel(int mode) {
|
||||
buffer.writeShadeModel(mode);
|
||||
commandCount++;
|
||||
public void writePopMatrix() {
|
||||
ensureCapacity(4);
|
||||
writeInt(GLCommand.POP_MATRIX);
|
||||
}
|
||||
|
||||
public void recordBindTexture(int target, int texture) {
|
||||
buffer.writeBindTexture(target, texture);
|
||||
commandCount++;
|
||||
public void writeStencilMask(int mask) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.STENCIL_MASK);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void recordTexParameteri(int target, int pname, int param) {
|
||||
buffer.writeTexParameteri(target, pname, param);
|
||||
commandCount++;
|
||||
public void writeDepthMask(boolean flag) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DEPTH_MASK);
|
||||
writeInt(flag ? 1 : 0);
|
||||
}
|
||||
|
||||
public void recordTexParameterf(int target, int pname, float param) {
|
||||
buffer.writeTexParameterf(target, pname, param);
|
||||
commandCount++;
|
||||
public void writeFrontFace(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.FRONT_FACE);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void recordMatrixMode(int mode) {
|
||||
buffer.writeMatrixMode(mode);
|
||||
commandCount++;
|
||||
}
|
||||
// === Two int commands ===
|
||||
|
||||
public void recordLoadIdentity() {
|
||||
buffer.writeLoadIdentity();
|
||||
commandCount++;
|
||||
public void writeBindTexture(int target, int texture) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.BIND_TEXTURE);
|
||||
writeInt(target);
|
||||
writeInt(texture);
|
||||
}
|
||||
|
||||
public void recordPushMatrix() {
|
||||
buffer.writePushMatrix();
|
||||
commandCount++;
|
||||
public void writePolygonMode(int face, int mode) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.POLYGON_MODE);
|
||||
writeInt(face);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void recordPopMatrix() {
|
||||
buffer.writePopMatrix();
|
||||
commandCount++;
|
||||
public void writeColorMaterial(int face, int mode) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.COLOR_MATERIAL);
|
||||
writeInt(face);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void recordMultMatrix(Matrix4f matrix) {
|
||||
buffer.writeMultMatrix(matrix);
|
||||
commandCount++;
|
||||
public void writeLineStipple(int factor, int pattern) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.LINE_STIPPLE);
|
||||
writeInt(factor);
|
||||
writeInt(pattern);
|
||||
}
|
||||
|
||||
public void recordLoadMatrix(Matrix4f matrix) {
|
||||
buffer.writeLoadMatrix(matrix);
|
||||
commandCount++;
|
||||
public void writeStencilMaskSeparate(int face, int mask) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.STENCIL_MASK_SEPARATE);
|
||||
writeInt(face);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void recordOrtho(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
buffer.writeOrtho(left, right, bottom, top, zNear, zFar);
|
||||
commandCount++;
|
||||
public void writeFogi(int pname, int param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.FOGI);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
public void recordFrustum(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
buffer.writeFrustum(left, right, bottom, top, zNear, zFar);
|
||||
commandCount++;
|
||||
public void writeHint(int target, int mode) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.HINT);
|
||||
writeInt(target);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
public void recordViewport(int x, int y, int width, int height) {
|
||||
buffer.writeViewport(x, y, width, height);
|
||||
commandCount++;
|
||||
}
|
||||
// === Three int commands ===
|
||||
|
||||
public void recordScissor(int x, int y, int width, int height) {
|
||||
buffer.writeScissor(x, y, width, height);
|
||||
commandCount++;
|
||||
public void writeStencilFunc(int func, int ref, int mask) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.STENCIL_FUNC);
|
||||
writeInt(func);
|
||||
writeInt(ref);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void recordPointSize(float size) {
|
||||
buffer.writePointSize(size);
|
||||
commandCount++;
|
||||
public void writeStencilOp(int fail, int zfail, int zpass) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.STENCIL_OP);
|
||||
writeInt(fail);
|
||||
writeInt(zfail);
|
||||
writeInt(zpass);
|
||||
}
|
||||
|
||||
public void recordLineWidth(float width) {
|
||||
buffer.writeLineWidth(width);
|
||||
commandCount++;
|
||||
public void writeTexParameteri(int target, int pname, int param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.TEX_PARAMETERI);
|
||||
writeInt(target);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
public void recordLineStipple(int factor, int pattern) {
|
||||
buffer.writeLineStipple(factor, pattern);
|
||||
commandCount++;
|
||||
}
|
||||
|
||||
public void recordPolygonOffset(float factor, float units) {
|
||||
buffer.writePolygonOffset(factor, units);
|
||||
commandCount++;
|
||||
}
|
||||
// === Four int commands ===
|
||||
|
||||
public void recordPolygonMode(int face, int mode) {
|
||||
buffer.writePolygonMode(face, mode);
|
||||
commandCount++;
|
||||
public void writeViewport(int x, int y, int width, int height) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.VIEWPORT);
|
||||
writeInt(x);
|
||||
writeInt(y);
|
||||
writeInt(width);
|
||||
writeInt(height);
|
||||
}
|
||||
|
||||
public void recordColorMaterial(int face, int mode) {
|
||||
buffer.writeColorMaterial(face, mode);
|
||||
commandCount++;
|
||||
public void writeScissor(int x, int y, int width, int height) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.SCISSOR);
|
||||
writeInt(x);
|
||||
writeInt(y);
|
||||
writeInt(width);
|
||||
writeInt(height);
|
||||
}
|
||||
|
||||
public void recordLogicOp(int opcode) {
|
||||
buffer.writeLogicOp(opcode);
|
||||
commandCount++;
|
||||
public void writeBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.BLEND_FUNC);
|
||||
writeInt(srcRgb);
|
||||
writeInt(dstRgb);
|
||||
writeInt(srcAlpha);
|
||||
writeInt(dstAlpha);
|
||||
}
|
||||
|
||||
public void recordActiveTexture(int texture) {
|
||||
buffer.writeActiveTexture(texture);
|
||||
commandCount++;
|
||||
public void writeColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.COLOR_MASK);
|
||||
writeInt(red ? 1 : 0);
|
||||
writeInt(green ? 1 : 0);
|
||||
writeInt(blue ? 1 : 0);
|
||||
writeInt(alpha ? 1 : 0);
|
||||
}
|
||||
|
||||
public void recordUseProgram(int program) {
|
||||
buffer.writeUseProgram(program);
|
||||
commandCount++;
|
||||
public void writeStencilFuncSeparate(int face, int func, int ref, int mask) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.STENCIL_FUNC_SEPARATE);
|
||||
writeInt(face);
|
||||
writeInt(func);
|
||||
writeInt(ref);
|
||||
writeInt(mask);
|
||||
}
|
||||
|
||||
public void recordPushAttrib(int mask) {
|
||||
buffer.writePushAttrib(mask);
|
||||
commandCount++;
|
||||
public void writeStencilOpSeparate(int face, int sfail, int dpfail, int dppass) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.STENCIL_OP_SEPARATE);
|
||||
writeInt(face);
|
||||
writeInt(sfail);
|
||||
writeInt(dpfail);
|
||||
writeInt(dppass);
|
||||
}
|
||||
|
||||
public void recordPopAttrib() {
|
||||
buffer.writePopAttrib();
|
||||
commandCount++;
|
||||
}
|
||||
// === Float commands ===
|
||||
|
||||
public void recordFogf(int pname, float param) {
|
||||
buffer.writeFogf(pname, param);
|
||||
commandCount++;
|
||||
public void writePointSize(float size) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.POINT_SIZE);
|
||||
writeFloat(size);
|
||||
}
|
||||
|
||||
public void recordFogi(int pname, int param) {
|
||||
buffer.writeFogi(pname, param);
|
||||
commandCount++;
|
||||
public void writeLineWidth(float width) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.LINE_WIDTH);
|
||||
writeFloat(width);
|
||||
}
|
||||
|
||||
public void recordHint(int target, int mode) {
|
||||
buffer.writeHint(target, mode);
|
||||
commandCount++;
|
||||
public void writePolygonOffset(float factor, float units) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.POLYGON_OFFSET);
|
||||
writeFloat(factor);
|
||||
writeFloat(units);
|
||||
}
|
||||
|
||||
public void recordFog(int pname, FloatBuffer params) {
|
||||
buffer.writeFog(pname, params);
|
||||
commandCount++;
|
||||
public void writeColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
public void recordLightf(int light, int pname, float param) {
|
||||
buffer.writeLightf(light, pname, param);
|
||||
commandCount++;
|
||||
public void writeClearColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.CLEAR_COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
public void recordLighti(int light, int pname, int param) {
|
||||
buffer.writeLighti(light, pname, param);
|
||||
commandCount++;
|
||||
public void writeClearDepth(double depth) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.CLEAR_DEPTH);
|
||||
writeDouble(depth);
|
||||
}
|
||||
|
||||
public void recordLight(int light, int pname, FloatBuffer params) {
|
||||
buffer.writeLight(light, pname, params);
|
||||
commandCount++;
|
||||
public void writeBlendColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.BLEND_COLOR);
|
||||
writeFloat(r);
|
||||
writeFloat(g);
|
||||
writeFloat(b);
|
||||
writeFloat(a);
|
||||
}
|
||||
|
||||
public void recordLightModelf(int pname, float param) {
|
||||
buffer.writeLightModelf(pname, param);
|
||||
commandCount++;
|
||||
}
|
||||
// === Mixed int+float commands ===
|
||||
|
||||
public void recordLightModeli(int pname, int param) {
|
||||
buffer.writeLightModeli(pname, param);
|
||||
commandCount++;
|
||||
public void writeAlphaFunc(int func, float ref) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.ALPHA_FUNC);
|
||||
writeInt(func);
|
||||
writeFloat(ref);
|
||||
}
|
||||
|
||||
public void recordLightModel(int pname, FloatBuffer params) {
|
||||
buffer.writeLightModel(pname, params);
|
||||
commandCount++;
|
||||
public void writeFogf(int pname, float param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.FOGF);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void recordMaterialf(int face, int pname, float val) {
|
||||
buffer.writeMaterialf(face, pname, val);
|
||||
commandCount++;
|
||||
public void writeLightf(int light, int pname, float param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.LIGHTF);
|
||||
writeInt(light);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void recordMaterial(int face, int pname, FloatBuffer params) {
|
||||
buffer.writeMaterial(face, pname, params);
|
||||
commandCount++;
|
||||
public void writeLightModelf(int pname, float param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.LIGHT_MODELF);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void recordClipPlane(int plane, double a, double b, double c, double d) {
|
||||
buffer.writeClipPlane(plane, a, b, c, d);
|
||||
commandCount++;
|
||||
public void writeLighti(int light, int pname, int param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.LIGHTI);
|
||||
writeInt(light);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
public void recordStencilFunc(int func, int ref, int mask) {
|
||||
buffer.writeStencilFunc(func, ref, mask);
|
||||
commandCount++;
|
||||
public void writeLightModeli(int pname, int param) {
|
||||
ensureCapacity(12);
|
||||
writeInt(GLCommand.LIGHT_MODELI);
|
||||
writeInt(pname);
|
||||
writeInt(param);
|
||||
}
|
||||
|
||||
public void recordStencilFuncSeparate(int face, int func, int ref, int mask) {
|
||||
buffer.writeStencilFuncSeparate(face, func, ref, mask);
|
||||
commandCount++;
|
||||
public void writeMaterialf(int face, int pname, float param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.MATERIALF);
|
||||
writeInt(face);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
public void recordStencilOp(int fail, int zfail, int zpass) {
|
||||
buffer.writeStencilOp(fail, zfail, zpass);
|
||||
commandCount++;
|
||||
|
||||
public void writeTexParameterf(int target, int pname, float param) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.TEX_PARAMETERF);
|
||||
writeInt(target);
|
||||
writeInt(pname);
|
||||
writeFloat(param);
|
||||
}
|
||||
|
||||
// === Transformation commands ===
|
||||
|
||||
public void writeTranslate(Vector3f translationVector) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.TRANSLATE);
|
||||
writeFloat(translationVector.x);
|
||||
writeFloat(translationVector.y);
|
||||
writeFloat(translationVector.z);
|
||||
}
|
||||
|
||||
public void writeScale(Vector3f scaleVector) {
|
||||
ensureCapacity(16);
|
||||
writeInt(GLCommand.SCALE);
|
||||
writeFloat(scaleVector.x);
|
||||
writeFloat(scaleVector.y);
|
||||
writeFloat(scaleVector.z);
|
||||
}
|
||||
|
||||
// === Matrix commands ===
|
||||
|
||||
public void writeMultMatrix(Matrix4f matrix) {
|
||||
ensureCapacity(68);
|
||||
writeInt(GLCommand.MULT_MATRIX);
|
||||
writeMatrix4f(matrix);
|
||||
}
|
||||
|
||||
public void writeLoadMatrix(Matrix4f matrix) {
|
||||
ensureCapacity(68);
|
||||
writeInt(GLCommand.LOAD_MATRIX);
|
||||
writeMatrix4f(matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a Matrix4f to the buffer in column-major order (OpenGL convention).
|
||||
* Uses accessor methods instead of getToAddress() to avoid JOML MemUtil issues.
|
||||
*/
|
||||
private void writeMatrix4f(Matrix4f m) {
|
||||
// Column 0
|
||||
writeFloat(m.m00()); writeFloat(m.m01()); writeFloat(m.m02()); writeFloat(m.m03());
|
||||
// Column 1
|
||||
writeFloat(m.m10()); writeFloat(m.m11()); writeFloat(m.m12()); writeFloat(m.m13());
|
||||
// Column 2
|
||||
writeFloat(m.m20()); writeFloat(m.m21()); writeFloat(m.m22()); writeFloat(m.m23());
|
||||
// Column 3
|
||||
writeFloat(m.m30()); writeFloat(m.m31()); writeFloat(m.m32()); writeFloat(m.m33());
|
||||
}
|
||||
|
||||
// === Buffer commands (inline 4 floats) ===
|
||||
|
||||
public void writeFog(int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(12 + count * 4);
|
||||
writeInt(GLCommand.FOG);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeLight(int light, int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(16 + count * 4);
|
||||
writeInt(GLCommand.LIGHT);
|
||||
writeInt(light);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeLightModel(int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(12 + count * 4);
|
||||
writeInt(GLCommand.LIGHT_MODEL);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMaterial(int face, int pname, FloatBuffer params) {
|
||||
final int count = Math.min(params.remaining(), 4);
|
||||
ensureCapacity(16 + count * 4);
|
||||
writeInt(GLCommand.MATERIAL);
|
||||
writeInt(face);
|
||||
writeInt(pname);
|
||||
writeInt(count);
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
writeFloat(params.get(pos + i));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeClipPlane(int plane, double a, double b, double c, double d) {
|
||||
ensureCapacity(40);
|
||||
writeInt(GLCommand.CLIP_PLANE);
|
||||
writeInt(plane);
|
||||
writeDouble(a);
|
||||
writeDouble(b);
|
||||
writeDouble(c);
|
||||
writeDouble(d);
|
||||
}
|
||||
|
||||
// === Draw commands ===
|
||||
|
||||
public void writeDrawRange(int vboIndex) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DRAW_RANGE);
|
||||
writeInt(vboIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a DRAW_RANGE_RESTORE command that draws a VBO range and restores GL current state after.
|
||||
* Used for immediate mode VBOs to restore GL_CURRENT_COLOR, GL_CURRENT_NORMAL, GL_CURRENT_TEXTURE_COORDS.
|
||||
*
|
||||
* @param vboIndex Index into ownedVbos array
|
||||
*/
|
||||
public void writeDrawRangeRestore(
|
||||
int vboIndex, AccumulatedDraw.RestoreData restoreData
|
||||
) {
|
||||
ensureCapacity(28);
|
||||
writeInt(GLCommand.DRAW_RANGE_RESTORE);
|
||||
writeInt(vboIndex);
|
||||
writeInt(restoreData.flags);
|
||||
// Last color (4 bytes)
|
||||
writeInt(restoreData.lastColor);
|
||||
// Last normal (4 bytes)
|
||||
writeInt(restoreData.lastNormal);
|
||||
// Last texcoord (2 floats = 8 bytes)
|
||||
writeFloat(restoreData.lastTexCoordU);
|
||||
writeFloat(restoreData.lastTexCoordV);
|
||||
}
|
||||
|
||||
|
||||
public void writeCallList(int listId) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.CALL_LIST);
|
||||
writeInt(listId);
|
||||
}
|
||||
|
||||
public void writeBindVBO(int vbo) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.BIND_VBO);
|
||||
writeInt(vbo);
|
||||
}
|
||||
|
||||
public void writeBindVAO(int vao) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.BIND_VAO);
|
||||
writeInt(vao);
|
||||
}
|
||||
|
||||
public void writeDrawBuffer(int mode) {
|
||||
ensureCapacity(8);
|
||||
writeInt(GLCommand.DRAW_BUFFER);
|
||||
writeInt(mode);
|
||||
}
|
||||
|
||||
private static final int MAX_DRAW_BUFFERS = 8;
|
||||
// cmd (4) + count (4) + buffers (4 * MAX_DRAW_BUFFERS)
|
||||
private static final int DRAW_BUFFERS_SIZE = 4 + 4 + 4 * MAX_DRAW_BUFFERS;
|
||||
|
||||
public void writeDrawBuffers(int count, int buffer) {
|
||||
ensureCapacity(DRAW_BUFFERS_SIZE);
|
||||
writeInt(GLCommand.DRAW_BUFFERS);
|
||||
writeInt(count);
|
||||
writeInt(buffer);
|
||||
for (int i = 1; i < MAX_DRAW_BUFFERS; i++) {
|
||||
writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeDrawBuffers(int count, IntBuffer bufs) {
|
||||
ensureCapacity(DRAW_BUFFERS_SIZE);
|
||||
writeInt(GLCommand.DRAW_BUFFERS);
|
||||
writeInt(count);
|
||||
final int pos = bufs.position();
|
||||
for (int i = 0; i < count && i < MAX_DRAW_BUFFERS; i++) {
|
||||
writeInt(bufs.get(pos + i));
|
||||
}
|
||||
for (int i = count; i < MAX_DRAW_BUFFERS; i++) {
|
||||
writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void recordStencilOpSeparate(int face, int sfail, int dpfail, int dppass) {
|
||||
buffer.writeStencilOpSeparate(face, sfail, dpfail, dppass);
|
||||
commandCount++;
|
||||
}
|
||||
// === Complex object reference ===
|
||||
|
||||
public void recordStencilMask(int mask) {
|
||||
buffer.writeStencilMask(mask);
|
||||
commandCount++;
|
||||
public void writeComplexCommand(DisplayListCommand cmd) {
|
||||
ensureCapacity(8);
|
||||
final int index = complexObjects.size();
|
||||
complexObjects.add(cmd);
|
||||
writeInt(GLCommand.COMPLEX_REF);
|
||||
writeInt(index);
|
||||
}
|
||||
|
||||
public void recordStencilMaskSeparate(int face, int mask) {
|
||||
buffer.writeStencilMaskSeparate(face, mask);
|
||||
commandCount++;
|
||||
}
|
||||
// === Lifecycle ===
|
||||
|
||||
public void recordCallList(int listId) {
|
||||
buffer.writeCallList(listId);
|
||||
commandCount++;
|
||||
public int size() {
|
||||
return (int) (writePointer - basePointer);
|
||||
}
|
||||
|
||||
public void recordDrawBuffer(int mode) {
|
||||
buffer.writeDrawBuffer(mode);
|
||||
commandCount++;
|
||||
public void delete() {
|
||||
for (IndexedDrawCapture c : indexedDraws.getCaptures()) {
|
||||
c.freeBuffers();
|
||||
}
|
||||
memFree(buffer);
|
||||
}
|
||||
|
||||
public void recordDrawBuffers(int count, int buf) {
|
||||
buffer.writeDrawBuffers(count, buf);
|
||||
commandCount++;
|
||||
/**
|
||||
* Deletes every allocated object & returns a ByteBuffer with the capacity equal to its limit.
|
||||
* This CommandRecorder object cannot be used after calling this anymore.
|
||||
*/
|
||||
public ByteBuffer finish() {
|
||||
for (IndexedDrawCapture c : indexedDraws.getCaptures()) {
|
||||
c.freeBuffers();
|
||||
}
|
||||
final int size = size();
|
||||
buffer.limit(size);
|
||||
return memRealloc(buffer, size);
|
||||
}
|
||||
|
||||
public void recordDrawBuffers(int count, IntBuffer bufs) {
|
||||
buffer.writeDrawBuffers(count, bufs);
|
||||
commandCount++;
|
||||
public DisplayListCommand[] getComplexObjects() {
|
||||
return complexObjects.toArray(new DisplayListCommand[complexObjects.size()]);
|
||||
}
|
||||
|
||||
public void recordBindVBO(int vbo) {
|
||||
buffer.writeBindVBO(vbo);
|
||||
commandCount++;
|
||||
public DisplayListCommand getComplexObject(int index) {
|
||||
return complexObjects.get(index);
|
||||
}
|
||||
|
||||
public void recordBindVAO(int vao) {
|
||||
buffer.writeBindVAO(vao);
|
||||
commandCount++;
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
public void recordComplexCommand(DisplayListCommand cmd) {
|
||||
buffer.writeComplexRef(cmd);
|
||||
commandCount++;
|
||||
/**
|
||||
* Clear the buffer for reuse. Does NOT free memory.
|
||||
*/
|
||||
public void clear() {
|
||||
writePointer = basePointer;
|
||||
complexObjects.clear();
|
||||
}
|
||||
}
|
||||
+23
-9
@@ -32,11 +32,11 @@ public final class CompiledDisplayList {
|
||||
public static final CompiledDisplayList EMPTY = new CompiledDisplayList(null, null, null, Collections.emptyList());
|
||||
|
||||
private final ByteBuffer commandBuffer; // Off-heap command storage, must be freed
|
||||
private final Object[] complexObjects; // Complex commands (TexImage2D, etc.)
|
||||
private final DisplayListCommand[] complexObjects; // Complex commands (TexImage2D, etc.)
|
||||
private final DisplayListVBO ownedVbos; // GPU resources referenced by index
|
||||
private final List<IndexedDrawBatch> indexedBatches; // Shared VAO/VBO/EBO triples per layout group
|
||||
|
||||
public CompiledDisplayList(ByteBuffer commandBuffer, Object[] complexObjects, DisplayListVBO ownedVbos, List<IndexedDrawBatch> indexedBatches) {
|
||||
public CompiledDisplayList(ByteBuffer commandBuffer, DisplayListCommand[] complexObjects, DisplayListVBO ownedVbos, List<IndexedDrawBatch> indexedBatches) {
|
||||
this.commandBuffer = commandBuffer;
|
||||
this.complexObjects = complexObjects;
|
||||
this.ownedVbos = ownedVbos;
|
||||
@@ -47,8 +47,12 @@ public final class CompiledDisplayList {
|
||||
* Render this display list by executing all commands.
|
||||
*/
|
||||
public void render() {
|
||||
render(-1);
|
||||
}
|
||||
|
||||
public void render(int list) {
|
||||
if (commandBuffer != null && commandBuffer.limit() > 0) {
|
||||
CommandBufferExecutor.execute(commandBuffer, complexObjects, ownedVbos);
|
||||
CommandBufferExecutor.execute(commandBuffer, complexObjects, ownedVbos, list, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,10 +61,8 @@ public final class CompiledDisplayList {
|
||||
*/
|
||||
public void delete() {
|
||||
if (complexObjects != null) {
|
||||
for (Object obj : complexObjects) {
|
||||
if (obj instanceof DisplayListCommand cmd) {
|
||||
cmd.delete();
|
||||
}
|
||||
for (DisplayListCommand cmd : complexObjects) {
|
||||
cmd.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +94,7 @@ public final class CompiledDisplayList {
|
||||
/**
|
||||
* Get complex objects for inlining into another display list (nested lists).
|
||||
*/
|
||||
public Object[] getComplexObjects() {
|
||||
public DisplayListCommand[] getComplexObjects() {
|
||||
return complexObjects;
|
||||
}
|
||||
|
||||
@@ -144,8 +146,20 @@ public final class CompiledDisplayList {
|
||||
return opcodes;
|
||||
}
|
||||
|
||||
public String getCompiledDisplayListString(int listId, StackTraceElement[] stackTrace) {
|
||||
return DisplayListManager.getCompiledDisplayListString(listId, this, stackTrace);
|
||||
}
|
||||
|
||||
public String toString(int listId, StackTraceElement[] stackTrace) {
|
||||
return getCompiledDisplayListString(listId, stackTrace);
|
||||
}
|
||||
|
||||
public String toString(int listId) {
|
||||
return toString(listId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return DisplayListManager.getCompiledDisplayListString(0, this, null);
|
||||
return toString(0);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,10 @@ public final class DisplayListVBO {
|
||||
return vbos;
|
||||
}
|
||||
|
||||
public SubVBO getVBO(int index) {
|
||||
return vbos[index];
|
||||
}
|
||||
|
||||
public void render(int index) {
|
||||
vbos[index].render();
|
||||
}
|
||||
@@ -41,6 +45,18 @@ public final class DisplayListVBO {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getDrawMode() {
|
||||
return drawMode;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
vao.delete();
|
||||
}
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ public final class DisplayListVBOBuilder {
|
||||
allBuffers.addAll(drawBuffers);
|
||||
int size = 0;
|
||||
for (ByteBuffer buffer : drawBuffers) {
|
||||
size += buffer.remaining();
|
||||
size += buffer.limit();
|
||||
}
|
||||
vertexCount = format.getVertexCount(size);
|
||||
}
|
||||
|
||||
@@ -74,10 +74,7 @@ public final class GLCommand {
|
||||
|
||||
// === Double commands ===
|
||||
public static final int TRANSLATE = 70; // [cmd:4][mode:4][x:8d][y:8d][z:8d] = 32 bytes
|
||||
public static final int ROTATE = 71; // [cmd:4][mode:4][angle:8d][x:8d][y:8d][z:8d] = 40 bytes
|
||||
public static final int SCALE = 72; // [cmd:4][mode:4][x:8d][y:8d][z:8d] = 32 bytes
|
||||
public static final int ORTHO = 73; // [cmd:4][left:8d][right:8d][bottom:8d][top:8d][zNear:8d][zFar:8d] = 52 bytes
|
||||
public static final int FRUSTUM = 74; // [cmd:4][left:8d][right:8d][bottom:8d][top:8d][zNear:8d][zFar:8d] = 52 bytes
|
||||
public static final int CLEAR_DEPTH = 75; // [cmd:4][depth:8d] = 12 bytes
|
||||
|
||||
// === Matrix commands (72 bytes) ===
|
||||
@@ -167,10 +164,7 @@ public final class GLCommand {
|
||||
case MATERIALF -> "MATERIALF";
|
||||
case TEX_PARAMETERF -> "TEX_PARAMETERF";
|
||||
case TRANSLATE -> "TRANSLATE";
|
||||
case ROTATE -> "ROTATE";
|
||||
case SCALE -> "SCALE";
|
||||
case ORTHO -> "ORTHO";
|
||||
case FRUSTUM -> "FRUSTUM";
|
||||
case CLEAR_DEPTH -> "CLEAR_DEPTH";
|
||||
case MULT_MATRIX -> "MULT_MATRIX";
|
||||
case LOAD_MATRIX -> "LOAD_MATRIX";
|
||||
@@ -235,10 +229,8 @@ public final class GLCommand {
|
||||
case GLCommand.DRAW_BUFFERS -> 40;
|
||||
|
||||
// Double commands
|
||||
case GLCommand.TRANSLATE, GLCommand.SCALE -> 28; // cmd + 3 doubles
|
||||
case GLCommand.TRANSLATE, GLCommand.SCALE -> 16; // cmd + 3 floats
|
||||
case GLCommand.CLEAR_DEPTH -> 12; // cmd + 1 double
|
||||
case GLCommand.ROTATE -> 36; // cmd + 4 doubles
|
||||
case GLCommand.ORTHO, GLCommand.FRUSTUM -> 52; // cmd + 6 doubles
|
||||
|
||||
// Matrix commands
|
||||
case GLCommand.MULT_MATRIX, GLCommand.LOAD_MATRIX -> 68; // cmd + 16 floats
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.CallbackTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
@@ -15,7 +16,7 @@ import java.util.Arrays;
|
||||
* Used both during display list compilation and for live immediate mode emulation in core profile.
|
||||
*/
|
||||
public final class ImmediateModeRecorder {
|
||||
private static final DirectTessellator tessellator = new DirectTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
|
||||
private static final CallbackTessellator tessellator = new CallbackTessellator(TessellatorManager.DEFAULT_BUFFER_SIZE);
|
||||
|
||||
/** Separate tessellator for the splash thread. Nulled after splash completes. */
|
||||
private static DirectTessellator splashTessellator;
|
||||
@@ -29,6 +30,7 @@ public final class ImmediateModeRecorder {
|
||||
}
|
||||
|
||||
public static void destroySplashTessellator() {
|
||||
splashTessellator.delete();
|
||||
splashTessellator = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,65 +27,75 @@ public class LightModelState implements ISettableState<LightModelState> {
|
||||
twoSide = 0.0F;
|
||||
}
|
||||
|
||||
public void setAmbient(FloatBuffer newBuffer) {
|
||||
public boolean setAmbient(FloatBuffer newBuffer) {
|
||||
vector4f.set(newBuffer);
|
||||
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
|
||||
this.ambient.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setAmbient(IntBuffer newBuffer) {
|
||||
public boolean setAmbient(IntBuffer newBuffer) {
|
||||
vector4i.set(newBuffer);
|
||||
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
|
||||
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
|
||||
this.ambient.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setColorControl(int val) {
|
||||
public boolean setColorControl(int val) {
|
||||
if (GLStateManager.shouldBypassCache() || this.colorControl != val) {
|
||||
this.colorControl = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setColorControl(IntBuffer newBuffer) {
|
||||
setColorControl(newBuffer.get());
|
||||
public boolean setColorControl(IntBuffer newBuffer) {
|
||||
return setColorControl(newBuffer.get());
|
||||
}
|
||||
|
||||
public void setLocalViewer(float val) {
|
||||
public boolean setLocalViewer(float val) {
|
||||
if (GLStateManager.shouldBypassCache() || Float.compare(this.localViewer, val) != 0) {
|
||||
this.localViewer = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setLocalViewer(FloatBuffer newBuffer) {
|
||||
setLocalViewer(newBuffer.get());
|
||||
public boolean setLocalViewer(FloatBuffer newBuffer) {
|
||||
return setLocalViewer(newBuffer.get());
|
||||
}
|
||||
|
||||
public void setLocalViewer(IntBuffer newBuffer) {
|
||||
setLocalViewer((float) newBuffer.get());
|
||||
public boolean setLocalViewer(IntBuffer newBuffer) {
|
||||
return setLocalViewer((float) newBuffer.get());
|
||||
}
|
||||
|
||||
public void setLocalViewer(int val) {
|
||||
setLocalViewer((float) val);
|
||||
public boolean setLocalViewer(int val) {
|
||||
return setLocalViewer((float) val);
|
||||
}
|
||||
|
||||
public void setTwoSide(float val) {
|
||||
public boolean setTwoSide(float val) {
|
||||
if (GLStateManager.shouldBypassCache() || Float.compare(this.twoSide, val) != 0) {
|
||||
this.twoSide = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setTwoSide(FloatBuffer newBuffer) {
|
||||
setTwoSide(newBuffer.get());
|
||||
public boolean setTwoSide(FloatBuffer newBuffer) {
|
||||
return setTwoSide(newBuffer.get());
|
||||
}
|
||||
|
||||
public void setTwoSide(IntBuffer newBuffer) {
|
||||
setTwoSide((float) newBuffer.get());
|
||||
public boolean setTwoSide(IntBuffer newBuffer) {
|
||||
return setTwoSide((float) newBuffer.get());
|
||||
}
|
||||
|
||||
public void setTwoSide(int val) {
|
||||
setTwoSide((float) val);
|
||||
public boolean setTwoSide(int val) {
|
||||
return setTwoSide((float) val);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@ public class LightState implements ISettableState<LightState> {
|
||||
|
||||
private static final Vector4i vector4i = new Vector4i();
|
||||
private static final Vector4f vector4f = new Vector4f();
|
||||
private static final Vector3f vector3f = new Vector3f();
|
||||
private static final Vector3i vector3i = new Vector3i();
|
||||
private static final Matrix3f matrix3f = new Matrix3f();
|
||||
|
||||
@@ -27,6 +28,10 @@ public class LightState implements ISettableState<LightState> {
|
||||
public final Vector4f specular;
|
||||
public final Vector4f position;
|
||||
public final Vector3f spotDirection;
|
||||
private final Vector4f rawPosition;
|
||||
private int posMvGeneration = -1;
|
||||
private final Vector3f rawSpotDirection;
|
||||
private int spotDirLinearGeneration = -1;
|
||||
public float spotExponent;
|
||||
public float spotCutoff;
|
||||
public float spotCosCutoff;
|
||||
@@ -66,178 +71,221 @@ public class LightState implements ISettableState<LightState> {
|
||||
this.linearAttenuation = linearAttenuation;
|
||||
this.quadraticAttenuation = quadraticAttenuation;
|
||||
|
||||
this.rawPosition = new Vector4f(position);
|
||||
this.position = position;
|
||||
this.position.mul(GLStateManager.getModelViewMatrix());
|
||||
this.posMvGeneration = GLStateManager.mvGeneration;
|
||||
|
||||
this.rawSpotDirection = new Vector3f(spotDirection);
|
||||
this.spotDirection = spotDirection;
|
||||
GLStateManager.getModelViewMatrix().get3x3(matrix3f);
|
||||
this.spotDirection.mul(matrix3f);
|
||||
this.spotDirLinearGeneration = GLStateManager.mvLinearGeneration;
|
||||
}
|
||||
|
||||
public void setAmbient(FloatBuffer newBuffer) {
|
||||
public boolean setAmbient(FloatBuffer newBuffer) {
|
||||
vector4f.set(newBuffer);
|
||||
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
|
||||
this.ambient.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setAmbient(IntBuffer newBuffer) {
|
||||
public boolean setAmbient(IntBuffer newBuffer) {
|
||||
vector4i.set(newBuffer);
|
||||
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
|
||||
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
|
||||
this.ambient.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setDiffuse(FloatBuffer newBuffer) {
|
||||
public boolean setDiffuse(FloatBuffer newBuffer) {
|
||||
vector4f.set(newBuffer);
|
||||
if (GLStateManager.shouldBypassCache() || !this.diffuse.equals(vector4f)) {
|
||||
this.diffuse.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setDiffuse(IntBuffer newBuffer) {
|
||||
public boolean setDiffuse(IntBuffer newBuffer) {
|
||||
vector4i.set(newBuffer);
|
||||
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
|
||||
if (GLStateManager.shouldBypassCache() || !this.diffuse.equals(vector4f)) {
|
||||
this.diffuse.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSpecular(FloatBuffer newBuffer) {
|
||||
public boolean setSpecular(FloatBuffer newBuffer) {
|
||||
vector4f.set(newBuffer);
|
||||
if (GLStateManager.shouldBypassCache() || !this.specular.equals(vector4f)) {
|
||||
this.specular.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSpecular(IntBuffer newBuffer) {
|
||||
public boolean setSpecular(IntBuffer newBuffer) {
|
||||
vector4i.set(newBuffer);
|
||||
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
|
||||
if (GLStateManager.shouldBypassCache() || !this.specular.equals(vector4f)) {
|
||||
this.specular.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setPosition(FloatBuffer newBuffer) {
|
||||
// We are bypassing cache everytime for position, because the necessary components to enable tracking the cache
|
||||
// are big and probably more than just bypassing. We would need to track the modelview matrix it was transformed
|
||||
// with and the untransformed coordinates in addition to the final transformation.
|
||||
this.position.set(newBuffer);
|
||||
this.position.mul(GLStateManager.getModelViewMatrix());
|
||||
public boolean setPosition(FloatBuffer newBuffer) {
|
||||
vector4f.set(newBuffer);
|
||||
return applyPosition(vector4f);
|
||||
}
|
||||
|
||||
public void setPosition(IntBuffer newBuffer) {
|
||||
public boolean setPosition(IntBuffer newBuffer) {
|
||||
vector4i.set(newBuffer);
|
||||
this.position.set((float) vector4i.x, (float) vector4i.y, (float) vector4i.z, (float) vector4i.w);
|
||||
this.position.mul(GLStateManager.getModelViewMatrix());
|
||||
vector4f.set((float) vector4i.x, (float) vector4i.y, (float) vector4i.z, (float) vector4i.w);
|
||||
return applyPosition(vector4f);
|
||||
}
|
||||
|
||||
public void setSpotDirection(FloatBuffer newBuffer) {
|
||||
// We are bypassing cache everytime for spot direction, because the necessary components to enable tracking the cache
|
||||
// are big and probably more than just bypassing. We would need to track the modelview matrix it was transformed
|
||||
// with and the untransformed coordinates in addition to the final transformation.
|
||||
GLStateManager.getModelViewMatrix().get3x3(matrix3f);
|
||||
this.spotDirection.set(newBuffer);
|
||||
this.spotDirection.mul(matrix3f);
|
||||
private boolean applyPosition(Vector4f raw) {
|
||||
final int mvGen = GLStateManager.mvGeneration;
|
||||
if (GLStateManager.shouldBypassCache() || this.posMvGeneration != mvGen || !this.rawPosition.equals(raw)) {
|
||||
this.rawPosition.set(raw);
|
||||
this.posMvGeneration = mvGen;
|
||||
this.position.set(raw);
|
||||
this.position.mul(GLStateManager.getModelViewMatrix());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSpotDirection(IntBuffer newBuffer) {
|
||||
public boolean setSpotDirection(FloatBuffer newBuffer) {
|
||||
vector3f.set(newBuffer);
|
||||
return applySpotDirection(vector3f);
|
||||
}
|
||||
|
||||
public boolean setSpotDirection(IntBuffer newBuffer) {
|
||||
vector3i.set(newBuffer);
|
||||
GLStateManager.getModelViewMatrix().get3x3(matrix3f);
|
||||
this.spotDirection.set((float) vector3i.x, (float) vector3i.y, (float) vector3i.z);
|
||||
this.spotDirection.mul(matrix3f);
|
||||
vector3f.set((float) vector3i.x, (float) vector3i.y, (float) vector3i.z);
|
||||
return applySpotDirection(vector3f);
|
||||
}
|
||||
|
||||
public void setSpotExponent(FloatBuffer newBuffer) {
|
||||
setSpotExponent(newBuffer.get());
|
||||
private boolean applySpotDirection(Vector3f raw) {
|
||||
final int mvLinearGen = GLStateManager.mvLinearGeneration;
|
||||
if (GLStateManager.shouldBypassCache() || this.spotDirLinearGeneration != mvLinearGen || !this.rawSpotDirection.equals(raw)) {
|
||||
this.rawSpotDirection.set(raw);
|
||||
this.spotDirLinearGeneration = mvLinearGen;
|
||||
GLStateManager.getModelViewMatrix().get3x3(matrix3f);
|
||||
this.spotDirection.set(raw);
|
||||
this.spotDirection.mul(matrix3f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSpotExponent(IntBuffer newBuffer) {
|
||||
setSpotExponent((float) newBuffer.get());
|
||||
public boolean setSpotExponent(FloatBuffer newBuffer) {
|
||||
return setSpotExponent(newBuffer.get());
|
||||
}
|
||||
|
||||
public void setSpotExponent(int i) {
|
||||
setSpotExponent((float) i);
|
||||
public boolean setSpotExponent(IntBuffer newBuffer) {
|
||||
return setSpotExponent((float) newBuffer.get());
|
||||
}
|
||||
|
||||
public void setSpotExponent(float f) {
|
||||
public boolean setSpotExponent(int i) {
|
||||
return setSpotExponent((float) i);
|
||||
}
|
||||
|
||||
public boolean setSpotExponent(float f) {
|
||||
if (GLStateManager.shouldBypassCache() || Float.compare(f, this.spotExponent) != 0) {
|
||||
this.spotExponent = f;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSpotCutoff(FloatBuffer newBuffer) {
|
||||
setSpotCutoff(newBuffer.get());
|
||||
public boolean setSpotCutoff(FloatBuffer newBuffer) {
|
||||
return setSpotCutoff(newBuffer.get());
|
||||
}
|
||||
|
||||
public void setSpotCutoff(IntBuffer newBuffer) {
|
||||
setSpotCutoff((float) newBuffer.get());
|
||||
public boolean setSpotCutoff(IntBuffer newBuffer) {
|
||||
return setSpotCutoff((float) newBuffer.get());
|
||||
}
|
||||
|
||||
public void setSpotCutoff(int i) {
|
||||
setSpotCutoff((float) i);
|
||||
public boolean setSpotCutoff(int i) {
|
||||
return setSpotCutoff((float) i);
|
||||
}
|
||||
|
||||
public void setSpotCutoff(float f) {
|
||||
public boolean setSpotCutoff(float f) {
|
||||
if (GLStateManager.shouldBypassCache() || Float.compare(f, this.spotCutoff) != 0) {
|
||||
this.spotCutoff = f;
|
||||
this.spotCosCutoff = (float) Math.cos(Math.toRadians(f));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setConstantAttenuation(FloatBuffer newBuffer) {
|
||||
setConstantAttenuation(newBuffer.get());
|
||||
public boolean setConstantAttenuation(FloatBuffer newBuffer) {
|
||||
return setConstantAttenuation(newBuffer.get());
|
||||
}
|
||||
|
||||
public void setConstantAttenuation(IntBuffer newBuffer) {
|
||||
setConstantAttenuation((float) newBuffer.get());
|
||||
public boolean setConstantAttenuation(IntBuffer newBuffer) {
|
||||
return setConstantAttenuation((float) newBuffer.get());
|
||||
}
|
||||
|
||||
public void setConstantAttenuation(int i) {
|
||||
setConstantAttenuation((float) i);
|
||||
public boolean setConstantAttenuation(int i) {
|
||||
return setConstantAttenuation((float) i);
|
||||
}
|
||||
|
||||
public void setConstantAttenuation(float f) {
|
||||
public boolean setConstantAttenuation(float f) {
|
||||
if (GLStateManager.shouldBypassCache() || Float.compare(f, this.constantAttenuation) != 0) {
|
||||
this.constantAttenuation = f;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setLinearAttenuation(FloatBuffer newBuffer) {
|
||||
setLinearAttenuation(newBuffer.get());
|
||||
public boolean setLinearAttenuation(FloatBuffer newBuffer) {
|
||||
return setLinearAttenuation(newBuffer.get());
|
||||
}
|
||||
|
||||
public void setLinearAttenuation(IntBuffer newBuffer) {
|
||||
setLinearAttenuation((float) newBuffer.get());
|
||||
public boolean setLinearAttenuation(IntBuffer newBuffer) {
|
||||
return setLinearAttenuation((float) newBuffer.get());
|
||||
}
|
||||
|
||||
public void setLinearAttenuation(int i) {
|
||||
setLinearAttenuation((float) i);
|
||||
public boolean setLinearAttenuation(int i) {
|
||||
return setLinearAttenuation((float) i);
|
||||
}
|
||||
|
||||
public void setLinearAttenuation(float f) {
|
||||
public boolean setLinearAttenuation(float f) {
|
||||
if (GLStateManager.shouldBypassCache() || Float.compare(f, this.linearAttenuation) != 0) {
|
||||
this.linearAttenuation = f;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setQuadraticAttenuation(FloatBuffer newBuffer) {
|
||||
setQuadraticAttenuation(newBuffer.get());
|
||||
public boolean setQuadraticAttenuation(FloatBuffer newBuffer) {
|
||||
return setQuadraticAttenuation(newBuffer.get());
|
||||
}
|
||||
|
||||
public void setQuadraticAttenuation(IntBuffer newBuffer) {
|
||||
setQuadraticAttenuation((float) newBuffer.get());
|
||||
public boolean setQuadraticAttenuation(IntBuffer newBuffer) {
|
||||
return setQuadraticAttenuation((float) newBuffer.get());
|
||||
}
|
||||
|
||||
public void setQuadraticAttenuation(int i) {
|
||||
setQuadraticAttenuation((float) i);
|
||||
public boolean setQuadraticAttenuation(int i) {
|
||||
return setQuadraticAttenuation((float) i);
|
||||
}
|
||||
|
||||
public void setQuadraticAttenuation(float f) {
|
||||
public boolean setQuadraticAttenuation(float f) {
|
||||
if (GLStateManager.shouldBypassCache() || Float.compare(f, this.quadraticAttenuation) != 0) {
|
||||
this.quadraticAttenuation = f;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -246,7 +294,11 @@ public class LightState implements ISettableState<LightState> {
|
||||
this.diffuse.set(state.diffuse);
|
||||
this.specular.set(state.specular);
|
||||
this.position.set(state.position);
|
||||
this.rawPosition.set(state.rawPosition);
|
||||
this.posMvGeneration = state.posMvGeneration;
|
||||
this.spotDirection.set(state.spotDirection);
|
||||
this.rawSpotDirection.set(state.rawSpotDirection);
|
||||
this.spotDirLinearGeneration = state.spotDirLinearGeneration;
|
||||
this.spotExponent = state.spotExponent;
|
||||
this.spotCutoff = state.spotCutoff;
|
||||
this.spotCosCutoff = state.spotCosCutoff;
|
||||
|
||||
@@ -37,82 +37,100 @@ public class MaterialState implements ISettableState<MaterialState> {
|
||||
colorIndexes = new Vector3f(0.0F, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
public void setAmbient(FloatBuffer newBuffer) {
|
||||
public boolean setAmbient(FloatBuffer newBuffer) {
|
||||
vector4f.set(newBuffer);
|
||||
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
|
||||
this.ambient.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setAmbient(IntBuffer newBuffer) {
|
||||
public boolean setAmbient(IntBuffer newBuffer) {
|
||||
vector4i.set(newBuffer);
|
||||
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
|
||||
if (GLStateManager.shouldBypassCache() || !this.ambient.equals(vector4f)) {
|
||||
this.ambient.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setDiffuse(FloatBuffer newBuffer) {
|
||||
public boolean setDiffuse(FloatBuffer newBuffer) {
|
||||
vector4f.set(newBuffer);
|
||||
if (GLStateManager.shouldBypassCache() || !this.diffuse.equals(vector4f)) {
|
||||
this.diffuse.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setDiffuse(IntBuffer newBuffer) {
|
||||
public boolean setDiffuse(IntBuffer newBuffer) {
|
||||
vector4i.set(newBuffer);
|
||||
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
|
||||
if (GLStateManager.shouldBypassCache() || !this.diffuse.equals(vector4f)) {
|
||||
this.diffuse.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSpecular(FloatBuffer newBuffer) {
|
||||
public boolean setSpecular(FloatBuffer newBuffer) {
|
||||
vector4f.set(newBuffer);
|
||||
if (GLStateManager.shouldBypassCache() || !this.specular.equals(vector4f)) {
|
||||
this.specular.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSpecular(IntBuffer newBuffer) {
|
||||
public boolean setSpecular(IntBuffer newBuffer) {
|
||||
vector4i.set(newBuffer);
|
||||
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
|
||||
if (GLStateManager.shouldBypassCache() || !this.specular.equals(vector4f)) {
|
||||
this.specular.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setEmission(FloatBuffer newBuffer) {
|
||||
public boolean setEmission(FloatBuffer newBuffer) {
|
||||
vector4f.set(newBuffer);
|
||||
if (GLStateManager.shouldBypassCache() || !this.emission.equals(vector4f)) {
|
||||
this.emission.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setEmission(IntBuffer newBuffer) {
|
||||
public boolean setEmission(IntBuffer newBuffer) {
|
||||
vector4i.set(newBuffer);
|
||||
vector4f.set(i2f(vector4i.x), i2f(vector4i.y), i2f(vector4i.z), i2f(vector4i.w));
|
||||
if (GLStateManager.shouldBypassCache() || !this.emission.equals(vector4f)) {
|
||||
this.emission.set(vector4f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setShininess(float val) {
|
||||
public boolean setShininess(float val) {
|
||||
if (GLStateManager.shouldBypassCache() || Float.compare(this.shininess, val) != 0) {
|
||||
this.shininess = val;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// On the buffer versions of shininess, we need to call `get(0)` explicitly specifying the index
|
||||
// otherwise calling `get()` advances the buffer position, and some mods may re-use the same buffer
|
||||
// in subsequent calls, resulting in a BufferUnderflowException.
|
||||
public void setShininess(FloatBuffer newBuffer) { setShininess(newBuffer.get(0)); }
|
||||
public boolean setShininess(FloatBuffer newBuffer) { return setShininess(newBuffer.get(0)); }
|
||||
|
||||
public void setShininess(IntBuffer newBuffer) { setShininess((float) newBuffer.get(0)); }
|
||||
public boolean setShininess(IntBuffer newBuffer) { return setShininess((float) newBuffer.get(0)); }
|
||||
|
||||
public void setShininess(int val) { setShininess((float) val);}
|
||||
public boolean setShininess(int val) { return setShininess((float) val);}
|
||||
|
||||
public void setColorIndexes(FloatBuffer newBuffer) {
|
||||
public boolean setColorIndexes(FloatBuffer newBuffer) {
|
||||
// You are reading this correctly, nvidia drivers flip the y and z values in
|
||||
// glMaterial specifically for GL_COLOR_INDEXES. Other drivers do not do this.
|
||||
// This probably breaks things but who knows, GLSM is setup to track to what the
|
||||
@@ -125,15 +143,19 @@ public class MaterialState implements ISettableState<MaterialState> {
|
||||
|
||||
if (GLStateManager.shouldBypassCache() || !this.colorIndexes.equals(vector3f)) {
|
||||
this.colorIndexes.set(vector3f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setColorIndexes(IntBuffer newBuffer) {
|
||||
public boolean setColorIndexes(IntBuffer newBuffer) {
|
||||
vector3i.set(newBuffer.get(0), newBuffer.get(2), newBuffer.get(1));
|
||||
vector3f.set((float) vector3i.x, (float) vector3i.y, (float) vector3i.z);
|
||||
if (GLStateManager.shouldBypassCache() || !this.colorIndexes.equals(vector3f)) {
|
||||
this.colorIndexes.set(vector3f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+15
-15
@@ -5,10 +5,10 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -69,42 +69,42 @@ public class TessellatorStreamingDrawer {
|
||||
/**
|
||||
* Draw the vanilla Tessellator's data using streaming VBO+VAO instead of FFP client arrays.
|
||||
*/
|
||||
public static int draw(ITessellatorData tess) {
|
||||
if (!tess.isDrawing()) {
|
||||
public static int draw(Tessellator tess) {
|
||||
if (!tess.isDrawing) {
|
||||
throw new IllegalStateException("Not tesselating!");
|
||||
}
|
||||
|
||||
tess.setDrawing(false);
|
||||
tess.isDrawing = false;
|
||||
|
||||
final int vertexCount = tess.getVertexCount();
|
||||
final int vertexCount = tess.vertexCount;
|
||||
if (vertexCount == 0) {
|
||||
final int result = tess.getRawBufferIndex() * 4;
|
||||
tess.angelica$reset();
|
||||
final int result = tess.rawBufferIndex * 4;
|
||||
tess.reset();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Determine the optimal vertex format from the tessellator's flags
|
||||
final int flags = VertexFlags.convertToFlags(tess.hasTexture(), tess.hasColor(), tess.hasNormals(), tess.hasBrightness());
|
||||
final int flags = VertexFlags.convertToFlags(tess.hasTexture, tess.hasColor, tess.hasNormals, tess.hasBrightness);
|
||||
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[flags];
|
||||
final int vertexSize = format.getVertexSize();
|
||||
|
||||
final int requiredBytes = vertexCount * vertexSize;
|
||||
ensureRepackCapacity(requiredBytes);
|
||||
|
||||
final long writePtr = format.writeToBuffer0(repackAddress, tess.getRawBuffer(), tess.getRawBufferIndex());
|
||||
final long writePtr = format.writeToBuffer0(repackAddress, tess.rawBuffer, tess.rawBufferIndex);
|
||||
repackBuffer.position(0);
|
||||
repackBuffer.limit((int)(writePtr - repackAddress));
|
||||
|
||||
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.getDrawMode(), vertexCount);
|
||||
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
|
||||
// Shrink rawBuffer if oversized
|
||||
if (tess.getRawBufferSize() > 0x20000 && tess.getRawBufferIndex() < (tess.getRawBufferSize() << 3)) {
|
||||
tess.setRawBufferSize(0x10000);
|
||||
tess.setRawBuffer(new int[tess.getRawBufferSize()]);
|
||||
if (tess.rawBufferSize > 0x20000 && tess.rawBufferIndex < (tess.rawBufferSize << 3)) {
|
||||
tess.rawBufferSize = 0x10000;
|
||||
tess.rawBuffer = new int[tess.rawBufferSize];
|
||||
}
|
||||
|
||||
final int result = tess.getRawBufferIndex() * 4;
|
||||
tess.angelica$reset();
|
||||
final int result = tess.rawBufferIndex * 4;
|
||||
tess.reset();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ public class TextureInfo {
|
||||
|
||||
@Getter protected final int id;
|
||||
protected int internalFormat = -1;
|
||||
protected int resolvedInternalFormat = -1;
|
||||
protected int width = -1;
|
||||
protected int height = -1;
|
||||
|
||||
@@ -34,6 +35,17 @@ public class TextureInfo {
|
||||
return internalFormat;
|
||||
}
|
||||
|
||||
public int getResolvedInternalFormat() {
|
||||
if (resolvedInternalFormat == -1) {
|
||||
resolvedInternalFormat = RenderSystem.getTexLevelParameteri(id, 0, GL11.GL_TEXTURE_INTERNAL_FORMAT);
|
||||
}
|
||||
return resolvedInternalFormat;
|
||||
}
|
||||
|
||||
public boolean needsInternalFormatResolve() {
|
||||
return resolvedInternalFormat == -1;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
if (width == -1) {
|
||||
width = RenderSystem.getTexLevelParameteri(id, 0, GL11.GL_TEXTURE_WIDTH);
|
||||
|
||||
@@ -5,6 +5,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -45,6 +48,7 @@ public class TextureInfoCache {
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
if (info == null) return;
|
||||
info.internalFormat = internalformat;
|
||||
info.resolvedInternalFormat = isGenericCompressedInternalFormat(internalformat) ? -1 : internalformat;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
}
|
||||
@@ -55,11 +59,24 @@ public class TextureInfoCache {
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
if (info == null) return;
|
||||
info.internalFormat = internalformat;
|
||||
info.resolvedInternalFormat = isGenericCompressedInternalFormat(internalformat) ? -1 : internalformat;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isGenericCompressedInternalFormat(int internalFormat) {
|
||||
return switch (internalFormat) {
|
||||
case GL30.GL_COMPRESSED_RED,
|
||||
GL30.GL_COMPRESSED_RG,
|
||||
GL13.GL_COMPRESSED_RGB,
|
||||
GL13.GL_COMPRESSED_RGBA,
|
||||
GL21.GL_COMPRESSED_SRGB,
|
||||
GL21.GL_COMPRESSED_SRGB_ALPHA -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
public void onDeleteTexture(int id) {
|
||||
if (id < 0) return;
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class CompatUniformManagerTest {
|
||||
|
||||
// Use program and upload — should not error
|
||||
GL20.glUseProgram(program);
|
||||
CompatUniformManager.onUseProgram(program);
|
||||
CompatUniformManager.refreshCompatUniforms(program);
|
||||
|
||||
// Verify no GL errors
|
||||
assertEquals(0, org.lwjgl.opengl.GL11.glGetError(), "GL error after uniform upload");
|
||||
@@ -146,7 +146,7 @@ class CompatUniformManagerTest {
|
||||
|
||||
// Use program and upload — should not error
|
||||
GL20.glUseProgram(program);
|
||||
CompatUniformManager.onUseProgram(program);
|
||||
CompatUniformManager.refreshCompatUniforms(program);
|
||||
|
||||
assertEquals(0, org.lwjgl.opengl.GL11.glGetError(), "GL error after fog uniform upload");
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ class DisplayListCompilationGLSMTest {
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glTranslatef(10, 0, 0); // Applied immediately
|
||||
DisplayListManager.flushMatrix();
|
||||
|
||||
FloatBuffer afterTranslate = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, afterTranslate);
|
||||
@@ -216,6 +217,7 @@ class DisplayListCompilationGLSMTest {
|
||||
|
||||
GLStateManager.glTranslatef(10, 0, 0); // Applied immediately
|
||||
GLStateManager.glRotatef(45, 0, 1, 0); // Applied immediately
|
||||
DisplayListManager.flushMatrix();
|
||||
|
||||
FloatBuffer afterTransforms = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, afterTransforms);
|
||||
@@ -241,6 +243,7 @@ class DisplayListCompilationGLSMTest {
|
||||
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(10, 0, 0); // Applied immediately
|
||||
DisplayListManager.flushMatrix();
|
||||
|
||||
FloatBuffer afterTranslate = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, afterTranslate);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import org.junit.jupiter.api.extension.AfterAllCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.ContextAttribs;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.DisplayMode;
|
||||
import org.lwjgl.opengl.PixelFormat;
|
||||
|
||||
/**
|
||||
* 3.3 core-profile context extension
|
||||
*/
|
||||
public class GLSMCoreExtension implements BeforeAllCallback, AfterAllCallback {
|
||||
private boolean ownsDisplay = false;
|
||||
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) throws LWJGLException {
|
||||
if (Display.isCreated()) return;
|
||||
Display.setDisplayModeAndFullscreen(new DisplayMode(800, 600));
|
||||
Display.setResizable(false);
|
||||
Display.setFullscreen(false);
|
||||
Display.create(
|
||||
new PixelFormat().withDepthBits(24).withStencilBits(8),
|
||||
new ContextAttribs(3, 3).withProfileCore(true).withForwardCompatible(true));
|
||||
ownsDisplay = true;
|
||||
GLStateManager.initialize(GLSMInitConfig.builder().displaySize(800, 600).build());
|
||||
GLStateManager.setRunningSplash(false);
|
||||
GLStateManager.markSplashComplete();
|
||||
GLStateManager.BYPASS_CACHE = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterAll(ExtensionContext context) {
|
||||
if (ownsDisplay && Display.isCreated()) Display.destroy();
|
||||
ownsDisplay = false;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
|
||||
import org.junit.jupiter.api.extension.ExecutionCondition;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.Display;
|
||||
@@ -19,7 +21,19 @@ import java.lang.reflect.Field;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
|
||||
|
||||
public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback, ExtensionContext.Store.CloseableResource {
|
||||
public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback, ExecutionCondition, ExtensionContext.Store.CloseableResource {
|
||||
|
||||
// Compat profile is not available on OSX
|
||||
private static final boolean IS_MAC = System.getProperty("os.name", "").toLowerCase().contains("mac");
|
||||
private static final boolean FORCE_RUN = Boolean.getBoolean("angelica.glsm.test.forceRun");
|
||||
|
||||
@Override
|
||||
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
|
||||
if (IS_MAC && !FORCE_RUN) {
|
||||
return ConditionEvaluationResult.disabled("Skipped on macOS: needs GL3+ compat profile");
|
||||
}
|
||||
return ConditionEvaluationResult.enabled("OS supports GL3+ compat profile");
|
||||
}
|
||||
|
||||
private static final Unsafe theUnsafe;
|
||||
static {
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.joml.Vector4f;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
public class GLSM_DirtyTracking_UnitTest {
|
||||
|
||||
static final FloatBuffer f4b = ByteBuffer.allocateDirect(4 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
static final FloatBuffer f3b = ByteBuffer.allocateDirect(3 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
|
||||
@BeforeEach
|
||||
void pushTrackedState() {
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT | GL11.GL_FOG_BIT | GL11.GL_CURRENT_BIT);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void popTrackedState() {
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
|
||||
@Test
|
||||
void materialBumpsOnlyOnChange() {
|
||||
newf4b(0.1F, 0.2F, 0.3F, 0.4F);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
|
||||
final int gen = GLStateManager.lightingGeneration;
|
||||
|
||||
newf4b(0.1F, 0.2F, 0.3F, 0.4F);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
|
||||
assertEquals(gen, GLStateManager.lightingGeneration, "identical material must not bump lightingGeneration");
|
||||
|
||||
newf4b(0.9F, 0.2F, 0.3F, 0.4F);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
|
||||
assertEquals(gen + 1, GLStateManager.lightingGeneration, "changed material must bump lightingGeneration once");
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightModelBumpsOnlyOnChange() {
|
||||
newf4b(0.5F, 0.5F, 0.5F, 1.0F);
|
||||
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b);
|
||||
final int gen = GLStateManager.lightingGeneration;
|
||||
|
||||
newf4b(0.5F, 0.5F, 0.5F, 1.0F);
|
||||
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b);
|
||||
assertEquals(gen, GLStateManager.lightingGeneration, "identical light model ambient must not bump");
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightColorBumpsOnlyOnChange() {
|
||||
newf4b(0.25F, 0.5F, 0.75F, 1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b);
|
||||
final int gen = GLStateManager.lightingGeneration;
|
||||
|
||||
newf4b(0.25F, 0.5F, 0.75F, 1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b);
|
||||
assertEquals(gen, GLStateManager.lightingGeneration, "identical light diffuse must not bump");
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightPositionDirtySkipsUnderSameModelview() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glTranslatef(1.0F, 2.0F, 3.0F);
|
||||
|
||||
newf4b(0.1F, 0.2F, 0.3F, 1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
|
||||
final int gen = GLStateManager.lightingGeneration;
|
||||
|
||||
newf4b(0.1F, 0.2F, 0.3F, 1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
|
||||
assertEquals(gen, GLStateManager.lightingGeneration, "same position under same modelview must not bump");
|
||||
|
||||
GLStateManager.glTranslatef(5.0F, 0.0F, 0.0F);
|
||||
newf4b(0.1F, 0.2F, 0.3F, 1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
|
||||
assertEquals(gen + 1, GLStateManager.lightingGeneration, "same raw position under changed modelview must bump");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
@Test
|
||||
void translationDoesNotBumpLinearGeneration() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
|
||||
final int mv = GLStateManager.mvGeneration;
|
||||
final int lin = GLStateManager.mvLinearGeneration;
|
||||
|
||||
GLStateManager.glTranslatef(1.0F, 2.0F, 3.0F);
|
||||
assertEquals(mv + 1, GLStateManager.mvGeneration, "translate must bump mvGeneration");
|
||||
assertEquals(lin, GLStateManager.mvLinearGeneration, "translate must NOT bump mvLinearGeneration");
|
||||
|
||||
GLStateManager.glRotatef(30.0F, 0.0F, 1.0F, 0.0F);
|
||||
assertEquals(mv + 2, GLStateManager.mvGeneration, "rotate must bump mvGeneration");
|
||||
assertEquals(lin + 1, GLStateManager.mvLinearGeneration, "rotate must bump mvLinearGeneration");
|
||||
|
||||
GLStateManager.glScalef(2.0F, 2.0F, 2.0F);
|
||||
assertEquals(lin + 2, GLStateManager.mvLinearGeneration, "scale must bump mvLinearGeneration");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fogBumpsOnlyOnChange() {
|
||||
GLStateManager.glFogf(GL11.GL_FOG_DENSITY, 0.5F);
|
||||
final int gen = GLStateManager.fragmentGeneration;
|
||||
|
||||
GLStateManager.glFogf(GL11.GL_FOG_DENSITY, 0.5F);
|
||||
assertEquals(gen, GLStateManager.fragmentGeneration, "identical fog density must not bump fragmentGeneration");
|
||||
|
||||
GLStateManager.glFogf(GL11.GL_FOG_DENSITY, 0.7F);
|
||||
assertEquals(gen + 1, GLStateManager.fragmentGeneration, "changed fog density must bump fragmentGeneration");
|
||||
}
|
||||
|
||||
@Test
|
||||
void spotDirectionDirtySkipsUnderTranslationOnly() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
newf3b(0.0F, 0.0F, -1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b);
|
||||
final int gen = GLStateManager.lightingGeneration;
|
||||
|
||||
GLStateManager.glTranslatef(5.0F, 0.0F, 0.0F);
|
||||
newf3b(0.0F, 0.0F, -1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b);
|
||||
assertEquals(gen, GLStateManager.lightingGeneration, "spot direction under translation-only must not bump");
|
||||
|
||||
GLStateManager.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
|
||||
newf3b(0.0F, 0.0F, -1.0F);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b);
|
||||
assertEquals(gen + 1, GLStateManager.lightingGeneration, "same raw spot direction under changed 3x3 must bump");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorMaterialFreezesLastColorOnDisable() {
|
||||
GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE);
|
||||
GLStateManager.glColor4f(1.0F, 0.0F, 0.0F, 1.0F); // enable-time color
|
||||
GLStateManager.enableColorMaterial();
|
||||
GLStateManager.glColor4f(0.0F, 0.0F, 1.0F, 1.0F); // last color while enabled
|
||||
GLStateManager.disableColorMaterial();
|
||||
|
||||
final Vector4f diffuse = GLStateManager.getFrontMaterial().diffuse;
|
||||
assertEquals(0.0F, diffuse.x, "diffuse must freeze at last color (blue), not enable-time red");
|
||||
assertEquals(0.0F, diffuse.y, "green channel must be 0");
|
||||
assertEquals(1.0F, diffuse.z, "blue channel must be 1");
|
||||
}
|
||||
|
||||
static void newf4b(float x, float y, float z, float w) {
|
||||
f4b.clear();
|
||||
f4b.put(x).put(y).put(z).put(w);
|
||||
f4b.flip();
|
||||
}
|
||||
|
||||
static void newf3b(float x, float y, float z) {
|
||||
f3b.clear();
|
||||
f3b.put(x).put(y).put(z);
|
||||
f3b.flip();
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -94,9 +94,9 @@ class GLSM_DisplayList_Batching_Test {
|
||||
// Verify: 3 DrawRangeCmds (one per draw, each with different transform)
|
||||
Int2IntMap counts = compiled.getCommandCounts();
|
||||
int drawCount = counts.getOrDefault(GLCommand.DRAW_RANGE, 0);
|
||||
int multMatrixCount = counts.getOrDefault(GLCommand.MULT_MATRIX, 0);
|
||||
assertEquals(3, drawCount, "3 draws with different transforms should produce 3 DrawRangeCmds");
|
||||
assertEquals(2, multMatrixCount, "Should have MultMatrix commands for each unique transform");
|
||||
int multMatrixCount = counts.getOrDefault(GLCommand.TRANSLATE, 0);
|
||||
assertEquals(1, drawCount, "3 draws with different transforms should produce 1 DrawRangeCmd (transforms get prebaked)");
|
||||
assertEquals(1, multMatrixCount, "Should have 1 Translate at the end (the transforms in the draws are prebaked)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -140,13 +140,14 @@ class GLSM_DisplayList_Batching_Test {
|
||||
// Verify: 3 DrawRangeCmds (different transforms require separate draw commands)
|
||||
Int2IntMap counts = compiled.getCommandCounts();
|
||||
int drawCount = counts.getOrDefault(GLCommand.DRAW_RANGE, 0);
|
||||
assertEquals(3, drawCount, "Draws with different recorded transforms must produce separate DrawRangeCmds");
|
||||
assertEquals(1, drawCount, "3 Draws with different transforms should get prebaked + merged");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDrawsWithSameTransformProduceSingleDrawRangeCmd() {
|
||||
CompiledDisplayList compiled = createDisplayList(() -> {
|
||||
GLStateManager.glTranslatef(1.0f, 2.0f, 3.0f);
|
||||
GLStateManager.glScalef(2, 1, 3);
|
||||
createSimpleDraw();
|
||||
createSimpleDraw();
|
||||
createSimpleDraw();
|
||||
@@ -207,7 +208,7 @@ class GLSM_DisplayList_Batching_Test {
|
||||
int drawRestoreCount = counts.getOrDefault(GLCommand.DRAW_RANGE_RESTORE, 0);
|
||||
|
||||
// Verify: 2 VBOs (one for format A, one for format B)
|
||||
assertEquals(2, getVBOCount(compiled), "A A B A A pattern should produce 2 VBOs (one per format)");
|
||||
// assertEquals(2, getVBOCount(compiled), "A A B A A pattern should produce 2 VBOs (one per format)");
|
||||
|
||||
// Verify: 2 DrawRangeCmds (all A's merged into 1, B is 1)
|
||||
// With format-based batching, all A draws go into the same FormatBuffer
|
||||
|
||||
+10
-12
@@ -61,7 +61,6 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
Int2IntMap counts = compiled.getCommandCounts();
|
||||
int multMatrixCount = counts.getOrDefault(GLCommand.MULT_MATRIX, 0);
|
||||
int individualTransformCount = counts.getOrDefault(GLCommand.TRANSLATE, 0)
|
||||
+ counts.getOrDefault(GLCommand.ROTATE, 0)
|
||||
+ counts.getOrDefault(GLCommand.SCALE, 0);
|
||||
|
||||
assertEquals(1, multMatrixCount,
|
||||
@@ -420,7 +419,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
// Count MultMatrix commands before first PushMatrix
|
||||
int multMatrixBeforePushCount = 0;
|
||||
for (int i = 0; i < opcodes.size() && i < pushIndex; i++) {
|
||||
if (opcodes.get(i) == GLCommand.MULT_MATRIX) {
|
||||
if (opcodes.get(i) == GLCommand.TRANSLATE) {
|
||||
multMatrixBeforePushCount++;
|
||||
}
|
||||
}
|
||||
@@ -666,13 +665,11 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
assertNotNull(compiled);
|
||||
|
||||
IntList opcodes = compiled.getCommandOpcodes();
|
||||
int pushIndex = opcodes.indexOf(GLCommand.PUSH_MATRIX);
|
||||
int popIndex = opcodes.indexOf(GLCommand.POP_MATRIX);
|
||||
int multMatrixIndex = opcodes.indexOf(GLCommand.MULT_MATRIX);
|
||||
int scaleIndex = opcodes.indexOf(GLCommand.SCALE);
|
||||
int drawRangeIndex = opcodes.indexOf(GLCommand.DRAW_RANGE);
|
||||
|
||||
assertTrue(pushIndex < multMatrixIndex, "Push before MultMatrix");
|
||||
assertTrue(multMatrixIndex < drawRangeIndex, "MultMatrix before DrawRange");
|
||||
assertTrue(scaleIndex == -1, "Scale gets prebaked");
|
||||
assertTrue(drawRangeIndex < popIndex, "DrawRange before Pop");
|
||||
}
|
||||
|
||||
@@ -800,6 +797,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
GLStateManager.glNewList(testList, mode);
|
||||
|
||||
GLStateManager.glScalef(0.5f, 0.5f, 1.0f);
|
||||
GLStateManager.glTranslatef(2, 2, 2);
|
||||
// Barrier commands between scale and draw
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.disableLighting();
|
||||
@@ -824,8 +822,8 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
assertEquals(1, counts.getOrDefault(GLCommand.DRAW_RANGE, 0), "1 DrawRange for mode " + mode);
|
||||
|
||||
IntList opcodes = compiled.getCommandOpcodes();
|
||||
assertTrue(opcodes.indexOf(GLCommand.MULT_MATRIX) < opcodes.indexOf(GLCommand.DRAW_RANGE),
|
||||
"MultMatrix before DrawRange for mode " + mode);
|
||||
assertTrue(opcodes.indexOf(GLCommand.MULT_MATRIX) > opcodes.indexOf(GLCommand.DRAW_RANGE),
|
||||
"MultMatrix after DrawRange for mode " + mode);
|
||||
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
@@ -874,10 +872,10 @@ class GLSM_DisplayList_TransformCollapsing_Test {
|
||||
Int2IntMap counts = compiled.getCommandCounts();
|
||||
IntList opcodes = compiled.getCommandOpcodes();
|
||||
|
||||
// Should have exactly 2 MULT_MATRIX (T1 before push, T2 after push)
|
||||
// Bug would produce 3 (T1, T2, spurious T1^-1)
|
||||
assertEquals(2, counts.getOrDefault(GLCommand.MULT_MATRIX, 0),
|
||||
"2 MULT_MATRIX expected (T1 before push, T2 after push)");
|
||||
// Should have exactly 1 MULT_MATRIX (T1 before push, T2 after push is already prebaked, will get discarded.)
|
||||
// No prebaking would produce 2 (last one wouldn't get discarded)
|
||||
assertEquals(1, counts.getOrDefault(GLCommand.MULT_MATRIX, 0),
|
||||
"1 MULT_MATRIX expected (T1 before push, T2 after push is already prebaked, will get discarded.)");
|
||||
|
||||
// Verify ordering
|
||||
int firstMultMatrix = opcodes.indexOf(GLCommand.MULT_MATRIX);
|
||||
|
||||
+96
@@ -6,6 +6,10 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.EXTTextureCompressionS3TC;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -15,6 +19,98 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
@ExtendWith(GLSMExtension.class)
|
||||
public class GLSM_TextureInfoCache_UnitTest {
|
||||
|
||||
@Test
|
||||
void testGenericCompressedUploadResolvesToSpecificFormat() {
|
||||
int texId = GL11.glGenTextures();
|
||||
try {
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
ByteBuffer pixels = BufferUtils.createByteBuffer(64 * 64 * 4);
|
||||
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL13.GL_COMPRESSED_RGBA, 64, 64, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
int format = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_INTERNAL_FORMAT);
|
||||
assertNotEquals(-1, format);
|
||||
assertNotEquals(GL30.GL_COMPRESSED_RED, format);
|
||||
assertNotEquals(GL30.GL_COMPRESSED_RG, format);
|
||||
assertNotEquals(GL13.GL_COMPRESSED_RGB, format);
|
||||
assertNotEquals(GL13.GL_COMPRESSED_RGBA, format);
|
||||
assertNotEquals(GL21.GL_COMPRESSED_SRGB, format);
|
||||
assertNotEquals(GL21.GL_COMPRESSED_SRGB_ALPHA, format);
|
||||
} finally {
|
||||
GL11.glDeleteTextures(texId);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonGenericUploadCachesWithoutRoundtrip() {
|
||||
int texId = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
ByteBuffer pixels = BufferUtils.createByteBuffer(64 * 64 * 4);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 64, 64, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
TextureInfo info = TextureInfoCache.INSTANCE.getInfo(texId);
|
||||
assertFalse(info.needsInternalFormatResolve());
|
||||
assertEquals(GL11.GL_RGBA8, info.getResolvedInternalFormat());
|
||||
assertEquals(GL11.GL_RGBA8, GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_INTERNAL_FORMAT));
|
||||
} finally {
|
||||
GL11.glDeleteTextures(texId);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenericCompressedPreservesAsPassedInternalFormat() {
|
||||
int texId = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
ByteBuffer pixels = BufferUtils.createByteBuffer(64 * 64 * 4);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL13.GL_COMPRESSED_RGBA, 64, 64, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
TextureInfo info = TextureInfoCache.INSTANCE.getInfo(texId);
|
||||
assertEquals(GL13.GL_COMPRESSED_RGBA, info.getInternalFormat());
|
||||
assertTrue(info.needsInternalFormatResolve());
|
||||
} finally {
|
||||
GL11.glDeleteTextures(texId);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRepeatedQueryAfterResolveUsesCache() {
|
||||
int texId = GL11.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
ByteBuffer pixels = BufferUtils.createByteBuffer(64 * 64 * 4);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL13.GL_COMPRESSED_RGBA, 64, 64, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
TextureInfo info = TextureInfoCache.INSTANCE.getInfo(texId);
|
||||
assertTrue(info.needsInternalFormatResolve());
|
||||
int first = GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_INTERNAL_FORMAT);
|
||||
assertFalse(info.needsInternalFormatResolve());
|
||||
int second = GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_INTERNAL_FORMAT);
|
||||
assertEquals(first, second);
|
||||
} finally {
|
||||
GL11.glDeleteTextures(texId);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsGenericCompressedInternalFormatHelper() {
|
||||
assertTrue(TextureInfoCache.isGenericCompressedInternalFormat(GL30.GL_COMPRESSED_RED));
|
||||
assertTrue(TextureInfoCache.isGenericCompressedInternalFormat(GL30.GL_COMPRESSED_RG));
|
||||
assertTrue(TextureInfoCache.isGenericCompressedInternalFormat(GL13.GL_COMPRESSED_RGB));
|
||||
assertTrue(TextureInfoCache.isGenericCompressedInternalFormat(GL13.GL_COMPRESSED_RGBA));
|
||||
assertTrue(TextureInfoCache.isGenericCompressedInternalFormat(GL21.GL_COMPRESSED_SRGB));
|
||||
assertTrue(TextureInfoCache.isGenericCompressedInternalFormat(GL21.GL_COMPRESSED_SRGB_ALPHA));
|
||||
assertFalse(TextureInfoCache.isGenericCompressedInternalFormat(GL11.GL_RGBA));
|
||||
assertFalse(TextureInfoCache.isGenericCompressedInternalFormat(GL11.GL_RGBA8));
|
||||
assertFalse(TextureInfoCache.isGenericCompressedInternalFormat(EXTTextureCompressionS3TC.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT));
|
||||
assertFalse(TextureInfoCache.isGenericCompressedInternalFormat(0));
|
||||
assertFalse(TextureInfoCache.isGenericCompressedInternalFormat(-1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Texture deletion is server-side; cache must be invalidated regardless of client caching state.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,8 @@ class SharedDrawableVaoTest {
|
||||
private static final String VERT_330 = "#version 330 core\nvoid main() { gl_Position = vec4(0.0); }\n";
|
||||
private static final String FRAG_330 = "#version 330 core\nout vec4 o;\nvoid main() { o = vec4(0.0); }\n";
|
||||
|
||||
private static boolean ownsDisplay = false;
|
||||
|
||||
@BeforeAll
|
||||
static void createContext() throws LWJGLException {
|
||||
assumeTrue(!Display.isCreated(), "inherited Display -- skipping to avoid profile drift");
|
||||
@@ -31,11 +33,13 @@ class SharedDrawableVaoTest {
|
||||
new PixelFormat().withDepthBits(24).withStencilBits(8),
|
||||
new ContextAttribs(4, 1).withProfileCore(true).withForwardCompatible(true));
|
||||
GL30.glBindVertexArray(GL30.glGenVertexArrays());
|
||||
ownsDisplay = true;
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void destroyContext() {
|
||||
if (Display.isCreated()) Display.destroy();
|
||||
if (ownsDisplay && Display.isCreated()) Display.destroy();
|
||||
ownsDisplay = false;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+6
-6
@@ -28,7 +28,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
@Test
|
||||
void testGlColor4fSetsVertexAttrib() {
|
||||
GLStateManager.glColor4f(0.5f, 0.25f, 0.75f, 0.8f);
|
||||
GLStateManager.flushDeferredVertexAttribs();
|
||||
GLStateManager.flushDeferredVertexAttribs(0);
|
||||
|
||||
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
|
||||
assertEquals(0.5f, color[0], EPSILON, "color.r");
|
||||
@@ -40,7 +40,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
@Test
|
||||
void testGlNormal3fSetsVertexAttrib() {
|
||||
GLStateManager.glNormal3f(0.0f, 1.0f, 0.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs();
|
||||
GLStateManager.flushDeferredVertexAttribs(0);
|
||||
|
||||
float[] normal = getVertexAttrib4f(NORMAL_ATTRIB);
|
||||
assertEquals(0.0f, normal[0], EPSILON, "normal.x");
|
||||
@@ -51,7 +51,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
@Test
|
||||
void testSetLightmapTextureCoordsOnUnit1SetsVertexAttrib() {
|
||||
GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, 240.0f, 128.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs();
|
||||
GLStateManager.flushDeferredVertexAttribs(0);
|
||||
|
||||
float[] lm = getVertexAttrib4f(SECONDARY_UV_ATTRIB);
|
||||
assertEquals(240.0f, lm[0], EPSILON, "lightmap.s");
|
||||
@@ -64,7 +64,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
void testGlTexCoord2fSetsVertexAttrib() {
|
||||
// glTexCoord2f outside recording → sets default vertex attrib 2 (PRIMARY_UV)
|
||||
GLStateManager.glTexCoord2f(0.5f, 0.75f);
|
||||
GLStateManager.flushDeferredVertexAttribs();
|
||||
GLStateManager.flushDeferredVertexAttribs(0);
|
||||
|
||||
float[] uv = getVertexAttrib4f(PRIMARY_UV_ATTRIB);
|
||||
assertEquals(0.5f, uv[0], EPSILON, "texcoord.s");
|
||||
@@ -77,7 +77,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
// Second call is a cache hit — changeColor not called.
|
||||
// Vertex attrib should still hold the correct value from the first call.
|
||||
GLStateManager.glColor4f(0.3f, 0.6f, 0.9f, 1.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs();
|
||||
GLStateManager.flushDeferredVertexAttribs(0);
|
||||
|
||||
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
|
||||
assertEquals(0.3f, color[0], EPSILON, "color.r after dedup");
|
||||
@@ -89,7 +89,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
@Test
|
||||
void testCompileMode_DoesNotSetVertexAttrib() {
|
||||
GLStateManager.glColor4f(0.1f, 0.2f, 0.3f, 1.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs();
|
||||
GLStateManager.flushDeferredVertexAttribs(0);
|
||||
|
||||
int listId = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(listId, org.lwjgl.opengl.GL11.GL_COMPILE);
|
||||
|
||||
@@ -128,7 +128,7 @@ class FFPCombineShaderTest {
|
||||
setupXaeroState();
|
||||
|
||||
FragmentKey fk = FragmentKey.fromState();
|
||||
VertexKey vk = VertexKey.fromState(true, false, true, false);
|
||||
VertexKey vk = VertexKey.fromState(true, false, true, false, fk.enabledUnitMask());
|
||||
|
||||
String fragSource = FragmentShaderGenerator.generate(fk);
|
||||
String vertSource = VertexShaderGenerator.generate(vk);
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLSMCoreExtension;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
@ExtendWith(GLSMCoreExtension.class)
|
||||
class ProgramUniformTrackingTest {
|
||||
|
||||
private static Program buildProgram(boolean hasColor) {
|
||||
final VertexKey vk = VertexKey.fromState(hasColor, false, false, false, 0);
|
||||
final FragmentKey fk = FragmentKey.fromState();
|
||||
return Program.create(vk, fk, VertexShaderGenerator.generate(vk), FragmentShaderGenerator.generate(fk), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void matrixTrackingIsPerProgram() {
|
||||
final Uniforms uniforms = new Uniforms();
|
||||
final Program a = buildProgram(true);
|
||||
final Program b = buildProgram(false);
|
||||
assertNotEquals(a.getProgramId(), b.getProgramId(), "distinct vertex keys must yield distinct programs");
|
||||
try {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
GLStateManager.glTranslatef(1.0F, 2.0F, 3.0F);
|
||||
final int genA = GLStateManager.mvGeneration;
|
||||
GL20.glUseProgram(a.getProgramId());
|
||||
uniforms.upload(a);
|
||||
assertEquals(genA, a.uploadState.mvGen, "A records the mv generation it uploaded");
|
||||
|
||||
GLStateManager.glTranslatef(4.0F, 0.0F, 0.0F);
|
||||
final int genB = GLStateManager.mvGeneration;
|
||||
assertNotEquals(genA, genB);
|
||||
GL20.glUseProgram(b.getProgramId());
|
||||
uniforms.upload(b);
|
||||
assertEquals(genB, b.uploadState.mvGen, "B records the new mv generation");
|
||||
assertEquals(genA, a.uploadState.mvGen, "A is unchanged -- tracking is per-program, not global");
|
||||
|
||||
// Return to A with the modelview still at genB: A is stale -> re-uploads and catches up.
|
||||
GL20.glUseProgram(a.getProgramId());
|
||||
uniforms.upload(a);
|
||||
assertEquals(genB, a.uploadState.mvGen, "A re-uploads on return and catches up to genB");
|
||||
|
||||
// Re-upload A with no change: gate (mvGeneration == A.mvGen) is satisfied -> skip.
|
||||
uniforms.upload(a);
|
||||
assertEquals(genB, a.uploadState.mvGen, "A already current -> skipped");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
} finally {
|
||||
GL20.glUseProgram(0);
|
||||
a.destroy();
|
||||
b.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -207,7 +207,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
|
||||
new AttribLayoutKey(new int[]{0}, new int[]{2}, new int[]{GL11.GL_FLOAT}, new boolean[]{false}),
|
||||
GL11.GL_TRIANGLES, 4, 6, vtx, idx, new BatchedIndexedDrawCmd());
|
||||
|
||||
rec.recordIndexedDrawCapture(cap);
|
||||
rec.writeIndexedDrawCapture(cap);
|
||||
assertFalse(cap.isFreed());
|
||||
|
||||
rec.free();
|
||||
|
||||
@@ -74,6 +74,7 @@ dependencies {
|
||||
// LWJGL3
|
||||
implementation("org.lwjgl:lwjgl:${lwjglVersion}")
|
||||
implementation("org.lwjgl:lwjgl-opengl:${lwjglVersion}")
|
||||
compileOnly("org.lwjgl:lwjgl-sdl:${lwjglVersion}")
|
||||
runtimeOnly("org.lwjgl:lwjgl:$lwjglVersion:$lwjglNatives")
|
||||
runtimeOnly("org.lwjgl:lwjgl-opengl:$lwjglVersion:$lwjglNatives")
|
||||
|
||||
|
||||
+54
-1
@@ -23,6 +23,9 @@ import org.lwjgl.opengl.GL44C;
|
||||
import org.lwjgl.opengl.GL45C;
|
||||
import org.lwjgl.opengl.GLCapabilities;
|
||||
import org.lwjgl.opengl.GLDebugMessageCallback;
|
||||
import org.lwjgl.sdl.SDL_DropEvent;
|
||||
import org.lwjgl.sdl.SDL_EventFilter;
|
||||
import org.lwjgl.sdl.SDLEvents;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -31,6 +34,10 @@ import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* LWJGL3 GL implementation of {@link RenderBackend}.
|
||||
@@ -43,6 +50,10 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
private GLDebugMessageCallback debugCallback;
|
||||
private boolean debugOutputActive;
|
||||
|
||||
private SDL_EventFilter dropEventFilter;
|
||||
private final ConcurrentLinkedQueue<String> droppedFiles = new ConcurrentLinkedQueue<>();
|
||||
private volatile boolean watchingDrops;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
caps = GL.getCapabilities();
|
||||
@@ -50,7 +61,49 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
// no-op
|
||||
stopFileDrop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFileDrop() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startFileDrop() {
|
||||
if (watchingDrops) return;
|
||||
dropEventFilter = SDL_EventFilter.create((userData, eventPtr) -> {
|
||||
if (SDL_DropEvent.ntype(eventPtr) == SDLEvents.SDL_EVENT_DROP_FILE) {
|
||||
final String path = SDL_DropEvent.ndataString(eventPtr);
|
||||
if (path != null && !path.isEmpty()) {
|
||||
droppedFiles.add(path);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
SDLEvents.SDL_AddEventWatch(dropEventFilter, 0L);
|
||||
watchingDrops = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopFileDrop() {
|
||||
if (!watchingDrops) return;
|
||||
SDLEvents.SDL_RemoveEventWatch(dropEventFilter, 0L);
|
||||
dropEventFilter.free();
|
||||
dropEventFilter = null;
|
||||
droppedFiles.clear();
|
||||
watchingDrops = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> pollDroppedFiles() {
|
||||
if (droppedFiles.isEmpty()) return Collections.emptyList();
|
||||
final List<String> out = new ArrayList<>();
|
||||
String p;
|
||||
while ((p = droppedFiles.poll()) != null) {
|
||||
out.add(p);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.gtnewhorizons.angelica.lwjgl3;
|
||||
|
||||
import static org.lwjgl.sdl.SDLMessageBox.SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
|
||||
import static org.lwjgl.sdl.SDLMessageBox.SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
|
||||
import static org.lwjgl.sdl.SDLMessageBox.SDL_MESSAGEBOX_ERROR;
|
||||
import static org.lwjgl.sdl.SDLMessageBox.SDL_ShowMessageBox;
|
||||
|
||||
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
|
||||
import me.eigenraven.lwjgl3ify.client.MainThreadExec;
|
||||
import org.lwjgl.sdl.SDL_MessageBoxButtonData;
|
||||
import org.lwjgl.sdl.SDL_MessageBoxData;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
@Lwjgl3Aware
|
||||
public final class MissingDependencySdl {
|
||||
|
||||
private MissingDependencySdl() {}
|
||||
|
||||
public static void showFatal(String title, String message) {
|
||||
MainThreadExec.runOnMainThread(() -> showFatalOnMainThread(title, message));
|
||||
}
|
||||
|
||||
private static void showFatalOnMainThread(String title, String message) {
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
final SDL_MessageBoxData box = SDL_MessageBoxData.calloc(stack);
|
||||
box.flags(SDL_MESSAGEBOX_ERROR);
|
||||
box.title(stack.UTF8(title));
|
||||
box.message(stack.UTF8(message));
|
||||
|
||||
final SDL_MessageBoxButtonData.Buffer buttons = SDL_MessageBoxButtonData.calloc(1, stack);
|
||||
buttons.get(0).set(
|
||||
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT | SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
||||
0,
|
||||
stack.UTF8("OK"));
|
||||
box.buttons(buttons);
|
||||
|
||||
SDL_ShowMessageBox(box, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -16,7 +16,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.gtnewhorizons.gtnhsettingsconvention") version ("2.0.24")
|
||||
id("com.gtnewhorizons.gtnhsettingsconvention") version ("2.0.25")
|
||||
}
|
||||
|
||||
include("glsm")
|
||||
|
||||
@@ -560,6 +560,9 @@ public class BatchingFontRenderer {
|
||||
boolean curWave = false;
|
||||
boolean curDinnerbone = false;
|
||||
boolean curGradient = false;
|
||||
boolean curShadow = false;
|
||||
boolean curShadowCustomColor = false;
|
||||
int curShadowColorOverride = 0;
|
||||
int gradientStartRgb = 0, gradientEndRgb = 0;
|
||||
int gradientCharIndex = 0, gradientTotalChars = 0;
|
||||
float gradientStep = 0f;
|
||||
@@ -651,6 +654,19 @@ public class BatchingFontRenderer {
|
||||
curWave = !curWave;
|
||||
} else if (fmtCode == 'v' && AngelicaConfig.enableDinnerboneText) {
|
||||
curDinnerbone = !curDinnerbone;
|
||||
} else if (fmtCode == 'u' && AngelicaConfig.enableDropShadow) {
|
||||
int customRgb = (charIdx + SECTION_X_LENGTH < stringEnd)
|
||||
? ColorCodeUtils.parseSectionXAt(string, charIdx + 1)
|
||||
: -1;
|
||||
if (customRgb != -1) {
|
||||
curShadow = true;
|
||||
curShadowCustomColor = true;
|
||||
curShadowColorOverride = customRgb;
|
||||
charIdx += SECTION_X_LENGTH;
|
||||
} else {
|
||||
curShadow = !curShadow;
|
||||
if (!curShadow) curShadowCustomColor = false;
|
||||
}
|
||||
} else if (fmtCode == 'g' && AngelicaConfig.enableGradients && charIdx + GRADIENT_PAYLOAD < stringEnd) {
|
||||
int color1 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1);
|
||||
int color2 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1 + SECTION_X_LENGTH);
|
||||
@@ -674,6 +690,8 @@ public class BatchingFontRenderer {
|
||||
curWave = false;
|
||||
curDinnerbone = false;
|
||||
curGradient = false;
|
||||
curShadow = false;
|
||||
curShadowCustomColor = false;
|
||||
curColor = color;
|
||||
curShadowColor = shadowColor;
|
||||
}
|
||||
@@ -682,14 +700,14 @@ public class BatchingFontRenderer {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chr == ColorCodeUtils.ESCAPED_AMPERSAND) { chr = '&'; }
|
||||
|
||||
if (FontConfig.enableCustomFont && FontConfig.enableGlyphReplacements) {
|
||||
String chrReplacement = GlyphReplacements.customGlyphs.get(String.valueOf(chr));
|
||||
if (chrReplacement != null) {
|
||||
char replacement = chrReplacement.charAt(0);
|
||||
boolean isReplacementCharAvailable =
|
||||
FontProviderCustom.getPrimary().isGlyphAvailable(replacement)
|
||||
|| FontProviderCustom.getFallback().isGlyphAvailable(replacement);
|
||||
if (isReplacementCharAvailable) {
|
||||
final char replacement = GlyphReplacements.getReplacementGlyph(chr);
|
||||
if (replacement != 0) {
|
||||
if (FontProviderCustom.getPrimary().isGlyphAvailable(replacement)
|
||||
|| FontProviderCustom.getFallback().isGlyphAvailable(replacement)
|
||||
) {
|
||||
chr = replacement;
|
||||
}
|
||||
}
|
||||
@@ -749,13 +767,17 @@ public class BatchingFontRenderer {
|
||||
renderY += (float) Math.sin(visibleCharIndex * WAVE_FREQUENCY + time) * AngelicaConfig.waveAmplitude;
|
||||
}
|
||||
|
||||
if (enableShadow) {
|
||||
final boolean drawShadow = enableShadow || curShadow;
|
||||
if (drawShadow) {
|
||||
final int effectiveShadowColor = curShadowCustomColor
|
||||
? ((curColor & 0xFF000000) | (curShadowColorOverride & 0x00FFFFFF))
|
||||
: curShadowColor;
|
||||
for (int n = 1; n <= shadowCopies; n++) {
|
||||
final float shadowOffsetPart = shadowOffset * ((float) n / shadowCopies);
|
||||
pushTexRect(curX + shadowOffsetPart, renderY + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz, curDinnerbone);
|
||||
pushTexRect(curX + shadowOffsetPart, renderY + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, effectiveShadowColor, uStart, vStart, uSz, vSz, curDinnerbone);
|
||||
|
||||
if (curBold) {
|
||||
pushTexRect(curX + 2.0f * shadowOffsetPart, renderY + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, curShadowColor, uStart, vStart, uSz, vSz, curDinnerbone);
|
||||
pushTexRect(curX + 2.0f * shadowOffsetPart, renderY + shadowOffsetPart, glyphW - 1.0f, heightSouth, itOff, effectiveShadowColor, uStart, vStart, uSz, vSz, curDinnerbone);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -777,7 +799,7 @@ public class BatchingFontRenderer {
|
||||
both: 4(1 + 2 * shadowCopies + boldCopies)
|
||||
*/
|
||||
int charCount = 1;
|
||||
if (enableShadow) { charCount += shadowCopies * (curBold ? 2 : 1); }
|
||||
if (drawShadow) { charCount += shadowCopies * (curBold ? 2 : 1); }
|
||||
if (curBold) { charCount += boldCopies; }
|
||||
final int vtxCount = 4 * charCount;
|
||||
pushDrawCmd(idxId, vtxCount / 2 * 3, texture, chr > 255);
|
||||
@@ -824,6 +846,7 @@ public class BatchingFontRenderer {
|
||||
}
|
||||
|
||||
public float getCharWidthFine(char chr) {
|
||||
if (chr == ColorCodeUtils.ESCAPED_AMPERSAND) { chr = '&'; }
|
||||
if (chr == FORMATTING_CHAR) { return -1; }
|
||||
|
||||
if (chr == ' ' || chr == '\u00A0' || chr == '\u202F') {
|
||||
|
||||
@@ -18,6 +18,7 @@ public final class ColorCodeUtils {
|
||||
private ColorCodeUtils() {}
|
||||
|
||||
public static final char FORMATTING_CHAR = '§';
|
||||
public static final char ESCAPED_AMPERSAND = '';
|
||||
|
||||
public static final int SECTION_X_PAYLOAD = 12;
|
||||
public static final int SECTION_X_LENGTH = 14;
|
||||
@@ -25,7 +26,7 @@ public final class ColorCodeUtils {
|
||||
public static final int GRADIENT_LENGTH = 30;
|
||||
|
||||
/** Valid single {@code &} codes. {@code g} is excluded; {@code &g} only converts as part of a gradient. */
|
||||
public static final String VALID_SINGLE_CODES = "0123456789abcdefklmnorqzv";
|
||||
public static final String VALID_SINGLE_CODES = "0123456789abcdefklmnorqzvu";
|
||||
|
||||
private static final String[] SECTION_PREFIX = new String[128];
|
||||
static {
|
||||
@@ -87,6 +88,14 @@ public final class ColorCodeUtils {
|
||||
StringBuilder sb = null;
|
||||
int last = 0;
|
||||
while (idx != -1 && idx + 1 < len) {
|
||||
if (idx > 0 && text.charAt(idx - 1) == '\\') {
|
||||
if (sb == null) sb = new StringBuilder(len + 16);
|
||||
sb.append(text, last, idx - 1);
|
||||
sb.append(ESCAPED_AMPERSAND);
|
||||
last = idx + 1;
|
||||
idx = text.indexOf('&', last);
|
||||
continue;
|
||||
}
|
||||
if (text.charAt(idx + 1) == '#' && idx + 7 < len) {
|
||||
boolean validHex = true;
|
||||
for (int i = 2; i <= 7; i++) {
|
||||
|
||||
@@ -104,9 +104,7 @@ public class DeferredDrawBatcher {
|
||||
subEnd++;
|
||||
|
||||
// Don't try to merge strips, fans, loops or polygons
|
||||
if (drawMode == GL11.GL_TRIANGLE_STRIP || drawMode == GL11.GL_TRIANGLE_FAN || drawMode == GL11.GL_LINE_STRIP ||
|
||||
drawMode == GL11.GL_LINE_LOOP || drawMode == GL11.GL_QUAD_STRIP || drawMode == GL11.GL_POLYGON)
|
||||
break;
|
||||
if (GLStateManager.isContinuousDraw(drawMode)) break;
|
||||
}
|
||||
|
||||
drawPackedBatch(batchTessellator, ranges, i, subEnd, totalBytes, totalVertices, drawMode, flags);
|
||||
|
||||
+1
-2
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.compat.bettercrashes;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IRenderGlobalExt;
|
||||
import cpw.mods.fml.common.Optional;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -27,7 +26,7 @@ public class BetterCrashesCompat implements StateManager.IResettable {
|
||||
|
||||
@Override
|
||||
public void resetState() {
|
||||
((ITessellatorData) Tessellator.instance).setDrawing(false);
|
||||
Tessellator.instance.isDrawing = false;
|
||||
AngelicaMod.LOGGER.info("Reloading SodiumRenderer");
|
||||
((IRenderGlobalExt) Minecraft.getMinecraft().renderGlobal).angelica$reload();
|
||||
AngelicaMod.LOGGER.info("Resetting GLStateManager");
|
||||
|
||||
@@ -132,6 +132,10 @@ public class AngelicaConfig {
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableDinnerboneText;
|
||||
|
||||
@Config.Comment("Enable per-segment drop shadow toggle (&u) and colored shadow (&u&#RRGGBB)")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableDropShadow;
|
||||
|
||||
@Config.Comment("Wave text amplitude (how far characters bounce)")
|
||||
@Config.DefaultFloat(2.0f)
|
||||
@Config.RangeFloat(min = 1.0f, max = 8.0f)
|
||||
@@ -306,6 +310,16 @@ public class AngelicaConfig {
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public boolean narrowEars;
|
||||
|
||||
@Config.Comment("Narrow Fisk's Superheroes transformer exclusions to allow GL redirection")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public boolean narrowFiskHeroes;
|
||||
|
||||
@Config.Comment("Narrow FoamFix transformer exclusions to allow GL redirection in its repackaged Ears")
|
||||
@Config.DefaultBoolean(true)
|
||||
@Config.RequiresMcRestart
|
||||
public boolean narrowFoamFix;
|
||||
}
|
||||
|
||||
@Config.Comment("Renders chunks before neighbors are ready. Improves loading at render distance edges, useful for low render distance servers.")
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.gtnewhorizons.angelica.event;
|
||||
|
||||
import net.minecraftforge.eventbus.api.bus.EventBus;
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class ChunkBiomeDataChangedEvent extends MutableEvent {
|
||||
|
||||
public static final EventBus<ChunkBiomeDataChangedEvent> BUS = EventBus.create(ChunkBiomeDataChangedEvent.class);
|
||||
|
||||
private static final ChunkBiomeDataChangedEvent INSTANCE = new ChunkBiomeDataChangedEvent();
|
||||
|
||||
public int chunkX;
|
||||
public int chunkZ;
|
||||
|
||||
public static void post(int chunkX, int chunkZ) {
|
||||
if (!BUS.hasListeners()) return;
|
||||
INSTANCE.chunkX = chunkX;
|
||||
INSTANCE.chunkZ = chunkZ;
|
||||
BUS.post(INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.gtnewhorizons.angelica.event;
|
||||
|
||||
import net.minecraftforge.eventbus.api.bus.CancellableEventBus;
|
||||
import net.minecraftforge.eventbus.api.event.RecordEvent;
|
||||
import net.minecraftforge.eventbus.api.event.characteristic.Cancellable;
|
||||
|
||||
/**
|
||||
* Posted from {@link net.coderbot.iris.pipeline.HandRenderer}'s {@code canRender}
|
||||
* <p>
|
||||
* Cancel the event to prevent the hand from rendering.
|
||||
*/
|
||||
public record RenderHandEvent() implements RecordEvent, Cancellable {
|
||||
|
||||
public static final CancellableEventBus<RenderHandEvent> BUS = CancellableEventBus.create(RenderHandEvent.class);
|
||||
|
||||
private static final RenderHandEvent INSTANCE = new RenderHandEvent();
|
||||
|
||||
/**
|
||||
* @return true if a listener cancelled the event, meaning the hand should NOT render.
|
||||
*/
|
||||
public static boolean post() {
|
||||
if (!BUS.hasListeners()) return false;
|
||||
return BUS.post(INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class HUDCaching {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
|
||||
if (!AngelicaConfig.hudCachingActive) {
|
||||
if (!AngelicaConfig.hudCachingActive || mc.displayWidth < 16 || mc.displayHeight < 16) {
|
||||
ingame.renderGameOverlay(partialTicks, hasScreen, mouseX, mouseY);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ public class IrisGLSMBridge {
|
||||
private static Runnable fogStartListener = null;
|
||||
private static Runnable fogEndListener = null;
|
||||
private static Runnable fogDensityListener = null;
|
||||
private static Runnable colorModulatorListener = null;
|
||||
|
||||
static {
|
||||
StateUpdateNotifiers.alphaFuncNotifier = listener -> alphaFuncListener = listener;
|
||||
@@ -35,6 +36,7 @@ public class IrisGLSMBridge {
|
||||
StateUpdateNotifiers.fogStartNotifier = listener -> fogStartListener = listener;
|
||||
StateUpdateNotifiers.fogEndNotifier = listener -> fogEndListener = listener;
|
||||
StateUpdateNotifiers.fogDensityNotifier = listener -> fogDensityListener = listener;
|
||||
StateUpdateNotifiers.colorModulatorNotifier = listener -> colorModulatorListener = listener;
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
@@ -108,6 +110,10 @@ public class IrisGLSMBridge {
|
||||
}
|
||||
});
|
||||
|
||||
GLSMHooks.SHADER_COLOR_CHANGE.addListener(event -> {
|
||||
if (Iris.enabled && colorModulatorListener != null) colorModulatorListener.run();
|
||||
});
|
||||
|
||||
GLSMHooks.FOG_STATE_CHANGE.addListener(event -> {
|
||||
if (Iris.enabled) {
|
||||
if (fogModeListener != null) fogModeListener.run();
|
||||
@@ -157,17 +163,17 @@ public class IrisGLSMBridge {
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (event.postBind) return;
|
||||
|
||||
|
||||
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (!(pipeline instanceof DeferredWorldRenderingPipeline drp)) return;
|
||||
if (!drp.shouldOverrideShaders()) return;
|
||||
if (drp.getActivePassProgramId() == -1) return;
|
||||
|
||||
|
||||
if (DepthColorStorage.isOwnedProgram(event.newProgram)) {
|
||||
DepthColorStorage.unlockDepthColor();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (!event.postBind) return;
|
||||
|
||||
@@ -11,9 +11,11 @@ import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.DependencyVerifier;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
|
||||
import com.gtnewhorizons.angelica.loading.fml.compat.CompatHandlers;
|
||||
import com.gtnewhorizons.angelica.lwjgl3.MissingDependencySdl;
|
||||
import com.gtnewhorizons.angelica.mixins.Mixins;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.SharedConfig;
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
import cpw.mods.fml.relauncher.FMLRelaunchLog;
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
import jss.notfine.asm.AsmTransformers;
|
||||
import jss.notfine.asm.mappings.Namer;
|
||||
@@ -29,6 +31,8 @@ import org.apache.logging.log4j.core.config.LoggerConfig;
|
||||
import org.spongepowered.asm.launch.GlobalProperties;
|
||||
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -49,8 +53,15 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
|
||||
Launch.classLoader.addTransformerExclusion("com.gtnewhorizons.angelica.loading");
|
||||
Launch.classLoader.addTransformerExclusion("com.gtnewhorizons.angelica.glsm.GLStateManager");
|
||||
|
||||
if (Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE) {
|
||||
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
|
||||
if (rfbLoaded) {
|
||||
addLwjgl3ifyExclusions();
|
||||
} else {
|
||||
// Fucking java 8 and non RFB
|
||||
try {
|
||||
Class.forName("com.gtnewhorizon.gtnhlib.core.GTNHLibCore", true, Launch.classLoader);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -80,9 +91,10 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
|
||||
// packages to avoid interfering with mixin delegation on game classes. AngelicaLateTweaker removes this registration once
|
||||
// mixins are setup.
|
||||
if (FMLLaunchHandler.side().isClient()) {
|
||||
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
|
||||
if (!rfbLoaded) {
|
||||
Launch.classLoader.registerTransformer("com.gtnewhorizons.angelica.loading.fml.transformers.EarlyRedirectorTransformer");
|
||||
final String transformer = "com.gtnewhorizons.angelica.loading.fml.transformers.EarlyRedirectorTransformer";
|
||||
FMLRelaunchLog.finer("Registering transformer %s", transformer);
|
||||
Launch.classLoader.registerTransformer(transformer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,14 +113,37 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
|
||||
}
|
||||
|
||||
private static void verifyDependencies() {
|
||||
DependencyVerifier.verify(AngelicaTweaker.class, List.of(
|
||||
new DependencyVerifier.Check(
|
||||
"/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class",
|
||||
"Missing dependency: Angelica requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new DependencyVerifier.Check(
|
||||
"/xyz/wagyourtail/jvmdg/exc/MissingStubError.class",
|
||||
"GTNHLib is outdated: Angelica requires GTNHLib 0.9.0 or newer! Download: https://modrinth.com/mod/gtnhlib")
|
||||
));
|
||||
try {
|
||||
DependencyVerifier.verify(AngelicaTweaker.class, List.of(
|
||||
new DependencyVerifier.Check(
|
||||
"/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class",
|
||||
"Missing dependency: Angelica requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib"),
|
||||
new DependencyVerifier.Check(
|
||||
"/com/gtnewhorizon/gtnhlib/client/renderer/VertexCallbackManager.class",
|
||||
"GTNHLib is outdated: Angelica requires GTNHLib 0.10.0 or newer! Download: https://modrinth.com/mod/gtnhlib")
|
||||
));
|
||||
} catch (RuntimeException ex) {
|
||||
fatalDependencyError(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static void fatalDependencyError(String message) {
|
||||
final String title = "Angelica - Missing Dependency";
|
||||
System.err.println("FATAL: " + message);
|
||||
try {
|
||||
LOGGER.fatal(message);
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
final int lwjgl3ifyMajor = ((Integer) Launch.blackboard.getOrDefault("lwjgl3ify:major-version", Integer.MIN_VALUE));
|
||||
try {
|
||||
if (lwjgl3ifyMajor >= 3) {
|
||||
MissingDependencySdl.showFatal(title, message);
|
||||
} else if (!GraphicsEnvironment.isHeadless()) {
|
||||
JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
Runtime.getRuntime().halt(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,6 +155,10 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
|
||||
final List<String> notFineTransformers = Arrays.asList(ITransformers.getTransformers(AsmTransformers.class));
|
||||
if (!notFineTransformers.isEmpty()) Namer.initNames();
|
||||
transformers.addAll(notFineTransformers);
|
||||
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
|
||||
if (!rfbLoaded) {
|
||||
transformers.add("com.gtnewhorizons.angelica.loading.fml.transformers.IsbrhTessellatorAbuseTransformer");
|
||||
}
|
||||
transformerClasses = transformers.toArray(new String[0]);
|
||||
}
|
||||
return transformerClasses;
|
||||
@@ -147,6 +186,8 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
|
||||
narrowTransformerConfig("AdvancedLightsabers", AngelicaConfig.transformerCompat.narrowAdvancedLightsabers);
|
||||
narrowTransformerConfig("Alfheim", AngelicaConfig.transformerCompat.narrowAlfheim);
|
||||
narrowTransformerConfig("Ears", AngelicaConfig.transformerCompat.narrowEars);
|
||||
narrowTransformerConfig("FiskHeroes", AngelicaConfig.transformerCompat.narrowFiskHeroes);
|
||||
narrowTransformerConfig("FoamFix", AngelicaConfig.transformerCompat.narrowFoamFix);
|
||||
|
||||
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.IncompatibleModsDisablerTweaker");
|
||||
if (AngelicaConfig.enableHudCaching) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.gtnewhorizons.angelica.loading.fml.compat.handlers.ThaumcraftCompatHa
|
||||
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.ThaumicHorizonsCompatHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -45,20 +44,18 @@ public enum CompatHandlers {
|
||||
|
||||
/**
|
||||
* Returns extra transformers as well as the main transformer.
|
||||
* Returns an empty list if no handlers registered.
|
||||
*/
|
||||
public static List<String> getTransformers() {
|
||||
final List<ICompatHandler> handlers = getHandlers();
|
||||
if (handlers.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> transformers = new ArrayList<>();
|
||||
List<String> transformers = new ArrayList<>();
|
||||
for (ICompatHandler handler : handlers) {
|
||||
if (handler.extraTransformers() != null) {
|
||||
transformers.addAll(handler.extraTransformers());
|
||||
}
|
||||
}
|
||||
transformers.add("com.gtnewhorizons.angelica.loading.fml.transformers.GenericCompatTransformer");
|
||||
if (!handlers.isEmpty()) {
|
||||
transformers.add("com.gtnewhorizons.angelica.loading.fml.transformers.GenericCompatTransformer");
|
||||
}
|
||||
return transformers;
|
||||
}
|
||||
}
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.loading.fml.transformers;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
|
||||
import com.gtnewhorizons.angelica.loading.shared.transformers.AngelicaRedirector;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
|
||||
+1
-1
@@ -8,6 +8,7 @@ import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
/** IClassTransformer wrapper for {@link CeleritasBlockTransform} */
|
||||
public class CeleritasBlockTransformer implements IClassTransformer {
|
||||
|
||||
private final CeleritasBlockTransform inner;
|
||||
@@ -16,7 +17,6 @@ public class CeleritasBlockTransformer implements IClassTransformer {
|
||||
public CeleritasBlockTransformer() {
|
||||
this.inner = new CeleritasBlockTransform(AngelicaClientTweaker.isObfEnv());
|
||||
this.exclusions = inner.getTransformerExclusions();
|
||||
this.inner.setCeleritasSetting();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.gtnewhorizons.angelica.loading.fml.transformers;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
|
||||
import com.gtnewhorizons.angelica.loading.shared.transformers.IsbrhTessellatorAbuseTransform;
|
||||
import net.minecraft.launchwrapper.IClassTransformer;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
/** IClassTransformer wrapper for {@link IsbrhTessellatorAbuseTransform} */
|
||||
public class IsbrhTessellatorAbuseTransformer implements IClassTransformer {
|
||||
|
||||
private final IsbrhTessellatorAbuseTransform inner = new IsbrhTessellatorAbuseTransform();
|
||||
|
||||
@Override
|
||||
public byte[] transform(String name, String transformedName, byte[] basicClass) {
|
||||
if (!inner.shouldTransform(basicClass)) return basicClass;
|
||||
|
||||
final ClassReader cr = new ClassReader(basicClass);
|
||||
final ClassNode cn = new ClassNode();
|
||||
cr.accept(cn, 0);
|
||||
if (!inner.transformClassNode(cn, AngelicaClientTweaker.isObfEnv())) return basicClass;
|
||||
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(cw);
|
||||
final byte[] bytes = cw.toByteArray();
|
||||
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.loading.rfb;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.rfb.transformers.RFBAngelicaRedirector;
|
||||
import com.gtnewhorizons.angelica.loading.rfb.transformers.RFBCeleritasBlockTransformer;
|
||||
import com.gtnewhorizons.angelica.loading.rfb.transformers.RFBIsbrhTessellatorAbuseTransformer;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.PluginContext;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RetroFuturaBootstrap;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
|
||||
@@ -28,7 +29,8 @@ public class AngelicaRfbPlugin implements RfbPlugin {
|
||||
final boolean isObf = RetroFuturaBootstrap.API.launchClassLoader().findClassMetadata("net.minecraft.world.World") == null;
|
||||
return new RfbClassTransformer[] {
|
||||
new RFBAngelicaRedirector(),
|
||||
new RFBCeleritasBlockTransformer(isObf)
|
||||
new RFBCeleritasBlockTransformer(isObf),
|
||||
new RFBIsbrhTessellatorAbuseTransformer(isObf)
|
||||
};
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -52,12 +52,20 @@ public class RFBAngelicaRedirector implements RfbClassTransformer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(@NotNull ExtensibleClassLoader classLoader, @NotNull RfbClassTransformer.Context context,
|
||||
@Nullable Manifest manifest, @NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
public boolean transformClassIfNeeded(@NotNull ExtensibleClassLoader classLoader,
|
||||
@NotNull RfbClassTransformer.Context context, @Nullable Manifest manifest,
|
||||
@NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
final boolean changed = inner.transformClassNode(className, classNode.getNode());
|
||||
if (changed) {
|
||||
classNode.computeMaxs();
|
||||
AngelicaClassDump.dumpRFBClass(className, classNode, this);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(@NotNull ExtensibleClassLoader classLoader, @NotNull RfbClassTransformer.Context context,
|
||||
@Nullable Manifest manifest, @NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
transformClassIfNeeded(classLoader, context, manifest, className, classNode);
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -59,12 +59,20 @@ public class RFBCeleritasBlockTransformer implements RfbClassTransformer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(@NotNull ExtensibleClassLoader classLoader, @NotNull RfbClassTransformer.Context context,
|
||||
@Nullable Manifest manifest, @NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
public boolean transformClassIfNeeded(@NotNull ExtensibleClassLoader classLoader,
|
||||
@NotNull RfbClassTransformer.Context context, @Nullable Manifest manifest,
|
||||
@NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
final boolean changed = inner.transformClassNode(className, classNode.getNode());
|
||||
if (changed) {
|
||||
classNode.computeMaxs();
|
||||
AngelicaClassDump.dumpRFBClass(className, classNode, this);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(@NotNull ExtensibleClassLoader classLoader, @NotNull RfbClassTransformer.Context context,
|
||||
@Nullable Manifest manifest, @NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
transformClassIfNeeded(classLoader, context, manifest, className, classNode);
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package com.gtnewhorizons.angelica.loading.rfb.transformers;
|
||||
|
||||
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
|
||||
import com.gtnewhorizons.angelica.loading.shared.transformers.IsbrhTessellatorAbuseTransform;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassHeaderMetadata;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ExtensibleClassLoader;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
|
||||
import org.intellij.lang.annotations.Pattern;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
/** RfbClassTransformer wrapper for {@link IsbrhTessellatorAbuseTransform} */
|
||||
public class RFBIsbrhTessellatorAbuseTransformer implements RfbClassTransformer {
|
||||
|
||||
private final IsbrhTessellatorAbuseTransform inner;
|
||||
private final boolean isObf;
|
||||
|
||||
public RFBIsbrhTessellatorAbuseTransformer(boolean isObf) {
|
||||
this.inner = new IsbrhTessellatorAbuseTransform();
|
||||
this.isObf = isObf;
|
||||
}
|
||||
|
||||
@Pattern("[a-z0-9-]+")
|
||||
@Override
|
||||
public @NotNull String id() {
|
||||
return "isbrh-tessellator-abuse";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] sortAfter() {
|
||||
return new String[] {"*", "mixin:mixin"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String @Nullable [] sortBefore() {
|
||||
return new String[] {"lwjgl3ify:redirect"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldTransformClass(@NotNull ExtensibleClassLoader classLoader,
|
||||
@NotNull RfbClassTransformer.Context context, @Nullable Manifest manifest, @NotNull String className,
|
||||
@NotNull ClassNodeHandle classNode) {
|
||||
if (!classNode.isPresent()) return false;
|
||||
final ClassHeaderMetadata metadata = classNode.getOriginalMetadata();
|
||||
return metadata != null && metadata.binaryInterfaceNames.contains(IsbrhTessellatorAbuseTransform.ISBRH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transformClassIfNeeded(@NotNull ExtensibleClassLoader classLoader, @NotNull RfbClassTransformer.Context context,
|
||||
@Nullable Manifest manifest, @NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
final boolean changed = inner.transformClassNode(classNode.getNode(), this.isObf);
|
||||
if (changed) {
|
||||
classNode.computeMaxs();
|
||||
AngelicaClassDump.dumpRFBClass(className, classNode, this);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformClass(@NotNull ExtensibleClassLoader classLoader, @NotNull RfbClassTransformer.Context context,
|
||||
@Nullable Manifest manifest, @NotNull String className, @NotNull ClassNodeHandle classNode) {
|
||||
transformClassIfNeeded(classLoader, context, manifest, className, classNode);
|
||||
}
|
||||
}
|
||||
+30
-34
@@ -31,16 +31,21 @@ public final class CeleritasBlockTransform implements Opcodes {
|
||||
private static final Logger LOGGER = LogManager.getLogger("CeleritasBlockTransformer");
|
||||
private static final String BlockClass = "net/minecraft/block/Block";
|
||||
private static final String ThreadedBlockData = "com/gtnewhorizons/angelica/client/rendering/ThreadedBlockData";
|
||||
/** All classes under <tt>net.minecraft.block.Block*</tt> are Block subclasses save for these. */
|
||||
private static final String[] VanillaBlockExclusions = {
|
||||
"net/minecraft/block/IGrowable",
|
||||
"net/minecraft/block/ITileEntityProvider",
|
||||
"net/minecraft/block/BlockEventData",
|
||||
"net/minecraft/block/BlockSourceImpl",
|
||||
"net/minecraft/block/material/"
|
||||
};
|
||||
|
||||
private final Map<String, String> fieldNameToRedirect = new HashMap<>();
|
||||
private final Set<String> blockSubclasses = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
private final Set<String> moddedBlockSubclasses = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
// Block subclass owners we shouldn't redirect because they shadow some fields we want to redirect
|
||||
private final Set<String> blockSubclassExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
private final ClassConstantPoolParser cstPoolParser;
|
||||
|
||||
private final MethodHandle celeritasEnabledGetter;
|
||||
private boolean isCeleritasEnabled;
|
||||
|
||||
public CeleritasBlockTransform(boolean isObf) {
|
||||
final List<Pair<String, String>> mappings = ImmutableList.of(
|
||||
Pair.of("minX", "field_149759_B"),
|
||||
@@ -56,37 +61,30 @@ public final class CeleritasBlockTransform implements Opcodes {
|
||||
}
|
||||
|
||||
this.cstPoolParser = new ClassConstantPoolParser(this.fieldNameToRedirect.keySet().toArray(new String[0]));
|
||||
}
|
||||
|
||||
blockSubclasses.add(BlockClass);
|
||||
|
||||
try {
|
||||
// Needed because the config is loaded in LaunchClassLoader, but we need to access it in the parent system loader.
|
||||
final Class<?> angelicaConfig = Class.forName("com.gtnewhorizons.angelica.config.AngelicaConfig", true, Launch.classLoader);
|
||||
celeritasEnabledGetter = MethodHandles.lookup().findStaticGetter(angelicaConfig, "enableCeleritas", boolean.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
private boolean isVanillaBlockSubclass(String className) {
|
||||
if (!className.startsWith(BlockClass)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setCeleritasSetting() {
|
||||
isCeleritasEnabled = true;
|
||||
}
|
||||
|
||||
private boolean isCeleritasEnabled() {
|
||||
if (isCeleritasEnabled) return true;
|
||||
try {
|
||||
return (boolean) celeritasEnabledGetter.invokeExact();
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
for (String exclusion : VanillaBlockExclusions) {
|
||||
if (className.startsWith(exclusion)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This method needs to be called for every class, including the ones we don't want to transform
|
||||
private boolean isBlockSubclass(String className) {
|
||||
return isVanillaBlockSubclass(className) || moddedBlockSubclasses.contains(className);
|
||||
}
|
||||
|
||||
// This method needs to be called for every class, including the ones we don't want to transform.
|
||||
// Vanilla blocks are recognized by name; only modded subclasses need tracking, and FML's
|
||||
// EventSubscriptionTransformer guarantees a modded class's superclass loads first.
|
||||
public void trackBlockSubclasses(String className, String superClassName) {
|
||||
// It works because the deepest subclasses are always transformed first due to
|
||||
// ForgeEventTransformer recursive superclass loading
|
||||
if (blockSubclasses.contains(superClassName)) {
|
||||
blockSubclasses.add(className);
|
||||
if (!isVanillaBlockSubclass(className) && isBlockSubclass(superClassName)) {
|
||||
moddedBlockSubclasses.add(className);
|
||||
|
||||
if (blockSubclassExclusions.contains(superClassName)) {
|
||||
LOGGER.info("Class {} extends a class with shadowed block bounds fields and will be skipped from redirecting", className);
|
||||
@@ -110,9 +108,6 @@ public final class CeleritasBlockTransform implements Opcodes {
|
||||
|
||||
/** @return Was the class changed? */
|
||||
public boolean transformClassNode(String transformedName, ClassNode cn) {
|
||||
if (!isCeleritasEnabled()) {
|
||||
return false;
|
||||
}
|
||||
boolean changed = false;
|
||||
if (BlockClass.equals(cn.name)) {
|
||||
changed = cn.fields.removeIf(field -> fieldNameToRedirect.containsKey(field.name));
|
||||
@@ -124,7 +119,8 @@ public final class CeleritasBlockTransform implements Opcodes {
|
||||
}
|
||||
|
||||
private void trackBlockShadowingFields(ClassNode cn) {
|
||||
if (blockSubclasses.contains(cn.name)) {
|
||||
// Only modded subclasses can shadow; vanilla declares these fields solely in Block.
|
||||
if (moddedBlockSubclasses.contains(cn.name)) {
|
||||
for (FieldNode field : cn.fields) {
|
||||
if (fieldNameToRedirect.containsKey(field.name)) {
|
||||
LOGGER.info("Class '{}' shadows one or more block bounds fields, these accesses won't be redirected!", cn.name);
|
||||
@@ -141,7 +137,7 @@ public final class CeleritasBlockTransform implements Opcodes {
|
||||
for (AbstractInsnNode node = mn.instructions.getFirst(); node != null; node = node.getNext()) {
|
||||
if ((node.getOpcode() == GETFIELD || node.getOpcode() == PUTFIELD) && node instanceof FieldInsnNode fNode) {
|
||||
String newFieldName = fieldNameToRedirect.get(fNode.name);
|
||||
if (newFieldName != null && blockSubclasses.contains(fNode.owner) && !blockSubclassExclusions.contains(fNode.owner)) {
|
||||
if (newFieldName != null && isBlockSubclass(fNode.owner) && !blockSubclassExclusions.contains(fNode.owner)) {
|
||||
if (LOG_SPAM) {
|
||||
LOGGER.info("Redirecting Block.{} in {} to thread-safe wrapper", fNode.name, transformedName);
|
||||
}
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.gtnewhorizons.angelica.loading.shared.transformers;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.asm.ClassConstantPoolParser;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
public final class IsbrhTessellatorAbuseTransform {
|
||||
|
||||
public static final String ISBRH = "cpw/mods/fml/client/registry/ISimpleBlockRenderingHandler";
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("IsbrhTessellatorAbuse");
|
||||
private static final ClassConstantPoolParser CST_POOL_PARSER = new ClassConstantPoolParser(ISBRH);
|
||||
private static final String TESSELLATOR = "net/minecraft/client/renderer/Tessellator";
|
||||
private static final String RENDER_WORLD_BLOCK_DESC = "(Lnet/minecraft/world/IBlockAccess;IIILnet/minecraft/block/Block;ILnet/minecraft/client/renderer/RenderBlocks;)Z";
|
||||
|
||||
public boolean shouldTransform(byte[] classBytes) {
|
||||
return CST_POOL_PARSER.find(classBytes);
|
||||
}
|
||||
|
||||
public boolean transformClassNode(ClassNode cn, boolean isObf) {
|
||||
MethodNode target = null;
|
||||
for (int i = 0, n = cn.methods.size(); i < n; i++) {
|
||||
final MethodNode mn = cn.methods.get(i);
|
||||
if ("renderWorldBlock".equals(mn.name) && RENDER_WORLD_BLOCK_DESC.equals(mn.desc)) {
|
||||
target = mn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target == null) return false;
|
||||
|
||||
final String draw = isObf ? "func_78381_a" : "draw";
|
||||
final String startDrawingQuads = isObf ? "func_78382_b" : "startDrawingQuads";
|
||||
final String startDrawing = isObf ? "func_78371_b" : "startDrawing";
|
||||
|
||||
boolean changed = false;
|
||||
AbstractInsnNode insn = target.instructions.getFirst();
|
||||
while (insn != null) {
|
||||
final AbstractInsnNode next = insn.getNext();
|
||||
if (insn instanceof MethodInsnNode min && min.getOpcode() == Opcodes.INVOKEVIRTUAL && TESSELLATOR.equals(min.owner)) {
|
||||
|
||||
final int popOp;
|
||||
final boolean pushIntResult;
|
||||
if (min.name.equals(draw) && "()I".equals(min.desc)) {
|
||||
popOp = Opcodes.POP;
|
||||
pushIntResult = true;
|
||||
} else if (min.name.equals(startDrawingQuads) && "()V".equals(min.desc)) {
|
||||
popOp = Opcodes.POP;
|
||||
pushIntResult = false;
|
||||
} else if (min.name.equals(startDrawing) && "(I)V".equals(min.desc)) {
|
||||
popOp = Opcodes.POP2;
|
||||
pushIntResult = false;
|
||||
} else {
|
||||
insn = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
target.instructions.insertBefore(insn, new InsnNode(popOp));
|
||||
if (pushIntResult) target.instructions.insertBefore(insn, new InsnNode(Opcodes.ICONST_0));
|
||||
target.instructions.remove(insn);
|
||||
LOGGER.info("Stripped Tessellator.{} from {}#renderWorldBlock", min.name, cn.name);
|
||||
changed = true;
|
||||
}
|
||||
insn = next;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
@@ -35,15 +35,19 @@ public enum Mixins implements IMixins {
|
||||
, "angelica.MixinGameSettings"
|
||||
, "angelica.MixinMinecraft"
|
||||
, "angelica.MixinMinecraft_FrameHook"
|
||||
, "angelica.MixinMinecraft_IconifyGuard"
|
||||
, "angelica.MixinMinecraftServer"
|
||||
, "angelica.bugfixes.MixinItemRenderer_EdgeDepth"
|
||||
, "angelica.bugfixes.MixinModelCreeper_AuraBodyInflate"
|
||||
, "angelica.bugfixes.MixinModelSkeleton_LegPelvisZFight"
|
||||
, "angelica.bugfixes.MixinModelWither_ArmorCentering"
|
||||
, "angelica.bugfixes.MixinRenderBlocks_CrossedSquaresNormal"
|
||||
, "angelica.bugfixes.MixinRenderCreeper_AuraDepth"
|
||||
, "angelica.bugfixes.MixinRenderGlobal_DeferredEntityOverlay"
|
||||
, "angelica.bugfixes.MixinRenderGlobal_DestroyBlock"
|
||||
, "angelica.bugfixes.MixinRenderMooshroom_MushroomTint"
|
||||
, "angelica.bugfixes.MixinRendererLivingEntity_DeferredEntityOverlay"
|
||||
, "angelica.bugfixes.MixinRendererLivingEntity_OverlayTint"
|
||||
, "angelica.bugfixes.MixinRenderWither_ArmorCentering"
|
||||
, "angelica.bugfixes.MixinRendererLivingEntity_EyeDepth"
|
||||
, "angelica.debug.MixinMinecraft_FPSCap"
|
||||
@@ -70,6 +74,12 @@ public enum Mixins implements IMixins {
|
||||
.addClientMixins("angelica.gui.MixinGuiMainMenu")
|
||||
),
|
||||
|
||||
ANGELICA_GL_SPLASH_TEXT(
|
||||
new MixinBuilder("Rewrite 'OpenGL 1.2!' splash to reflect the actual GL context")
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins("angelica.gui.MixinGuiMainMenuSplash")
|
||||
),
|
||||
|
||||
ANGELICA_FONT_RENDERER(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableFontRenderer)
|
||||
@@ -122,7 +132,10 @@ public enum Mixins implements IMixins {
|
||||
|
||||
ANGELICA_ITEM_RENDERER_OPTIMIZATION(new MixinBuilder("Optimizes in-world item rendering")
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins("angelica.itemrenderer.MixinItemRenderer")
|
||||
.addClientMixins(
|
||||
"angelica.itemrenderer.MixinItemRenderer",
|
||||
"angelica.itemrenderer.MixinRenderBlocks"
|
||||
)
|
||||
.setApplyIf(() -> AngelicaConfig.optimizeInWorldItemRendering)),
|
||||
|
||||
ANGELICA_OPTIMIZE_GLALLOCATION(new MixinBuilder("Replace HashMap with fastutil Int2IntMap in GLAllocation")
|
||||
@@ -264,6 +277,8 @@ public enum Mixins implements IMixins {
|
||||
, "shaders.MixinRenderNameTag"
|
||||
, "shaders.MixinRenderPlayerArmor"
|
||||
, "shaders.MixinTileEntityBeaconRenderer"
|
||||
, "shaders.MixinWorldNbtCache"
|
||||
, "shaders.MixinNetHandlerPlayClient_NbtRemesh"
|
||||
, "shaders.MixinRenderEndPortal"
|
||||
, "shaders.MixinTileEntityRendererDispatcher"
|
||||
, "shaders.MixinGlProgram"
|
||||
@@ -314,6 +329,15 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
IRIS_SHADERS_CHISEL_BEACON(new MixinBuilder("Iris Chisel Beacon Beam")
|
||||
.setPhase(Phase.LATE)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addRequiredMod(TargetedMod.CHISEL)
|
||||
.addClientMixins(
|
||||
"chisel.MixinRenderCarvableBeacon"
|
||||
)
|
||||
),
|
||||
|
||||
ANGELICA_TEXTURE(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris || AngelicaConfig.enableCeleritas)
|
||||
|
||||
@@ -10,6 +10,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
ARCHAICFIX("org.embeddedt.archaicfix.ArchaicCore", "archaicfix"),
|
||||
BACKHAND("xonin.backhand.coremod.BackhandLoadingPlugin", "backhand"),
|
||||
CAMPFIRE_BACKPORT(null, "campfirebackport"),
|
||||
CHISEL(null, "chisel"),
|
||||
COFHCORE( "cofh.asm.LoadingPlugin", "CoFHCore"),
|
||||
DYNAMIC_SURROUNDINGS_MIST("org.blockartistry.mod.DynSurround.mixinplugin.DynamicSurroundingsEarlyMixins", "dsurround"),
|
||||
DYNAMIC_SURROUNDINGS_ORIGINAL("org.blockartistry.mod.DynSurround.asm.TransformLoader", "dsurround"),
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasSetup;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.threading.ChunkTaskRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.threading.DefaultChunkTaskProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.threading.ThreadedChunkTaskProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.items.BlockRenderListManager;
|
||||
import com.gtnewhorizons.angelica.rendering.items.ItemRenderListManager;
|
||||
import com.gtnewhorizons.angelica.utils.AnimationMode;
|
||||
import com.gtnewhorizons.angelica.utils.ManagedEnum;
|
||||
import com.gtnewhorizons.angelica.zoom.Zoom;
|
||||
@@ -145,6 +147,10 @@ public final class ClientProxy extends CommonProxy {
|
||||
if (AngelicaConfig.enableDynamicLights) {
|
||||
EntityLightConfig.init(new java.io.File(mc.mcDataDir, "config"));
|
||||
}
|
||||
if(AngelicaConfig.optimizeInWorldItemRendering){
|
||||
ItemRenderListManager.registerReloadListener();
|
||||
BlockRenderListManager.registerReloadListener();
|
||||
}
|
||||
|
||||
// Register debug commands in dev environment only
|
||||
if (!AngelicaClientTweaker.isObfEnv()) {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.gtnewhorizons.angelica.render;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
final class CloudEdges {
|
||||
private static final int CELL_EMPTY_THRESHOLD = 10;
|
||||
|
||||
final int width, height;
|
||||
private final int wMask, hMask;
|
||||
private final boolean whPow2;
|
||||
private final long[] opaqueBits;
|
||||
final int opaqueCount;
|
||||
|
||||
CloudEdges(int width, int height, ByteBuffer rgba) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.wMask = width - 1;
|
||||
this.hMask = height - 1;
|
||||
this.whPow2 = (width & wMask) == 0 && (height & hMask) == 0;
|
||||
final int total = width * height;
|
||||
final long[] bits = new long[(total + 63) >>> 6];
|
||||
int op = 0;
|
||||
for (int i = 0; i < total; i++) {
|
||||
if ((rgba.get(i * 4 + 3) & 0xFF) >= CELL_EMPTY_THRESHOLD) {
|
||||
bits[i >>> 6] |= 1L << (i & 63);
|
||||
op++;
|
||||
}
|
||||
}
|
||||
this.opaqueBits = bits;
|
||||
this.opaqueCount = op;
|
||||
}
|
||||
|
||||
boolean cellOpaque(int x, int z) {
|
||||
final int xw, zh;
|
||||
if (whPow2) {
|
||||
xw = x & wMask;
|
||||
zh = z & hMask;
|
||||
} else {
|
||||
xw = Math.floorMod(x, width);
|
||||
zh = Math.floorMod(z, height);
|
||||
}
|
||||
final int idx = xw + zh * width;
|
||||
return ((opaqueBits[idx >>> 6] >>> (idx & 63)) & 1L) != 0L;
|
||||
}
|
||||
|
||||
long readOpaqueRowBits(int x0, int z) {
|
||||
final int zMod, x0Mod;
|
||||
if (whPow2) {
|
||||
zMod = z & hMask;
|
||||
x0Mod = x0 & wMask;
|
||||
} else {
|
||||
zMod = Math.floorMod(z, height);
|
||||
x0Mod = Math.floorMod(x0, width);
|
||||
}
|
||||
if (x0Mod + 10 <= width) {
|
||||
return readContigBits(opaqueBits, (long) zMod * width + x0Mod, 10);
|
||||
}
|
||||
final int colsBeforeWrap = width - x0Mod;
|
||||
final long firstPart = readContigBits(opaqueBits, (long) zMod * width + x0Mod, colsBeforeWrap);
|
||||
final long secondPart = readContigBits(opaqueBits, (long) zMod * width, 10 - colsBeforeWrap);
|
||||
return firstPart | (secondPart << colsBeforeWrap);
|
||||
}
|
||||
|
||||
private static long readContigBits(long[] bits, long startBitIdx, int count) {
|
||||
final int wordIdx = (int) (startBitIdx >>> 6);
|
||||
final int bitOff = (int) (startBitIdx & 63);
|
||||
final long mask = count == 64 ? -1L : (1L << count) - 1L;
|
||||
final long w0 = bits[wordIdx];
|
||||
if (bitOff + count <= 64) {
|
||||
return (w0 >>> bitOff) & mask;
|
||||
}
|
||||
final long w1 = bits[wordIdx + 1];
|
||||
return ((w0 >>> bitOff) | (w1 << (64 - bitOff))) & mask;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,78 @@
|
||||
package com.gtnewhorizons.angelica.render;
|
||||
|
||||
import org.embeddedt.embeddium.impl.gl.shader.ShaderBindingContext;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformFloat4v;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformInt;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformMatrix4f;
|
||||
|
||||
final class CloudUniforms {
|
||||
static final float ALPHA = 0.8f;
|
||||
|
||||
final GlUniformMatrix4f mvp;
|
||||
final GlUniformFloat4v colorMult;
|
||||
final GlUniformFloat4v fogParams;
|
||||
final GlUniformFloat4v fogColor;
|
||||
final GlUniformInt fogEnabled;
|
||||
final GlUniformInt tex;
|
||||
|
||||
private int lastFogEnabled = -1;
|
||||
private float lastFogR = Float.NaN, lastFogG = Float.NaN, lastFogB = Float.NaN;
|
||||
private float lastColorR = Float.NaN, lastColorG = Float.NaN, lastColorB = Float.NaN;
|
||||
private float lastFogParamX = Float.NaN, lastFogParamY = Float.NaN;
|
||||
private final float[] uploadBuf = new float[4];
|
||||
|
||||
CloudUniforms(ShaderBindingContext ctx) {
|
||||
mvp = ctx.bindUniform("u_MVPMatrix", GlUniformMatrix4f::new);
|
||||
colorMult = ctx.bindUniform("u_ColorMult", GlUniformFloat4v::new);
|
||||
fogParams = ctx.bindUniform("u_FogParams", GlUniformFloat4v::new);
|
||||
fogColor = ctx.bindUniform("u_FogColor", GlUniformFloat4v::new);
|
||||
fogEnabled = ctx.bindUniform("u_FogEnabled", GlUniformInt::new);
|
||||
tex = ctx.bindUniform("u_Tex", GlUniformInt::new);
|
||||
}
|
||||
|
||||
void setFogEnabled(boolean enabled) {
|
||||
final int val = enabled ? 1 : 0;
|
||||
if (val != lastFogEnabled) {
|
||||
lastFogEnabled = val;
|
||||
fogEnabled.setInt(val);
|
||||
}
|
||||
}
|
||||
|
||||
void setFogColor(float r, float g, float b) {
|
||||
if (r != lastFogR || g != lastFogG || b != lastFogB) {
|
||||
lastFogR = r;
|
||||
lastFogG = g;
|
||||
lastFogB = b;
|
||||
uploadBuf[0] = r;
|
||||
uploadBuf[1] = g;
|
||||
uploadBuf[2] = b;
|
||||
uploadBuf[3] = 1.0f;
|
||||
fogColor.set(uploadBuf);
|
||||
}
|
||||
}
|
||||
|
||||
void setFogParams(float x, float y) {
|
||||
if (x != lastFogParamX || y != lastFogParamY) {
|
||||
lastFogParamX = x;
|
||||
lastFogParamY = y;
|
||||
uploadBuf[0] = x;
|
||||
uploadBuf[1] = y;
|
||||
uploadBuf[2] = 0f;
|
||||
uploadBuf[3] = 0f;
|
||||
fogParams.set(uploadBuf);
|
||||
}
|
||||
}
|
||||
|
||||
void setColorMult(float r, float g, float b) {
|
||||
if (r != lastColorR || g != lastColorG || b != lastColorB) {
|
||||
lastColorR = r;
|
||||
lastColorG = g;
|
||||
lastColorB = b;
|
||||
uploadBuf[0] = r;
|
||||
uploadBuf[1] = g;
|
||||
uploadBuf[2] = b;
|
||||
uploadBuf[3] = ALPHA;
|
||||
colorMult.set(uploadBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,10 @@ import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.resources.FallbackResourceManager;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.client.resources.IResourcePack;
|
||||
import net.minecraft.client.resources.SimpleReloadableResourceManager;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
@@ -50,8 +54,23 @@ public class EmissiveTextureAutoloader {
|
||||
loc.getResourceDomain(),
|
||||
"textures/blocks/" + loc.getResourcePath() + ".png"
|
||||
);
|
||||
|
||||
final IResourceManager resMan = Minecraft.getMinecraft().getResourceManager();
|
||||
if (resMan instanceof SimpleReloadableResourceManager simple) {
|
||||
FallbackResourceManager fallback = simple.domainResourceManagers.get(full.getResourceDomain());
|
||||
if (fallback != null) {
|
||||
for (IResourcePack rp : fallback.resourcePacks) {
|
||||
if (rp != null && rp.resourceExists(full)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
try {
|
||||
Minecraft.getMinecraft().getResourceManager().getResource(full);
|
||||
resMan.getResource(full);
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
return false;
|
||||
|
||||
@@ -1,28 +1,45 @@
|
||||
package com.gtnewhorizons.angelica.render;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VertexBufferType;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.client.rendering.GlUniformFloat2v;
|
||||
import net.irisshaders.iris.api.v0.IrisApi;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.GlProgram;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.GlShader;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.ShaderConstants;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.ShaderType;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformFloat;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformFloat3v;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformFloat4v;
|
||||
import org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniformMatrix4f;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
import org.joml.Matrix4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public final class SelectionBoxRenderer {
|
||||
|
||||
private static GlProgram<SelectionBoxUniforms> program;
|
||||
private static int emptyVao;
|
||||
private static IVertexArrayObject vao;
|
||||
private static final Matrix4f mvpMatrix = new Matrix4f();
|
||||
private static final float[] colorBuf = new float[4];
|
||||
|
||||
private static final int VERT_COUNT = 24;
|
||||
private static final float[] UNIT_EDGES = {
|
||||
0,0,0, 1,0,0, 1,0,0, 1,0,1,
|
||||
1,0,1, 0,0,1, 0,0,1, 0,0,0,
|
||||
0,1,0, 1,1,0, 1,1,0, 1,1,1,
|
||||
1,1,1, 0,1,1, 0,1,1, 0,1,0,
|
||||
0,0,0, 0,1,0, 1,0,0, 1,1,0,
|
||||
1,0,1, 1,1,1, 0,0,1, 0,1,1
|
||||
};
|
||||
|
||||
private SelectionBoxRenderer() {}
|
||||
|
||||
public static void init() {
|
||||
@@ -40,8 +57,6 @@ public final class SelectionBoxRenderer {
|
||||
|
||||
program = builder.link(ctx -> new SelectionBoxUniforms(
|
||||
ctx.bindUniform("u_MVP", GlUniformMatrix4f::new),
|
||||
ctx.bindUniform("u_Min", GlUniformFloat3v::new),
|
||||
ctx.bindUniform("u_Max", GlUniformFloat3v::new),
|
||||
ctx.bindUniform("u_Color", GlUniformFloat4v::new),
|
||||
needsGS ? ctx.bindUniform("u_ViewportSize", GlUniformFloat2v::new) : null,
|
||||
needsGS ? ctx.bindUniform("u_LineWidth", GlUniformFloat::new) : null));
|
||||
@@ -50,20 +65,34 @@ public final class SelectionBoxRenderer {
|
||||
fs.destroy();
|
||||
if (gs != null) gs.destroy();
|
||||
|
||||
emptyVao = GLStateManager.glGenVertexArrays();
|
||||
final ByteBuffer buf = BufferUtils.createByteBuffer(UNIT_EDGES.length * Float.BYTES);
|
||||
buf.asFloatBuffer().put(UNIT_EDGES);
|
||||
vao = VertexBufferType.IMMUTABLE.allocate(DefaultVertexFormat.POSITION, GL11.GL_LINES, buf, VERT_COUNT);
|
||||
}
|
||||
|
||||
public static void draw(AxisAlignedBB aabb, int color) {
|
||||
if (program == null) return;
|
||||
if (program == null || vao == null) return;
|
||||
|
||||
final float minX = (float) aabb.minX, minY = (float) aabb.minY, minZ = (float) aabb.minZ;
|
||||
final float extX = (float) (aabb.maxX - aabb.minX);
|
||||
final float extY = (float) (aabb.maxY - aabb.minY);
|
||||
final float extZ = (float) (aabb.maxZ - aabb.minZ);
|
||||
|
||||
if (AngelicaConfig.enableIris && IrisApi.getInstance().isShaderPackInUse()) {
|
||||
drawThroughShader(minX, minY, minZ, extX, extY, extZ, color);
|
||||
} else {
|
||||
drawFast(minX, minY, minZ, extX, extY, extZ, color);
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawFast(float minX, float minY, float minZ, float extX, float extY, float extZ, int color) {
|
||||
final SelectionBoxUniforms uniforms = program.getInterface();
|
||||
|
||||
program.bind();
|
||||
|
||||
GLStateManager.getProjectionMatrix().mul(GLStateManager.getModelViewMatrix(), mvpMatrix);
|
||||
mvpMatrix.translate(minX, minY, minZ).scale(extX, extY, extZ);
|
||||
uniforms.mvp.set(mvpMatrix);
|
||||
uniforms.min.set((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ);
|
||||
uniforms.max.set((float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ);
|
||||
|
||||
if (color != -1) {
|
||||
colorBuf[0] = ((color >> 16) & 0xFF) / 255.0f;
|
||||
@@ -84,25 +113,42 @@ public final class SelectionBoxRenderer {
|
||||
uniforms.lineWidth.setFloat(GLStateManager.getLineState().getWidth());
|
||||
}
|
||||
|
||||
GLStateManager.glBindVertexArray(emptyVao);
|
||||
GLStateManager.glDrawArrays(GL11.GL_LINES, 0, 24);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
vao.render();
|
||||
|
||||
program.unbind();
|
||||
}
|
||||
|
||||
private static void drawThroughShader(float minX, float minY, float minZ, float extX, float extY, float extZ, int color) {
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(minX, minY, minZ);
|
||||
GLStateManager.glScalef(extX, extY, extZ);
|
||||
|
||||
final Color4 prev = GLStateManager.getColor();
|
||||
final boolean overrideColor = color != -1;
|
||||
if (overrideColor) {
|
||||
GLStateManager.glColor4f(((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, 1.0f);
|
||||
}
|
||||
|
||||
vao.render();
|
||||
|
||||
if (overrideColor) {
|
||||
GLStateManager.glColor4f(prev.getRed(), prev.getGreen(), prev.getBlue(), prev.getAlpha());
|
||||
}
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
public static void destroy() {
|
||||
if (program != null) {
|
||||
program.destroy();
|
||||
program = null;
|
||||
}
|
||||
if (emptyVao != 0) {
|
||||
GLStateManager.glDeleteVertexArrays(emptyVao);
|
||||
emptyVao = 0;
|
||||
if (vao != null) {
|
||||
vao.delete();
|
||||
vao = null;
|
||||
}
|
||||
}
|
||||
|
||||
private record SelectionBoxUniforms(GlUniformMatrix4f mvp, GlUniformFloat3v min, GlUniformFloat3v max, GlUniformFloat4v color, GlUniformFloat2v viewportSize, GlUniformFloat lineWidth) {
|
||||
private record SelectionBoxUniforms(GlUniformMatrix4f mvp, GlUniformFloat4v color, GlUniformFloat2v viewportSize, GlUniformFloat lineWidth) {
|
||||
|
||||
}
|
||||
}
|
||||
+35
-5
@@ -12,8 +12,11 @@ import com.prupe.mcpatcher.mal.block.RenderBlocksUtils;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
import net.coderbot.iris.block_rendering.BlockMaterialMapping;
|
||||
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
|
||||
import net.coderbot.iris.block_rendering.NbtConditionalIdMap;
|
||||
import net.coderbot.iris.vertices.ExtendedDataHelper;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockDoublePlant;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
@@ -118,6 +121,10 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
final Map<Block, BlockRenderLayer> blockTypeIds = provider != null ? provider.getBlockTypeIds() : null;
|
||||
final BlockRenderContext blockRenderContext = buildContext.getBlockRenderContext();
|
||||
|
||||
final NbtConditionalIdMap<Block> teMap = BlockRenderingSettings.INSTANCE.getBlockNbtMap();
|
||||
final net.minecraft.world.World mcWorld = Minecraft.getMinecraft().theWorld;
|
||||
final long currentTick = mcWorld != null ? mcWorld.getTotalWorldTime() : 0L;
|
||||
|
||||
final boolean threaded = isThreaded();
|
||||
final List<DeferredBlock> deferredBlocks = threaded ? new ArrayList<>() : null;
|
||||
|
||||
@@ -192,7 +199,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
continue;
|
||||
}
|
||||
|
||||
renderBlock(block, meta, x, y, z, pass, tessellator, renderBlocks, buffers, buildContext, blockRenderContext, minX, minY, minZ, materialOverride, isShaderPackOverride);
|
||||
renderBlock(block, meta, x, y, z, pass, tessellator, renderBlocks, buffers, buildContext, blockRenderContext, minX, minY, minZ, materialOverride, isShaderPackOverride, teMap, currentTick);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +212,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
|
||||
// Process deferred blocks on main thread if any
|
||||
if (deferredBlocks != null && !deferredBlocks.isEmpty()) {
|
||||
processDeferredBlocks(deferredBlocks, buildContext, buffers, region, minX, minY, minZ);
|
||||
processDeferredBlocks(deferredBlocks, buildContext, buffers, region, minX, minY, minZ, teMap, currentTick);
|
||||
}
|
||||
|
||||
final Reference2ReferenceMap<TerrainRenderPass, BuiltSectionMeshParts> meshes =
|
||||
@@ -236,7 +243,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderBlock(Block block, int metadata, int x, int y, int z, int pass, Tessellator tessellator, RenderBlocks renderBlocks, ChunkBuildBuffers buffers, AngelicaChunkBuildContext buildContext, BlockRenderContext blockRenderContext, int originX, int originY, int originZ, Material materialOverride, boolean isShaderPackOverride) {
|
||||
protected void renderBlock(Block block, int metadata, int x, int y, int z, int pass, Tessellator tessellator, RenderBlocks renderBlocks, ChunkBuildBuffers buffers, AngelicaChunkBuildContext buildContext, BlockRenderContext blockRenderContext, int originX, int originY, int originZ, Material materialOverride, boolean isShaderPackOverride, NbtConditionalIdMap<Block> teMap, long currentTick) {
|
||||
|
||||
final var blockMaterial = block.getMaterial();
|
||||
final boolean isFluid = blockMaterial == net.minecraft.block.material.Material.water || blockMaterial == net.minecraft.block.material.Material.lava;
|
||||
@@ -269,9 +276,32 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
&& BlockRenderingSettings.INSTANCE.getSnowyBlocks().contains(block)
|
||||
&& RenderBlocksUtils.isSnowCovered(renderBlocks.blockAccess, x, y, z)) {
|
||||
effectiveMeta |= BlockMaterialMapping.SNOWY_META_BIT;
|
||||
} else if (block instanceof BlockDoublePlant doublePlant && BlockDoublePlant.func_149887_c(metadata)) {
|
||||
effectiveMeta = 0x8 | (doublePlant.func_149885_e(renderBlocks.blockAccess, x, y, z) & 7);
|
||||
}
|
||||
contextEncoder.prepareToRenderBlock(blockRenderContext, block, effectiveMeta,
|
||||
ExtendedDataHelper.BLOCK_RENDER_TYPE, lightValue);
|
||||
// Check for TileEntity NBT-conditional shader block ID override
|
||||
if (teMap != null && teMap.hasConditions(block)) {
|
||||
final long packedPos = BlockRenderingSettings.packBlockPos(x, y, z);
|
||||
int teBlockId = BlockRenderingSettings.getCachedTeNbtId(packedPos, currentTick);
|
||||
|
||||
if (teBlockId == BlockRenderingSettings.CACHE_MISS) {
|
||||
final TileEntity te = getBlockAccess().getTileEntity(x, y, z);
|
||||
if (te != null) {
|
||||
final NBTTagCompound teNbt = new NBTTagCompound();
|
||||
te.writeToNBT(teNbt);
|
||||
teBlockId = teMap.resolve(block, teNbt);
|
||||
} else {
|
||||
teBlockId = -1;
|
||||
}
|
||||
BlockRenderingSettings.cacheTeNbtId(packedPos, teBlockId, currentTick);
|
||||
}
|
||||
|
||||
if (teBlockId != -1) {
|
||||
blockRenderContext.blockId = (short) teBlockId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +324,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
}
|
||||
}
|
||||
|
||||
private void processDeferredBlocks(List<DeferredBlock> deferredBlocks, AngelicaChunkBuildContext buildContext, ChunkBuildBuffers buffers, IBlockAccess region, int minX, int minY, int minZ) {
|
||||
private void processDeferredBlocks(List<DeferredBlock> deferredBlocks, AngelicaChunkBuildContext buildContext, ChunkBuildBuffers buffers, IBlockAccess region, int minX, int minY, int minZ, NbtConditionalIdMap<Block> teMap, long currentTick) {
|
||||
|
||||
final BlockRenderContext blockRenderContext = buildContext.getBlockRenderContext();
|
||||
|
||||
@@ -308,7 +338,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
|
||||
for (DeferredBlock deferred : deferredBlocks) {
|
||||
renderBlock(deferred.block(), deferred.meta(), deferred.x(), deferred.y(), deferred.z(), deferred.pass(),
|
||||
mainTessellator, mainThreadRenderBlocks, buffers, buildContext,
|
||||
blockRenderContext, minX, minY, minZ, deferred.materialOverride(), deferred.isShaderPackOverride());
|
||||
blockRenderContext, minX, minY, minZ, deferred.materialOverride(), deferred.isShaderPackOverride(), teMap, currentTick);
|
||||
}
|
||||
} finally {
|
||||
((StateAwareTessellator)mainTessellator).angelica$setCeleritasMeshing(false);
|
||||
|
||||
+29
@@ -7,6 +7,8 @@ import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHold
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.threading.ChunkTaskProvider;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.threading.ChunkTaskRegistry;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.world.cloned.ClonedChunkSectionCache;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
@@ -40,6 +42,7 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
private final WorldClient world;
|
||||
private final ClonedChunkSectionCache sectionCache;
|
||||
private final ChunkTaskProvider taskProvider;
|
||||
private final LongOpenHashSet biomeRebuildColumns = new LongOpenHashSet();
|
||||
|
||||
public AngelicaRenderSectionManager(RenderPassConfiguration<?> configuration, WorldClient world, int renderDistance, CommandList commandList, int minSection, int maxSection, int requestedThreads, ChunkTaskProvider taskProvider) {
|
||||
super(configuration, () -> new AngelicaChunkBuildContext(configuration, world), AngelicaChunkRenderer::new, renderDistance, commandList, minSection, maxSection, requestedThreads, true /* hasShadowPass = true for Iris */);
|
||||
@@ -144,9 +147,35 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
|
||||
this.sectionCache.invalidate(section.getChunkX(), section.getChunkY(), section.getChunkZ());
|
||||
}
|
||||
|
||||
public void onBiomesChanged(int chunkX, int chunkZ) {
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
for (int dz = -1; dz <= 1; dz++) {
|
||||
this.biomeRebuildColumns.add(PositionUtil.packChunk(chunkX + dx, chunkZ + dz));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void flushBiomeRebuilds() {
|
||||
if (this.biomeRebuildColumns.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final int sectionCountY = this.world.getHeight() >> 4;
|
||||
for (final LongIterator it = this.biomeRebuildColumns.iterator(); it.hasNext(); ) {
|
||||
final long column = it.nextLong();
|
||||
final int cx = PositionUtil.unpackChunkX(column);
|
||||
final int cz = PositionUtil.unpackChunkZ(column);
|
||||
for (int cy = 0; cy < sectionCountY; cy++) {
|
||||
this.sectionCache.invalidate(cx, cy, cz);
|
||||
this.scheduleRebuild(cx, cy, cz, false);
|
||||
}
|
||||
}
|
||||
this.biomeRebuildColumns.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateChunks(boolean updateImmediately) {
|
||||
this.sectionCache.cleanup();
|
||||
this.flushBiomeRebuilds();
|
||||
super.updateChunks(updateImmediately);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.event.ChunkBiomeDataChangedEvent;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
|
||||
import org.embeddedt.embeddium.impl.gl.device.GLRenderDevice;
|
||||
@@ -12,8 +13,16 @@ public class CeleritasSetup {
|
||||
public static void ensureInitialized() {
|
||||
if (!initialized && AngelicaConfig.enableCeleritas) {
|
||||
GLRenderDevice.VANILLA_STATE_RESETTER = () -> GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
ChunkBiomeDataChangedEvent.BUS.addListener(CeleritasSetup::onChunkBiomeDataChanged);
|
||||
AngelicaClientTweaker.LOGGER.debug("Celeritas init");
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void onChunkBiomeDataChanged(ChunkBiomeDataChangedEvent event) {
|
||||
final CeleritasWorldRenderer renderer = CeleritasWorldRenderer.getInstanceOrNull();
|
||||
if (renderer != null && renderer.isActive()) {
|
||||
renderer.getRenderSectionManager().onBiomesChanged(event.chunkX, event.chunkZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-1
@@ -74,6 +74,10 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static CeleritasWorldRenderer getInstanceOrNull() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public AngelicaRenderSectionManager getRenderSectionManager() {
|
||||
return this.renderSectionManager;
|
||||
}
|
||||
@@ -118,7 +122,16 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
|
||||
protected ChunkRenderMatrices createChunkRenderMatrices() {
|
||||
final boolean shadowPass = renderSectionManager.isInShadowPass();
|
||||
final Matrix4f projection = shadowPass ? ShadowRenderer.PROJECTION : RenderingState.INSTANCE.getProjectionMatrix();
|
||||
final Matrix4f modelView = shadowPass ? ShadowRenderer.MODELVIEW : RenderingState.INSTANCE.getModelViewMatrix();
|
||||
Matrix4f modelView = shadowPass ? ShadowRenderer.MODELVIEW : RenderingState.INSTANCE.getModelViewMatrix();
|
||||
if (!shadowPass) {
|
||||
// Minecraft's setupCameraTransform bakes translate(0, -eyeHeight, 0) into the modelview.
|
||||
// Chunk rendering passes eye position to drawChunkLayer so vertices arrive at
|
||||
// the shader as (vert_world - eye).
|
||||
final Entity view = mc.renderViewEntity;
|
||||
if (view != null) {
|
||||
modelView = new Matrix4f(modelView).translate(0f, view.getEyeHeight(), 0f);
|
||||
}
|
||||
}
|
||||
return new ChunkRenderMatrices(projection, modelView);
|
||||
}
|
||||
|
||||
|
||||
+13
@@ -26,6 +26,11 @@ public class IrisExtendedChunkVertexEncoder implements ContextAwareChunkVertexEn
|
||||
private static final int NORMAL_OFFSET = IrisExtendedChunkVertexType.VERTEX_FORMAT.getAttribute("iris_Normal").getPointer();
|
||||
private static final int MC_ENTITY_OFFSET = IrisExtendedChunkVertexType.VERTEX_FORMAT.getAttribute("mc_Entity").getPointer();
|
||||
private static final int MID_BLOCK_OFFSET = IrisExtendedChunkVertexType.VERTEX_FORMAT.getAttribute("at_midBlock").getPointer();
|
||||
private static final int A_TEXCOORD_OFFSET = IrisExtendedChunkVertexType.VERTEX_FORMAT.getAttribute("a_TexCoord").getPointer();
|
||||
|
||||
// One unit of UV-space shift toward the quad centroid. Stops bilinear /
|
||||
// mipmap sampling from bleeding pixels from neighboring atlas cells.
|
||||
private static final float TEX_CENTROID_BIAS = 1.0f / 32768.0f;
|
||||
|
||||
private final ChunkVertexEncoder baseEncoder = IrisExtendedChunkVertexType.BASE_TYPE.createEncoder();
|
||||
private final CeleritasQuadView quad = new CeleritasQuadView();
|
||||
@@ -98,6 +103,14 @@ public class IrisExtendedChunkVertexEncoder implements ContextAwareChunkVertexEn
|
||||
memPutInt(ptr + MID_TEX_OFFSET - STRIDE * 2, midUV);
|
||||
memPutInt(ptr + MID_TEX_OFFSET - STRIDE * 3, midUV);
|
||||
|
||||
for (int vIdx = 0; vIdx < 4; vIdx++) {
|
||||
final long uvBase = ptr - (long) (3 - vIdx) * STRIDE + A_TEXCOORD_OFFSET;
|
||||
final float vU = memGetFloat(uvBase);
|
||||
final float vV = memGetFloat(uvBase + 4L);
|
||||
memPutFloat(uvBase, vU + (vU < midU ? TEX_CENTROID_BIAS : -TEX_CENTROID_BIAS));
|
||||
memPutFloat(uvBase + 4L, vV + (vV < midV ? TEX_CENTROID_BIAS : -TEX_CENTROID_BIAS));
|
||||
}
|
||||
|
||||
quad.setup(ptr, STRIDE);
|
||||
NormalHelper.computeFaceNormal(normal, quad);
|
||||
final int packedNormal = NormI8.pack(normal);
|
||||
|
||||
+27
-26
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas.world.cloned;
|
||||
|
||||
import com.falsepattern.endlessids.mixin.helpers.ChunkBiomeHook;
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.api.SectionLightData;
|
||||
import com.gtnewhorizons.angelica.compat.ExtendedBlockStorageExt;
|
||||
@@ -20,6 +19,7 @@ import net.minecraft.world.ChunkPosition;
|
||||
import net.minecraft.world.EnumSkyBlock;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
import net.minecraft.world.biome.WorldChunkManager;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.NibbleArray;
|
||||
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||
@@ -62,50 +62,51 @@ public class ClonedChunkSection {
|
||||
this.pos = pos;
|
||||
this.data = new ExtendedBlockStorageExt(chunk, section);
|
||||
|
||||
final int bArrLength;
|
||||
if (ModStatus.isEIDBiomeLoaded) {
|
||||
bArrLength = ((ChunkBiomeHook) chunk).getBiomeShortArray().length;
|
||||
} else {
|
||||
bArrLength = chunk.getBiomeArray().length;
|
||||
}
|
||||
this.biomeData = new BiomeGenBase[bArrLength];
|
||||
this.biomeData = new BiomeGenBase[16 * 16];
|
||||
|
||||
copyBlockEntities(chunk, pos);
|
||||
|
||||
// Fill biome data
|
||||
for (int z = pos.getMinZ(); z <= pos.getMaxZ(); z++) {
|
||||
for (int x = pos.getMinX(); x <= pos.getMaxX(); x++) {
|
||||
this.biomeData[(x & 15) | ((z & 15) << 4)] = world.getBiomeGenForCoords(x, z);
|
||||
}
|
||||
}
|
||||
fillBiomeData(chunk);
|
||||
|
||||
this.sectionLightData = BlockLightProvider.getInstance().prepareSectionData(chunk, pos.y);
|
||||
}
|
||||
|
||||
private void fillBiomeData(Chunk chunk) {
|
||||
final WorldChunkManager wcm = world.getWorldChunkManager();
|
||||
for (int lz = 0; lz < 16; lz++) {
|
||||
for (int lx = 0; lx < 16; lx++) {
|
||||
this.biomeData[lx | (lz << 4)] = chunk.getBiomeGenForWorldCoords(lx, lz, wcm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void copyBlockEntities(Chunk chunk, ChunkSectionPos pos) {
|
||||
this.tileEntities.clear();
|
||||
|
||||
final ConcurrentTileEntityMap map = ((IChunkTileEntityMapHolder) chunk).angelica$getConcurrentTEMap();
|
||||
|
||||
map.withReadLock(() -> {
|
||||
map.readLock();
|
||||
try {
|
||||
final Object2ObjectOpenHashMap<ChunkPosition, TileEntity> delegate = map.getDelegate();
|
||||
|
||||
if (delegate.isEmpty()) return;
|
||||
if (!delegate.isEmpty()) {
|
||||
final int minY = pos.getMinY();
|
||||
final int maxY = pos.getMaxY();
|
||||
|
||||
final int minY = pos.getMinY();
|
||||
final int maxY = pos.getMaxY();
|
||||
for (Object2ObjectMap.Entry<ChunkPosition, TileEntity> entry : Object2ObjectMaps.fastIterable(delegate)) {
|
||||
final ChunkPosition tePos = entry.getKey();
|
||||
if (tePos.chunkPosY < minY || tePos.chunkPosY > maxY) continue;
|
||||
|
||||
for (Object2ObjectMap.Entry<ChunkPosition, TileEntity> entry : Object2ObjectMaps.fastIterable(delegate)) {
|
||||
final ChunkPosition tePos = entry.getKey();
|
||||
if (tePos.chunkPosY < minY || tePos.chunkPosY > maxY) continue;
|
||||
|
||||
final TileEntity te = entry.getValue();
|
||||
if (te != null && !te.isInvalid()) {
|
||||
this.tileEntities.put(ChunkSectionPos.packLocal(tePos.chunkPosX & 15, tePos.chunkPosY & 15, tePos.chunkPosZ & 15), te);
|
||||
final TileEntity te = entry.getValue();
|
||||
if (te != null && !te.isInvalid()) {
|
||||
this.tileEntities.put(ChunkSectionPos.packLocal(tePos.chunkPosX & 15, tePos.chunkPosY & 15, tePos.chunkPosZ & 15), te);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
map.readUnlock();
|
||||
}
|
||||
|
||||
this.tileEntities.trim();
|
||||
}
|
||||
|
||||
+11
-6
@@ -21,7 +21,13 @@ public class ClonedChunkSectionCache {
|
||||
|
||||
public synchronized void cleanup() {
|
||||
this.time = getMonotonicTimeSource();
|
||||
this.byPosition.values().removeIf(entry -> this.time > (entry.getLastUsedTimestamp() + MAX_CACHE_DURATION));
|
||||
while (!this.byPosition.isEmpty()) {
|
||||
final ClonedChunkSection oldest = this.byPosition.get(this.byPosition.firstLongKey());
|
||||
if (this.time <= oldest.getLastUsedTimestamp() + MAX_CACHE_DURATION) {
|
||||
break;
|
||||
}
|
||||
this.byPosition.removeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized ClonedChunkSection acquire(int x, int y, int z) {
|
||||
@@ -32,18 +38,17 @@ public class ClonedChunkSectionCache {
|
||||
while (this.byPosition.size() >= MAX_CACHE_SIZE) {
|
||||
this.byPosition.removeFirst();
|
||||
}
|
||||
section = this.createSection(x, y, z);
|
||||
section = this.createSection(key, x, y, z);
|
||||
}
|
||||
|
||||
section.setLastUsedTimestamp(this.time);
|
||||
return section;
|
||||
}
|
||||
|
||||
private ClonedChunkSection createSection(int x, int y, int z) {
|
||||
private ClonedChunkSection createSection(long key, int x, int y, int z) {
|
||||
final ClonedChunkSection section = new ClonedChunkSection(this, this.world);
|
||||
final ChunkSectionPos pos = ChunkSectionPos.from(x, y, z);
|
||||
section.init(pos);
|
||||
this.byPosition.putAndMoveToLast(pos.asLong(), section);
|
||||
section.init(ChunkSectionPos.from(x, y, z));
|
||||
this.byPosition.putAndMoveToLast(key, section);
|
||||
return section;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.gtnewhorizons.angelica.rendering.items;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.IReloadableResourceManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class BlockRenderListManager {
|
||||
private static final BlockMeta key = new BlockMeta();
|
||||
|
||||
private static final Object2IntOpenHashMap<BlockMeta> displayListMap = new Object2IntOpenHashMap<>();
|
||||
|
||||
|
||||
private static final int ISBRH_CUTOFF = 40;
|
||||
|
||||
private static final boolean[] notISBRH = new boolean[ISBRH_CUTOFF];
|
||||
|
||||
static {
|
||||
for (int i : new int[]
|
||||
{ 0, 31, 39, 16, 26, 1, 19, 23, 13, 6, 2, 10, 27, 11, 21, 32, 35, 34, 38 }
|
||||
) {
|
||||
notISBRH[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isISBRH(int renderType) {
|
||||
return renderType >= ISBRH_CUTOFF || renderType < 0 || !notISBRH[renderType];
|
||||
}
|
||||
|
||||
public static int getDisplayList(Block block, int meta) {
|
||||
key.block = block;
|
||||
key.meta = meta;
|
||||
return displayListMap.getInt(key);
|
||||
}
|
||||
|
||||
public static int startCompiling() {
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void endCompiling(int list, Block block, int meta) {
|
||||
DisplayListManager.discardMatrixTransforms();
|
||||
GLStateManager.glEndList();
|
||||
displayListMap.put(new BlockMeta(block, meta), list);
|
||||
}
|
||||
|
||||
public static void registerReloadListener(){
|
||||
IReloadableResourceManager resourceManager = (IReloadableResourceManager) Minecraft.getMinecraft()
|
||||
.getResourceManager();
|
||||
resourceManager.registerReloadListener(_ -> {
|
||||
for (int list : displayListMap.values()) {
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
displayListMap.clear();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static final class BlockMeta {
|
||||
private Block block;
|
||||
private int meta;
|
||||
|
||||
public BlockMeta() {
|
||||
|
||||
}
|
||||
|
||||
public BlockMeta(Block block, int meta) {
|
||||
this.block = block;
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (block.hashCode() * 31 + meta) * 31;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof BlockMeta other)) return false;
|
||||
return block == other.block && meta == other.meta;
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -23,7 +23,7 @@
|
||||
* along with FalseTweaks. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
package com.gtnewhorizons.angelica.rendering.items;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
|
||||
@@ -37,6 +37,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.IReloadableResourceManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class ItemRenderListManager {
|
||||
@@ -98,6 +99,17 @@ public class ItemRenderListManager {
|
||||
return Minecraft.getMinecraft().thePlayer.ticksExisted;
|
||||
}
|
||||
|
||||
public static void registerReloadListener(){
|
||||
IReloadableResourceManager resourceManager = (IReloadableResourceManager) Minecraft.getMinecraft()
|
||||
.getResourceManager();
|
||||
resourceManager.registerReloadListener(_ -> {
|
||||
for (CachedVBO value : vboCache.values()) {
|
||||
value.delete();
|
||||
}
|
||||
vboCache.clear();
|
||||
});
|
||||
}
|
||||
|
||||
public static final class CachedVBO {
|
||||
private final IVertexArrayObject vertexBuffer;
|
||||
private final IndexBuffer ebo;
|
||||
@@ -42,6 +42,14 @@ public class ConcurrentTileEntityMap implements Map<ChunkPosition, TileEntity> {
|
||||
}
|
||||
}
|
||||
|
||||
public void readLock() {
|
||||
lock.readLock().lock();
|
||||
}
|
||||
|
||||
public void readUnlock() {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
|
||||
public void withWriteLock(Runnable action) {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
|
||||
@@ -20,11 +20,7 @@ final class BlockOrientation extends RenderBlockState {
|
||||
// 1 2 3
|
||||
// c: coordinate (x,y,z) 0-2
|
||||
private static final int[][][] NEIGHBOR_OFFSET = new int[][][] {
|
||||
(
|
||||
fixedBottomFaceUV
|
||||
? makeNeighborOffset(EAST_FACE, SOUTH_FACE, WEST_FACE, NORTH_FACE) // BOTTOM_FACE fixedBottomFaceUV
|
||||
: makeNeighborOffset(WEST_FACE, SOUTH_FACE, EAST_FACE, NORTH_FACE) // BOTTOM_FACE not fixed
|
||||
),
|
||||
makeNeighborOffset(EAST_FACE, SOUTH_FACE, WEST_FACE, NORTH_FACE), // BOTTOM_FACE
|
||||
makeNeighborOffset(WEST_FACE, SOUTH_FACE, EAST_FACE, NORTH_FACE), // TOP_FACE
|
||||
makeNeighborOffset(EAST_FACE, BOTTOM_FACE, WEST_FACE, TOP_FACE), // NORTH_FACE
|
||||
makeNeighborOffset(WEST_FACE, BOTTOM_FACE, EAST_FACE, TOP_FACE), // SOUTH_FACE
|
||||
@@ -48,6 +44,7 @@ final class BlockOrientation extends RenderBlockState {
|
||||
private int textureFace;
|
||||
private int textureFaceOrig;
|
||||
private int rotateUV;
|
||||
private boolean flipBottom;
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
@@ -60,6 +57,7 @@ final class BlockOrientation extends RenderBlockState {
|
||||
offsetsComputed = false;
|
||||
haveOffsets = false;
|
||||
dx = dy = dz = 0;
|
||||
flipBottom = !fixedBottomFaceUV;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,7 +102,7 @@ final class BlockOrientation extends RenderBlockState {
|
||||
|
||||
@Override
|
||||
public int[] getOffset(int blockFace, int relativeDirection) {
|
||||
return NEIGHBOR_OFFSET[blockFace][rotateUV(relativeDirection)];
|
||||
return NEIGHBOR_OFFSET[flipBottom && blockFace == 0 ? 1 : blockFace][rotateUV(relativeDirection)];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,6 +145,11 @@ final class BlockOrientation extends RenderBlockState {
|
||||
return haveOffsets;
|
||||
}
|
||||
|
||||
|
||||
public void setFlipBottom(){
|
||||
flipBottom = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldConnectByBlock(Block neighbor, int neighborX, int neighborY, int neighborZ) {
|
||||
return block == neighbor
|
||||
@@ -172,6 +175,7 @@ final class BlockOrientation extends RenderBlockState {
|
||||
copy.textureFace = textureFace;
|
||||
copy.textureFaceOrig = textureFaceOrig;
|
||||
copy.rotateUV = rotateUV;
|
||||
copy.flipBottom = flipBottom;
|
||||
|
||||
copy.blockAccess = blockAccess;
|
||||
copy.block = block;
|
||||
@@ -204,6 +208,7 @@ final class BlockOrientation extends RenderBlockState {
|
||||
rotateUV = 0;
|
||||
textureFace = blockFaceToTextureFace(blockFace);
|
||||
metadataBits = (1 << metadata) | (1 << altMetadata);
|
||||
flipBottom = !fixedBottomFaceUV;
|
||||
}
|
||||
|
||||
void setBlockMetadata(Block block, int metadata, int face) {
|
||||
|
||||
@@ -53,6 +53,9 @@ public final class CompactCtmQuadProcessor {
|
||||
}
|
||||
|
||||
public boolean processFace(RenderBlocks rb, RenderBlockState renderBlockState, IIcon origIcon) {
|
||||
if(renderBlockState instanceof BlockOrientation blockOrientation){
|
||||
blockOrientation.setFlipBottom();
|
||||
}
|
||||
int neighborBits = override.getNeighborBits(renderBlockState, origIcon);
|
||||
|
||||
int ctmIndex = TileOverride.neighborMap[neighborBits & 0xFF];
|
||||
|
||||
@@ -10,6 +10,7 @@ import cpw.mods.fml.relauncher.SideOnly;
|
||||
import jss.notfine.gui.options.control.NotFineControlValueFormatter;
|
||||
import jss.notfine.gui.options.named.AlwaysNever;
|
||||
import jss.notfine.gui.options.named.BackgroundSelect;
|
||||
import jss.notfine.gui.options.named.BobviewMode;
|
||||
import jss.notfine.gui.options.named.DownfallQuality;
|
||||
import jss.notfine.gui.options.named.FOVMode;
|
||||
import jss.notfine.gui.options.named.GraphicsQualityOff;
|
||||
@@ -50,6 +51,7 @@ public enum Settings {
|
||||
SettingsManager.downfallDistanceUpdated();
|
||||
}
|
||||
},
|
||||
BOBVIEW_MODE(new NotFineOptionCycling<>(BobviewMode.DEFAULT, null)),
|
||||
DYNAMIC_FOV(new NotFineOptionCycling<>(FOVMode.ALL, OptionImpact.VARIES)),
|
||||
HURT_SHAKE(new NotFineOptionSliderPercentage(100, 0, 300, 5, OptionImpact.LOW)),
|
||||
DYNAMIC_LIGHTS(new NotFineOptionCycling<>(DynamicLightsMode.FANCY, OptionImpact.VARIES)){
|
||||
|
||||
Loaded 100 of 197 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user