mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-14 16:26:07 +01:00
Make the BatchingEngine not jittery
- Stop having threads compete for a single BufferBuilder - ...by skirting around minecraft's BufferSource - Begin work on making vertex writing sane
This commit is contained in:
parent
831f990153
commit
b0f6d07b0b
32 changed files with 948 additions and 397 deletions
|
@ -11,6 +11,7 @@ import org.apache.logging.log4j.Logger;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.backend.instancing.batching.WaitGroup;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
|
@ -104,7 +105,7 @@ public class BatchExecutor implements TaskEngine {
|
|||
Runnable job;
|
||||
|
||||
// Finish everyone else's work...
|
||||
while ((job = getNextTask(false)) != null) {
|
||||
while ((job = this.jobQueue.pollLast()) != null) {
|
||||
processTask(job);
|
||||
}
|
||||
|
||||
|
@ -116,10 +117,10 @@ public class BatchExecutor implements TaskEngine {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private Runnable getNextTask(boolean block) {
|
||||
Runnable job = this.jobQueue.poll();
|
||||
private Runnable getNextTask() {
|
||||
Runnable job = this.jobQueue.pollFirst();
|
||||
|
||||
if (job == null && block) {
|
||||
if (job == null) {
|
||||
synchronized (BatchExecutor.this.jobNotifier) {
|
||||
try {
|
||||
BatchExecutor.this.jobNotifier.wait();
|
||||
|
@ -134,6 +135,8 @@ public class BatchExecutor implements TaskEngine {
|
|||
private void processTask(Runnable job) {
|
||||
try {
|
||||
job.run();
|
||||
} catch (Exception e) {
|
||||
Flywheel.log.error(e);
|
||||
} finally {
|
||||
BatchExecutor.this.wg.done();
|
||||
}
|
||||
|
@ -158,13 +161,13 @@ public class BatchExecutor implements TaskEngine {
|
|||
public void run() {
|
||||
// Run until the chunk builder shuts down
|
||||
while (this.running.get()) {
|
||||
Runnable job = BatchExecutor.this.getNextTask(true);
|
||||
Runnable job = BatchExecutor.this.getNextTask();
|
||||
|
||||
if (job == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
processTask(job);
|
||||
BatchExecutor.this.processTask(job);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ public class InstanceWorld {
|
|||
*/
|
||||
public void renderLayer(RenderLayerEvent event) {
|
||||
taskEngine.syncPoint();
|
||||
engine.render(event, event.buffers.bufferSource());
|
||||
engine.render(event);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,9 +10,8 @@ public interface RenderDispatcher {
|
|||
* Render every model for every material.
|
||||
*
|
||||
* @param event Context for rendering.
|
||||
* @param buffers The buffer source for which batched rendering should happen.
|
||||
*/
|
||||
void render(RenderLayerEvent event, MultiBufferSource buffers);
|
||||
void render(RenderLayerEvent event);
|
||||
|
||||
/**
|
||||
* Maintain the integer origin coordinate to be within a certain distance from the camera in all directions.
|
||||
|
|
|
@ -28,9 +28,9 @@ public class BatchedMaterial<D extends InstanceData> implements Material<D> {
|
|||
return models.computeIfAbsent(key, $ -> new CPUInstancer<>(type, modelSupplier.get()));
|
||||
}
|
||||
|
||||
public void render(PoseStack stack, VertexConsumer buffer, FormatContext context) {
|
||||
public void setupAndRenderInto(PoseStack stack, VertexConsumer buffer) {
|
||||
for (CPUInstancer<D> instancer : models.values()) {
|
||||
instancer.setup(context);
|
||||
instancer.setup();
|
||||
instancer.drawAll(stack, buffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,41 +42,44 @@ public class BatchedMaterialGroup implements MaterialGroup {
|
|||
if (buffer instanceof DirectBufferBuilder direct) {
|
||||
renderParallel(stack, pool, direct);
|
||||
} else {
|
||||
renderSerial(stack, buffer, FormatContext.defaultContext());
|
||||
renderSerial(stack, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderParallel(PoseStack stack, TaskEngine pool, DirectBufferBuilder direct) {
|
||||
int vertexCount = calculateNeededVertices();
|
||||
int vertexCount = 0;
|
||||
for (BatchedMaterial<?> material : materials.values()) {
|
||||
for (CPUInstancer<?> instancer : material.models.values()) {
|
||||
instancer.setup();
|
||||
vertexCount += instancer.getTotalVertexCount();
|
||||
}
|
||||
}
|
||||
|
||||
DirectVertexConsumer consumer = direct.intoDirectConsumer(vertexCount);
|
||||
FormatContext context = new FormatContext(consumer.hasOverlay());
|
||||
|
||||
// avoids rendering garbage, but doesn't fix the issue of some instances not being buffered
|
||||
consumer.memSetZero();
|
||||
|
||||
for (BatchedMaterial<?> material : materials.values()) {
|
||||
for (CPUInstancer<?> instancer : material.models.values()) {
|
||||
instancer.setup(context);
|
||||
|
||||
if (consumer.hasOverlay()) {
|
||||
instancer.sbb.context.fullNormalTransform = false;
|
||||
instancer.sbb.context.outputColorDiffuse = false;
|
||||
} else {
|
||||
instancer.sbb.context.fullNormalTransform = false;
|
||||
instancer.sbb.context.outputColorDiffuse = true;
|
||||
}
|
||||
instancer.submitTasks(stack, pool, consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderSerial(PoseStack stack, VertexConsumer consumer, FormatContext context) {
|
||||
private void renderSerial(PoseStack stack, VertexConsumer consumer) {
|
||||
for (BatchedMaterial<?> value : materials.values()) {
|
||||
value.render(stack, consumer, context);
|
||||
value.setupAndRenderInto(stack, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
private int calculateNeededVertices() {
|
||||
int total = 0;
|
||||
for (BatchedMaterial<?> material : materials.values()) {
|
||||
for (CPUInstancer<?> instancer : material.models.values()) {
|
||||
total += instancer.getTotalVertexCount();
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
materials.values().forEach(BatchedMaterial::clear);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@ import com.jozufozu.flywheel.backend.RenderLayer;
|
|||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||
import com.jozufozu.flywheel.event.RenderLayerEvent;
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
|
@ -17,8 +19,9 @@ import net.minecraft.client.renderer.RenderType;
|
|||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
|
||||
public class BatchingEngine implements Engine {
|
||||
public class BatchingEngine implements Engine, MultiBufferSource {
|
||||
|
||||
protected final Map<RenderType, BufferBuilder> buffers = new HashMap<>();
|
||||
protected final Map<RenderLayer, Map<RenderType, BatchedMaterialGroup>> layers;
|
||||
protected final TaskEngine taskEngine;
|
||||
|
||||
|
@ -42,7 +45,7 @@ public class BatchingEngine implements Engine {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void render(RenderLayerEvent event, MultiBufferSource buffers) {
|
||||
public void render(RenderLayerEvent event) {
|
||||
PoseStack stack = event.stack;
|
||||
|
||||
stack.pushPose();
|
||||
|
@ -50,14 +53,27 @@ public class BatchingEngine implements Engine {
|
|||
stack.translate(-event.camX, -event.camY, -event.camZ);
|
||||
|
||||
for (BatchedMaterialGroup group : layers.get(event.getLayer()).values()) {
|
||||
group.render(stack, buffers, taskEngine);
|
||||
group.render(stack, this, taskEngine);
|
||||
}
|
||||
|
||||
taskEngine.syncPoint();
|
||||
|
||||
stack.popPose();
|
||||
|
||||
event.buffers.bufferSource().endBatch();
|
||||
// TODO: when/if this causes trouble with shaders, try to inject our BufferBuilders
|
||||
// into the RenderBuffers from context.
|
||||
buffers.forEach((type, builder) -> type.end(builder, 0, 0, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer getBuffer(RenderType renderType) {
|
||||
BufferBuilder builder = buffers.computeIfAbsent(renderType, type -> new BufferBuilder(type.bufferSize()));
|
||||
|
||||
if (!builder.building()) {
|
||||
builder.begin(renderType.mode(), renderType.format());
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,12 +2,12 @@ package com.jozufozu.flywheel.backend.instancing.batching;
|
|||
|
||||
import com.jozufozu.flywheel.api.InstanceData;
|
||||
import com.jozufozu.flywheel.api.struct.Batched;
|
||||
import com.jozufozu.flywheel.api.struct.StructType;
|
||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
|
||||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
||||
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
|
||||
import com.jozufozu.flywheel.core.model.Model;
|
||||
import com.jozufozu.flywheel.core.model.ModelTransformer;
|
||||
import com.jozufozu.flywheel.util.Color;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
|
@ -15,15 +15,14 @@ public class CPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
|
|||
|
||||
private final Batched<D> batchingType;
|
||||
|
||||
private final ModelTransformer sbb;
|
||||
private final ModelTransformer.Params defaultParams;
|
||||
final ModelTransformer sbb;
|
||||
|
||||
public CPUInstancer(Batched<D> type, Model modelData) {
|
||||
super(type::create, modelData);
|
||||
batchingType = type;
|
||||
|
||||
sbb = new ModelTransformer(modelData);
|
||||
defaultParams = ModelTransformer.Params.defaultParams();
|
||||
modelData.configure(sbb.context);
|
||||
}
|
||||
|
||||
void submitTasks(PoseStack stack, TaskEngine pool, DirectVertexConsumer consumer) {
|
||||
|
@ -42,43 +41,47 @@ public class CPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDirty() {
|
||||
// noop
|
||||
}
|
||||
|
||||
private void drawRange(PoseStack stack, VertexConsumer buffer, int from, int to) {
|
||||
ModelTransformer.Params params = defaultParams.copy();
|
||||
ModelTransformer.Params params = new ModelTransformer.Params();
|
||||
|
||||
// Color color = Color.generateFromLong(from);
|
||||
|
||||
for (D d : data.subList(from, to)) {
|
||||
params.loadDefault();
|
||||
|
||||
batchingType.transform(d, params);
|
||||
|
||||
sbb.renderInto(params, stack, buffer);
|
||||
//params.color(color.getRGB());
|
||||
|
||||
params.load(defaultParams);
|
||||
sbb.renderInto(params, stack, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void drawAll(PoseStack stack, VertexConsumer buffer) {
|
||||
ModelTransformer.Params params = defaultParams.copy();
|
||||
ModelTransformer.Params params = new ModelTransformer.Params();
|
||||
for (D d : data) {
|
||||
params.loadDefault();
|
||||
|
||||
batchingType.transform(d, params);
|
||||
|
||||
sbb.renderInto(params, stack, buffer);
|
||||
|
||||
params.load(defaultParams);
|
||||
}
|
||||
}
|
||||
|
||||
void setup(FormatContext context) {
|
||||
void setup() {
|
||||
if (anyToRemove) {
|
||||
removeDeletedInstances();
|
||||
data.removeIf(InstanceData::isRemoved);
|
||||
anyToRemove = false;
|
||||
}
|
||||
|
||||
if (context.usesOverlay()) {
|
||||
defaultParams.overlay();
|
||||
if (false) {
|
||||
this.sbb.context.outputColorDiffuse = false;
|
||||
this.sbb.context.fullNormalTransform = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyDirty() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.batching;
|
||||
|
||||
public record FormatContext(boolean usesOverlay) {
|
||||
|
||||
public static FormatContext defaultContext() {
|
||||
return new FormatContext(false);
|
||||
}
|
||||
}
|
|
@ -14,8 +14,8 @@ import com.jozufozu.flywheel.backend.RenderWork;
|
|||
import com.jozufozu.flywheel.backend.model.ImmediateAllocator;
|
||||
import com.jozufozu.flywheel.backend.model.ModelAllocator;
|
||||
import com.jozufozu.flywheel.backend.model.ModelPool;
|
||||
import com.jozufozu.flywheel.core.Formats;
|
||||
import com.jozufozu.flywheel.core.model.Model;
|
||||
import com.jozufozu.flywheel.core.vertex.PosNormalTexType;
|
||||
|
||||
/**
|
||||
* A collection of Instancers that all have the same format.
|
||||
|
@ -33,7 +33,7 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
|
|||
if (Backend.getInstance().compat.onAMDWindows()) {
|
||||
allocator = ImmediateAllocator.INSTANCE;
|
||||
} else {
|
||||
allocator = new ModelPool(Formats.UNLIT_MODEL, 64);
|
||||
allocator = new ModelPool(PosNormalTexType.INSTANCE, 64);
|
||||
}
|
||||
this.models = CacheBuilder.newBuilder()
|
||||
.removalListener(notification -> {
|
||||
|
|
|
@ -82,7 +82,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
|||
* Render every model for every material.
|
||||
*/
|
||||
@Override
|
||||
public void render(RenderLayerEvent event, MultiBufferSource buffers) {
|
||||
public void render(RenderLayerEvent event) {
|
||||
double camX;
|
||||
double camY;
|
||||
double camZ;
|
||||
|
|
|
@ -34,7 +34,7 @@ public class BufferedModel implements IBufferedModel {
|
|||
|
||||
// mirror it in system memory so we can write to it, and upload our model.
|
||||
try (MappedBuffer buffer = vbo.getBuffer(0, model.size())) {
|
||||
model.buffer(new VecBufferWriter(buffer));
|
||||
model.getType().copyInto(buffer.unwrap(), model.getReader());
|
||||
} catch (Exception e) {
|
||||
Flywheel.log.error(String.format("Error uploading model '%s':", model.name()), e);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,20 @@
|
|||
package com.jozufozu.flywheel.backend.model;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
|
||||
/**
|
||||
* Duck interface used on {@link BufferBuilder} to provide lower level access to the backing memory.
|
||||
*/
|
||||
public interface DirectBufferBuilder {
|
||||
|
||||
/**
|
||||
* Create a DirectVertexConsumer from this BufferBuilder.
|
||||
*
|
||||
* <p>
|
||||
* After this function returns, the internal state of the BufferBuilder will be as if
|
||||
* {@link BufferBuilder#endVertex()} was called vertexCount times. It is up to the callee
|
||||
* to actually populate the BufferBuilder with vertices using the returned value.
|
||||
* </p>
|
||||
*/
|
||||
DirectVertexConsumer intoDirectConsumer(int vertexCount);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,11 @@ import com.mojang.blaze3d.vertex.VertexConsumer;
|
|||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import com.mojang.blaze3d.vertex.VertexFormatElement;
|
||||
|
||||
/**
|
||||
* An unsafe vertex consumer allowing for unchecked writes into a ByteBuffer.
|
||||
*
|
||||
* @see DirectBufferBuilder
|
||||
*/
|
||||
public class DirectVertexConsumer implements VertexConsumer {
|
||||
public final VertexFormat format;
|
||||
private final int stride;
|
||||
|
@ -68,12 +73,16 @@ public class DirectVertexConsumer implements VertexConsumer {
|
|||
this.end = parent.vertexBase + (long) maxVertices * this.stride;
|
||||
}
|
||||
|
||||
public void memSetZero() {
|
||||
MemoryUtil.memSet(vertexBase, 0, end - vertexBase);
|
||||
}
|
||||
|
||||
public boolean hasOverlay() {
|
||||
return uv1 >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split off the head of this consumer into a new object and advance our write-pointer.
|
||||
* Split off the head of this consumer into a new object and advance this object's write-pointer.
|
||||
* @param vertexCount The number of vertices that must be written to the head.
|
||||
* @return The head of this consumer.
|
||||
*/
|
||||
|
@ -102,8 +111,8 @@ public class DirectVertexConsumer implements VertexConsumer {
|
|||
public VertexConsumer color(int r, int g, int b, int a) {
|
||||
if (color < 0) return this;
|
||||
long base = vertexBase + color;
|
||||
int color = ((r & 0xFF)) | ((g & 0xFF) << 8) | ((b & 0xFF) << 16) | ((a & 0xFF) << 24);
|
||||
//MemoryUtil.memPutInt(base, color);
|
||||
// int color = ((r & 0xFF)) | ((g & 0xFF) << 8) | ((b & 0xFF) << 16) | ((a & 0xFF) << 24);
|
||||
// MemoryUtil.memPutInt(base, color);
|
||||
MemoryUtil.memPutByte(base, (byte) r);
|
||||
MemoryUtil.memPutByte(base + 1, (byte) g);
|
||||
MemoryUtil.memPutByte(base + 2, (byte) b);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.jozufozu.flywheel.backend.model;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -13,12 +14,12 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
|||
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
|
||||
import com.jozufozu.flywheel.core.model.Model;
|
||||
import com.jozufozu.flywheel.core.model.VecBufferWriter;
|
||||
import com.jozufozu.flywheel.core.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.util.AttribUtil;
|
||||
|
||||
public class ModelPool implements ModelAllocator {
|
||||
|
||||
protected final VertexFormat format;
|
||||
protected final VertexType vertexType;
|
||||
|
||||
private final List<PooledModel> models = new ArrayList<>();
|
||||
|
||||
|
@ -32,9 +33,9 @@ public class ModelPool implements ModelAllocator {
|
|||
private boolean dirty;
|
||||
private boolean anyToRemove;
|
||||
|
||||
public ModelPool(VertexFormat format, int initialSize) {
|
||||
this.format = format;
|
||||
bufferSize = format.getStride() * initialSize;
|
||||
public ModelPool(VertexType vertexType, int initialSize) {
|
||||
this.vertexType = vertexType;
|
||||
bufferSize = vertexType.getStride() * initialSize;
|
||||
|
||||
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
|
||||
|
||||
|
@ -104,7 +105,7 @@ public class ModelPool implements ModelAllocator {
|
|||
* @return true if the buffer was reallocated
|
||||
*/
|
||||
private boolean realloc() {
|
||||
int neededSize = vertices * format.getStride();
|
||||
int neededSize = vertices * vertexType.getStride();
|
||||
if (neededSize > bufferSize) {
|
||||
bufferSize = neededSize + 128;
|
||||
vbo.alloc(bufferSize);
|
||||
|
@ -117,14 +118,14 @@ public class ModelPool implements ModelAllocator {
|
|||
|
||||
private void uploadAll() {
|
||||
try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) {
|
||||
|
||||
VecBufferWriter consumer = new VecBufferWriter(buffer);
|
||||
ByteBuffer buf = buffer.unwrap();
|
||||
|
||||
int vertices = 0;
|
||||
for (PooledModel model : models) {
|
||||
model.first = vertices;
|
||||
model.model.buffer(consumer);
|
||||
if (model.callback != null) model.callback.onAlloc(model);
|
||||
|
||||
buffer(buf, model);
|
||||
|
||||
vertices += model.getVertexCount();
|
||||
}
|
||||
|
||||
|
@ -135,14 +136,9 @@ public class ModelPool implements ModelAllocator {
|
|||
|
||||
private void uploadPending() {
|
||||
try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) {
|
||||
VecBufferWriter consumer = new VecBufferWriter(buffer);
|
||||
|
||||
int stride = format.getStride();
|
||||
ByteBuffer buf = buffer.unwrap();
|
||||
for (PooledModel model : pendingUpload) {
|
||||
int pos = model.first * stride;
|
||||
buffer.position(pos);
|
||||
model.model.buffer(consumer);
|
||||
if (model.callback != null) model.callback.onAlloc(model);
|
||||
buffer(buf, model);
|
||||
}
|
||||
pendingUpload.clear();
|
||||
} catch (Exception e) {
|
||||
|
@ -150,6 +146,13 @@ public class ModelPool implements ModelAllocator {
|
|||
}
|
||||
}
|
||||
|
||||
private void buffer(ByteBuffer buf, PooledModel model) {
|
||||
int pos = model.first * vertexType.getStride();
|
||||
buf.position(pos);
|
||||
vertexType.copyInto(buf, model.model.getReader());
|
||||
if (model.callback != null) model.callback.onAlloc(model);
|
||||
}
|
||||
|
||||
private void setDirty() {
|
||||
dirty = true;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
|||
|
||||
public class Formats {
|
||||
public static final VertexFormat UNLIT_MODEL = VertexFormat.builder()
|
||||
.addAttributes(CommonAttributes.VEC3, CommonAttributes.NORMAL, CommonAttributes.UV)
|
||||
.addAttributes(CommonAttributes.VEC3, CommonAttributes.UV, CommonAttributes.NORMAL)
|
||||
.build();
|
||||
|
||||
public static final VertexFormat COLORED_LIT_MODEL = VertexFormat.builder()
|
||||
|
|
|
@ -77,7 +77,7 @@ public class CrumblingRenderer {
|
|||
|
||||
instanceManager.beginFrame(info);
|
||||
|
||||
materials.render(event, null);
|
||||
materials.render(event);
|
||||
|
||||
instanceManager.invalidate();
|
||||
}
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
package com.jozufozu.flywheel.core.model;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
||||
import com.jozufozu.flywheel.core.Formats;
|
||||
import com.jozufozu.flywheel.util.BufferBuilderReader;
|
||||
import com.jozufozu.flywheel.util.UnsafeBlockFormatReader;
|
||||
import com.jozufozu.flywheel.util.ModelReader;
|
||||
import com.jozufozu.flywheel.util.RenderMath;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
|
@ -33,38 +29,27 @@ public class BlockModel implements Model {
|
|||
}
|
||||
|
||||
public BlockModel(BakedModel model, BlockState referenceState, PoseStack ms) {
|
||||
reader = new BufferBuilderReader(ModelUtil.getBufferBuilder(model, referenceState, ms));
|
||||
reader = new UnsafeBlockFormatReader(ModelUtil.getBufferBuilder(model, referenceState, ms));
|
||||
name = referenceState.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(ModelTransformer.Context ctx) {
|
||||
ctx.inputHasDiffuse = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexFormat format() {
|
||||
return Formats.UNLIT_MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vertexCount() {
|
||||
return reader.getVertexCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buffer(VertexConsumer buffer) {
|
||||
|
||||
int vertexCount = vertexCount();
|
||||
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
buffer.vertex(reader.getX(i), reader.getY(i), reader.getZ(i));
|
||||
|
||||
buffer.normal(reader.getNX(i), reader.getNY(i), reader.getNZ(i));
|
||||
|
||||
buffer.uv(reader.getU(i), reader.getV(i));
|
||||
|
||||
buffer.endVertex();
|
||||
}
|
||||
public ModelReader getReader() {
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package com.jozufozu.flywheel.core.model;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
||||
import com.jozufozu.flywheel.core.Formats;
|
||||
import com.jozufozu.flywheel.core.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.util.ModelReader;
|
||||
import com.jozufozu.flywheel.util.RenderMath;
|
||||
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
|
||||
public class BlockType implements VertexType {
|
||||
|
||||
public static final BlockType INSTANCE = new BlockType();
|
||||
|
||||
@Override
|
||||
public VertexFormat getFormat() {
|
||||
return Formats.COLORED_LIT_MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyInto(ByteBuffer buffer, ModelReader reader) {
|
||||
int stride = getStride();
|
||||
long addr = MemoryUtil.memAddress(buffer);
|
||||
|
||||
int vertexCount = reader.getVertexCount();
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
float x = reader.getX(i);
|
||||
float y = reader.getY(i);
|
||||
float z = reader.getZ(i);
|
||||
|
||||
float xN = reader.getNX(i);
|
||||
float yN = reader.getNY(i);
|
||||
float zN = reader.getNZ(i);
|
||||
|
||||
float u = reader.getU(i);
|
||||
float v = reader.getV(i);
|
||||
|
||||
byte r = reader.getR(i);
|
||||
byte g = reader.getG(i);
|
||||
byte b = reader.getB(i);
|
||||
byte a = reader.getA(i);
|
||||
|
||||
int light = reader.getLight(i);
|
||||
|
||||
MemoryUtil.memPutFloat(addr, x);
|
||||
MemoryUtil.memPutFloat(addr + 4, y);
|
||||
MemoryUtil.memPutFloat(addr + 8, z);
|
||||
MemoryUtil.memPutByte(addr + 12, RenderMath.nb(xN));
|
||||
MemoryUtil.memPutByte(addr + 13, RenderMath.nb(yN));
|
||||
MemoryUtil.memPutByte(addr + 14, RenderMath.nb(zN));
|
||||
MemoryUtil.memPutFloat(addr + 15, u);
|
||||
MemoryUtil.memPutFloat(addr + 19, v);
|
||||
MemoryUtil.memPutByte(addr + 23, r);
|
||||
MemoryUtil.memPutByte(addr + 24, g);
|
||||
MemoryUtil.memPutByte(addr + 25, b);
|
||||
MemoryUtil.memPutByte(addr + 26, a);
|
||||
|
||||
byte block = (byte) (LightTexture.block(light) << 4);
|
||||
byte sky = (byte) (LightTexture.sky(light) << 4);
|
||||
|
||||
MemoryUtil.memPutByte(addr + 27, block);
|
||||
MemoryUtil.memPutByte(addr + 28, sky);
|
||||
|
||||
addr += stride;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,8 +3,9 @@ package com.jozufozu.flywheel.core.model;
|
|||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
||||
import com.jozufozu.flywheel.backend.model.ElementBuffer;
|
||||
import com.jozufozu.flywheel.core.QuadConverter;
|
||||
import com.jozufozu.flywheel.core.vertex.PosNormalTexType;
|
||||
import com.jozufozu.flywheel.core.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.util.ModelReader;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
/**
|
||||
* A model that can be rendered by flywheel.
|
||||
|
@ -33,20 +34,27 @@ public interface Model {
|
|||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Copy this model into the given buffer.
|
||||
*/
|
||||
void buffer(VertexConsumer buffer);
|
||||
ModelReader getReader();
|
||||
|
||||
/**
|
||||
* @return The number of vertices the model has.
|
||||
*/
|
||||
int vertexCount();
|
||||
|
||||
default void configure(ModelTransformer.Context ctx) {
|
||||
|
||||
}
|
||||
|
||||
default VertexType getType() {
|
||||
return PosNormalTexType.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The format of this model's vertices
|
||||
*/
|
||||
VertexFormat format();
|
||||
default VertexFormat format() {
|
||||
return getType().getFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an element buffer object that indexes the vertices of this model.
|
||||
|
@ -77,6 +85,4 @@ public interface Model {
|
|||
default boolean empty() {
|
||||
return vertexCount() == 0;
|
||||
}
|
||||
|
||||
ModelReader getReader();
|
||||
}
|
||||
|
|
|
@ -3,29 +3,35 @@ package com.jozufozu.flywheel.core.model;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
|
||||
import com.jozufozu.flywheel.core.Formats;
|
||||
import com.jozufozu.flywheel.core.vertex.PosNormalTexReader;
|
||||
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriter;
|
||||
import com.jozufozu.flywheel.util.ModelReader;
|
||||
import com.jozufozu.flywheel.util.RenderMath;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.math.Vector3f;
|
||||
import com.mojang.blaze3d.platform.MemoryTracker;
|
||||
|
||||
public class ModelPart implements Model {
|
||||
|
||||
private final List<PartBuilder.CuboidBuilder> cuboids;
|
||||
private int vertices;
|
||||
private final int vertices;
|
||||
private final String name;
|
||||
private final PosNormalTexReader reader;
|
||||
|
||||
public ModelPart(List<PartBuilder.CuboidBuilder> cuboids, String name) {
|
||||
this.cuboids = cuboids;
|
||||
this.name = name;
|
||||
|
||||
vertices = 0;
|
||||
|
||||
for (PartBuilder.CuboidBuilder cuboid : cuboids) {
|
||||
vertices += cuboid.vertices();
|
||||
{
|
||||
int vertices = 0;
|
||||
for (PartBuilder.CuboidBuilder cuboid : cuboids) {
|
||||
vertices += cuboid.vertices();
|
||||
}
|
||||
this.vertices = vertices;
|
||||
}
|
||||
|
||||
ByteBuffer buffer = MemoryTracker.create(size());
|
||||
PosTexNormalWriter writer = new PosTexNormalWriter(buffer);
|
||||
for (PartBuilder.CuboidBuilder cuboid : cuboids) {
|
||||
cuboid.buffer(writer);
|
||||
}
|
||||
|
||||
reader = new PosNormalTexReader(buffer, vertices);
|
||||
}
|
||||
|
||||
public static PartBuilder builder(String name, int sizeU, int sizeV) {
|
||||
|
@ -37,111 +43,13 @@ public class ModelPart implements Model {
|
|||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buffer(VertexConsumer buffer) {
|
||||
for (PartBuilder.CuboidBuilder cuboid : cuboids) {
|
||||
cuboid.buffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vertexCount() {
|
||||
return vertices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexFormat format() {
|
||||
return Formats.UNLIT_MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelReader getReader() {
|
||||
return new PartReader(this);
|
||||
}
|
||||
|
||||
private class PartReader implements ModelReader {
|
||||
|
||||
private final ByteBuffer buffer;
|
||||
|
||||
private PartReader(ModelPart part) {
|
||||
this.buffer = ByteBuffer.allocate(part.size());
|
||||
VecBufferWriter writer = new VecBufferWriter(this.buffer);
|
||||
|
||||
buffer(writer);
|
||||
}
|
||||
|
||||
private int vertIdx(int vertexIndex) {
|
||||
return vertexIndex * format().getStride();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getX(int index) {
|
||||
return buffer.getFloat(vertIdx(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getY(int index) {
|
||||
return buffer.getFloat(vertIdx(index) + 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getZ(int index) {
|
||||
return buffer.getFloat(vertIdx(index) + 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getR(int index) {
|
||||
return (byte) 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getG(int index) {
|
||||
return (byte) 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getB(int index) {
|
||||
return (byte) 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getA(int index) {
|
||||
return (byte) 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getU(int index) {
|
||||
return buffer.getFloat(vertIdx(index) + 15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getV(int index) {
|
||||
return buffer.getFloat(vertIdx(index) + 19);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLight(int index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getNX(int index) {
|
||||
return RenderMath.f(buffer.get(vertIdx(index) + 12));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getNY(int index) {
|
||||
return RenderMath.f(buffer.get(vertIdx(index) + 13));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getNZ(int index) {
|
||||
return RenderMath.f(buffer.get(vertIdx(index) + 14));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVertexCount() {
|
||||
return vertices;
|
||||
}
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.jozufozu.flywheel.core.model;
|
||||
|
||||
import com.jozufozu.flywheel.util.ModelReader;
|
||||
import com.jozufozu.flywheel.util.RenderMath;
|
||||
import com.jozufozu.flywheel.util.transform.Transform;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
@ -16,6 +17,8 @@ public class ModelTransformer {
|
|||
private final Model model;
|
||||
private final ModelReader reader;
|
||||
|
||||
public final Context context = new Context();
|
||||
|
||||
public ModelTransformer(Model model) {
|
||||
this.model = model;
|
||||
reader = model.getReader();
|
||||
|
@ -34,7 +37,7 @@ public class ModelTransformer {
|
|||
modelMat.multiply(params.model);
|
||||
|
||||
Matrix3f normalMat;
|
||||
if (params.fullNormalTransform) {
|
||||
if (context.fullNormalTransform) {
|
||||
normalMat = input.last().normal().copy();
|
||||
normalMat.mul(params.normal);
|
||||
} else {
|
||||
|
@ -61,42 +64,46 @@ public class ModelTransformer {
|
|||
float ny = normal.y();
|
||||
float nz = normal.z();
|
||||
|
||||
float instanceDiffuse = LightUtil.diffuseLight(nx, ny, nz);
|
||||
|
||||
switch (params.colorMode) {
|
||||
case MODEL_ONLY -> builder.color(reader.getR(i), reader.getG(i), reader.getB(i), reader.getA(i));
|
||||
case DIFFUSE_ONLY -> builder.color(instanceDiffuse, instanceDiffuse, instanceDiffuse, 1f);
|
||||
case MODEL_DIFFUSE -> {
|
||||
int r = Byte.toUnsignedInt(reader.getR(i));
|
||||
int g = Byte.toUnsignedInt(reader.getG(i));
|
||||
int b = Byte.toUnsignedInt(reader.getB(i));
|
||||
int a = Byte.toUnsignedInt(reader.getA(i));
|
||||
|
||||
float diffuse = switch (params.diffuseMode) {
|
||||
case NONE -> 1f;
|
||||
case INSTANCE -> instanceDiffuse;
|
||||
case ONE_OVER_STATIC -> 1 / LightUtil.diffuseLight(normalX, normalY, normalZ);
|
||||
case INSTANCE_OVER_STATIC -> instanceDiffuse / LightUtil.diffuseLight(normalX, normalY, normalZ);
|
||||
};
|
||||
|
||||
if (diffuse != 1) {
|
||||
r = transformColor(r, diffuse);
|
||||
g = transformColor(g, diffuse);
|
||||
b = transformColor(b, diffuse);
|
||||
}
|
||||
|
||||
builder.color(r, g, b, a);
|
||||
}
|
||||
case RECOLOR -> {
|
||||
if (params.diffuseMode == DiffuseMode.NONE) {
|
||||
builder.color(params.r, params.g, params.b, params.a);
|
||||
} else {
|
||||
if (params.useParamColor) {
|
||||
if (context.outputColorDiffuse) {
|
||||
float instanceDiffuse = LightUtil.diffuseLight(nx, ny, nz);
|
||||
int colorR = transformColor(params.r, instanceDiffuse);
|
||||
int colorG = transformColor(params.g, instanceDiffuse);
|
||||
int colorB = transformColor(params.b, instanceDiffuse);
|
||||
builder.color(colorR, colorG, colorB, params.a);
|
||||
} else {
|
||||
builder.color(params.r, params.g, params.b, params.a);
|
||||
}
|
||||
} else {
|
||||
if (context.inputHasDiffuse) {
|
||||
int r = Byte.toUnsignedInt(reader.getR(i));
|
||||
int g = Byte.toUnsignedInt(reader.getG(i));
|
||||
int b = Byte.toUnsignedInt(reader.getB(i));
|
||||
int a = Byte.toUnsignedInt(reader.getA(i));
|
||||
|
||||
float undoStaticDiffuse = 1 / LightUtil.diffuseLight(normalX, normalY, normalZ);
|
||||
float diffuse;
|
||||
if (context.outputColorDiffuse) {
|
||||
diffuse = LightUtil.diffuseLight(nx, ny, nz) * undoStaticDiffuse;
|
||||
} else {
|
||||
diffuse = undoStaticDiffuse;
|
||||
}
|
||||
|
||||
if (diffuse != 1) {
|
||||
r = transformColor(r, diffuse);
|
||||
g = transformColor(g, diffuse);
|
||||
b = transformColor(b, diffuse);
|
||||
}
|
||||
|
||||
builder.color(r, g, b, a);
|
||||
} else {
|
||||
if (context.outputColorDiffuse) {
|
||||
int d = RenderMath.unb(LightUtil.diffuseLight(nx, ny, nz));
|
||||
builder.color(d, d, d, 0xFF);
|
||||
} else {
|
||||
builder.color(reader.getR(i), reader.getG(i), reader.getB(i), reader.getA(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//builder.color(Math.max(0, (int) (nx * 255)), Math.max(0, (int) (ny * 255)), Math.max(0, (int) (nz * 255)), 0xFF);
|
||||
|
@ -110,11 +117,10 @@ public class ModelTransformer {
|
|||
builder.uv(u, v);
|
||||
}
|
||||
|
||||
if (params.hasOverlay) {
|
||||
builder.overlayCoords(params.overlay);
|
||||
}
|
||||
// not always used, but will be ignored by formats that don't use it
|
||||
builder.overlayCoords(params.overlay);
|
||||
|
||||
builder.uv2(params.hasCustomLight ? params.packedLightCoords : reader.getLight(i));
|
||||
builder.uv2(params.useParamLight ? params.packedLightCoords : reader.getLight(i));
|
||||
|
||||
builder.normal(nx, ny, nz);
|
||||
|
||||
|
@ -128,7 +134,7 @@ public class ModelTransformer {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SuperByteBuffer[" + model + ']';
|
||||
return "ModelTransformer[" + model + ']';
|
||||
}
|
||||
|
||||
public static int transformColor(int component, float scale) {
|
||||
|
@ -140,28 +146,32 @@ public class ModelTransformer {
|
|||
void shift(VertexConsumer builder, float u, float v);
|
||||
}
|
||||
|
||||
public enum ColorMode {
|
||||
MODEL_ONLY,
|
||||
MODEL_DIFFUSE,
|
||||
DIFFUSE_ONLY,
|
||||
RECOLOR
|
||||
}
|
||||
public static class Context {
|
||||
/**
|
||||
* Do we need to include the PoseStack transforms in our transformation of the normal?
|
||||
*/
|
||||
public boolean fullNormalTransform = false;
|
||||
|
||||
/**
|
||||
* Does the model we're transforming have diffuse light baked into its colors?
|
||||
*/
|
||||
public boolean inputHasDiffuse = false;
|
||||
|
||||
/**
|
||||
* Do we need to bake diffuse lighting into the output colors?
|
||||
*/
|
||||
public boolean outputColorDiffuse = true;
|
||||
|
||||
public enum DiffuseMode {
|
||||
NONE,
|
||||
INSTANCE,
|
||||
ONE_OVER_STATIC,
|
||||
INSTANCE_OVER_STATIC,
|
||||
}
|
||||
|
||||
public static class Params implements Transform<Params> {
|
||||
// Vertex Position
|
||||
|
||||
// Transform
|
||||
public final Matrix4f model;
|
||||
public final Matrix3f normal;
|
||||
|
||||
// Vertex Coloring
|
||||
public ColorMode colorMode;
|
||||
public DiffuseMode diffuseMode;
|
||||
public boolean useParamColor;
|
||||
public int r;
|
||||
public int g;
|
||||
public int b;
|
||||
|
@ -171,46 +181,47 @@ public class ModelTransformer {
|
|||
public SpriteShiftFunc spriteShiftFunc;
|
||||
|
||||
// Vertex Overlay Color
|
||||
public boolean hasOverlay;
|
||||
public int overlay;
|
||||
|
||||
// Vertex Lighting
|
||||
public boolean hasCustomLight;
|
||||
public boolean useParamLight;
|
||||
public int packedLightCoords;
|
||||
|
||||
// Vertex Normals
|
||||
public boolean fullNormalTransform;
|
||||
|
||||
public Params() {
|
||||
model = new Matrix4f();
|
||||
normal = new Matrix3f();
|
||||
}
|
||||
|
||||
public void loadDefault() {
|
||||
model.setIdentity();
|
||||
normal.setIdentity();
|
||||
useParamColor = true;
|
||||
r = 0xFF;
|
||||
g = 0xFF;
|
||||
b = 0xFF;
|
||||
a = 0xFF;
|
||||
spriteShiftFunc = null;
|
||||
overlay = OverlayTexture.NO_OVERLAY;
|
||||
useParamLight = false;
|
||||
packedLightCoords = LightTexture.FULL_BRIGHT;
|
||||
}
|
||||
|
||||
public void load(Params from) {
|
||||
model.load(from.model);
|
||||
normal.load(from.normal);
|
||||
colorMode = from.colorMode;
|
||||
diffuseMode = from.diffuseMode;
|
||||
useParamColor = from.useParamColor;
|
||||
r = from.r;
|
||||
g = from.g;
|
||||
b = from.b;
|
||||
a = from.a;
|
||||
spriteShiftFunc = from.spriteShiftFunc;
|
||||
hasOverlay = from.hasOverlay;
|
||||
overlay = from.overlay;
|
||||
hasCustomLight = from.hasCustomLight;
|
||||
useParamLight = from.useParamLight;
|
||||
packedLightCoords = from.packedLightCoords;
|
||||
fullNormalTransform = from.fullNormalTransform;
|
||||
}
|
||||
|
||||
public Params copy() {
|
||||
Params params = new Params();
|
||||
params.load(this);
|
||||
return params;
|
||||
}
|
||||
|
||||
public Params color(int r, int g, int b, int a) {
|
||||
this.colorMode = ColorMode.RECOLOR;
|
||||
this.useParamColor = true;
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
|
@ -219,7 +230,7 @@ public class ModelTransformer {
|
|||
}
|
||||
|
||||
public Params color(byte r, byte g, byte b, byte a) {
|
||||
this.colorMode = ColorMode.RECOLOR;
|
||||
this.useParamColor = true;
|
||||
this.r = Byte.toUnsignedInt(r);
|
||||
this.g = Byte.toUnsignedInt(g);
|
||||
this.b = Byte.toUnsignedInt(b);
|
||||
|
@ -228,7 +239,7 @@ public class ModelTransformer {
|
|||
}
|
||||
|
||||
public Params color(int color) {
|
||||
this.colorMode = ColorMode.RECOLOR;
|
||||
this.useParamColor = true;
|
||||
this.r = ((color >> 16) & 0xFF);
|
||||
this.g = ((color >> 8) & 0xFF);
|
||||
this.b = (color & 0xFF);
|
||||
|
@ -241,29 +252,13 @@ public class ModelTransformer {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Params overlay() {
|
||||
this.hasOverlay = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Params overlay(int overlay) {
|
||||
this.hasOverlay = true;
|
||||
this.overlay = overlay;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms normals not only by the local matrix stack, but also by the passed matrix stack.
|
||||
*/
|
||||
public void entityMode() {
|
||||
this.hasOverlay = true;
|
||||
this.fullNormalTransform = true;
|
||||
this.diffuseMode = DiffuseMode.NONE;
|
||||
this.colorMode = ColorMode.RECOLOR;
|
||||
}
|
||||
|
||||
public Params light(int packedLightCoords) {
|
||||
this.hasCustomLight = true;
|
||||
this.useParamLight = true;
|
||||
this.packedLightCoords = packedLightCoords;
|
||||
return this;
|
||||
}
|
||||
|
@ -313,23 +308,5 @@ public class ModelTransformer {
|
|||
return this;
|
||||
}
|
||||
|
||||
public static Params defaultParams() {
|
||||
Params out = new Params();
|
||||
out.model.setIdentity();
|
||||
out.normal.setIdentity();
|
||||
out.colorMode = ColorMode.DIFFUSE_ONLY;
|
||||
out.diffuseMode = DiffuseMode.INSTANCE;
|
||||
out.r = 0xFF;
|
||||
out.g = 0xFF;
|
||||
out.b = 0xFF;
|
||||
out.a = 0xFF;
|
||||
out.spriteShiftFunc = null;
|
||||
out.hasOverlay = false;
|
||||
out.overlay = OverlayTexture.NO_OVERLAY;
|
||||
out.hasCustomLight = false;
|
||||
out.packedLightCoords = LightTexture.FULL_BRIGHT;
|
||||
out.fullNormalTransform = false;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import java.util.EnumSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriter;
|
||||
import com.mojang.math.Matrix3f;
|
||||
import com.mojang.math.Quaternion;
|
||||
import com.mojang.math.Vector3f;
|
||||
|
@ -160,7 +160,7 @@ public class PartBuilder {
|
|||
return visibleFaces.size() * 4;
|
||||
}
|
||||
|
||||
public void buffer(VertexConsumer buffer) {
|
||||
public void buffer(PosTexNormalWriter buffer) {
|
||||
|
||||
float sizeX = posX2 - posX1;
|
||||
float sizeY = posY2 - posY1;
|
||||
|
@ -235,11 +235,11 @@ public class PartBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
public void quad(VertexConsumer buffer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) {
|
||||
buffer.vertex(vertices[0].x(), vertices[0].y(), vertices[0].z()).normal(normal.x(), normal.y(), normal.z()).uv(maxU, minV).endVertex();
|
||||
buffer.vertex(vertices[1].x(), vertices[1].y(), vertices[1].z()).normal(normal.x(), normal.y(), normal.z()).uv(minU, minV).endVertex();
|
||||
buffer.vertex(vertices[2].x(), vertices[2].y(), vertices[2].z()).normal(normal.x(), normal.y(), normal.z()).uv(minU, maxV).endVertex();
|
||||
buffer.vertex(vertices[3].x(), vertices[3].y(), vertices[3].z()).normal(normal.x(), normal.y(), normal.z()).uv(maxU, maxV).endVertex();
|
||||
public void quad(PosTexNormalWriter buffer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) {
|
||||
buffer.putVertex(vertices[0].x(), vertices[0].y(), vertices[0].z(), normal.x(), normal.y(), normal.z(), maxU, minV);
|
||||
buffer.putVertex(vertices[1].x(), vertices[1].y(), vertices[1].z(), normal.x(), normal.y(), normal.z(), minU, minV);
|
||||
buffer.putVertex(vertices[2].x(), vertices[2].y(), vertices[2].z(), normal.x(), normal.y(), normal.z(), minU, maxV);
|
||||
buffer.putVertex(vertices[3].x(), vertices[3].y(), vertices[3].z(), normal.x(), normal.y(), normal.z(), maxU, maxV);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -2,14 +2,10 @@ package com.jozufozu.flywheel.core.model;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
||||
import com.jozufozu.flywheel.core.Formats;
|
||||
import com.jozufozu.flywheel.util.BufferBuilderReader;
|
||||
import com.jozufozu.flywheel.core.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.util.UnsafeBlockFormatReader;
|
||||
import com.jozufozu.flywheel.util.ModelReader;
|
||||
import com.jozufozu.flywheel.util.RenderMath;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
|
@ -20,7 +16,7 @@ public class WorldModel implements Model {
|
|||
private final String name;
|
||||
|
||||
public WorldModel(BlockAndTintGetter renderWorld, RenderType layer, Collection<StructureTemplate.StructureBlockInfo> blocks, String name) {
|
||||
reader = new BufferBuilderReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks));
|
||||
reader = new UnsafeBlockFormatReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks));
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
@ -30,25 +26,8 @@ public class WorldModel implements Model {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void buffer(VertexConsumer vertices) {
|
||||
for (int i = 0; i < vertexCount(); i++) {
|
||||
vertices.vertex(reader.getX(i), reader.getY(i), reader.getZ(i));
|
||||
|
||||
vertices.normal(reader.getNX(i), reader.getNY(i), reader.getNZ(i));
|
||||
|
||||
vertices.uv(reader.getU(i), reader.getV(i));
|
||||
|
||||
vertices.color(reader.getR(i), reader.getG(i), reader.getB(i), reader.getA(i));
|
||||
|
||||
int light = reader.getLight(i);
|
||||
|
||||
byte block = (byte) (LightTexture.block(light) << 4);
|
||||
byte sky = (byte) (LightTexture.sky(light) << 4);
|
||||
|
||||
vertices.uv2(block, sky);
|
||||
|
||||
vertices.endVertex();
|
||||
}
|
||||
public VertexType getType() {
|
||||
return BlockType.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -56,11 +35,6 @@ public class WorldModel implements Model {
|
|||
return reader.getVertexCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexFormat format() {
|
||||
return Formats.COLORED_LIT_MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelReader getReader() {
|
||||
return reader;
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
package com.jozufozu.flywheel.core.vertex;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.jozufozu.flywheel.util.ModelReader;
|
||||
import com.jozufozu.flywheel.util.RenderMath;
|
||||
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
|
||||
public class PosNormalTexReader implements ModelReader {
|
||||
|
||||
private final ByteBuffer buffer;
|
||||
private final int vertexCount;
|
||||
private final long base;
|
||||
|
||||
public PosNormalTexReader(ByteBuffer buffer, int vertexCount) {
|
||||
this.buffer = buffer;
|
||||
this.vertexCount = vertexCount;
|
||||
this.base = MemoryUtil.memAddress(buffer);
|
||||
}
|
||||
|
||||
private long ptr(long idx) {
|
||||
return base + idx * 23;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getX(int index) {
|
||||
return MemoryUtil.memGetFloat(ptr(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getY(int index) {
|
||||
return MemoryUtil.memGetFloat(ptr(index) + 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getZ(int index) {
|
||||
return MemoryUtil.memGetFloat(ptr(index) + 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getR(int index) {
|
||||
return (byte) 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getG(int index) {
|
||||
return (byte) 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getB(int index) {
|
||||
return (byte) 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getA(int index) {
|
||||
return (byte) 0xFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getU(int index) {
|
||||
return MemoryUtil.memGetFloat(ptr(index) + 12);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getV(int index) {
|
||||
return MemoryUtil.memGetFloat(ptr(index) + 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLight(int index) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getNX(int index) {
|
||||
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 20));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getNY(int index) {
|
||||
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 21));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getNZ(int index) {
|
||||
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 22));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVertexCount() {
|
||||
return vertexCount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.jozufozu.flywheel.core.vertex;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
||||
import com.jozufozu.flywheel.core.Formats;
|
||||
import com.jozufozu.flywheel.util.ModelReader;
|
||||
|
||||
public class PosNormalTexType implements VertexType {
|
||||
|
||||
public static final PosNormalTexType INSTANCE = new PosNormalTexType();
|
||||
|
||||
@Override
|
||||
public VertexFormat getFormat() {
|
||||
return Formats.UNLIT_MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyInto(ByteBuffer buffer, ModelReader reader) {
|
||||
PosTexNormalWriter writer = new PosTexNormalWriter(buffer);
|
||||
|
||||
int vertexCount = reader.getVertexCount();
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
float x = reader.getX(i);
|
||||
float y = reader.getY(i);
|
||||
float z = reader.getZ(i);
|
||||
|
||||
float u = reader.getU(i);
|
||||
float v = reader.getV(i);
|
||||
|
||||
float xN = reader.getNX(i);
|
||||
float yN = reader.getNY(i);
|
||||
float zN = reader.getNZ(i);
|
||||
|
||||
writer.putVertex(x, y, z, xN, yN, zN, u, v);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.jozufozu.flywheel.core.vertex;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.jozufozu.flywheel.util.RenderMath;
|
||||
|
||||
public class PosTexNormalWriter {
|
||||
|
||||
private long addr;
|
||||
|
||||
private int vertexCount;
|
||||
|
||||
public PosTexNormalWriter(ByteBuffer buffer) {
|
||||
addr = MemoryUtil.memAddress(buffer);
|
||||
}
|
||||
|
||||
public void putVertex(float x, float y, float z, float nX, float nY, float nZ, float u, float v) {
|
||||
MemoryUtil.memPutFloat(addr, x);
|
||||
MemoryUtil.memPutFloat(addr + 4, y);
|
||||
MemoryUtil.memPutFloat(addr + 8, z);
|
||||
MemoryUtil.memPutFloat(addr + 12, u);
|
||||
MemoryUtil.memPutFloat(addr + 16, v);
|
||||
MemoryUtil.memPutByte(addr + 20, RenderMath.nb(nX));
|
||||
MemoryUtil.memPutByte(addr + 21, RenderMath.nb(nY));
|
||||
MemoryUtil.memPutByte(addr + 22, RenderMath.nb(nZ));
|
||||
|
||||
addr += 23;
|
||||
vertexCount++;
|
||||
}
|
||||
|
||||
public int getVertexCount() {
|
||||
return vertexCount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.jozufozu.flywheel.core.vertex;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
||||
import com.jozufozu.flywheel.util.ModelReader;
|
||||
|
||||
public interface VertexType {
|
||||
|
||||
VertexFormat getFormat();
|
||||
|
||||
void copyInto(ByteBuffer buffer, ModelReader reader);
|
||||
|
||||
default int getStride() {
|
||||
return getFormat().getStride();
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.mixin;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
@ -38,9 +39,11 @@ public abstract class BufferBuilderMixin implements DirectBufferBuilder {
|
|||
private int nextElementByte;
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public DirectVertexConsumer intoDirectConsumer(int vertexCount) {
|
||||
int bytes = vertexCount * format.getVertexSize();
|
||||
ensureCapacity(bytes);
|
||||
// ensure we have capacity for one extra vertex, BufferBuilder does this on #endVertex
|
||||
ensureCapacity(bytes + format.getVertexSize());
|
||||
|
||||
DirectVertexConsumer consumer = new DirectVertexConsumer(this.buffer, this.format, vertexCount);
|
||||
|
||||
|
@ -49,7 +52,7 @@ public abstract class BufferBuilderMixin implements DirectBufferBuilder {
|
|||
.get(0);
|
||||
this.elementIndex = 0;
|
||||
this.nextElementByte += bytes;
|
||||
this.buffer.position(consumer.startPos + bytes);
|
||||
this.buffer.position(this.buffer.position() + bytes);
|
||||
|
||||
return consumer;
|
||||
}
|
||||
|
|
|
@ -3,36 +3,24 @@ package com.jozufozu.flywheel.util;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
|
||||
public class BufferBuilderReader implements ModelReader {
|
||||
public class BlockFormatReader implements ModelReader {
|
||||
|
||||
private final ByteBuffer buffer;
|
||||
private final int vertexCount;
|
||||
private final int formatSize;
|
||||
private final int size;
|
||||
private final int stride;
|
||||
|
||||
public BufferBuilderReader(BufferBuilder builder) {
|
||||
VertexFormat vertexFormat = builder.getVertexFormat();
|
||||
public BlockFormatReader(BufferBuilder builder) {
|
||||
Pair<BufferBuilder.DrawState, ByteBuffer> data = builder.popNextBuffer();
|
||||
buffer = data.getSecond();
|
||||
|
||||
formatSize = vertexFormat.getVertexSize();
|
||||
stride = builder.getVertexFormat()
|
||||
.getVertexSize();
|
||||
|
||||
vertexCount = data.getFirst()
|
||||
.vertexCount();
|
||||
|
||||
size = vertexCount * formatSize;
|
||||
|
||||
// TODO: adjust the getters based on the input format
|
||||
// ImmutableList<VertexFormatElement> elements = vertexFormat.getElements();
|
||||
// for (int i = 0, size = elements.size(); i < size; i++) {
|
||||
// VertexFormatElement element = elements.get(i);
|
||||
// int offset = vertexFormat.getOffset(i);
|
||||
//
|
||||
// element.getUsage()
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -41,7 +29,7 @@ public class BufferBuilderReader implements ModelReader {
|
|||
}
|
||||
|
||||
private int vertIdx(int vertexIndex) {
|
||||
return vertexIndex * formatSize;
|
||||
return vertexIndex * stride;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -114,7 +102,4 @@ public class BufferBuilderReader implements ModelReader {
|
|||
return vertexCount;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
}
|
309
src/main/java/com/jozufozu/flywheel/util/Color.java
Normal file
309
src/main/java/com/jozufozu/flywheel/util/Color.java
Normal file
|
@ -0,0 +1,309 @@
|
|||
package com.jozufozu.flywheel.util;
|
||||
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.mojang.math.Vector3f;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
@SuppressWarnings("PointlessBitwiseExpression")
|
||||
public class Color {
|
||||
public final static Color TRANSPARENT_BLACK = new Color(0, 0, 0, 0).setImmutable();
|
||||
public final static Color BLACK = new Color(0, 0, 0).setImmutable();
|
||||
public final static Color WHITE = new Color(255, 255, 255).setImmutable();
|
||||
public final static Color RED = new Color(255, 0, 0).setImmutable();
|
||||
public final static Color GREEN = new Color(0, 255, 0).setImmutable();
|
||||
public final static Color SPRING_GREEN = new Color(0, 255, 187).setImmutable();
|
||||
|
||||
protected boolean mutable = true;
|
||||
protected int value;
|
||||
|
||||
public Color(int r, int g, int b) {
|
||||
this(r, g, b, 0xff);
|
||||
}
|
||||
|
||||
public Color(int r, int g, int b, int a) {
|
||||
value = ((a & 0xff) << 24) |
|
||||
((r & 0xff) << 16) |
|
||||
((g & 0xff) << 8) |
|
||||
((b & 0xff) << 0);
|
||||
}
|
||||
|
||||
public Color(float r, float g, float b, float a) {
|
||||
this(
|
||||
(int) (0.5 + 0xff * Mth.clamp(r, 0, 1)),
|
||||
(int) (0.5 + 0xff * Mth.clamp(g, 0, 1)),
|
||||
(int) (0.5 + 0xff * Mth.clamp(b, 0, 1)),
|
||||
(int) (0.5 + 0xff * Mth.clamp(a, 0, 1))
|
||||
);
|
||||
}
|
||||
|
||||
public Color(int rgba) {
|
||||
value = rgba;
|
||||
}
|
||||
|
||||
public Color(int rgb, boolean hasAlpha) {
|
||||
if (hasAlpha) {
|
||||
value = rgb;
|
||||
} else {
|
||||
value = rgb | 0xff_000000;
|
||||
}
|
||||
}
|
||||
|
||||
public Color copy() {
|
||||
return copy(true);
|
||||
}
|
||||
|
||||
public Color copy(boolean mutable) {
|
||||
if (mutable)
|
||||
return new Color(value);
|
||||
else
|
||||
return new Color(value).setImmutable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this color as immutable. Attempting to mutate this color in the future
|
||||
* will instead cause a copy to be created that can me modified.
|
||||
*/
|
||||
public Color setImmutable() {
|
||||
this.mutable = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the red component in the range 0-255.
|
||||
* @see #getRGB
|
||||
*/
|
||||
public int getRed() {
|
||||
return (getRGB() >> 16) & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the green component in the range 0-255.
|
||||
* @see #getRGB
|
||||
*/
|
||||
public int getGreen() {
|
||||
return (getRGB() >> 8) & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the blue component in the range 0-255.
|
||||
* @see #getRGB
|
||||
*/
|
||||
public int getBlue() {
|
||||
return (getRGB() >> 0) & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the alpha component in the range 0-255.
|
||||
* @see #getRGB
|
||||
*/
|
||||
public int getAlpha() {
|
||||
return (getRGB() >> 24) & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the red component in the range 0-1f.
|
||||
*/
|
||||
public float getRedAsFloat() {
|
||||
return getRed() / 255f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the green component in the range 0-1f.
|
||||
*/
|
||||
public float getGreenAsFloat() {
|
||||
return getGreen() / 255f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the blue component in the range 0-1f.
|
||||
*/
|
||||
public float getBlueAsFloat() {
|
||||
return getBlue() / 255f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the alpha component in the range 0-1f.
|
||||
*/
|
||||
public float getAlphaAsFloat() {
|
||||
return getAlpha() / 255f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the RGB value representing this color
|
||||
* (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue).
|
||||
* @return the RGB value of the color
|
||||
*/
|
||||
public int getRGB() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Vec3 asVector() {
|
||||
return new Vec3(getRedAsFloat(), getGreenAsFloat(), getBlueAsFloat());
|
||||
}
|
||||
|
||||
public Vector3f asVectorF() {
|
||||
return new Vector3f(getRedAsFloat(), getGreenAsFloat(), getBlueAsFloat());
|
||||
}
|
||||
|
||||
public Color setRed(int r) {
|
||||
return ensureMutable().setRedUnchecked(r);
|
||||
}
|
||||
|
||||
public Color setGreen(int g) {
|
||||
return ensureMutable().setGreenUnchecked(g);
|
||||
}
|
||||
|
||||
public Color setBlue(int b) {
|
||||
return ensureMutable().setBlueUnchecked(b);
|
||||
}
|
||||
|
||||
public Color setAlpha(int a) {
|
||||
return ensureMutable().setAlphaUnchecked(a);
|
||||
}
|
||||
|
||||
public Color setRed(float r) {
|
||||
return ensureMutable().setRedUnchecked((int) (0xff * Mth.clamp(r, 0, 1)));
|
||||
}
|
||||
|
||||
public Color setGreen(float g) {
|
||||
return ensureMutable().setGreenUnchecked((int) (0xff * Mth.clamp(g, 0, 1)));
|
||||
}
|
||||
|
||||
public Color setBlue(float b) {
|
||||
return ensureMutable().setBlueUnchecked((int) (0xff * Mth.clamp(b, 0, 1)));
|
||||
}
|
||||
|
||||
public Color setAlpha(float a) {
|
||||
return ensureMutable().setAlphaUnchecked((int) (0xff * Mth.clamp(a, 0, 1)));
|
||||
}
|
||||
|
||||
public Color scaleAlpha(float factor) {
|
||||
return ensureMutable().setAlphaUnchecked((int) (getAlpha() * Mth.clamp(factor, 0, 1)));
|
||||
}
|
||||
|
||||
public Color mixWith(Color other, float weight) {
|
||||
return ensureMutable()
|
||||
.setRedUnchecked((int) (getRed() + (other.getRed() - getRed()) * weight))
|
||||
.setGreenUnchecked((int) (getGreen() + (other.getGreen() - getGreen()) * weight))
|
||||
.setBlueUnchecked((int) (getBlue() + (other.getBlue() - getBlue()) * weight))
|
||||
.setAlphaUnchecked((int) (getAlpha() + (other.getAlpha() - getAlpha()) * weight));
|
||||
}
|
||||
|
||||
public Color darker() {
|
||||
int a = getAlpha();
|
||||
return ensureMutable().mixWith(BLACK, .25f).setAlphaUnchecked(a);
|
||||
}
|
||||
|
||||
public Color brighter() {
|
||||
int a = getAlpha();
|
||||
return ensureMutable().mixWith(WHITE, .25f).setAlphaUnchecked(a);
|
||||
}
|
||||
|
||||
public Color setValue(int value) {
|
||||
return ensureMutable().setValueUnchecked(value);
|
||||
}
|
||||
|
||||
public Color modifyValue(UnaryOperator<Integer> function) {
|
||||
int newValue = function.apply(value);
|
||||
if (newValue == value)
|
||||
return this;
|
||||
|
||||
return ensureMutable().setValueUnchecked(newValue);
|
||||
}
|
||||
|
||||
// ********* //
|
||||
|
||||
protected Color ensureMutable() {
|
||||
if (this.mutable)
|
||||
return this;
|
||||
|
||||
return new Color(this.value);
|
||||
}
|
||||
|
||||
protected Color setRedUnchecked(int r) {
|
||||
this.value = (this.value & 0xff_00ffff) | ((r & 0xff) << 16);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected Color setGreenUnchecked(int g) {
|
||||
this.value = (this.value & 0xff_ff00ff) | ((g & 0xff) << 8);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected Color setBlueUnchecked(int b) {
|
||||
this.value = (this.value & 0xff_ffff00) | ((b & 0xff) << 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected Color setAlphaUnchecked(int a) {
|
||||
this.value = (this.value & 0x00_ffffff) | ((a & 0xff) << 24);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected Color setValueUnchecked(int value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
// ********* //
|
||||
|
||||
public static Color mixColors(@Nonnull Color c1, @Nonnull Color c2, float w) {
|
||||
return new Color(
|
||||
(int) (c1.getRed() + (c2.getRed() - c1.getRed()) * w),
|
||||
(int) (c1.getGreen() + (c2.getGreen() - c1.getGreen()) * w),
|
||||
(int) (c1.getBlue() + (c2.getBlue() - c1.getBlue()) * w),
|
||||
(int) (c1.getAlpha() + (c2.getAlpha() - c1.getAlpha()) * w)
|
||||
);
|
||||
}
|
||||
|
||||
public static int mixColors(int color1, int color2, float w) {
|
||||
int a1 = (color1 >> 24);
|
||||
int r1 = (color1 >> 16) & 0xFF;
|
||||
int g1 = (color1 >> 8) & 0xFF;
|
||||
int b1 = color1 & 0xFF;
|
||||
int a2 = (color2 >> 24);
|
||||
int r2 = (color2 >> 16) & 0xFF;
|
||||
int g2 = (color2 >> 8) & 0xFF;
|
||||
int b2 = color2 & 0xFF;
|
||||
|
||||
return
|
||||
((int) (a1 + (a2 - a1) * w) << 24) +
|
||||
((int) (r1 + (r2 - r1) * w) << 16) +
|
||||
((int) (g1 + (g2 - g1) * w) << 8) +
|
||||
((int) (b1 + (b2 - b1) * w) << 0);
|
||||
}
|
||||
|
||||
public static Color rainbowColor(int timeStep) {
|
||||
int localTimeStep = Math.abs(timeStep) % 1536;
|
||||
int timeStepInPhase = localTimeStep % 256;
|
||||
int phaseBlue = localTimeStep / 256;
|
||||
int red = colorInPhase(phaseBlue + 4, timeStepInPhase);
|
||||
int green = colorInPhase(phaseBlue + 2, timeStepInPhase);
|
||||
int blue = colorInPhase(phaseBlue, timeStepInPhase);
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
private static int colorInPhase(int phase, int progress) {
|
||||
phase = phase % 6;
|
||||
if (phase <= 1)
|
||||
return 0;
|
||||
if (phase == 2)
|
||||
return progress;
|
||||
if (phase <= 4)
|
||||
return 255;
|
||||
else
|
||||
return 255 - progress;
|
||||
}
|
||||
|
||||
public static Color generateFromLong(long l) {
|
||||
return rainbowColor(Hashing.crc32().hashLong(l).asInt())
|
||||
.mixWith(WHITE, 0.5f);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package com.jozufozu.flywheel.util;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
|
||||
public class UnsafeBlockFormatReader implements ModelReader {
|
||||
|
||||
private final int vertexCount;
|
||||
private final int stride;
|
||||
private final long base;
|
||||
|
||||
public UnsafeBlockFormatReader(BufferBuilder builder) {
|
||||
VertexFormat vertexFormat = builder.getVertexFormat();
|
||||
Pair<BufferBuilder.DrawState, ByteBuffer> data = builder.popNextBuffer();
|
||||
this.base = MemoryUtil.memAddress(data.getSecond());
|
||||
this.vertexCount = data.getFirst().vertexCount();
|
||||
this.stride = vertexFormat.getVertexSize();
|
||||
}
|
||||
|
||||
private long ptr(long index) {
|
||||
return base + index * stride;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return vertexCount == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getX(int index) {
|
||||
return MemoryUtil.memGetFloat(ptr(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getY(int index) {
|
||||
return MemoryUtil.memGetFloat(ptr(index) + 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getZ(int index) {
|
||||
return MemoryUtil.memGetFloat(ptr(index) + 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getR(int index) {
|
||||
return MemoryUtil.memGetByte(ptr(index) + 12);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getG(int index) {
|
||||
return MemoryUtil.memGetByte(ptr(index) + 13);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getB(int index) {
|
||||
return MemoryUtil.memGetByte(ptr(index) + 14);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getA(int index) {
|
||||
return MemoryUtil.memGetByte(ptr(index) + 15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getU(int index) {
|
||||
return MemoryUtil.memGetFloat(ptr(index) + 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getV(int index) {
|
||||
return MemoryUtil.memGetFloat(ptr(index) + 20);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLight(int index) {
|
||||
return MemoryUtil.memGetInt(ptr(index) + 24);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getNX(int index) {
|
||||
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 28));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getNY(int index) {
|
||||
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 29));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getNZ(int index) {
|
||||
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 30));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVertexCount() {
|
||||
return vertexCount;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
struct Vertex {
|
||||
vec3 pos;
|
||||
vec3 normal;
|
||||
vec2 texCoords;
|
||||
vec3 normal;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue