dminer example, initial versin

This commit is contained in:
Vadim Lopatin 2016-04-25 08:39:24 +03:00
parent 7ab93a22f5
commit 17e8f78857
14 changed files with 2387 additions and 0 deletions

View File

@ -50,6 +50,11 @@ Project("{002A2DE9-8BB6-484D-9802-7E4AD4084715}") = "ircclient", "examples\irccl
{52A2ABB9-2CF7-4D5F-AE8C-75B21F8585A5} = {52A2ABB9-2CF7-4D5F-AE8C-75B21F8585A5}
EndProjectSection
EndProject
Project("{002A2DE9-8BB6-484D-9802-7E4AD4084715}") = "dminer", "examples\dminer\dminer.visualdproj", "{5F443F6A-6612-4404-B89E-D0D0205DC8E5}"
ProjectSection(ProjectDependencies) = postProject
{52A2ABB9-2CF7-4D5F-AE8C-75B21F8585A5} = {52A2ABB9-2CF7-4D5F-AE8C-75B21F8585A5}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@ -173,6 +178,16 @@ Global
{20722E6B-CA27-467F-8BB8-07F80106B478}.Unittest|Win32.Build.0 = Release|Win32
{20722E6B-CA27-467F-8BB8-07F80106B478}.Unittest|x64.ActiveCfg = Release|Win32
{20722E6B-CA27-467F-8BB8-07F80106B478}.Unittest|x64.Build.0 = Release|Win32
{5F443F6A-6612-4404-B89E-D0D0205DC8E5}.Debug|Win32.ActiveCfg = Debug|Win32
{5F443F6A-6612-4404-B89E-D0D0205DC8E5}.Debug|Win32.Build.0 = Debug|Win32
{5F443F6A-6612-4404-B89E-D0D0205DC8E5}.Debug|x64.ActiveCfg = Debug|Win32
{5F443F6A-6612-4404-B89E-D0D0205DC8E5}.Release|Win32.ActiveCfg = Release|Win32
{5F443F6A-6612-4404-B89E-D0D0205DC8E5}.Release|Win32.Build.0 = Release|Win32
{5F443F6A-6612-4404-B89E-D0D0205DC8E5}.Release|x64.ActiveCfg = Release|Win32
{5F443F6A-6612-4404-B89E-D0D0205DC8E5}.Unittest|Win32.ActiveCfg = Release|Win32
{5F443F6A-6612-4404-B89E-D0D0205DC8E5}.Unittest|Win32.Build.0 = Release|Win32
{5F443F6A-6612-4404-B89E-D0D0205DC8E5}.Unittest|x64.ActiveCfg = Release|Win32
{5F443F6A-6612-4404-B89E-D0D0205DC8E5}.Unittest|x64.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -36,6 +36,7 @@
"./examples/example1/",
"./examples/dmledit/",
"./examples/d3d/",
"./examples/dminer/",
"./examples/tetris/",
"./examples/opengl/",
"./examples/ircclient/",

View File

