mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 07:26:48 +01:00
Turn it up to 11
- Consolidate context shaders into the common shaders, guarded by ifdefs - Make ContextShaders into an enum - Cache shaders by name instead of SourceComponents so the defines for contexts actually get compiled - Move all embedding stuff into backend.embed - Cannibalize GPULightVolume into an api better suited for environments - Separate light storage from 3d texture management - Add #delete to Environment - Add light volume debug mode
This commit is contained in:
parent
5d142d2f13
commit
b90b80c8b0
35 changed files with 509 additions and 373 deletions
|
@ -5,6 +5,8 @@ import org.joml.Matrix4fc;
|
|||
|
||||
import com.jozufozu.flywheel.api.BackendImplemented;
|
||||
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
|
||||
@BackendImplemented
|
||||
public interface VisualEmbedding extends VisualizationContext {
|
||||
/**
|
||||
|
@ -14,4 +16,36 @@ public interface VisualEmbedding extends VisualizationContext {
|
|||
* @param normal The normal matrix.
|
||||
*/
|
||||
void transforms(Matrix4fc pose, Matrix3fc normal);
|
||||
|
||||
/**
|
||||
* Collect light information from the given level for the given box.
|
||||
*
|
||||
* <p>Call this method on as many or as few boxes as you need to
|
||||
* encompass all child visuals of this embedding.</p>
|
||||
*
|
||||
* <p>After this method is called, instances rendered from this
|
||||
* embedding within the given box will be lit as if they were in
|
||||
* the given level.</p>
|
||||
*
|
||||
* @param level The level to collect light information from.
|
||||
* @param minX The minimum x coordinate of the box.
|
||||
* @param minY The minimum y coordinate of the box.
|
||||
* @param minZ The minimum z coordinate of the box.
|
||||
* @param sizeX The size of the box in the x direction.
|
||||
* @param sizeY The size of the box in the y direction.
|
||||
* @param sizeZ The size of the box in the z direction.
|
||||
*/
|
||||
void light(BlockAndTintGetter level, int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ);
|
||||
|
||||
/**
|
||||
* Reset any collected lighting information for the given box.
|
||||
*
|
||||
* @param minX The minimum x coordinate of the box.
|
||||
* @param minY The minimum y coordinate of the box.
|
||||
* @param minZ The minimum z coordinate of the box.
|
||||
* @param sizeX The size of the box in the x direction.
|
||||
* @param sizeY The size of the box in the y direction.
|
||||
* @param sizeZ The size of the box in the z direction.
|
||||
*/
|
||||
void invalidateLight(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ);
|
||||
}
|
||||
|
|
|
@ -8,4 +8,5 @@ public class Samplers {
|
|||
public static final GlTextureUnit LIGHT = GlTextureUnit.T2;
|
||||
public static final GlTextureUnit CRUMBLING = GlTextureUnit.T3;
|
||||
public static final GlTextureUnit INSTANCE_BUFFER = GlTextureUnit.T4;
|
||||
public static final GlTextureUnit EMBEDDED_LIGHT = GlTextureUnit.T5;
|
||||
}
|
||||
|
|
|
@ -1,48 +1,40 @@
|
|||
package com.jozufozu.flywheel.backend.compile;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Samplers;
|
||||
import com.jozufozu.flywheel.backend.compile.core.Compilation;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
public enum ContextShader {
|
||||
DEFAULT(null, $ -> {
|
||||
}),
|
||||
CRUMBLING("_FLW_CRUMBLING", program -> program.setSamplerBinding("_flw_crumblingTex", Samplers.CRUMBLING)),
|
||||
EMBEDDED("_FLW_EMBEDDED", program -> program.setSamplerBinding("_flw_lightVolume", Samplers.EMBEDDED_LIGHT));
|
||||
|
||||
public record ContextShader(ResourceLocation vertexShader, ResourceLocation fragmentShader,
|
||||
Consumer<GlProgram> onLink) {
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
@Nullable
|
||||
private final String define;
|
||||
private final Consumer<GlProgram> onLink;
|
||||
|
||||
ContextShader(@Nullable String define, Consumer<GlProgram> onLink) {
|
||||
this.define = define;
|
||||
this.onLink = onLink;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
@Nullable
|
||||
private ResourceLocation vertexShader;
|
||||
@Nullable
|
||||
private ResourceLocation fragmentShader;
|
||||
@Nullable
|
||||
private Consumer<GlProgram> onLink;
|
||||
public void onLink(GlProgram program) {
|
||||
onLink.accept(program);
|
||||
}
|
||||
|
||||
public Builder vertexShader(ResourceLocation shader) {
|
||||
this.vertexShader = shader;
|
||||
return this;
|
||||
public void onCompile(Compilation comp) {
|
||||
if (define != null) {
|
||||
comp.define(define);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder fragmentShader(ResourceLocation shader) {
|
||||
this.fragmentShader = shader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder onLink(Consumer<GlProgram> onLink) {
|
||||
this.onLink = onLink;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContextShader build() {
|
||||
Objects.requireNonNull(vertexShader);
|
||||
Objects.requireNonNull(fragmentShader);
|
||||
Objects.requireNonNull(onLink);
|
||||
return new ContextShader(vertexShader, fragmentShader, onLink);
|
||||
}
|
||||
public String nameLowerCase() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.compile;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.api.internal.InternalFlywheelApi;
|
||||
import com.jozufozu.flywheel.api.registry.Registry;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualEmbedding;
|
||||
import com.jozufozu.flywheel.backend.Samplers;
|
||||
|
||||
public class ContextShaders {
|
||||
public static final Registry<ContextShader> REGISTRY = InternalFlywheelApi.INSTANCE.createRegistry();
|
||||
public static final ContextShader DEFAULT = REGISTRY.registerAndGet(ContextShader.builder()
|
||||
.vertexShader(Flywheel.rl("internal/context/default.vert"))
|
||||
.fragmentShader(Flywheel.rl("internal/context/default.frag"))
|
||||
.onLink($ -> {
|
||||
})
|
||||
.build());
|
||||
public static final ContextShader CRUMBLING = REGISTRY.registerAndGet(ContextShader.builder()
|
||||
.vertexShader(Flywheel.rl("internal/context/crumbling.vert"))
|
||||
.fragmentShader(Flywheel.rl("internal/context/crumbling.frag"))
|
||||
.onLink(program -> program.setSamplerBinding("_flw_crumblingTex", Samplers.CRUMBLING))
|
||||
.build());
|
||||
public static final ContextShader EMBEDDED = REGISTRY.registerAndGet(ContextShader.builder()
|
||||
.vertexShader(Flywheel.rl("internal/context/embedded.vert"))
|
||||
.fragmentShader(Flywheel.rl("internal/context/embedded.frag"))
|
||||
.onLink($ -> {
|
||||
})
|
||||
.build());
|
||||
|
||||
public static ContextShader forEmbedding(@Nullable VisualEmbedding level) {
|
||||
if (level == null) {
|
||||
return DEFAULT;
|
||||
} else {
|
||||
return EMBEDDED;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ public final class FlwPrograms {
|
|||
|
||||
private static ImmutableList<PipelineProgramKey> createPipelineKeys() {
|
||||
ImmutableList.Builder<PipelineProgramKey> builder = ImmutableList.builder();
|
||||
for (ContextShader contextShader : ContextShaders.REGISTRY) {
|
||||
for (ContextShader contextShader : ContextShader.values()) {
|
||||
for (InstanceType<?> instanceType : InstanceType.REGISTRY) {
|
||||
builder.add(new PipelineProgramKey(instanceType, contextShader));
|
||||
}
|
||||
|
|
|
@ -22,10 +22,12 @@ public class PipelineCompiler {
|
|||
var instance = ResourceUtil.toDebugFileNameNoExtension(key.instanceType()
|
||||
.vertexShader());
|
||||
|
||||
var context = ResourceUtil.toDebugFileNameNoExtension(key.contextShader()
|
||||
.vertexShader());
|
||||
var context = key.contextShader()
|
||||
.nameLowerCase();
|
||||
return "pipeline/" + pipeline.compilerMarker() + "/" + instance + "_" + context;
|
||||
})
|
||||
.onCompile((key, comp) -> key.contextShader()
|
||||
.onCompile(comp))
|
||||
.withResource(pipeline.vertexApiImpl())
|
||||
.withResource(InternalVertex.LAYOUT_SHADER)
|
||||
.withComponent(key -> pipeline.assembler()
|
||||
|
@ -33,23 +35,18 @@ public class PipelineCompiler {
|
|||
.withComponents(vertexComponents)
|
||||
.withResource(key -> key.instanceType()
|
||||
.vertexShader())
|
||||
.withResource(key -> key.contextShader()
|
||||
.vertexShader())
|
||||
.withResource(pipeline.vertexMain()))
|
||||
.link(PIPELINE.shader(pipeline.glslVersion(), ShaderType.FRAGMENT)
|
||||
.nameMapper(key -> {
|
||||
var instance = ResourceUtil.toDebugFileNameNoExtension(key.instanceType()
|
||||
.vertexShader());
|
||||
|
||||
var context = ResourceUtil.toDebugFileNameNoExtension(key.contextShader()
|
||||
.fragmentShader());
|
||||
return "pipeline/" + pipeline.compilerMarker() + "/" + instance + "_" + context;
|
||||
var context = key.contextShader()
|
||||
.nameLowerCase();
|
||||
return "pipeline/" + pipeline.compilerMarker() + "/" + context;
|
||||
})
|
||||
.enableExtension("GL_ARB_conservative_depth")
|
||||
.onCompile((key, comp) -> key.contextShader()
|
||||
.onCompile(comp))
|
||||
.withResource(pipeline.fragmentApiImpl())
|
||||
.withComponents(fragmentComponents)
|
||||
.withResource(key -> key.contextShader()
|
||||
.fragmentShader())
|
||||
.withResource(pipeline.fragmentMain()))
|
||||
.preLink((key, program) -> {
|
||||
program.bindAttribLocation("_flw_a_pos", 0);
|
||||
|
@ -71,8 +68,7 @@ public class PipelineCompiler {
|
|||
pipeline.onLink()
|
||||
.accept(program);
|
||||
key.contextShader()
|
||||
.onLink()
|
||||
.accept(program);
|
||||
.onLink(program);
|
||||
|
||||
GlProgram.unbind();
|
||||
})
|
||||
|
|
|
@ -106,7 +106,7 @@ public class Compile<K> {
|
|||
private final GlslVersion glslVersion;
|
||||
private final ShaderType shaderType;
|
||||
private final List<BiFunction<K, SourceLoader, @Nullable SourceComponent>> fetchers = new ArrayList<>();
|
||||
private Consumer<Compilation> compilationCallbacks = $ -> {
|
||||
private BiConsumer<K, Compilation> compilationCallbacks = ($, $$) -> {
|
||||
};
|
||||
private Function<K, String> nameMapper = Object::toString;
|
||||
|
||||
|
@ -146,17 +146,17 @@ public class Compile<K> {
|
|||
return withResource($ -> resourceLocation);
|
||||
}
|
||||
|
||||
public ShaderCompiler<K> onCompile(Consumer<Compilation> cb) {
|
||||
public ShaderCompiler<K> onCompile(BiConsumer<K, Compilation> cb) {
|
||||
compilationCallbacks = compilationCallbacks.andThen(cb);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShaderCompiler<K> define(String def, int value) {
|
||||
return onCompile(ctx -> ctx.define(def, String.valueOf(value)));
|
||||
return onCompile(($, ctx) -> ctx.define(def, String.valueOf(value)));
|
||||
}
|
||||
|
||||
public ShaderCompiler<K> enableExtension(String extension) {
|
||||
return onCompile(ctx -> ctx.enableExtension(extension));
|
||||
return onCompile(($, ctx) -> ctx.enableExtension(extension));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -175,7 +175,8 @@ public class Compile<K> {
|
|||
return null;
|
||||
}
|
||||
|
||||
return compiler.compile(glslVersion, shaderType, nameMapper.apply(key), compilationCallbacks, components);
|
||||
Consumer<Compilation> cb = ctx -> compilationCallbacks.accept(key, ctx);
|
||||
return compiler.compile(glslVersion, shaderType, nameMapper.apply(key), cb, components);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public class ShaderCache {
|
|||
|
||||
@Nullable
|
||||
public GlShader compile(GlslVersion glslVersion, ShaderType shaderType, String name, Consumer<Compilation> callback, List<SourceComponent> sourceComponents) {
|
||||
var key = new ShaderKey(glslVersion, shaderType, sourceComponents);
|
||||
var key = new ShaderKey(glslVersion, shaderType, name);
|
||||
var cached = inner.get(key);
|
||||
if (cached != null) {
|
||||
return cached.unwrap();
|
||||
|
@ -69,6 +69,6 @@ public class ShaderCache {
|
|||
included.addAll(component.included());
|
||||
}
|
||||
|
||||
private record ShaderKey(GlslVersion glslVersion, ShaderType shaderType, List<SourceComponent> sourceComponents) {
|
||||
private record ShaderKey(GlslVersion glslVersion, ShaderType shaderType, String name) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import com.jozufozu.flywheel.api.instance.InstancerProvider;
|
|||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualEmbedding;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.EmbeddedEnvironment;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.Environment;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.Environment;
|
||||
import com.jozufozu.flywheel.lib.util.AtomicBitset;
|
||||
|
||||
public abstract class AbstractInstancer<I extends Instance> implements Instancer<I> {
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.jozufozu.flywheel.api.event.RenderStage;
|
|||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.Environment;
|
||||
|
||||
public record InstancerKey<I extends Instance>(Environment environment, InstanceType<I> type, Model model,
|
||||
RenderStage stage) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.jozufozu.flywheel.api.instance.InstanceType;
|
|||
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||
import com.jozufozu.flywheel.api.instance.InstancerProvider;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.GlobalEnvironment;
|
||||
|
||||
public record InstancerProviderImpl(AbstractEngine engine, RenderStage renderStage) implements InstancerProvider {
|
||||
@Override
|
||||
|
|
|
@ -11,6 +11,10 @@ import com.jozufozu.flywheel.api.instance.Instance;
|
|||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.Environment;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceSet;
|
||||
|
||||
public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
|
||||
/**
|
||||
|
@ -19,6 +23,7 @@ public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
|
|||
* This map is populated as instancers are requested and contains both initialized and uninitialized instancers.
|
||||
*/
|
||||
protected final Map<InstancerKey<?>, N> instancers = new ConcurrentHashMap<>();
|
||||
protected final ReferenceSet<Environment> environments = new ReferenceLinkedOpenHashSet<>();
|
||||
/**
|
||||
* A list of instancers that have not yet been initialized.
|
||||
* <br>
|
||||
|
@ -32,6 +37,12 @@ public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
|
|||
}
|
||||
|
||||
public void delete() {
|
||||
// FIXME: The ownership of environments is a bit weird. Their resources are created and destroyed by the engine,
|
||||
// but the engine doesn't own the things themselves. This makes it hard for the engine to know when to delete
|
||||
// environments. For now, we just delete all environments when the engine is deleted, but this is not ideal.
|
||||
environments.forEach(Environment::delete);
|
||||
environments.clear();
|
||||
|
||||
instancers.clear();
|
||||
initializationQueue.clear();
|
||||
}
|
||||
|
@ -57,6 +68,8 @@ public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
|
|||
private N createAndDeferInit(InstancerKey<?> key) {
|
||||
var out = create(key);
|
||||
|
||||
environments.add(key.environment());
|
||||
|
||||
// Only queue the instancer for initialization if it has anything to render.
|
||||
if (checkAndWarnEmptyModel(key.model())) {
|
||||
// Thread safety: this method is called atomically from within computeIfAbsent,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.backend.engine;
|
||||
package com.jozufozu.flywheel.backend.engine.embed;
|
||||
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix3fc;
|
||||
|
@ -12,16 +12,21 @@ import com.jozufozu.flywheel.api.instance.Instancer;
|
|||
import com.jozufozu.flywheel.api.instance.InstancerProvider;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualEmbedding;
|
||||
import com.jozufozu.flywheel.backend.Samplers;
|
||||
import com.jozufozu.flywheel.backend.compile.ContextShader;
|
||||
import com.jozufozu.flywheel.backend.compile.ContextShaders;
|
||||
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
|
||||
public class EmbeddedEnvironment implements Environment, VisualEmbedding {
|
||||
private final Matrix4f pose = new Matrix4f();
|
||||
private final Matrix3f normal = new Matrix3f();
|
||||
|
||||
private final EmbeddedLightVolume lightVolume = new EmbeddedLightVolume();
|
||||
private final EmbeddedLightTexture lightTexture = new EmbeddedLightTexture();
|
||||
|
||||
private final InstancerProvider instancerProvider;
|
||||
private final AbstractEngine engine;
|
||||
private final RenderStage renderStage;
|
||||
|
@ -45,15 +50,39 @@ public class EmbeddedEnvironment implements Environment, VisualEmbedding {
|
|||
this.normal.set(normal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void light(BlockAndTintGetter level, int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
|
||||
lightVolume.collect(level, minX, minY, minZ, sizeX, sizeY, sizeZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateLight(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
|
||||
lightVolume.invalidate(minX, minY, minZ, sizeX, sizeY, sizeZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextShader contextShader() {
|
||||
return ContextShaders.EMBEDDED;
|
||||
return ContextShader.EMBEDDED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupDraw(GlProgram drawProgram) {
|
||||
drawProgram.setVec3("_flw_oneOverLightBoxSize", 1, 1, 1);
|
||||
drawProgram.setVec3("_flw_lightVolumeMin", 0, 0, 0);
|
||||
if (!lightVolume.empty()) {
|
||||
Samplers.EMBEDDED_LIGHT.makeActive();
|
||||
|
||||
lightTexture.bind();
|
||||
|
||||
lightTexture.ensureCapacity(lightVolume.sizeX, lightVolume.sizeY, lightVolume.sizeZ);
|
||||
|
||||
lightTexture.upload(lightVolume.ptr(), lightVolume.sizeX, lightVolume.sizeY, lightVolume.sizeZ);
|
||||
|
||||
float oneOverSizeX = 1f / (float) lightTexture.sizeX;
|
||||
float oneOverSizeY = 1f / (float) lightTexture.sizeY;
|
||||
float oneOverSizeZ = 1f / (float) lightTexture.sizeZ;
|
||||
|
||||
drawProgram.setVec3("_flw_oneOverLightBoxSize", oneOverSizeX, oneOverSizeY, oneOverSizeZ);
|
||||
drawProgram.setVec3("_flw_lightVolumeMin", lightVolume.minX, lightVolume.minY, lightVolume.minZ);
|
||||
}
|
||||
drawProgram.setMat4("_flw_model", pose);
|
||||
drawProgram.setMat3("_flw_normal", normal);
|
||||
}
|
||||
|
@ -78,4 +107,10 @@ public class EmbeddedEnvironment implements Environment, VisualEmbedding {
|
|||
public VisualEmbedding createEmbedding() {
|
||||
return new EmbeddedEnvironment(engine, renderStage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
lightVolume.delete();
|
||||
lightTexture.delete();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package com.jozufozu.flywheel.backend.engine.embed;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_LINEAR;
|
||||
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER;
|
||||
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER;
|
||||
import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_S;
|
||||
import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_T;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNPACK_ALIGNMENT;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNPACK_ROW_LENGTH;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNPACK_SKIP_PIXELS;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNPACK_SKIP_ROWS;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;
|
||||
import static org.lwjgl.opengl.GL11.glPixelStorei;
|
||||
import static org.lwjgl.opengl.GL11.glTexParameteri;
|
||||
import static org.lwjgl.opengl.GL12.GL_TEXTURE_3D;
|
||||
import static org.lwjgl.opengl.GL12.GL_TEXTURE_WRAP_R;
|
||||
import static org.lwjgl.opengl.GL12.GL_UNPACK_IMAGE_HEIGHT;
|
||||
import static org.lwjgl.opengl.GL12.GL_UNPACK_SKIP_IMAGES;
|
||||
import static org.lwjgl.opengl.GL12.glTexImage3D;
|
||||
import static org.lwjgl.opengl.GL12.glTexSubImage3D;
|
||||
import static org.lwjgl.opengl.GL14.GL_MIRRORED_REPEAT;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.GlTexture;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
public class EmbeddedLightTexture {
|
||||
@Nullable
|
||||
private GlTexture texture;
|
||||
|
||||
public int sizeX;
|
||||
public int sizeY;
|
||||
public int sizeZ;
|
||||
|
||||
public void bind() {
|
||||
|
||||
texture().bind();
|
||||
}
|
||||
|
||||
private GlTexture texture() {
|
||||
if (texture == null) {
|
||||
texture = new GlTexture(GL_TEXTURE_3D);
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
public void ensureCapacity(int sizeX, int sizeY, int sizeZ) {
|
||||
sizeX = Mth.smallestEncompassingPowerOfTwo(sizeX);
|
||||
sizeY = Mth.smallestEncompassingPowerOfTwo(sizeY);
|
||||
sizeZ = Mth.smallestEncompassingPowerOfTwo(sizeZ);
|
||||
|
||||
if (sizeX > this.sizeX || sizeY > this.sizeY || sizeZ > this.sizeZ) {
|
||||
this.sizeX = sizeX;
|
||||
this.sizeY = sizeY;
|
||||
this.sizeZ = sizeZ;
|
||||
|
||||
glTexImage3D(GL_TEXTURE_3D, 0, GL30.GL_RG8, sizeX, sizeY, sizeZ, 0, GL30.GL_RG, GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
|
||||
}
|
||||
}
|
||||
|
||||
public void upload(long ptr, int sizeX, int sizeY, int sizeZ) {
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
|
||||
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, (int) EmbeddedLightVolume.STRIDE);
|
||||
|
||||
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, GL30.GL_RG, GL_UNSIGNED_BYTE, ptr);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4 is the default
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
if (texture != null) {
|
||||
texture.delete();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
package com.jozufozu.flywheel.backend.engine.embed;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
|
||||
public class EmbeddedLightVolume {
|
||||
public static final long STRIDE = Short.BYTES;
|
||||
public int minX;
|
||||
public int minY;
|
||||
public int minZ;
|
||||
public int sizeX;
|
||||
public int sizeY;
|
||||
public int sizeZ;
|
||||
private final BlockPos.MutableBlockPos scratchPos = new BlockPos.MutableBlockPos();
|
||||
|
||||
@Nullable
|
||||
protected MemoryBlock block;
|
||||
protected boolean dirty;
|
||||
|
||||
public boolean empty() {
|
||||
return block == null;
|
||||
}
|
||||
|
||||
public void collect(BlockAndTintGetter level, int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
|
||||
maybeExpandForBox(minX, minY, minZ, sizeX, sizeY, sizeZ);
|
||||
|
||||
for (int z = minZ; z < minZ + sizeZ; z++) {
|
||||
for (int y = minY; y < minY + sizeY; y++) {
|
||||
for (int x = minX; x < minX + sizeX; x++) {
|
||||
paintLight(level, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
markDirty();
|
||||
}
|
||||
|
||||
public void invalidate(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
|
||||
// TODO: shrink the volume
|
||||
}
|
||||
|
||||
private void paintLight(BlockAndTintGetter level, int x, int y, int z) {
|
||||
scratchPos.set(x, y, z);
|
||||
|
||||
int block = level.getBrightness(LightLayer.BLOCK, scratchPos);
|
||||
int sky = level.getBrightness(LightLayer.SKY, scratchPos);
|
||||
|
||||
long ptr = worldPosToPtr(x, y, z);
|
||||
MemoryUtil.memPutShort(ptr, (short) ((block << 4) | sky << 12));
|
||||
}
|
||||
|
||||
private void maybeExpandForBox(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
|
||||
if (block == null) {
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
this.minZ = minZ;
|
||||
this.sizeX = sizeX;
|
||||
this.sizeY = sizeY;
|
||||
this.sizeZ = sizeZ;
|
||||
|
||||
int volume = sizeX * sizeY * sizeZ;
|
||||
|
||||
block = MemoryBlock.malloc(volume * STRIDE);
|
||||
block.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
int newMinX = Math.min(this.minX, minX);
|
||||
int newMinY = Math.min(this.minY, minY);
|
||||
int newMinZ = Math.min(this.minZ, minZ);
|
||||
|
||||
int newSizeX = Math.max(this.minX + this.sizeX, minX + sizeX) - newMinX;
|
||||
int newSizeY = Math.max(this.minY + this.sizeY, minY + sizeY) - newMinY;
|
||||
int newSizeZ = Math.max(this.minZ + this.sizeZ, minZ + sizeZ) - newMinZ;
|
||||
|
||||
if (newMinX == this.minX && newMinY == this.minY && newMinZ == this.minZ && newSizeX == this.sizeX && newSizeY == this.sizeY && newSizeZ == this.sizeZ) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newVolume = newSizeX * newSizeY * newSizeZ;
|
||||
|
||||
MemoryBlock newBlock = MemoryBlock.malloc(newVolume * STRIDE);
|
||||
newBlock.clear();
|
||||
|
||||
int xOff = newMinX - this.minX;
|
||||
int yOff = newMinY - this.minY;
|
||||
int zOff = newMinZ - this.minZ;
|
||||
|
||||
for (int z = 0; z < this.sizeZ; z++) {
|
||||
for (int y = 0; y < this.sizeY; y++) {
|
||||
for (int x = 0; x < this.sizeX; x++) {
|
||||
long oldPtr = boxPosToPtr(x, y, z);
|
||||
long newPtr = newBlock.ptr() + x + xOff + (newSizeX * (y + yOff + (z + zOff) * newSizeY)) * STRIDE;
|
||||
|
||||
MemoryUtil.memPutShort(newPtr, MemoryUtil.memGetShort(oldPtr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.minX = newMinX;
|
||||
this.minY = newMinY;
|
||||
this.minZ = newMinZ;
|
||||
this.sizeX = newSizeX;
|
||||
this.sizeY = newSizeY;
|
||||
this.sizeZ = newSizeZ;
|
||||
|
||||
block.free();
|
||||
block = newBlock;
|
||||
}
|
||||
|
||||
protected long worldPosToPtr(int x, int y, int z) {
|
||||
return block.ptr() + worldPosToPtrOffset(x, y, z);
|
||||
}
|
||||
|
||||
protected long boxPosToPtr(int x, int y, int z) {
|
||||
return block.ptr() + boxPosToPtrOffset(x, y, z);
|
||||
}
|
||||
|
||||
protected long worldPosToPtrOffset(int x, int y, int z) {
|
||||
return boxPosToPtrOffset(x - minX, y - minY, z - minZ);
|
||||
}
|
||||
|
||||
protected long boxPosToPtrOffset(int x, int y, int z) {
|
||||
return (x + sizeX * (y + z * sizeY)) * STRIDE;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
if (block != null) {
|
||||
block.free();
|
||||
block = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void markDirty() {
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
public long ptr() {
|
||||
return block.ptr();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.backend.engine;
|
||||
package com.jozufozu.flywheel.backend.engine.embed;
|
||||
|
||||
import com.jozufozu.flywheel.backend.compile.ContextShader;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||
|
@ -9,4 +9,6 @@ public interface Environment {
|
|||
void setupDraw(GlProgram drawProgram);
|
||||
|
||||
void setupCull(GlProgram cullProgram);
|
||||
|
||||
void delete();
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package com.jozufozu.flywheel.backend.engine;
|
||||
package com.jozufozu.flywheel.backend.engine.embed;
|
||||
|
||||
import com.jozufozu.flywheel.backend.compile.ContextShader;
|
||||
import com.jozufozu.flywheel.backend.compile.ContextShaders;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||
|
||||
public class GlobalEnvironment implements Environment {
|
||||
|
@ -12,7 +11,7 @@ public class GlobalEnvironment implements Environment {
|
|||
|
||||
@Override
|
||||
public ContextShader contextShader() {
|
||||
return ContextShaders.DEFAULT;
|
||||
return ContextShader.DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -24,4 +23,9 @@ public class GlobalEnvironment implements Environment {
|
|||
public void setupCull(GlProgram cullProgram) {
|
||||
cullProgram.setBool("_flw_useEmbeddedModel", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
|
||||
}
|
||||
}
|
|
@ -23,9 +23,9 @@ import com.jozufozu.flywheel.api.material.Material;
|
|||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.backend.compile.ContextShader;
|
||||
import com.jozufozu.flywheel.backend.compile.IndirectPrograms;
|
||||
import com.jozufozu.flywheel.backend.engine.Environment;
|
||||
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
|
||||
import com.jozufozu.flywheel.backend.engine.MeshPool;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.Environment;
|
||||
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
||||
import com.jozufozu.flywheel.backend.gl.Driver;
|
||||
import com.jozufozu.flywheel.backend.gl.GlCompat;
|
||||
|
|
|
@ -16,16 +16,16 @@ import com.jozufozu.flywheel.api.event.RenderStage;
|
|||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||
import com.jozufozu.flywheel.backend.Samplers;
|
||||
import com.jozufozu.flywheel.backend.compile.ContextShaders;
|
||||
import com.jozufozu.flywheel.backend.compile.ContextShader;
|
||||
import com.jozufozu.flywheel.backend.compile.IndirectPrograms;
|
||||
import com.jozufozu.flywheel.backend.engine.CommonCrumbling;
|
||||
import com.jozufozu.flywheel.backend.engine.Environment;
|
||||
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
||||
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
|
||||
import com.jozufozu.flywheel.backend.engine.MeshPool;
|
||||
import com.jozufozu.flywheel.backend.engine.TextureBinder;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.Environment;
|
||||
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
|
||||
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
|
||||
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
|
||||
|
@ -164,7 +164,7 @@ public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>>
|
|||
|
||||
// Set up the crumbling program buffers. Nothing changes here between draws.
|
||||
var program = cullingGroups.get(instanceTypeEntry.getKey())
|
||||
.bindWithContextShader(ContextShaders.CRUMBLING);
|
||||
.bindWithContextShader(ContextShader.CRUMBLING);
|
||||
|
||||
program.setSamplerBinding("crumblingTex", Samplers.CRUMBLING);
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import com.jozufozu.flywheel.api.instance.InstanceType;
|
|||
import com.jozufozu.flywheel.api.instance.InstanceWriter;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
||||
import com.jozufozu.flywheel.backend.engine.Environment;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.Environment;
|
||||
|
||||
public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I> {
|
||||
private final long objectStride;
|
||||
|
|
|
@ -20,7 +20,7 @@ import com.jozufozu.flywheel.api.instance.Instance;
|
|||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.backend.Samplers;
|
||||
import com.jozufozu.flywheel.backend.ShaderIndices;
|
||||
import com.jozufozu.flywheel.backend.compile.ContextShaders;
|
||||
import com.jozufozu.flywheel.backend.compile.ContextShader;
|
||||
import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
|
||||
import com.jozufozu.flywheel.backend.engine.CommonCrumbling;
|
||||
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
||||
|
@ -203,7 +203,7 @@ public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>
|
|||
|
||||
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, shader.material());
|
||||
|
||||
var program = programs.get(shader.instanceType(), ContextShaders.CRUMBLING);
|
||||
var program = programs.get(shader.instanceType(), ContextShader.CRUMBLING);
|
||||
program.bind();
|
||||
|
||||
uploadMaterialUniform(program, crumblingMaterial);
|
||||
|
|
|
@ -9,7 +9,7 @@ import com.jozufozu.flywheel.api.instance.Instance;
|
|||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||
import com.jozufozu.flywheel.api.instance.InstanceWriter;
|
||||
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
||||
import com.jozufozu.flywheel.backend.engine.Environment;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.Environment;
|
||||
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage;
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.jozufozu.flywheel.backend.engine.instancing;
|
|||
|
||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.backend.engine.Environment;
|
||||
import com.jozufozu.flywheel.backend.engine.embed.Environment;
|
||||
|
||||
public record ShaderState(Material material, InstanceType<?> instanceType, Environment environment) {
|
||||
}
|
||||
|
|
|
@ -23,7 +23,4 @@ public class GlTexture extends GlObject {
|
|||
GL20.glBindTexture(textureType, 0);
|
||||
}
|
||||
|
||||
public void setParameteri(int parameter, int value) {
|
||||
GL20.glTexParameteri(textureType, parameter, value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,4 +6,5 @@ public enum DebugMode {
|
|||
INSTANCE_ID,
|
||||
LIGHT,
|
||||
OVERLAY,
|
||||
LIGHT_VOLUME,
|
||||
}
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
package com.jozufozu.flywheel.lib.light;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_LINEAR;
|
||||
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER;
|
||||
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER;
|
||||
import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_S;
|
||||
import static org.lwjgl.opengl.GL11.GL_TEXTURE_WRAP_T;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNPACK_ALIGNMENT;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNPACK_ROW_LENGTH;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNPACK_SKIP_PIXELS;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNPACK_SKIP_ROWS;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;
|
||||
import static org.lwjgl.opengl.GL11.glPixelStorei;
|
||||
import static org.lwjgl.opengl.GL12.GL_TEXTURE_3D;
|
||||
import static org.lwjgl.opengl.GL12.GL_TEXTURE_WRAP_R;
|
||||
import static org.lwjgl.opengl.GL12.GL_UNPACK_IMAGE_HEIGHT;
|
||||
import static org.lwjgl.opengl.GL12.GL_UNPACK_SKIP_IMAGES;
|
||||
import static org.lwjgl.opengl.GL12.glTexImage3D;
|
||||
import static org.lwjgl.opengl.GL12.glTexSubImage3D;
|
||||
import static org.lwjgl.opengl.GL14.GL_MIRRORED_REPEAT;
|
||||
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.GlTexture;
|
||||
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
|
||||
import com.jozufozu.flywheel.lib.box.Box;
|
||||
import com.jozufozu.flywheel.lib.box.MutableBox;
|
||||
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
|
||||
public class GPULightVolume extends LightVolume {
|
||||
|
||||
protected final MutableBox sampleVolume = new MutableBox();
|
||||
private final GlTexture glTexture;
|
||||
|
||||
private final GlTextureUnit textureUnit = GlTextureUnit.T4;
|
||||
protected boolean bufferDirty;
|
||||
|
||||
public GPULightVolume(BlockAndTintGetter level, Box sampleVolume) {
|
||||
super(level, sampleVolume);
|
||||
this.sampleVolume.assign(sampleVolume);
|
||||
|
||||
glTexture = new GlTexture(GL_TEXTURE_3D);
|
||||
|
||||
GlTextureUnit oldState = GlTextureUnit.getActive();
|
||||
|
||||
// allocate space for the texture
|
||||
textureUnit.makeActive();
|
||||
glTexture.bind();
|
||||
|
||||
int sizeX = box.sizeX();
|
||||
int sizeY = box.sizeY();
|
||||
int sizeZ = box.sizeZ();
|
||||
glTexImage3D(GL_TEXTURE_3D, 0, GL30.GL_RG8, sizeX, sizeY, sizeZ, 0, GL30.GL_RG, GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
glTexture.setParameteri(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexture.setParameteri(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexture.setParameteri(GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
|
||||
glTexture.setParameteri(GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
|
||||
glTexture.setParameteri(GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
|
||||
|
||||
glTexture.unbind();
|
||||
oldState.makeActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setBox(Box box) {
|
||||
this.box.assign(box);
|
||||
this.box.nextPowerOf2Centered();
|
||||
// called during super ctor
|
||||
if (sampleVolume != null) this.sampleVolume.assign(box);
|
||||
}
|
||||
|
||||
public void bind() {
|
||||
// just in case something goes wrong, or we accidentally call this before this volume is properly disposed of.
|
||||
if (lightData == null || lightData.size() == 0) return;
|
||||
|
||||
textureUnit.makeActive();
|
||||
glTexture.bind();
|
||||
|
||||
uploadTexture();
|
||||
}
|
||||
|
||||
private void uploadTexture() {
|
||||
if (bufferDirty) {
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
|
||||
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 2); // we use 2 bytes per texel
|
||||
int sizeX = box.sizeX();
|
||||
int sizeY = box.sizeY();
|
||||
int sizeZ = box.sizeZ();
|
||||
|
||||
glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, sizeX, sizeY, sizeZ, GL30.GL_RG, GL_UNSIGNED_BYTE, lightData.ptr());
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4 is the default
|
||||
bufferDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void unbind() {
|
||||
glTexture.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
super.delete();
|
||||
glTexture.delete();
|
||||
}
|
||||
|
||||
public void move(Box newSampleVolume) {
|
||||
if (lightData == null) return;
|
||||
|
||||
if (box.contains(newSampleVolume)) {
|
||||
sampleVolume.assign(newSampleVolume);
|
||||
initialize();
|
||||
} else {
|
||||
super.move(newSampleVolume);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Box getVolume() {
|
||||
return sampleVolume;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void markDirty() {
|
||||
this.bufferDirty = true;
|
||||
}
|
||||
}
|
|
@ -6,19 +6,42 @@
|
|||
layout (depth_greater) out float gl_FragDepth;
|
||||
#endif
|
||||
|
||||
out vec4 _flw_outputColor;
|
||||
#ifdef _FLW_CRUMBLING
|
||||
uniform sampler2D _flw_crumblingTex;
|
||||
|
||||
in vec2 _flw_crumblingTexCoord;
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_EMBEDDED
|
||||
uniform sampler3D _flw_lightVolume;
|
||||
|
||||
in vec3 _flw_lightVolumeCoord;
|
||||
|
||||
#endif
|
||||
|
||||
in vec4 _flw_debugColor;
|
||||
|
||||
out vec4 _flw_outputColor;
|
||||
|
||||
void _flw_main() {
|
||||
flw_sampleColor = texture(flw_diffuseTex, flw_vertexTexCoord);
|
||||
flw_fragColor = flw_vertexColor * flw_sampleColor;
|
||||
flw_fragOverlay = flw_vertexOverlay;
|
||||
flw_fragLight = flw_vertexLight;
|
||||
|
||||
flw_beginFragment();
|
||||
#ifdef _FLW_EMBEDDED
|
||||
flw_fragLight = max(flw_fragLight, texture(_flw_lightVolume, _flw_lightVolumeCoord).rg);
|
||||
#endif
|
||||
|
||||
flw_materialFragment();
|
||||
flw_endFragment();
|
||||
|
||||
#ifdef _FLW_CRUMBLING
|
||||
vec4 crumblingSampleColor = texture(_flw_crumblingTex, _flw_crumblingTexCoord);
|
||||
|
||||
// Make the crumbling overlay transparent when the fragment color after the material shader is transparent.
|
||||
flw_fragColor.rgb = crumblingSampleColor.rgb;
|
||||
flw_fragColor.a *= crumblingSampleColor.a;
|
||||
#endif
|
||||
|
||||
vec4 color = flw_fragColor;
|
||||
|
||||
|
|
|
@ -23,12 +23,97 @@ vec4 _flw_id2Color(in uint id) {
|
|||
|
||||
out vec4 _flw_debugColor;
|
||||
|
||||
#ifdef _FLW_CRUMBLING
|
||||
out vec2 _flw_crumblingTexCoord;
|
||||
|
||||
const int DOWN = 0;
|
||||
const int UP = 1;
|
||||
const int NORTH = 2;
|
||||
const int SOUTH = 3;
|
||||
const int WEST = 4;
|
||||
const int EAST = 5;
|
||||
|
||||
// based on net.minecraftforge.client.ForgeHooksClient.getNearestStable
|
||||
int getNearestFacing(vec3 normal) {
|
||||
float maxAlignment = -2;
|
||||
int face = 2;
|
||||
|
||||
// Calculate the alignment of the normal vector with each axis.
|
||||
// Note that `-dot(normal, axis) == dot(normal, -axis)`.
|
||||
vec3 alignment = vec3(
|
||||
dot(normal, vec3(1., 0., 0.)),
|
||||
dot(normal, vec3(0., 1., 0.)),
|
||||
dot(normal, vec3(0., 0., 1.))
|
||||
);
|
||||
|
||||
if (-alignment.y > maxAlignment) {
|
||||
maxAlignment = -alignment.y;
|
||||
face = DOWN;
|
||||
}
|
||||
if (alignment.y > maxAlignment) {
|
||||
maxAlignment = alignment.y;
|
||||
face = UP;
|
||||
}
|
||||
if (-alignment.z > maxAlignment) {
|
||||
maxAlignment = -alignment.z;
|
||||
face = NORTH;
|
||||
}
|
||||
if (alignment.z > maxAlignment) {
|
||||
maxAlignment = alignment.z;
|
||||
face = SOUTH;
|
||||
}
|
||||
if (-alignment.x > maxAlignment) {
|
||||
maxAlignment = -alignment.x;
|
||||
face = WEST;
|
||||
}
|
||||
if (alignment.x > maxAlignment) {
|
||||
maxAlignment = alignment.x;
|
||||
face = EAST;
|
||||
}
|
||||
|
||||
return face;
|
||||
}
|
||||
|
||||
vec2 getCrumblingTexCoord() {
|
||||
switch (getNearestFacing(flw_vertexNormal)) {
|
||||
case DOWN: return vec2(flw_vertexPos.x, -flw_vertexPos.z);
|
||||
case UP: return vec2(flw_vertexPos.x, flw_vertexPos.z);
|
||||
case NORTH: return vec2(-flw_vertexPos.x, -flw_vertexPos.y);
|
||||
case SOUTH: return vec2(flw_vertexPos.x, -flw_vertexPos.y);
|
||||
case WEST: return vec2(-flw_vertexPos.z, -flw_vertexPos.y);
|
||||
case EAST: return vec2(flw_vertexPos.z, -flw_vertexPos.y);
|
||||
}
|
||||
|
||||
// default to north
|
||||
return vec2(-flw_vertexPos.x, -flw_vertexPos.y);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_EMBEDDED
|
||||
uniform vec3 _flw_oneOverLightBoxSize;
|
||||
uniform vec3 _flw_lightVolumeMin;
|
||||
uniform mat4 _flw_model;
|
||||
uniform mat3 _flw_normal;
|
||||
|
||||
out vec3 _flw_lightVolumeCoord;
|
||||
#endif
|
||||
|
||||
|
||||
void _flw_main(in FlwInstance instance, in uint stableInstanceID) {
|
||||
_flw_layoutVertex();
|
||||
flw_beginVertex();
|
||||
flw_instanceVertex(instance);
|
||||
flw_materialVertex();
|
||||
flw_endVertex();
|
||||
|
||||
#ifdef _FLW_CRUMBLING
|
||||
_flw_crumblingTexCoord = getCrumblingTexCoord();
|
||||
#endif
|
||||
|
||||
#ifdef _FLW_EMBEDDED
|
||||
flw_vertexPos = _flw_model * flw_vertexPos;
|
||||
flw_vertexNormal = _flw_normal * flw_vertexNormal;
|
||||
|
||||
_flw_lightVolumeCoord = (flw_vertexPos.xyz - _flw_lightVolumeMin) * _flw_oneOverLightBoxSize;
|
||||
#endif
|
||||
|
||||
flw_vertexNormal = normalize(flw_vertexNormal);
|
||||
|
||||
|
@ -52,5 +137,10 @@ void _flw_main(in FlwInstance instance, in uint stableInstanceID) {
|
|||
case 4u:
|
||||
_flw_debugColor = vec4(flw_vertexOverlay / 16., 0., 1.);
|
||||
break;
|
||||
#ifdef _FLW_LIGHT_VOLUME
|
||||
case 5u:
|
||||
_flw_debugColor = vec4(_flw_lightVolumeCoord, 1.);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
uniform sampler2D _flw_crumblingTex;
|
||||
|
||||
in vec2 crumblingTexCoord;
|
||||
|
||||
vec4 crumblingSampleColor;
|
||||
|
||||
void flw_beginFragment() {
|
||||
}
|
||||
|
||||
void flw_endFragment() {
|
||||
crumblingSampleColor = texture(_flw_crumblingTex, crumblingTexCoord);
|
||||
|
||||
// Make the crumbling overlay transparent when the fragment color after the material shader is transparent.
|
||||
flw_fragColor.rgb = crumblingSampleColor.rgb;
|
||||
flw_fragColor.a *= crumblingSampleColor.a;
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
out vec2 crumblingTexCoord;
|
||||
|
||||
const int DOWN = 0;
|
||||
const int UP = 1;
|
||||
const int NORTH = 2;
|
||||
const int SOUTH = 3;
|
||||
const int WEST = 4;
|
||||
const int EAST = 5;
|
||||
|
||||
// based on net.minecraftforge.client.ForgeHooksClient.getNearestStable
|
||||
int getNearestFacing(vec3 normal) {
|
||||
float maxAlignment = -2;
|
||||
int face = 2;
|
||||
|
||||
// Calculate the alignment of the normal vector with each axis.
|
||||
// Note that `-dot(normal, axis) == dot(normal, -axis)`.
|
||||
vec3 alignment = vec3(
|
||||
dot(normal, vec3(1., 0., 0.)),
|
||||
dot(normal, vec3(0., 1., 0.)),
|
||||
dot(normal, vec3(0., 0., 1.))
|
||||
);
|
||||
|
||||
if (-alignment.y > maxAlignment) {
|
||||
maxAlignment = -alignment.y;
|
||||
face = DOWN;
|
||||
}
|
||||
if (alignment.y > maxAlignment) {
|
||||
maxAlignment = alignment.y;
|
||||
face = UP;
|
||||
}
|
||||
if (-alignment.z > maxAlignment) {
|
||||
maxAlignment = -alignment.z;
|
||||
face = NORTH;
|
||||
}
|
||||
if (alignment.z > maxAlignment) {
|
||||
maxAlignment = alignment.z;
|
||||
face = SOUTH;
|
||||
}
|
||||
if (-alignment.x > maxAlignment) {
|
||||
maxAlignment = -alignment.x;
|
||||
face = WEST;
|
||||
}
|
||||
if (alignment.x > maxAlignment) {
|
||||
maxAlignment = alignment.x;
|
||||
face = EAST;
|
||||
}
|
||||
|
||||
return face;
|
||||
}
|
||||
|
||||
vec2 getCrumblingTexCoord() {
|
||||
switch (getNearestFacing(flw_vertexNormal)) {
|
||||
case DOWN: return vec2(flw_vertexPos.x, -flw_vertexPos.z);
|
||||
case UP: return vec2(flw_vertexPos.x, flw_vertexPos.z);
|
||||
case NORTH: return vec2(-flw_vertexPos.x, -flw_vertexPos.y);
|
||||
case SOUTH: return vec2(flw_vertexPos.x, -flw_vertexPos.y);
|
||||
case WEST: return vec2(-flw_vertexPos.z, -flw_vertexPos.y);
|
||||
case EAST: return vec2(flw_vertexPos.z, -flw_vertexPos.y);
|
||||
}
|
||||
|
||||
// default to north
|
||||
return vec2(-flw_vertexPos.x, -flw_vertexPos.y);
|
||||
}
|
||||
|
||||
void flw_beginVertex() {
|
||||
}
|
||||
|
||||
void flw_endVertex() {
|
||||
crumblingTexCoord = getCrumblingTexCoord();
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
void flw_beginFragment() {
|
||||
}
|
||||
|
||||
void flw_endFragment() {
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
void flw_beginVertex() {
|
||||
}
|
||||
|
||||
void flw_endVertex() {
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
uniform sampler3D _flw_lightVolume;
|
||||
|
||||
in vec3 _flw_lightVolumeCoord;
|
||||
|
||||
void flw_beginFragment() {
|
||||
flw_fragLight = max(flw_fragLight, texture(_flw_lightVolume, _flw_lightVolumeCoord).rg);
|
||||
}
|
||||
|
||||
void flw_endFragment() {
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
uniform vec3 _flw_oneOverLightBoxSize;
|
||||
uniform vec3 _flw_lightVolumeMin;
|
||||
uniform mat4 _flw_model;
|
||||
uniform mat3 _flw_normal;
|
||||
|
||||
out vec3 _flw_lightVolumeCoord;
|
||||
|
||||
void flw_beginVertex() {
|
||||
}
|
||||
|
||||
void flw_endVertex() {
|
||||
_flw_lightVolumeCoord = (flw_vertexPos.xyz - _flw_lightVolumeMin) * _flw_oneOverLightBoxSize;
|
||||
|
||||
flw_vertexPos = _flw_model * flw_vertexPos;
|
||||
flw_vertexNormal = _flw_normal * flw_vertexNormal;
|
||||
}
|
Loading…
Reference in a new issue