dlangui/examples/d3d/src/d3d.d

431 lines
18 KiB
D

module d3d;
import dlangui;
import dlangui.graphics.scene.scene3d;
import dlangui.graphics.scene.camera;
import dlangui.graphics.scene.mesh;
import dlangui.graphics.scene.material;
import dlangui.graphics.glsupport;
import dlangui.graphics.gldrawbuf;
import derelict.opengl3.gl3;
import derelict.opengl3.gl;
import dminer.core.world;
import dminer.core.minetypes;
import dminer.core.blocks;
mixin APP_ENTRY_POINT;
/// entry point for dlangui based application
extern (C) int UIAppMain(string[] args) {
// embed resources listed in views/resources.list into executable
embeddedResourceList.addResources(embedResourcesFromList!("resources.list")());
// create window
Window window = Platform.instance.createWindow("DlangUI example - 3D Application", null, WindowFlag.Resizable, 600, 500);
window.mainWidget = new UiWidget();
//MeshPart part = new MeshPart();
// show window
window.show();
// run message loop
return Platform.instance.enterMessageLoop();
}
class UiWidget : VerticalLayout, CellVisitor {
this() {
super("OpenGLView");
layoutWidth = FILL_PARENT;
layoutHeight = FILL_PARENT;
alignment = Align.Center;
try {
parseML(q{
{
margins: 10
padding: 10
backgroundImageId: "tx_fabric.tiled"
layoutWidth: fill
layoutHeight: fill
VerticalLayout {
id: glView
margins: 10
padding: 10
layoutWidth: fill
layoutHeight: fill
TextWidget { text: "There should be OpenGL animation on background"; textColor: "red"; fontSize: 150%; fontWeight: 800; fontFace: "Arial" }
TextWidget { text: "Do you see it? If no, there is some bug in Mesh rendering code..."; fontSize: 120% }
// arrange controls as form - table with two columns
TableLayout {
colCount: 6
TextWidget { text: "Translation X" }
TextWidget { id: lblTranslationX; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
ScrollBar { id: sbTranslationX; orientation: horizontal; minValue: -100; maxValue: 100; position: 0; minWidth: 200; alpha: 0.6 }
TextWidget { text: "Rotation X" }
TextWidget { id: lblRotationX; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
ScrollBar { id: sbRotationX; orientation: horizontal; minValue: -180; maxValue: 180; position: 0; minWidth: 200; alpha: 0.6 }
TextWidget { text: "Translation Y" }
TextWidget { id: lblTranslationY; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
ScrollBar { id: sbTranslationY; orientation: horizontal; minValue: -100; maxValue: 100; position: 15; minWidth: 200; alpha: 0.6 }
TextWidget { text: "Rotation Y" }
TextWidget { id: lblRotationY; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
ScrollBar { id: sbRotationY; orientation: horizontal; minValue: -180; maxValue: 180; position: 0; minWidth: 150; alpha: 0.6 }
TextWidget { text: "Translation Z" }
TextWidget { id: lblTranslationZ; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
ScrollBar { id: sbTranslationZ; orientation: horizontal; minValue: -100; maxValue: 100; position: 45; minWidth: 150; alpha: 0.6 }
TextWidget { text: "Rotation Z" }
TextWidget { id: lblRotationZ; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
ScrollBar { id: sbRotationZ; orientation: horizontal; minValue: -180; maxValue: 180; position: 0; minWidth: 150; alpha: 0.6 }
TextWidget { text: "Near" }
TextWidget { id: lblNear; text: "0.1"; minWidth: 80; backgroundColor: 0x80FFFFFF }
ScrollBar { id: sbNear; orientation: horizontal; minValue: 1; maxValue: 100; position: 1; minWidth: 150; alpha: 0.6 }
TextWidget { text: "Far" }
TextWidget { id: lblFar; text: "0.0"; minWidth: 80; backgroundColor: 0x80FFFFFF }
ScrollBar { id: sbFar; orientation: horizontal; minValue: 20; maxValue: 1000; position: 1000; minWidth: 150; alpha: 0.6 }
}
VSpacer { layoutWeight: 30 }
HorizontalLayout {
TextWidget { text: "Some buttons:" }
Button { id: btnOk; text: "Ok"; fontSize: 27px }
Button { id: btnCancel; text: "Cancel"; fontSize: 27px }
}
}
}
}, "", this);
} catch (Exception e) {
Log.e("Failed to parse dml", e);
}
// assign OpenGL drawable to child widget background
childById("glView").backgroundDrawable = DrawableRef(new OpenGLDrawable(&doDraw));
controlsToVars();
assignHandlers();
_scene = new Scene3d();
_cam = new Camera();
_cam.translate(vec3(0, 14, -7));
_scene.activeCamera = _cam;
int x0 = 0;
int y0 = 0;
int z0 = 0;
_mesh = Mesh.createCubeMesh(vec3(x0+ 0, y0 + 0, z0 + 0), 0.3f);
for (int i = 0; i < 10; i++) {
_mesh.addCubeMesh(vec3(x0+ 0, y0+0, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, 1, 1, 1));
_mesh.addCubeMesh(vec3(x0+ i * 2 + 1.0f, y0+0, z0+ 0), 0.2f, vec4(1, i / 12, 1, 1));
_mesh.addCubeMesh(vec3(x0+ -i * 2 - 1.0f, y0+0, z0+ 0), 0.2f, vec4(1, i / 12, 1, 1));
_mesh.addCubeMesh(vec3(x0+ 0, y0+i * 2 + 1.0f, z0+ 0), 0.2f, vec4(1, 1, i / 12 + 0.1, 1));
_mesh.addCubeMesh(vec3(x0+ 0, y0+-i * 2 - 1.0f, z0+ 0), 0.2f, vec4(1, 1, i / 12 + 0.1, 1));
_mesh.addCubeMesh(vec3(x0+ i * 2 + 1.0f, y0+i * 2 + 1.0f, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, i / 12, i / 12, 1));
_mesh.addCubeMesh(vec3(x0+ -i * 2 + 1.0f, y0+i * 2 + 1.0f, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, i / 12, 1 - i / 12, 1));
_mesh.addCubeMesh(vec3(x0+ i * 2 + 1.0f, y0+-i * 2 + 1.0f, z0+ i * 2 + 1.0f), 0.2f, vec4(i / 12, 1 - i / 12, i / 12, 1));
_mesh.addCubeMesh(vec3(x0+ -i * 2 - 1.0f, y0+-i * 2 - 1.0f, z0+ -i * 2 - 1.0f), 0.2f, vec4(1 - i / 12, i / 12, i / 12, 1));
}
_minerMesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL, VertexElementType.COLOR, VertexElementType.TEXCOORD0));
_world = new World();
for (int x = -100; x < 100; x++)
for (int z = -100; z < 100; z++)
_world.setCell(x, 0, z, 1);
_world.setCell(0, 11, 10, 2);
_world.setCell(5, 11, 15, 2);
Random rnd;
rnd.setSeed(12345);
for(int i = 0; i < 1000; i++)
_world.setCell(rnd.next(6)-32, rnd.next(4), rnd.next(6)-32, 3);
_world.camPosition = Position(Vector3d(0, 3, 0), Vector3d(0, 0, 1));
updateMinerMesh();
//CellVisitor visitor = new TestVisitor();
//Log.d("Testing cell visitor");
//long ts = currentTimeMillis;
//_world.visitVisibleCells(_world.camPosition, visitor);
//long duration = currentTimeMillis - ts;
//Log.d("DiamondVisitor finished in ", duration, " ms");
//destroy(w);
}
float rotationX;
float rotationY;
float rotationZ;
float translationX;
float translationY;
float translationZ;
float near;
float far;
/// handle scroll event
bool onScrollEvent(AbstractSlider source, ScrollEvent event) {
controlsToVars();
return true;
}
void assignHandlers() {
childById!ScrollBar("sbNear").scrollEvent = &onScrollEvent;
childById!ScrollBar("sbFar").scrollEvent = &onScrollEvent;
childById!ScrollBar("sbRotationX").scrollEvent = &onScrollEvent;
childById!ScrollBar("sbRotationY").scrollEvent = &onScrollEvent;
childById!ScrollBar("sbRotationZ").scrollEvent = &onScrollEvent;
childById!ScrollBar("sbTranslationX").scrollEvent = &onScrollEvent;
childById!ScrollBar("sbTranslationY").scrollEvent = &onScrollEvent;
childById!ScrollBar("sbTranslationZ").scrollEvent = &onScrollEvent;
}
void controlsToVars() {
near = childById!ScrollBar("sbNear").position / 10.0f;
far = childById!ScrollBar("sbFar").position / 10.0f;
translationX = childById!ScrollBar("sbTranslationX").position / 10.0f;
translationY = childById!ScrollBar("sbTranslationY").position / 10.0f;
translationZ = childById!ScrollBar("sbTranslationZ").position / 10.0f;
rotationX = childById!ScrollBar("sbRotationX").position;
rotationY = childById!ScrollBar("sbRotationY").position;
rotationZ = childById!ScrollBar("sbRotationZ").position;
childById("lblNear").text = to!dstring(near);
childById("lblFar").text = to!dstring(far);
childById("lblTranslationX").text = to!dstring(translationX);
childById("lblTranslationY").text = to!dstring(translationY);
childById("lblTranslationZ").text = to!dstring(translationZ);
childById("lblRotationX").text = to!dstring(rotationX);
childById("lblRotationY").text = to!dstring(rotationY);
childById("lblRotationZ").text = to!dstring(rotationZ);
}
void visit(World world, ref Position camPosition, Vector3d pos, cell_t cell, int visibleFaces) {
BlockDef def = BLOCK_DEFS[cell];
def.createFaces(world, world.camPosition, pos, visibleFaces, _minerMesh);
}
void updateMinerMesh() {
_minerMesh.reset();
long ts = currentTimeMillis;
_world.visitVisibleCells(_world.camPosition, this);
long duration = currentTimeMillis - ts;
Log.d("DiamondVisitor finished in ", duration, " ms ", "Vertex count: ", _minerMesh.vertexCount);
}
World _world;
/// returns true is widget is being animated - need to call animate() and redraw
@property override bool animating() { return true; }
/// animates window; interval is time left from previous draw, in hnsecs (1/10000000 of second)
override void animate(long interval) {
//Log.d("animating");
_cam.rotateX(0.01);
_cam.rotateY(0.02);
angle += interval * 0.000002f;
invalidate();
}
float angle = 0;
MyGLProgram _program;
Scene3d _scene;
Camera _cam;
Mesh _mesh;
Mesh _minerMesh;
GLTexture _tx;
GLTexture _blockstx;
/// this is OpenGLDrawableDelegate implementation
private void doDraw(Rect windowRect, Rect rc) {
if (!_program) {
_program = new MyGLProgram();
}
if (!_program.check())
return;
if (!_tx)
_tx = new GLTexture("crate");
if (!_blockstx)
_blockstx = new GLTexture("blocks");
if (!_tx.isValid || !_blockstx.isValid) {
Log.e("Invalid texture");
return;
}
_cam.setPerspective(rc.width, rc.height, 45.0f, near, far);
_cam.setIdentity();
//_cam.translate(vec3(
// childById!ScrollBar("sbTranslationX").position / 10.0f,
// childById!ScrollBar("sbTranslationY").position / 10.0f,
// childById!ScrollBar("sbTranslationZ").position / 10.0f));
_cam.translateX(translationX);
_cam.translateY(translationY);
_cam.translateZ(translationZ);
_cam.rotateX(rotationX);
_cam.rotateY(rotationY);
_cam.rotateZ(rotationZ);
//_cam.translate(vec3(-1, -1.5, -1)); // - angle/1000
//_cam.translate(vec3(0, 0, -1.1)); // - angle/1000
//_cam.translate(vec3(0, 3, - angle/1000)); //
//_cam.rotateZ(30.0f + angle * 0.3456778);
mat4 projectionViewMatrix = _cam.projectionViewMatrix;
// ======== Model Matrix ==================
mat4 modelMatrix;
//modelMatrix.scale(0.1f);
//modelMatrix.rotatez(30.0f + angle * 0.3456778);
//modelMatrix.rotatey(25);
//modelMatrix.rotatex(15);
//modelMatrix.rotatey(angle);
//modelMatrix.rotatex(angle * 1.98765f);
mat4 projectionViewModelMatrix = projectionViewMatrix * modelMatrix;
//Log.d("projectionViewModelMatrix: ", projectionViewModelMatrix.dump);
//{
// mat4 projection;
// projection.setPerspective(45.0f, cast(float)rc.width / rc.height, near, far);
// mat4 view;
// view.translate(translationX, translationY, translationZ);
// Log.d(" .viewMatrix.trans ", view.dump);
// view.rotateX(rotationX);
// Log.d(" .viewMatrix.rx ", view.dump);
// view.rotateY(rotationY);
// Log.d(" .viewMatrix.ry ", view.dump);
// view.rotateZ(rotationZ);
// Log.d(" .viewMatrix.rz ", view.dump);
// mat4 projectionView = projection * view;
// Log.d(" .projectionMatrix: ", projection.dump);
// Log.d(" .viewMatrix: ", view.dump);
// Log.d(" .projectionViewMatrix: ", projectionView.dump);
// Log.d(" .projectionViewMMatrix: ", (projectionView * modelMatrix).dump);
//}
//{
// import gl3n.linalg;
// static string dump(mat4 m) {
// m.transpose;
// return to!string(m[0]) ~ to!string(m[1]) ~ to!string(m[2]) ~ to!string(m[3]);
// }
// static float toRad(float angle) { return angle * 2 * PI / 360; }
// mat4 projection = mat4.perspective(rc.width, rc.height, 45.0f, near, far);
// mat4 view = mat4.identity.translate(translationX, translationY, translationZ).rotatex(toRad(rotationX)).rotatey(toRad(rotationY)).rotatez(toRad(rotationZ));
// Log.d("gl3n.viewMatrix: tr ", dump(mat4.identity.translate(translationX, translationY, translationZ)));
// Log.d("gl3n.viewMatrix: rx ", dump(mat4.identity.translate(translationX, translationY, translationZ).rotatex(toRad(rotationX))));
// Log.d("gl3n.viewMatrix: ry ", dump(mat4.identity.translate(translationX, translationY, translationZ).rotatex(toRad(rotationX)).rotatey(toRad(rotationY))));
// Log.d("gl3n.viewMatrix: rz ", dump(mat4.identity.translate(translationX, translationY, translationZ).rotatex(toRad(rotationX)).rotatey(toRad(rotationY)).rotatez(toRad(rotationZ))));
// mat4 projectionView = projection * view;
// Log.d("gl3n.projectionMatrix: ", dump(projection));
// Log.d("gl3n.viewMatrix: ", dump(view));
// Log.d("gl3n.projectionViewMatrix: ", dump(projectionView));
// Log.d("gl3n.projectionViewMMatrix: ", dump(projectionView * mat4.identity));
//}
//projectionViewModelMatrix.setIdentity();
//Log.d("matrix uniform: ", projectionViewModelMatrix.m);
checkgl!glEnable(GL_CULL_FACE);
//checkgl!glDisable(GL_CULL_FACE);
checkgl!glEnable(GL_DEPTH_TEST);
checkgl!glCullFace(GL_BACK);
_program.bind();
_program.setUniform("matrix", projectionViewModelMatrix);
_tx.texture.setup();
_tx.texture.setSamplerParams(true);
_program.draw(_mesh);
_tx.texture.unbind();
_blockstx.texture.setup();
_blockstx.texture.setSamplerParams(false);
_program.draw(_minerMesh);
_blockstx.texture.unbind();
_program.unbind();
checkgl!glDisable(GL_DEPTH_TEST);
checkgl!glDisable(GL_CULL_FACE);
}
~this() {
destroy(_scene);
if (_program)
destroy(_program);
if (_tx)
destroy(_tx);
destroy(_world);
}
}
class TestVisitor : CellVisitor {
//void newDirection(ref Position camPosition) {
// Log.d("TestVisitor.newDirection");
//}
//void visitFace(World world, ref Position camPosition, Vector3d pos, cell_t cell, Dir face) {
// Log.d("TestVisitor.visitFace ", pos, " cell=", cell, " face=", face);
//}
void visit(World world, ref Position camPosition, Vector3d pos, cell_t cell, int visibleFaces) {
//Log.d("TestVisitor.visit ", pos, " cell=", cell);
}
}
// Simple texture + color shader
class MyGLProgram : GLProgram {
@property override string vertexSource() {
return q{
in vec4 vertex;
in vec4 colAttr;
in vec4 texCoord;
out vec4 col;
out vec4 texc;
uniform mat4 matrix;
void main(void)
{
gl_Position = matrix * vertex;
col = colAttr;
texc = texCoord;
}
};
}
@property override string fragmentSource() {
return q{
uniform sampler2D tex;
in vec4 col;
in vec4 texc;
out vec4 outColor;
void main(void)
{
outColor = texture(tex, texc.st) * col;
}
};
}
// attribute locations
protected int matrixLocation;
protected int vertexLocation;
protected int colAttrLocation;
protected int texCoordLocation;
override bool initLocations() {
matrixLocation = getUniformLocation("matrix");
vertexLocation = getAttribLocation("vertex");
colAttrLocation = getAttribLocation("colAttr");
texCoordLocation = getAttribLocation("texCoord");
return matrixLocation >= 0 && vertexLocation >= 0 && colAttrLocation >= 0 && texCoordLocation >= 0;
}
/// get location for vertex attribute
override int getVertexElementLocation(VertexElementType type) {
switch(type) with(VertexElementType) {
case POSITION:
return vertexLocation;
case COLOR:
return colAttrLocation;
case TEXCOORD0:
return texCoordLocation;
default:
return super.getVertexElementLocation(type);
}
}
}