Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

304 рядки
14 KiB

  1. package roadtrip.view;
  2. import com.jme3.asset.AssetManager;
  3. import com.jme3.bullet.PhysicsSpace;
  4. import com.jme3.bullet.collision.shapes.BoxCollisionShape;
  5. import com.jme3.bullet.collision.shapes.ConeCollisionShape;
  6. import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
  7. import com.jme3.bullet.control.RigidBodyControl;
  8. import com.jme3.light.AmbientLight;
  9. import com.jme3.light.DirectionalLight;
  10. import com.jme3.material.Material;
  11. import com.jme3.math.ColorRGBA;
  12. import com.jme3.math.Vector2f;
  13. import com.jme3.math.Vector3f;
  14. import com.jme3.post.FilterPostProcessor;
  15. import com.jme3.post.filters.DepthOfFieldFilter;
  16. import com.jme3.renderer.Camera;
  17. import com.jme3.scene.Node;
  18. import com.jme3.scene.Spatial;
  19. import com.jme3.terrain.geomipmap.*;
  20. import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
  21. import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
  22. import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
  23. import com.jme3.terrain.noise.ShaderUtils;
  24. import com.jme3.terrain.noise.basis.FilteredBasis;
  25. import com.jme3.terrain.noise.filter.IterativeFilter;
  26. import com.jme3.terrain.noise.filter.OptimizedErode;
  27. import com.jme3.terrain.noise.filter.PerturbFilter;
  28. import com.jme3.terrain.noise.filter.SmoothFilter;
  29. import com.jme3.terrain.noise.fractal.FractalSum;
  30. import com.jme3.terrain.noise.modulator.NoiseModulator;
  31. import com.jme3.texture.Texture;
  32. import java.util.Random;
  33. import roadtrip.model.MapObjectInstance;
  34. import roadtrip.model.ProceduralMapQuadBlock;
  35. import roadtrip.model.TerrainDataProvider;
  36. import roadtrip.view.model.GameWorldState;
  37. /**
  38. * Created by dejvino on 14.01.2017.
  39. */
  40. public class GameWorldView {
  41. public static boolean DEBUG = false;//true;
  42. private final GameWorldState state;
  43. private final AssetManager assetManager;
  44. private final Camera camera;
  45. private final Node rootNode;
  46. private final PhysicsSpace physicsSpace;
  47. public TerrainView terrain = new TerrainView(new TerrainDataProvider());
  48. public GameWorldView(GameWorldState gameWorldState, AssetManager assetManager, Camera camera, Node rootNode, PhysicsSpace physicsSpace) {
  49. this.state = gameWorldState;
  50. this.assetManager = assetManager;
  51. this.camera = camera;
  52. this.rootNode = rootNode;
  53. this.physicsSpace = physicsSpace;
  54. }
  55. public static GameWorldView create(GameWorldState gameWorldState, AssetManager assetManager, Camera camera, Node rootNode, PhysicsSpace physicsSpace) {
  56. GameWorldView gameWorldView = new GameWorldView(gameWorldState, assetManager, camera, rootNode, physicsSpace);
  57. gameWorldView.initialize();
  58. return gameWorldView;
  59. }
  60. private void initialize()
  61. {
  62. // Environment
  63. DirectionalLight dl = new DirectionalLight();
  64. dl.setColor(ColorRGBA.LightGray);
  65. dl.setDirection(new Vector3f(1, -1, 1));
  66. rootNode.addLight(dl);
  67. AmbientLight al = new AmbientLight();
  68. al.setColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
  69. rootNode.addLight(al);
  70. // TERRAIN TEXTURE material
  71. terrain.mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/HeightBasedTerrain.j3md");
  72. if (DEBUG) {
  73. terrain.mat_terrain.getAdditionalRenderState().setWireframe(true);
  74. }
  75. float heightScale = 400f;
  76. // Parameters to material:
  77. // regionXColorMap: X = 1..4 the texture that should be appliad to state X
  78. // regionX: a Vector3f containing the following information:
  79. // regionX.x: the start height of the region
  80. // regionX.y: the end height of the region
  81. // regionX.z: the texture scale for the region
  82. // it might not be the most elegant way for storing these 3 values, but it packs the data nicely :)
  83. // slopeColorMap: the texture to be used for cliffs, and steep mountain sites
  84. // slopeTileFactor: the texture scale for slopes
  85. // terrainSize: the total size of the terrain (used for scaling the texture)
  86. // GRASS texture
  87. Texture grass = this.assetManager.loadTexture("Textures/solid-grass.png");
  88. grass.setWrap(Texture.WrapMode.Repeat);
  89. Texture dirt = this.assetManager.loadTexture("Textures/solid-road.png");
  90. dirt.setWrap(Texture.WrapMode.Repeat);
  91. Texture rock = this.assetManager.loadTexture("Textures/solid-stone.png");
  92. rock.setWrap(Texture.WrapMode.Repeat);
  93. float modif = (heightScale / 100f) / 3f;
  94. terrain.mat_terrain.setTexture("region1ColorMap", dirt);
  95. terrain.mat_terrain.setVector3("region1", new Vector3f(0, 80 * modif, terrain.dirtScale));
  96. terrain.mat_terrain.setTexture("region2ColorMap", grass);
  97. terrain.mat_terrain.setVector3("region2", new Vector3f(100 * modif, 160 * modif, terrain.grassScale));
  98. terrain.mat_terrain.setTexture("region3ColorMap", rock);
  99. terrain.mat_terrain.setVector3("region3", new Vector3f(190 * modif, 240 * modif, terrain.rockScale));
  100. terrain.mat_terrain.setTexture("region4ColorMap", dirt);
  101. terrain.mat_terrain.setVector3("region4", new Vector3f(250 * modif, 350 * modif, terrain.dirtScale));
  102. terrain.mat_terrain.setTexture("slopeColorMap", rock);
  103. terrain.mat_terrain.setFloat("slopeTileFactor", 32);
  104. terrain.mat_terrain.setFloat("terrainSize", 513);
  105. terrain.terrainDataProvider.base = new FractalSum();
  106. terrain.terrainDataProvider.base.setRoughness(0.6f);
  107. terrain.terrainDataProvider.base.setFrequency(1.0f);
  108. terrain.terrainDataProvider.base.setAmplitude(1.0f);
  109. terrain.terrainDataProvider.base.setLacunarity(4.12f);
  110. terrain.terrainDataProvider.base.setOctaves(8);
  111. terrain.terrainDataProvider.base.setScale(0.02125f);
  112. terrain.terrainDataProvider.base.addModulator(new NoiseModulator() {
  113. @Override
  114. public float value(float... in) {
  115. return ShaderUtils.clamp(in[0] * 0.5f + 0.5f, 0, 1);
  116. }
  117. });
  118. FilteredBasis ground = new FilteredBasis(terrain.terrainDataProvider.base);
  119. terrain.terrainDataProvider.perturb = new PerturbFilter();
  120. terrain.terrainDataProvider.perturb.setMagnitude(0.119f);
  121. terrain.terrainDataProvider.therm = new OptimizedErode();
  122. terrain.terrainDataProvider.therm.setRadius(5);
  123. terrain.terrainDataProvider.therm.setTalus(0.011f);
  124. terrain.terrainDataProvider.smooth = new SmoothFilter();
  125. terrain.terrainDataProvider.smooth.setRadius(1);
  126. terrain.terrainDataProvider.smooth.setEffect(0.3f);
  127. terrain.terrainDataProvider.iterate = new IterativeFilter();
  128. terrain.terrainDataProvider.iterate.addPreFilter(terrain.terrainDataProvider.perturb);
  129. terrain.terrainDataProvider.iterate.addPostFilter(terrain.terrainDataProvider.smooth);
  130. terrain.terrainDataProvider.iterate.setFilter(terrain.terrainDataProvider.therm);
  131. terrain.terrainDataProvider.iterate.setIterations(2);
  132. ground.addPreFilter(terrain.terrainDataProvider.iterate);
  133. int patchSize = 16;
  134. Vector3f terrainScale = new Vector3f(8f, 1f, 8f);
  135. //terrain.terrainGrid = new TerrainGrid("terrain", 16 + 1, 512 + 1, new FractalTileLoader(ground, 300f));
  136. terrain.terrainGrid = new FineTerrainGrid("terrain", patchSize + 1, 128 + 1, terrainScale, new FractalTileLoader(ground, heightScale));
  137. terrain.terrainGrid.setMaterial(terrain.mat_terrain);
  138. //terrain.terrainGrid.setLocalTranslation(0, -200, 0);
  139. terrain.terrainGrid.setLocalScale(terrainScale);
  140. this.rootNode.attachChild(terrain.terrainGrid);
  141. final TerrainLodControl lodControl = new FineTerrainGridLodControl(terrain.terrainGrid, camera);
  142. lodControl.setLodCalculator(new DistanceLodCalculator(patchSize + 1, 3.7f)); // patch size, and a multiplier
  143. terrain.terrainGrid.addControl(lodControl);
  144. final Spatial treeModel = assetManager.loadModel("Models/tree.j3o");
  145. final Spatial houseModel = assetManager.loadModel("Models/house1.j3o");
  146. final Spatial grassModel = assetManager.loadModel("Models/grass.j3o");
  147. final FineTerrainGrid terrainGrid = terrain.terrainGrid;
  148. terrainGrid.addListener(new TerrainGridListener() {
  149. @Override
  150. public void gridMoved(Vector3f newCenter) {
  151. }
  152. @Override
  153. public void tileAttached(Vector3f cell, TerrainQuad quad) {
  154. lodControl.forceUpdate();
  155. while(quad.getControl(RigidBodyControl.class)!=null){
  156. quad.removeControl(RigidBodyControl.class);
  157. }
  158. quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrainGrid.getLocalScale()), 0));
  159. physicsSpace.add(quad);
  160. removeQuadObjectsNode(quad);
  161. String quadObjectsNodeKey = getQuadObjectsNodeKey(quad);
  162. Node objects = new Node(quadObjectsNodeKey);
  163. populateQuadObjectsNode(quad, objects);
  164. rootNode.attachChild(objects);
  165. }
  166. protected void populateQuadObjectsNode(TerrainQuad quad, Node objects)
  167. {
  168. ProceduralMapQuadBlock mapQuadBlock = state.proceduralMap.getMapQuadBlock(quad);
  169. /*/ DEBUG pole in the middle of a quad
  170. Spatial m = treeModel.clone();
  171. m.setLocalTranslation(quad.getWorldTranslation().add(new Vector3f(0f, getHeight(quad, quad.getWorldTranslation()), 0f)));
  172. m.setLocalScale(new Vector3f(1f, 20f, 1f));
  173. objects.attachChild(m);
  174. /**/
  175. // Add map objects
  176. Random rand = mapQuadBlock.getBlockRandom();
  177. for (MapObjectInstance mapObject : mapQuadBlock.getMapObjects()) {
  178. Vector3f pos = mapObject.getPosition();
  179. Vector3f scale = Vector3f.UNIT_XYZ;
  180. Vector3f boxHalf = Vector3f.UNIT_XYZ;
  181. Spatial modelInstance;
  182. RigidBodyControl modelPhysics;
  183. switch (mapObject.getType()) {
  184. case "tree":
  185. modelInstance = treeModel.clone();
  186. float s = 0.2f + rand.nextFloat() * 5f;
  187. scale = new Vector3f(s, s, s);
  188. boxHalf = new Vector3f(s * 0.2f, s * 3f, s * 0.2f);
  189. modelPhysics = new RigidBodyControl(new BoxCollisionShape(boxHalf), 0f);
  190. break;
  191. case "house":
  192. modelInstance = houseModel.clone();
  193. boxHalf = new Vector3f(2f + rand.nextFloat() * 10f, 2f + rand.nextFloat() * 10f, 2f + rand.nextFloat() * 10f);
  194. scale = boxHalf;
  195. modelPhysics = new RigidBodyControl(new BoxCollisionShape(boxHalf), 0f);
  196. break;
  197. default:
  198. throw new RuntimeException("Unhandled object type: " + mapObject.getType());
  199. }
  200. modelInstance.setLocalTranslation(pos);
  201. modelInstance.setLocalScale(scale);
  202. // TODO: physics from the model and not hard-coded
  203. //RigidBodyControl control = treeInstance.getControl(RigidBodyControl.class);
  204. if (modelPhysics != null) {
  205. modelPhysics.isActive();
  206. modelInstance.addControl(modelPhysics);
  207. modelPhysics.setPhysicsLocation(pos);
  208. //physicsSpace.add(modelPhysics);
  209. }
  210. objects.attachChild(modelInstance);
  211. }
  212. for (int i = 0; i < rand.nextInt(10000); i++) {
  213. Vector3f pos = new Vector3f((rand.nextFloat() - 0.5f) * 128f, 0f, (rand.nextFloat() - 0.5f) * 128f).addLocal(quad.getWorldTranslation());
  214. pos.addLocal(0f, getHeight(quad, pos), 0f);
  215. Vector3f scale = Vector3f.UNIT_XYZ;
  216. Spatial modelInstance;
  217. switch ("grass") {
  218. case "grass":
  219. modelInstance = grassModel.clone();
  220. float s = 0.2f + rand.nextFloat() * 2f;
  221. scale = new Vector3f(s, s, s);
  222. break;
  223. default:
  224. throw new RuntimeException("Unhandled object type");
  225. }
  226. modelInstance.setLocalTranslation(pos);
  227. modelInstance.setLocalScale(scale);
  228. objects.attachChild(modelInstance);
  229. }
  230. }
  231. @Override
  232. public void tileDetached(Vector3f cell, TerrainQuad quad) {
  233. if (quad.getControl(RigidBodyControl.class) != null) {
  234. physicsSpace.remove(quad);
  235. quad.removeControl(RigidBodyControl.class);
  236. }
  237. removeQuadObjectsNode(quad);
  238. }
  239. protected void removeQuadObjectsNode(TerrainQuad quad)
  240. {
  241. Spatial quadObjectsNodeOld = rootNode.getChild(getQuadObjectsNodeKey(quad));
  242. if (quadObjectsNodeOld != null) {
  243. physicsSpace.removeAll(quadObjectsNodeOld);
  244. quadObjectsNodeOld.removeFromParent();
  245. }
  246. }
  247. private String getQuadObjectsNodeKey(TerrainQuad quad)
  248. {
  249. return "Objects-" + quad.getName();
  250. }
  251. private float getHeight(TerrainQuad quad, Vector3f pos)
  252. {
  253. return quad.getHeight(new Vector2f(pos.x, pos.z));
  254. }
  255. });
  256. /**/
  257. }
  258. }