Simplest of Examples

Hello

Could somebody PLEEEASE enlighten me with the simplest of examples of how to create a mesh from xyz coordinates in an external text file. Having looked at other examples of reading XML files to display dialogues in GUI I still can’t see how to adapt the methods used for my needs. This is the way my data is formatted. Thanks

692760.000000,541490.000000,1.5480
692760.000000,541440.000000,1.5513
692760.000000,541390.000000,1.5525
692760.000000,541340.000000,1.5528
692760.000000,541290.000000,1.5531
692760.000000,541240.000000,1.5557
692810.000000,541690.000000,1.5396
692810.000000,541640.000000,1.5392
692810.000000,541590.000000,1.5406
692810.000000,541540.000000,1.5462
692810.000000,541490.000000,1.5521
692810.000000,541440.000000,1.5554
692810.000000,541390.000000,1.5555
692810.000000,541340.000000,1.5546
692810.000000,541290.000000,1.5533
692810.000000,541240.000000,1.5563
692810.000000,541190.000000,1.5616
692810.000000,541140.000000,1.5666
692810.000000,541090.000000,1.5704
692810.000000,541040.000000,1.5729
692810.000000,540990.000000,1.5748
692810.000000,540940.000000,1.5763
692810.000000,540890.000000,1.5779
692860.000000,541940.000000,1.5343
692860.000000,541890.000000,1.5396
692860.000000,541840.000000,1.5438
692860.000000,541790.000000,1.5466
692860.000000,541740.000000,1.5476
692860.000000,541690.000000,1.5474
692860.000000,541640.000000,1.5468
692860.000000,541590.000000,1.5474
692860.000000,541540.000000,1.5520
692860.000000,541490.000000,1.5567
692860.000000,541440.000000,1.5588

I dont know however I would start with

The reference manual is exceptional, I’m glad the developers have worked on it as you rarely have to see example code

OK, I’ve had a look at the reference.

Seems a bit of a roundabout way of making a cube rom scratch, besides, my triangles only seem to cover one face of the cube. I guess there’s something wrong in the way I order them?

However the idea is still to create a shape from the text file. So given that I can put the cubes vertex data into a file like:
-0.5, -0.5, 0.5
0.5, -0.5, 0.5
etc, etc, etc
etc, etc, etc

How would I then pass that info into the code?

function Start () {
    var mesh = new Mesh ();
    GetComponent (MeshFilter).mesh = mesh;
    mesh.vertices = [Vector3(-0.5, -0.5, 0.5), Vector3(0.5, -0.5, 0.5), Vector3(-0.5, 0.5, 0.5), Vector3(-0.5, -0.5, 0.5), Vector3(0.5, -0.5, 0.5), Vector3(0.5, 0.5, 0.5), Vector3(0.5, 0.5, 0.5), Vector3(-0.5, 0.5, 0.5), Vector3(-0.5, 0.5, -0.5), Vector3(-0.5, 0.5, 0.5), Vector3(0.5, 0.5, 0.5), Vector3(0.5, 0.5, -0.5), Vector3(0.5, 0.5, -0.5), Vector3(-0.5, 0.5, -0.5), Vector3(-0.5, -0.5, -0.5), Vector3(-0.5, 0.5, -0.5), Vector3(0.5, 0.5, -0.5), Vector3(0.5, -0.5, -0.5), Vector3(0.5, -0.5, -0.5), Vector3(-0.5, -0.5, -0.5), Vector3(-0.5, -0.5, 0.5), Vector3(-0.5, -0.5, -0.5), Vector3(0.5, -0.5, -0.5), Vector3(0.5, -0.5, 0.5)];
    mesh.triangles = [0, 1, 3, 0, 2, 3, 3, 2, 5, 3, 5, 4, 5, 2, 1, 5, 1, 6, 3, 4, 7, 3, 7, 0, 0, 7, 6, 0, 6, 1, 4, 5, 6, 4, 6, 7];
}

If you just need cubes of a certain size then look at the draw function.

I can model my objects in Maya for example and then import them into Unity, but I would rather create them from data.

The reason for doing it is as a research experiment to see if I can input terrains into Unity from xyz elevation data. I know I can use greyscale heightmaps, make displacements of them in maya and then import the resulting file, however I really want a Unity solution.

Further down the line I would like to see if an additional column of data, describing each xyz point in space color value (for example) can be added. But this is for the future.

