mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 17:55:08 +00:00
Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
864cc4ea82 | ||
|
|
30fe456909 | ||
|
|
85658bb7fb | ||
|
|
ecf073c09d | ||
|
|
1df25ac753 | ||
|
|
3df6a830a2 | ||
|
|
51a5fd2f59 | ||
|
|
902f0f4741 | ||
|
|
406ce93c38 | ||
|
|
5958408019 | ||
|
|
36a3c20db7 | ||
|
|
7572b829ef | ||
|
|
94c2a9d4f5 | ||
|
|
e44caddc7b | ||
|
|
a3c3c103fc | ||
|
|
58f771562b | ||
|
|
ac3dd972b3 | ||
|
|
ce8f9341ca | ||
|
|
fc88137c3b | ||
|
|
bf3c828f6f | ||
|
|
10e9bc26fb | ||
|
|
a32e034aa5 | ||
|
|
9a5ba2a17c | ||
|
|
08cbbbb537 | ||
|
|
e72708b2c5 | ||
|
|
224455b6ca | ||
|
|
5e1dd876f2 |
No files matched your search
+5
-5
@@ -12,11 +12,11 @@ tasks.withType<DowngradeFiles>().configureEach { logLevel.set("FATAL") }
|
||||
minecraft {
|
||||
extraRunJvmArguments.add("-Dangelica.enableTestBlocks=true")
|
||||
extraRunJvmArguments.add("-Dangelica.dumpClass=true")
|
||||
// extraRunJvmArguments.add("-Dangelica.useVulkan=true")
|
||||
// extraRunJvmArguments.add("-Dangelica.vulkanDebug=true")
|
||||
// extraRunJvmArguments.add("-Dorg.lwjgl.util.Debug=true")
|
||||
// extraRunJvmArguments.addAll("-Dlegacy.debugClassLoadingSave=true")
|
||||
// extraRunJvmArguments.addAll("-Drfb.dumpLoadedClasses=true", "-Drfb.dumpLoadedClassesPerTransformer=true")
|
||||
//extraRunJvmArguments.add("-Dangelica.useVulkan=true")
|
||||
//extraRunJvmArguments.add("-Dangelica.vulkanDebug=true")
|
||||
//extraRunJvmArguments.add("-Dorg.lwjgl.util.Debug=true")
|
||||
//extraRunJvmArguments.addAll("-Dlegacy.debugClassLoadingSave=true")
|
||||
//extraRunJvmArguments.addAll("-Drfb.dumpLoadedClasses=true", "-Drfb.dumpLoadedClassesPerTransformer=true")
|
||||
//extraRunJvmArguments.add("-Dangelica.redirectorLogspam=true")
|
||||
}
|
||||
|
||||
|
||||
+16
-2
@@ -63,10 +63,13 @@ configurations {
|
||||
// Exclude self-reference from all configurations - prevents transitive Angelica deps from resolving against this project
|
||||
configurations.all {
|
||||
exclude group: "com.github.GTNewHorizons", module: "Angelica"
|
||||
resolutionStrategy {
|
||||
force 'com.github.GTNewHorizons:Hodgepodge:9:dev'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.158:dev")
|
||||
implementation("com.github.GTNewHorizons:Hodgepodge:9:dev")
|
||||
|
||||
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.1.0") { transitive = false }
|
||||
// runtimeOnlyNonPublishable("com.github.GTNewHorizons:lwjgl3ify:3.0.99")
|
||||
@@ -76,7 +79,7 @@ dependencies {
|
||||
|
||||
// Iris Shaders
|
||||
compileOnly('org.jetbrains:annotations:26.0.2')
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.11.4:dev")
|
||||
api("com.github.GTNewHorizons:GTNHLib:9:dev")
|
||||
|
||||
shadowImplementation(project(":glsm")) { transitive = false }
|
||||
shadowImplementation(project(":lwjgl3-backend")) { transitive = false }
|
||||
@@ -97,6 +100,14 @@ dependencies {
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.8.35-GTNH:dev')
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
|
||||
|
||||
api("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
|
||||
|
||||
/*
|
||||
devOnlyNonPublishable('com.github.GTNewHorizons:GT5-Unofficial:9:dev') {
|
||||
exclude group: 'com.github.GTNewHorizons', module: 'Angelica'
|
||||
}
|
||||
*/
|
||||
|
||||
// Display List Testing
|
||||
/*
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-749-GTNH:dev")
|
||||
@@ -115,6 +126,9 @@ dependencies {
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:HoloInventory:2.5.8-GTNH:dev")
|
||||
*/
|
||||
|
||||
// Font Testing
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Nuclear-Control:2.7.9:dev")
|
||||
|
||||
//devOnlyNonPublishable('com.github.GTNewHorizons:worldedit-gtnh:v0.0.9')
|
||||
|
||||
// Notfine Deps
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.events.SplashDestroyEvent;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
@@ -78,6 +79,7 @@ import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL45;
|
||||
import org.lwjgl.opengl.GLContext;
|
||||
import org.lwjgl.opengl.KHRDebug;
|
||||
|
||||
@@ -96,6 +98,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAllocFloat;
|
||||
import static com.gtnewhorizons.angelica.glsm.Vendor.AMD;
|
||||
import static com.gtnewhorizons.angelica.glsm.Vendor.INTEL;
|
||||
import static com.gtnewhorizons.angelica.glsm.Vendor.MESA;
|
||||
@@ -507,6 +510,22 @@ public class GLStateManager {
|
||||
return DisplayListManager.displayListExists(list);
|
||||
}
|
||||
|
||||
public static long glFenceSync(int flags, int i) {
|
||||
return RENDER_BACKEND.fenceSync(flags, i);
|
||||
}
|
||||
|
||||
public static int glClientWaitSync(long fence, int flags, long l) {
|
||||
return RENDER_BACKEND.clientWaitSync(fence, flags, l);
|
||||
}
|
||||
|
||||
public static void glDeleteSync(long fence) {
|
||||
RENDER_BACKEND.deleteSync(fence);
|
||||
}
|
||||
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) {
|
||||
return RENDER_BACKEND.mapBufferRange(target, offset, length, access);
|
||||
}
|
||||
|
||||
public static class GLFeatureSet extends IntOpenHashSet {
|
||||
|
||||
private static final long serialVersionUID = 8558779940775721010L;
|
||||
@@ -1798,7 +1817,38 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
private static int getBoundTexture(int unit) {
|
||||
return textures.getTextureUnitBindings(unit).getBinding();
|
||||
return textures.getTextureUnitBindings(unit).getTexture2D();
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int unit, int target, int texture) {
|
||||
final int activeUnit = activeTextureUnit.getValue();
|
||||
if (unit == activeTextureUnit.getValue()) {
|
||||
glBindTexture(target, texture);
|
||||
return;
|
||||
}
|
||||
|
||||
final TextureBinding textureUnit = textures.getTextureUnitBindings(unit);
|
||||
|
||||
|
||||
if (target == GL11.GL_TEXTURE_2D) {
|
||||
final int cachedBinding = textureUnit.getTexture2D();
|
||||
if (cachedBinding != texture) {
|
||||
glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
glBindTexture(target, texture);
|
||||
glActiveTexture(GL13.GL_TEXTURE0);
|
||||
}
|
||||
} else if (target == GL30.GL_TEXTURE_2D_ARRAY) {
|
||||
final int cachedBinding = textureUnit.getTexture2DArray();
|
||||
if (cachedBinding != texture) {
|
||||
glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
glBindTexture(target, texture);
|
||||
glActiveTexture(GL13.GL_TEXTURE0);
|
||||
}
|
||||
} else {
|
||||
glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
glBindTexture(target, texture);
|
||||
glActiveTexture(GL13.GL_TEXTURE0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glBindTexture(int target, int texture) {
|
||||
@@ -1814,13 +1864,6 @@ public class GLStateManager {
|
||||
deferredDeleteTextures.remove(texture);
|
||||
}
|
||||
|
||||
if (target != GL11.GL_TEXTURE_2D) {
|
||||
// Non-2D targets (e.g. GL_TEXTURE_2D_ARRAY) pass through without caching
|
||||
logUncachedTextureTarget(target);
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCachingEnabled()) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
return;
|
||||
@@ -1828,24 +1871,40 @@ public class GLStateManager {
|
||||
|
||||
final int activeUnit = GLStateManager.activeTextureUnit.getValue();
|
||||
final TextureBinding textureUnit = textures.getTextureUnitBindings(activeUnit);
|
||||
final int cachedBinding = textureUnit.getBinding();
|
||||
|
||||
if (cachedBinding != texture) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
textureUnit.setBinding(texture);
|
||||
if (texture != 0 && activeUnit > maxBoundTextureUnit) {
|
||||
maxBoundTextureUnit = activeUnit;
|
||||
}
|
||||
if (!lockBindCallback && activeTextureUnit.getValue() == 0) {
|
||||
lockBindCallback = true;
|
||||
if (GLSMHooks.TEXTURE_BIND.hasListeners()) {
|
||||
GLSMHooks.textureBindEvent.textureId = texture;
|
||||
GLSMHooks.TEXTURE_BIND.post(GLSMHooks.textureBindEvent);
|
||||
if (target == GL11.GL_TEXTURE_2D) {
|
||||
final int cachedBinding = textureUnit.getTexture2D();
|
||||
if (cachedBinding != texture) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
textureUnit.setTexture2D(texture);
|
||||
if (texture != 0 && activeUnit > maxBoundTextureUnit) {
|
||||
maxBoundTextureUnit = activeUnit;
|
||||
}
|
||||
if (!lockBindCallback && activeTextureUnit.getValue() == 0) {
|
||||
lockBindCallback = true;
|
||||
if (GLSMHooks.TEXTURE_BIND.hasListeners()) {
|
||||
GLSMHooks.textureBindEvent.textureId = texture;
|
||||
GLSMHooks.TEXTURE_BIND.post(GLSMHooks.textureBindEvent);
|
||||
}
|
||||
RenderSystem.bindTextureToUnit(0, texture);
|
||||
lockBindCallback = false;
|
||||
}
|
||||
RenderSystem.bindTextureToUnit(0, texture);
|
||||
lockBindCallback = false;
|
||||
}
|
||||
} else if (target == GL30.GL_TEXTURE_2D_ARRAY) {
|
||||
final int cachedBinding = textureUnit.getTexture2DArray();
|
||||
if (cachedBinding != texture) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
textureUnit.setTexture2DArray(texture);
|
||||
if (texture != 0 && activeUnit > maxBoundTextureUnit) {
|
||||
maxBoundTextureUnit = activeUnit;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Other targets pass through without caching
|
||||
logUncachedTextureTarget(target);
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static int changeFormatIfDeprecated(int internalformat) {
|
||||
@@ -2046,24 +2105,38 @@ public class GLStateManager {
|
||||
if (id == 0) return;
|
||||
|
||||
final int savedUnit = GLStateManager.activeTextureUnit.getValue();
|
||||
final int savedBinding = textures.getTextureUnitBindings(savedUnit).getBinding();
|
||||
final int savedBinding = textures.getTextureUnitBindings(savedUnit).getTexture2D();
|
||||
boolean changedUnit = false;
|
||||
|
||||
boolean isTexture2D = TextureInfoCache.INSTANCE.hasEntry(id);
|
||||
// Unbind from all units that have this texture
|
||||
for (int i = 0; i <= maxBoundTextureUnit; i++) {
|
||||
if (textures.getTextureUnitBindings(i).getBinding() == id) {
|
||||
if (textures.getTextureUnitBindings(i).getTexture2D() == id) {
|
||||
if (i != savedUnit) {
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + i);
|
||||
changedUnit = true;
|
||||
}
|
||||
isTexture2D = true;
|
||||
RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
}
|
||||
if (textures.getTextureUnitBindings(i).getTexture2DArray() == id) {
|
||||
if (i != savedUnit) {
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + i);
|
||||
changedUnit = true;
|
||||
}
|
||||
RENDER_BACKEND.bindTexture(GL30.GL_TEXTURE_2D_ARRAY, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore active unit, then shrink the texture via a temporary bind
|
||||
if (changedUnit) {
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + savedUnit);
|
||||
}
|
||||
//GL11.glDeleteTextures(id);
|
||||
if (!isTexture2D) {
|
||||
GL11.glDeleteTextures(id); // Only defer delete GL_TEXTURE_2D textures
|
||||
return;
|
||||
}
|
||||
RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
RENDER_BACKEND.texParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0);
|
||||
suspendPixelUnpackBuffer();
|
||||
@@ -3145,9 +3218,7 @@ public class GLStateManager {
|
||||
|
||||
// Always delete
|
||||
for (int i = 0; i <= maxBoundTextureUnit; i++) {
|
||||
if (textures.getTextureUnitBindings(i).getBinding() == id) {
|
||||
textures.getTextureUnitBindings(i).setBinding(0);
|
||||
}
|
||||
textures.getTextureUnitBindings(i).unbindIfEquals(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3188,6 +3259,9 @@ public class GLStateManager {
|
||||
splashComplete = true;
|
||||
drawableGLHolder = null;
|
||||
drawableGL = null;
|
||||
if (GLSMHooks.SPLASH_DESTROY.hasListeners()) {
|
||||
GLSMHooks.SPLASH_DESTROY.post(new SplashDestroyEvent());
|
||||
}
|
||||
}
|
||||
|
||||
public static void glNewList(int list, int mode) {
|
||||
@@ -3349,7 +3423,7 @@ public class GLStateManager {
|
||||
// Restore texture bindings for all units, then restore active unit. activeTextureUnit stores the 0-based index, glActiveTexture needs GL_TEXTURE0 + index.
|
||||
for (int i = 0; i < MAX_TEXTURE_UNITS; i++) {
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + i);
|
||||
RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, textures.getTextureUnitBindings(i).getBinding());
|
||||
RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, textures.getTextureUnitBindings(i).getTexture2D());
|
||||
}
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + activeTextureUnit.getValue());
|
||||
}
|
||||
@@ -3767,10 +3841,6 @@ public class GLStateManager {
|
||||
return activeTextureUnit.getValue();
|
||||
}
|
||||
|
||||
public static int getListMode() {
|
||||
return DisplayListManager.getListMode();
|
||||
}
|
||||
|
||||
private static boolean handleRemovedTexParam(int target, int pname, int value) {
|
||||
if (pname == GL14.GL_GENERATE_MIPMAP) {
|
||||
if (target == GL11.GL_TEXTURE_2D) {
|
||||
@@ -4655,6 +4725,23 @@ public class GLStateManager {
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glTexSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth, int format, int type, IntBuffer pixels) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw new UnsupportedOperationException("glTexSubImage3D in display lists not yet implemented - if you see this, please report!");
|
||||
}
|
||||
suspendPixelUnpackBuffer();
|
||||
GL12.glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
|
||||
//RENDER_BACKEND.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
|
||||
restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void glGetTextureSubImage(int texture, int level, int xoffset, int yoffset, int zoffset,
|
||||
int width, int height, int depth,
|
||||
int format, int type, int pixels_bufSize, long pixels_buffer_offset
|
||||
) {
|
||||
GL45.glGetTextureSubImage(texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels_bufSize, pixels_buffer_offset);
|
||||
}
|
||||
|
||||
public static void glCopyTexImage1D(int target, int level, int internalFormat, int x, int y, int width, int border) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glCopyTexImage1D");
|
||||
@@ -6081,6 +6168,19 @@ public class GLStateManager {
|
||||
RENDER_BACKEND.uniformMatrix4(location, transpose, matrices);
|
||||
}
|
||||
|
||||
public static final Matrix4f scratchMvp = new Matrix4f();
|
||||
public static final FloatBuffer mvpBuffer = memAllocFloat(16);
|
||||
|
||||
public static void uploadMVPMatrix(int mvpMatrixLocation) {
|
||||
getMVPMatrix(scratchMvp);
|
||||
scratchMvp.get(mvpBuffer);
|
||||
glUniformMatrix4(mvpMatrixLocation, false, mvpBuffer);
|
||||
}
|
||||
|
||||
public static Matrix4f getMVPMatrix(Matrix4f dest) {
|
||||
return getProjectionMatrix().mul(getModelViewMatrix(), dest);
|
||||
}
|
||||
|
||||
public static void glGetActiveUniform(int program, int index, IntBuffer length, IntBuffer size, IntBuffer type, ByteBuffer name) {
|
||||
RENDER_BACKEND.getActiveUniform(program, index, length, size, type, name);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class DSAARB extends DSAUnsupported {
|
||||
super.bindTextureToUnit(unit, texture);
|
||||
} else {
|
||||
RENDER_BACKEND.bindTextureUnit(unit, texture);
|
||||
GLStateManager.getTextures().getTextureUnitBindings(unit).setBinding(texture);
|
||||
GLStateManager.getTextures().getTextureUnitBindings(unit).setTexture2D(texture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<SplashDestroyEvent> SPLASH_DESTROY = EventBus.create(SplashDestroyEvent.class);
|
||||
public static final EventBus<ShaderColorChangeEvent> SHADER_COLOR_CHANGE = EventBus.create(ShaderColorChangeEvent.class);
|
||||
|
||||
// Reusable event instances
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks.events;
|
||||
|
||||
import net.minecraftforge.eventbus.api.event.MutableEvent;
|
||||
|
||||
public final class SplashDestroyEvent extends MutableEvent {
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
public class TextureBinding implements ISettableState<TextureBinding> {
|
||||
@Setter protected int binding;
|
||||
@Setter protected int texture2D;
|
||||
@Setter protected int texture2DArray;
|
||||
|
||||
@Override
|
||||
public TextureBinding set(TextureBinding state) {
|
||||
this.binding = state.binding;
|
||||
this.texture2D = state.texture2D;
|
||||
this.texture2DArray = state.texture2DArray;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -17,10 +19,20 @@ public class TextureBinding implements ISettableState<TextureBinding> {
|
||||
public boolean sameAs(Object state) {
|
||||
if (this == state) return true;
|
||||
if (!(state instanceof TextureBinding textureBinding)) return false;
|
||||
return binding == textureBinding.binding;
|
||||
return texture2D == textureBinding.texture2D
|
||||
&& texture2DArray == textureBinding.texture2DArray;
|
||||
}
|
||||
@Override
|
||||
public TextureBinding copy() {
|
||||
return new TextureBinding().set(this);
|
||||
}
|
||||
|
||||
public final void unbindIfEquals(int id) {
|
||||
if (getTexture2D() == id) {
|
||||
this.setTexture2D(0);
|
||||
}
|
||||
if (getTexture2DArray() == id) {
|
||||
setTexture2DArray(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public final class StreamingUploader {
|
||||
MAP_BUFFER_RANGE
|
||||
}
|
||||
|
||||
private static final int MAP_WRITE_INVALIDATE_BUFFER = GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_BUFFER_BIT;
|
||||
public static final int MAP_WRITE_INVALIDATE_BUFFER = GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_BUFFER_BIT;
|
||||
|
||||
public static int upload(ByteBuffer data, int capacity) {
|
||||
return upload(GLStateManager.getInitConfig().getStreamingUploadStrategy(), data, capacity);
|
||||
|
||||
@@ -43,6 +43,11 @@ public class TextureInfoCache {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasEntry(int id) {
|
||||
if (id < 0) return false;
|
||||
return cache.containsKey(id);
|
||||
}
|
||||
|
||||
public void onTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable Buffer pixels) {
|
||||
if (target == GL11.GL_TEXTURE_2D && level == 0) {
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.font.FontRendering;
|
||||
|
||||
public final class AngelicaTextPreprocessor implements FontRendering.TextPreprocessor {
|
||||
|
||||
@Override
|
||||
public String apply(final String text) {
|
||||
return ColorCodeUtils.convertImpl(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlesAmpCodes() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.font.FontRendering;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
|
||||
/**
|
||||
@@ -18,6 +19,7 @@ public final class ColorCodeUtils {
|
||||
private ColorCodeUtils() {}
|
||||
|
||||
public static final char FORMATTING_CHAR = '§';
|
||||
public static final String FORMATTING_CHAR_STRING = String.valueOf(FORMATTING_CHAR);
|
||||
public static final char ESCAPED_AMPERSAND = '';
|
||||
|
||||
public static final int SECTION_X_PAYLOAD = 12;
|
||||
@@ -25,22 +27,28 @@ public final class ColorCodeUtils {
|
||||
public static final int GRADIENT_PAYLOAD = 28;
|
||||
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 = "0123456789abcdefklmnorqzvu";
|
||||
private static final boolean[] isColorCode = new boolean[255];
|
||||
|
||||
private static final String[] SECTION_PREFIX = new String[128];
|
||||
static {
|
||||
for (int i = 0; i < VALID_SINGLE_CODES.length(); i++) {
|
||||
char c = VALID_SINGLE_CODES.charAt(i);
|
||||
SECTION_PREFIX[c] = String.valueOf(FORMATTING_CHAR) + c;
|
||||
/** Valid single {@code &} codes. {@code g} is excluded; {@code &g} only converts as part of a gradient. */
|
||||
final String s = "0123456789abcdefklmnorqzvu";
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
final char ch = s.charAt(i);
|
||||
isColorCode[ch] = true;
|
||||
isColorCode[Character.toUpperCase(ch)] = true;
|
||||
}
|
||||
SECTION_PREFIX['x'] = String.valueOf(FORMATTING_CHAR) + 'x';
|
||||
SECTION_PREFIX['g'] = String.valueOf(FORMATTING_CHAR) + 'g';
|
||||
}
|
||||
|
||||
public static String sectionPrefix(char code) {
|
||||
char c = (code < 128) ? Character.toLowerCase(code) : code;
|
||||
return (c < 128) ? SECTION_PREFIX[c] : null;
|
||||
public static void initPreprocessor() {
|
||||
if (AngelicaConfig.enableAmpersandConversion) {
|
||||
FontRendering.setTextPreprocessor(new AngelicaTextPreprocessor());
|
||||
} else {
|
||||
FontRendering.setTextPreprocessor((FontRendering.TextPreprocessor) null);
|
||||
}
|
||||
}
|
||||
|
||||
public static String sectionPrefix(final char code) {
|
||||
return FORMATTING_CHAR_STRING + code;
|
||||
}
|
||||
|
||||
public static int parseHexPairs(CharSequence str, int start, int count) {
|
||||
@@ -66,21 +74,7 @@ public final class ColorCodeUtils {
|
||||
return parseSectionXAt(str, start) != -1;
|
||||
}
|
||||
|
||||
private static String lastConversionInput;
|
||||
private static String lastConversionOutput;
|
||||
|
||||
public static String convertAmpersandToSectionX(String text) {
|
||||
if (text == null || !AngelicaConfig.enableAmpersandConversion) return text;
|
||||
|
||||
if (text == lastConversionInput) return lastConversionOutput;
|
||||
|
||||
final String out = convertImpl(text);
|
||||
lastConversionInput = text;
|
||||
lastConversionOutput = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
private static String convertImpl(String text) {
|
||||
public static String convertImpl(final String text) {
|
||||
int idx = text.indexOf('&');
|
||||
if (idx == -1) return text;
|
||||
|
||||
@@ -88,6 +82,16 @@ public final class ColorCodeUtils {
|
||||
StringBuilder sb = null;
|
||||
int last = 0;
|
||||
while (idx != -1 && idx + 1 < len) {
|
||||
final char code = text.charAt(idx + 1);
|
||||
if (code >= 255) continue;
|
||||
if (isColorCode[code]) {
|
||||
if (sb == null) sb = new StringBuilder(len + 16);
|
||||
sb.append(text, last, idx);
|
||||
sb.append(FORMATTING_CHAR).append(text.charAt(idx + 1));
|
||||
last = idx + 2;
|
||||
idx = text.indexOf('&', last);
|
||||
continue;
|
||||
}
|
||||
if (idx > 0 && text.charAt(idx - 1) == '\\') {
|
||||
if (sb == null) sb = new StringBuilder(len + 16);
|
||||
sb.append(text, last, idx - 1);
|
||||
@@ -96,27 +100,28 @@ public final class ColorCodeUtils {
|
||||
idx = text.indexOf('&', last);
|
||||
continue;
|
||||
}
|
||||
if (text.charAt(idx + 1) == '#' && idx + 7 < len) {
|
||||
boolean validHex = true;
|
||||
for (int i = 2; i <= 7; i++) {
|
||||
if (Character.digit(text.charAt(idx + i), 16) == -1) {
|
||||
validHex = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (validHex) {
|
||||
if (sb == null) sb = new StringBuilder(len + 16);
|
||||
sb.append(text, last, idx);
|
||||
sb.append(FORMATTING_CHAR).append('x');
|
||||
if (code == '#') {
|
||||
if (idx + 7 < len) {
|
||||
boolean validHex = true;
|
||||
for (int i = 2; i <= 7; i++) {
|
||||
sb.append(FORMATTING_CHAR).append(text.charAt(idx + i));
|
||||
if (Character.digit(text.charAt(idx + i), 16) == -1) {
|
||||
validHex = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (validHex) {
|
||||
if (sb == null) sb = new StringBuilder(len + 16);
|
||||
sb.append(text, last, idx);
|
||||
sb.append(FORMATTING_CHAR).append('x');
|
||||
for (int i = 2; i <= 7; i++) {
|
||||
sb.append(FORMATTING_CHAR).append(text.charAt(idx + i));
|
||||
}
|
||||
last = idx + 8;
|
||||
idx = text.indexOf('&', last);
|
||||
continue;
|
||||
}
|
||||
last = idx + 8;
|
||||
idx = text.indexOf('&', last);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (Character.toLowerCase(text.charAt(idx + 1)) == 'g' && idx + 17 < len
|
||||
} else if (Character.toLowerCase(code) == 'g' && idx + 17 < len
|
||||
&& text.charAt(idx + 2) == '&' && text.charAt(idx + 3) == '#'
|
||||
&& text.charAt(idx + 10) == '&' && text.charAt(idx + 11) == '#') {
|
||||
boolean valid1 = true, valid2 = true;
|
||||
@@ -137,13 +142,6 @@ public final class ColorCodeUtils {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
char code = Character.toLowerCase(text.charAt(idx + 1));
|
||||
if (VALID_SINGLE_CODES.indexOf(code) != -1) {
|
||||
if (sb == null) sb = new StringBuilder(len + 16);
|
||||
sb.append(text, last, idx);
|
||||
sb.append(FORMATTING_CHAR).append(text.charAt(idx + 1));
|
||||
last = idx + 2;
|
||||
}
|
||||
idx = text.indexOf('&', idx + 1);
|
||||
}
|
||||
if (sb == null) return text;
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.MatrixHelper;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.postprocessing.CustomFramebuffer;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.shader.IShaderDefinesInjector;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.shader.ShaderProgram;
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.utils.InstancedHelper;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.ClientProxy.mc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.nmemFree;
|
||||
|
||||
//TODO move this + shader to gtnhlib
|
||||
public final class FontOverlayShader extends ShaderProgram {
|
||||
|
||||
private int mvpMatrixLocation;
|
||||
private int uTexelSize;
|
||||
private int uScale;
|
||||
private int uTime;
|
||||
private int uTexBounds;
|
||||
private final int padding;
|
||||
|
||||
public float xStart;
|
||||
public float xEnd;
|
||||
public float yStart;
|
||||
public float yEnd;
|
||||
|
||||
private static CustomFramebuffer framebuffer;
|
||||
private static int vao;
|
||||
private static int vbo;
|
||||
private static ByteBuffer buffer;
|
||||
|
||||
// public static FontOverlayShader TEMPLATE = new FontOverlayShader(
|
||||
// new ResourceLocation(AngelicaMod.MOD_ID, "shaders/font/fontShaderTemplate.fsh")
|
||||
// );
|
||||
|
||||
public FontOverlayShader(ResourceLocation fragShader, IShaderDefinesInjector... defines) {
|
||||
this(fragShader, 4, defines);
|
||||
}
|
||||
|
||||
public FontOverlayShader(ResourceLocation fragShader, int padding, IShaderDefinesInjector... defines) {
|
||||
super(
|
||||
loadShaderSource(getVertexShader()),
|
||||
loadShaderSource(fragShader, defines)
|
||||
);
|
||||
this.padding = padding;
|
||||
this.bindTextureSlots("textFBO", "sceneFBO");
|
||||
mvpMatrixLocation = this.getUniformLocation("u_MVPMatrix");
|
||||
uTexelSize = this.getUniformLocation("uTexelSize");
|
||||
uScale = this.getUniformLocation("uScale");
|
||||
uTime = this.getUniformLocation("uTime");
|
||||
uTexBounds = this.getUniformLocation("uTexBounds");
|
||||
}
|
||||
|
||||
public static ResourceLocation getVertexShader() {
|
||||
return new ResourceLocation(AngelicaMod.MOD_ID, "shaders/font/fontShader.vsh");
|
||||
}
|
||||
|
||||
public boolean begin(BatchingFontRenderer fontRenderer) {
|
||||
fontRenderer.flushBatch();
|
||||
xStart = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void updateBounds(float x, float y, float width, float height) {
|
||||
if (xStart == -1) {
|
||||
xStart = x;
|
||||
yStart = y;
|
||||
}
|
||||
xEnd = x + width;
|
||||
yEnd = y + height;
|
||||
}
|
||||
|
||||
public boolean end(BatchingFontRenderer fontRenderer) {
|
||||
if (framebuffer == null) {
|
||||
framebuffer = new CustomFramebuffer(0);
|
||||
}
|
||||
if (framebuffer.framebufferWidth != mc.displayWidth || framebuffer.framebufferHeight != mc.displayHeight) {
|
||||
framebuffer.createBindFramebuffer(mc.displayWidth, mc.displayHeight);
|
||||
}
|
||||
framebuffer.clearBindFramebuffer();
|
||||
fontRenderer.flushBatch();
|
||||
framebuffer.unbindFramebuffer();
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, mc.getFramebuffer().framebufferTexture);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
framebuffer.bindFramebufferTexture();
|
||||
|
||||
final int padding = this.padding;
|
||||
this.use();
|
||||
|
||||
if (vao == 0) {
|
||||
buffer = memAlloc(32);
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, InstancedHelper.getQuadEBO());
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
// position
|
||||
GLStateManager.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, 8, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
}
|
||||
|
||||
long address = memAddress0(buffer);
|
||||
buffer.limit(32);
|
||||
addVertex(address, xStart - padding, yStart - padding);
|
||||
addVertex(address + 8, xEnd + padding, yStart - padding);
|
||||
addVertex(address + 16, xStart - padding, yEnd + padding);
|
||||
addVertex(address + 24, xEnd + padding, yEnd + padding);
|
||||
|
||||
Matrix4f mvp = GLStateManager.getMVPMatrix(new Matrix4f());
|
||||
Vector4f temp = new Vector4f();
|
||||
temp.x = xStart;
|
||||
temp.y = yEnd;
|
||||
temp.z = 0;
|
||||
temp.w = 1;
|
||||
MatrixHelper.transformVertex(mvp, temp);
|
||||
|
||||
float boundXStart = temp.x * 0.5f + 0.5f;
|
||||
float boundYStart = temp.y * 0.5f + 0.5f;
|
||||
|
||||
temp.x = xEnd;
|
||||
temp.y = yStart;
|
||||
temp.z = 0;
|
||||
temp.w = 1;
|
||||
MatrixHelper.transformVertex(mvp, temp);
|
||||
|
||||
float boundXEnd = temp.x * 0.5f + 0.5f;
|
||||
float boundYEnd = temp.y * 0.5f + 0.5f;
|
||||
|
||||
this.uploadBounds(boundXStart, boundXEnd, boundYStart, boundYEnd);
|
||||
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STREAM_DRAW);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 6, GL11.GL_UNSIGNED_SHORT, 0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
|
||||
nmemFree(address);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addVertex(long ptr, float x, float y) {
|
||||
// v, v
|
||||
memPutFloat(ptr, x);
|
||||
memPutFloat(ptr + 4, y);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void use() {
|
||||
super.use();
|
||||
GLStateManager.uploadMVPMatrix(mvpMatrixLocation);
|
||||
if (uTexelSize != -1) {
|
||||
GLStateManager.glUniform2f(uTexelSize, 1f / mc.displayWidth, 1f / mc.displayHeight);
|
||||
}
|
||||
|
||||
this.uploadTime(uTime);
|
||||
|
||||
if (uScale != -1) {
|
||||
final ScaledResolution res = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||
GLStateManager.glUniform1i(uScale, res.getScaleFactor());
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadBounds(float minX, float maxX, float minY, float maxY) {
|
||||
GLStateManager.glUniform4f(uTexBounds, minX, maxX, minY, maxY);
|
||||
}
|
||||
|
||||
public static final class Builder implements IShaderDefinesInjector {
|
||||
|
||||
@Override
|
||||
public void writeDefines(StringBuilder out) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public interface FontProvider {
|
||||
|
||||
/**
|
||||
* For use with §k. Should fetch a character of the same width as provided.
|
||||
*/
|
||||
char getRandomReplacement(char chr);
|
||||
boolean isGlyphAvailable(char chr);
|
||||
float getUStart(char chr);
|
||||
float getVStart(char chr);
|
||||
float getXAdvance(char chr);
|
||||
float getGlyphW(char chr);
|
||||
float getUSize(char chr);
|
||||
float getVSize(char chr);
|
||||
float getShadowOffset();
|
||||
ResourceLocation getTexture(char chr);
|
||||
float getYScaleMultiplier();
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import lombok.Setter;
|
||||
import lombok.Value;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.ITextureObject;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class FontProviderCustom implements FontProvider {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
public static final String FONT_DIR = "fonts/custom/";
|
||||
static final int ATLAS_SIZE = 128;
|
||||
static final int ATLAS_COUNT = 512;
|
||||
private final byte id; // 0 - primary font, 1 - fallback font
|
||||
private FontAtlas[] fontAtlases = new FontAtlas[ATLAS_COUNT];
|
||||
private float currentFontQuality = FontConfig.customFontQuality;
|
||||
@Setter
|
||||
private Font font;
|
||||
|
||||
private FontProviderCustom(byte id) {
|
||||
this.id = id;
|
||||
Font[] availableFonts = FontStrategist.getAvailableFonts();
|
||||
String myFontName = switch (this.id) {
|
||||
case 0 -> FontConfig.customFontNamePrimary;
|
||||
case 1 -> FontConfig.customFontNameFallback;
|
||||
default -> null;
|
||||
};
|
||||
if (Objects.equals(myFontName, "(none)")) {
|
||||
this.font = null;
|
||||
return;
|
||||
}
|
||||
int fontPos = -1;
|
||||
for (int i = 0; i < availableFonts.length; i++) {
|
||||
if (Objects.equals(myFontName, availableFonts[i].getFontName())) {
|
||||
fontPos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fontPos == -1) {
|
||||
LOGGER.info("Could not find previously set font \"{}\". ", myFontName);
|
||||
this.font = null;
|
||||
return;
|
||||
}
|
||||
this.font = availableFonts[fontPos].deriveFont(this.currentFontQuality);
|
||||
}
|
||||
private static class InstLoader {
|
||||
static final FontProviderCustom instance0 = new FontProviderCustom((byte)0);
|
||||
static final FontProviderCustom instance1 = new FontProviderCustom((byte)1);
|
||||
}
|
||||
public static FontProviderCustom getPrimary() { return InstLoader.instance0; }
|
||||
public static FontProviderCustom getFallback() { return InstLoader.instance1; }
|
||||
|
||||
public void reloadFont(int fontID) {
|
||||
this.currentFontQuality = FontConfig.customFontQuality;
|
||||
this.font = FontStrategist.getAvailableFonts()[fontID].deriveFont(this.currentFontQuality);
|
||||
|
||||
File[] files = new File(getFontDir()).listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
if (!Files.isSymbolicLink(f.toPath())) {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextureManager tm = Minecraft.getMinecraft().getTextureManager();
|
||||
Map mapTextureObjects = tm.mapTextureObjects;
|
||||
for (int i = 0; i < ATLAS_COUNT; i++) {
|
||||
ResourceLocation key = new ResourceLocation(getAtlasResourceName(i));
|
||||
if (mapTextureObjects.containsKey(key)) {
|
||||
ITextureObject obj = (ITextureObject) mapTextureObjects.get(key);
|
||||
TextureUtil.deleteTexture(obj.getGlTextureId());
|
||||
mapTextureObjects.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
this.fontAtlases = new FontAtlas[ATLAS_COUNT];
|
||||
}
|
||||
|
||||
private String getFontDir() {
|
||||
return FONT_DIR + "f" + this.id + "/";
|
||||
}
|
||||
|
||||
private String getAtlasFilename(int atlasId) {
|
||||
return "f" + this.id + "p" + atlasId;
|
||||
}
|
||||
|
||||
String getAtlasResourceName(int atlasId) {
|
||||
return "minecraft:angelica_c" + getAtlasFilename(atlasId);
|
||||
}
|
||||
|
||||
String getAtlasFullPath(int atlasId) {
|
||||
return getFontDir() + getAtlasFilename(atlasId) + ".png";
|
||||
}
|
||||
|
||||
@Value
|
||||
private class GlyphData {
|
||||
float uStart;
|
||||
float vStart;
|
||||
float xAdvance;
|
||||
float glyphW;
|
||||
float uSz;
|
||||
float vSz;
|
||||
}
|
||||
|
||||
private class FontAtlas {
|
||||
|
||||
GlyphData[] glyphData = new GlyphData[ATLAS_SIZE];
|
||||
private ResourceLocation texture;
|
||||
private final int id;
|
||||
|
||||
FontAtlas(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
void construct(Font font) {
|
||||
int atlasChars = 0;
|
||||
for (int i = 0; i < ATLAS_SIZE; i++) {
|
||||
final char ch = (char) (i + ATLAS_SIZE * this.id);
|
||||
if (font.canDisplay(ch)) { atlasChars++; }
|
||||
}
|
||||
if (atlasChars == 0) { return; }
|
||||
|
||||
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = image.createGraphics();
|
||||
g2d.setFont(font);
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
g2d.dispose();
|
||||
|
||||
final int atlasTilesX = (int) Math.ceil(Math.sqrt(atlasChars) * 1.5f);
|
||||
final int atlasTilesY = (int) Math.ceil((double) atlasChars / atlasTilesX);
|
||||
final float charSeparator = currentFontQuality / 3f;
|
||||
int rowWidth = 0;
|
||||
int maxRowWidth = 0;
|
||||
atlasChars = 0;
|
||||
|
||||
for (int i = 0; i < ATLAS_SIZE; i++) {
|
||||
if (atlasChars % atlasTilesX == 0) {
|
||||
maxRowWidth = Math.max(maxRowWidth, rowWidth);
|
||||
rowWidth = 0;
|
||||
}
|
||||
final char ch = (char)(i + ATLAS_SIZE * this.id);
|
||||
if (font.canDisplay(ch)) {
|
||||
rowWidth += (int) (charSeparator + fm.charWidth(ch));
|
||||
atlasChars++;
|
||||
}
|
||||
}
|
||||
maxRowWidth = Math.max(maxRowWidth, rowWidth);
|
||||
|
||||
final int lineHeight = fm.getHeight();
|
||||
final float desc = fm.getDescent();
|
||||
|
||||
final int imageWidth = (int) (maxRowWidth + charSeparator);
|
||||
final int imageHeight = (int) ((charSeparator + lineHeight) * atlasTilesY + charSeparator);
|
||||
|
||||
image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
|
||||
g2d = image.createGraphics();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
g2d.setFont(font);
|
||||
fm = g2d.getFontMetrics();
|
||||
|
||||
int tileX = 0, tileY = 0; // position in atlas tiles
|
||||
int imgX = (int) charSeparator; // position in pixels
|
||||
|
||||
for (int i = 0; i < ATLAS_SIZE; i++) {
|
||||
final char ch = (char) (i + ATLAS_SIZE * this.id);
|
||||
if (!font.canDisplay(ch)) { continue; }
|
||||
|
||||
if (tileX >= atlasTilesX) {
|
||||
tileX = 0;
|
||||
imgX = (int) charSeparator;
|
||||
tileY++;
|
||||
}
|
||||
|
||||
final int charWidth = fm.charWidth(ch);
|
||||
final float charAspectRatio = (float) charWidth / lineHeight;
|
||||
final float inset = currentFontQuality / 16;
|
||||
g2d.drawString(Character.toString(ch), imgX, (lineHeight + charSeparator) * (tileY + 1) - desc);
|
||||
final float uStart = (float) (imgX - inset * charAspectRatio) / imageWidth;
|
||||
final float vStart = ((lineHeight + charSeparator) * (tileY + 1) - lineHeight - inset) / imageHeight;
|
||||
final float xAdvance = charAspectRatio * 8 * charWidth / (charWidth + 2 * inset * charAspectRatio);
|
||||
final float glyphW = charAspectRatio * 8 + 1;
|
||||
final float uSz = (float) (charWidth + 2 * inset * charAspectRatio) / imageWidth;
|
||||
final float vSz = (float) (lineHeight + 2 * inset) / imageHeight;
|
||||
this.glyphData[i] = new GlyphData(uStart, vStart, xAdvance, glyphW, uSz, vSz);
|
||||
imgX += (int) (charWidth + charSeparator);
|
||||
tileX++;
|
||||
}
|
||||
g2d.dispose();
|
||||
try {
|
||||
Files.createDirectories(Paths.get(getFontDir()));
|
||||
ImageIO.write(image, "png", new File(getAtlasFullPath(this.id)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.texture = new ResourceLocation(getAtlasResourceName(this.id));
|
||||
}
|
||||
}
|
||||
|
||||
private FontAtlas getAtlas(char chr) {
|
||||
int id = chr / ATLAS_SIZE;
|
||||
FontAtlas fa = this.fontAtlases[id];
|
||||
if (fa == null) {
|
||||
fa = new FontAtlas(id);
|
||||
fa.construct(this.font);
|
||||
this.fontAtlases[id] = fa;
|
||||
}
|
||||
return fa;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlyphAvailable(char chr) {
|
||||
if (this.font == null) { return false; }
|
||||
return (getAtlas(chr).glyphData[chr % ATLAS_SIZE] != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getRandomReplacement(char chr) {
|
||||
return chr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUStart(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].uStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVStart(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].vStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXAdvance(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].xAdvance * FontConfig.customFontScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getGlyphW(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].glyphW * FontConfig.customFontScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUSize(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].uSz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVSize(char chr) {
|
||||
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].vSz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShadowOffset() {
|
||||
return FontConfig.fontShadowOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTexture(char chr) {
|
||||
return getAtlas(chr).texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYScaleMultiplier() {
|
||||
return FontConfig.customFontScale;
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import it.unimi.dsi.fastutil.chars.Char2ShortOpenHashMap;
|
||||
import jss.util.RandomXoshiro256StarStar;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public final class FontProviderMC implements FontProvider {
|
||||
|
||||
/**
|
||||
* The full list of characters present in the default Minecraft font, excluding the Unicode font
|
||||
*/
|
||||
@SuppressWarnings("UnnecessaryUnicodeEscape")
|
||||
private static final String MCFONT_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u2302\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
|
||||
|
||||
private FontProviderMC() {}
|
||||
private static class InstLoader {
|
||||
static final FontProviderMC instance = new FontProviderMC();
|
||||
static final FontProviderMC instanceSGA = new FontProviderMC();
|
||||
}
|
||||
public static FontProviderMC get(boolean isSGA) {
|
||||
return (isSGA ? InstLoader.instanceSGA : InstLoader.instance);
|
||||
}
|
||||
|
||||
private static final Char2ShortOpenHashMap MCFONT_ASCII_MAP = new Char2ShortOpenHashMap();
|
||||
ResourceLocation locationFontTexture = null;
|
||||
private final RandomXoshiro256StarStar fontRandom = new RandomXoshiro256StarStar();
|
||||
public int[] charWidth;
|
||||
|
||||
static {
|
||||
MCFONT_ASCII_MAP.defaultReturnValue((short) -1);
|
||||
for (short i = 0; i < MCFONT_CHARS.length(); i++) {
|
||||
MCFONT_ASCII_MAP.put(MCFONT_CHARS.charAt(i), i);
|
||||
}
|
||||
}
|
||||
|
||||
public int lookupMcFontPosition(char ch) {
|
||||
return MCFONT_ASCII_MAP.get(ch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlyphAvailable(char chr) {
|
||||
return MCFONT_ASCII_MAP.containsKey(chr);
|
||||
}
|
||||
|
||||
public char getRandomReplacement(char chr){
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
if (lutIndex != -1) {
|
||||
int randomReplacementIndex;
|
||||
do {
|
||||
randomReplacementIndex = fontRandom.nextInt(this.charWidth.length);
|
||||
} while (this.charWidth[lutIndex] != this.charWidth[randomReplacementIndex]);
|
||||
|
||||
lutIndex = randomReplacementIndex;
|
||||
return MCFONT_CHARS.charAt(lutIndex);
|
||||
}
|
||||
return chr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUStart(char chr) {
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
return ((lutIndex % 16) * 8) / 128.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVStart(char chr) {
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
return (float) ((lutIndex / 16) * 8) / 128.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXAdvance(char chr) {
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
return charWidth[lutIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getGlyphW(char chr) {
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
return charWidth[lutIndex] - 0.01F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUSize(char chr) {
|
||||
int lutIndex = lookupMcFontPosition(chr);
|
||||
return (charWidth[lutIndex] - 1.01F) / 128.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVSize(char chr) {
|
||||
return 7.99F / 128.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShadowOffset() {
|
||||
return FontConfig.fontShadowOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTexture(char chr) {
|
||||
return locationFontTexture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYScaleMultiplier() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public final class FontProviderUnicode implements FontProvider {
|
||||
|
||||
private FontProviderUnicode() {
|
||||
try {
|
||||
InputStream inputstream = Minecraft.getMinecraft().getResourceManager()
|
||||
.getResource(new ResourceLocation("font/glyph_sizes.bin")).getInputStream();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
inputstream.read(this.glyphWidth);
|
||||
}
|
||||
catch (IOException ioexception) {
|
||||
throw new RuntimeException(ioexception);
|
||||
}
|
||||
}
|
||||
|
||||
private static class InstLoader { static final FontProviderUnicode instance = new FontProviderUnicode(); }
|
||||
public static FontProviderUnicode get() { return FontProviderUnicode.InstLoader.instance; }
|
||||
|
||||
private static final ResourceLocation[] unicodePageLocations = new ResourceLocation[256];
|
||||
private final byte[] glyphWidth = new byte[65536];
|
||||
|
||||
private ResourceLocation getUnicodePageLocation(int page) {
|
||||
final ResourceLocation lookup = unicodePageLocations[page];
|
||||
if (lookup == null) {
|
||||
final ResourceLocation rl = new ResourceLocation(String.format(
|
||||
"textures/font/unicode_page_%02x.png",
|
||||
page));
|
||||
unicodePageLocations[page] = rl;
|
||||
return rl;
|
||||
} else {
|
||||
return lookup;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlyphAvailable(char chr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char getRandomReplacement(char chr) {
|
||||
return chr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUStart(char chr) {
|
||||
final float startColumnF = (float)((this.glyphWidth[chr] >>> 4) & 15);
|
||||
return ((float) (chr % 16 * 16) + startColumnF + 0.21f) / 256.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVStart(char chr) {
|
||||
return ((float) ((chr & 255) / 16 * 16) + 0.21f) / 256.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getXAdvance(char chr) {
|
||||
final int startColumn = (this.glyphWidth[chr] >>> 4) & 15;
|
||||
final int endColumn = this.glyphWidth[chr] & 15;
|
||||
final float startColumnF = (float) startColumn;
|
||||
final float endColumnF = (float) (endColumn + 1);
|
||||
return (endColumnF - startColumnF) / 2.0F + 1.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getGlyphW(char chr) {
|
||||
final int startColumn = (this.glyphWidth[chr] >>> 4) & 15;
|
||||
final int endColumn = this.glyphWidth[chr] & 15;
|
||||
final float startColumnF = (float) startColumn;
|
||||
final float endColumnF = (float) (endColumn + 1);
|
||||
final float chrWidth = endColumnF - startColumnF - 0.02F;
|
||||
return chrWidth / 2.0f + 1.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getUSize(char chr) {
|
||||
final int startColumn = (this.glyphWidth[chr] >>> 4) & 15;
|
||||
final int endColumn = this.glyphWidth[chr] & 15;
|
||||
final float startColumnF = (float) startColumn;
|
||||
final float endColumnF = (float) (endColumn + 1);
|
||||
final float chrWidth = endColumnF - startColumnF - 0.02F;
|
||||
return (chrWidth - 0.42f) / 256.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVSize(char chr) {
|
||||
return (16.0f - 0.42f) / 256.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShadowOffset() {
|
||||
return FontConfig.fontShadowOffset * FontConfig.fontShadowOffsetUC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTexture(char chr) {
|
||||
final int uniPage = chr / 256;
|
||||
return getUnicodePageLocation(uniPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getYScaleMultiplier() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class FontShaderManager {
|
||||
|
||||
public static final List<FontOverlayShader> fontShaders = new ArrayList<>();
|
||||
|
||||
public static void registerShader(FontOverlayShader shader) {
|
||||
fontShaders.add(shader);
|
||||
}
|
||||
|
||||
static boolean isOverlayShader(char ch) {
|
||||
return ((int) ch) < fontShaders.size();
|
||||
}
|
||||
|
||||
public static FontOverlayShader begin(char ch, BatchingFontRenderer fontRenderer) {
|
||||
final byte index = (byte) ch;
|
||||
final FontOverlayShader shader = get(index);
|
||||
shader.begin(fontRenderer);
|
||||
return shader;
|
||||
}
|
||||
|
||||
public static FontOverlayShader get(byte index) {
|
||||
return fontShaders.get(index);
|
||||
}
|
||||
}
|
||||
@@ -2,29 +2,26 @@ package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.ResourceAccessor;
|
||||
import cpw.mods.fml.client.SplashProgress;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.resources.DefaultResourcePack;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.io.File;
|
||||
import javax.annotation.Nullable;
|
||||
import java.awt.*;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class FontStrategist {
|
||||
import static com.gtnewhorizons.angelica.AngelicaMod.LOGGER;
|
||||
import static com.gtnewhorizons.angelica.client.font.BatchingFontRenderer.reloadFonts;
|
||||
|
||||
public final class FontStrategist {
|
||||
|
||||
@Getter
|
||||
private static final Font[] availableFonts;
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
|
||||
public static Font primaryFont;
|
||||
public static Font secondaryFont;
|
||||
|
||||
static {
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
@@ -60,70 +57,49 @@ public class FontStrategist {
|
||||
availableFonts = fontSet.values().stream().sorted(Comparator.comparing(Font::getFontName)).toArray(Font[]::new);
|
||||
|
||||
LOGGER.info("Got {} fonts from GraphicsEnvironment ({} after deduplication)", availableFontsDirty.length, availableFonts.length);
|
||||
}
|
||||
|
||||
// create and add the resource pack that provides fonts
|
||||
HashMap<String, File> packMap = new HashMap<>();
|
||||
for (int i = 0; i < FontProviderCustom.ATLAS_COUNT; i++) {
|
||||
packMap.put(FontProviderCustom.getPrimary().getAtlasResourceName(i), new File(FontProviderCustom.getPrimary().getAtlasFullPath(i)));
|
||||
packMap.put(FontProviderCustom.getFallback().getAtlasResourceName(i), new File(FontProviderCustom.getFallback().getAtlasFullPath(i)));
|
||||
}
|
||||
|
||||
DefaultResourcePack fontResourcePack = new DefaultResourcePack(packMap);
|
||||
List defaultResourcePacks = ((ResourceAccessor) Minecraft.getMinecraft()).angelica$getDefaultResourcePacks();
|
||||
defaultResourcePacks.add(fontResourcePack);
|
||||
}
|
||||
|
||||
/**
|
||||
Lets you get a FontProvider per char while respecting font priority and fallbacks, the unicode flag, whether
|
||||
SGA is on, if we're in a splash screen, if a font can even display a character in the first place, etc.
|
||||
*/
|
||||
public static FontProvider getFontProvider(BatchingFontRenderer me, char chr, boolean customFontEnabled, boolean forceUnicode) {
|
||||
if (me.isSGA && FontProviderMC.get(true).isGlyphAvailable(chr)) {
|
||||
return FontProviderMC.get(true);
|
||||
}
|
||||
if (me.bookMode) {
|
||||
return FontProviderUnicode.get();
|
||||
}
|
||||
if (customFontEnabled && !me.isSplash) {
|
||||
FontProvider fp;
|
||||
fp = FontProviderCustom.getPrimary();
|
||||
if (fp.isGlyphAvailable(chr)) { return fp; }
|
||||
fp = FontProviderCustom.getFallback();
|
||||
if (fp.isGlyphAvailable(chr)) { return fp; }
|
||||
return FontProviderUnicode.get();
|
||||
} else {
|
||||
if (!forceUnicode && FontProviderMC.get(false).isGlyphAvailable(chr)) {
|
||||
return FontProviderMC.get(false);
|
||||
} else {
|
||||
return FontProviderUnicode.get();
|
||||
}
|
||||
reloadCustomFontProviders();
|
||||
}
|
||||
}
|
||||
|
||||
public static @Nullable Font[] getConfigFonts() {
|
||||
if (primaryFont != null && secondaryFont != null) {
|
||||
return new Font[] { primaryFont, secondaryFont };
|
||||
}
|
||||
if (primaryFont != null) {
|
||||
return new Font[] { primaryFont };
|
||||
}
|
||||
if (secondaryFont != null) {
|
||||
return new Font[] { secondaryFont };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static void reloadCustomFontProviders() {
|
||||
FontProviderCustom.getPrimary().setFont(null);
|
||||
FontProviderCustom.getFallback().setFont(null);
|
||||
primaryFont = null;
|
||||
secondaryFont = null;
|
||||
for (int i = 0; i < availableFonts.length; i++) {
|
||||
if (Objects.equals(FontConfig.customFontNamePrimary, availableFonts[i].getFontName())) {
|
||||
FontProviderCustom.getPrimary().reloadFont(i);
|
||||
final Font font = availableFonts[i];
|
||||
final String fontName = font.getFontName();
|
||||
if (FontConfig.customFontNamePrimary.equals(fontName)) {
|
||||
primaryFont = font.deriveFont((float) FontConfig.customFontQuality);
|
||||
}
|
||||
if (Objects.equals(FontConfig.customFontNameFallback, availableFonts[i].getFontName())) {
|
||||
FontProviderCustom.getFallback().reloadFont(i);
|
||||
if (FontConfig.customFontNameFallback.equals(fontName)) {
|
||||
secondaryFont = font.deriveFont((float) FontConfig.customFontQuality);
|
||||
}
|
||||
}
|
||||
reloadFonts();
|
||||
}
|
||||
|
||||
public static boolean isSplashFontRendererActive(FontRenderer fontRenderer) {
|
||||
// noinspection deprecation
|
||||
boolean active = fontRenderer instanceof SplashProgress.SplashFontRenderer;
|
||||
if (fontRenderer instanceof SplashProgress.SplashFontRenderer) return true;
|
||||
|
||||
try {
|
||||
Class<?> customSplashClass = Class.forName("gkappa.modernsplash.CustomSplash$SplashFontRenderer");
|
||||
active = active || customSplashClass.isInstance(fontRenderer);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
return customSplashClass.isInstance(fontRenderer);
|
||||
} catch (ClassNotFoundException ignored) {}
|
||||
|
||||
return active;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.gtnewhorizons.angelica.client.font;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
|
||||
public final class GlyphData {
|
||||
public final float uStart;
|
||||
public final float vStart;
|
||||
public final float xStart;
|
||||
public final float xWidth; // The width of the actual char
|
||||
public final float xAdvance; // The amount to advance
|
||||
public final float uSize;
|
||||
public final float vSize;
|
||||
|
||||
// For MC Fonts (where xStart = 0, glyphW = charWidth - 1, xAdvance = charWidth)
|
||||
public GlyphData(float uStart, float vStart, float glyphW, float uSize, float vSize) {
|
||||
this(uStart, vStart, 0, glyphW, glyphW + 1, uSize, vSize);
|
||||
}
|
||||
|
||||
public GlyphData(float uStart, float vStart, float xStart, float xWidth, float xAdvance, float uSize, float vSize) {
|
||||
this.uStart = uStart;
|
||||
this.vStart = vStart;
|
||||
this.xAdvance = xAdvance + FontConfig.glyphSpacing;
|
||||
this.xStart = xStart;
|
||||
this.xWidth = xWidth;
|
||||
this.uSize = uSize;
|
||||
this.vSize = vSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package com.gtnewhorizons.angelica.client.font.atlas;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.font.GlyphReplacements;
|
||||
import com.gtnewhorizons.angelica.client.font.BatchingFontRenderer;
|
||||
import com.gtnewhorizons.angelica.client.font.GlyphData;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public final class FontProviderCustom extends FontTextureArray {
|
||||
private static final int ATLAS_CHARS = 256;
|
||||
|
||||
private final Font[] fonts;
|
||||
|
||||
private FontProviderCustom(int size, int layers, short[] layersLookupArray, int filter, Font[] fonts) {
|
||||
super(size, layers, layersLookupArray, filter);
|
||||
this.fonts = fonts;
|
||||
}
|
||||
|
||||
public static FontProviderCustom createTextureArray(final Font[] fonts) {
|
||||
final int fontAmount = fonts.length;
|
||||
if (fontAmount == 0) return null;
|
||||
final FontMetrics[] fontMetrics = new FontMetrics[fontAmount];
|
||||
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = image.createGraphics();
|
||||
for (int i = 0; i < fontAmount; i++) {
|
||||
g2d.setFont(fonts[i]);
|
||||
fontMetrics[i] = g2d.getFontMetrics();
|
||||
}
|
||||
g2d.dispose();
|
||||
|
||||
|
||||
short[] layerLookup = new short[256];
|
||||
short layer = 0;
|
||||
loop:
|
||||
for (int atlasId = 0; atlasId < 256; atlasId++) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
final char ch = (char) (i + atlasId * 256);
|
||||
for (Font font : fonts) {
|
||||
if (font.canDisplay(ch)) {
|
||||
layerLookup[atlasId] = layer;
|
||||
layer++;
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
// Can't display any chars, skip.
|
||||
layerLookup[atlasId] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
final int size = getAtlasSize(fonts, fontMetrics, 2);
|
||||
|
||||
return new FontProviderCustom(
|
||||
size,
|
||||
layer,
|
||||
layerLookup,
|
||||
GL11.GL_LINEAR,
|
||||
fonts
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private static int getAtlasSize(Font[] fonts, FontMetrics[] fontMetrics, int separator) {
|
||||
int size = 512;
|
||||
|
||||
int maxCharHeight = 0;
|
||||
for (FontMetrics fm : fontMetrics) {
|
||||
if (fm.getHeight() > maxCharHeight) maxCharHeight = fm.getHeight();
|
||||
}
|
||||
|
||||
loop:
|
||||
while (size <= 2048) {
|
||||
for (int atlasId = 0; atlasId < 256; atlasId++) {
|
||||
int imgX = 0;
|
||||
int imgY = 0; //TODO idk
|
||||
for (int atlasChar = 0; atlasChar < 256; atlasChar++) {
|
||||
final char ch = (char) (atlasChar + 256 * atlasId);
|
||||
for (int i = 0; i < fonts.length; i++) {
|
||||
if (!fonts[i].canDisplay(ch)) continue;
|
||||
|
||||
final int lineHeight = fontMetrics[i].getHeight();
|
||||
final int charHeight = (lineHeight + separator);
|
||||
|
||||
final int charWidth = fontMetrics[i].charWidth(ch);
|
||||
|
||||
if (imgX + charWidth >= size) {
|
||||
imgX = 0;
|
||||
imgY += charHeight;
|
||||
}
|
||||
imgX += (charWidth + separator);
|
||||
}
|
||||
|
||||
}
|
||||
//if (imgX == 0 && imgY == 0) return 0; TODO integrate this somehow
|
||||
// Total used height includes final row
|
||||
int usedHeight = imgY + maxCharHeight;
|
||||
|
||||
if (usedHeight > size) {
|
||||
size *= 2;
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
System.out.println("Size ended up being over 2048. This is a bug!");
|
||||
return size;
|
||||
}
|
||||
|
||||
private boolean canDisplayAtlas(int atlasId) {
|
||||
for (int i = 0; i < ATLAS_CHARS; i++) {
|
||||
final char ch = (char) (i + ATLAS_CHARS * atlasId);
|
||||
for (Font font : fonts) {
|
||||
if (font.canDisplay(ch)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer generateGlyphData(int atlasId, GlyphData[] glyphs) {
|
||||
if (!canDisplayAtlas(atlasId)) return null;
|
||||
|
||||
final int imageWidth = textureSize;
|
||||
final int imageHeight = textureSize;
|
||||
|
||||
BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = image.createGraphics();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
|
||||
int maxCharHeight = 0;
|
||||
final FontMetrics[] fontMetrics = new FontMetrics[fonts.length];
|
||||
for (int i = 0; i < fontMetrics.length; i++) {
|
||||
g2d.setFont(fonts[i]);
|
||||
final FontMetrics fm = g2d.getFontMetrics();
|
||||
fontMetrics[i] = fm;
|
||||
if (fm.getHeight() > maxCharHeight) maxCharHeight = fm.getHeight();
|
||||
}
|
||||
|
||||
|
||||
final int padding = 2;
|
||||
|
||||
|
||||
int imgX = padding; // position in pixels
|
||||
int imgY = maxCharHeight + padding;
|
||||
|
||||
|
||||
Font lastFont = null; // Store last font to save on repeated setFont() calls
|
||||
|
||||
final float height = BatchingFontRenderer.getStringHeight();
|
||||
|
||||
|
||||
final int atlasBegin = ATLAS_CHARS * atlasId;
|
||||
for (int atlasChar = 0; atlasChar < ATLAS_CHARS; atlasChar++) {
|
||||
char ch = (char) (atlasChar + atlasBegin);
|
||||
|
||||
final char replacement = GlyphReplacements.getReplacementGlyph(ch);
|
||||
if (replacement != 0) {
|
||||
if (FontConfig.enableGlyphReplacements) {
|
||||
for (Font font : fonts) {
|
||||
if (font.canDisplay(replacement)) {
|
||||
ch = replacement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < fonts.length; i++) {
|
||||
final Font font = fonts[i];
|
||||
if (font.canDisplay(ch)) {
|
||||
if (lastFont != font) {
|
||||
g2d.setFont(font);
|
||||
lastFont = font;
|
||||
}
|
||||
final FontMetrics fm = fontMetrics[i];
|
||||
|
||||
final int lineHeight = fm.getHeight();
|
||||
final float desc = fm.getDescent();
|
||||
|
||||
final int advanceWidth = fm.charWidth(ch);
|
||||
|
||||
final GlyphVector gv = font.createGlyphVector(
|
||||
g2d.getFontRenderContext(),
|
||||
Character.toString(ch));
|
||||
|
||||
if (imgX + advanceWidth >= imageWidth) {
|
||||
imgX = padding;
|
||||
imgY += (maxCharHeight + padding);
|
||||
}
|
||||
|
||||
final Rectangle bounds = gv.getPixelBounds(
|
||||
g2d.getFontRenderContext(),
|
||||
0, 0);
|
||||
|
||||
g2d.drawGlyphVector(
|
||||
gv,
|
||||
imgX - bounds.x,
|
||||
imgY - desc
|
||||
);
|
||||
final float scale = (height / lineHeight) * getGlyphScaleX();
|
||||
final float glyphW = advanceWidth * scale;
|
||||
final float xStart = bounds.x * scale;
|
||||
final float xWidth = bounds.width * scale;
|
||||
|
||||
final float uStart = (float) (imgX) / imageWidth;
|
||||
final float vStart = (float) (imgY - lineHeight) / imageHeight;
|
||||
|
||||
final float uSz = bounds.width / (float) imageWidth;
|
||||
final float vSz = lineHeight / (float) imageHeight;
|
||||
glyphs[atlasChar] = new GlyphData(uStart, vStart, xStart, xWidth, glyphW, uSz, vSz);
|
||||
imgX += bounds.width + padding;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
g2d.dispose();
|
||||
|
||||
if (FontTextureArray.DUMP_ATLASES) {
|
||||
try {
|
||||
File dir = new File(Minecraft.getMinecraft().mcDataDir, "debug/fonts");
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
ImageIO.write(image, "png", new File(dir, "custom_font_" + fonts[0].getFontName() + "_" + atlasId + ".png"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return getPixelBuffer(image);
|
||||
}
|
||||
|
||||
static float getGlyphScaleX() {
|
||||
return (FontConfig.glyphScale * (float) Math.pow(2, FontConfig.glyphAspect));
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package com.gtnewhorizons.angelica.client.font.atlas;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.textures.TextureLoader;
|
||||
import com.gtnewhorizons.angelica.client.font.BatchingFontRenderer;
|
||||
import com.gtnewhorizons.angelica.client.font.GlyphData;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
|
||||
public final class FontProviderMinecraft extends FontTextureArray {
|
||||
|
||||
public static final GlyphData[] unicodeGlyphData = new GlyphData[256];
|
||||
public static int unicodeLayer;
|
||||
|
||||
public static FontProviderMinecraft create() {
|
||||
short layer = 0;
|
||||
final int amountLayers = 256 + 1; // 256 + 1 because of Unicode pages.
|
||||
final short[] layersLookupArray = new short[amountLayers];
|
||||
for (int i = 0; i < amountLayers; i++) {
|
||||
if (TextureLoader.resourceExists(getUnicodePage(i))) {
|
||||
layersLookupArray[i] = layer;
|
||||
layer++;
|
||||
} else {
|
||||
layersLookupArray[i] = -1;
|
||||
}
|
||||
}
|
||||
layersLookupArray[256] = layer;
|
||||
unicodeLayer = layer;
|
||||
System.out.println("Amount layers: " + layer);
|
||||
System.out.println(Arrays.toString(layersLookupArray));
|
||||
return new FontProviderMinecraft(256, layer + 1, layersLookupArray, GL11.GL_NEAREST);
|
||||
}
|
||||
|
||||
|
||||
private FontProviderMinecraft(int size, int layers, short[] layersLookupArray, int filter) {
|
||||
super(size, layers, layersLookupArray, filter);
|
||||
loadAtlas(0); // Load first texture from bound texture atlas
|
||||
|
||||
final ResourceLocation resource = getUnicodePage(0);
|
||||
final ByteBuffer pixels = getTextureFromResource(resource);
|
||||
|
||||
bind();
|
||||
|
||||
if (pixels == null) {
|
||||
return;
|
||||
}
|
||||
final int textureSize = (int) Math.sqrt(pixels.capacity());
|
||||
GLStateManager.glTexSubImage3D(
|
||||
GL30.GL_TEXTURE_2D_ARRAY,
|
||||
0,
|
||||
0, 0, layers - 1,
|
||||
textureSize, textureSize,
|
||||
1,
|
||||
GL11.GL_RED,
|
||||
GL11.GL_UNSIGNED_BYTE,
|
||||
pixels
|
||||
);
|
||||
|
||||
memFree(pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer generateGlyphData(int atlasId, GlyphData[] glyphs) {
|
||||
if (atlasId == 0) {
|
||||
final int size = getBoundTextureSize();
|
||||
final int[] charWidth = BatchingFontRenderer.asciiCharWidths;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
final int index = BatchingFontRenderer.lookupMcFontPosition((char) i);
|
||||
final float uStart = ((index & 15) * 8) / 256.0F;
|
||||
final float vStart = ((index >> 4) * 8) / 256.0F;
|
||||
final float glyphW = charWidth[index] - 1F;
|
||||
final float uSize = (charWidth[index] - 1F) / 256.0F;
|
||||
final float vSize = 7.99F / 256.0F;
|
||||
|
||||
glyphs[i] = new GlyphData(
|
||||
uStart, vStart,
|
||||
glyphW,
|
||||
uSize,
|
||||
vSize
|
||||
);
|
||||
}
|
||||
|
||||
populateUnicodeGlyphs(0, unicodeGlyphData);
|
||||
|
||||
return getBoundTextureData(size);
|
||||
}
|
||||
|
||||
populateUnicodeGlyphs(atlasId, glyphs);
|
||||
|
||||
return getTextureFromResource(getUnicodePage(atlasId));
|
||||
}
|
||||
|
||||
private void populateUnicodeGlyphs(int atlasId, GlyphData[] glyphs) {
|
||||
final byte[] glyphWidth = BatchingFontRenderer.glyphWidths;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
final char chr = (char) ((atlasId * 256) | i);
|
||||
int row = glyphWidth[chr] >>> 4;
|
||||
int column = glyphWidth[chr] & 15;
|
||||
float uStart = ((float)(chr % 16 * 16) + row) / 256F;
|
||||
float vStart = ((float)((chr & 255) / 16 * 16)) / 256F;
|
||||
float uSize = ((column + 1) - row - 0.02F) / 256F;
|
||||
float vSize = 15.98F / 256.0F;
|
||||
float glyphW = ((column + 1) - row) / 2.0F + 1.0F;
|
||||
glyphs[i] = new GlyphData(
|
||||
uStart, vStart,
|
||||
glyphW - 1,
|
||||
uSize,
|
||||
vSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static ResourceLocation getUnicodePage(int atlasId) {
|
||||
return new ResourceLocation(String.format("textures/font/unicode_page_%02x.png", atlasId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gtnewhorizons.angelica.client.font.atlas;
|
||||
|
||||
import com.gtnewhorizons.angelica.client.font.BatchingFontRenderer;
|
||||
import com.gtnewhorizons.angelica.client.font.GlyphData;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public final class FontProviderSGA extends FontTextureArray {
|
||||
|
||||
public static FontProviderSGA create() {
|
||||
final int size = getBoundTextureSize();
|
||||
return new FontProviderSGA(size, 1, new short[] {0}, GL11.GL_NEAREST);
|
||||
}
|
||||
|
||||
private FontProviderSGA(int size, int layers, short[] layersLookupArray, int filter) {
|
||||
super(size, layers, layersLookupArray, filter);
|
||||
loadAtlas(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer generateGlyphData(int atlasId, GlyphData[] glyphs) {
|
||||
if (atlasId == 0) {
|
||||
final int[] charWidth = BatchingFontRenderer.sgaCharWidths;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
final int index = BatchingFontRenderer.lookupMcFontPosition((char) i);
|
||||
final float uStart = ((index & 15) * 8) / 256.0F;
|
||||
final float vStart = ((index >> 4) * 8) / 256.0F;
|
||||
final float glyphW = charWidth[index] - 1F;
|
||||
final float uSize = (charWidth[index] - 1F) / 256.0F;
|
||||
final float vSize = 7.99F / 256.0F;
|
||||
|
||||
glyphs[i] = new GlyphData(
|
||||
uStart, vStart,
|
||||
glyphW,
|
||||
uSize,
|
||||
vSize
|
||||
);
|
||||
}
|
||||
|
||||
return getBoundTextureData(this.textureSize);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.gtnewhorizons.angelica.client.font.atlas;
|
||||
|
||||
import com.gtnewhorizons.angelica.client.font.BatchingFontRenderer;
|
||||
import com.gtnewhorizons.angelica.client.font.GlyphData;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public final class FontProviderSplash extends FontTextureArray {
|
||||
|
||||
public static FontProviderSplash create() {
|
||||
final int size = getBoundTextureSize();
|
||||
return new FontProviderSplash(size, 1, new short[] {0}, GL11.GL_NEAREST);
|
||||
}
|
||||
|
||||
private FontProviderSplash(int size, int layers, short[] layersLookupArray, int filter) {
|
||||
super(size, layers, layersLookupArray, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer generateGlyphData(int atlasId, GlyphData[] glyphs) {
|
||||
if (atlasId == 0) {
|
||||
final int[] charWidth = BatchingFontRenderer.asciiCharWidths;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
final int index = BatchingFontRenderer.lookupMcFontPosition((char) i);
|
||||
final float size = textureSize;
|
||||
final float uStart = ((index & 15) * 8) / size;
|
||||
final float vStart = ((index >> 4) * 8) / size;
|
||||
final float xAdvance = charWidth[index];
|
||||
final float glyphW = charWidth[index] - 1F;
|
||||
final float uSize = (charWidth[index] - 1F) / size;
|
||||
final float vSize = 7.99F / size;
|
||||
|
||||
glyphs[i] = new GlyphData(
|
||||
uStart, vStart,
|
||||
glyphW,
|
||||
uSize,
|
||||
vSize
|
||||
);
|
||||
}
|
||||
|
||||
return getBoundTextureData(this.textureSize);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
package com.gtnewhorizons.angelica.client.font.atlas;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.textures.TextureLoader;
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.client.font.GlyphData;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.awt.image.Raster;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAllocFloat;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutByte;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
|
||||
public abstract class FontTextureArray {
|
||||
protected final int id;
|
||||
protected final int textureSize;
|
||||
protected final short[] layersLookup;
|
||||
protected final GlyphData[][] atlasGlyphs = new GlyphData[256][];
|
||||
|
||||
static final boolean DUMP_ATLASES = false;
|
||||
|
||||
|
||||
FontTextureArray(int size, int layers, short[] layersLookupArray, final int filter) {
|
||||
this.layersLookup = layersLookupArray;
|
||||
this.id = GLStateManager.glGenTextures();
|
||||
this.textureSize = size;
|
||||
|
||||
bind();
|
||||
GLStateManager.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_MIN_FILTER, filter);
|
||||
GLStateManager.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_MAG_FILTER, filter);
|
||||
GLStateManager.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_WRAP_S, GL13.GL_CLAMP_TO_BORDER);
|
||||
GLStateManager.glTexParameteri(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_WRAP_T, GL13.GL_CLAMP_TO_BORDER);
|
||||
final FloatBuffer buffer = memAllocFloat(4);
|
||||
final long ptr = memAddress0(buffer);
|
||||
memPutFloat(ptr, 1);
|
||||
memPutFloat(ptr + 4, 1);
|
||||
memPutFloat(ptr + 8, 1);
|
||||
memPutFloat(ptr + 12, 1);
|
||||
GLStateManager.glTexParameter(GL30.GL_TEXTURE_2D_ARRAY, GL11.GL_TEXTURE_BORDER_COLOR, buffer);
|
||||
memFree(buffer);
|
||||
GLStateManager.glTexImage3D(
|
||||
GL30.GL_TEXTURE_2D_ARRAY,
|
||||
0,
|
||||
GL30.GL_R8,
|
||||
size, size, layers,
|
||||
0,
|
||||
GL11.GL_RED,
|
||||
GL11.GL_UNSIGNED_BYTE,
|
||||
(ByteBuffer) null
|
||||
);
|
||||
}
|
||||
//
|
||||
// public GlyphData[] addAtlas(int depth, ResourceLocation resource) {
|
||||
// try {
|
||||
// InputStream inputstream = Minecraft.getMinecraft().getResourceManager()
|
||||
// .getResource(resource).getInputStream();
|
||||
// final GlyphData[] data = addAtlas(depth, ImageIO.read(inputstream));
|
||||
// inputstream.close();
|
||||
// return data;
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// System.out.println("Failed to initialize resource " + resource + ".");
|
||||
// //initializedLayers[depth] = true;
|
||||
// final GlyphData[] glyphData = new GlyphData[256];
|
||||
// atlasGlyphs[depth] = glyphData;
|
||||
// return glyphData;
|
||||
// }
|
||||
// }
|
||||
|
||||
public final GlyphData[] getAtlasGlyphs(final int atlasId) {
|
||||
GlyphData[] glyphs = atlasGlyphs[atlasId];
|
||||
if (glyphs == null) {
|
||||
glyphs = addAtlas(atlasId);
|
||||
}
|
||||
return glyphs;
|
||||
}
|
||||
|
||||
public final GlyphData getGlyphData(final char ch) {
|
||||
return getAtlasGlyphs(ch >> 8)[ch & 255];
|
||||
}
|
||||
|
||||
public final boolean isGlyphAvailable(char ch) {
|
||||
return getAtlasGlyphs(ch >> 8)[ch & 255] != null;
|
||||
}
|
||||
|
||||
public final int getDepth(char ch) {
|
||||
return layersLookup[ch >> 8];
|
||||
}
|
||||
|
||||
public int getTexture() {
|
||||
return id;
|
||||
}
|
||||
|
||||
protected final void loadAtlas(int atlasId) {
|
||||
if (atlasGlyphs[atlasId] == null) {
|
||||
addAtlas(atlasId);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract ByteBuffer generateGlyphData(int atlasId, GlyphData[] glyphs);
|
||||
|
||||
|
||||
protected final GlyphData[] addAtlas(int atlasId) {
|
||||
final GlyphData[] glyphData = new GlyphData[256];
|
||||
try {
|
||||
final ByteBuffer pixels = generateGlyphData(atlasId, glyphData);
|
||||
|
||||
bind();
|
||||
|
||||
atlasGlyphs[atlasId] = glyphData;
|
||||
|
||||
if (pixels == null) {
|
||||
return glyphData;
|
||||
}
|
||||
final int size = (int) Math.sqrt(pixels.capacity());
|
||||
GLStateManager.glTexSubImage3D(
|
||||
GL30.GL_TEXTURE_2D_ARRAY,
|
||||
0,
|
||||
0, 0, layersLookup[atlasId],
|
||||
size, size,
|
||||
1,
|
||||
GL11.GL_RED,
|
||||
GL11.GL_UNSIGNED_BYTE,
|
||||
pixels
|
||||
);
|
||||
|
||||
memFree(pixels);
|
||||
|
||||
if (DUMP_ATLASES) {
|
||||
try {
|
||||
saveLayerAsPNG(layersLookup[atlasId], size, "test" + layersLookup[atlasId] + ".png");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AngelicaMod.LOGGER.error("Failed to initialize glyph data for atlas id " + id + "! Did this get called in another thread?", e);
|
||||
// Return the data (because it's correct), but don't initialize it (texture is still missing)
|
||||
return glyphData;
|
||||
|
||||
}
|
||||
|
||||
return glyphData;
|
||||
}
|
||||
|
||||
public void saveLayerAsPNG(int layer, int size, String fileName) {
|
||||
ByteBuffer pixels = memAlloc(size * size);
|
||||
|
||||
GLStateManager.glGetTextureSubImage(
|
||||
id,
|
||||
0,
|
||||
0, 0, layer,
|
||||
size, size, 1,
|
||||
GL11.GL_RED,
|
||||
GL11.GL_UNSIGNED_BYTE,
|
||||
size * size,
|
||||
memAddress(pixels)
|
||||
);
|
||||
|
||||
saveBufferAsPNG(size, pixels, fileName);
|
||||
}
|
||||
|
||||
private void saveBufferAsPNG(int size, ByteBuffer pixels, String fileName) {
|
||||
File dir = new File(Minecraft.getMinecraft().mcDataDir, "debug/texturearray");
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
int[] row = new int[size];
|
||||
|
||||
for (int y = 0; y < size; y++) {
|
||||
|
||||
int rowOffset = y * size;
|
||||
|
||||
for (int x = 0; x < size; x++) {
|
||||
|
||||
int i = rowOffset + x;
|
||||
|
||||
int c = pixels.get(i) & 0xFF;
|
||||
|
||||
row[x] = 0xFF000000 | (c << 16) | (c << 8) | c;
|
||||
}
|
||||
|
||||
// Flip vertically because OpenGL origin is bottom-left
|
||||
image.setRGB(0, size - 1 - y, size, 1, row, 0, size);
|
||||
}
|
||||
|
||||
// Save PNG
|
||||
try {
|
||||
ImageIO.write(image, "PNG", new File(dir, fileName));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void bind() {
|
||||
GLStateManager.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, id);
|
||||
}
|
||||
|
||||
public void unbind() {
|
||||
GLStateManager.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, 0);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
GLStateManager.glDeleteTextures(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static ByteBuffer getBoundTextureData(final int size) {
|
||||
final ByteBuffer pixels = memAlloc(size * size);
|
||||
GLStateManager.glGetTexImage(
|
||||
GL11.GL_TEXTURE_2D,
|
||||
0,
|
||||
GL11.GL_RED,
|
||||
GL11.GL_UNSIGNED_BYTE,
|
||||
pixels
|
||||
);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
static int getBoundTextureSize() {
|
||||
return GLStateManager.glGetTexLevelParameteri(
|
||||
GL11.GL_TEXTURE_2D,
|
||||
0,
|
||||
GL11.GL_TEXTURE_WIDTH
|
||||
);
|
||||
}
|
||||
|
||||
static ByteBuffer getPixelBuffer(BufferedImage image) {
|
||||
final int width = image.getWidth();
|
||||
final int height = image.getHeight();
|
||||
final ByteBuffer pixelBuffer = memAlloc(width * height);
|
||||
long ptr = memAddress0(pixelBuffer);
|
||||
|
||||
// Cheap Path
|
||||
if (image.getRaster().getDataBuffer() instanceof DataBufferInt dataBufferInt) {
|
||||
final int[] pixelValues = dataBufferInt.getData();
|
||||
for (int i : pixelValues) {
|
||||
memPutByte(ptr++, (byte) ((i >> 24) & 0xFF));
|
||||
}
|
||||
pixelBuffer.limit(pixelValues.length);
|
||||
return pixelBuffer;
|
||||
}
|
||||
|
||||
Raster raster = image.getRaster();
|
||||
ColorModel colorModel = image.getColorModel();
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
Object pixel = raster.getDataElements(x, y, null);
|
||||
int alpha = colorModel.getAlpha(pixel);
|
||||
|
||||
memPutByte(ptr++, (byte) alpha);
|
||||
}
|
||||
}
|
||||
pixelBuffer.limit(image.getHeight() * image.getWidth());
|
||||
return pixelBuffer;
|
||||
}
|
||||
|
||||
static ByteBuffer getTextureFromResource(ResourceLocation resource) {
|
||||
try {
|
||||
final BufferedImage image = TextureLoader.getBufferedImage(Minecraft.getMinecraft().getResourceManager()
|
||||
.getResource(resource));
|
||||
if (image.getWidth() != image.getHeight()) {
|
||||
throw new UnsupportedOperationException(resource + "has a different width and height, they cannot get rendered!");
|
||||
}
|
||||
return getPixelBuffer(image);
|
||||
} catch (IOException e) {
|
||||
System.out.println("Failed to load resource " + resource + ".");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.gtnewhorizon.gtnhlib.config.ConfigException;
|
||||
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
|
||||
import com.gtnewhorizons.angelica.client.font.FontStrategist;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import net.coderbot.iris.gui.element.widget.IrisButton;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
@@ -13,8 +14,9 @@ import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -47,9 +49,9 @@ public class FontConfigScreen extends GuiScreen {
|
||||
private IrisButton fwdButton;
|
||||
private IrisButton backButton;
|
||||
|
||||
SliderClone.Option optFontQuality = new SliderClone.Option(6, 72, 6);
|
||||
SliderClone.Option optFontQuality = new SliderClone.Option(18, 72, 1);
|
||||
SliderClone.Option optShadowOffset = new SliderClone.Option(0, 2, 0.05f);
|
||||
SliderClone.Option optShadowCopies = new SliderClone.Option(1, 8, 1);
|
||||
SliderClone.Option optBrightness = new SliderClone.Option(0, 10, 1);
|
||||
SliderClone.Option optBoldCopies = new SliderClone.Option(1, 8, 1);
|
||||
SliderClone.Option optGlyphAspect = new SliderClone.Option(-1, 1, 0.05f);
|
||||
SliderClone.Option optGlyphScale = new SliderClone.Option(0.1f, 3, 0.05f);
|
||||
@@ -83,7 +85,7 @@ public class FontConfigScreen extends GuiScreen {
|
||||
searchBox = new GuiTextField(this.fontRendererObj, this.width / 2 - 120, 24, 240, 20);
|
||||
searchBox.setMaxStringLength(64);
|
||||
fontList = new FontList();
|
||||
testArea = new GuiTextField(this.fontRendererObj, this.width * 5 / 6 - this.width / 7, 63 - 10, this.width * 2 / 7, 20);
|
||||
testArea = new GuiTextField(this.fontRendererObj, this.width * 5 / 6 - this.width / 7, 0, this.width * 2 / 7, 20);
|
||||
testArea.setMaxStringLength(512);
|
||||
testArea.setText(I18n.format("options.angelica.fontconfig.testareaexample"));
|
||||
initButtons();
|
||||
@@ -124,11 +126,79 @@ public class FontConfigScreen extends GuiScreen {
|
||||
.height(sliderHeight)
|
||||
.option(optFontQuality)
|
||||
.initialValue(FontConfig.customFontQuality)
|
||||
.setter(this::setFontQuality)
|
||||
.setter(FontConfig::setCustomFontQuality)
|
||||
.langKey("options.angelica.fontconfig.font_quality")
|
||||
.formatString("%2.0f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optBrightness)
|
||||
.initialValue(FontConfig.fontBrightness)
|
||||
.setter(FontConfig::setFontBrightness)
|
||||
.langKey("options.angelica.fontconfig.brightness")
|
||||
.formatString("%.0f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optGlyphAspect)
|
||||
.initialValue(FontConfig.glyphAspect)
|
||||
.setter(FontConfig::setGlyphAspect)
|
||||
.langKey("options.angelica.fontconfig.glyph_aspect")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optGlyphScale)
|
||||
.initialValue(FontConfig.glyphScale)
|
||||
.setter(FontConfig::setGlyphScale)
|
||||
.langKey("options.angelica.fontconfig.glyph_scale")
|
||||
.formatString("x%3.2f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optFontAAStrength)
|
||||
.initialValue(FontConfig.fontAAStrength)
|
||||
.setter(FontConfig::setFontAAStrength)
|
||||
.langKey("options.angelica.fontconfig.font_aa_strength")
|
||||
.formatString("%.0f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optFontAAMode)
|
||||
.initialValue(FontConfig.fontAAMode)
|
||||
.setter(FontConfig::setFontAAMode)
|
||||
.formatter(this::fontAAModeFormat)
|
||||
.langKey("options.angelica.fontconfig.aamode")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optWhitespaceScale)
|
||||
.initialValue(FontConfig.whitespaceScale)
|
||||
.setter(value -> FontConfig.whitespaceScale = value.intValue())
|
||||
.langKey("options.angelica.fontconfig.whitespace_scale")
|
||||
.formatString("x%3.2f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optGlyphSpacing)
|
||||
.initialValue(FontConfig.glyphSpacing)
|
||||
.setter(FontConfig::setGlyphSpacing)
|
||||
.langKey("options.angelica.fontconfig.glyph_spacing")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
@@ -139,74 +209,6 @@ public class FontConfigScreen extends GuiScreen {
|
||||
.formatString("x%3.2f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optGlyphAspect)
|
||||
.initialValue(FontConfig.glyphAspect)
|
||||
.setter(value -> FontConfig.glyphAspect = value)
|
||||
.langKey("options.angelica.fontconfig.glyph_aspect")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optGlyphScale)
|
||||
.initialValue(FontConfig.glyphScale)
|
||||
.setter(value -> FontConfig.glyphScale = value)
|
||||
.langKey("options.angelica.fontconfig.glyph_scale")
|
||||
.formatString("x%3.2f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optFontAAStrength)
|
||||
.initialValue(FontConfig.fontAAStrength)
|
||||
.setter(value -> FontConfig.fontAAStrength = value.intValue())
|
||||
.langKey("options.angelica.fontconfig.font_aa_strength")
|
||||
.formatString("%.0f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optFontAAMode)
|
||||
.initialValue(FontConfig.fontAAMode)
|
||||
.setter(value -> FontConfig.fontAAMode = value.intValue())
|
||||
.formatter(this::fontAAModeFormat)
|
||||
.langKey("options.angelica.fontconfig.aamode")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optWhitespaceScale)
|
||||
.initialValue(FontConfig.whitespaceScale)
|
||||
.setter(value -> FontConfig.whitespaceScale = value)
|
||||
.langKey("options.angelica.fontconfig.whitespace_scale")
|
||||
.formatString("x%3.2f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optGlyphSpacing)
|
||||
.initialValue(FontConfig.glyphSpacing)
|
||||
.setter(value -> FontConfig.glyphSpacing = value)
|
||||
.langKey("options.angelica.fontconfig.glyph_spacing")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
.option(optShadowCopies)
|
||||
.initialValue(FontConfig.shadowCopies)
|
||||
.setter(value -> FontConfig.shadowCopies = value.intValue())
|
||||
.langKey("options.angelica.fontconfig.shadow_copies")
|
||||
.formatString("%.0f")
|
||||
.build()
|
||||
);
|
||||
sliders.add(new SliderClone.SliderCloneBuilder()
|
||||
.width(sliderWidth)
|
||||
.height(sliderHeight)
|
||||
@@ -222,7 +224,7 @@ public class FontConfigScreen extends GuiScreen {
|
||||
.height(sliderHeight)
|
||||
.option(optCustomFontScale)
|
||||
.initialValue(FontConfig.customFontScale)
|
||||
.setter(value -> FontConfig.customFontScale = value)
|
||||
.setter(FontConfig::setCustomFontScale)
|
||||
.langKey("options.angelica.fontconfig.custom_font_scale")
|
||||
.formatString("x%3.2f")
|
||||
.build()
|
||||
@@ -279,10 +281,14 @@ public class FontConfigScreen extends GuiScreen {
|
||||
pos = selectedPrimaryFontListPos;
|
||||
if (pos >= 0 && pos < displayedFonts.size()) {
|
||||
FontConfig.customFontNamePrimary = displayedFonts.get(pos).getFontName();
|
||||
} else {
|
||||
FontConfig.customFontNamePrimary = "(none)";
|
||||
}
|
||||
pos = selectedFallbackFontListPos;
|
||||
if (pos >= 0 && pos < displayedFonts.size()) {
|
||||
FontConfig.customFontNameFallback = displayedFonts.get(pos).getFontName();
|
||||
} else {
|
||||
FontConfig.customFontNameFallback = "(none)";
|
||||
}
|
||||
|
||||
FontStrategist.reloadCustomFontProviders();
|
||||
@@ -307,7 +313,7 @@ public class FontConfigScreen extends GuiScreen {
|
||||
}
|
||||
|
||||
private void toggleCustomFont(IrisButton button) {
|
||||
FontConfig.enableCustomFont = !FontConfig.enableCustomFont;
|
||||
FontConfig.setEnableCustomFont(!FontConfig.enableCustomFont);
|
||||
applyChanges(false);
|
||||
button.displayString = FontConfig.enableCustomFont ?
|
||||
I18n.format("options.angelica.fontconfig.disable_custom_font") :
|
||||
@@ -338,9 +344,8 @@ public class FontConfigScreen extends GuiScreen {
|
||||
};
|
||||
}
|
||||
|
||||
private float lastMouseX = 0;
|
||||
private float lastMouseY = 0;
|
||||
private long lastStillTime = 0;
|
||||
private long hoverTimeStart = 0;
|
||||
private GuiButton hoveredButton;
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float delta) {
|
||||
drawBackground(0);
|
||||
@@ -374,7 +379,7 @@ public class FontConfigScreen extends GuiScreen {
|
||||
I18n.format("options.angelica.fontconfig.testareainfo"), this.width / 4
|
||||
);
|
||||
}
|
||||
int cumulativeY = 73;
|
||||
int cumulativeY = 44 + 5 + 10;
|
||||
for (String s : this.testAreaInfo) {
|
||||
drawCenteredString(this.fontRendererObj, s, this.width * 5 / 6, cumulativeY, 0xFFFFFF);
|
||||
cumulativeY += this.fontRendererObj.FONT_HEIGHT;
|
||||
@@ -387,13 +392,38 @@ public class FontConfigScreen extends GuiScreen {
|
||||
this.testArea.xPosition + this.fontRendererObj.getStringWidth(this.testAreaPrompt) / 2 + 4,
|
||||
this.testArea.yPosition + this.testArea.height / 2 - 4, 0xFFFFFF);
|
||||
}
|
||||
final String displayText = this.testArea.getText().replace('&', '§');
|
||||
List<String> testText = this.fontRendererObj.listFormattedStringToWidth(
|
||||
this.testArea.getText().replace('&', '§'), this.width / 4
|
||||
displayText, this.width / 4
|
||||
);
|
||||
cumulativeY += this.testArea.height + 4;
|
||||
for (String s : testText) {
|
||||
drawCenteredString(this.fontRendererObj, s, this.width * 5 / 6, cumulativeY, 0xFFFFFF);
|
||||
cumulativeY += this.fontRendererObj.FONT_HEIGHT;
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(this.width * 5 / 6, cumulativeY, 0);
|
||||
drawCenteredString(this.fontRendererObj, s + "§r (1.00x Scale)", 0, 0, 0xFFFFFF);
|
||||
cumulativeY += this.fontRendererObj.FONT_HEIGHT * 2;
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(this.width * 5 / 6, cumulativeY, 0);
|
||||
GLStateManager.glScalef(0.75f, 0.75f, 1);
|
||||
drawCenteredString(this.fontRendererObj, s + "§r (0.75x Scale)", 0, 0, 0xFFFFFF);
|
||||
cumulativeY += this.fontRendererObj.FONT_HEIGHT * 2 * 0.75;
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(this.width * 5 / 6, cumulativeY, 0);
|
||||
GLStateManager.glScalef(0.5f, 0.5f, 1);
|
||||
drawCenteredString(this.fontRendererObj, s + "§r (0.5x Scale)", 0, 0, 0xFFFFFF);
|
||||
cumulativeY += this.fontRendererObj.FONT_HEIGHT * 2 * 0.5;
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(this.width * 5 / 6, cumulativeY, 0);
|
||||
GLStateManager.glScalef(0.33f, 0.33f, 1);
|
||||
drawCenteredString(this.fontRendererObj, s + "§r (0.33x Scale)", 0, 0, 0xFFFFFF);
|
||||
cumulativeY += this.fontRendererObj.FONT_HEIGHT * 2 * 0.33;
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
super.drawScreen(mouseX, mouseY, delta);
|
||||
@@ -405,20 +435,19 @@ public class FontConfigScreen extends GuiScreen {
|
||||
final int left = slider.xPosition;
|
||||
final int right = slider.xPosition + slider.width;
|
||||
if (mouseY < top || mouseY >= bot || mouseX < left || mouseX >= right) { continue; }
|
||||
if (mouseX == lastMouseX && mouseY == lastMouseY) {
|
||||
if (lastStillTime == 0) {
|
||||
lastStillTime = System.currentTimeMillis();
|
||||
}
|
||||
if (lastStillTime + 500L < System.currentTimeMillis()) {
|
||||
displayTooltip(mouseX, mouseY, slider.tooltipKey);
|
||||
}
|
||||
} else {
|
||||
lastStillTime = 0;
|
||||
if (guiButton != hoveredButton) {
|
||||
hoveredButton = guiButton;
|
||||
hoverTimeStart = System.currentTimeMillis();
|
||||
}
|
||||
if (Mouse.isButtonDown(0) || Mouse.isButtonDown(1)) {
|
||||
hoverTimeStart = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
if (hoverTimeStart + 300L < System.currentTimeMillis()) {
|
||||
displayTooltip(mouseX, mouseY, slider.tooltipKey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
lastMouseX = mouseX;
|
||||
lastMouseY = mouseY;
|
||||
}
|
||||
|
||||
private void displayTooltip(int x, int y, String langKey) {
|
||||
@@ -511,19 +540,21 @@ public class FontConfigScreen extends GuiScreen {
|
||||
if (slotIndex < 0 || slotIndex >= this.getSize()) { return; }
|
||||
|
||||
if (mouseButton == 0) {
|
||||
onElemClicked(slotIndex, false);
|
||||
if (selectedPrimaryFontListPos == slotIndex) {
|
||||
selectedPrimaryFontListPos = -1;
|
||||
currentPrimaryFontName = "(none)";
|
||||
} else {
|
||||
selectedPrimaryFontListPos = slotIndex;
|
||||
currentPrimaryFontName = displayedFonts.get(slotIndex).getFontName();
|
||||
}
|
||||
} else if (mouseButton == 1) {
|
||||
onElemClicked(slotIndex, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onElemClicked(int index, boolean rightClick) {
|
||||
if (!rightClick) {
|
||||
selectedPrimaryFontListPos = index;
|
||||
currentPrimaryFontName = displayedFonts.get(index).getFontName();
|
||||
} else {
|
||||
selectedFallbackFontListPos = index;
|
||||
currentFallbackFontName = displayedFonts.get(index).getFontName();
|
||||
if (selectedFallbackFontListPos == slotIndex) {
|
||||
selectedFallbackFontListPos = -1;
|
||||
currentFallbackFontName = "(none)";
|
||||
} else {
|
||||
selectedFallbackFontListPos = slotIndex;
|
||||
currentFallbackFontName = displayedFonts.get(slotIndex).getFontName();
|
||||
}
|
||||
}
|
||||
applyChanges(false);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ public class DeferredDrawBatcher {
|
||||
*/
|
||||
static long captureStateKey() {
|
||||
final int activeUnit = GLStateManager.getActiveTextureUnit();
|
||||
final int textureId = GLStateManager.getTextures().getTextureUnitBindings(activeUnit).getBinding();
|
||||
final int textureId = GLStateManager.getTextures().getTextureUnitBindings(activeUnit).getTexture2D();
|
||||
final int srcRgb = GLStateManager.getBlendState().getSrcRgb();
|
||||
final int dstRgb = GLStateManager.getBlendState().getDstRgb();
|
||||
final boolean blendEnabled = GLStateManager.getBlendMode().isEnabled();
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package com.gtnewhorizons.angelica.client.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memRealloc;
|
||||
|
||||
public final class FallbackStreamingDrawer extends StreamingDrawer {
|
||||
|
||||
private int capacity;
|
||||
private int pendingDataSize;
|
||||
private int elementCount;
|
||||
// private ByteBuffer mappedBuffer;
|
||||
// private long mappedAddress;
|
||||
private ByteBuffer buffer;
|
||||
private long basePtr;
|
||||
|
||||
private void ensureCapacity(int needed) {
|
||||
if (needed + pendingDataSize > capacity) {
|
||||
resize(capacity * 2);
|
||||
}
|
||||
}
|
||||
|
||||
FallbackStreamingDrawer(int stride, int elementCapacity, VAOConsumer initVAO) {
|
||||
super(initVAO, stride);
|
||||
capacity = elementCapacity * stride;
|
||||
final int totalCapacity = capacity;
|
||||
|
||||
buffer = memAlloc(totalCapacity);
|
||||
basePtr = memAddress0(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawElementsInstanced(int mode, int indices_count, int type, long indices_buffer_offset) {
|
||||
if (this.vaoId == 0) {
|
||||
super.initVAO();
|
||||
}
|
||||
buffer.limit(pendingDataSize);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
|
||||
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STREAM_DRAW);
|
||||
|
||||
GL31.glDrawElementsInstanced(
|
||||
GL11.GL_TRIANGLES,
|
||||
6,
|
||||
GL11.GL_UNSIGNED_SHORT,
|
||||
0,
|
||||
elementCount
|
||||
);
|
||||
elementCount = 0;
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
pendingDataSize = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long writeSection(int needed) {
|
||||
ensureCapacity(needed);
|
||||
long pointer = basePtr + pendingDataSize;
|
||||
pendingDataSize += needed;
|
||||
elementCount++;
|
||||
return pointer;
|
||||
}
|
||||
|
||||
public void resize(int newSectionSize) {
|
||||
capacity = newSectionSize;
|
||||
buffer = memRealloc(buffer, newSectionSize);
|
||||
basePtr = memAddress0(buffer);
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
GLStateManager.glDeleteBuffers(vboId);
|
||||
vboId = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return elementCount == 0;
|
||||
}
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
package com.gtnewhorizons.angelica.client.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
import org.lwjgl.opengl.GL44;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
|
||||
/**
|
||||
* Triple-buffered persistent VAO/VBO streamer.
|
||||
* <br>
|
||||
* Unlike {@link com.gtnewhorizons.angelica.glsm.streaming.PersistentStreamingBuffer},
|
||||
* this provides direct writes into persistently mapped GPU memory, bypassing
|
||||
* temporary upload buffers for lower-overhead streaming.
|
||||
*/
|
||||
public final class PersistentStreamingDrawer extends StreamingDrawer {
|
||||
|
||||
private static final int NUM_SECTIONS = 4;
|
||||
|
||||
private static final int FLAGS =
|
||||
GL30.GL_MAP_WRITE_BIT
|
||||
| GL44.GL_MAP_PERSISTENT_BIT
|
||||
| GL44.GL_MAP_COHERENT_BIT;
|
||||
|
||||
private int sectionSize;
|
||||
//private int totalCapacity;
|
||||
private ByteBuffer persistentMapping;
|
||||
private long baseAddress;
|
||||
private final long[] fences = new long[NUM_SECTIONS];
|
||||
private int currentSection;
|
||||
private int sectionWriteOffset;
|
||||
private int sectionWriteStart;
|
||||
private long sectionWritePointer;
|
||||
private int sectionDrawCount;
|
||||
|
||||
private static int globalFPSCount;
|
||||
|
||||
PersistentStreamingDrawer(int stride, int elementCapacity, VAOConsumer initVAO) {
|
||||
super(initVAO, stride);
|
||||
sectionSize = elementCapacity * stride;
|
||||
final int totalCapacity = getTotalCapacity();
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
|
||||
GLStateManager.glBufferStorage(GL15.GL_ARRAY_BUFFER, totalCapacity, FLAGS);
|
||||
|
||||
persistentMapping = GLStateManager.glMapBufferRange(
|
||||
GL15.GL_ARRAY_BUFFER, 0, totalCapacity, FLAGS);
|
||||
baseAddress = memAddress0(persistentMapping);
|
||||
sectionWritePointer = baseAddress;
|
||||
|
||||
if (persistentMapping == null)
|
||||
throw new RuntimeException("Persistent mapping failed — check ARB_buffer_storage support.");
|
||||
}
|
||||
|
||||
public static void incrementFPS() { //TODO make this use Display
|
||||
globalFPSCount = (globalFPSCount + 1) % NUM_SECTIONS;
|
||||
}
|
||||
|
||||
private void ensureCapacity(int needed) {
|
||||
if (needed + sectionWriteOffset > sectionSize) {
|
||||
System.out.println("Resizing to " + sectionSize * 2 + " bytes.");
|
||||
new Exception().printStackTrace();
|
||||
resize(sectionSize * 2);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long writeSection(int needed) {
|
||||
if (globalFPSCount != currentSection) {
|
||||
lockCurrentSection();
|
||||
advanceSection();
|
||||
//System.out.println("Moving to section " + currentSection);
|
||||
}
|
||||
ensureCapacity(needed);
|
||||
long pointer = sectionWritePointer + sectionWriteOffset;
|
||||
sectionWriteOffset += needed;
|
||||
sectionDrawCount++;
|
||||
return pointer;
|
||||
}
|
||||
|
||||
// AI slop resize, seems to work well
|
||||
// Essentially copies any pending draw operations into the new buffer
|
||||
public void resize(int newSectionSize) {
|
||||
// Preserve pending batch
|
||||
int sectionBase = currentSection * sectionSize;
|
||||
|
||||
int batchSize = (sectionBase + sectionWriteOffset) - sectionWriteStart;
|
||||
|
||||
long tempBuffer = nmemAllocChecked(batchSize);
|
||||
|
||||
memCopy(
|
||||
sectionWritePointer,
|
||||
tempBuffer,
|
||||
batchSize
|
||||
);
|
||||
|
||||
// Wait for GPU
|
||||
for (int i = 0; i < NUM_SECTIONS; i++) {
|
||||
waitForSection(i);
|
||||
}
|
||||
|
||||
// Destroy old buffer
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
|
||||
GLStateManager.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
GLStateManager.glDeleteBuffers(vboId);
|
||||
|
||||
// Create new buffer
|
||||
sectionSize = newSectionSize;
|
||||
sectionWriteStart = currentSection * sectionSize;
|
||||
|
||||
vboId = GLStateManager.glGenBuffers();
|
||||
|
||||
final int totalCapacity = getTotalCapacity();
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
|
||||
GLStateManager.glBufferStorage(
|
||||
GL15.GL_ARRAY_BUFFER,
|
||||
totalCapacity,
|
||||
FLAGS
|
||||
);
|
||||
persistentMapping = GLStateManager.glMapBufferRange(
|
||||
GL15.GL_ARRAY_BUFFER,
|
||||
0,
|
||||
totalCapacity,
|
||||
FLAGS
|
||||
);
|
||||
baseAddress = memAddress0(persistentMapping);
|
||||
|
||||
sectionWritePointer = baseAddress + sectionWriteStart;
|
||||
sectionWriteOffset = batchSize;
|
||||
vaoId = 0;
|
||||
|
||||
// Restore pending batch at offset 0
|
||||
memCopy(
|
||||
tempBuffer,
|
||||
sectionWritePointer,
|
||||
batchSize
|
||||
);
|
||||
|
||||
nmemFree(tempBuffer);
|
||||
}
|
||||
|
||||
public int getTotalCapacity() {
|
||||
return sectionSize * NUM_SECTIONS;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
for (int i = 0; i < NUM_SECTIONS; i++) {
|
||||
if (fences[i] != 0) {
|
||||
GLStateManager.glDeleteSync(fences[i]);
|
||||
fences[i] = 0;
|
||||
}
|
||||
}
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
|
||||
GLStateManager.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
GLStateManager.glDeleteBuffers(vboId);
|
||||
persistentMapping = null;
|
||||
vboId = 0;
|
||||
}
|
||||
|
||||
private void lockCurrentSection() {
|
||||
if (fences[currentSection] != 0L)
|
||||
GLStateManager.glDeleteSync(fences[currentSection]);
|
||||
fences[currentSection] = GLStateManager.glFenceSync(GL32.GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
}
|
||||
|
||||
private void advanceSection() {
|
||||
currentSection = (currentSection + 1) % NUM_SECTIONS;
|
||||
sectionWriteOffset = 0;
|
||||
sectionWriteStart = currentSection * sectionSize;
|
||||
sectionWritePointer = baseAddress + sectionWriteStart;
|
||||
waitForSection(currentSection);
|
||||
}
|
||||
|
||||
private void waitForSection(int section) {
|
||||
if (fences[section] == 0) return;
|
||||
|
||||
while (true) {
|
||||
int result = GLStateManager.glClientWaitSync(
|
||||
fences[section], GL32.GL_SYNC_FLUSH_COMMANDS_BIT, 1_000_000_000L);
|
||||
|
||||
if (result == GL32.GL_ALREADY_SIGNALED || result == GL32.GL_CONDITION_SATISFIED) break;
|
||||
if (result == GL32.GL_WAIT_FAILED)
|
||||
throw new RuntimeException("glClientWaitSync failed for section " + section);
|
||||
// GL_TIMEOUT_EXPIRED — retry
|
||||
}
|
||||
|
||||
GLStateManager.glDeleteSync(fences[section]);
|
||||
fences[section] = 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void drawElementsInstanced(
|
||||
int mode, int indices_count, int type, long indices_buffer_offset
|
||||
) {
|
||||
if (this.vaoId == 0) {
|
||||
super.initVAO();
|
||||
}
|
||||
final int start = sectionWriteStart;
|
||||
final int offset = start / stride;
|
||||
GL42.glDrawElementsInstancedBaseInstance(
|
||||
mode,
|
||||
indices_count,
|
||||
type,
|
||||
indices_buffer_offset,
|
||||
sectionDrawCount,
|
||||
offset
|
||||
);
|
||||
sectionDrawCount = 0;
|
||||
|
||||
sectionWriteStart = currentSection * sectionSize + sectionWriteOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return sectionDrawCount == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.gtnewhorizons.angelica.client.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.ContextCapabilities;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
public abstract class StreamingDrawer {
|
||||
|
||||
protected int vboId;
|
||||
protected int vaoId;
|
||||
protected final VAOConsumer vaoConsumer;
|
||||
protected final int stride;
|
||||
|
||||
private static final boolean supportsPersistent;
|
||||
|
||||
static {
|
||||
final ContextCapabilities caps = GLStateManager.capabilities;
|
||||
// supportsPersistent = caps.OpenGL44
|
||||
// || (caps.GL_ARB_buffer_storage && caps.GL_ARB_base_instance);
|
||||
supportsPersistent = false;
|
||||
}
|
||||
|
||||
|
||||
public static StreamingDrawer create(int stride, int elementCapacity, VAOConsumer vaoConsumer) {
|
||||
return supportsPersistent ? new PersistentStreamingDrawer(stride, elementCapacity, vaoConsumer)
|
||||
: new FallbackStreamingDrawer(stride, elementCapacity, vaoConsumer);
|
||||
}
|
||||
|
||||
StreamingDrawer(VAOConsumer vaoConsumer, int stride) {
|
||||
this.vboId = GL15.glGenBuffers();
|
||||
this.vaoConsumer = vaoConsumer;
|
||||
this.stride = stride;
|
||||
}
|
||||
|
||||
public final void initVAO() {
|
||||
this.vaoId = GLStateManager.glGenVertexArrays();
|
||||
vaoConsumer.initialize(vaoId, vboId);
|
||||
}
|
||||
|
||||
public final int getVBO() {
|
||||
return vboId;
|
||||
}
|
||||
|
||||
public final int getVAO() {
|
||||
return vaoId;
|
||||
}
|
||||
|
||||
public abstract long writeSection(int needed);
|
||||
|
||||
public abstract void drawElementsInstanced(
|
||||
int mode, int indices_count, int type, long indices_buffer_offset
|
||||
);
|
||||
|
||||
public abstract boolean isEmpty();
|
||||
|
||||
public final void delete() {
|
||||
GL15.glDeleteBuffers(vboId);
|
||||
GL30.glDeleteVertexArrays(vaoId);
|
||||
}
|
||||
|
||||
public final void switchContext() {
|
||||
GL30.glDeleteVertexArrays(vaoId);
|
||||
vaoId = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.client.streaming;
|
||||
|
||||
public interface VAOConsumer {
|
||||
|
||||
/**
|
||||
* Passes in a vertex array object (VAO) and a vertex buffer object (VBO) and expects the program to initialize
|
||||
* the VAO with the correct pointers using things such as {@link org.lwjgl.opengl.GL20#glVertexAttribPointer}.
|
||||
* @return The Data size that the VAO expects
|
||||
*/
|
||||
void initialize(int vao, int vbo);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.compat;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.font.FontRendering;
|
||||
import com.gtnewhorizons.angelica.client.font.ColorCodeUtils;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
|
||||
public class GTNHLibCompat {
|
||||
|
||||
public static final boolean HAS_TEXT_PREPROCESSOR;
|
||||
|
||||
static {
|
||||
boolean found = false;
|
||||
try {
|
||||
FontRendering.class.getMethod("setTextPreprocessor", Function.class);
|
||||
found = true;
|
||||
} catch (NoSuchMethodException ignored) {}
|
||||
HAS_TEXT_PREPROCESSOR = found;
|
||||
}
|
||||
|
||||
/** Only call when {@link #HAS_TEXT_PREPROCESSOR} is true. */
|
||||
public static void registerPreprocessor() {
|
||||
FontRendering.setTextPreprocessor(new FontRendering.TextPreprocessor() {
|
||||
|
||||
@Override
|
||||
public String apply(String text) {
|
||||
return AngelicaConfig.enableAmpersandConversion
|
||||
? ColorCodeUtils.convertAmpersandToSectionX(text)
|
||||
: text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlesAmpCodes() {
|
||||
return AngelicaConfig.enableAmpersandConversion;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package com.gtnewhorizons.angelica.config;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.config.Config;
|
||||
import com.gtnewhorizons.angelica.client.font.BatchingFontRenderer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.client.font.BatchingFontRenderer.reloadShaders;
|
||||
import static com.gtnewhorizons.angelica.client.font.BatchingFontRenderer.reloadFonts;
|
||||
|
||||
@Config(modid = "angelica", category = "customfont")
|
||||
public class FontConfig {
|
||||
@@ -8,6 +12,11 @@ public class FontConfig {
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean enableCustomFont;
|
||||
|
||||
public static void setEnableCustomFont(boolean enableCustomFont) {
|
||||
FontConfig.enableCustomFont = enableCustomFont;
|
||||
reloadFonts();
|
||||
}
|
||||
|
||||
@Config.Comment("Name of the primary custom font. Best not to set it from here.")
|
||||
@Config.DefaultString("(none)")
|
||||
public static String customFontNamePrimary;
|
||||
@@ -17,19 +26,29 @@ public class FontConfig {
|
||||
public static String customFontNameFallback;
|
||||
|
||||
@Config.Comment("The quality at which custom fonts are rendered, making them less pixelated but increasing memory usage.")
|
||||
@Config.DefaultInt(30)
|
||||
@Config.DefaultInt(32)
|
||||
@Config.RangeInt(min = 6, max = 72)
|
||||
public static int customFontQuality;
|
||||
|
||||
public static void setCustomFontQuality(float customFontQuality) {
|
||||
FontConfig.customFontQuality = (int) customFontQuality;
|
||||
reloadFonts();
|
||||
}
|
||||
|
||||
@Config.Comment("Controls the distance at which the font's shadow is drawn.")
|
||||
@Config.DefaultFloat(1F)
|
||||
@Config.RangeFloat(min = 0F, max = 2F)
|
||||
public static float fontShadowOffset;
|
||||
|
||||
@Config.Comment("The number of shadows to be drawn behind text at various offsets.")
|
||||
@Config.DefaultInt(1)
|
||||
@Config.RangeInt(min = 1, max = 8)
|
||||
public static int shadowCopies;
|
||||
@Config.Comment("Adds extra brightness to fonts. 0 = None, 10 = 2x brighter")
|
||||
@Config.DefaultInt(0)
|
||||
@Config.RangeInt(min = 0, max = 10)
|
||||
public static int fontBrightness;
|
||||
|
||||
public static void setFontBrightness(float fontBrightness) {
|
||||
FontConfig.fontBrightness = (int) fontBrightness;
|
||||
BatchingFontRenderer.reloadShaders();
|
||||
}
|
||||
|
||||
@Config.Comment("The number of bold copies to be drawn behind text at various offsets.")
|
||||
@Config.DefaultInt(2)
|
||||
@@ -41,35 +60,70 @@ public class FontConfig {
|
||||
@Config.RangeFloat(min = -1F, max = 1F)
|
||||
public static float glyphAspect;
|
||||
|
||||
public static void setGlyphAspect(float glyphAspect) {
|
||||
FontConfig.glyphAspect = glyphAspect;
|
||||
reloadFonts();
|
||||
}
|
||||
|
||||
@Config.Comment("Scale of each glyph, whitespace excluded.")
|
||||
@Config.DefaultFloat(1F)
|
||||
@Config.RangeFloat(min = 0.1F, max = 3F)
|
||||
public static float glyphScale;
|
||||
|
||||
public static void setGlyphScale(float glyphScale) {
|
||||
FontConfig.glyphScale = glyphScale;
|
||||
reloadFonts();
|
||||
}
|
||||
|
||||
@Config.Comment("Whitespace scale.")
|
||||
@Config.DefaultFloat(1F)
|
||||
@Config.RangeFloat(min = 0.1F, max = 3F)
|
||||
public static float whitespaceScale;
|
||||
@Config.DefaultInt(1)
|
||||
@Config.RangeInt(min = 1, max = 3)
|
||||
public static int whitespaceScale;
|
||||
|
||||
public static void setWhitespaceScale(int whitespaceScale) {
|
||||
FontConfig.whitespaceScale = whitespaceScale;
|
||||
reloadFonts();
|
||||
}
|
||||
|
||||
@Config.Comment("Adds extra spacing between glyphs.")
|
||||
@Config.DefaultFloat(0F)
|
||||
@Config.RangeFloat(min = -2F, max = 2F)
|
||||
public static float glyphSpacing;
|
||||
|
||||
public static void setGlyphSpacing(float glyphSpacing) {
|
||||
FontConfig.glyphSpacing = glyphSpacing;
|
||||
reloadFonts();
|
||||
}
|
||||
|
||||
@Config.Comment("Controls font antialiasing. 0 = none, 1 = 4x MSAA, 2 = 16x MSAA.")
|
||||
@Config.DefaultInt(2)
|
||||
@Config.RangeInt(min = 0, max = 2)
|
||||
public static int fontAAMode;
|
||||
|
||||
public static void setFontAAMode(float fontAAMode) {
|
||||
FontConfig.fontAAMode = (int) fontAAMode;
|
||||
reloadShaders();
|
||||
}
|
||||
|
||||
@Config.Comment("Affects font antialiasing sample spacing. Higher values increase blur.")
|
||||
@Config.DefaultInt(7)
|
||||
@Config.RangeInt(min = 1, max = 24)
|
||||
public static int fontAAStrength;
|
||||
|
||||
public static void setFontAAStrength(float fontAAStrength) {
|
||||
FontConfig.fontAAStrength = (int) fontAAStrength;
|
||||
reloadShaders();
|
||||
}
|
||||
|
||||
@Config.Comment("Custom font scale multiplier, for bugfixes.")
|
||||
@Config.DefaultFloat(1.5F)
|
||||
@Config.RangeFloat(min = 0.1F, max = 3F)
|
||||
public static float customFontScale;
|
||||
public static float customFontScale; //TODO wat
|
||||
|
||||
public static void setCustomFontScale(float customFontScale) {
|
||||
FontConfig.customFontScale = customFontScale;
|
||||
reloadFonts();
|
||||
}
|
||||
|
||||
@Config.Comment("A shadow offset multiplier applied only to the Unicode font.")
|
||||
@Config.DefaultFloat(0.5F)
|
||||
@@ -82,4 +136,9 @@ public class FontConfig {
|
||||
"See for additional details: https://github.com/GTNewHorizons/Angelica/issues/1239#issuecomment-3729877936"})
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean enableGlyphReplacements;
|
||||
|
||||
public static void setEnableGlyphReplacements(boolean enableGlyphReplacements) {
|
||||
FontConfig.enableGlyphReplacements = enableGlyphReplacements;
|
||||
reloadFonts();
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,6 @@ public enum Mixins implements IMixins {
|
||||
, "angelica.gui.MixinGuiOptions"
|
||||
, "angelica.optimizations.MixinRendererLivingEntity"
|
||||
, "angelica.rendering.MixinRenderGlobal_SelectionBox"
|
||||
, "angelica.gui.MixinGuiIngameForge_ModernF3"
|
||||
)
|
||||
),
|
||||
|
||||
|
||||
@@ -10,4 +10,8 @@ public interface FontRendererAccessor {
|
||||
BatchingFontRenderer angelica$getBatcher();
|
||||
|
||||
void angelica$bindTexture(ResourceLocation location);
|
||||
|
||||
int[] angelica$getCharWidths();
|
||||
|
||||
byte[] angelica$getGlyphWidths();
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import biomesoplenty.api.content.BOPCBlocks;
|
||||
import com.google.common.base.Objects;
|
||||
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelRegistry;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VAOManager;
|
||||
import com.gtnewhorizons.angelica.client.font.ColorCodeUtils;
|
||||
import com.gtnewhorizons.angelica.commands.AngelicaCommand;
|
||||
import com.gtnewhorizons.angelica.common.BlockError;
|
||||
import com.gtnewhorizons.angelica.compat.ModStatus;
|
||||
@@ -109,6 +110,7 @@ public final class ClientProxy extends CommonProxy {
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
MinecraftForge.EVENT_BUS.register(new EmissiveTextureAutoloader());
|
||||
ModelRegistry.registerModid(MOD_ID);
|
||||
ColorCodeUtils.initPreprocessor();
|
||||
blockError = new BlockError();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.gtnewhorizons.angelica.utils;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
|
||||
public final class InstancedHelper {
|
||||
private static int quadVBO;
|
||||
private static int quadEBO;
|
||||
|
||||
public static int getQuadVBO() {
|
||||
if (quadVBO > 0) return quadVBO;
|
||||
quadVBO = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, quadVBO);
|
||||
|
||||
final ByteBuffer data = memAlloc(32);
|
||||
final long address = memAddress0(data);
|
||||
memPutFloat(address, 0); memPutFloat(address + 4, 0);
|
||||
memPutFloat(address + 8, 1); memPutFloat(address + 12, 0);
|
||||
memPutFloat(address + 16, 0); memPutFloat(address + 20, 1);
|
||||
memPutFloat(address + 24, 1); memPutFloat(address + 28, 1);
|
||||
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW);
|
||||
memFree(data);
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
return quadVBO;
|
||||
}
|
||||
|
||||
public static int getQuadEBO() {
|
||||
if (quadEBO > 0) return quadEBO;
|
||||
quadEBO = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, quadEBO);
|
||||
|
||||
final ByteBuffer data = memAlloc(12);
|
||||
final long address = memAddress0(data);
|
||||
// triangle 1
|
||||
memPutShort(address, (short) 0);
|
||||
memPutShort(address + 2, (short) (2));
|
||||
memPutShort(address + 4, (short) (1));
|
||||
|
||||
// triangle 2
|
||||
memPutShort(address + 6, (short) (2));
|
||||
memPutShort(address + 8, (short) (3));
|
||||
memPutShort(address + 10, (short) (1));
|
||||
|
||||
GLStateManager.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW);
|
||||
memFree(data);
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
return quadEBO;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.jellysquid.mods.sodium.client.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.gtnewhorizons.angelica.client.font.ColorCodeUtils;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
@@ -591,8 +592,11 @@ public class SodiumGameOptionPages {
|
||||
.setName(I18n.format("options.angelica.texteffects.ampersand_conversion"))
|
||||
.setTooltip(I18n.format("options.angelica.texteffects.ampersand_conversion.tooltip", "\\&"))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.enableAmpersandConversion = value,
|
||||
opts -> AngelicaConfig.enableAmpersandConversion)
|
||||
.setBinding((opts, value) -> {
|
||||
AngelicaConfig.enableAmpersandConversion = value;
|
||||
ColorCodeUtils.initPreprocessor();
|
||||
},
|
||||
opts -> AngelicaConfig.enableAmpersandConversion)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ options.dynamiclights.entity.tooltip=Toggle dynamic lighting for %s entities. Di
|
||||
options.angelica.search.no_results=No matching options
|
||||
options.angelica.fontconfig=Font Configuration...
|
||||
options.angelica.fontconfig.title=Font Configuration
|
||||
options.angelica.fontconfig.currentfonts=Primary font: §e%s§r Fallback font: §b%s
|
||||
options.angelica.fontconfig.currentfonts=Primary font: §e%s (LMB)§r Fallback font: §b%s (RMB)
|
||||
options.angelica.fontconfig.searchprompt=§7§oSearch fonts...
|
||||
options.angelica.fontconfig.testareaprompt=§7§oTest area...
|
||||
options.angelica.fontconfig.testareainfo=Enter text below to see how it renders. Use & for formatting.
|
||||
@@ -177,7 +177,7 @@ options.angelica.fontconfig.reset_values=Reset Values
|
||||
options.angelica.fontconfig.no_fonts_warning=Custom fonts are not available on your system. Check logs for details.
|
||||
options.angelica.fontconfig.font_quality=Custom Font Quality: %s
|
||||
options.angelica.fontconfig.shadow_offset=Shadow Offset: %s
|
||||
options.angelica.fontconfig.shadow_copies=Shadow Copies: %s
|
||||
options.angelica.fontconfig.brightness=Brightness: %s
|
||||
options.angelica.fontconfig.bold_copies=Bold Copies: %s
|
||||
options.angelica.fontconfig.glyph_scale=Glyph Scale: %s
|
||||
options.angelica.fontconfig.glyph_aspect=Glyph Aspect: %s
|
||||
@@ -191,7 +191,7 @@ options.angelica.fontconfig.custom_font_scale=Custom Font Scale: %s
|
||||
options.angelica.fontconfig.shadow_offset_unicode=Unicode Shadow Offset: %s
|
||||
options.angelica.fontconfig.font_quality.tooltip=The quality at which custom fonts are rendered, making them less pixelated at the expense of some performance and memory.
|
||||
options.angelica.fontconfig.shadow_offset.tooltip=The distance at which the fonts' shadows are drawn. Also affects the way §lbold§r text is drawn.
|
||||
options.angelica.fontconfig.shadow_copies.tooltip=The number of shadows to be drawn behind text, all offset so as to provide a fake 3D effect.
|
||||
options.angelica.fontconfig.brightness.tooltip=Increases the brightness of the text, resulting in more readable text for lower resolutions. Most noticeable for custom fonts.
|
||||
options.angelica.fontconfig.bold_copies.tooltip=The number of text copies drawn when rendering bold text. Can improve the appearance of bold text with thinner custom fonts.
|
||||
options.angelica.fontconfig.glyph_scale.tooltip=The scale of each individual glyph. Extreme values might make your game unplayable.
|
||||
options.angelica.fontconfig.glyph_aspect.tooltip=The aspect ratio of each individual glyph.
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#version 330 core
|
||||
|
||||
uniform sampler2DArray sampler0; // Minecraft fonts
|
||||
#ifdef MULTISAMPLING
|
||||
uniform sampler2DArray sampler1; // Custom fonts
|
||||
#endif
|
||||
uniform float strength;
|
||||
|
||||
flat in vec4 tB; // uMin, vMin, uMax, vMax
|
||||
flat in vec4 color;
|
||||
flat in uint layer;
|
||||
|
||||
#ifdef MULTISAMPLING
|
||||
flat in uint secondTexture;
|
||||
#endif
|
||||
|
||||
|
||||
in vec2 texCoord;
|
||||
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
/*
|
||||
Hacky MSAA and anisotropic filtering. Cursed beyond belief. There _may_ have been simpler means of
|
||||
achieving the produced effects, but this appears to work without noticeable performance losses.
|
||||
*/
|
||||
|
||||
float totalWt;
|
||||
|
||||
float aaSample(vec2 uv, float du, float dv, float factorU, float factorV) {
|
||||
float distSquared = sqrt(du * du + dv * dv);
|
||||
float weight = exp(-distSquared / 6);
|
||||
totalWt += weight;
|
||||
float finalU = uv.x + factorU * du;
|
||||
float finalV = uv.y + factorV * dv;
|
||||
if (finalU < tB.x || finalV < tB.y || finalU > tB.z || finalV > tB.w) return 0.0f;
|
||||
return weight * texture(sampler0, vec3(finalU, finalV, layer)).r;
|
||||
}
|
||||
|
||||
float easeOut(float t) {
|
||||
t = clamp(t, 0.0, 1.0);
|
||||
return 1.0 - pow(1.0 - t, 2.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 col = color;
|
||||
float alpha = 0;
|
||||
// For custom fonts, don't apply the AA as it results in a blurry look.
|
||||
|
||||
#ifdef MULTISAMPLING
|
||||
if (secondTexture != 0u) {
|
||||
alpha = texture(sampler1, vec3(texCoord, layer)).r;
|
||||
if (alpha < 0.05) discard;
|
||||
|
||||
|
||||
#ifdef FONT_BRIGHTNESS
|
||||
alpha = min(alpha * FONT_BRIGHTNESS, 1);
|
||||
#endif
|
||||
|
||||
fragColor = vec4(color.rgb, alpha * color.a);
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if AA_MODE == 0
|
||||
|
||||
|
||||
alpha = texture(sampler0, vec3(texCoord, layer)).r;
|
||||
|
||||
#else
|
||||
|
||||
vec2 texScaled = texCoord * AA_STRENGTH;
|
||||
float fu = abs(dFdx(texScaled.x)) + abs(dFdy(texScaled.x));
|
||||
float fv = abs(dFdx(texScaled.y)) + abs(dFdy(texScaled.y));
|
||||
|
||||
float res = 0;
|
||||
totalWt = 0;
|
||||
|
||||
|
||||
#if AA_MODE == 1
|
||||
|
||||
res += aaSample(texCoord, 2, 6, fu, fv);
|
||||
res += aaSample(texCoord, 6, -2, fu, fv);
|
||||
res += aaSample(texCoord, -2, -6, fu, fv);
|
||||
res += aaSample(texCoord, -6, 2, fu, fv);
|
||||
|
||||
#elif AA_MODE == 2
|
||||
|
||||
res += aaSample(texCoord, 1, 1, fu, fv);
|
||||
res += aaSample(texCoord, -1, -3, fu, fv);
|
||||
res += aaSample(texCoord, -3, 2, fu, fv);
|
||||
res += aaSample(texCoord, 4, -1, fu, fv);
|
||||
res += aaSample(texCoord, -5, -2, fu, fv);
|
||||
res += aaSample(texCoord, 2, 5, fu, fv);
|
||||
res += aaSample(texCoord, 5, 3, fu, fv);
|
||||
res += aaSample(texCoord, 3, -5, fu, fv);
|
||||
res += aaSample(texCoord, -2, 6, fu, fv);
|
||||
res += aaSample(texCoord, 0, -7, fu, fv);
|
||||
res += aaSample(texCoord, -4, -6, fu, fv);
|
||||
res += aaSample(texCoord, -6, 4, fu, fv);
|
||||
res += aaSample(texCoord, -8, 0, fu, fv);
|
||||
res += aaSample(texCoord, 7, -4, fu, fv);
|
||||
res += aaSample(texCoord, 6, 7, fu, fv);
|
||||
res += aaSample(texCoord, -7, -8, fu, fv);
|
||||
|
||||
#endif
|
||||
|
||||
alpha = res / totalWt;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef FONT_BRIGHTNESS
|
||||
alpha = min(alpha * FONT_BRIGHTNESS, 1);
|
||||
#endif
|
||||
|
||||
if (alpha < 0.05) discard;
|
||||
|
||||
fragColor = vec4(color.rgb, alpha * color.a);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec2 quadCorner;
|
||||
layout(location = 1) in vec4 glyphRect;
|
||||
layout(location = 2) in vec4 uvRect; // uMin, vMin, uSize, vSize
|
||||
layout(location = 3) in vec4 aColor;
|
||||
layout(location = 4) in uint aLayer;
|
||||
layout(location = 5) in uint flags;
|
||||
|
||||
|
||||
uniform mat4 u_MVPMatrix;
|
||||
|
||||
flat out vec4 tB;
|
||||
flat out vec4 color;
|
||||
flat out uint layer;
|
||||
|
||||
#ifdef MULTISAMPLING
|
||||
flat out uint secondTexture;
|
||||
#endif
|
||||
|
||||
out vec2 texCoord;
|
||||
|
||||
void main() {
|
||||
vec2 pos = glyphRect.xy + quadCorner * glyphRect.zw;
|
||||
if ((flags & FLAG_ITALIC) != 0u) {
|
||||
pos.x += (1.0 - 2.0 * quadCorner.y);
|
||||
}
|
||||
vec2 uv = uvRect.xy + quadCorner * uvRect.zw;
|
||||
if ((flags & FLAG_DINNERBONE) != 0u) {
|
||||
uv = vec2(uvRect.x + quadCorner.x * uvRect.z, uvRect.y + (1.0 - quadCorner.y) * uvRect.w);
|
||||
}
|
||||
gl_Position = u_MVPMatrix * vec4(pos, 0.0, 1.0);
|
||||
texCoord = uv;
|
||||
color = aColor;
|
||||
tB = vec4(uvRect.xy, uvRect.x + uvRect.z, uvRect.y + uvRect.w);
|
||||
layer = aLayer;
|
||||
|
||||
#ifdef MULTISAMPLING
|
||||
secondTexture = (flags & FLAG_SECOND_TEXTURE);
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
#version 330 core
|
||||
|
||||
|
||||
// ----- GLOW -----
|
||||
#define USE_GLOW
|
||||
//#define RGB_GLOW
|
||||
#define GLOW_COLOR vec3(0.882, 0.686, 1.0)
|
||||
|
||||
// ----- SPARKLES -----
|
||||
#define USE_SPARKLES
|
||||
#define SPARKLE_COLOR vec3(1)
|
||||
|
||||
// ----- WAVE -----
|
||||
//#define USE_WAVE
|
||||
#define WAVE_COLOR vec3(1)
|
||||
|
||||
// ----- TEXT -----
|
||||
#define TEXT_COLOR vec3(0, 0, 0)
|
||||
|
||||
// ----- SPOTLIGHT -----
|
||||
#define USE_SPOTLIGHT
|
||||
#define SPOTLIGHT_COLOR GLOW_COLOR
|
||||
#define SPOTLIGHT_STRENGTH 0.333
|
||||
|
||||
|
||||
|
||||
|
||||
uniform sampler2D textFBO;
|
||||
uniform sampler2D sceneFBO;
|
||||
uniform vec2 uTexelSize;
|
||||
uniform float uTime;
|
||||
uniform int uScale; //TODO integrate this
|
||||
uniform vec4 uTexBounds;
|
||||
|
||||
const int MAX_RADIUS = 16;
|
||||
|
||||
in vec2 texCoord;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
|
||||
|
||||
vec3 mixHexColors(int colorA, int colorB, float t) {
|
||||
t = clamp(t, 0.0, 1.0);
|
||||
|
||||
// Convert packed hex ints to normalized RGB vec3
|
||||
vec3 a = vec3(
|
||||
float((colorA >> 16) & 0xFF),
|
||||
float((colorA >> 8) & 0xFF),
|
||||
float(colorA & 0xFF)
|
||||
) / 255.0;
|
||||
|
||||
vec3 b = vec3(
|
||||
float((colorB >> 16) & 0xFF),
|
||||
float((colorB >> 8) & 0xFF),
|
||||
float(colorB & 0xFF)
|
||||
) / 255.0;
|
||||
|
||||
return mix(a, b, t);
|
||||
}
|
||||
|
||||
float random(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
vec2 random2(vec2 p) {
|
||||
float r = random(p);
|
||||
return vec2(r, random(p + r));
|
||||
}
|
||||
|
||||
|
||||
float glow(vec2 uv) {
|
||||
int uRadius = 6;
|
||||
|
||||
float dist = 1e10;
|
||||
|
||||
for (int y = -MAX_RADIUS; y <= MAX_RADIUS; y++) {
|
||||
for (int x = -MAX_RADIUS; x <= MAX_RADIUS; x++) {
|
||||
if (abs(x) > uRadius || abs(y) > uRadius) continue;
|
||||
|
||||
// Manhatten for better readability
|
||||
//float currDist = abs(x) + abs(y);
|
||||
float currDist = max(abs(x), abs(y)) + min(abs(x), abs(y)) * 0.5;
|
||||
vec2 offset = vec2(x, y) * uTexelSize;
|
||||
|
||||
if (texture(textFBO, vec2(uv.x + offset.x, uv.y + offset.y)).a > 0.1) {
|
||||
if (dist > currDist) {
|
||||
dist = currDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dist == 1e10) return 0;
|
||||
|
||||
dist = dist - 1.5;
|
||||
|
||||
//TODO
|
||||
//float jitter = (fract(sin(uTime + (texCoord.x + texCoord.y) * 2) * 43758.0) - 0.5) * 5;
|
||||
//dist += jitter;
|
||||
//TODO
|
||||
|
||||
//if (dist <= 0) return 1;
|
||||
//else return 0;
|
||||
float timeGlow = (sin(uTime * 2.0)) * 0.2;
|
||||
float d = dist;
|
||||
//d = d / (2 + timeGlow);
|
||||
float glow = exp(-d * 0.7);
|
||||
//float d = (dist / (1 + (sin(uTime * 2.0)) * 0.1));
|
||||
//return min(1.0 / pow(d, 1.2), 1);
|
||||
return min(glow, 1);
|
||||
}
|
||||
|
||||
// Adapted from https://www.shadertoy.com/view/3d33zM
|
||||
float starfield(vec2 uv) {
|
||||
vec2 resolution = 1.0 / uTexelSize;
|
||||
vec2 fragCoord = uv * resolution;
|
||||
|
||||
float widthHeightRatio = resolution.x / resolution.y;
|
||||
|
||||
float t = uTime * 0.015;
|
||||
|
||||
float dist = 0.0;
|
||||
|
||||
const float layers = 4.0;
|
||||
const float scale = 256.0;
|
||||
const float star_size = 1.0 / 10;
|
||||
|
||||
vec2 centre = vec2(0.5);
|
||||
|
||||
for (float i = 0.0; i < layers; i++) {
|
||||
float depth = fract(i/layers);
|
||||
|
||||
centre.x = 0.5 + 0.1 * t * depth;
|
||||
centre.y = 0.5 + 0.1 * t * depth;
|
||||
|
||||
vec2 uv = centre - fragCoord / resolution;
|
||||
|
||||
uv.y /= widthHeightRatio;
|
||||
|
||||
uv *= mix(scale, 0.0, depth);
|
||||
|
||||
vec2 index = floor(uv);
|
||||
|
||||
vec2 seed = 20.0 * i + index;
|
||||
|
||||
vec2 local_uv = fract(i + uv) - 0.5;
|
||||
|
||||
vec2 pos = 0.8 * (random2(seed) - 0.5);
|
||||
|
||||
float phase = 128.0 * random(seed);
|
||||
|
||||
vec2 d = (local_uv - pos) * star_size;
|
||||
float len = length(d);
|
||||
|
||||
// Core
|
||||
float radial = max(0.0, 1.0 - len * 3.5);
|
||||
radial = pow(radial, 30.0);
|
||||
|
||||
// Fade spikes with distance from center
|
||||
float spikeFade = pow(max(0.0, 1.0 - len * 6.0), 4.0);
|
||||
|
||||
// Compact sparkle spikes
|
||||
float spikeX = pow(max(0.0, 1.0 - abs(d.x) * 35.0), 8.0);
|
||||
float spikeY = pow(max(0.0, 1.0 - abs(d.y) * 35.0), 8.0);
|
||||
|
||||
float spikeD1 = pow(max(0.0, 1.0 - abs(d.x + d.y) * 25.0), 8.0);
|
||||
float spikeD2 = pow(max(0.0, 1.0 - abs(d.x - d.y) * 25.0), 8.0);
|
||||
|
||||
// Apply radial fade to spikes
|
||||
float spikes =
|
||||
(spikeX + spikeY) * 0.12 +
|
||||
(spikeD1 + spikeD2) * 0.05;
|
||||
|
||||
spikes *= spikeFade;
|
||||
|
||||
// Final sparkle
|
||||
float sparkle = radial * 0.9 + spikes;
|
||||
|
||||
float speed = mix(0.5, 3.0, random(seed + 17.0));
|
||||
|
||||
float twinkle =
|
||||
sin(uTime * speed + phase) * 0.5 + 0.5;
|
||||
|
||||
twinkle = pow(twinkle, 6.0);
|
||||
|
||||
float brightness =
|
||||
mix(0.2, 1.0, random(seed + 42.0));
|
||||
|
||||
dist += sparkle * twinkle * brightness * depth;
|
||||
}
|
||||
|
||||
return dist * 2;
|
||||
}
|
||||
|
||||
vec3 hsv2rgb(vec3 c) {
|
||||
vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
vec3 rgb(vec2 uv) {
|
||||
float h = (uv.x + uv.y) * 4 + uTime / 10; // 0 → 1 hue
|
||||
return hsv2rgb(vec3(h, 1.0, 1.0));
|
||||
}
|
||||
|
||||
float wave(vec2 uv) {
|
||||
float speed = 0.25;
|
||||
float pos = fract(uTime * speed);
|
||||
|
||||
float lines = 5.0;
|
||||
|
||||
float x = uv.x * lines;
|
||||
|
||||
float id = floor(x);
|
||||
float local = fract(x);
|
||||
|
||||
float dist = abs(local - pos);
|
||||
|
||||
float thickness = 0.1;
|
||||
|
||||
return 1.0 - smoothstep(0.0, thickness, dist);
|
||||
}
|
||||
|
||||
float spotlight(
|
||||
vec2 uv,
|
||||
float minX,
|
||||
float maxX,
|
||||
float minY,
|
||||
float maxY
|
||||
) {
|
||||
vec2 center = vec2(
|
||||
(minX + maxX) * 0.5,
|
||||
(minY + maxY) * 0.5
|
||||
);
|
||||
|
||||
vec2 radius = vec2(
|
||||
(maxX - minX) * 0.5,
|
||||
(maxY - minY) * 0.5
|
||||
);
|
||||
|
||||
vec2 p = abs((uv - center) / radius);
|
||||
|
||||
float dist = pow(
|
||||
pow(p.x, 4.0) + pow(p.y, 4.0),
|
||||
1.0 / 4.0
|
||||
);
|
||||
|
||||
return (1.0 - smoothstep(0.0, 1.0, dist)) * SPOTLIGHT_STRENGTH;
|
||||
}
|
||||
|
||||
//0xfda63a orang
|
||||
|
||||
|
||||
//0xfda63a
|
||||
//0xe1afff
|
||||
|
||||
void main() {
|
||||
float minX = uTexBounds.x;
|
||||
float maxX = uTexBounds.y;
|
||||
float minY = uTexBounds.z;
|
||||
float maxY = uTexBounds.w;
|
||||
vec2 relativeUV = vec2(texCoord.x - minX, texCoord.y - minY);
|
||||
|
||||
vec3 overlay = vec3(0);
|
||||
|
||||
#ifdef USE_SPARKLES
|
||||
if (
|
||||
texCoord.x >= minX - uTexelSize.x * 2 &&
|
||||
texCoord.x <= maxX + uTexelSize.x * 2 &&
|
||||
texCoord.y >= minY - uTexelSize.y * 4 &&
|
||||
texCoord.y <= maxY + uTexelSize.y * 4
|
||||
) {
|
||||
float sparkle = starfield(vec2(texCoord.x - minX, texCoord.y - minY));
|
||||
if (sparkle > 0.05) {
|
||||
// glow sits in front of sparkle
|
||||
overlay = mix(overlay, SPARKLE_COLOR, sparkle);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SPOTLIGHT
|
||||
overlay = mix(overlay, SPOTLIGHT_COLOR, spotlight(texCoord, minX, maxX, minY, maxY));
|
||||
#endif
|
||||
|
||||
#ifdef USE_GLOW
|
||||
float glowVal = glow(texCoord);
|
||||
if (glowVal > 0.1) {
|
||||
#ifdef RGB_GLOW
|
||||
vec3 glowColor = rgb(relativeUV);
|
||||
#else
|
||||
vec3 glowColor = GLOW_COLOR;
|
||||
#endif
|
||||
#ifdef USE_WAVE
|
||||
glowColor = mix(glowColor, vec3(1, 1, 1), wave(relativeUV));
|
||||
#endif
|
||||
overlay = mix(overlay, glowColor, glowVal);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Since Minecraft doesn't have HDR colors, I need an alternative approach to blending the colors together
|
||||
vec4 scene = texture(sceneFBO, texCoord);
|
||||
vec3 base = scene.rgb;
|
||||
|
||||
vec3 finalColor = vec3(1.0) - (vec3(1.0) - base) * (vec3(1.0) - overlay);
|
||||
|
||||
// Mix based on text color
|
||||
vec4 textColor = texture(textFBO, texCoord);
|
||||
if (textColor.a > 0.1) {
|
||||
finalColor = mix(finalColor, TEXT_COLOR, textColor.a);
|
||||
}
|
||||
|
||||
fragColor = vec4(finalColor, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec2 a_Position;
|
||||
|
||||
uniform mat4 u_MVPMatrix;
|
||||
|
||||
out vec2 texCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_MVPMatrix * vec4(a_Position, 0.0, 1.0);
|
||||
texCoord = (gl_Position.xy / gl_Position.w) * 0.5 + 0.5;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#version 330 core
|
||||
|
||||
// Given Uniforms:
|
||||
uniform sampler2D textFBO; // The texture of the text's Framebuffer (use this for the post-processing)
|
||||
uniform sampler2D sceneFBO; // The texture of the scene (for better scene blending)
|
||||
uniform vec2 uTexelSize; // The size of each pixel (1 / displayWidth, 1 / displayHeight)
|
||||
uniform float uTime; // The time, measured in seconds
|
||||
uniform int uScale; // The ScaledResolution scale
|
||||
uniform vec4 uTexBounds; // The bounds of the texture
|
||||
|
||||
// In variables:
|
||||
in vec2 texCoord;
|
||||
|
||||
// Out variables:
|
||||
out vec4 fragColor;
|
||||
|
||||
// Example:
|
||||
void main() {
|
||||
vec4 scene = texture(sceneFBO, texCoord);
|
||||
vec4 textColor = texture(textFBO, texCoord);
|
||||
|
||||
fragColor = vec4(mix(vec3(0, 0, 0), textColor.rgb, textColor.a), 1);
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#version 330 core
|
||||
|
||||
uniform sampler2D sampler;
|
||||
uniform int aaMode;
|
||||
uniform float strength;
|
||||
uniform float alphaTestRef;
|
||||
|
||||
in vec4 color;
|
||||
in vec4 tB;
|
||||
in vec2 texCoord;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
/*
|
||||
Hacky MSAA and anisotropic filtering. Cursed beyond belief. There _may_ have been simpler means of
|
||||
achieving the produced effects, but this appears to work without noticeable performance losses.
|
||||
*/
|
||||
|
||||
float totalWt;
|
||||
|
||||
float txSample(vec2 uv, float du, float dv, float factorU, float factorV) {
|
||||
float distSquared = sqrt(du * du + dv * dv);
|
||||
float weight = exp(-distSquared / 6);
|
||||
totalWt += weight;
|
||||
float finalU = uv.x + factorU * du;
|
||||
float finalV = uv.y + factorV * dv;
|
||||
if (finalU < tB.x || finalU > tB.y || finalV < tB.z || finalV > tB.w) {
|
||||
return 0.0f;
|
||||
}
|
||||
return weight * texture(sampler, vec2(finalU, finalV)).a;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 col = color;
|
||||
float original_alpha = col.a;
|
||||
if (texCoord.s != 0 || texCoord.t != 0) {
|
||||
if (aaMode == 0) {
|
||||
// No AA - single texture sample
|
||||
col.a = original_alpha * texture(sampler, texCoord).a;
|
||||
} else {
|
||||
vec2 texScaled = texCoord * strength;
|
||||
float res = 0;
|
||||
float fu = abs(dFdx(texScaled.x)) + abs(dFdy(texScaled.x));
|
||||
float fv = abs(dFdx(texScaled.y)) + abs(dFdy(texScaled.y));
|
||||
totalWt = 0;
|
||||
if (aaMode == 1) {
|
||||
res += txSample(texCoord, 2, 6, fu, fv);
|
||||
res += txSample(texCoord, 6, -2, fu, fv);
|
||||
res += txSample(texCoord, -2, -6, fu, fv);
|
||||
res += txSample(texCoord, -6, 2, fu, fv);
|
||||
} else {
|
||||
res += txSample(texCoord, 1, 1, fu, fv);
|
||||
res += txSample(texCoord, -1, -3, fu, fv);
|
||||
res += txSample(texCoord, -3, 2, fu, fv);
|
||||
res += txSample(texCoord, 4, -1, fu, fv);
|
||||
res += txSample(texCoord, -5, -2, fu, fv);
|
||||
res += txSample(texCoord, 2, 5, fu, fv);
|
||||
res += txSample(texCoord, 5, 3, fu, fv);
|
||||
res += txSample(texCoord, 3, -5, fu, fv);
|
||||
res += txSample(texCoord, -2, 6, fu, fv);
|
||||
res += txSample(texCoord, 0, -7, fu, fv);
|
||||
res += txSample(texCoord, -4, -6, fu, fv);
|
||||
res += txSample(texCoord, -6, 4, fu, fv);
|
||||
res += txSample(texCoord, -8, 0, fu, fv);
|
||||
res += txSample(texCoord, 7, -4, fu, fv);
|
||||
res += txSample(texCoord, 6, 7, fu, fv);
|
||||
res += txSample(texCoord, -7, -8, fu, fv);
|
||||
}
|
||||
res /= totalWt;
|
||||
col.a = original_alpha * res;
|
||||
}
|
||||
}
|
||||
|
||||
if (col.a <= alphaTestRef) {
|
||||
discard;
|
||||
}
|
||||
|
||||
fragColor = col;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#version 330 core
|
||||
|
||||
layout(location = 0) in vec2 a_Position;
|
||||
layout(location = 1) in vec2 a_TexCoord0;
|
||||
layout(location = 2) in vec4 a_Color;
|
||||
layout(location = 3) in vec4 a_TexBounds;
|
||||
|
||||
uniform mat4 u_MVPMatrix;
|
||||
|
||||
out vec4 tB;
|
||||
out vec4 color;
|
||||
out vec2 texCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_MVPMatrix * vec4(a_Position, 0.0, 1.0);
|
||||
texCoord = a_TexCoord0;
|
||||
color = a_Color;
|
||||
tB = a_TexBounds;
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.client.streaming.PersistentStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.IGameSettingsExt;
|
||||
import com.gtnewhorizons.angelica.proxy.ClientProxy;
|
||||
@@ -9,7 +11,6 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import org.embeddedt.embeddium.impl.render.frame.RenderAheadManager;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
@@ -125,5 +126,6 @@ public abstract class MixinMinecraft {
|
||||
@Inject(method = "runTick", at = @At("RETURN"))
|
||||
private void angelica$streamingBufferEndFrame(CallbackInfo ci) {
|
||||
TessellatorStreamingDrawer.endFrame();
|
||||
PersistentStreamingDrawer.incrementFPS();
|
||||
}
|
||||
}
|
||||
+117
-91
@@ -1,15 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.fontrenderer;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.font.FontRendering;
|
||||
import com.gtnewhorizon.gtnhlib.util.font.IFontParameters;
|
||||
import com.gtnewhorizons.angelica.client.font.BatchingFontRenderer;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.FORMATTING_CHAR;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.GRADIENT_LENGTH;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_LENGTH;
|
||||
|
||||
import com.gtnewhorizons.angelica.client.font.ColorCodeUtils;
|
||||
import com.gtnewhorizons.angelica.compat.GTNHLibCompat;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
@@ -17,23 +13,26 @@ import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.FORMATTING_CHAR;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.GRADIENT_LENGTH;
|
||||
import static com.gtnewhorizons.angelica.client.font.ColorCodeUtils.SECTION_X_LENGTH;
|
||||
|
||||
/**
|
||||
* Fixes the horrible performance of FontRenderer
|
||||
* @author eigenraven
|
||||
*/
|
||||
@Mixin(FontRenderer.class)
|
||||
@Mixin(value = FontRenderer.class, priority = 999)
|
||||
public abstract class MixinFontRenderer implements FontRendererAccessor, IFontParameters {
|
||||
|
||||
@Shadow
|
||||
@@ -115,21 +114,27 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
|
||||
@Shadow(remap = false)
|
||||
protected abstract void bindTexture(ResourceLocation location);
|
||||
|
||||
@Shadow
|
||||
protected abstract void renderStringAtPos(String p_78255_1_, boolean p_78255_2_);
|
||||
|
||||
@Shadow
|
||||
protected abstract void enableAlpha();
|
||||
|
||||
@Shadow
|
||||
protected abstract void resetStyles();
|
||||
|
||||
@Unique
|
||||
public BatchingFontRenderer angelica$batcher;
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void angelica$injectBatcher(GameSettings settings, ResourceLocation fontLocation, TextureManager texManager,
|
||||
boolean unicodeMode, CallbackInfo ci) {
|
||||
angelica$batcher = new BatchingFontRenderer((FontRenderer) (Object) this, this.charWidth, this.colorCode, this.locationFontTexture);
|
||||
if (GTNHLibCompat.HAS_TEXT_PREPROCESSOR) {
|
||||
GTNHLibCompat.registerPreprocessor();
|
||||
}
|
||||
angelica$batcher = new BatchingFontRenderer((FontRenderer) (Object) this, this.colorCode, this.locationFontTexture);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static boolean angelica$charInRange(char what, char fromInclusive, char toInclusive) {
|
||||
return (what >= fromInclusive) && (what <= toInclusive);
|
||||
@Inject(method = "readFontTexture", at = @At("RETURN"))
|
||||
private void angelica$onReadFontTexture(CallbackInfo ci) {
|
||||
angelica$batcher.initializeTextures();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,101 +142,127 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
|
||||
* Batched font renderer is not compatible with display lists, and won't really
|
||||
* help performance when display lists are already being used anyway.
|
||||
*/
|
||||
@Inject(method = "drawString(Ljava/lang/String;IIIZ)I", at = @At("HEAD"), cancellable = true)
|
||||
public void angelica$BatchedFontRendererDrawString(String text, int x, int y, int argb, boolean dropShadow, CallbackInfoReturnable<Integer> cir)
|
||||
{
|
||||
if (GLStateManager.getListMode() == 0) {
|
||||
cir.setReturnValue(angelica$drawStringBatched(text, x, y, argb, dropShadow));
|
||||
|
||||
/**
|
||||
* @author Sisyphus
|
||||
* @reason Optimize Font rendering performance
|
||||
*/
|
||||
@Overwrite
|
||||
public int drawString(String text, int x, int y, int argb, boolean dropShadow) {
|
||||
if (text == null || text.isEmpty()) return 0;
|
||||
text = FontRendering.preprocessText(text);
|
||||
|
||||
if (DisplayListManager.getListMode() == 0) {
|
||||
return angelica$drawStringBatched(text, x, y, argb, dropShadow);
|
||||
}
|
||||
|
||||
this.enableAlpha();
|
||||
this.resetStyles();
|
||||
int width;
|
||||
|
||||
if (dropShadow) {
|
||||
width = this.renderString(text, x + 1, y + 1, argb, true);
|
||||
width = Math.max(width, this.renderString(text, x, y, argb, false));
|
||||
} else {
|
||||
width = this.renderString(text, x, y, argb, false);
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* See above explanation about batched renderer in display lists.
|
||||
* @author Sisyphus
|
||||
* @reason Optimize Font rendering performance
|
||||
*/
|
||||
@Inject(method = "renderString", at = @At("HEAD"), cancellable = true)
|
||||
public void angelica$BatchedFontRendererRenderString(String text, int x, int y, int argb, boolean dropShadow, CallbackInfoReturnable<Integer> cir) {
|
||||
if (GLStateManager.getListMode() == 0) {
|
||||
cir.setReturnValue(angelica$drawStringBatched(text, x, y, argb, dropShadow));
|
||||
@Overwrite
|
||||
private int renderString(String text, int x, int y, int argb, boolean dropShadow) {
|
||||
if (text == null || text.isEmpty()) return 0;
|
||||
text = FontRendering.preprocessText(text);
|
||||
|
||||
if (DisplayListManager.getListMode() == 0) {
|
||||
return angelica$drawStringBatched(text, x, y, argb, dropShadow);
|
||||
}
|
||||
|
||||
|
||||
if (this.bidiFlag) {
|
||||
text = this.bidiReorder(text);
|
||||
}
|
||||
|
||||
if ((argb & 0xfc000000) == 0) {
|
||||
argb |= 0xff000000;
|
||||
}
|
||||
|
||||
this.red = (float) (argb >> 16 & 255) / 255.0F;
|
||||
this.blue = (float) (argb >> 8 & 255) / 255.0F;
|
||||
this.green = (float) (argb & 255) / 255.0F;
|
||||
this.alpha = (float) (argb >> 24 & 255) / 255.0F;
|
||||
this.posX = (float) x;
|
||||
this.posY = (float) y;
|
||||
this.renderStringAtPos(text, dropShadow);
|
||||
return (int) this.posX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int angelica$drawStringBatched(String text, int x, int y, int argb, boolean dropShadow) {
|
||||
if (text == null)
|
||||
{
|
||||
return 0;
|
||||
public final int angelica$drawStringBatched(String text, int x, int y, int argb, boolean dropShadow) {
|
||||
if (this.bidiFlag) {
|
||||
text = this.bidiReorder(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = ColorCodeUtils.convertAmpersandToSectionX(text);
|
||||
|
||||
if (this.bidiFlag)
|
||||
{
|
||||
text = this.bidiReorder(text);
|
||||
}
|
||||
|
||||
if ((argb & 0xfc000000) == 0)
|
||||
{
|
||||
argb |= 0xff000000;
|
||||
}
|
||||
|
||||
this.red = (float)(argb >> 16 & 255) / 255.0F;
|
||||
this.blue = (float)(argb >> 8 & 255) / 255.0F;
|
||||
this.green = (float)(argb & 255) / 255.0F;
|
||||
this.alpha = (float)(argb >> 24 & 255) / 255.0F;
|
||||
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
this.posX = (float)x;
|
||||
this.posY = (float)y;
|
||||
return (int) angelica$batcher.drawString(x, y, argb, dropShadow, unicodeFlag, text, 0, text.length());
|
||||
if ((argb & 0xfc000000) == 0) {
|
||||
argb |= 0xff000000;
|
||||
}
|
||||
|
||||
this.red = (float) (argb >> 16 & 255) / 255.0F;
|
||||
this.blue = (float) (argb >> 8 & 255) / 255.0F;
|
||||
this.green = (float) (argb & 255) / 255.0F;
|
||||
this.alpha = (float) (argb >> 24 & 255) / 255.0F;
|
||||
this.posX = (float) x;
|
||||
this.posY = (float) y;
|
||||
return angelica$batcher.drawString(text, x, y, argb, dropShadow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Sisyphus
|
||||
* @reason Optimize Font rendering performance
|
||||
*/
|
||||
@Overwrite
|
||||
public int getCharWidth(char c) {
|
||||
return (int) angelica$batcher.getCharWidthFine(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatchingFontRenderer angelica$getBatcher() {
|
||||
public final byte[] angelica$getGlyphWidths() {
|
||||
return glyphWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int[] angelica$getCharWidths() {
|
||||
return this.charWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void angelica$bindTexture(ResourceLocation location) {
|
||||
this.bindTexture(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final BatchingFontRenderer angelica$getBatcher() {
|
||||
return angelica$batcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void angelica$bindTexture(ResourceLocation location) { this.bindTexture(location); }
|
||||
|
||||
@ModifyConstant(method = "getCharWidth", constant = @Constant(intValue = 7))
|
||||
private int angelica$maxCharWidth(int original) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Inject(method = "getCharWidth", at = @At("HEAD"), cancellable = true)
|
||||
public void getCharWidth(char c, CallbackInfoReturnable<Integer> cir) {
|
||||
cir.setReturnValue((int) angelica$getBatcher().getCharWidthFine(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getGlyphScaleX() {
|
||||
return angelica$getBatcher().getGlyphScaleX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getGlyphScaleY() {
|
||||
return angelica$getBatcher().getGlyphScaleY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getGlyphSpacing() {
|
||||
return angelica$getBatcher().getGlyphSpacing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getWhitespaceScale() {
|
||||
return angelica$getBatcher().getWhitespaceScale();
|
||||
return angelica$batcher.getWhitespaceScale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShadowOffset() {
|
||||
return angelica$getBatcher().getShadowOffset();
|
||||
return angelica$batcher.getShadowOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCharWidthFine(char chr) {
|
||||
return angelica$getBatcher().getCharWidthFine(chr);
|
||||
return angelica$batcher.getCharWidthFine(chr);
|
||||
}
|
||||
|
||||
@Inject(method = "getFormatFromString", at = @At("HEAD"), cancellable = true)
|
||||
@@ -247,7 +278,7 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
|
||||
|
||||
@Unique
|
||||
private static String angelica$extractFormat(String text) {
|
||||
text = ColorCodeUtils.convertAmpersandToSectionX(text);
|
||||
text = FontRendering.preprocessText(text);
|
||||
final int len = text.length();
|
||||
if (len < 2) return "";
|
||||
|
||||
@@ -317,18 +348,13 @@ public abstract class MixinFontRenderer implements FontRendererAccessor, IFontPa
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "getStringWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
||||
private String angelica$convertBeforeWidth(String str) {
|
||||
return ColorCodeUtils.convertAmpersandToSectionX(str);
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "listFormattedStringToWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
||||
private String angelica$convertBeforeListWrap(String str) {
|
||||
return ColorCodeUtils.convertAmpersandToSectionX(str);
|
||||
return FontRendering.preprocessText(str);
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "wrapFormattedStringToWidth", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
||||
private String angelica$convertBeforeWrap(String str) {
|
||||
return ColorCodeUtils.convertAmpersandToSectionX(str);
|
||||
return FontRendering.preprocessText(str);
|
||||
}
|
||||
}
|
||||
+99
-8
@@ -1,35 +1,126 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.fontrenderer;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraftforge.client.GuiIngameForge;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
|
||||
@Mixin(GuiIngameForge.class)
|
||||
public class MixinGuiIngameForge {
|
||||
|
||||
@Shadow(remap = false)
|
||||
private FontRenderer fontrenderer;
|
||||
|
||||
@Inject(
|
||||
@Unique private ByteBuffer angelica$data = memAlloc(2048);
|
||||
@Unique private int angelica$ptrOffset = 0;
|
||||
@Unique private long angelica$basePtr = memAddress0(angelica$data);
|
||||
|
||||
@Redirect(
|
||||
method = "renderHUDText",
|
||||
remap = false,
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraftforge/client/event/RenderGameOverlayEvent$Text.<init>(Lnet/minecraftforge/client/event/RenderGameOverlayEvent;Ljava/util/ArrayList;Ljava/util/ArrayList;)V"),
|
||||
remap = false)
|
||||
private void angelica$startF3TextBatching(int width, int height, CallbackInfo ci) {
|
||||
FontRendererAccessor fra = (FontRendererAccessor) (Object) fontrenderer;
|
||||
fra.angelica$getBatcher().beginBatch();
|
||||
target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;III)I",
|
||||
ordinal = 0,
|
||||
remap = true))
|
||||
private int renderBackgroundLeft(FontRenderer fontRenderer, String text, int x, int y, int color) {
|
||||
angelica$writeQuad(
|
||||
x - 1, y - 1,
|
||||
x + fontRenderer.getStringWidth(text) + 1,
|
||||
y + fontRenderer.FONT_HEIGHT
|
||||
);
|
||||
return fontRenderer.drawString(text, x, y, 0xe0e0e0);
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "renderHUDText",
|
||||
remap = false,
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;III)I",
|
||||
ordinal = 1,
|
||||
remap = true))
|
||||
private int renderBackgroundRight(FontRenderer fontRenderer, String text, int x, int y, int color) {
|
||||
x += 8;
|
||||
angelica$writeQuad(
|
||||
x - 1, y - 1,
|
||||
x + fontRenderer.getStringWidth(text) + 1, y + fontRenderer.FONT_HEIGHT
|
||||
);
|
||||
return fontRenderer.drawString(text, x, y, 0xe0e0e0);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void angelica$writeQuad(int left, int bottom, int right, int top) {
|
||||
if ((angelica$ptrOffset + 48) > angelica$data.capacity()) {
|
||||
angelica$data = memRealloc(angelica$data, angelica$data.capacity() * 2);
|
||||
angelica$basePtr = memAddress0(angelica$data);
|
||||
}
|
||||
final long ptr = angelica$basePtr + angelica$ptrOffset;
|
||||
memPutFloat(ptr, left);
|
||||
memPutFloat(ptr + 4, top);
|
||||
memPutFloat(ptr + 8, 0);
|
||||
|
||||
memPutFloat(ptr + 12, right);
|
||||
memPutFloat(ptr + 16, top);
|
||||
memPutFloat(ptr + 20, 0);
|
||||
|
||||
memPutFloat(ptr + 24, right);
|
||||
memPutFloat(ptr + 28, bottom);
|
||||
memPutFloat(ptr + 32, 0);
|
||||
|
||||
memPutFloat(ptr + 36, left);
|
||||
memPutFloat(ptr + 40, bottom);
|
||||
memPutFloat(ptr + 44, 0);
|
||||
|
||||
angelica$ptrOffset += 48;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "renderHUDText",
|
||||
at = @At(value = "INVOKE", target = "net/minecraft/profiler/Profiler.endSection()V"))
|
||||
private void angelica$endF3TextBatching(int width, int height, CallbackInfo ci) {
|
||||
at = @At("HEAD"),
|
||||
remap = false)
|
||||
private void angelica$startF3Batching(int width, int height, CallbackInfo ci) {
|
||||
FontRendererAccessor fra = (FontRendererAccessor) (Object) fontrenderer;
|
||||
fra.angelica$getBatcher().beginBatch();
|
||||
|
||||
angelica$ptrOffset = 0;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "renderHUDText",
|
||||
at = @At("RETURN"), remap = false)
|
||||
private void angelica$endF3Batching(int width, int height, CallbackInfo ci) {
|
||||
angelica$data.limit(angelica$ptrOffset);
|
||||
|
||||
GLStateManager.glColor4f(0.3137f, 0.3137f, 0.3137f, 0.5647f); // 0x90505050
|
||||
GLStateManager.glEnable(GL11.GL_BLEND);
|
||||
GLStateManager.glDisable(GL11.GL_TEXTURE_2D);
|
||||
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
|
||||
TessellatorStreamingDrawer.drawPacked(
|
||||
angelica$data,
|
||||
GL11.GL_QUADS,
|
||||
VertexFlags.POSITION_BIT,
|
||||
angelica$ptrOffset / 12
|
||||
);
|
||||
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
|
||||
// Font rendering has to go AFTER batched rects
|
||||
FontRendererAccessor fra = (FontRendererAccessor) (Object) fontrenderer;
|
||||
fra.angelica$getBatcher().endBatch();
|
||||
}
|
||||
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica.gui;
|
||||
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraftforge.client.GuiIngameForge;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(GuiIngameForge.class)
|
||||
public class MixinGuiIngameForge_ModernF3 {
|
||||
|
||||
@Redirect(
|
||||
method = "renderHUDText",
|
||||
remap = false,
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;III)I",
|
||||
ordinal = 0,
|
||||
remap = true))
|
||||
private int renderBackgroundLeft(FontRenderer fontRenderer, String text, int x, int y, int color) {
|
||||
Gui.drawRect(x - 1, y - 1, x + fontRenderer.getStringWidth(text) + 1, y + fontRenderer.FONT_HEIGHT, 0x90505050);
|
||||
return fontRenderer.drawString(text, x, y, 0xe0e0e0);
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "renderHUDText",
|
||||
remap = false,
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/gui/FontRenderer;drawStringWithShadow(Ljava/lang/String;III)I",
|
||||
ordinal = 1,
|
||||
remap = true))
|
||||
private int renderBackgroundRight(FontRenderer fontRenderer, String text, int x, int y, int color) {
|
||||
x += 8;
|
||||
Gui.drawRect(x - 1, y - 1, x + fontRenderer.getStringWidth(text) + 1, y + fontRenderer.FONT_HEIGHT, 0x90505050);
|
||||
return fontRenderer.drawString(text, x, y, 0xe0e0e0);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user