Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Skepticism on Unity. Flat Earth? how do you morph Unity terrain into sphere?

Discussion in 'General Discussion' started by particlemars, May 31, 2017.

  1. particlemars

    particlemars

    Joined:
    Nov 20, 2016
    Posts:
    30
    This is insane. Has anyone seen Google Earth & Google Map? When you zoom out you get nice earth sphere. But Unity 3D. it's glass dome and terrain. Unable to rotate terrain any angle and place around the sphere. Terrain can only be move around circle.

    On unity. What's source code for morph terrain into sphere?
     
  2. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    It's not a good idea to use Unity terrain for this purpose. In fact it just isn't compatible. You can butcher it into doing something but it will always be broken.

    Instead consider using a mesh.
     
  3. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,134
    Alternatively, and humorously this goes somewhat in line with the message the video was suggesting, you could use an effect that gives the observer (the player) the impression that they are on a round world when it isn't round in the slightest. Below is a very well rated asset that can create a curved world visual effect while leaving the scene unaffected.

    https://www.assetstore.unity3d.com/en/#!/content/26165
     
    Last edited: May 31, 2017
  4. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    990
    Holy crap, that is impressive
     
  5. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    That's nice indeed but you will have a lot of problems with Unity terrain even with it still, but yeah probably best option for OP!
     
  6. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,324
    When approaching planet from space, create dummy terrain patch object (dense grid with or without hardware subdivision), roughly 10x10 km, and use its center as world origin for purposes of rendering and origin shifting. For vertices in the fragment, adjust positions and altitudes based on map coordinates and elevation data.

    When approaching boundary of the patch, move patch to a new position, rebuild based on elevation data from new location, and origin shift to center of the new patch..

    Should be fairly straightforward.
     
  7. Not_Sure

    Not_Sure

    Joined:
    Dec 13, 2011
    Posts:
    3,541
    See, this is why we need to move away from square terrain maps and switch to triangles. That way they can be stitched together to make a icosahedron based globe.



    Left sphere: BOOOOOO!!!

    Right sphere: YAAAAAY!!!

    EDIT: Also, the terrain should be made into part of a globe by default so that the horizon shows the curvature of the planet.
     
  8. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    The challenge...placing hand made terrains on the sphere. How do you translate from rectangular terrain heightmaps to the sphere?
    I'm not talking procedural, but hand made. Using something like World Creator, Gaia, TC2, etc.

    And before you say a QuadSphere...uh uh. The corners will get you.
     
  9. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,324
    You will still have to deal with map coordinates. Also, isntead of icosahedron, I think a much better idea would be to sue quad based sphere, because mapping coordinates to polygons should be much more straightforward.
    cube-sphere.png

    Also... having round planet by default will introduce complication in physics (because gravity is now directed towards specific point and not in the same direction for everybody), plus with default floating point precision planet will be going beyond the range where precision errors start appearing.

    Now, triangle-based sphere does have a few advantages - unity does not support quad based domains in hardware tesselator, so triangle-based mesh will be slightly more suitable for hardware subdivision.
     
    Last edited: Jun 1, 2017
    Not_Sure and angrypenguin like this.
  10. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    Just to get this out of the way, comparing Google Maps/Earth with a game engine is asinine.

    When most of us think of interacting with the environment around us, we don't think about the world as a sphere. Down is just 'down', not a point 4,000 miles below our feet. And for most practical applications, a flat terrain serves this purpose perfectly.

    Saying the default should be changed to suit a very particular use case is not a great idea. I could support adding another "PlanetaryTerrain" option, but not changing the default.
     
    Martin_H, Kiwasi, mysticfall and 3 others like this.
  11. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,134
    Yes, because then we'd have to cave in to a certain someone's desire for 64-bit floats. :p
     
    Not_Sure, Kiwasi and Schneider21 like this.
  12. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,324
    64bit floats are so last century. We need 128bit float. This way we'll be able to address any point in the observable universe with micron precision. Think about the possibilities!

    (That was a joke).
    Binary128 has 112 fractonal bits, which allows to store values up to

    2^112 micros is according to wolfram alpha this is bigger than observable universe by factor of 5 or 6)
     
  13. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    A quad sphere is great for outer space. But the problem is the transition to planet surface.
    If you try to have a terrain per face, then as long as you are in the middle or middle edge of the terrain, it's fine. You can pull in tiles around the square you are in, so 3 above your square, 3 below, one to the left and one to the right. 9 tiles in all, including the one you are in.

    By tiles, image each subdivision face you show on the quadsphere as being in a terrain section to show around the player.

    But the corners mean you can't pull in from nice tiles of terrain around you, you don't have a nice grid-based layout of tiles to pull from. There is some serious warping going on there.

    I'm talking about a scenario where you have a large terrain for each face, that you have to split into tiles. I guess you could do some sort of math, and warp the faces around the player to appear flat. But that would mean in loading in a lot of data, and showing some pretty big terrains.
     
  14. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,514
    That's right, at some level you'd have to be working with some spherical coordinate system or another and map that to the terrain data, and that mapping is in fact going to have to account for some amount of warp on all terrain tiles.
     
  15. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    Yeah. Honestly...I'm almost resigned to just doing cylindrical mapping of a terrain onto a grid. The poles will look pinched from orbit, and won't really match the poles when you are on the surface...meh. I don't think it's a big enough issue, and I'm not smart enough it seems to figure out a better way that doesn't involve some sort of math degree.
     
  16. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,324
    I don't see any problem.

    You'll need to draw map for the player using any proejction of your choice, and within one of the planetary quads (x+,. x-, y+, y-, z+, z-) underlying quads can be made to be of uniform size. Basically you'll have 6 connected gird maps per planet with nice tile-based layout. It can't be made cleaner/simpler than this.
     
    jc_lvngstn likes this.
  17. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,324
    I did a few tests... it is WAAAAY easier to make a quad-based sphere than dealing with anything triangular.
    polysphere.png
    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. [ExecuteInEditMode]
    7. public class PlanetGenerator: MonoBehaviour{
    8.     [SerializeField] int sphereGridSize = 6;
    9.     [SerializeField] float radius = 1.0f;
    10.     [SerializeField] int numTetraSubdivisions = 3;
    11.     [SerializeField] bool showSphereGrid = true;
    12.     [SerializeField] bool showTetraGrid = true;
    13.  
    14.     GameObject tetra = null;
    15.     GameObject sphereGrid = null;
    16.     Mesh sphereMesh = null;
    17.     Mesh tetraMesh = null;
    18.     MeshRenderer sphereRenderer = null;
    19.     MeshRenderer tetraRenderer = null;
    20.     MeshFilter sphereFilter = null;
    21.     MeshFilter tetraFilter = null;
    22.     [SerializeField] Material meshMaterial = null;
    23.  
    24.     List<Vector3> verts = new List<Vector3>();
    25.     List<int> indexes = new List<int>();
    26.     List<Vector3> normals = new List<Vector3>();
    27.  
    28.     float lastRadius = -1.0f;
    29.     int lastGridSize = -1;
    30.     int lastSubdivisions = -1;
    31.  
    32.     void killObjects(){
    33.         DestroyImmediate(tetraFilter);
    34.         DestroyImmediate(sphereFilter);
    35.         DestroyImmediate(tetraRenderer);
    36.         DestroyImmediate(sphereRenderer);
    37.         DestroyImmediate(sphereMesh);
    38.         DestroyImmediate(tetraMesh);
    39.         DestroyImmediate(tetra);
    40.         DestroyImmediate(sphereGrid);
    41.     }
    42.  
    43.     C getOrCreateTmpComponent<C>(GameObject obj) where C: Component{
    44.         var result = obj.GetComponent<C>();
    45.         if (result)
    46.             return result;
    47.         result = obj.AddComponent<C>();
    48.         result.hideFlags = HideFlags.DontSave;
    49.         return result;
    50.     }
    51.     void rebuildMeshes(){
    52.         if (!tetra){
    53.             tetra = new GameObject();
    54.             tetra.transform.SetParent (transform);
    55.             tetra.transform.localPosition = new Vector3(radius, 0.0f, 0.0f);
    56.             tetra.hideFlags = HideFlags.DontSave;
    57.         }
    58.         if (!sphereGrid){
    59.             sphereGrid = new GameObject();
    60.             sphereGrid.transform.SetParent (transform);
    61.             sphereGrid.transform.localPosition = new Vector3(-radius, 0.0f, 0.0f);
    62.             sphereGrid.hideFlags = HideFlags.DontSave;
    63.         }
    64.         sphereFilter = getOrCreateTmpComponent<MeshFilter>(sphereGrid);
    65.         tetraFilter = getOrCreateTmpComponent<MeshFilter>(tetra);
    66.         sphereRenderer = getOrCreateTmpComponent<MeshRenderer>(sphereGrid);
    67.         tetraRenderer = getOrCreateTmpComponent<MeshRenderer>(tetra);
    68.         if (!sphereMesh){
    69.             sphereMesh = new Mesh();
    70.             sphereMesh.hideFlags = HideFlags.DontSave;
    71.             sphereFilter.mesh = sphereMesh;
    72.         }
    73.         if (!tetraMesh){
    74.             tetraMesh = new Mesh();
    75.             tetraMesh.hideFlags = HideFlags.DontSave;
    76.             tetraFilter.mesh = tetraMesh;
    77.         }
    78.         sphereRenderer.sharedMaterial = meshMaterial;
    79.         tetraRenderer.sharedMaterial = meshMaterial;
    80.  
    81.         verts.Clear();
    82.         normals.Clear();
    83.         indexes.Clear();
    84.  
    85.         processTetraGrid(numTetraSubdivisions, (a, b, c)=>{
    86.             var idx = verts.Count;
    87.             var n = Vector3.Cross(b - a, c -a).normalized;
    88.             verts.Add(a * radius);
    89.             verts.Add(b * radius);
    90.             verts.Add(c * radius);
    91.             normals.Add(n);
    92.             normals.Add(n);
    93.             normals.Add(n);
    94.             indexes.Add(idx + 0);
    95.             indexes.Add(idx + 1);
    96.             indexes.Add(idx + 2);
    97.         });
    98.         tetraMesh.Clear();
    99.         tetraMesh.subMeshCount = 1;
    100.         tetraMesh.SetVertices(verts);
    101.         tetraMesh.SetNormals(normals);
    102.         tetraMesh.SetTriangles(indexes, 0);
    103.         tetraRenderer.sharedMaterial = meshMaterial;
    104.  
    105.         verts.Clear();
    106.         indexes.Clear();
    107.         normals.Clear();
    108.  
    109.         processSphereGrid(sphereGridSize, (a, b, c, d) => {
    110.             var idx = verts.Count;
    111.             var n = Vector3.Cross(c - a, b - a).normalized;
    112.             verts.Add(a * radius);
    113.             verts.Add(b * radius);
    114.             verts.Add(c * radius);
    115.             verts.Add(d * radius);
    116.  
    117.             normals.Add(n);
    118.             normals.Add(n);
    119.             normals.Add(n);
    120.             normals.Add(n);
    121.  
    122.             indexes.Add(idx + 0);
    123.             indexes.Add(idx + 2);
    124.             indexes.Add(idx + 1);
    125.             indexes.Add(idx + 1);
    126.             indexes.Add(idx + 2);
    127.             indexes.Add(idx + 3);
    128.         });
    129.         sphereMesh.Clear();
    130.         sphereMesh.subMeshCount = 1;
    131.         sphereMesh.SetVertices(verts);
    132.         sphereMesh.SetNormals(normals);
    133.         sphereMesh.SetTriangles(indexes, 0);
    134.         sphereRenderer.sharedMaterial = meshMaterial;
    135.     }
    136.  
    137.     static readonly float sq3 = Mathf.Sqrt(1.0f/3);
    138.  
    139.     static Vector3 getPoint(float u, float v, Vector3 xVec, Vector3 yVec, Vector3 zVec, Vector3 posVec){
    140.         var xMinTop = new Vector3(-sq3, sq3, sq3);
    141.         var xMinBottom = new Vector3(-sq3, -sq3, sq3);
    142.         var xMaxTop = new Vector3(sq3, sq3, sq3);
    143.         var xMaxBottom = new Vector3(sq3, -sq3, sq3);
    144.  
    145.         //slerp
    146.         var xMin = Vector3.Slerp(xMinTop, xMinBottom, v);
    147.         var xMax = Vector3.Slerp(xMaxTop, xMaxBottom, v);
    148.         var result = Vector3.Slerp(xMin, xMax, u);
    149.         /*
    150.         //lerp
    151.         var xMin = Vector3.Lerp(xMinTop, xMinBottom, v);
    152.         var xMax = Vector3.Lerp(xMaxTop, xMaxBottom, v);
    153.         var result = Vector3.Lerp(xMin, xMax, u).normalized;
    154.         */
    155.         return posVec + result.x * xVec + result.y * yVec + result.z * zVec;
    156.     }
    157.  
    158.     static void processFaceGrid(Vector3 xVec, Vector3 yVec, Vector3 zVec, int gridSize, QuadCallback callback){
    159.         float valStep = 1.0f/gridSize;//Mathf.PI/(2.0f*gridSize);
    160.  
    161.         float valStart = 0.0f;//-Mathf.PI*0.25f;
    162.         for(int x = 0; x < gridSize; x++){
    163.             float u1 = valStart + valStep * x;
    164.             float u2 = valStart + valStep * (x + 1);
    165.             for (int y = 0; y < gridSize; y++){
    166.                 float v1 = valStart + valStep * y;
    167.                 float v2 = valStart + valStep * (y + 1);
    168.  
    169.                 var p1 = getPoint(u1, v1, xVec, yVec, zVec, Vector3.zero);
    170.                 var p2 = getPoint(u2, v1, xVec, yVec, zVec, Vector3.zero);
    171.                 var p3 = getPoint(u1, v2, xVec, yVec, zVec, Vector3.zero);
    172.                 var p4 = getPoint(u2, v2, xVec, yVec, zVec, Vector3.zero);
    173.  
    174.                 callback(p1, p2, p3, p4);
    175.                 /*Gizmos.DrawLine(p1, p2);
    176.                 Gizmos.DrawLine(p3, p4);
    177.                 Gizmos.DrawLine(p1, p3);
    178.                 Gizmos.DrawLine(p2, p4);*/
    179.             }
    180.         }
    181.     }
    182.  
    183.     static void processSphereGrid(int gridSize, QuadCallback cb){
    184.         processFaceGrid(Vector3.right, Vector3.up, Vector3.forward, gridSize, cb);
    185.         processFaceGrid(-Vector3.right, Vector3.up, -Vector3.forward, gridSize, cb);
    186.  
    187.         processFaceGrid(Vector3.forward, Vector3.up, -Vector3.right, gridSize, cb);
    188.         processFaceGrid(-Vector3.forward, Vector3.up, Vector3.right, gridSize, cb);
    189.  
    190.         processFaceGrid(Vector3.right, -Vector3.forward, Vector3.up, gridSize, cb);
    191.         processFaceGrid(-Vector3.right, -Vector3.forward, -Vector3.up, gridSize, cb);
    192.     }
    193.  
    194.     void drawSphereGrid(){
    195.         if (!showSphereGrid)
    196.             return;
    197.         Gizmos.color = Color.green;
    198.  
    199.         var offset = transform.up * radius * 2.0f - transform.right * radius;
    200.         processSphereGrid(sphereGridSize, (a, b, c, d) => {
    201.             var a1 = transform.TransformPoint(a * radius) + offset;
    202.             var b1 = transform.TransformPoint(b * radius) + offset;
    203.             var c1 = transform.TransformPoint(c * radius) + offset;
    204.             var d1 = transform.TransformPoint(d * radius) + offset;
    205.             Gizmos.DrawLine(a1, b1);
    206.             Gizmos.DrawLine(b1, c1);
    207.             Gizmos.DrawLine(c1, d1);
    208.             Gizmos.DrawLine(d1, a1);
    209.         });
    210.     }
    211.  
    212.     static void processSphereTriangle(Vector3 a, Vector3 b, Vector3 c, int numSubdivisions, TriangleCallback callback){
    213.         if (numSubdivisions <= 1){
    214.             callback(a, b, c);
    215.             return;
    216.         }
    217.         else{
    218.             var ab = ((a + b) * 0.5f).normalized;
    219.             var ac = ((a + c) * 0.5f).normalized;
    220.             var bc = ((b + c) * 0.5f).normalized;
    221.  
    222.             var nextSubdiv = numSubdivisions-1;
    223.             processSphereTriangle(a, ab, ac, nextSubdiv, callback);
    224.             processSphereTriangle(ab, b, bc, nextSubdiv, callback);
    225.             processSphereTriangle(bc, c, ac, nextSubdiv, callback);
    226.             processSphereTriangle(ab, bc, ac, nextSubdiv, callback);
    227.         }
    228.  
    229.     }
    230.  
    231.     public delegate void TriangleCallback(Vector3 a, Vector3 b, Vector3 c);
    232.     public delegate void QuadCallback(Vector3 a, Vector3 b, Vector3 c, Vector3 d);
    233.  
    234.     static void processTetraGrid(int numSubdivs, TriangleCallback callback){
    235.         var f2 = 1.0f/Mathf.Sqrt(2.0f);
    236.         var a = new Vector3(1.0f, 0.0f, -f2);
    237.         var b = new Vector3(-1.0f, 0.0f, -f2);
    238.         var c = new Vector3(0.0f, 1.0f, f2);
    239.         var d = new Vector3(0.0f, -1.0f, f2);
    240.  
    241.         a = a.normalized;
    242.         b = b.normalized;
    243.         c = c.normalized;
    244.         d = d.normalized;
    245.  
    246.         processSphereTriangle(a, b, c, numSubdivs, callback);
    247.         processSphereTriangle(a, d, b, numSubdivs, callback);
    248.         processSphereTriangle(a, c, d, numSubdivs, callback);
    249.         processSphereTriangle(b, d, c, numSubdivs, callback);
    250.     }
    251.  
    252.     void drawTetraGrid(){
    253.         if (!showTetraGrid)
    254.             return;
    255.  
    256.         var offset = transform.up * radius * 2.0f + transform.right * radius;
    257.  
    258.         Gizmos.color = Color.yellow;
    259.  
    260.         processTetraGrid(numTetraSubdivisions, (a, b, c) =>{
    261.             var a1 = transform.TransformPoint(a * radius) + offset;
    262.             var b1 = transform.TransformPoint(b * radius) + offset;
    263.             var c1 = transform.TransformPoint(c * radius) + offset;
    264.             Gizmos.DrawLine(a1, b1);
    265.             Gizmos.DrawLine(a1, c1);
    266.             Gizmos.DrawLine(b1, c1);
    267.         });
    268.     }
    269.  
    270.     void OnDrawGizmos(){
    271.         drawSphereGrid();
    272.         drawTetraGrid();
    273.     }
    274.  
    275.     void OnEnable(){
    276.         rebuildMeshes();
    277.     }
    278.  
    279.     void OnDisable(){
    280.         killObjects();
    281.     }
    282.  
    283.     void Update(){
    284.         if ((lastRadius == radius) && (lastGridSize == sphereGridSize)
    285.             && (lastSubdivisions == numTetraSubdivisions))
    286.             return;
    287.         lastRadius = radius;
    288.         lastGridSize = sphereGridSize;
    289.         lastSubdivisions = numTetraSubdivisions;
    290.         rebuildMeshes();
    291.     }
    292. }
    293.  
    294.  

    Distortion of quads near corners can be minimized, to extent, but in turn it can make determining sector coordinates more difficult.

    As far as I can tell, a good idea in general would be to determine quad based sector, put its center to origin, and align one of the sides with, say, global X coordinate.
     
    Not_Sure likes this.
  18. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I'm not sure if this technique is directly applicable, but its certainly an interesting way to approach spherical worlds.

     
    jc_lvngstn likes this.
  19. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    I'll have to think about this.
    The problem I see is, the six connected grids. Say you are in the front terrain for face, in the small quad in the top right hand corner.

    What is to your right? You could grab that quad, it's the top left hand quad of the face to your right. Fine.
    What about the quad above you? Is it the bottom right hand quad of the top face of the sphere? If so, then...what is above and to the right ? There isn't a quad there. The faces of the sphere do not arrange themselves into a nice layout, there are gaps and missing quads around the player when you are in a corner.

    My struggle (often) is I have a hard time visualizing abstract things, maybe there is some fundamental thing I'm missing. I do appreciate any insight you might have, though.
     
  20. jc_lvngstn

    jc_lvngstn

    Joined:
    Jul 19, 2006
    Posts:
    1,508
    I'll have to watch this when I get home from work, thanks BoredMormon!
     
  21. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,324
    Here's "QuadSphere" mapped onto cylindrical projection.
    sphere-earth.png
    Basically, a sector on this map usually has 8 neighbors, with exception of "corner quads" which only have 7 neighbors.

    To answer "what is to your right" question you'd need to know which way you're looking.

    The way I see it:
    1. You're in a sector that is defined by a quad. The sector is not a square and is not a rectangle, just a quad, which is known to be convex.
    2. Sector has coordinates (XYZ) that define is location on a grid map. The easiest way to store such coordinates is to use 3 components, similar to referencing cubemap color.
    3. Sector borders can explicitly specify which sector is beyond them.
    4. Alternatively you could just use object coordinates to compute which sector is beyond them. Transform object from sector space to planet space, add forward vector, and compute coordinates of new location (in case of "cubemap coordinates" it is (point - planet center).normalized), you'll get new sector coordinates.

    sphere-earth-2.jpg
     
  22. MikeUpchat

    MikeUpchat

    Joined:
    Sep 24, 2010
    Posts:
    1,055
  23. Not_Sure

    Not_Sure

    Joined:
    Dec 13, 2011
    Posts:
    3,541
    I'm just of the point of view that height maps are pretty much the same exact approach that has been done for 30 years now and see it as a good time for a change.

    And to me that change would be moving from a splatmap approach to a vector based one.

    Using an icosahedron or a quadsphere would both be great alternatives.

    For a starting point it's better to use a sphere (which would meet all needs) rather than a flat terrain, which looks incorrect and looks more and more incorrect the higher up you go.
     
  24. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    The issue I have with that though is that it's not a great fit for small maps. If I just want to represent a single town in my game, the curvature of the Earth does not need to be represented, and would just be more complex math to have to deal with unnecessarily. Heck, any game map that isn't infinite (requires full-planet traversal) and stays more or less at ground level wouldn't really benefit from the curved surface terrain, as the curvature of the Earth isn't even noticeable from down here.

    Again, I'm totally not opposed to adding an option for it, but am very much against changing the default. And then you can easily make the argument that this is in the realm of the Asset Store, since it is such a specialized need.
     
    Kiwasi likes this.
  25. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    Its possible if you use vertex normal direction

    Code (CSharp):
    1.     transform.rotation = Quaternion.FromToRotation(Vector3.up, Rayhit.normal);
     
  26. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    Well, sure. But now we're raycasting every frame when we didn't have to be before. I'm not trying to say it makes things impossible or even necessarily more difficult. It's just... more. And more isn't always better. The simplest solution is always the best, and until you need a more complex solution (curved terrain), you should be fine using flat-earth design, right?

    I'm actually really surprised there's not more alignment from the vets around here on this. Maybe... maybe I'm wrong?

     
  27. Ironmax

    Ironmax

    Joined:
    May 12, 2015
    Posts:
    890
    Raycasting (linecast) can be done cheap and smart without any allocation issue. (over frames)
     
  28. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This is pretty much why there is no standard solution for this. In most games, there really is no need to have curved terrain. Many games tend to focus on a narrow, well defined area, making curved terrain superfluous.

    Those games that do need spherical terrain tend to benefit from a custom solution.
     
    Schneider21 likes this.