For now I would just be happy to read a small xyz file and output a simple mesh.

Thanks

You need to consider normals as well

If this is what you want, then I think the method I looked at is the only possibility

There is just no way to simplify the job. Any 3D object contains at least the position of each vertice, the faces and normals. It may also contain information as to the material. You cant just miss out those steps unless you find something which works directly with the terrain elevation.

Are you working with raw DTM data? I was interested in working with the data from NASA HiRise though it was not really feasable for me

First you need also the triangle indices in your textfile. That is needed because you have to know how to connect the vertrices.

I assume you know how to read the textfile.

public Mesh CreateMesh(Vector3[] points, int[] indices)
{
    Mesh msh = new Mesh();
    msh.vertices = Points;
    msh.triangles = indices;
    msh.RecalculateNormals();
    msh.RecalculateBounds();

    return msh

I’ve managed to get plotting at least 1 triangle however theres a few complex issues

The main one is your data, those values push the data off the map. You need to offset them by reducing them or something.

Make a cube, and drag a script onto it containing a cut paste script from here

This should create a triangle on that cube, a success of sorts!

Then alter the vertices to your values, I have done this via arrays however even this is not simple.

Here is my code, note you would not see anything unless you change your values which are off the map, change them to as per the example and you’ll see them

This is not really complete as I havent got my head around triangles at all, its a hybrid of your data and the example script from the reference manual that just uses the first 3 vertices.

function Awake () {
List.Push(Vector3(692760.000000, 541490.000000, 1.5480));
List.Push(Vector3(692760.000000,541440.000000,1.5513));
List.Push(Vector3(692760.000000,541390.000000,1.5525));
List.Push(Vector3(692760.000000,541340.000000,1.5528));
List.Push(Vector3(692760.000000,541290.000000,1.5531));
List.Push(Vector3(692760.000000,541240.000000,1.5557));
List.Push(Vector3(692810.000000,541690.000000,1.5396));
List.Push(Vector3(692810.000000,541640.000000,1.5392));
List.Push(Vector3(692810.000000,541590.000000,1.5406));
List.Push(Vector3(692810.000000,541540.000000,1.5462));
List.Push(Vector3(692810.000000,541490.000000,1.5521));
List.Push(Vector3(692810.000000,541440.000000,1.5554));
List.Push(Vector3(692810.000000,541390.000000,1.5555));
List.Push(Vector3(692810.000000,541340.000000,1.5546));
List.Push(Vector3(692810.000000,541290.000000,1.5533));
List.Push(Vector3(692810.000000,541240.000000,1.5563));
List.Push(Vector3(692810.000000,541190.000000,1.5616));
List.Push(Vector3(692810.000000,541140.000000,1.5666));
List.Push(Vector3(692810.000000,541090.000000,1.5704));
List.Push(Vector3(692810.000000,541040.000000,1.5729));
List.Push(Vector3(692810.000000,540990.000000,1.5748));
List.Push(Vector3(692810.000000,540940.000000,1.5763));
List.Push(Vector3(692810.000000,540890.000000,1.5779));
List.Push(Vector3(692860.000000,541940.000000,1.5343));
List.Push(Vector3(692860.000000,541890.000000,1.5396));
List.Push(Vector3(692860.000000,541840.000000,1.5438));
List.Push(Vector3(692860.000000,541790.000000,1.5466));
List.Push(Vector3(692860.000000,541740.000000,1.5476));
List.Push(Vector3(692860.000000,541690.000000,1.5474));
List.Push(Vector3(692860.000000,541640.000000,1.5468));
List.Push(Vector3(692860.000000,541590.000000,1.5474));
List.Push(Vector3(692860.000000,541540.000000,1.5520));
List.Push(Vector3(692860.000000,541490.000000,1.5567));
List.Push(Vector3(692860.000000,541440.000000,1.5588));

// Builds a mesh containing a single triangle with uv's.
gameObject.AddComponent("MeshFilter");
gameObject.AddComponent("MeshRenderer");
var mesh : Mesh = GetComponent(MeshFilter).mesh;

mesh.Clear();

mesh.vertices = [Vector3(List[0].x, List[0].y, List[0].z), Vector3(List[1].x, List[1].y, List[1].z), Vector3(List[2].x, List[2].y, List[2].z)];
mesh.uv = [Vector2 (0, 0), Vector2 (0, 1), Vector2 (1, 1)];
mesh.triangles = [0, 1, 2];
}

And why do you want to use a text file? Why not use the heightmap directly, in Unity?

You can certainly add whatever data you want to your custom data file; it’s just a case of parsing it out and feeding it into the appropriate APIs to get the result you want :slight_smile:

Assuming you have the code to load the data working OK, it should be trivial. In outline:

List vertices = <read list of Vector3d from text file>;
Mesh mesh = GetComponent(MeshFilter).mesh;
mesh.vertices = vertices.ToArray();
mest.triangles = triangles.ToArray();

Take a look at the Proceedural Content sample project; there are a number of examples in there of building and/or deforming meshes at run-time.

OKAY some limited success in reading vector3 from an xml file and parsing it. For anyone who is interested here is what I cobbled together. It’s not really what I need for my purposes yet, but it’s a start… If anyone can help me out some more, please do.

The xml file containing vector3 info foramted as follows:

   <start number="6"> 
      <pos>4,1,2</pos> 
      <pos>3,2,6</pos> 
      <pos>2,3,3</pos>
      <pos>11,18,12</pos> 
      <pos>13,12,16</pos> 
      <pos>20,9,8</pos>
   </start>

this script reads the file and on a click of a GUI button instantiates a game object at the first location. The next click instantiates the object at the second location etc… (I would like to instantiate them all at once, anyone knows how?) Anyway, this works.

import System.Xml; 

var obj : GameObject;
var maxData : int; 
var showData : int; 
var data : String[];
var outVector3 : Vector3; 
var splitString = new Array();


function OnGUI() 
{  
   if(showData<maxData) 
   {  
      GUI.Label(Rect(400,20,60,20), data[showData]); 
      if(GUI.Button(Rect(400,40,60,20),"CLICK")) 
      {  
         showData++;
         Instantiate(obj, outVector3, Quaternion.identity);
         if(showData>=maxData) 
            showData=0; 
      } 
   } 
}
      
function Start() { 
      
   maxData = 0; 
   showData = 0;
   data = null; 
     
      var reader:XmlReader = XmlReader.Create("grid.xml"); 
      while(reader.Read()) 
      {  
         if(reader.IsStartElement("start")) 
         { 
         maxData = parseInt(reader.GetAttribute("number")); 
          
         data = new String[maxData]; 
           
         for(showData = 0;showData<maxData;showData++) 
         { 
            reader.Read(); 
            if(reader.IsStartElement("pos")) 
            { 
               data[showData] = reader.ReadString();
                               
            }             
         } 
         showData=0; 
         } 
     }  
}

function Update () {

var newstring = data[showData].ToString();    
   splitString = newstring.Split("," [0]); 
    
   outVector3.x = parseFloat(splitString[0]); 
   outVector3.y = parseFloat(splitString[1]); 
   outVector3.z = parseFloat(splitString[2]); 
    
return outVector3;

}

[/code]

A little more success??? :?

The script reads a text asset which looks like this… and instantiates a GameObject at the first location (21,14,11). Great. But the problem remains… How do I instantiate a GameObject at all of the locations at the same time?

21,14,11
6,1,1
11,1,1
1,1,6
var grid : TextAsset; 
var obj : GameObject;
var outVector3 : Vector3; 
var vectors = new Array();

function Start () {

var alltext = grid.text.ToString();

lines = alltext.Split("\n"[0]);
       
for(var line : String in lines) 
   {  
var vectors: String[] = line.Split(","[0]);
    
   outVector3.x = parseFloat(vectors[0]); 
   outVector3.y = parseFloat(vectors[1]); 
   outVector3.z = parseFloat(vectors[2]);

Instantiate(obj, outVector3, Quaternion.identity);
    
return outVector3;

}
}

SUCCESS!!!

This works, but it does give me a warning “FormatException : Invalid format.”
Now for the next question… Not that anyone is answering. "How to instantiate a single, static particle in those positions, instead of a game object?

var grid : TextAsset; 
var obj : Transform;
var outVector3 : Vector3; 
var vectors : Array = new Array(); 

 
function Start() 
{
      var lines : String[] = grid.text.Split("\n"[0]); 
      for (var line : String in lines) 
      {    
         var vectors : String[] = line.Split(","[0]); 
 
         outVector3.x = parseFloat(vectors[0]); 
         outVector3.y = parseFloat(vectors[1]); 
         outVector3.z = parseFloat(vectors[2]);          
         MakeGrid();
      }  
   } 
 
function MakeGrid() 
{ 

Instantiate(obj,outVector3,Quaternion.identity); 
            
            vectors.Push(obj); 
}

Congratulations on your progress :slight_smile: To instantiate a particle system just drag the particle system prefab onto the ‘obj’ variable in the inspector (instead of whatever game object you have it set to now).

Thanks Laurie

But wouldn’t that mean a particle system at each of the locations? Whether I need one solitary particle on each of the locations. Which I’m guessing means having to pass the positions array into the particle array, which I don’t know how to do… yet :wink:

That’s not something I’ve tried, but I assume you would just need to create an array of Particle, setting the position of each particle from your vertex array (and setting other properties as needed), then just assign that array to your emitter’s ‘particles’ variable.

Something along these lines (rough, untested, total guess :-)):