@ -0,0 +1,220 @@
<DProject>
<ProjectGuid>{5F443F6A-6612-4404-B89E-D0D0205DC8E5}</ProjectGuid>
<Config name="Debug" platform="Win32">
<obj>0</obj>
<link>0</link>
<lib>0</lib>
<subsystem>2</subsystem>
<multiobj>0</multiobj>
<singleFileCompilation>0</singleFileCompilation>
<oneobj>0</oneobj>
<mscoff>0</mscoff>
<trace>0</trace>
<quiet>0</quiet>
<verbose>0</verbose>
<vtls>0</vtls>
<vgc>0</vgc>
<symdebug>3</symdebug>
<optimize>0</optimize>
<cpu>0</cpu>
<isX86_64>0</isX86_64>
<isLinux>0</isLinux>
<isOSX>0</isOSX>
<isWindows>0</isWindows>
<isFreeBSD>0</isFreeBSD>
<isSolaris>0</isSolaris>
<scheduler>0</scheduler>
<useDeprecated>0</useDeprecated>
<errDeprecated>0</errDeprecated>
<useAssert>0</useAssert>
<useInvariants>0</useInvariants>
<useIn>0</useIn>
<useOut>0</useOut>
<useArrayBounds>0</useArrayBounds>
<noboundscheck>0</noboundscheck>
<useSwitchError>0</useSwitchError>
<useUnitTests>0</useUnitTests>
<useInline>0</useInline>
<release>0</release>
<preservePaths>0</preservePaths>
<warnings>0</warnings>
<infowarnings>0</infowarnings>
<checkProperty>0</checkProperty>
<genStackFrame>0</genStackFrame>
<pic>0</pic>
<cov>0</cov>
<nofloat>0</nofloat>
<Dversion>2.043</Dversion>
<ignoreUnsupportedPragmas>0</ignoreUnsupportedPragmas>
<allinst>0</allinst>
<stackStomp>0</stackStomp>
<compiler>0</compiler>
<otherDMD>0</otherDMD>
<cccmd>$(CC) -c</cccmd>
<ccTransOpt>1</ccTransOpt>
<program>$(DMDInstallDir)windows\bin\dmd.exe</program>
<imppath>$(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source</imppath>
<fileImppath>views views/res views/res/i18n views/res/mdpi views/res/hdpi</fileImppath>
<outdir>$(ConfigurationName)</outdir>
<objdir>$(OutDir)</objdir>
<objname />
<libname />
<doDocComments>0</doDocComments>
<docdir />
<docname />
<modules_ddoc />
<ddocfiles />
<doHdrGeneration>0</doHdrGeneration>
<hdrdir />
<hdrname />
<doXGeneration>1</doXGeneration>
<xfilename>$(IntDir)\$(TargetName).json</xfilename>
<debuglevel>0</debuglevel>
<debugids />
<versionlevel>0</versionlevel>
<versionids />
<dump_source>0</dump_source>
<mapverbosity>0</mapverbosity>
<createImplib>0</createImplib>
<defaultlibname />
<debuglibname />
<moduleDepsFile />
<run>0</run>
<runargs />
<runCv2pdb>1</runCv2pdb>
<pathCv2pdb>$(VisualDInstallDir)cv2pdb\cv2pdb.exe</pathCv2pdb>
<cv2pdbPre2043>0</cv2pdbPre2043>
<cv2pdbNoDemangle>0</cv2pdbNoDemangle>
<cv2pdbEnumType>0</cv2pdbEnumType>
<cv2pdbOptions />
<objfiles />
<linkswitches />
<libfiles>ole32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib</libfiles>
<libpaths />
<deffile />
<resfile />
<exefile>$(OutDir)\$(ProjectName).exe</exefile>
<useStdLibPath>1</useStdLibPath>
<cRuntime>2</cRuntime>
<privatePhobos>0</privatePhobos>
<additionalOptions />
<preBuildCommand />
<postBuildCommand />
<filesToClean>*.obj;*.cmd;*.build;*.json;*.dep</filesToClean>
</Config>
<Config name="Release" platform="Win32">
<obj>0</obj>
<link>0</link>
<lib>0</lib>
<subsystem>2</subsystem>
<multiobj>0</multiobj>
<singleFileCompilation>0</singleFileCompilation>
<oneobj>0</oneobj>
<mscoff>0</mscoff>
<trace>0</trace>
<quiet>0</quiet>
<verbose>0</verbose>
<vtls>0</vtls>
<vgc>0</vgc>
<symdebug>0</symdebug>
<optimize>1</optimize>
<cpu>0</cpu>
<isX86_64>0</isX86_64>
<isLinux>0</isLinux>
<isOSX>0</isOSX>
<isWindows>0</isWindows>
<isFreeBSD>0</isFreeBSD>
<isSolaris>0</isSolaris>
<scheduler>0</scheduler>
<useDeprecated>0</useDeprecated>
<errDeprecated>0</errDeprecated>
<useAssert>0</useAssert>
<useInvariants>0</useInvariants>
<useIn>0</useIn>
<useOut>0</useOut>
<useArrayBounds>0</useArrayBounds>
<noboundscheck>0</noboundscheck>
<useSwitchError>0</useSwitchError>
<useUnitTests>0</useUnitTests>
<useInline>1</useInline>
<release>1</release>
<preservePaths>0</preservePaths>
<warnings>0</warnings>
<infowarnings>0</infowarnings>
<checkProperty>0</checkProperty>
<genStackFrame>0</genStackFrame>
<pic>0</pic>
<cov>0</cov>
<nofloat>0</nofloat>
<Dversion>2.043</Dversion>
<ignoreUnsupportedPragmas>0</ignoreUnsupportedPragmas>
<allinst>0</allinst>
<stackStomp>0</stackStomp>
<compiler>0</compiler>
<otherDMD>0</otherDMD>
<cccmd>$(CC) -c</cccmd>
<ccTransOpt>1</ccTransOpt>
<program>$(DMDInstallDir)windows\bin\dmd.exe</program>
<imppath>$(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source</imppath>
<fileImppath>views views/res views/res/i18n views/res/mdpi views/res/hdpi</fileImppath>
<outdir>$(ConfigurationName)</outdir>
<objdir>$(OutDir)</objdir>
<objname />
<libname />
<doDocComments>0</doDocComments>
<docdir />
<docname />
<modules_ddoc />
<ddocfiles />
<doHdrGeneration>0</doHdrGeneration>
<hdrdir />
<hdrname />
<doXGeneration>1</doXGeneration>
<xfilename>$(IntDir)\$(TargetName).json</xfilename>
<debuglevel>0</debuglevel>
<debugids />
<versionlevel>0</versionlevel>
<versionids />
<dump_source>0</dump_source>
<mapverbosity>0</mapverbosity>
<createImplib>0</createImplib>
<defaultlibname />
<debuglibname />
<moduleDepsFile />
<run>0</run>
<runargs />
<runCv2pdb>0</runCv2pdb>
<pathCv2pdb>$(VisualDInstallDir)cv2pdb\cv2pdb.exe</pathCv2pdb>
<cv2pdbPre2043>0</cv2pdbPre2043>
<cv2pdbNoDemangle>0</cv2pdbNoDemangle>
<cv2pdbEnumType>0</cv2pdbEnumType>
<cv2pdbOptions />
<objfiles />
<linkswitches />
<libfiles>ole32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib</libfiles>
<libpaths />
<deffile />
<resfile />
<exefile>$(OutDir)\$(ProjectName).exe</exefile>
<useStdLibPath>1</useStdLibPath>
<cRuntime>1</cRuntime>
<privatePhobos>0</privatePhobos>
<additionalOptions />
<preBuildCommand />
<postBuildCommand />
<filesToClean>*.obj;*.cmd;*.build;*.json;*.dep</filesToClean>
</Config>
<Folder name="dminer">
<Folder name="src">
<Folder name="dminer">
<Folder name="core">
<File path="src\dminer\core\blocks.d" />
<File path="src\dminer\core\minetypes.d" />
<File path="src\dminer\core\terrain.d" />
<File path="src\dminer\core\world.d" />
</Folder>
</Folder>
<File path="src\minermain.d" />
</Folder>
</Folder>
</DProject>

21
examples/dminer/dub.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "dminer",
"description": "dlangui library example DMiner",
"homepage": "https://github.com/buggins/dlangui",
"license": "Boost",
"authors": ["Vadim Lopatin"],
"stringImportPaths": ["views", "views/res", "views/res/i18n", "views/res/mdpi"],
"targetPath": "bin",
"targetName": "dminer",
"targetType": "executable",
"sourceFiles-windows": ["$PACKAGE_DIR/src/win_app.def"],
"versions": ["EmbedStandardResources"],
"dependencies": {
"dlangui": {"path": "../../"}
}
}

View File

@ -0,0 +1,287 @@
module dminer.core.blocks;
import dminer.core.minetypes;
import dminer.core.world;
import dlangui.graphics.scene.mesh;
immutable string BLOCK_TEXTURE_FILENAME = "blocks";
immutable int BLOCK_TEXTURE_DX = 1024;
immutable int BLOCK_TEXTURE_DY = 1024;
immutable int BLOCK_SPRITE_SIZE = 16;
immutable int BLOCK_SPRITE_STEP = 16;
immutable int BLOCK_SPRITE_OFFSET = 0;
immutable int BLOCK_TEXTURE_SPRITES_PER_LINE = 1024/16;
immutable int VERTEX_COMPONENTS = 12;
enum BlockVisibility {
INVISIBLE,
OPAQUE, // completely opaque (cells covered by this block are invisible)
OPAQUE_SEPARATE_TX,
HALF_OPAQUE, // partially paque, cells covered by this block can be visible, render as normal block
HALF_OPAQUE_SEPARATE_TX,
HALF_TRANSPARENT, // should be rendered last (semi transparent texture)
}
class BlockDef {
public:
cell_t id;
string name;
BlockVisibility visibility = BlockVisibility.INVISIBLE;
int txIndex;
this() {
}
this(cell_t blockId, string blockName, BlockVisibility v, int tx) {
id = blockId;
name = blockName;
visibility = v;
txIndex = tx;
}
~this() {
}
// blocks behind this block can be visible
@property bool canPass() {
return visibility == BlockVisibility.INVISIBLE
|| visibility == BlockVisibility.HALF_OPAQUE
|| visibility == BlockVisibility.HALF_OPAQUE_SEPARATE_TX
|| visibility == BlockVisibility.HALF_TRANSPARENT;
}
// block is fully opaque (all blocks behind are invisible)
@property bool isOpaque() {
return visibility == BlockVisibility.OPAQUE
|| visibility == BlockVisibility.OPAQUE_SEPARATE_TX;
}
// block is visible
@property bool isVisible() {
return visibility != BlockVisibility.INVISIBLE;
}
@property bool terrainSmoothing() {
return false;
}
/// create cube face
void createFace(World world, ref Position camPosition, Vector3d pos, Dir face, Mesh mesh) {
// default implementation
ushort startVertexIndex = cast(ushort)mesh.vertexCount;
float[VERTEX_COMPONENTS * 4] vptr;
ushort[6] iptr;
createFaceMesh(vptr.ptr, face, pos.x, pos.y, pos.z, txIndex);
for (int i = 0; i < 6; i++)
iptr[i] = cast(ushort)(startVertexIndex + face_indexes[i]);
//if (HIGHLIGHT_GRID && ((pos.x & 7) == 0 || (pos.z & 7) == 0)) {
// for (int i = 0; i < 4; i++) {
// vptr[11 * i + 6 + 0] = 1.4f;
// vptr[11 * i + 6 + 1] = 1.4f;
// vptr[11 * i + 6 + 2] = 1.4f;
// }
//}
mesh.addVertexes(vptr);
mesh.addPart(PrimitiveType.triangles, iptr);
}
/// create faces
void createFaces(World world, ref Position camPosition, Vector3d pos, int visibleFaces, Mesh mesh) {
for (int i = 0; i < 6; i++)
if (visibleFaces & (1 << i))
createFace(world, camPosition, pos, cast(Dir)i, mesh);
}
}
// pos, normal, color, tx
static const float[VERTEX_COMPONENTS * 4] face_vertices_north =
[
-0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
-0.5, -0.5, -0.5, 0.0, 0.0, -1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
0.5, -0.5, -0.5, 0.0, 0.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
];
static const float[VERTEX_COMPONENTS * 4] face_vertices_south =
[
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
-0.5, 0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
0.5, 0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
];
static const float[VERTEX_COMPONENTS * 4] face_vertices_west =
[
-0.5, -0.5, -0.5, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
-0.5, -0.5, 0.5, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
-0.5, 0.5, -0.5, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
-0.5, 0.5, 0.5, -1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0
];
static const float[VERTEX_COMPONENTS * 4] face_vertices_east =
[
0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
0.5, -0.5, -0.5, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
0.5, 0.5, -0.5, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
];
static const float[VERTEX_COMPONENTS * 4] face_vertices_up =
[
-0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
-0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
];
static const float[VERTEX_COMPONENTS * 4] face_vertices_down =
[
-0.5, -0.5, -0.5, 0.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
0.5, -0.5, -0.5, 0.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
-0.5, -0.5, 0.5, 0.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
0.5, -0.5, 0.5, 0.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
];
static const ushort[6] face_indexes =
[
0, 1, 2, 2, 1, 3
];
static const ushort[6] face_indexes_back =
[
0, 2, 1, 2, 3, 1
];
static void fillFaceMesh(float * data, const float * src, float x0, float y0, float z0, int tileX, int tileY) {
for (int i = 0; i < 4; i++) {
const float * srcvertex = src + i * VERTEX_COMPONENTS;
float * dstvertex = data + i * VERTEX_COMPONENTS;
for (int j = 0; j < VERTEX_COMPONENTS; j++) {
float v = srcvertex[j];
switch (j) {
case 0: // x
v += x0;
break;
case 1: // y
v += y0;
break;
case 2: // z
v += z0;
break;
case 10: // tx.u
v = ((tileX + v * BLOCK_SPRITE_SIZE)) / cast(float)BLOCK_TEXTURE_DX;
break;
case 11: // tx.v
//v = (BLOCK_TEXTURE_DY - (tileY + v * BLOCK_SPRITE_SIZE)) / cast(float)BLOCK_TEXTURE_DY;
v = ((tileY + v * BLOCK_SPRITE_SIZE)) / cast(float)BLOCK_TEXTURE_DY;
break;
default:
break;
}
dstvertex[j] = v;
}
}
}
static void createFaceMesh(float * data, Dir face, float x0, float y0, float z0, int tileIndex) {
int tileX = (tileIndex % BLOCK_TEXTURE_SPRITES_PER_LINE) * BLOCK_SPRITE_STEP + BLOCK_SPRITE_OFFSET;
int tileY = (tileIndex / BLOCK_TEXTURE_SPRITES_PER_LINE) * BLOCK_SPRITE_STEP + BLOCK_SPRITE_OFFSET;
// data is 11 comp * 4 vert floats
switch (face) with(Dir) {
default:
case NORTH:
fillFaceMesh(data, face_vertices_north.ptr, x0, y0, z0, tileX, tileY);
break;
case SOUTH:
fillFaceMesh(data, face_vertices_south.ptr, x0, y0, z0, tileX, tileY);
break;
case WEST:
fillFaceMesh(data, face_vertices_west.ptr, x0, y0, z0, tileX, tileY);
break;
case EAST:
fillFaceMesh(data, face_vertices_east.ptr, x0, y0, z0, tileX, tileY);
break;
case UP:
fillFaceMesh(data, face_vertices_up.ptr, x0, y0, z0, tileX, tileY);
break;
case DOWN:
fillFaceMesh(data, face_vertices_down.ptr, x0, y0, z0, tileX, tileY);
break;
}
}
// block type definitions
__gshared BlockDef[256] BLOCK_DEFS;
// faster check for block->canPass()
__gshared bool[256] BLOCK_TYPE_CAN_PASS;
// faster check for block->isOpaque()
__gshared bool[256] BLOCK_TYPE_OPAQUE;
// faster check for block->isVisible()
__gshared bool[256] BLOCK_TYPE_VISIBLE;
// faster check for block->isVisible()
__gshared bool[256] BLOCK_TERRAIN_SMOOTHING;
/// registers new block type
void registerBlockType(BlockDef def) {
if (BLOCK_DEFS[def.id]) {
if (BLOCK_DEFS[def.id] is def)
return;
destroy(BLOCK_DEFS[def.id]);
}
BLOCK_DEFS[def.id] = def;
// init property shortcuts
BLOCK_TYPE_CAN_PASS[def.id] = def.canPass;
BLOCK_TYPE_OPAQUE[def.id] = def.isOpaque;
BLOCK_TYPE_VISIBLE[def.id] = def.isVisible;
BLOCK_TERRAIN_SMOOTHING[def.id] = def.terrainSmoothing;
}
enum BlockImage : int {
stone,
grass_top,
grass_side,
grass_top_footsteps,
dirt,
bedrock,
sand,
gravel,
sandstone,
clay,
cobblestone,
cobblestone_mossy,
brick,
stonebrick,
red_sand,
}
/// init block types array
__gshared static this() {
import std.string;
for (int i = 0; i < 256; i++) {
if (!BLOCK_DEFS[i]) {
registerBlockType(new BlockDef(cast(cell_t)i, "undef%d".format(i), BlockVisibility.INVISIBLE, 0));
}
}
BLOCK_TYPE_CAN_PASS[BOUND_SKY] = false;
BLOCK_TYPE_VISIBLE[BOUND_SKY] = false;
BLOCK_TYPE_CAN_PASS[BOUND_BOTTOM] = false;
BLOCK_TYPE_VISIBLE[BOUND_BOTTOM] = true;
// empty cell
registerBlockType(new BlockDef(0, "empty", BlockVisibility.INVISIBLE, 0));
// standard block types
registerBlockType(new BlockDef(1, "gray_brick", BlockVisibility.OPAQUE, BlockImage.stonebrick));
registerBlockType(new BlockDef(2, "brick", BlockVisibility.OPAQUE, BlockImage.brick));
registerBlockType(new BlockDef(3, "bedrock", BlockVisibility.OPAQUE, BlockImage.bedrock));
registerBlockType(new BlockDef(4, "clay", BlockVisibility.OPAQUE, BlockImage.clay));
registerBlockType(new BlockDef(5, "cobblestone", BlockVisibility.OPAQUE, BlockImage.cobblestone));
registerBlockType(new BlockDef(6, "gravel", BlockVisibility.OPAQUE, BlockImage.gravel));
registerBlockType(new BlockDef(7, "red_sand", BlockVisibility.OPAQUE, BlockImage.red_sand));
registerBlockType(new BlockDef(8, "sand", BlockVisibility.OPAQUE, BlockImage.sand));
registerBlockType(new BlockDef(50, "box", BlockVisibility.HALF_OPAQUE, 50));
//registerBlockType(new TerrainBlock(100, "terrain_bedrock", 2));
//registerBlockType(new TerrainBlock(101, "terrain_clay", 3));
//registerBlockType(new TerrainBlock(102, "terrain_cobblestone", 4));
//registerBlockType(new TerrainBlock(103, "terrain_gravel", 5));
//registerBlockType(new TerrainBlock(104, "terrain_red_sand", 6));
//registerBlockType(new TerrainBlock(105, "terrain_sand", 7));
}

View File

@ -0,0 +1,731 @@
module dminer.core.minetypes;
alias cell_t = ubyte;
immutable cell_t NO_CELL = 0;
immutable cell_t END_OF_WORLD = 253;
immutable cell_t VISITED_CELL = 255;
immutable cell_t VISITED_OCCUPIED = 254;
immutable cell_t BOUND_BOTTOM = 253;
immutable cell_t BOUND_SKY = 252;
enum Dir : ubyte {
NORTH = 0,
SOUTH,
EAST,
WEST,
UP,
DOWN,
}
// 26 direction masks based on Dir
enum DirMask : ubyte {
MASK_NORTH = (1 << Dir.NORTH),
MASK_SOUTH = (1 << Dir.SOUTH),
MASK_EAST = (1 << Dir.EAST),
MASK_WEST = (1 << Dir.WEST),
MASK_UP = (1 << Dir.UP),
MASK_DOWN = (1 << Dir.DOWN),
MASK_ALL = 0x3F,
//MASK_WEST_UP = (1 << Dir.WEST) | MASK_UP,
//MASK_EAST_UP = (1 << Dir.EAST) | MASK_UP,
//MASK_WEST_DOWN = (1 << Dir.WEST) | MASK_DOWN,
//MASK_EAST_DOWN = (1 << Dir.EAST) | MASK_DOWN,
//MASK_NORTH_WEST = MASK_NORTH | MASK_WEST,
//MASK_NORTH_EAST = MASK_NORTH | MASK_EAST,
//MASK_NORTH_UP = MASK_NORTH | MASK_UP,
//MASK_NORTH_DOWN = MASK_NORTH | MASK_DOWN,
//MASK_NORTH_WEST_UP = MASK_NORTH | MASK_WEST | MASK_UP,
//MASK_NORTH_EAST_UP = MASK_NORTH | MASK_EAST | MASK_UP,
//MASK_NORTH_WEST_DOWN = MASK_NORTH | MASK_WEST | MASK_DOWN,
//MASK_NORTH_EAST_DOWN = MASK_NORTH | MASK_EAST | MASK_DOWN,
//MASK_SOUTH_WEST = MASK_SOUTH | MASK_WEST,
//MASK_SOUTH_EAST = MASK_SOUTH | MASK_EAST,
//MASK_SOUTH_UP = MASK_SOUTH | MASK_UP,
//MASK_SOUTH_DOWN = MASK_SOUTH | MASK_DOWN,
//MASK_SOUTH_WEST_UP = MASK_SOUTH | MASK_WEST | MASK_UP,
//MASK_SOUTH_EAST_UP = MASK_SOUTH | MASK_EAST | MASK_UP,
//MASK_SOUTH_WEST_DOWN = MASK_SOUTH | MASK_WEST | MASK_DOWN,
//MASK_SOUTH_EAST_DOWN = MASK_SOUTH | MASK_EAST | MASK_DOWN,
}
struct Vector2d {
int x;
int y;
this(int xx, int yy) {
x = xx;
y = yy;
}
//bool opEqual(Vector2d v) const {
// return x == v.x && y == v.y;
//}
}
immutable Vector2d ZERO2 = Vector2d(0, 0);
struct Vector3d {
int x;
int y;
int z;
this(int xx, int yy, int zz) {
x = xx;
y = yy;
z = zz;
}
//bool opEqual(const Vector3d v) const {
// return x == v.x && y == v.y && z == v.z;
//}
/// returns vector with all components which are negative of components for this vector
Vector3d opUnary(string op : "-")() const {
return Vector3d(-x, -y, -z);
}
/// subtract vectors
Vector3d opBinary(string op : "-")(const Vector3d v) const {
return Vector3d(x - v.x, y - v.y, z - v.z);
}
/// add vectors
Vector3d opBinary(string op : "+")(const Vector3d v) const {
return Vector3d(x + v.x, y + v.y, z + v.z);
}
///
int opBinary(string op : "*")(const Vector3d v) const {
return x*v.x + y*v.y + z*v.z;
}
/// multiply vector elements by constant
Vector3d opBinary(string op : "*")(int n) const {
return Vector3d(x * n, y * n, z * n);
}
///
ref Vector3d opOpAssign(string op : "+")(const Vector3d v) {
x += v.x;
y += v.y;
z += v.z;
return this;
}
///
ref Vector3d opOpAssign(string op : "-")(const Vector3d v) {
x -= v.x;
y -= v.y;
z -= v.z;
return this;
}
///
ref Vector3d opOpAssign(string op : "*")(int n) {
x *= n;
y *= n;
z *= n;
return this;
}
Vector3d turnLeft() {
return Vector3d(z, y, -x);
}
Vector3d turnRight() {
return Vector3d(-z, y, x);
}
Vector3d turnUp() {
return Vector3d(x, -z, y);
}
Vector3d turnDown() {
return Vector3d(x, z, -y);
}
Vector3d move(Dir dir) {
Vector3d res = this;
switch (dir) with(Dir) {
case NORTH:
res.z--;
break;
case SOUTH:
res.z++;
break;
case WEST:
res.x--;
break;
case EAST:
res.x++;
break;
case UP:
res.y++;
break;
case DOWN:
res.y--;
break;
default:
break;
}
return res;
}
}
const Vector3d ZERO3 = Vector3d(0, 0, 0);
struct Array(T) {
private:
int _length;
T[] _data;
public:
T * ptr(int index = 0) {
return _data.ptr + index;
}
void swap(ref Array v) {
int tmp;
tmp = _length; _length = v._length; v._length = tmp;
T[] ptmp;
ptmp = _data; _data = v._data; v._data = ptmp;
}
/// ensure capacity is enough to fit sz items
void reserve(int sz) {
sz += _length;
if (_data.length < sz) {
int oldsize = cast(int)_data.length;
int newsize = 1024;
while (newsize < sz)
newsize <<= 1;
_data.length = newsize;
for (int i = oldsize; i < newsize; i++)
_data.ptr[i] = T.init;
_data.assumeSafeAppend();
}
}
@property int length() {
return _length;
}
/// append single item by ref
void append(ref const T value) {
if (_length >= _data.length)
reserve(cast(int)(_data.length == 0 ? 64 : _data.length * 2 - _length));
_data.ptr[_length++] = value;
}
/// append single item by value
void append(T value) {
if (_length >= _data.length)
reserve(cast(int)(_data.length == 0 ? 64 : _data.length * 2 - _length));
_data.ptr[_length++] = value;
}
/// append single item w/o check
void appendNoCheck(ref const T value) {
_data.ptr[_length++] = value;
}
/// append single item w/o check
void appendNoCheck(T value) {
_data.ptr[_length++] = value;
}
/// appends same value several times, return pointer to appended items
T* append(ref const T value, int count) {
reserve(count);
int startLen = _length;
for (int i = 0; i < count; i++)
_data.ptr[_length++] = value;
return _data.ptr + startLen;
}
/// appends same value several times, return pointer to appended items
T* append(T value, int count) {
reserve(count);
int startLen = _length;
for (int i = 0; i < count; i++)
_data.ptr[_length++] = value;
return _data.ptr + startLen;
}
void clear() {
_length = 0;
}
T get(int index) {
return _data.ptr[index];
}
void set(int index, T value) {
_data.ptr[index] = value;
}
ref T opIndex(int index) {
return _data.ptr[index];
}
}
alias FloatArray = Array!(float);
alias IntArray = Array!(int);
alias CellArray = Array!(cell_t);
alias Vector2dArray = Array!(Vector2d);
alias Vector3dArray = Array!(Vector3d);
/// array with support of both positive and negative indexes
struct InfiniteArray(T) {
private:
T[] dataPlus;
T[] dataMinus;
int minIdx;
int maxIdx;
public:
@property int minIndex() { return minIdx; }
@property int maxIndex() { return maxIdx; }
void disposeFunction(T p) {
destroy(p);
}
~this() {
foreach(p; dataPlus)
if (p !is T.init)
disposeFunction(p);
foreach(p; dataMinus)
if (p !is T.init)
disposeFunction(p);
}
T get(int index) {
if (index >= 0) {
if (index >= maxIdx)
return T.init;
return dataPlus[index];
} else {
if (index <= minIdx)
return T.init;
return dataMinus[-index];
}
}
void set(int index, T value) {
if (index >= 0) {
if (index >= maxIdx) {
// extend array
if (index <= dataPlus.length) {
int oldsize = dataPlus.length;
int newsize = 1024;
while (newsize <= index)
newsize <<= 1;
dataPlus.length = newsize;
dataPlus.assumeSafeAppend;
for(int i = oldsize; i < newsize; i++)
dataPlus[i] = T.init;
}
maxIdx = index + 1;
}
if (dataPlus[index] !is T.init && dataPlus[index] !is value)
disposeFunction(dataPlus[index]);
dataPlus[index] = value;
} else {
if (index <= minIdx) {
// extend array
if (-index <= dataMinus.length) {
int oldsize = dataMinus.length;
int newsize = 1024;
while (newsize <= -index)
newsize <<= 1;
dataMinus.length = newsize;
dataMinus.assumeSafeAppend;
for(int i = oldsize; i < newsize; i++)
dataMinus[i] = T.init;
}
maxIdx = index - 1;
}
if (dataMinus[-index] !is T.init && dataMinus[-index] !is value)
disposeFunction(dataMinus[-index]);
dataMinus[-index] = value;
}
}
}
struct InfiniteMatrix(T) {
private:
int _size = 0;
int _sizeShift = 0;
int _sizeShiftMul2 = 0;
int _sizeMask = 0;
int _invSizeMask = 0;
T[] _data;
void resize(int newSizeShift) {
int newSize = (1<<newSizeShift);
T[] newdata;
newdata.length = newSize * 2 * newSize * 2;
newdata[0 .. $] = null;
for (int y = -_size; y < _size; y++) {
for (int x = -_size; x < _size; x++) {
T v = get(x, y);
if (x < -newSize || x >= newSize || y < -newSize || y >= newSize) {
// destory: // outside new size
destroy(v);
} else {
// move
newdata[((y + newSize) << (newSizeShift + 1)) | (x + newSize)] = v;
}
}
}
_data = newdata;
_size = newSize;
_sizeShift = newSizeShift;
_sizeShiftMul2 = _sizeShift + 1;
_sizeMask = (1 << _sizeShiftMul2) - 1;
_invSizeMask = ~_sizeMask;
}
int calcIndex(int x, int y) {
return (y << _sizeShiftMul2) + x;
}
public:
@property int size() { return _size; }
T get(int x, int y) {
if (!_data)
return null;
x += _size;
y += _size;
if (!((x | y) & ~_sizeMask)) {
return _data.ptr[(y << _sizeShiftMul2) + x]; //calcIndex(x, y)
}
return null;
}
void set(int x, int y, T v) {
if (x < -_size || x >= _size || y < -_size || y >= _size) {
int newSizeShift = _sizeShift < 6 ? 6 : _sizeShift + 1;
for (; ;newSizeShift++) {
int sz = 1 << newSizeShift;
if (x < -sz || x >= sz || y < -sz || y >= sz)
continue;
break;
}
resize(newSizeShift);
}
x += _size;
y += _size;
int index = calcIndex(x, y);
if (_data.ptr[index])
destroy(_data.ptr[index]);
_data.ptr[index] = v;
}
~this() {
foreach(ref v; _data)
if (v)
destroy(v);
}
}
struct Position {
Vector3d pos;
Direction direction;
this(ref Position p) {
pos = p.pos;
direction = p.direction;
}
this(Vector3d position, Vector3d dir) {
pos = position;
direction = dir;
}
Vector2d calcPlaneCoords(Vector3d v) {
v = v - pos;
switch (direction.dir) with(Dir) {
default:
case NORTH:
return Vector2d(v.x, v.y);
case SOUTH:
return Vector2d(-v.x, v.y);
case EAST:
return Vector2d(v.z, v.y);
case WEST:
return Vector2d(-v.z, v.y);
case UP:
return Vector2d(-v.z, v.x);
case DOWN:
return Vector2d(v.z, v.x);
}
}
void turnLeft() {
direction.turnLeft();
}
void turnRight() {
direction.turnRight();
}
void shiftLeft(int step = 1) {
pos += direction.left * step;
}
void shiftRight(int step = 1) {
pos += direction.right * step;
}
void turnUp() {
direction.turnUp();
}
void turnDown() {
direction.turnDown();
}
void forward(int step = 1) {
pos += direction.forward * step;
}
void backward(int step = 1) {
pos -= direction.forward * step;
}
void moveUp(int step = 1) {
pos += direction.up * step;
}
void moveDown(int step = 1) {
pos += direction.down * step;
}
void moveLeft(int step = 1) {
pos += direction.left * step;
}
void moveRight(int step = 1) {
pos += direction.right * step;
}
}
/// returns opposite direction to specified direction
Dir opposite(Dir d) {
return cast(Dir)(d ^ 1);
}
Dir turnLeft(Dir d) {
switch (d) with (Dir) {
case WEST:
return SOUTH;
case EAST:
return NORTH;
default:
case NORTH:
return WEST;
case SOUTH:
return EAST;
case UP:
return SOUTH;
case DOWN:
return NORTH;
}
}
Dir turnRight(Dir d) {
switch (d) with (Dir) {
case WEST:
return NORTH;
case EAST:
return SOUTH;
default:
case NORTH:
return EAST;
case SOUTH:
return WEST;
case UP:
return NORTH;
case DOWN:
return SOUTH;
}
}
Dir turnUp(Dir d) {
switch (d) with (Dir) {
case WEST:
return UP;
case EAST:
return UP;
default:
case NORTH:
return UP;
case SOUTH:
return UP;
case UP:
return SOUTH;
case DOWN:
return NORTH;
}
}
Dir turnDown(Dir d) {
switch (d) with (Dir) {
case WEST:
return DOWN;
case EAST:
return DOWN;
default:
case NORTH:
return DOWN;
case SOUTH:
return DOWN;
case UP:
return NORTH;
case DOWN:
return SOUTH;
}
}
struct Direction {
this(int x, int y, int z) {
set(x, y, z);
}
this(Vector3d v) {
set(v);
}
this(Dir d) {
set(d);
}
/// returns Y axis rotation angle in degrees (0, 90, 180, 270)
@property float angle() {
switch (dir) with (Dir) {
default:
case NORTH:
return 0;
case SOUTH:
return 180;
case WEST:
return 90;
case EAST:
return 270;
case UP:
case DOWN:
return 0;
}
}
/// set by direction code
void set(Dir d) {
switch (d) with (Dir) {
default:
case NORTH:
set(0, 0, -1);
break;
case SOUTH:
set(0, 0, 1);
break;
case WEST:
set(-1, 0, 0);
break;
case EAST:
set(1, 0, 0);
break;
case UP:
set(0, 1, 0);
break;
case DOWN:
set(0, -1, 0);
break;
}
}
/// set by vector
void set(Vector3d v) { set(v.x, v.y, v.z); }
/// set by vector
void set(int x, int y, int z) {
forward = Vector3d(x, y, z);
if (x) {
dir = (x > 0) ? Dir.EAST : Dir.WEST;
}
else if (y) {
dir = (y > 0) ? Dir.UP : Dir.DOWN;
}
else {
dir = (z > 0) ? Dir.SOUTH : Dir.NORTH;
}
switch (dir) with (Dir) {
case UP:
up = Vector3d(1, 0, 0);
left = Vector3d(0, 0, 1);
break;
case DOWN:
up = Vector3d(1, 0, 0);
left = Vector3d(0, 0, -1);
break;
default:
case NORTH:
up = Vector3d(0, 1, 0);
left = Vector3d(-1, 0, 0);
break;
case SOUTH:
up = Vector3d(0, 1, 0);
left = Vector3d(1, 0, 0);
break;
case EAST:
up = Vector3d(0, 1, 0);
left = Vector3d(0, 0, -1);
break;
case WEST:
up = Vector3d(0, 1, 0);
left = Vector3d(0, 0, 1);
break;
}
down = -up;
right = -left;
forwardUp = forward + up;
forwardDown = forward + down;
forwardLeft = forward + left;
forwardLeftUp = forward + left + up;
forwardLeftDown = forward + left + down;
forwardRight = forward + right;
forwardRightUp = forward + right + up;
forwardRightDown = forward + right + down;
}
void turnLeft() {
set(.turnLeft(dir));
}
void turnRight() {
set(.turnRight(dir));
}
void turnUp() {
set(.turnUp(dir));
}
void turnDown() {
set(.turnDown(dir));
}
Dir dir;
Vector3d forward;
Vector3d up;
Vector3d right;
Vector3d left;
Vector3d down;
Vector3d forwardUp;
Vector3d forwardDown;
Vector3d forwardLeft;
Vector3d forwardLeftUp;
Vector3d forwardLeftDown;
Vector3d forwardRight;
Vector3d forwardRightUp;
Vector3d forwardRightDown;
}
/// returns number of bits to store integer
int bitsFor(int n) {
int res;
for (res = 0; n > 0; res++)
n >>= 1;
return res;
}
/// returns 0 for 0, 1 for negatives, 2 for positives
int mySign(int n) {
if (n > 0)
return 1;
else if (n < 0)
return -1;
else
return 0;
}
immutable ulong RANDOM_MULTIPLIER = 0x5DEECE66D;
immutable ulong RANDOM_MASK = ((cast(ulong)1 << 48) - 1);
immutable ulong RANDOM_ADDEND = cast(ulong)0xB;
struct Random {
ulong seed;
//Random();
void setSeed(ulong value) {
seed = (value ^ RANDOM_MULTIPLIER) & RANDOM_MASK;
}
int next(int bits) {
seed = (seed * RANDOM_MULTIPLIER + RANDOM_ADDEND) & RANDOM_MASK;
return cast(int)(seed >> (48 - bits));
}
int nextInt() {
return next(31);
}
int nextInt(int n) {
if ((n & -n) == n) // i.e., n is a power of 2
return cast(int)((n * cast(long)next(31)) >> 31);
int bits, val;
do {
bits = next(31);
val = bits % n;
} while (bits - val + (n - 1) < 0);
return val;
}
}
const Vector3d[6] DIRECTION_VECTORS = [
Vector3d(0, 0, -1),
Vector3d(0, 0, 1),
Vector3d(-1, 0, 0),
Vector3d(1, 0, 0),
Vector3d(0, 1, 0),
Vector3d(0, -1, 0)
];

View File

@ -0,0 +1,153 @@
module dminer.core.terrain;
import dminer.core.minetypes;
struct TerrainGen {
private int dx;
private int dy;
private int xpow;
private int ypow;
private short[] data;
private Random rnd;
private void diamond(int x, int y, int size, int offset) {
int avg = (get(x, y - size) + get(x + size, y) + get(x, y + size) + get(x - size, y)) >> 2;
set(x, y, avg + offset);
}
private void square(int x, int y, int size, int offset) {
int avg = (get(x - size, y - size) + get(x + size, y - size) + get(x - size, y + size) + get(x - size, y - size)) >> 2;
set(x, y, avg + offset);
}
this(int xbits, int zbits) {
xpow = xbits;
ypow = zbits;
dx = (1 << xpow) + 1;
dy = (1 << ypow) + 1;
data = new short[dx * dy];
}
~this() {
}
void filter(int range) {
short[] tmp = new short[dx * dy];
int div = (range * 2 + 1) * (range * 2 + 1);
for (int y = 0; y < dy; y++) {
for (int x = 0; x < dx; x++) {
int s = 0;
for (int yy = -range; yy <= range; yy++) {
for (int xx = -range; xx <= range; xx++) {
s += get(x + xx, y + yy);
}
}
s /= div;
tmp[(y << ypow) + y + x] = cast(short)s;
}
}
int sz = dx * dy;
data[0 .. sz] = tmp[0 .. sz];
}
void generate(int seed, short[] initData, int stepBits) {
rnd.setSeed(seed);
int step = 1 << stepBits;
int index = 0;
for (int y = 0; y <= dy; y += step) {
for (int x = 0; x <= dx; x += step) {
set(x, y, initData[index++]);
}
}
int half = step >> 1;
while (half > 0) {
int scale = step;
for (int y = half; y < dy; y += step) {
for (int x = half; x < dx; x++) {
square(x, y, half, rnd.nextInt(scale * 2) - scale);
}
}
for (int y = 0; y <= dy; y += half) {
for (int x = (y + half) % step; x <= dx; x += step) {
diamond(x, y, half, rnd.nextInt(scale * 2) - scale);
}
}
step >>= 1;
half >>= 1;
}
}
void generateWithScale(int seed, short[] initData, int stepBits, TerrainGen scaleMap) {
rnd.setSeed(seed);
int step = 1 << stepBits;
int index = 0;
for (int y = 0; y <= dy; y += step) {
for (int x = 0; x <= dx; x += step) {
set(x, y, initData[index++]);
}
}
int half = step >> 1;
while (half > 0) {
for (int y = half; y < dy; y += step) {
for (int x = half; x < dx; x++) {
int scale = (scaleMap.get(x, y) * step) >> 8;
scale = rnd.nextInt(scale * 2) - scale;
if (step < 4)
scale = 0;
square(x, y, half, scale);
}
}
for (int y = 0; y <= dy; y += half) {
for (int x = (y + half) % step; x <= dx; x += step) {
int scale = (scaleMap.get(x, y) * step) >> 8;
scale = rnd.nextInt(scale * 2) - scale;
if (step < 4)
scale = 0;
diamond(x, y, half, scale);
}
}
step >>= 1;
half >>= 1;
}
}
@property int width() {
return dx - 1;
}
@property int height() {
return dy - 1;
}
int get(int x, int y) {
if (x < 0 || y < 0 || x >= dx || y >= dy)
return 0;
return data[(y << ypow) + y + x];
}
void set(int x, int y, int value) {
if (x < 0 || y < 0 || x >= dx || y >= dy)
return;
if (value < -32767)
value = -32767;
if (value > 32767)
value = 32767;
data[(y << ypow) + y + x] = cast(short)value;
}
/// ensure that data is in range [minvalue, maxvalue]
void limit(int minvalue, int maxvalue) {
// find actual min/max
int minv, maxv;
minv = maxv = get(0, 0);
for (int y = 0; y <= dy; y++) {
for (int x = 0; x <= dx; x++) {
int v = get(x, y);
if (minv > v)
minv = v;
if (maxv < v)
maxv = v;
}
}
int mul = (maxvalue - minvalue);
int div = (maxv - minv);
if (div > 0) {
for (int y = 0; y <= dy; y++) {
for (int x = 0; x <= dx; x++) {
set(x, y, minvalue + (get(x, y) - minv) * mul / div);
}
}
}
}
}

View File

@ -0,0 +1,501 @@
module dminer.core.world;
import dminer.core.minetypes;
import dminer.core.blocks;
const int MAX_VIEW_DISTANCE_BITS = 6;
const int MAX_VIEW_DISTANCE = (1 << MAX_VIEW_DISTANCE_BITS);
// Layer is 16x16 (CHUNK_DX_SHIFT x CHUNK_DX_SHIFT) cells
immutable int CHUNK_DX_SHIFT = 4;
immutable int CHUNK_DX = (1<<CHUNK_DX_SHIFT);
immutable int CHUNK_DX_MASK = (CHUNK_DX - 1);
// Y range: 0..CHUNK_DY-1
immutable int CHUNK_DY_SHIFT = 6;
immutable int CHUNK_DY = (1<<CHUNK_DY_SHIFT);
immutable int CHUNK_DY_MASK = (CHUNK_DY - 1);
immutable int CHUNK_DY_INV_MASK = ~CHUNK_DY_MASK;
//extern bool HIGHLIGHT_GRID;
// Layer is 256x16x16 CHUNK_DY layers = CHUNK_DY * (CHUNK_DX_SHIFT x CHUNK_DX_SHIFT) cells
struct ChunkLayer {
cell_t[CHUNK_DX * CHUNK_DX] cells;
cell_t* ptr(int x, int z) {
return &cells.ptr[(z << CHUNK_DX_SHIFT) + x];
}
cell_t get(int x, int z) {
return cells.ptr[(z << CHUNK_DX_SHIFT) + x];
}
void set(int x, int z, cell_t cell) {
cells.ptr[(z << CHUNK_DX_SHIFT) + x] = cell;
}
}
struct Chunk {
private:
ChunkLayer*[CHUNK_DY] layers;
int bottomLayer = - 1;
int topLayer = -1;
public:
~this() {
for (int i = 0; i < CHUNK_DY; i++)
if (layers[i])
destroy(layers[i]);
}
int getMinLayer() { return bottomLayer; }
int getMaxLayer() { return topLayer; }
void updateMinMaxLayer(ref int minLayer, ref int maxLayer) {
if (minLayer == -1 || minLayer > bottomLayer)
minLayer = bottomLayer;
if (maxLayer == -1 || maxLayer < topLayer)
maxLayer = topLayer;
}
cell_t get(int x, int y, int z) {
//if (!this)
// return NO_CELL;
ChunkLayer * layer = layers[y & CHUNK_DY_MASK];
if (!layer)
return NO_CELL;
return layer.get(x & CHUNK_DX_MASK, z & CHUNK_DY_MASK);
}
/// get, x, y, z are already checked for bounds
cell_t getNoCheck(int x, int y, int z) {
ChunkLayer * layer = layers.ptr[y];
if (!layer) // likely
return NO_CELL;
return layer.cells.ptr[(z << CHUNK_DX_SHIFT) + x]; // inlined return layer.get(x, z);
}
void set(int x, int y, int z, cell_t cell) {
int layerIndex = y & CHUNK_DY_MASK;
ChunkLayer * layer = layers.ptr[layerIndex];
if (!layer) {
layer = new ChunkLayer();
layers.ptr[layerIndex] = layer;
if (topLayer == -1 || topLayer < layerIndex)
topLayer = layerIndex;
if (bottomLayer == -1 || bottomLayer > layerIndex)
bottomLayer = layerIndex;
}
layer.set(x & CHUNK_DX_MASK, z & CHUNK_DY_MASK, cell);
}
/// srcpos coords x, z are in chunk bounds
//void getCells(Vector3d srcpos, Vector3d dstpos, Vector3d size, VolumeData & buf);
}
alias ChunkMatrix = InfiniteMatrix!(Chunk *);
/// Voxel World
class World {
private:
Position _camPosition;
int maxVisibleRange = MAX_VIEW_DISTANCE;
ChunkMatrix chunks;
DiamondVisitor visitorHelper;
public:
this() {
_camPosition = Position(Vector3d(0, 13, 0), Vector3d(0, 0, 1));
}
~this() {
}
@property final ref Position camPosition() { return _camPosition; }
final cell_t getCell(int x, int y, int z) {
if (!(y & CHUNK_DY_INV_MASK)) {
if (Chunk * p = chunks.get(x >> CHUNK_DX_SHIFT, z >> CHUNK_DX_SHIFT))
return p.getNoCheck(x & CHUNK_DX_MASK, y, z & CHUNK_DX_MASK);
return NO_CELL;
}
// y out of bounds
if (y < 0)
return BOUND_BOTTOM;
//if (y >= CHUNK_DY)
else
return BOUND_SKY;
}
final bool isOpaque(int x, int y, int z) {
cell_t cell = getCell(x, y, z);
return BLOCK_TYPE_OPAQUE.ptr[cell] && cell != BOUND_SKY;
}
final void setCell(int x, int y, int z, cell_t value) {
int chunkx = x >> CHUNK_DX_SHIFT;
int chunkz = z >> CHUNK_DX_SHIFT;
Chunk * p = chunks.get(chunkx, chunkz);
if (!p) {
p = new Chunk();
chunks.set(chunkx, chunkz, p);
}
p.set(x & CHUNK_DX_MASK, y, z & CHUNK_DX_MASK, value);
}
void setCellRange(Vector3d pos, Vector3d sz, cell_t value) {
for (int x = 0; x < sz.x; x++)
for (int y = 0; y < sz.y; y++)
for (int z = 0; z < sz.z; z++)
setCell(pos.x + x, pos.y + y, pos.z + z, value);
}
bool canPass(Vector3d pos) {
return canPass(Vector3d(pos.x - 2, pos.y - 3, pos.z - 2), Vector3d(4, 5, 4));
}
bool canPass(Vector3d pos, Vector3d size) {
for (int x = 0; x <= size.x; x++)
for (int z = 0; z <= size.z; z++)
for (int y = 0; y < size.y; y++) {
if (isOpaque(pos.x + x, pos.y + y, pos.z + z))
return false;
}
return true;
}
final void visitVisibleCells(ref Position position, CellVisitor visitor) {
visitorHelper.init(this,
&position,
visitor);
visitorHelper.visitAll(maxVisibleRange);
}
}
interface CellVisitor {
//void newDirection(ref Position camPosition);
//void visitFace(World world, ref Position camPosition, Vector3d pos, cell_t cell, Dir face);
void visit(World world, ref Position camPosition, Vector3d pos, cell_t cell, int visibleFaces);
}
struct DiamondVisitor {
int maxDist;
int maxDistBits;
int dist;
World world;
Position * position;
Vector3d pos0;
CellVisitor visitor;
CellArray visited;
cell_t * visited_ptr;
Vector3dArray oldcells;
Vector3dArray newcells;
ubyte visitedId;
//ubyte visitedEmpty;
int m0;
int m0mask;
void init(World w, Position * pos, CellVisitor v) {
world = w;
position = pos;
visitor = v;
pos0 = position.pos;
}
void visitCell(int vx, int vy, int vz) {
//CRLog::trace("visitCell(%d %d %d) dist=%d", v.x, v.y, v.z, myAbs(v.x) + myAbs(v.y) + myAbs(v.z));
//int occupied = visitedOccupied;
int index = (vx + m0) + ((vz + m0) << (maxDistBits + 1));
if (vy < 0) {
// inverse index for lower half
index ^= m0mask;
}
//int index = diamondIndex(v, maxDistBits);
if (visited_ptr[index] == visitedId)// || cell == visitedEmpty)
return;
visitCellNoCheck(vx, vy, vz);
visited_ptr[index] = visitedId; // cell;
}
void visitCellNoCheck(int vx, int vy, int vz) {
//if (v * position.direction.forward < dist / 3) // limit by visible from cam
// return;
//Vector3d pos = pos0 + v;
int posx = pos0.x + vx;
int posy = pos0.y + vy;
int posz = pos0.z + vz;
cell_t cell = world.getCell(posx, posy, posz);
// read cell from world
if (BLOCK_TYPE_VISIBLE.ptr[cell]) {
int visibleFaces = 0;
if (vy <= 0 && !world.isOpaque(posx, posy + 1, posz))
visibleFaces |= DirMask.MASK_UP;
if (vy >= 0 && !world.isOpaque(posx, posy - 1, posz))
visibleFaces |= DirMask.MASK_DOWN;
if (vx <= 0 && !world.isOpaque(posx + 1, posy, posz))
visibleFaces |= DirMask.MASK_EAST;
if (vx >= 0 && !world.isOpaque(posx - 1, posy, posz))
visibleFaces |= DirMask.MASK_WEST;
if (vz <= 0 && !world.isOpaque(posx, posy, posz + 1))
visibleFaces |= DirMask.MASK_SOUTH;
if (vz >= 0 && !world.isOpaque(posx, posy, posz - 1))
visibleFaces |= DirMask.MASK_NORTH;
visitor.visit(world, *position, Vector3d(posx, posy, posz), cell, visibleFaces);
}
// mark as visited
if (BLOCK_TYPE_CAN_PASS.ptr[cell])
newcells.append(Vector3d(vx, vy, vz));
//cell = BLOCK_TYPE_CAN_PASS[cell] ? visitedEmpty : visitedOccupied;
}
bool needVisit(int index) {
if (visited_ptr[index] != visitedId) {
visited_ptr[index] = visitedId;
return true;
}
return false;
}
static int myAbs(int n) {
return n < 0 ? -n : n;
}
void visitAll(int maxDistance) {
maxDist = maxDistance;
maxDistance *= 2;
maxDistBits = bitsFor(maxDist);
int maxDistMask = ~((1 << maxDistBits) - 1);
maxDistBits++;
m0 = 1 << maxDistBits;
m0mask = (m0 - 1) + ((m0 - 1) << (maxDistBits + 1));
oldcells.clear();
newcells.clear();
oldcells.reserve(maxDist * 4 * 4);
newcells.reserve(maxDist * 4 * 4);
dist = 1;
int vsize = ((1 << maxDistBits) * (1 << maxDistBits)) << 2;
visited.clear();
visited.append(cast(ubyte)0, vsize);
visited_ptr = visited.ptr();
visitedId = 2;
oldcells.clear();
oldcells.append(Vector3d(0, 0, 0));
Dir dir = position.direction.dir;
int zstep = 1 << (maxDistBits + 1);
for (; dist < maxDistance; dist++) {
// for each distance
if (oldcells.length() == 0) { // no cells to pass through
import dlangui.core.logger;
Log.d("No more cells at distance ", dist);
break;
}
newcells.clear();
visitedId++;
int maxUp = (((dist + 1) * 7) / 8) + 1;
int maxDown = - (dist < 3 ? 3 : (((dist + 1) * 7) / 8)) - 1;
//CRLog::trace("dist: %d cells: %d", dist, oldcells.length());
for (int i = 0; i < oldcells.length(); i++) {
Vector3d pt = oldcells[i];
assert(myAbs(pt.x) + myAbs(pt.y) + myAbs(pt.z) == dist - 1);
if (((pt.x + maxDist) | (pt.y + maxDist) | (pt.z + maxDist)) & maxDistMask)
continue;
if (dist > 2) {
// skip some directions
if (pt.y > maxUp || pt.y < maxDown)
continue;
if (dir == Dir.SOUTH) {
if (pt.z < -1)
continue;
} else if (dir == Dir.NORTH) {
if (pt.z > 1)
continue;
} else if (dir == Dir.EAST) {
if (pt.x < -1)
continue;
} else { // WEST
if (pt.x > 1)
continue;
}
}
int mx = pt.x;
int my = pt.y;
int mz = pt.z;
int sx = mx > 0 ? 1 : 0;
int sy = my > 0 ? 1 : 0;
int sz = mz > 0 ? 1 : 0;
if (mx < 0) {
mx = -mx;
sx = -1;
}
if (my < 0) {
my = -my;
sy = -1;
}
if (mz < 0) {
mz = -mz;
sz = -1;
}
int ymask = sy < 0 ? m0mask : 0;
int index = ((pt.x + m0) + ((pt.z + m0) << (maxDistBits + 1))) ^ ymask;
if (sx && sy && sz) {
//bool noStepZ = (mx > mz) || (my > mz);
// 1, 1, 1
int xindex = index + (sy < 0 ? -sx : sx);
if (visited_ptr[xindex] != visitedId) {
visitCellNoCheck(pt.x + sx, pt.y, pt.z);
visited_ptr[xindex] = visitedId;
}
int zindex = index + (sz * sy > 0 ? zstep : -zstep);
if (visited_ptr[zindex] != visitedId) {
visitCellNoCheck(pt.x, pt.y, pt.z + sz);
visited_ptr[zindex] = visitedId;
}
if (!ymask && sy < 0)
index ^= m0mask;
if (visited_ptr[index] != visitedId) {
visitCellNoCheck(pt.x, pt.y + sy, pt.z);
visited_ptr[index] = visitedId;
}
} else {
// has 0 in one of coords
if (!sx) {
if (!sy) {
if (!sz) {
// 0, 0, 0
visitCell(pt.x + 1, pt.y, pt.z);
visitCell(pt.x - 1, pt.y, pt.z);
visitCell(pt.x, pt.y + 1, pt.z);
visitCell(pt.x, pt.y - 1, pt.z);
visitCell(pt.x, pt.y, pt.z + 1);
visitCell(pt.x, pt.y, pt.z - 1);
} else {
// 0, 0, 1
visitCell(pt.x, pt.y, pt.z + sz);
visitCell(pt.x + 1, pt.y, pt.z);
visitCell(pt.x - 1, pt.y, pt.z);
visitCell(pt.x, pt.y + 1, pt.z);
visitCell(pt.x, pt.y - 1, pt.z);
}
} else {
if (!sz) {
// 0, 1, 0
visitCell(pt.x, pt.y + sy, pt.z);
visitCell(pt.x + 1, pt.y, pt.z);
visitCell(pt.x - 1, pt.y, pt.z);
visitCell(pt.x, pt.y, pt.z + 1);
visitCell(pt.x, pt.y, pt.z - 1);
} else {
// 0, 1, 1
visitCell(pt.x, pt.y + sy, pt.z);
visitCell(pt.x, pt.y, pt.z + sz);
visitCell(pt.x + 1, pt.y, pt.z);
visitCell(pt.x - 1, pt.y, pt.z);
}
}
} else {
if (!sy) {
if (!sz) {
// 1, 0, 0
visitCell(pt.x + sx, pt.y, pt.z);
visitCell(pt.x, pt.y + 1, pt.z);
visitCell(pt.x, pt.y - 1, pt.z);
visitCell(pt.x, pt.y, pt.z + 1);
visitCell(pt.x, pt.y, pt.z - 1);
} else {
// 1, 0, 1
visitCell(pt.x + sx, pt.y, pt.z);
visitCell(pt.x, pt.y, pt.z + sz);
visitCell(pt.x, pt.y + 1, pt.z);
visitCell(pt.x, pt.y - 1, pt.z);
}
} else {
// 1, 1, 0
visitCell(pt.x + sx, pt.y, pt.z);
visitCell(pt.x, pt.y + sy, pt.z);
visitCell(pt.x, pt.y, pt.z + 1);
visitCell(pt.x, pt.y, pt.z - 1);
}
}
}
}
newcells.swap(oldcells);
}
}
}
static short[] TERRAIN_INIT_DATA = [
// V
10, 10, 10, 10, 30, 30, 30, 30, 30, 30, 30, 30, 10, 10, 10, 10, 10,
10, 10, 20, 50, 50, 50, 50, 50, 50, 50, 50, 50, 20, 20, 20, 20, 10,
10, 20, 20, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 20, 20, 10,
10, 20, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 20, 10,
10, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 20, 30,
30, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 30,
30, 50, 50, 50, 50, 50, 50, 50, 120, 50, 50, 50, 50, 50, 50, 50, 30,
30, 50, 50, 50, 50, 50, 50, 110, 140, 130, 50, 50, 50, 50, 50, 50, 30,
30, 50, 50, 50, 50, 50, 50, 140, 150, 140, 50, 50, 50, 50, 50, 50, 30, // <==
30, 50, 50, 50, 50, 50, 50, 110, 140, 120, 50, 50, 50, 50, 50, 50, 30,
30, 50, 50, 50, 50, 50, 50, 50, 110, 50, 50, 50, 50, 50, 50, 50, 30,
30, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 10,
30, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 10,
30, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 40, 50, 10,
30, 20, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 40, 20, 20, 10,
30, 20, 20, 50, 50, 50, 50, 50, 50, 50, 40, 20, 20, 20, 20, 20, 10,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 10, 10, 10, 10, 10,
// ^
];
static short[] TERRAIN_SCALE_DATA = [
// V
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 30, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 45, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 80, 20, 20, 20, 40, 50, 40, 20, 20,
20, 20, 20, 20, 20, 20, 90, 20, 80, 20, 30, 20, 20, 30, 20, 20, 20,
20, 20, 20, 20, 20, 90, 20, 80, 30, 20, 40, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 90, 30, 40, 30, 50, 20, 20, 20, 20, 20, 20, // <==
20, 20, 20, 20, 20, 20, 50, 20, 30, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 40, 70, 40, 90, 20, 40, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 80, 20, 50, 70, 50, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 60, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
// ^
];
void initWorldTerrain(World world, int terrSizeBits = 10, int x0 = 0, int z0 = 0) {
import dminer.core.terrain;
int terrSize = 1 << terrSizeBits;
TerrainGen scaleterr = TerrainGen(terrSizeBits, terrSizeBits); // 512x512
scaleterr.generate(4321, TERRAIN_SCALE_DATA, terrSizeBits - 4); // init grid is 16x16 (1 << (9-7))
scaleterr.filter(1);
//scaleterr.filter(2);
scaleterr.limit(0, 90);
TerrainGen terr = TerrainGen(terrSizeBits, terrSizeBits); // 512x512
terr.generateWithScale(123456, TERRAIN_INIT_DATA, terrSizeBits - 4, scaleterr); // init grid is 16x16 (1 << (9-7))
terr.filter(1);
terr.limit(5, CHUNK_DY * 3 / 4);
terr.filter(1);
for (int x = 0; x < terrSize; x++) {
for (int z = 0; z < terrSize; z++) {
int h = terr.get(x, z);
cell_t cell = 1;
//if (h < CHUNK_DY / 10)
// cell = 100;
//else if (h < CHUNK_DY / 5)
// cell = 101;
//else if (h < CHUNK_DY / 4)
// cell = 102;
//else if (h < CHUNK_DY / 3)
// cell = 103;
//else if (h < CHUNK_DY / 2)
// cell = 104;
//else
// cell = 105;
for (int y = 0; y < h; y++) {
world.setCell(x0 + x - terrSize / 2, y, z0 + z - terrSize / 2, cell);
}
}
}
}

View File

@ -0,0 +1,380 @@
module minermain;
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.glsupport;
import dlangui.graphics.gldrawbuf;
import dlangui.graphics.scene.effect;
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")());
//embeddedResourceList.dumpEmbeddedResources();
// create window
Window window = Platform.instance.createWindow("DlangUI Voxel RPG", 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: 0
padding: 0
//backgroundImageId: "tx_fabric.tiled"
backgroundColor: 0x000000;
layoutWidth: fill
layoutHeight: fill
VerticalLayout {
id: glView
margins: 0
padding: 0
layoutWidth: fill
layoutHeight: fill
TextWidget { text: "MinerD example"; textColor: "red"; fontSize: 150%; fontWeight: 800; fontFace: "Arial" }
VSpacer { layoutWeight: 30 }
TextWidget { id: lblPosition; text: ""; backgroundColor: 0x80202020; textColor: 0xFFE0E0 }
}
}
}, "", 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));
_scene = new Scene3d();
_cam = new Camera();
_cam.translate(vec3(0, 14, -7));
_scene.activeCamera = _cam;
dirLightNode = new Node3d();
dirLightNode.rotateY(-15);
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.enabled = true;
_scene.addChild(dirLightNode);
int x0 = 0;
int y0 = 0;
int z0 = 0;
_minerMesh = new Mesh(VertexFormat(VertexElementType.POSITION, VertexElementType.NORMAL, VertexElementType.COLOR, VertexElementType.TEXCOORD0));
_world = new World();
initWorldTerrain(_world);
int cy0 = 3;
for (int y = CHUNK_DY - 1; y > 0; y--)
if (!_world.canPass(Vector3d(0, y, 0))) {
cy0 = y;
break;
}
_world.camPosition = Position(Vector3d(0, cy0, 0), Vector3d(0, 0, 1));
_world.setCellRange(Vector3d(3, 11, 5), Vector3d(1, 100, 1), 1);
_world.setCellRange(Vector3d(13, 11, -5), Vector3d(1, 100, 1), 3);
_world.setCellRange(Vector3d(-6, 11, 10), Vector3d(1, 100, 1), 4);
_world.setCellRange(Vector3d(-8, 11, 15), Vector3d(1, 100, 1), 5);
_world.setCellRange(Vector3d(12, 11, -7), Vector3d(1, 100, 1), 6);
_world.setCellRange(Vector3d(5, 11, 9), Vector3d(1, 100, 1), 7);
_world.setCellRange(Vector3d(9, 11, 5), Vector3d(1, 100, 1), 7);
_world.setCellRange(Vector3d(-5, 11, 9), Vector3d(1, 100, 1), 7);
_world.setCellRange(Vector3d(9, 11, -5), Vector3d(1, 100, 1), 7);
_world.setCellRange(Vector3d(5, 11, -9), Vector3d(1, 100, 1), 7);
_world.setCellRange(Vector3d(-9, 11, 5), Vector3d(1, 100, 1), 7);
_world.setCellRange(Vector3d(7, 11, 3), Vector3d(1, 100, 1), 8);
_world.setCellRange(Vector3d(-7, 11, 3), Vector3d(1, 100, 1), 8);
_world.setCellRange(Vector3d(7, 11, -3), Vector3d(1, 100, 1), 8);
_world.setCellRange(Vector3d(-7, 11, 3), Vector3d(1, 100, 1), 8);
updateCamPosition(false);
updateMinerMesh();
Material minerMaterial = new Material(EffectId("textured.vert", "textured.frag", null), "blocks");
minerMaterial.ambientColor = vec3(0.2,0.2,0.2);
minerMaterial.textureLinear = false;
Model minerDrawable = new Model(minerMaterial, _minerMesh);
Node3d minerNode = new Node3d("miner", minerDrawable);
_scene.addChild(minerNode);
focusable = true;
}
/// process key event, return true if event is processed.
override bool onKeyEvent(KeyEvent event) {
if (event.action == KeyAction.KeyDown) {
switch(event.keyCode) with(KeyCode) {
case KEY_W:
case UP:
_world.camPosition.forward(1);
updateCamPosition();
return true;
case DOWN:
case KEY_S:
_world.camPosition.backward(1);
updateCamPosition();
return true;
case KEY_A:
case LEFT:
_world.camPosition.turnLeft();
updateCamPosition();
return true;
case KEY_D:
case RIGHT:
_world.camPosition.turnRight();
updateCamPosition();
return true;
case HOME:
case KEY_E:
_world.camPosition.moveUp();
updateCamPosition();
return true;
case END:
case KEY_Q:
_world.camPosition.moveDown();
updateCamPosition();
return true;
case KEY_Z:
_world.camPosition.moveLeft();
updateCamPosition();
return true;
case KEY_C:
_world.camPosition.moveRight();
updateCamPosition();
return true;
case KEY_F:
flying = !flying;
if (!flying)
_world.camPosition.pos.y = CHUNK_DY - 3;
updateCamPosition();
return true;
case KEY_U:
enableMeshUpdate = !enableMeshUpdate;
updateCamPosition();
return true;
default:
return false;
}
}
return false;
}
Node3d dirLightNode;
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);
}
bool flying = false;
bool enableMeshUpdate = true;
void updateCamPosition(bool animateIt = true) {
import std.string;
import std.conv : to;
import std.utf : toUTF32;
import std.format;
if (!flying) {
while(_world.canPass(_world.camPosition.pos + Vector3d(0, -1, 0)))
_world.camPosition.pos += Vector3d(0, -1, 0);
if(!_world.canPass(_world.camPosition.pos + Vector3d(0, -1, 0))) {
if (_world.canPass(_world.camPosition.pos + Vector3d(0, 1, 0)))
_world.camPosition.pos += Vector3d(0, 1, 0);
else if (_world.canPass(_world.camPosition.pos + Vector3d(1, 0, 0)))
_world.camPosition.pos += Vector3d(1, 0, 0);
else if (_world.canPass(_world.camPosition.pos + Vector3d(-1, 0, 0)))
_world.camPosition.pos += Vector3d(-1, 0, 0);
else if (_world.canPass(_world.camPosition.pos + Vector3d(0, 0, 1)))
_world.camPosition.pos += Vector3d(0, 0, 1);
else if (_world.canPass(_world.camPosition.pos + Vector3d(0, 0, -1)))
_world.camPosition.pos += Vector3d(0, 0, -1);
while(_world.canPass(_world.camPosition.pos + Vector3d(0, -1, 0)))
_world.camPosition.pos += Vector3d(0, -1, 0);
}
}
setPos(vec3(_world.camPosition.pos.x + 0.5f, _world.camPosition.pos.y + 0.5f, _world.camPosition.pos.z + 0.5f), animateIt);
setAngle(_world.camPosition.direction.angle, animateIt);
Widget w = childById("lblPosition");
string dir = _world.camPosition.direction.dir.to!string;
dstring s = format("pos(%d,%d) h=%d %s [F]lying: %s [U]pdateMesh: %s", _world.camPosition.pos.x, _world.camPosition.pos.z, _world.camPosition.pos.y, dir,
flying, enableMeshUpdate).toUTF32;
w.text = s;
if (enableMeshUpdate)
updateMinerMesh();
}
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);
invalidate();
//for (int i = 0; i < 20; i++)
// Log.d("vertex: ", _minerMesh.vertex(i));
}
World _world;
vec3 _position;
float _angle;
vec3 _animatingPosition;
float _animatingAngle;
void setPos(vec3 newPos, bool animateIt = false) {
if (animateIt) {
_position = newPos;
} else {
_animatingPosition = newPos;
_position = newPos;
}
}
void setAngle(float newAngle, bool animateIt = false) {
if (animateIt) {
_angle = newAngle;
} else {
_animatingAngle = newAngle;
_angle = newAngle;
}
}
/// 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");
if (_animatingAngle != _angle) {
float delta = _angle - _animatingAngle;
if (delta > 180)
delta -= 360;
else if (delta < -180)
delta += 360;
float dist = delta < 0 ? -delta : delta;
if (dist < 5) {
_animatingAngle = _angle;
} else {
float speed = 360;
float step = speed * interval / 10000000.0f;
//Log.d("Rotate animation delta=", delta, " dist=", dist, " elapsed=", interval, " step=", step);
if (step > dist)
step = dist;
delta = delta * (step /dist);
_animatingAngle += delta;
}
}
if (_animatingPosition != _position) {
vec3 delta = _position - _animatingPosition;
float dist = delta.length;
if (dist < 0.01) {
_animatingPosition = _position;
// done
} else {
float speed = 8;
if (dist > 2)
speed = (dist - 2) * 3 + speed;
float step = speed * interval / 10000000.0f;
//Log.d("Move animation delta=", delta, " dist=", dist, " elapsed=", interval, " step=", step);
if (step > dist)
step = dist;
delta = delta * (step / dist);
_animatingPosition += delta;
}
}
invalidate();
}
float angle = 0;
Scene3d _scene;
Camera _cam;
Mesh _minerMesh;
/// this is OpenGLDrawableDelegate implementation
private void doDraw(Rect windowRect, Rect rc) {
_cam.setPerspective(rc.width, rc.height, 45.0f, 0.1, 100);
//_cam.translate(vec3(
// childById!ScrollBar("sbTranslationX").position / 10.0f,
// childById!ScrollBar("sbTranslationY").position / 10.0f,
// childById!ScrollBar("sbTranslationZ").position / 10.0f));
//_world.camPosition.pos.x;
_cam.setIdentity();
_cam.translate(_animatingPosition);
_cam.rotateY(_animatingAngle);
//_cam.rotateX(-15);
//_cam.lookAt(vec3(dir.x, dir.y, dir.z), vec3(pos.x, pos.y, pos.z), vec3(0,1,0));
//_cam.translateX(_world.camPosition.pos.x);
//_cam.translateY(_world.camPosition.pos.y);
//_cam.translateZ(_world.camPosition.pos.z);
dirLightNode.setIdentity();
dirLightNode.translate(_animatingPosition);
dirLightNode.rotateY(_animatingAngle);
//dirLightNode.setIdentity();
//dirLightNode.translateX(_world.camPosition.pos.x);
//dirLightNode.translateY(_world.camPosition.pos.y);
//dirLightNode.translateZ(_world.camPosition.pos.z);
//_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);
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(_scene);
destroy(_world);
}
}

