Refactored ShapeRenderProgram into SRP and WorldRenderProgram
- ShapeRP now handles positions, textures and color multipliers only, normals, view transforms and light calculations are now in WorldRP - Renamed WorldRenderer to ShapeRenderHelper - Moved normal computation to WorldRP code from Face - No default ShapeRP exists, methods in Faces and Shapes now explicitly require a program - Removed unnecessary glBind from texture loader - Refactored and renamed GLSL code - Added a view bobbing test, enable with I_WANT_TO_THROW_UP in LayerWorld
This commit is contained in:
parent
16205909ed
commit
8aa0bb8aa0
@ -21,8 +21,8 @@ import ru.windcorp.optica.Proxy;
|
|||||||
import ru.windcorp.optica.client.graphics.GUI;
|
import ru.windcorp.optica.client.graphics.GUI;
|
||||||
import ru.windcorp.optica.client.graphics.backend.GraphicsBackend;
|
import ru.windcorp.optica.client.graphics.backend.GraphicsBackend;
|
||||||
import ru.windcorp.optica.client.graphics.backend.RenderTaskQueue;
|
import ru.windcorp.optica.client.graphics.backend.RenderTaskQueue;
|
||||||
import ru.windcorp.optica.client.graphics.model.ShapeRenderProgram;
|
|
||||||
import ru.windcorp.optica.client.graphics.world.LayerWorld;
|
import ru.windcorp.optica.client.graphics.world.LayerWorld;
|
||||||
|
import ru.windcorp.optica.client.graphics.world.WorldRenderProgram;
|
||||||
|
|
||||||
public class ClientProxy implements Proxy {
|
public class ClientProxy implements Proxy {
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ public class ClientProxy implements Proxy {
|
|||||||
public void initialize() {
|
public void initialize() {
|
||||||
GraphicsBackend.initialize();
|
GraphicsBackend.initialize();
|
||||||
try {
|
try {
|
||||||
RenderTaskQueue.waitAndInvoke(ShapeRenderProgram::init);
|
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package ru.windcorp.optica.client.graphics.model;
|
package ru.windcorp.optica.client.graphics.model;
|
||||||
|
|
||||||
import glm.mat._4.Mat4;
|
import glm.mat._4.Mat4;
|
||||||
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
|
|
||||||
|
|
||||||
public class EmptyModel extends Model {
|
public class EmptyModel extends Model {
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ public class EmptyModel extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(WorldRenderer renderer) {
|
public void render(ShapeRenderHelper helper) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.ShortBuffer;
|
import java.nio.ShortBuffer;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
|
||||||
import ru.windcorp.optica.client.graphics.texture.Texture;
|
import ru.windcorp.optica.client.graphics.texture.Texture;
|
||||||
|
|
||||||
public class Face {
|
public class Face {
|
||||||
@ -64,39 +63,6 @@ public class Face {
|
|||||||
checkIndices();
|
checkIndices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void computeNormals() {
|
|
||||||
Vec3 a = new Vec3();
|
|
||||||
Vec3 b = new Vec3();
|
|
||||||
Vec3 c = new Vec3();
|
|
||||||
Vec3 normal = new Vec3();
|
|
||||||
|
|
||||||
for (int i = 0; i < getIndexCount(); i += 3) {
|
|
||||||
int indexA = getIndex(i + 0);
|
|
||||||
int indexB = getIndex(i + 1);
|
|
||||||
int indexC = getIndex(i + 2);
|
|
||||||
|
|
||||||
loadVertexPosition(indexA, a);
|
|
||||||
loadVertexPosition(indexB, b);
|
|
||||||
loadVertexPosition(indexC, c);
|
|
||||||
|
|
||||||
computeOneNormal(a, b, c, normal);
|
|
||||||
|
|
||||||
saveVertexNormal(indexA, normal);
|
|
||||||
saveVertexNormal(indexB, normal);
|
|
||||||
saveVertexNormal(indexC, normal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void computeOneNormal(
|
|
||||||
Vec3 a, Vec3 b, Vec3 c,
|
|
||||||
Vec3 normal
|
|
||||||
) {
|
|
||||||
b.sub(a);
|
|
||||||
c.sub(a);
|
|
||||||
b.cross(c, normal);
|
|
||||||
normal.normalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkVertices() {
|
private void checkVertices() {
|
||||||
if (vertices.remaining() % getBytesPerVertex() != 0) {
|
if (vertices.remaining() % getBytesPerVertex() != 0) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
@ -143,6 +109,12 @@ public class Face {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void markForVertexUpdate() {
|
||||||
|
if (shape != null) checkVertices();
|
||||||
|
markShapeForReassembly();
|
||||||
|
verticesUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
boolean needsVerticesUpdate() {
|
boolean needsVerticesUpdate() {
|
||||||
return verticesUpdated;
|
return verticesUpdated;
|
||||||
}
|
}
|
||||||
@ -180,37 +152,9 @@ public class Face {
|
|||||||
return vertices;
|
return vertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadVertexPosition(int index, Vec3 result) {
|
|
||||||
int offset = vertices.position() + index * getBytesPerVertex();
|
|
||||||
|
|
||||||
result.set(
|
|
||||||
vertices.getFloat(offset + 0 * Float.BYTES),
|
|
||||||
vertices.getFloat(offset + 1 * Float.BYTES),
|
|
||||||
vertices.getFloat(offset + 2 * Float.BYTES)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveVertexNormal(int index, Vec3 normal) {
|
|
||||||
int offset = vertices.position() + index * getBytesPerVertex() + (
|
|
||||||
3 * Float.BYTES +
|
|
||||||
3 * Float.BYTES +
|
|
||||||
2 * Float.BYTES
|
|
||||||
);
|
|
||||||
|
|
||||||
vertices.putFloat(offset + 0 * Float.BYTES, normal.x);
|
|
||||||
vertices.putFloat(offset + 1 * Float.BYTES, normal.y);
|
|
||||||
vertices.putFloat(offset + 2 * Float.BYTES, normal.z);
|
|
||||||
|
|
||||||
verticesUpdated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Face setVertices(ByteBuffer vertices) {
|
public Face setVertices(ByteBuffer vertices) {
|
||||||
this.vertices = Objects.requireNonNull(vertices, "vertices");
|
this.vertices = Objects.requireNonNull(vertices, "vertices");
|
||||||
markShapeForReassembly();
|
markForVertexUpdate();
|
||||||
this.verticesUpdated = true;
|
|
||||||
|
|
||||||
if (shape != null) checkVertices();
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,13 +30,14 @@ public class Faces {
|
|||||||
private Faces() {}
|
private Faces() {}
|
||||||
|
|
||||||
public static Face createRectangle(
|
public static Face createRectangle(
|
||||||
|
ShapeRenderProgram program,
|
||||||
Texture texture,
|
Texture texture,
|
||||||
Vec3 colorMultiplier,
|
Vec3 colorMultiplier,
|
||||||
Vec3 origin,
|
Vec3 origin,
|
||||||
Vec3 width,
|
Vec3 width,
|
||||||
Vec3 height
|
Vec3 height
|
||||||
) {
|
) {
|
||||||
VertexBuilder builder = new VertexBuilder();
|
VertexBuilder builder = program.getVertexBuilder();
|
||||||
|
|
||||||
Vec3 pos = new Vec3();
|
Vec3 pos = new Vec3();
|
||||||
Vec2 texCoords = new Vec2();
|
Vec2 texCoords = new Vec2();
|
||||||
@ -70,6 +71,7 @@ public class Faces {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Face createBlockFace(
|
public static Face createBlockFace(
|
||||||
|
ShapeRenderProgram program,
|
||||||
Texture texture,
|
Texture texture,
|
||||||
Vec3 colorMultiplier,
|
Vec3 colorMultiplier,
|
||||||
Vec3 blockCenter,
|
Vec3 blockCenter,
|
||||||
@ -78,6 +80,7 @@ public class Faces {
|
|||||||
switch (face) {
|
switch (face) {
|
||||||
case TOP:
|
case TOP:
|
||||||
return createRectangle(
|
return createRectangle(
|
||||||
|
program,
|
||||||
texture, colorMultiplier,
|
texture, colorMultiplier,
|
||||||
blockCenter.add(-0.5f, +0.5f, +0.5f),
|
blockCenter.add(-0.5f, +0.5f, +0.5f),
|
||||||
new Vec3( 0, -1, 0),
|
new Vec3( 0, -1, 0),
|
||||||
@ -85,6 +88,7 @@ public class Faces {
|
|||||||
);
|
);
|
||||||
case BOTTOM:
|
case BOTTOM:
|
||||||
return createRectangle(
|
return createRectangle(
|
||||||
|
program,
|
||||||
texture, colorMultiplier,
|
texture, colorMultiplier,
|
||||||
blockCenter.add(-0.5f, -0.5f, -0.5f),
|
blockCenter.add(-0.5f, -0.5f, -0.5f),
|
||||||
new Vec3( 0, +1, 0),
|
new Vec3( 0, +1, 0),
|
||||||
@ -92,6 +96,7 @@ public class Faces {
|
|||||||
);
|
);
|
||||||
case NORTH:
|
case NORTH:
|
||||||
return createRectangle(
|
return createRectangle(
|
||||||
|
program,
|
||||||
texture, colorMultiplier,
|
texture, colorMultiplier,
|
||||||
blockCenter.add(+0.5f, -0.5f, -0.5f),
|
blockCenter.add(+0.5f, -0.5f, -0.5f),
|
||||||
new Vec3( 0, +1, 0),
|
new Vec3( 0, +1, 0),
|
||||||
@ -99,6 +104,7 @@ public class Faces {
|
|||||||
);
|
);
|
||||||
case SOUTH:
|
case SOUTH:
|
||||||
return createRectangle(
|
return createRectangle(
|
||||||
|
program,
|
||||||
texture, colorMultiplier,
|
texture, colorMultiplier,
|
||||||
blockCenter.add(-0.5f, +0.5f, -0.5f),
|
blockCenter.add(-0.5f, +0.5f, -0.5f),
|
||||||
new Vec3( 0, -1, 0),
|
new Vec3( 0, -1, 0),
|
||||||
@ -106,6 +112,7 @@ public class Faces {
|
|||||||
);
|
);
|
||||||
case EAST:
|
case EAST:
|
||||||
return createRectangle(
|
return createRectangle(
|
||||||
|
program,
|
||||||
texture, colorMultiplier,
|
texture, colorMultiplier,
|
||||||
blockCenter.add(-0.5f, -0.5f, -0.5f),
|
blockCenter.add(-0.5f, -0.5f, -0.5f),
|
||||||
new Vec3(+1, 0, 0),
|
new Vec3(+1, 0, 0),
|
||||||
@ -113,6 +120,7 @@ public class Faces {
|
|||||||
);
|
);
|
||||||
case WEST:
|
case WEST:
|
||||||
return createRectangle(
|
return createRectangle(
|
||||||
|
program,
|
||||||
texture, colorMultiplier,
|
texture, colorMultiplier,
|
||||||
blockCenter.add(+0.5f, +0.5f, -0.5f),
|
blockCenter.add(+0.5f, +0.5f, -0.5f),
|
||||||
new Vec3(-1, 0, 0),
|
new Vec3(-1, 0, 0),
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package ru.windcorp.optica.client.graphics.model;
|
package ru.windcorp.optica.client.graphics.model;
|
||||||
|
|
||||||
import glm.mat._4.Mat4;
|
import glm.mat._4.Mat4;
|
||||||
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
|
|
||||||
|
|
||||||
public abstract class Model implements WorldRenderable {
|
public abstract class Model implements WorldRenderable {
|
||||||
|
|
||||||
@ -31,16 +30,16 @@ public abstract class Model implements WorldRenderable {
|
|||||||
protected abstract Mat4 getTransform(int partIndex);
|
protected abstract Mat4 getTransform(int partIndex);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(WorldRenderer renderer) {
|
public void render(ShapeRenderHelper helper) {
|
||||||
for (int i = 0; i < parts.length; ++i) {
|
for (int i = 0; i < parts.length; ++i) {
|
||||||
WorldRenderable part = parts[i];
|
WorldRenderable part = parts[i];
|
||||||
Mat4 transform = getTransform(i);
|
Mat4 transform = getTransform(i);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
renderer.pushWorldTransform().mul(transform);
|
helper.pushWorldTransform().mul(transform);
|
||||||
part.render(renderer);
|
part.render(helper);
|
||||||
} finally {
|
} finally {
|
||||||
renderer.popWorldTransform();
|
helper.popWorldTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import org.lwjgl.BufferUtils;
|
|||||||
|
|
||||||
import ru.windcorp.optica.client.graphics.backend.Usage;
|
import ru.windcorp.optica.client.graphics.backend.Usage;
|
||||||
import ru.windcorp.optica.client.graphics.backend.VertexBufferObject;
|
import ru.windcorp.optica.client.graphics.backend.VertexBufferObject;
|
||||||
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
|
|
||||||
|
|
||||||
public class Shape implements WorldRenderable {
|
public class Shape implements WorldRenderable {
|
||||||
|
|
||||||
@ -48,18 +47,14 @@ public class Shape implements WorldRenderable {
|
|||||||
this.usage = usage;
|
this.usage = usage;
|
||||||
|
|
||||||
configureFaces();
|
configureFaces();
|
||||||
|
program.preprocess(this);
|
||||||
|
|
||||||
assembleBuffers();
|
assembleBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Shape(Usage usage, Face... faces) {
|
|
||||||
this(usage, ShapeRenderProgram.getDefault(), faces);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureFaces() {
|
private void configureFaces() {
|
||||||
for (Face face : faces) {
|
for (Face face : faces) {
|
||||||
face.setShape(this);
|
face.setShape(this);
|
||||||
face.computeNormals();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,12 +148,12 @@ public class Shape implements WorldRenderable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(WorldRenderer renderer) {
|
public void render(ShapeRenderHelper helper) {
|
||||||
if (!initialized) initialize();
|
if (!initialized) initialize();
|
||||||
if (needsAssembly) assembleBuffers();
|
if (needsAssembly) assembleBuffers();
|
||||||
if (needsVBOUpdate) updateVBO();
|
if (needsVBOUpdate) updateVBO();
|
||||||
|
|
||||||
program.render(renderer, this);
|
program.render(helper, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Optica
|
||||||
|
* Copyright (C) 2020 Wind Corporation
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*******************************************************************************/
|
||||||
|
package ru.windcorp.optica.client.graphics.model;
|
||||||
|
|
||||||
|
import glm.mat._4.Mat4;
|
||||||
|
import ru.windcorp.optica.common.util.StashingStack;
|
||||||
|
|
||||||
|
public class ShapeRenderHelper {
|
||||||
|
|
||||||
|
protected static final int TRANSFORM_STACK_SIZE = 64;
|
||||||
|
|
||||||
|
private final StashingStack<Mat4> transformStack = new StashingStack<>(
|
||||||
|
TRANSFORM_STACK_SIZE, Mat4::new
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
transformStack.push().identity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mat4 pushWorldTransform() {
|
||||||
|
Mat4 previous = transformStack.getHead();
|
||||||
|
return transformStack.push().set(previous);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void popWorldTransform() {
|
||||||
|
transformStack.removeHead();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mat4 getWorldTransform() {
|
||||||
|
return transformStack.getHead();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mat4 getFinalTransform() {
|
||||||
|
return getWorldTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
transformStack.removeAll();
|
||||||
|
transformStack.push().identity();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -32,33 +32,16 @@ import ru.windcorp.optica.client.graphics.backend.VertexBufferObject;
|
|||||||
import ru.windcorp.optica.client.graphics.backend.VertexBufferObject.BindTarget;
|
import ru.windcorp.optica.client.graphics.backend.VertexBufferObject.BindTarget;
|
||||||
import ru.windcorp.optica.client.graphics.backend.shaders.CombinedShader;
|
import ru.windcorp.optica.client.graphics.backend.shaders.CombinedShader;
|
||||||
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
|
import ru.windcorp.optica.client.graphics.backend.shaders.Program;
|
||||||
import ru.windcorp.optica.client.graphics.backend.shaders.attributes.AttributeVertexArray;
|
import ru.windcorp.optica.client.graphics.backend.shaders.attributes.*;
|
||||||
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform1Int;
|
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.*;
|
||||||
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform2Float;
|
|
||||||
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform4Matrix;
|
|
||||||
import ru.windcorp.optica.client.graphics.texture.Sprite;
|
import ru.windcorp.optica.client.graphics.texture.Sprite;
|
||||||
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
|
|
||||||
|
|
||||||
public class ShapeRenderProgram extends Program {
|
public class ShapeRenderProgram extends Program {
|
||||||
|
|
||||||
private static ShapeRenderProgram def = null;
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
def = new ShapeRenderProgram(
|
|
||||||
new String[] {"WorldDefault.vertex.glsl"},
|
|
||||||
new String[] {"WorldDefault.fragment.glsl"}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ShapeRenderProgram getDefault() {
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int DEFAULT_BYTES_PER_VERTEX =
|
private static final int DEFAULT_BYTES_PER_VERTEX =
|
||||||
3 * Float.BYTES + // Position
|
3 * Float.BYTES + // Position
|
||||||
3 * Float.BYTES + // Color multiplier
|
3 * Float.BYTES + // Color multiplier
|
||||||
2 * Float.BYTES + // Texture coordinates
|
2 * Float.BYTES; // Texture coordinates
|
||||||
3 * Float.BYTES; // Normals
|
|
||||||
|
|
||||||
private static final String SHAPE_VERTEX_SHADER_RESOURCE =
|
private static final String SHAPE_VERTEX_SHADER_RESOURCE =
|
||||||
"Shape.vertex.glsl";
|
"Shape.vertex.glsl";
|
||||||
@ -67,24 +50,20 @@ public class ShapeRenderProgram extends Program {
|
|||||||
|
|
||||||
private static final String
|
private static final String
|
||||||
FINAL_TRANSFORM_UNIFORM_NAME = "finalTransform",
|
FINAL_TRANSFORM_UNIFORM_NAME = "finalTransform",
|
||||||
WORLD_TRANSFORM_UNIFORM_NAME = "worldTransform",
|
|
||||||
POSITIONS_ATTRIBUTE_NAME = "inputPositions",
|
POSITIONS_ATTRIBUTE_NAME = "inputPositions",
|
||||||
COLOR_MULTIPLER_ATTRIBUTE_NAME = "inputColorMultiplier",
|
COLOR_MULTIPLER_ATTRIBUTE_NAME = "inputColorMultiplier",
|
||||||
TEXTURE_COORDS_ATTRIBUTE_NAME = "inputTextureCoords",
|
TEXTURE_COORDS_ATTRIBUTE_NAME = "inputTextureCoords",
|
||||||
TEXTURE_SLOT_UNIFORM_NAME = "textureSlot",
|
TEXTURE_SLOT_UNIFORM_NAME = "textureSlot",
|
||||||
TEXTURE_START_UNIFORM_NAME = "textureStart",
|
TEXTURE_START_UNIFORM_NAME = "textureStart",
|
||||||
TEXTURE_SIZE_UNIFORM_NAME = "textureSize",
|
TEXTURE_SIZE_UNIFORM_NAME = "textureSize";
|
||||||
NORMALS_ATTRIBUTE_NAME = "inputNormals";
|
|
||||||
|
|
||||||
private final Uniform4Matrix finalTransformUniform;
|
private final Uniform4Matrix finalTransformUniform;
|
||||||
private final Uniform4Matrix worldTransformUniform;
|
|
||||||
private final AttributeVertexArray positionsAttribute;
|
private final AttributeVertexArray positionsAttribute;
|
||||||
private final AttributeVertexArray colorsAttribute;
|
private final AttributeVertexArray colorsAttribute;
|
||||||
private final AttributeVertexArray textureCoordsAttribute;
|
private final AttributeVertexArray textureCoordsAttribute;
|
||||||
private final Uniform1Int textureSlotUniform;
|
private final Uniform1Int textureSlotUniform;
|
||||||
private final Uniform2Float textureStartUniform;
|
private final Uniform2Float textureStartUniform;
|
||||||
private final Uniform2Float textureSizeUniform;
|
private final Uniform2Float textureSizeUniform;
|
||||||
private final AttributeVertexArray normalsAttribute;
|
|
||||||
|
|
||||||
public ShapeRenderProgram(
|
public ShapeRenderProgram(
|
||||||
String[] vertexShaderResources,
|
String[] vertexShaderResources,
|
||||||
@ -102,9 +81,6 @@ public class ShapeRenderProgram extends Program {
|
|||||||
this.finalTransformUniform = getUniform(FINAL_TRANSFORM_UNIFORM_NAME)
|
this.finalTransformUniform = getUniform(FINAL_TRANSFORM_UNIFORM_NAME)
|
||||||
.as4Matrix();
|
.as4Matrix();
|
||||||
|
|
||||||
this.worldTransformUniform = getUniform(WORLD_TRANSFORM_UNIFORM_NAME)
|
|
||||||
.as4Matrix();
|
|
||||||
|
|
||||||
this.positionsAttribute =
|
this.positionsAttribute =
|
||||||
getAttribute(POSITIONS_ATTRIBUTE_NAME).asVertexArray();
|
getAttribute(POSITIONS_ATTRIBUTE_NAME).asVertexArray();
|
||||||
|
|
||||||
@ -122,9 +98,6 @@ public class ShapeRenderProgram extends Program {
|
|||||||
|
|
||||||
this.textureSizeUniform = getUniform(TEXTURE_SIZE_UNIFORM_NAME)
|
this.textureSizeUniform = getUniform(TEXTURE_SIZE_UNIFORM_NAME)
|
||||||
.as2Float();
|
.as2Float();
|
||||||
|
|
||||||
this.normalsAttribute = getAttribute(NORMALS_ATTRIBUTE_NAME)
|
|
||||||
.asVertexArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String[] attachVertexShader(String[] others) {
|
private static String[] attachVertexShader(String[] others) {
|
||||||
@ -136,36 +109,39 @@ public class ShapeRenderProgram extends Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void render(
|
public void render(
|
||||||
WorldRenderer renderer,
|
ShapeRenderHelper helper,
|
||||||
Shape shape
|
Shape shape
|
||||||
) {
|
) {
|
||||||
use();
|
use();
|
||||||
configure(renderer);
|
configure(helper);
|
||||||
|
|
||||||
bindVertices(shape.getVerticesVbo());
|
bindVertices(shape.getVerticesVbo());
|
||||||
bindIndices(shape.getIndicesVbo());
|
bindIndices(shape.getIndicesVbo());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
positionsAttribute.enable();
|
enableAttributes();
|
||||||
colorsAttribute.enable();
|
|
||||||
textureCoordsAttribute.enable();
|
|
||||||
normalsAttribute.enable();
|
|
||||||
|
|
||||||
for (Face face : shape.getFaces()) {
|
for (Face face : shape.getFaces()) {
|
||||||
renderFace(face);
|
renderFace(face);
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
positionsAttribute.disable();
|
disableAttributes();
|
||||||
colorsAttribute.disable();
|
|
||||||
textureCoordsAttribute.disable();
|
|
||||||
normalsAttribute.disable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void configure(WorldRenderer renderer) {
|
protected void enableAttributes() {
|
||||||
finalTransformUniform.set(renderer.getFinalTransform());
|
positionsAttribute.enable();
|
||||||
worldTransformUniform.set(renderer.getWorldTransform());
|
colorsAttribute.enable();
|
||||||
|
textureCoordsAttribute.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void disableAttributes() {
|
||||||
|
positionsAttribute.disable();
|
||||||
|
colorsAttribute.disable();
|
||||||
|
textureCoordsAttribute.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void configure(ShapeRenderHelper helper) {
|
||||||
|
finalTransformUniform.set(helper.getFinalTransform());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int bindVertices(VertexBufferObject vertices) {
|
protected int bindVertices(VertexBufferObject vertices) {
|
||||||
@ -190,12 +166,6 @@ public class ShapeRenderProgram extends Program {
|
|||||||
);
|
);
|
||||||
offset += 2 * Float.BYTES;
|
offset += 2 * Float.BYTES;
|
||||||
|
|
||||||
normalsAttribute.set(
|
|
||||||
3, GL11.GL_FLOAT, false, vertexStride, vertices,
|
|
||||||
offset
|
|
||||||
);
|
|
||||||
offset += 3 * Float.BYTES;
|
|
||||||
|
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +194,31 @@ public class ShapeRenderProgram extends Program {
|
|||||||
return DEFAULT_BYTES_PER_VERTEX;
|
return DEFAULT_BYTES_PER_VERTEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class VertexBuilder {
|
public void preprocess(Shape shape) {
|
||||||
|
// To be overridden
|
||||||
|
}
|
||||||
|
|
||||||
|
public VertexBuilder getVertexBuilder() {
|
||||||
|
return new SRPVertexBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface VertexBuilder {
|
||||||
|
VertexBuilder addVertex(
|
||||||
|
float x, float y, float z,
|
||||||
|
float r, float g, float b,
|
||||||
|
float tx, float ty
|
||||||
|
);
|
||||||
|
|
||||||
|
VertexBuilder addVertex(
|
||||||
|
Vec3 position,
|
||||||
|
Vec3 colorMultiplier,
|
||||||
|
Vec2 textureCoords
|
||||||
|
);
|
||||||
|
|
||||||
|
ByteBuffer assemble();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SRPVertexBuilder implements VertexBuilder {
|
||||||
|
|
||||||
private static class Vertex {
|
private static class Vertex {
|
||||||
final Vec3 position;
|
final Vec3 position;
|
||||||
@ -282,10 +276,7 @@ public class ShapeRenderProgram extends Program {
|
|||||||
.putFloat(v.colorMultiplier.y)
|
.putFloat(v.colorMultiplier.y)
|
||||||
.putFloat(v.colorMultiplier.z)
|
.putFloat(v.colorMultiplier.z)
|
||||||
.putFloat(v.textureCoords.x)
|
.putFloat(v.textureCoords.x)
|
||||||
.putFloat(v.textureCoords.y)
|
.putFloat(v.textureCoords.y);
|
||||||
.putFloat(Float.NaN)
|
|
||||||
.putFloat(Float.NaN)
|
|
||||||
.putFloat(Float.NaN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.flip();
|
result.flip();
|
||||||
|
@ -24,6 +24,8 @@ import ru.windcorp.optica.client.graphics.texture.Texture;
|
|||||||
public class Shapes {
|
public class Shapes {
|
||||||
|
|
||||||
public static Shape createParallelepiped( // Try saying that 10 times fast
|
public static Shape createParallelepiped( // Try saying that 10 times fast
|
||||||
|
ShapeRenderProgram program,
|
||||||
|
|
||||||
Vec3 origin,
|
Vec3 origin,
|
||||||
|
|
||||||
Vec3 width,
|
Vec3 width,
|
||||||
@ -44,6 +46,7 @@ public class Shapes {
|
|||||||
Vec3 faceWidth = new Vec3();
|
Vec3 faceWidth = new Vec3();
|
||||||
|
|
||||||
Face top = Faces.createRectangle(
|
Face top = Faces.createRectangle(
|
||||||
|
program,
|
||||||
topTexture, colorMultiplier,
|
topTexture, colorMultiplier,
|
||||||
faceOrigin.set(origin).add(height).add(width),
|
faceOrigin.set(origin).add(height).add(width),
|
||||||
faceWidth.set(width).negate(),
|
faceWidth.set(width).negate(),
|
||||||
@ -51,6 +54,7 @@ public class Shapes {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Face bottom = Faces.createRectangle(
|
Face bottom = Faces.createRectangle(
|
||||||
|
program,
|
||||||
bottomTexture, colorMultiplier,
|
bottomTexture, colorMultiplier,
|
||||||
origin,
|
origin,
|
||||||
width,
|
width,
|
||||||
@ -58,6 +62,7 @@ public class Shapes {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Face north = Faces.createRectangle(
|
Face north = Faces.createRectangle(
|
||||||
|
program,
|
||||||
northTexture, colorMultiplier,
|
northTexture, colorMultiplier,
|
||||||
faceOrigin.set(origin).add(depth),
|
faceOrigin.set(origin).add(depth),
|
||||||
width,
|
width,
|
||||||
@ -65,6 +70,7 @@ public class Shapes {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Face south = Faces.createRectangle(
|
Face south = Faces.createRectangle(
|
||||||
|
program,
|
||||||
southTexture, colorMultiplier,
|
southTexture, colorMultiplier,
|
||||||
faceOrigin.set(origin).add(width),
|
faceOrigin.set(origin).add(width),
|
||||||
faceWidth.set(width).negate(),
|
faceWidth.set(width).negate(),
|
||||||
@ -72,6 +78,7 @@ public class Shapes {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Face east = Faces.createRectangle(
|
Face east = Faces.createRectangle(
|
||||||
|
program,
|
||||||
eastTexture, colorMultiplier,
|
eastTexture, colorMultiplier,
|
||||||
origin,
|
origin,
|
||||||
depth,
|
depth,
|
||||||
@ -79,6 +86,7 @@ public class Shapes {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Face west = Faces.createRectangle(
|
Face west = Faces.createRectangle(
|
||||||
|
program,
|
||||||
westTexture, colorMultiplier,
|
westTexture, colorMultiplier,
|
||||||
faceOrigin.set(origin).add(width).add(depth),
|
faceOrigin.set(origin).add(width).add(depth),
|
||||||
faceWidth.set(depth).negate(),
|
faceWidth.set(depth).negate(),
|
||||||
@ -87,6 +95,7 @@ public class Shapes {
|
|||||||
|
|
||||||
Shape result = new Shape(
|
Shape result = new Shape(
|
||||||
Usage.STATIC,
|
Usage.STATIC,
|
||||||
|
program,
|
||||||
top, bottom, north, south, east, west
|
top, bottom, north, south, east, west
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -95,6 +104,8 @@ public class Shapes {
|
|||||||
|
|
||||||
public static class PppBuilder {
|
public static class PppBuilder {
|
||||||
|
|
||||||
|
private final ShapeRenderProgram program;
|
||||||
|
|
||||||
private final Vec3 origin = new Vec3(-0.5f, -0.5f, -0.5f);
|
private final Vec3 origin = new Vec3(-0.5f, -0.5f, -0.5f);
|
||||||
|
|
||||||
private final Vec3 depth = new Vec3(1, 0, 0);
|
private final Vec3 depth = new Vec3(1, 0, 0);
|
||||||
@ -111,6 +122,7 @@ public class Shapes {
|
|||||||
private final Texture westTexture;
|
private final Texture westTexture;
|
||||||
|
|
||||||
public PppBuilder(
|
public PppBuilder(
|
||||||
|
ShapeRenderProgram program,
|
||||||
Texture top,
|
Texture top,
|
||||||
Texture bottom,
|
Texture bottom,
|
||||||
Texture north,
|
Texture north,
|
||||||
@ -118,6 +130,7 @@ public class Shapes {
|
|||||||
Texture east,
|
Texture east,
|
||||||
Texture west
|
Texture west
|
||||||
) {
|
) {
|
||||||
|
this.program = program;
|
||||||
this.topTexture = top;
|
this.topTexture = top;
|
||||||
this.bottomTexture = bottom;
|
this.bottomTexture = bottom;
|
||||||
this.northTexture = north;
|
this.northTexture = north;
|
||||||
@ -126,8 +139,8 @@ public class Shapes {
|
|||||||
this.westTexture = west;
|
this.westTexture = west;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PppBuilder(Texture texture) {
|
public PppBuilder(ShapeRenderProgram program, Texture texture) {
|
||||||
this(texture, texture, texture, texture, texture, texture);
|
this(program, texture, texture, texture, texture, texture, texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PppBuilder setOrigin(Vec3 origin) {
|
public PppBuilder setOrigin(Vec3 origin) {
|
||||||
@ -201,6 +214,7 @@ public class Shapes {
|
|||||||
|
|
||||||
public Shape create() {
|
public Shape create() {
|
||||||
return createParallelepiped(
|
return createParallelepiped(
|
||||||
|
program,
|
||||||
origin,
|
origin,
|
||||||
width, height, depth,
|
width, height, depth,
|
||||||
colorMultiplier,
|
colorMultiplier,
|
||||||
|
@ -17,10 +17,8 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package ru.windcorp.optica.client.graphics.model;
|
package ru.windcorp.optica.client.graphics.model;
|
||||||
|
|
||||||
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
|
|
||||||
|
|
||||||
public interface WorldRenderable {
|
public interface WorldRenderable {
|
||||||
|
|
||||||
void render(WorldRenderer renderer);
|
void render(ShapeRenderHelper renderer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,6 @@ class Pixels {
|
|||||||
data // Data buffer
|
data // Data buffer
|
||||||
);
|
);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,18 +42,18 @@ public class Camera {
|
|||||||
|
|
||||||
public Camera() {}
|
public Camera() {}
|
||||||
|
|
||||||
public void apply(WorldRenderer renderer) {
|
public void apply(WorldRenderHelper helper) {
|
||||||
Mat4 previous = renderer.getViewTransform();
|
Mat4 previous = helper.getViewTransform();
|
||||||
Glm.perspective(
|
Glm.perspective(
|
||||||
computeFovY(),
|
computeFovY(),
|
||||||
GraphicsInterface.getAspectRatio(),
|
GraphicsInterface.getAspectRatio(),
|
||||||
0.01f, 10000.0f,
|
0.01f, 10000.0f,
|
||||||
renderer.pushViewTransform()
|
helper.pushViewTransform()
|
||||||
).mul(previous);
|
).mul(previous);
|
||||||
|
|
||||||
renderer.pushViewTransform().rotateX(pitch).rotateY(yaw);
|
helper.pushViewTransform().rotateX(pitch).rotateY(yaw);
|
||||||
|
|
||||||
renderer.pushViewTransform().translate(position.negate());
|
helper.pushViewTransform().translate(position.negate());
|
||||||
position.negate();
|
position.negate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,10 @@ public class LayerWorld extends Layer {
|
|||||||
private int movementY = 0;
|
private int movementY = 0;
|
||||||
private int movementZ = 0;
|
private int movementZ = 0;
|
||||||
|
|
||||||
private final WorldRenderer renderer = new WorldRenderer();
|
private static final boolean I_WANT_TO_THROW_UP = false;
|
||||||
|
private float shakeParam = 0;
|
||||||
|
|
||||||
|
private final WorldRenderHelper helper = new WorldRenderHelper();
|
||||||
|
|
||||||
private final WorldRender world = new WorldRender(new WorldData());
|
private final WorldRender world = new WorldRender(new WorldData());
|
||||||
|
|
||||||
@ -66,13 +69,14 @@ public class LayerWorld extends Layer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRender() {
|
protected void doRender() {
|
||||||
camera.apply(renderer);
|
camera.apply(helper);
|
||||||
renderWorld();
|
renderWorld();
|
||||||
renderer.reset();
|
helper.reset();
|
||||||
|
|
||||||
angMat.set().rotateY(-camera.getYaw());
|
angMat.set().rotateY(-camera.getYaw());
|
||||||
|
|
||||||
tmp.set(movementX, 0, movementZ);
|
tmp.set(movementX, 0, movementZ);
|
||||||
|
if (movementX != 0 && movementZ != 0) tmp.normalize();
|
||||||
angMat.mul_(tmp); // bug in jglm
|
angMat.mul_(tmp); // bug in jglm
|
||||||
tmp.y = movementY;
|
tmp.y = movementY;
|
||||||
tmp.mul(0.1f);
|
tmp.mul(0.1f);
|
||||||
@ -82,10 +86,23 @@ public class LayerWorld extends Layer {
|
|||||||
tmp.set(velocity);
|
tmp.set(velocity);
|
||||||
tmp.mul((float) (GraphicsInterface.getFrameLength() * 60));
|
tmp.mul((float) (GraphicsInterface.getFrameLength() * 60));
|
||||||
camera.move(tmp);
|
camera.move(tmp);
|
||||||
|
|
||||||
|
if (I_WANT_TO_THROW_UP) {
|
||||||
|
shakeParam += tmp.set(tmp.x, 0, tmp.z).length() * 1.5f;
|
||||||
|
float vel = tmp.set(velocity).set(tmp.x, 0, tmp.z).length() * 0.7f;
|
||||||
|
|
||||||
|
helper.pushViewTransform().translate(
|
||||||
|
(float) Math.sin(shakeParam) * vel,
|
||||||
|
(float) Math.sin(2 * shakeParam) * vel,
|
||||||
|
0.25f
|
||||||
|
).rotateZ(
|
||||||
|
(float) Math.sin(shakeParam) * vel * 0.15f
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderWorld() {
|
private void renderWorld() {
|
||||||
world.render(renderer);
|
world.render(helper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Camera getCamera() {
|
public Camera getCamera() {
|
||||||
|
@ -18,38 +18,20 @@
|
|||||||
package ru.windcorp.optica.client.graphics.world;
|
package ru.windcorp.optica.client.graphics.world;
|
||||||
|
|
||||||
import glm.mat._4.Mat4;
|
import glm.mat._4.Mat4;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.ShapeRenderHelper;
|
||||||
import ru.windcorp.optica.common.util.StashingStack;
|
import ru.windcorp.optica.common.util.StashingStack;
|
||||||
|
|
||||||
public class WorldRenderer {
|
public class WorldRenderHelper extends ShapeRenderHelper {
|
||||||
|
|
||||||
private static final int TRANSFORM_STACK_SIZE = 64;
|
|
||||||
|
|
||||||
private final StashingStack<Mat4> worldTransformStack = new StashingStack<>(
|
|
||||||
TRANSFORM_STACK_SIZE, Mat4::new
|
|
||||||
);
|
|
||||||
|
|
||||||
private final StashingStack<Mat4> viewTransformStack = new StashingStack<>(
|
private final StashingStack<Mat4> viewTransformStack = new StashingStack<>(
|
||||||
TRANSFORM_STACK_SIZE, Mat4::new
|
TRANSFORM_STACK_SIZE, Mat4::new
|
||||||
);
|
);
|
||||||
|
|
||||||
private final Mat4 finalTransform = new Mat4();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
reset();
|
viewTransformStack.push().identity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mat4 pushWorldTransform() {
|
private final Mat4 finalTransform = new Mat4();
|
||||||
Mat4 previous = worldTransformStack.getHead();
|
|
||||||
return worldTransformStack.push().set(previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void popWorldTransform() {
|
|
||||||
worldTransformStack.removeHead();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mat4 getWorldTransform() {
|
|
||||||
return worldTransformStack.getHead();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mat4 pushViewTransform() {
|
public Mat4 pushViewTransform() {
|
||||||
Mat4 previous = viewTransformStack.getHead();
|
Mat4 previous = viewTransformStack.getHead();
|
||||||
@ -64,13 +46,14 @@ public class WorldRenderer {
|
|||||||
return viewTransformStack.getHead();
|
return viewTransformStack.getHead();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Mat4 getFinalTransform() {
|
public Mat4 getFinalTransform() {
|
||||||
return finalTransform.set(getViewTransform()).mul(getWorldTransform());
|
return finalTransform.set(getViewTransform()).mul(getWorldTransform());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
worldTransformStack.removeAll();
|
super.reset();
|
||||||
worldTransformStack.push().identity();
|
|
||||||
viewTransformStack.removeAll();
|
viewTransformStack.removeAll();
|
||||||
viewTransformStack.push().identity();
|
viewTransformStack.push().identity();
|
||||||
}
|
}
|
@ -0,0 +1,283 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Optica
|
||||||
|
* Copyright (C) 2020 Wind Corporation
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*******************************************************************************/
|
||||||
|
package ru.windcorp.optica.client.graphics.world;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import com.google.common.collect.ObjectArrays;
|
||||||
|
|
||||||
|
import glm.vec._2.Vec2;
|
||||||
|
import glm.vec._3.Vec3;
|
||||||
|
import ru.windcorp.optica.client.graphics.backend.VertexBufferObject;
|
||||||
|
import ru.windcorp.optica.client.graphics.backend.shaders.attributes.*;
|
||||||
|
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.*;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.Face;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.Shape;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.ShapeRenderHelper;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.ShapeRenderProgram;
|
||||||
|
|
||||||
|
public class WorldRenderProgram extends ShapeRenderProgram {
|
||||||
|
|
||||||
|
private static WorldRenderProgram def = null;
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
def = new WorldRenderProgram(
|
||||||
|
new String[] {"WorldDefault.vertex.glsl"},
|
||||||
|
new String[] {"WorldDefault.fragment.glsl"}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorldRenderProgram getDefault() {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int DEFAULT_BYTES_PER_VERTEX =
|
||||||
|
3 * Float.BYTES + // Position
|
||||||
|
3 * Float.BYTES + // Color multiplier
|
||||||
|
2 * Float.BYTES + // Texture coordinates
|
||||||
|
3 * Float.BYTES; // Normals
|
||||||
|
|
||||||
|
private static final String WORLD_VERTEX_SHADER_RESOURCE =
|
||||||
|
"World.vertex.glsl";
|
||||||
|
private static final String WORLD_FRAGMENT_SHADER_RESOURCE =
|
||||||
|
"World.fragment.glsl";
|
||||||
|
|
||||||
|
private static final String
|
||||||
|
WORLD_TRANSFORM_UNIFORM_NAME = "worldTransform",
|
||||||
|
NORMALS_ATTRIBUTE_NAME = "inputNormals";
|
||||||
|
|
||||||
|
private final Uniform4Matrix worldTransformUniform;
|
||||||
|
private final AttributeVertexArray normalsAttribute;
|
||||||
|
|
||||||
|
public WorldRenderProgram(
|
||||||
|
String[] vertexShaderResources,
|
||||||
|
String[] fragmentShaderResources
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
attachVertexShader(vertexShaderResources),
|
||||||
|
attachFragmentShader(fragmentShaderResources)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.worldTransformUniform = getUniform(WORLD_TRANSFORM_UNIFORM_NAME)
|
||||||
|
.as4Matrix();
|
||||||
|
|
||||||
|
this.normalsAttribute = getAttribute(NORMALS_ATTRIBUTE_NAME)
|
||||||
|
.asVertexArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] attachVertexShader(String[] others) {
|
||||||
|
return ObjectArrays.concat(WORLD_VERTEX_SHADER_RESOURCE, others);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] attachFragmentShader(String[] others) {
|
||||||
|
return ObjectArrays.concat(WORLD_FRAGMENT_SHADER_RESOURCE, others);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(ShapeRenderHelper helper) {
|
||||||
|
super.configure(helper);
|
||||||
|
worldTransformUniform.set(helper.getWorldTransform());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int bindVertices(VertexBufferObject vertices) {
|
||||||
|
int vertexStride = getBytesPerVertex();
|
||||||
|
int offset = super.bindVertices(vertices);
|
||||||
|
|
||||||
|
normalsAttribute.set(
|
||||||
|
3, GL11.GL_FLOAT, false, vertexStride, vertices,
|
||||||
|
offset
|
||||||
|
);
|
||||||
|
offset += 3 * Float.BYTES;
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void enableAttributes() {
|
||||||
|
super.enableAttributes();
|
||||||
|
normalsAttribute.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void disableAttributes() {
|
||||||
|
super.disableAttributes();
|
||||||
|
normalsAttribute.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBytesPerVertex() {
|
||||||
|
return super.getBytesPerVertex() +
|
||||||
|
3 * Float.BYTES; // Normals
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preprocess(Shape shape) {
|
||||||
|
super.preprocess(shape);
|
||||||
|
|
||||||
|
for (Face face : shape.getFaces()) {
|
||||||
|
computeNormals(face);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void computeNormals(Face face) {
|
||||||
|
Vec3 a = new Vec3();
|
||||||
|
Vec3 b = new Vec3();
|
||||||
|
Vec3 c = new Vec3();
|
||||||
|
Vec3 normal = new Vec3();
|
||||||
|
|
||||||
|
for (int i = 0; i < face.getIndexCount(); i += 3) {
|
||||||
|
int indexA = face.getIndex(i + 0);
|
||||||
|
int indexB = face.getIndex(i + 1);
|
||||||
|
int indexC = face.getIndex(i + 2);
|
||||||
|
|
||||||
|
loadVertexPosition(face, indexA, a);
|
||||||
|
loadVertexPosition(face, indexB, b);
|
||||||
|
loadVertexPosition(face, indexC, c);
|
||||||
|
|
||||||
|
computeOneNormal(a, b, c, normal);
|
||||||
|
|
||||||
|
saveVertexNormal(face, indexA, normal);
|
||||||
|
saveVertexNormal(face, indexB, normal);
|
||||||
|
saveVertexNormal(face, indexC, normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void computeOneNormal(
|
||||||
|
Vec3 a, Vec3 b, Vec3 c,
|
||||||
|
Vec3 normal
|
||||||
|
) {
|
||||||
|
b.sub(a);
|
||||||
|
c.sub(a);
|
||||||
|
b.cross(c, normal);
|
||||||
|
normal.normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadVertexPosition(Face face, int index, Vec3 result) {
|
||||||
|
ByteBuffer vertices = face.getVertices();
|
||||||
|
int offset = vertices.position() + index * getBytesPerVertex();
|
||||||
|
|
||||||
|
result.set(
|
||||||
|
vertices.getFloat(offset + 0 * Float.BYTES),
|
||||||
|
vertices.getFloat(offset + 1 * Float.BYTES),
|
||||||
|
vertices.getFloat(offset + 2 * Float.BYTES)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveVertexNormal(Face face, int index, Vec3 normal) {
|
||||||
|
ByteBuffer vertices = face.getVertices();
|
||||||
|
int offset = vertices.position() + index * getBytesPerVertex() + (
|
||||||
|
3 * Float.BYTES +
|
||||||
|
3 * Float.BYTES +
|
||||||
|
2 * Float.BYTES
|
||||||
|
);
|
||||||
|
|
||||||
|
vertices.putFloat(offset + 0 * Float.BYTES, normal.x);
|
||||||
|
vertices.putFloat(offset + 1 * Float.BYTES, normal.y);
|
||||||
|
vertices.putFloat(offset + 2 * Float.BYTES, normal.z);
|
||||||
|
|
||||||
|
face.markForVertexUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VertexBuilder getVertexBuilder() {
|
||||||
|
return new WRPVertexBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WRPVertexBuilder implements VertexBuilder {
|
||||||
|
// TODO Throw VertexBuilders out the window and rewrite completely.
|
||||||
|
// I want to _extend_ VBs, not re-implement them for children of SRP
|
||||||
|
|
||||||
|
private static class Vertex {
|
||||||
|
final Vec3 position;
|
||||||
|
final Vec3 colorMultiplier;
|
||||||
|
final Vec2 textureCoords;
|
||||||
|
|
||||||
|
Vertex(Vec3 position, Vec3 colorMultiplier, Vec2 textureCoords) {
|
||||||
|
this.position = position;
|
||||||
|
this.colorMultiplier = colorMultiplier;
|
||||||
|
this.textureCoords = textureCoords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<Vertex> vertices = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VertexBuilder addVertex(
|
||||||
|
float x, float y, float z,
|
||||||
|
float r, float g, float b,
|
||||||
|
float tx, float ty
|
||||||
|
) {
|
||||||
|
vertices.add(new Vertex(
|
||||||
|
new Vec3(x, y, z),
|
||||||
|
new Vec3(r, g, b),
|
||||||
|
new Vec2(tx, ty)
|
||||||
|
));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VertexBuilder addVertex(
|
||||||
|
Vec3 position,
|
||||||
|
Vec3 colorMultiplier,
|
||||||
|
Vec2 textureCoords
|
||||||
|
) {
|
||||||
|
vertices.add(new Vertex(
|
||||||
|
new Vec3(position),
|
||||||
|
new Vec3(colorMultiplier),
|
||||||
|
new Vec2(textureCoords)
|
||||||
|
));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer assemble() {
|
||||||
|
ByteBuffer result = BufferUtils.createByteBuffer(
|
||||||
|
DEFAULT_BYTES_PER_VERTEX * vertices.size()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (Vertex v : vertices) {
|
||||||
|
result
|
||||||
|
.putFloat(v.position.x)
|
||||||
|
.putFloat(v.position.y)
|
||||||
|
.putFloat(v.position.z)
|
||||||
|
.putFloat(v.colorMultiplier.x)
|
||||||
|
.putFloat(v.colorMultiplier.y)
|
||||||
|
.putFloat(v.colorMultiplier.z)
|
||||||
|
.putFloat(v.textureCoords.x)
|
||||||
|
.putFloat(v.textureCoords.y)
|
||||||
|
.putFloat(Float.NaN)
|
||||||
|
.putFloat(Float.NaN)
|
||||||
|
.putFloat(Float.NaN);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.flip();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -26,7 +26,7 @@ import ru.windcorp.optica.client.graphics.model.Shape;
|
|||||||
import ru.windcorp.optica.client.graphics.model.StaticModel;
|
import ru.windcorp.optica.client.graphics.model.StaticModel;
|
||||||
import ru.windcorp.optica.client.graphics.model.StaticModel.Builder;
|
import ru.windcorp.optica.client.graphics.model.StaticModel.Builder;
|
||||||
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
|
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
|
||||||
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
|
import ru.windcorp.optica.client.graphics.model.ShapeRenderHelper;
|
||||||
import ru.windcorp.optica.client.world.renders.BlockRender;
|
import ru.windcorp.optica.client.world.renders.BlockRender;
|
||||||
import ru.windcorp.optica.client.world.renders.BlockRenderNone;
|
import ru.windcorp.optica.client.world.renders.BlockRenderNone;
|
||||||
import ru.windcorp.optica.client.world.renders.BlockRenders;
|
import ru.windcorp.optica.client.world.renders.BlockRenders;
|
||||||
@ -70,7 +70,7 @@ public class ChunkRender {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(WorldRenderer renderer) {
|
public void render(ShapeRenderHelper renderer) {
|
||||||
if (model == null || needsUpdate()) {
|
if (model == null || needsUpdate()) {
|
||||||
buildModel();
|
buildModel();
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import java.util.Collection;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
|
import ru.windcorp.optica.client.graphics.model.ShapeRenderHelper;
|
||||||
import ru.windcorp.optica.common.world.ChunkData;
|
import ru.windcorp.optica.common.world.ChunkData;
|
||||||
import ru.windcorp.optica.common.world.WorldData;
|
import ru.windcorp.optica.common.world.WorldData;
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ public class WorldRender {
|
|||||||
return chunks.values();
|
return chunks.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(WorldRenderer renderer) {
|
public void render(ShapeRenderHelper renderer) {
|
||||||
renderer.pushWorldTransform().rotateX(-Math.PI / 2);
|
renderer.pushWorldTransform().rotateX(-Math.PI / 2);
|
||||||
|
|
||||||
for (ChunkRender chunk : getChunks()) {
|
for (ChunkRender chunk : getChunks()) {
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
package ru.windcorp.optica.client.world.renders;
|
package ru.windcorp.optica.client.world.renders;
|
||||||
|
|
||||||
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
|
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
|
||||||
import ru.windcorp.optica.client.graphics.world.WorldRenderer;
|
import ru.windcorp.optica.client.graphics.model.ShapeRenderHelper;
|
||||||
import ru.windcorp.optica.common.util.Namespaced;
|
import ru.windcorp.optica.common.util.Namespaced;
|
||||||
|
|
||||||
public abstract class BlockRender extends Namespaced {
|
public abstract class BlockRender extends Namespaced {
|
||||||
@ -41,7 +41,7 @@ public abstract class BlockRender extends Namespaced {
|
|||||||
this.optimizer = optimizer;
|
this.optimizer = optimizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(WorldRenderer renderer) {
|
public void render(ShapeRenderHelper renderer) {
|
||||||
throw new UnsupportedOperationException(
|
throw new UnsupportedOperationException(
|
||||||
"BlockRender.render() not implemented"
|
"BlockRender.render() not implemented"
|
||||||
);
|
);
|
||||||
|
@ -24,6 +24,7 @@ import java.util.EnumMap;
|
|||||||
import ru.windcorp.optica.client.graphics.model.Shapes;
|
import ru.windcorp.optica.client.graphics.model.Shapes;
|
||||||
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
|
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
|
||||||
import ru.windcorp.optica.client.graphics.texture.Texture;
|
import ru.windcorp.optica.client.graphics.texture.Texture;
|
||||||
|
import ru.windcorp.optica.client.graphics.world.WorldRenderProgram;
|
||||||
import ru.windcorp.optica.common.block.BlockFace;
|
import ru.windcorp.optica.common.block.BlockFace;
|
||||||
|
|
||||||
public abstract class BlockRenderTexturedCube extends BlockRender {
|
public abstract class BlockRenderTexturedCube extends BlockRender {
|
||||||
@ -54,6 +55,7 @@ public abstract class BlockRenderTexturedCube extends BlockRender {
|
|||||||
@Override
|
@Override
|
||||||
public WorldRenderable createRenderable() {
|
public WorldRenderable createRenderable() {
|
||||||
return new Shapes.PppBuilder(
|
return new Shapes.PppBuilder(
|
||||||
|
WorldRenderProgram.getDefault(),
|
||||||
getTexture(TOP), getTexture(BOTTOM),
|
getTexture(TOP), getTexture(BOTTOM),
|
||||||
getTexture(NORTH), getTexture(SOUTH),
|
getTexture(NORTH), getTexture(SOUTH),
|
||||||
getTexture(EAST), getTexture(WEST)
|
getTexture(EAST), getTexture(WEST)
|
||||||
|
@ -28,6 +28,7 @@ import ru.windcorp.optica.client.graphics.model.Face;
|
|||||||
import ru.windcorp.optica.client.graphics.model.Faces;
|
import ru.windcorp.optica.client.graphics.model.Faces;
|
||||||
import ru.windcorp.optica.client.graphics.model.Shape;
|
import ru.windcorp.optica.client.graphics.model.Shape;
|
||||||
import ru.windcorp.optica.client.graphics.texture.Texture;
|
import ru.windcorp.optica.client.graphics.texture.Texture;
|
||||||
|
import ru.windcorp.optica.client.graphics.world.WorldRenderProgram;
|
||||||
import ru.windcorp.optica.client.world.ChunkRender;
|
import ru.windcorp.optica.client.world.ChunkRender;
|
||||||
import ru.windcorp.optica.client.world.renders.BlockRender;
|
import ru.windcorp.optica.client.world.renders.BlockRender;
|
||||||
import ru.windcorp.optica.common.block.BlockFace;
|
import ru.windcorp.optica.common.block.BlockFace;
|
||||||
@ -167,6 +168,7 @@ public class BlockRenderOpaqueCubeOptimizer extends BlockRenderOptimizer {
|
|||||||
|
|
||||||
return new Shape(
|
return new Shape(
|
||||||
Usage.STATIC,
|
Usage.STATIC,
|
||||||
|
WorldRenderProgram.getDefault(),
|
||||||
shapeFaces.toArray(new Face[shapeFaces.size()])
|
shapeFaces.toArray(new Face[shapeFaces.size()])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -177,6 +179,7 @@ public class BlockRenderOpaqueCubeOptimizer extends BlockRenderOptimizer {
|
|||||||
Texture texture = blockRender.getTexture(face);
|
Texture texture = blockRender.getTexture(face);
|
||||||
|
|
||||||
return Faces.createBlockFace(
|
return Faces.createBlockFace(
|
||||||
|
WorldRenderProgram.getDefault(),
|
||||||
texture,
|
texture,
|
||||||
COLOR_MULTIPLIER,
|
COLOR_MULTIPLIER,
|
||||||
blockCenter.set(x, y, z),
|
blockCenter.set(x, y, z),
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
varying vec3 varyingColorMultiplier;
|
varying vec3 varyingColorMultiplier;
|
||||||
varying vec2 varyingTextureCoords;
|
varying vec2 varyingTextureCoords;
|
||||||
varying vec3 varyingNormals;
|
|
||||||
|
|
||||||
uniform sampler2D textureSlot;
|
uniform sampler2D textureSlot;
|
||||||
uniform vec2 textureStart;
|
uniform vec2 textureStart;
|
||||||
@ -36,16 +35,6 @@ void applyColorMultiplier() {
|
|||||||
linearMultiply(gl_FragColor, vec4(varyingColorMultiplier, 1.0));
|
linearMultiply(gl_FragColor, vec4(varyingColorMultiplier, 1.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyShading() {
|
|
||||||
vec3 light = normalize(vec3(0.5, 1.0, 0.2));
|
|
||||||
vec3 normal = varyingNormals;
|
|
||||||
|
|
||||||
float angleCos = dot(normal, light);
|
|
||||||
float lightness = (angleCos + 1.5) / 2;
|
|
||||||
|
|
||||||
linearMultiply(gl_FragColor, vec4(lightness, lightness, lightness, 1.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
void applyAlpha() {
|
void applyAlpha() {
|
||||||
if (gl_FragColor.w < 0.01) {
|
if (gl_FragColor.w < 0.01) {
|
||||||
discard;
|
discard;
|
||||||
|
@ -8,20 +8,13 @@ varying vec3 varyingColorMultiplier;
|
|||||||
attribute vec2 inputTextureCoords;
|
attribute vec2 inputTextureCoords;
|
||||||
varying vec2 varyingTextureCoords;
|
varying vec2 varyingTextureCoords;
|
||||||
|
|
||||||
attribute vec3 inputNormals;
|
|
||||||
varying vec3 varyingNormals;
|
|
||||||
|
|
||||||
uniform mat4 worldTransform;
|
|
||||||
uniform mat4 finalTransform;
|
uniform mat4 finalTransform;
|
||||||
|
|
||||||
vec4 applyFinalTransform(vec4 vector) {
|
vec4 applyFinalTransform(vec4 vector) {
|
||||||
return finalTransform * vector;
|
return finalTransform * vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
void transferToFragment() {
|
void shapeTransferToFragment() {
|
||||||
varyingColorMultiplier = inputColorMultiplier;
|
varyingColorMultiplier = inputColorMultiplier;
|
||||||
varyingTextureCoords = inputTextureCoords;
|
varyingTextureCoords = inputTextureCoords;
|
||||||
|
|
||||||
mat3 worldRotation = mat3(worldTransform);
|
|
||||||
varyingNormals = normalize(worldRotation * inputNormals);
|
|
||||||
}
|
}
|
13
src/main/resources/assets/shaders/World.fragment.glsl
Normal file
13
src/main/resources/assets/shaders/World.fragment.glsl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#version 120
|
||||||
|
|
||||||
|
varying vec3 varyingNormals;
|
||||||
|
|
||||||
|
void applyShading() {
|
||||||
|
vec3 light = normalize(vec3(0.5, 1.0, 0.2));
|
||||||
|
vec3 normal = varyingNormals;
|
||||||
|
|
||||||
|
float angleCos = dot(normal, light);
|
||||||
|
float lightness = (angleCos + 1.5) / 2;
|
||||||
|
|
||||||
|
linearMultiply(gl_FragColor, vec4(lightness, lightness, lightness, 1.0));
|
||||||
|
}
|
13
src/main/resources/assets/shaders/World.vertex.glsl
Normal file
13
src/main/resources/assets/shaders/World.vertex.glsl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#version 120
|
||||||
|
|
||||||
|
attribute vec3 inputNormals;
|
||||||
|
varying vec3 varyingNormals;
|
||||||
|
|
||||||
|
uniform mat4 worldTransform;
|
||||||
|
|
||||||
|
void worldTransferToFragment() {
|
||||||
|
shapeTransferToFragment();
|
||||||
|
|
||||||
|
mat3 worldRotation = mat3(worldTransform);
|
||||||
|
varyingNormals = normalize(worldRotation * inputNormals);
|
||||||
|
}
|
@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
gl_Position = applyFinalTransform(vec4(inputPositions, 1.0));
|
gl_Position = applyFinalTransform(vec4(inputPositions, 1.0));
|
||||||
transferToFragment();
|
worldTransferToFragment();
|
||||||
}
|
}
|
Reference in New Issue
Block a user