function Start()
{
  var lines : String[] = grid.text.Split("\n"[0]);
  var particles : Particle[] = Particle[lines.Length];
  for (var line : String in lines)
  {
    var vectors : String[] = line.Split(","[0]);
    outVector3.x = parseFloat(vectors[0]);
    outVector3.y = parseFloat(vectors[1]);
    outVector3.z = parseFloat(vectors[2]);

    var particle : Particle = Particle();
    particle.position = outVector;
    // set other particle properties here as needed
    particles[i] = particle;
  }

  // Assign the particles array to a particle emitter
  gameObject.Find("MyParticleEmitter").particles = particles;
}

Thank you Laurie, that looks prommissing, but I’m getting this error with your script.

“Type ‘System.Type’ does not support slicing.”

Any ideas?
:wink:

YAAAY! It works… except for a little problem… It instantiates copies of the obj at 0,0,0 and I only want one obj to be instantiated. OK, maybe two little problems, the instantiated object is a mesh emitter so I can see it emitting particles, as well as those that are static set by the newVector3s. Definite progress though…

Consider this NOOB colored proud. :slight_smile:

var grid : TextAsset; 
var obj : Transform;
var outVector3 : Vector3; 
var vectors : Array = new Array();

 
function Start() 
{ 
  var lines : String[] = grid.text.Split("\n"[0]);
  for (var line : String in lines) 
  { 
    var vectors : String[] = line.Split(","[0]); 
    outVector3.x = parseFloat(vectors[0]); 
    outVector3.y = parseFloat(vectors[1]); 
    outVector3.z = parseFloat(vectors[2]); 

var myEmitter = Instantiate(obj,Vector3(0,0,0),Quaternion.identity);
    
var emitter = myEmitter.GetComponent(ParticleEmitter); 
    
emitter.Emit(); 
    
var p : Array = new Array(emitter.particles); 
     
for (var i=0; i<p.length; i++) { 
p[i].position = outVector3; 

}  
   emitter.particles = p.ToBuiltin(Particle);
}
}