View File

@ -0,0 +1,40 @@
EXIT=Exit
MENU_FILE=&File
MENU_FILE_NEW=&New
MENU_FILE_OPEN=&Open
MENU_FILE_OPEN_RECENT=Open recent
MENU_FILE_SAVE=&Save
MENU_FILE_EXIT=E&xit
MENU_EDIT=&Edit
MENU_EDIT_COPY=&Copy
MENU_EDIT_PASTE=&Paste
MENU_EDIT_CUT=Cu&t
MENU_EDIT_UNDO=&Undo
MENU_EDIT_REDO=&Redo
MENU_EDIT_INDENT=Indent block
MENU_EDIT_UNINDENT=Unindent block
MENU_EDIT_TOGGLE_LINE_COMMENT=Toggle line comment
MENU_EDIT_TOGGLE_BLOCK_COMMENT=Toggle block comment
MENU_EDIT_PREFERENCES=&Preferences
MENU_VIEW=&View
MENU_VIEW_LANGUAGE=Interface &Language
MENU_VIEW_LANGUAGE_EN=English
MENU_VIEW_LANGUAGE_RU=Русский
MENU_VIEW_THEME=&Theme
MENU_VIEW_THEME_DEFAULT=Default
MENU_VIEW_THEME_DARK=Dark
MENU_VIEW_THEME_CUSTOM1=Custom 1
MENU_WINDOW=&Window
MENU_WINDOW_PREFERENCES=&Preferences
MENU_HELP=&Help
MENU_HELP_VIEW_HELP=&View help
MENU_HELP_ABOUT=&About
MENU_DEBUG_UPDATE_PREVIEW=Update Preview
TAB_LONG_LIST=Long list
TAB_BUTTONS=Buttons
TAB_ANIMATION=Animation
TAB_TABLE_LAYOUT=Table layout
TAB_EDITORS=Editors
TAB_CANVAS=Canvas

