1 /* 2 Copyright (c) 2018-2022 Rafał Ziemniewski, Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 module dagon.graphics.terrain; 29 30 import dlib.core.memory; 31 import dlib.core.ownership; 32 import dlib.container.array; 33 import dlib.math.vector; 34 import dlib.geometry.sphere; 35 import dlib.geometry.triangle; 36 import dlib.image.color; 37 38 import dagon.graphics.drawable; 39 import dagon.graphics.mesh; 40 import dagon.graphics.heightmap; 41 import dagon.graphics.entity; 42 import dagon.graphics.material; 43 import dagon.graphics.texture; 44 45 class TerrainMaterial: Material 46 { 47 Array!Material layers; 48 49 this(Owner o) 50 { 51 super(o); 52 alphaTestThreshold = 0.0f; 53 } 54 55 Material addLayer() 56 { 57 Material layerMaterial = New!Material(this); 58 layerMaterial.alphaTestThreshold = alphaTestThreshold; 59 layers.append(layerMaterial); 60 return layerMaterial; 61 } 62 63 // TODO: remove layer 64 65 ~this() 66 { 67 layers.free(); 68 } 69 } 70 71 class Terrain: Owner, Drawable 72 { 73 uint width; 74 uint height; 75 Mesh mesh; 76 Mesh collisionMesh; 77 Heightmap heightmap; 78 79 this(uint meshResolution, uint collisionMeshResolution, Heightmap heightmap, Owner owner) 80 { 81 super(owner); 82 83 this.width = meshResolution; 84 this.height = meshResolution; 85 this.heightmap = heightmap; 86 87 mesh = generateMesh(width, height, 1, owner); 88 mesh.dataReady = true; 89 mesh.prepareVAO(); 90 91 float scale = cast(float)meshResolution / collisionMeshResolution; 92 collisionMesh = generateMesh(collisionMeshResolution, collisionMeshResolution, scale, owner); 93 } 94 95 this(uint meshResolution, Heightmap heightmap, Owner owner) 96 { 97 this(meshResolution, 80, heightmap, owner); 98 } 99 100 Mesh generateMesh(uint w, uint h, float scale, Owner o) 101 { 102 Mesh mesh = New!Mesh(o); 103 104 size_t numVerts = w * h; 105 mesh.vertices = New!(Vector3f[])(numVerts); 106 mesh.normals = New!(Vector3f[])(numVerts); 107 mesh.texcoords = New!(Vector2f[])(numVerts); 108 mesh.indices = New!(uint[3][])(numVerts * 2); 109 110 int i = 0; 111 foreach(x; 0..w) 112 foreach(z; 0..h) 113 { 114 float y = heightmap.getHeight( 115 cast(float)x / cast(float)(w-1), 116 cast(float)z / cast(float)(h-1)); 117 mesh.vertices[i] = Vector3f(x * scale, y, z * scale); 118 mesh.texcoords[i] = Vector2f( 119 cast(float)x / cast(float)(w-1), 120 cast(float)z / cast(float)(h-1)); 121 i += 1; 122 } 123 124 i = 0; 125 foreach(x; 0..w-1) 126 foreach(z; 0..h-1) 127 { 128 uint LU = x + z * w; 129 uint RU = x+1 + z * w; 130 uint LB = x + (z+1) * w; 131 uint RB = x+1 + (z+1) * w; 132 133 mesh.indices[i] = [LU, RU, RB]; 134 mesh.indices[i+1] = [LU, RB, LB]; 135 i += 2; 136 } 137 138 mesh.generateNormals(); 139 140 return mesh; 141 } 142 143 void update(double dt) 144 { 145 146 } 147 148 void render(GraphicsState* state) 149 { 150 mesh.render(state); 151 } 152 153 void refreshChanges() 154 { 155 mesh.generateNormals(); 156 mesh.prepareVAO(); 157 } 158 159 float getHeight(Entity e, Vector3f pos) 160 { 161 Vector3f ts = (pos - e.position) / e.scaling; 162 float x = ts.x / width; 163 float z = ts.z / height; 164 float y = heightmap.getHeight(x, z); 165 return y * e.scaling.y; 166 } 167 168 TerrainSphereTraverseAggregate traverseBySphere(Sphere* sphere) 169 { 170 return TerrainSphereTraverseAggregate(this, sphere); 171 } 172 } 173 174 struct TerrainSphereTraverseAggregate 175 { 176 Terrain terrain; 177 Sphere* sphere; 178 179 int opApply(int delegate(ref Triangle) dg) 180 { 181 int result = 0; 182 183 uint x = 0; 184 uint y = 0; 185 186 Vector3f c = sphere.center; 187 // TODO: transform c with position and scale? 188 if (c.x > terrain.width - 1) x = terrain.width - 1; 189 else if (c.x < 0) x = 0; 190 else x = cast(uint)c.x; 191 192 if (c.z > terrain.height - 1) y = terrain.height - 1; 193 else if (c.z < 0) y = 0; 194 else y = cast(uint)c.z; 195 196 Triangle tri = terrain.mesh.getTriangle(y * terrain.width + x); 197 tri.barycenter = (tri.v[0] + tri.v[1] + tri.v[2]) / 3; 198 199 result = dg(tri); 200 201 return result; 202 } 203 } 204 205 bool entityIsTerrain(Entity e) 206 { 207 if (e.type == EntityType.Terrain) 208 return true; 209 210 Drawable d = e.drawable; 211 if (d) 212 { 213 if (cast(Terrain)d) 214 return true; 215 } 216 return false; 217 }