:slight_smile:

SO, second problem solved by simply unchecking the EMIT field in the prefab particle emitter. But the first question remains. Only need one object emitting.

Create/instantiate the emitter before your “for (var line : String in lines)” loop. Also create the array for the particles before the loop as well. Then in the loop add all the particles to the array. Once your loop is finished, assign the particles array back to the emitter.

I’ve tried it, but now I’m only getting one particle from one emitter. How do I assign the array back to the emitter?

Thanks

var grid : TextAsset; 
var obj : Transform;
var outVector3 : Vector3; 
var vectors : Array = new Array();

 
function Start() { 
  var lines : String[] = grid.text.Split("\n"[0]);
  
  var myEmitter = Instantiate(obj,Vector3(0,0,0),Quaternion.identity);
  var emitter = myEmitter.GetComponent(ParticleEmitter); 
  emitter.Emit(); 
  var p : Array = new Array(emitter.particles);
  
  
  for (var line : String in lines) { 
    var vectors : String[] = line.Split(","[0]); 
    outVector3.x = parseFloat(vectors[0]); 
    outVector3.y = parseFloat(vectors[1]); 
    outVector3.z = parseFloat(vectors[2]); 

for (var i=0; i<p.length; i++) { 
p[i].position = outVector3; 

}  
   emitter.particles = p.ToBuiltin(Particle);
}
}