View File

@ -0,0 +1,34 @@
EXIT=Выход
MENU_FILE=&Файл
MENU_FILE_OPEN=&Открыть
MENU_FILE_OPEN_RECENT=Открыть из последних
MENU_FILE_SAVE=&Сохранить
MENU_FILE_EXIT=Вы&ход
MENU_EDIT=&Правка
MENU_EDIT_COPY=&Копировать
MENU_EDIT_PASTE=&Вставить
MENU_EDIT_CUT=Вырезать
MENU_EDIT_UNDO=&Отмена
MENU_EDIT_REDO=&Повторить
MENU_EDIT_PREFERENCES=&Настройки
MENU_VIEW=&Вид
MENU_VIEW_LANGUAGE=&Язык интерфейса
MENU_VIEW_LANGUAGE_EN=English
MENU_VIEW_LANGUAGE_RU=Русский
MENU_VIEW_THEME=&Тема
MENU_VIEW_THEME_DEFAULT=Стандартная
MENU_VIEW_THEME_DARK=Тёмная
MENU_VIEW_THEME_CUSTOM1=Пример 1
MENU_WINDOW=&Окно
MENU_WINDOW_PREFERENCES=&Настройки
MENU_HELP=&Справка
MENU_HELP_VIEW_HELP=&Просмотр справки
MENU_HELP_ABOUT=&О программе
TAB_LONG_LIST=Длинный список
TAB_BUTTONS=Кнопки
TAB_ANIMATION=Анимация
TAB_TABLE_LAYOUT=Табличный layout
TAB_EDITORS=Редакторы

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -0,0 +1,4 @@
res/i18n/en.ini
res/i18n/ru.ini
res/mdpi/cr3_logo.png
res/mdpi/blocks.png