dlangui/examples/d3d/src/d3d.d

422 lines
19 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.scene.effect;
import dlangui.graphics.scene.model;
import dlangui.graphics.scene.node;
import dlangui.graphics.scene.light;
import dlangui.graphics.scene.objimport;
import dlangui.graphics.scene.fbximport;
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);
static if (ENABLE_OPENGL) {
window.mainWidget = new UiWidget();
} else {
window.mainWidget = new TextWidget("error", "Please build with OpenGL enabled"d);
}
//MeshPart part = new MeshPart();
// show window
window.show();
// run message loop
return Platform.instance.enterMessageLoop();
}
static if (ENABLE_OPENGL):
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;
dirLightNode = new Node3d();
//dirLightNode.lookAt(vec3(-5, -5, -5), vec3(0, 0, 0), vec3(0, 1, 0));
dirLightNode.rotateY(-15);
//dirLightNode.rotateX(20);
dirLightNode.translateX(2);
dirLightNode.translateY(3);
dirLightNode.translateZ(0);
dirLightNode.light = Light.createPoint(vec3(2, 2, 2), 15); //Light.createDirectional(vec3(1, 0.5, 0.5));
//dirLightNode.light = Light.createDirectional(vec3(1, 0.5, 0.8));
dirLightNode.light.enabled = true;
_scene.addChild(dirLightNode);
int x0 = 0;
int y0 = 0;
int z0 = 0;
Mesh _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));
}
Material cubeMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "crate");
Model cubeDrawable = new Model(cubeMaterial, _mesh);
Node3d cubeNode = new Node3d("cubes", cubeDrawable);
_scene.addChild(cubeNode);
debug(fbximport) {
// test FBX import
FbxModelImport importer;
string src = loadTextResource("suzanne.fbx");
importer.filename = "suzanne.fbx";
importer.parse(src);
}
ObjModelImport importer;
string src = loadTextResource("suzanne.obj");
importer.parse(src);
Log.d("suzanne mesh:", importer.mesh.dumpVertexes(20));
Material suzanneMaterial = new Material(EffectId("colored.vert", "colored.frag", null), null); //"SPECULAR"
//suzanneMaterial.ambientColor = vec3(0.5, 0.5, 0.5);
suzanneMaterial.diffuseColor = vec4(0.7, 0.7, 0.5, 1.0);
//suzanneMaterial.specular = true;
Model suzanneDrawable = new Model(suzanneMaterial, importer.mesh);
suzanneNode = new Node3d("suzanne", suzanneDrawable);
suzanneNode.translate(vec3(2, 2, -5));
_scene.addChild(suzanneNode);
brickNode = new Node3d("brick");
brickNode.translate(vec3(-2, 2, -3));
Mesh brickMesh = Mesh.createCubeMesh(vec3(0, 0, 0), 0.8, vec4(0.8, 0.8, 0.8, 1));
Material brickMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "brick", "brickn"); // with bump mapping
brickNode.drawable = new Model(brickMaterial, brickMesh);
_scene.addChild(brickNode);
_minerMesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL, VertexElementType.COLOR, VertexElementType.TEXCOORD0));
_world = new World();
_world.setCell(0, 11, 10, 2);
_world.setCell(5, 11, 15, 2);
for (int x = -100; x < 100; x++)
for (int z = -100; z < 100; z++)
_world.setCell(x, 0, z, 2);
Random rnd;
rnd.setSeed(12345);
for(int i = 0; i < 1000; i++) {
int bx = rnd.next(6)-32;
int by = rnd.next(4);
int bz = rnd.next(6)-32;
//Log.fd("Setting cell %d,%d,%d", bx, by, bz);
_world.setCell(bx, by, bz, 3);
}
_world.camPosition = Position(Vector3d(0, 3, 0), Vector3d(0, 0, 1));
updateMinerMesh();
Material minerMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "blocks");
minerMaterial.textureLinear = false;
Model minerDrawable = new Model(minerMaterial, _minerMesh);
Node3d minerNode = new Node3d("miner", minerDrawable);
_scene.addChild(minerNode);
//minerNode.visible = false;
//cubeNode.visible = false;
//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);
}
Node3d dirLightNode;
Node3d suzanneNode;
Node3d brickNode;
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);
for (int i = 0; i < 20; i++)
Log.d("vertex: ", _minerMesh.vertex(i));
}
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();
suzanneNode.rotateY(interval * 0.000002f);
brickNode.rotateY(interval * 0.00000123f);
brickNode.rotateZ(interval * 0.0000004123f);
brickNode.rotateX(interval * 0.0000007543f);
}
float angle = 0;
Scene3dRef _scene;
Camera _cam;
Mesh _minerMesh;
/// this is OpenGLDrawableDelegate implementation
private void doDraw(Rect windowRect, Rect rc) {
_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);
//Log.d("camPosition: ", _scene.cameraPosition);
//Log.d("camDirection: ", _scene.forwardVectorWorld);
//Log.d("lightPosition: ", dirLightNode.light.position);
//Log.d("lightDirection: ", dirLightNode.light.direction);
//Log.d("lightColor: ", dirLightNode.light.color);
//_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);
_scene.drawScene(false);
checkgl!glDisable(GL_DEPTH_TEST);
checkgl!glDisable(GL_CULL_FACE);
}
~this() {
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);
}
}