[RESOURCE] Makers' Mesh Separator Script

Hi everyone!
I was used to script for the bge, but since i needed a script that subdivides a mesh of a terrain in many squares to be loaded dynamically, i wrote it on my own.

I started writing it in python, but was sooooo slow and always crashed on meshes with more than 1500 vertices, so…

I wrote it in C.

Since with C i have the memory under my control, i managed to write a lot faster and a lot less memory consuming script, so now it works on many more vertices(on my notebook with a i 450M i managed to run it without any problems with 65000 vertices and it took less than 2 minutes. I haven’t tried on more vertices, but i think it can work even on a million)

When the university doesn’t take all my time, i’ll try to update and correct bugs.
One of the “features” i want to implement in the “next version” is to make it multithreaded, so multicores processors will go from 2 to 8 time faster. :slight_smile:

HOW TO USE:
Just put the compiled C file in the main folder of blender, run blender, run the script from the text editor, press the button “Separe”, and now it starts working :slight_smile:
http://img816.imageshack.us/img816/8343/terrainsplit.th.jpg

http://img522.imageshack.us/img522/9004/terrainsplit2.th.jpg

Hope it will help you!

http://www.mediafire.com/?5hnwb8ou952gpt9

Here the script with all the informations, the C cource and a compiled binary(for win7 64)
I have tryed it on a mesh generated with the ANT landscape generator(thank you Jimmy Hazevoet)
(ps:not sure if it will work on other os, the encoding of the written files is acii end not binary, so something may fail)
(if you are on windows you can use devc++ or minggw to compile, on unix you can use gcc)

Due to a server error, this haven’t been posted

And here the plain text
python script


############################################
#
#                MESH SEPARATOR SCRIPT
#
#You can use, share and modify this script as you want, just always remind the author(me).
#
#This script splits the mesh of an object in many squares.
#I have wrote this in order to split terrain meshes for my dynamic loading terrain script.
#
#
#At first i did this with python, but was orribly slow and memory consuming(on my notebook it crashed if the vertices were over 1000)
#
#So i wrote it in C, and made the python script "interface" with the C program.
#Now it can handle more than 64000 vertices(i did't tryed it, but i think the script
#can easly go over a milion vertices, but the time it will require will grow a lot) and
# separe the mesh in less then 2 minutes(on a notebook).
#You'll find the C source commented at the end of this script.
#
#Since i put a lot of effort in doing this, if you have good tips, bugs, or sugestions
#(if supported with code, or an idea on how to solve/add them is even better) i'll really
#appreciate if you mail me at makers.f (at) gmail (dot) com
#
#If you use it to get nice results, please mail me too, so i'll see if my work is usefeul ;)
#
#
#I'm still working on this, so when i'll have time(the university consume it a lot)
#i'll make it multithread so that the multicore processors will go from 2 to 8 time faster(based
#on how many thread you can compute at the same time)
#
#By Francesco Zoffoli(aka Makers_F)
############################################


import bpy
from bpy.props import IntProperty
from math import pow
import os

def separemesh(context, function, size):
 #starting infos
 passatr=str(function) + " " + str(len(bpy.context.active_object.data.vertices)) + " " + str(size)
 
 #add the infos of all vertices(z vertex is unused, so it can be removedo, but ONLY IF DONE IN THE C PROGRAM TOO
 for v in bpy.context.active_object.data.vertices:
  passatr+=" " + str(v.index) + " " + str(v.co[0])+ " " + str(v.co[1])+ " " + str(v.co[2])
 
 

 #write infos to file
 path="testo.txt"
 f=open(path,"w")
 f.write(passatr)
 f.close
 
 #delete the variable now useless(save memory)
 del passatr
 #run the C program
 os.system("FaceDividerFile.exe " + path)
 
 if function==2:
  #get the infos given by the c program
  f=open("square_list.txt","r")
  list=[]
  nl=f.readline()
  while(nl!=''):
   list.append(int(nl))
   nl=f.readline()
  
  f.close()
  
  #deselect al vertices
  bpy.ops.object.mode_set(mode='EDIT')
  bpy.ops.mesh.select_all(action='DESELECT')
  bpy.ops.object.mode_set(mode='OBJECT')
  
  #select the useful vertices
  for i in list:
   bpy.context.active_object.data.vertices[i].select = True
  
  bpy.ops.object.mode_set(mode='EDIT')
  bpy.ops.mesh.separate(type="SELECTED")
  bpy.ops.object.mode_set(mode='OBJECT')
  return
 
 if function==1:
  f=open("max_min.txt","r")
  lower=int(f.readline())
  highter=int(f.readline())
  f.close()
  return [lower,highter]
  
  
  
class separeMesh(bpy.types.Panel):
 bl_label= "Separe Mesh"
 bl_space_type = "VIEW_3D"
 bl_region_type = "UI"
 
 '''
 #property(commented becouse it doesn't work..)
 bpy.context.scene.sideprop = IntProperty(name="Square Side", description="The lengh of the square side", min=2, max=5, default=4)
 '''
 
 @classmethod
 #only if the object selected is a mesh object the menu will be displayed
 def poll(self, context):
  if context.object and context.object.type == "MESH":# and context.object.mode== "EDIT":
   return 1

 
 
 def draw(self, context):
  layout = self.layout
  box = layout.box()
  row = box.row(align=False)
  '''
  row.prop(bpy.context.scene, 'sideprop')
  '''
  
  row=box.row(align=False)
  row.operator("object.SepareMesh")
  

class OBJECT_OT_SepareMesh(bpy.types.Operator):
 bl_idname = "OBJECT_OT_SepareMesh"
 bl_label = "Separe"
 context=bpy.context
 #obj_act=context.active_object
 #data_ob=bpy.context.active_object.data.vertices
 
 def execute(self, context):
  import bpy
  
  print("Esecuzione")
  
  size=2 #set it with prop(now that doesn't work...)
  size=pow(size,2)
  
  lower = separemesh(context,1,size)
  highter = lower[1]
  lower = lower[0]
  
  tiles = round((round(bpy.context.active_object.data.vertices[highter].co[0] - bpy.context.active_object.data.vertices[lower].co[0]) * round(bpy.context.active_object.data.vertices[highter].co[1] - bpy.context.active_object.data.vertices[lower].co[1]))/size)+1
  
  print("The mesh will be divided in " + str(tiles) + " objects")
  for i in range(tiles):
   separemesh(context,2,size)
   print(str(i+1) + " separated
")
  
  os.remove("testo.txt")
  os.remove("square_list.txt")
  os.remove("max_min.txt")
  print("Finito")
  return{'FINISHED'}

C code

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

typedef struct{
    int index;
    float x,y,z;
} vertex;

vertex *find_vertex(vertex *list, int len)
{
    int i;
    vertex lower,highter;
    lower=list[0];
    highter=list[1];
    for(i=0;i<len;i++)
    {
        if ((list[i].x<=lower.x) && (list[i].y<=lower.y))
            lower=list[i];
        if ((list[i].x>=highter.x) && (list[i].y>=highter.y))
            highter=list[i];
    }
    vertex *ret;//create a pointer to return(it's not possible to return array)
    ret=(vertex*)malloc(sizeof(vertex)*2);
    if (ret==NULL)
    {
        printf("Can't allocate the memory");
        system("pause");
        return 0;
    }
    ret[0]=lower;
    ret[1]=highter;
    return ret;
}

vertex *square_list_of_vertex(vertex *list,int len,vertex start, float size)
{
    int i=0,a=0;
    unsigned int *num;
    num=(int*)malloc(sizeof(unsigned int)*len);
    if (num==NULL)
    {
        printf("Can't allocate the memory");
        system("pause");
        return 0;
    }
    //controlls which points are in the right position and adds their main list's index in another list
    for(i=0;i<len;i++)
    {
        if ((list[i].x-start.x)<size && (list[i].y-start.y<size))
        {
            if (list[i].y-start.y>-size/100)
            {
                num[a]=i;
                a++;//len of the return list
            }
        }
    }
    
    //create the list with the right vertices
    vertex *retlist;
    retlist=(vertex*)malloc(sizeof(vertex)*(a+1));
    if (retlist==NULL)
    {
        printf("Can't allocate the memory");
        system("pause");
        return 0;
    }
    //the first index is used only as an info container
    vertex infos;
    infos.index=a+1;
    retlist[0]=infos;
    
    //set the value for the return pointer
    for(i=1;i<=a;i++)
    {
        retlist[i]=list[num[i-1]];
    }
    
    return retlist;
}

//the function that pass the data to python
void return_funct_1(vertex lower,vertex highter)
{
    FILE* ret;
    ret=fopen("max_min.txt","w");
    
    fprintf(ret,"%i
",lower.index);
    fprintf(ret,"%i
",highter.index);
    fclose(ret);
}

//the function that pass the data to python
void return_funct_2(vertex *squarelist)
{
    FILE* ret;
    int i,len;
    ret=fopen("square_list.txt","w");
    len=squarelist[0].index;
    for(i=1;i<len;i++)
    {
        //return all the informations
        //fprintf(ret,"%i %f %f %f
",squarelist[i].index,squarelist[i].x,squarelist[i].y,squarelist[i].z);
        
        //just return the index(it's enought for the python script
        fprintf(ret,"%i
",squarelist[i].index);
    }
    fclose(ret);
}





//argv: path of the file
int main(int argc, char *argv[])
{
    if(argc==1)
    {
        printf("%s need a path to a vectorlist file
",argv[0]);
        system("pause");
        return 0;
    }
    FILE* input;
    input=fopen(argv[1],"r");
    if (input==NULL)
    {
        printf("Error opening the file
");
        system("pause");
        return(0);
    }
    int func=0,i=0,a=0,u=0;
    char read;
    char* argument;
    argument=(char*)malloc(sizeof(char)*50);//i don't think the program will be used with more than 10^50 -1 vertices
    
    //get the first paramater in the file
    argument[0]=fgetc(input);
    argument[1]='\0';
    func=atoi(argument);
    
    //skipp the space
    read=fgetc(input);
    
    //get the number of vertices;
    i=0;
    do {
        read=fgetc(input);
        argument[i]=read;
        i++;
    }while(read!=' ' && !feof(input) );
    //set the end of the string
    argument[i]='\0';
    
    //set the variable to the correct integer value;
    int vnumber=atoi(argument);
    
    i=0;
    do {
        read=fgetc(input);
        argument[i]=read;
        i++;
    } while(read!=' ' && !feof(input));
    //set the end of the string
    argument[i]='\0';
    float sqsize=atof(argument);
    
    vertex *list;
    //allocate memory in the array to fit the number of vertex needed
    list=(vertex*)malloc(sizeof(vertex)*vnumber);
    
    //control if the memory get allocated
    if (list==NULL)
    {
        printf("Can't allocate the memory");
        system("pause");
        return 0;
    }
    
    //do the cycle for each vertex
    for(u=0;u<vnumber;u++)
    {
        //read the number and assign it to the proper value of the vertex
        for(a=0;a<4;a++)
        {
            i=0;
            do
            {
                read=fgetc(input);
                argument[i]=read;
                i++;
            } while(read!=' ' && !feof(input));
            argument[i]='\0';
            
            if(a==0)
                list[u].index=atoi(argument);
            if(a==1)
                list[u].x=atof(argument);
            if(a==2)
                list[u].y=atof(argument);
            if(a==3)
                list[u].z=atof(argument);
        }
        //printf("%i, %f, %f, %f
",list[u].index,list[u].x,list[u].y,list[u].z);
    }
    
    //close the file
    fclose(input);
    
    if (func==1)
    {
        //let's find the lower and the highter
        //find the lowest vertex and the higtest vertex
        vertex lower,highter;
        vertex *lohi;
        lohi=(vertex*)find_vertex(list, vnumber);
        lower=lohi[0];
        highter=lohi[1];
        free(lohi);
        return_funct_1(lower,highter);//the function that return the data to python
    }
    
    if(func==2)
    {
        //find the list to return
        vertex *lohi;
        lohi=(vertex*)find_vertex(list, vnumber);
        vertex lower;
        lower=lohi[0];
        free(lohi);
        return_funct_2(square_list_of_vertex(list,vnumber, lower, sqsize));//the function that return the data to python
    }
    //system("pause");
}

Put it up on a source-code hosting service like GitHub, SourceForge, Gitorious or something. Easier to manage development that way.

Since at least one person seamed interested in, i up the topic with the last development. By now i didn’t yet managed to use properties, but just change the size of the mesh inside the python script(commments will help you find where to change the settings :wink: )
After a bit of research, i found that the bottleneck for the use on big meshes(1kk vertices) is the atoi and atof C functions, and by now i’ve alreasy resolved the problem, but it need a bit of testing(bottleneck is relative, it just took 5 secs to run).

Here you the python code


############################################
#
#                MESH SEPARATOR SCRIPT
#
#You can use, share and modify this script as you want, just always remind the author(me).
#
#This script splits the mesh of an object in many squares.
#I have wrote this in order to split terrain meshes for my dynamic loading terrain script.
#
#
#At first i did this with python, but was orribly slow and memory consuming(on my notebook it crashed if the vertices were over 1000)
#
#So i wrote it in C, and made the python scriot "interface" with the C program.
#You'll find the C source commented at the end of this script.
#
#Since i put a lot of effort in doing this, if you have good tips, bugs, or sugestions
#(if supported with code, or an idea on how to solve/add them is even better) i'll really 
#appreciate if you mail me at makers.f (at) gmail (dot) com
#
#If you use it to get nice results, please mail me too, so i'll see if my work is usefeul ;)
############################################


import bpy
from mathutils import Vector
from bpy.props import IntProperty
from math import pow
import os

def separemesh(context, function, size, nomeID):
 #starting infos
 passatr=str(function) + " " + str(len(bpy.data.objects[nomeID].data.vertices)) + " " + str(size)
 
 #add the infos of all vertices(z vertex is unused, so it can be removedo, but ONLY IF DONE IN THE C PROGRAM TOO
 for v in bpy.data.objects[nomeID].data.vertices:
  passatr+=" " + str(v.index) + " " + str(v.co[0])+ " " + str(v.co[1])+ " " + str(v.co[2])
 
 
 #print(passatr)
 #write infos to file
 path="testo.txt"
 with open(path,"w") as f:
  f.write(passatr)
  f.close
 
 #delete the variable now useless(save memory)
 del passatr
 #run the c program
 
 os.system("FaceDividerFile.exe " + path)
 
 
 
 if function==2:
  #get the infos given by the c program
  with open("square_list.txt","r") as f:
   list=[]
   nl=f.readline()
   while(nl!=''):
    list.append(int(nl))
    nl=f.readline()
  
   f.close()
  
  #deselect al vertices
  bpy.ops.object.mode_set(mode='EDIT')
  bpy.ops.mesh.select_all(action='DESELECT')
  bpy.ops.object.mode_set(mode='OBJECT')
  
  #select the useful vertices
  for i in list:
   bpy.data.objects[nomeID].data.vertices[i].select = True
  
  bpy.context.scene.objects.active=bpy.data.objects[nomeID]
  bpy.ops.object.mode_set(mode='EDIT')
  bpy.ops.mesh.separate(type="SELECTED")
  bpy.ops.object.mode_set(mode='OBJECT')
  return
 
 if function==1:
  with open("max_min.txt","r") as f:
   lower=int(f.readline())
   highter=int(f.readline())
   f.close()
  return [lower,highter]
  

def pyseparemesh(context, func, size, obnome):
 low = 0
 hig = 1
 for i in range(len(bpy.data.objects[obnome].data.vertices)):
  if bpy.data.objects[obnome].data.vertices[i].co[0]<=bpy.data.objects[obnome].data.vertices[low].co[0] and bpy.data.objects[obnome].data.vertices[i].co[1]<=bpy.data.objects[obnome].data.vertices[low].co[1]:
   low=i
  elif bpy.data.objects[obnome].data.vertices[i].co[0]>=bpy.data.objects[obnome].data.vertices[hig].co[0] and bpy.data.objects[obnome].data.vertices[i].co[1]>=bpy.data.objects[obnome].data.vertices[hig].co[1]:
   hig=i
 return [low,hig]

def setorigin(context, size, nomeOBJ):
 
 obnome=nomeOBJ
 
 #print("Working on :" + obnome)
 
 oblower = pyseparemesh(context,1,size, obnome) #use the function written in python instead of the one written in C
 obhighter = oblower[1]
 oblower = oblower[0]
 #print("Lower :" + str(bpy.data.objects[obnome].data.vertices[oblower].co))
 #print("Highter :" + str(bpy.data.objects[obnome].data.vertices[obhighter].co))
 cursorx=bpy.data.objects[obnome].data.vertices[oblower].co[0] + (bpy.data.objects[obnome].data.vertices[obhighter].co[0] - bpy.data.objects[obnome].data.vertices[oblower].co[0])/2 
 cursory=bpy.data.objects[obnome].data.vertices[oblower].co[1] + (bpy.data.objects[obnome].data.vertices[obhighter].co[1] - bpy.data.objects[obnome].data.vertices[oblower].co[1])/2
 bpy.context.scene.cursor_location =  Vector((cursorx, cursory, 0))
 #print("Orig :" + str(bpy.context.scene.cursor_location))
 bpy.ops.object.select_all(action='DESELECT')
 bpy.ops.object.select_name(name=obnome)
 bpy.context.scene.objects.active=bpy.data.objects[obnome]
 bpy.ops.object.origin_set(type="ORIGIN_CURSOR")
 #os.remove("testo.txt")
 #os.remove("square_list.txt")
 #os.remove("max_min.txt")
 
 
 
  
class separeMesh(bpy.types.Panel):
 bl_label= "Separe Mesh"
 bl_space_type = "VIEW_3D"
 bl_region_type = "UI"
 
 '''
 #property(commented becouse it doesn't work..)
 bpy.context.scene.sideprop = IntProperty(name="Square Side", description="The lengh of the square side", min=2, max=5, default=4)
 '''
 
 @classmethod
 #only if the object selected is a mesh object the menu will be displayed
 def poll(self, context):
  if context.object and context.object.type == "MESH":# and context.object.mode== "EDIT":
   return 1

 
 
 def draw(self, context):
  layout = self.layout
  box = layout.box()
  row = box.row(align=False)
  '''
  row.prop(bpy.context.scene, 'sideprop')
  '''
  
  row=box.row(align=False)
  row.operator("object.SepareMesh")
  

class OBJECT_OT_SepareMesh(bpy.types.Operator):
 bl_idname = "OBJECT_OT_SepareMesh"
 bl_label = "Separe"
 context=bpy.context
 #obj_act=context.active_object
 #data_ob=bpy.context.active_object.data.vertices
 
 def execute(self, context):
  import bpy
  
  print("Esecuzione")
  
  sidesize=2 #set it with prop(now that doesn't work...)
  size=pow(sidesize,2)
  #function = 2 #set it to 1 or 2 in base at what you need (use prop)
  tiles = 5 #how many separations to do
  nome=bpy.context.active_object.name
  
  lower = separemesh(context,1,size, nome)
  highter = lower[1]
  lower = lower[0]
  #tiles = (lunghezza sul lato x * lunghezza sul lato y)=Area    Area/Aquadrati= numeri di quadrati per formarla
  tiles = (round((bpy.data.objects[nome].data.vertices[highter].co[0] - bpy.data.objects[nome].data.vertices[lower].co[0])/size) + 1 ) * (round((bpy.data.objects[nome].data.vertices[highter].co[1] - bpy.data.objects[nome].data.vertices[lower].co[1])/size) +1) -1
  lox=bpy.data.objects[nome].data.vertices[lower].co[0]
  loy=bpy.data.objects[nome].data.vertices[lower].co[1]
  hix=bpy.data.objects[nome].data.vertices[highter].co[0]
  hiy=bpy.data.objects[nome].data.vertices[highter].co[1]
  
  
  
  #SEPARE MESH
  print("The mesh will be divided in " + str(tiles) + " objects.")
  for i in range(tiles):
   separemesh(context,2,size, nome)
   #print(str(i+1) + " separated
")
   print(str(i) " divided")
  print("Done")
  #######################################
  
  
  #ORIGIN SET
  print("Setting the orgins..")
  #set the origin
  for obj in bpy.data.objects:
   #it the name of the object has the same name of the separed mesh followed by whatever
   if (obj.name.rfind(nome))==0 and obj.type=="MESH":
    #set the origin to the center of the square mesh
    setorigin(context, size, obj.name)
  print("Done")
  #######################################
  
  
  #NAME SET
  print("Setting the names..")
  for obj in bpy.data.objects:
   #it the name of the object has the same name of the separed mesh followed by whatever
   if obj.name.rfind(nome)==0 and obj.type=="MESH":
    #struct of the object name:      "xsector ysector"
    obj.name=str(int((obj.location[0]-lox)/pow(sidesize,2))) +" "
    obj.name+=str(int((obj.location[1]-loy)/pow(sidesize,2)))
  print("Done")
  #######################################
  
  os.remove("testo.txt")
  os.remove("square_list.txt")
  os.remove("max_min.txt")
  print("Finito")
  return{'FINISHED'}

And the C code to compile (devc++ or gcc will go)


#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

typedef struct{
    int index;
    float x,y,z;
} vertex;

vertex *find_vertex(vertex *list, int len)
{
    int i;
    vertex lower,highter;
    lower=list[0];
    highter=list[1];
    for(i=0;i<len;i++)
    {
        if ((list[i].x<=lower.x) && (list[i].y<=lower.y))
            lower=list[i];
        if ((list[i].x>=highter.x) && (list[i].y>=highter.y)) //SOMETHING WRONG HERE!!!! iT DOESN'T RETURN THE RIGHT VERTEX(or meybe it return the right one?
            highter=list[i];
    }
    vertex *ret;//create a pointer to return(it's not possible to return array)
    ret=(vertex*)malloc(sizeof(vertex)*2);
    if (ret==NULL)
    {
        printf("Can't allocate the memory");
        return 0;
    }
    ret[0]=lower;
    ret[1]=highter;
    return ret;
}

vertex *square_list_of_vertex(vertex *list,int len,vertex start, float size)
{
    int i=0,a=0;
    unsigned int *num;
    num=(int*)malloc(sizeof(unsigned int)*len);
    if (num==NULL)
    {
        printf("Can't allocate the memory");
        return 0;
    }
    //controlls with point is in the right position and adds its indix in the main list in another list
    for(i=0;i<len;i++)
    {
        if ((list[i].x-start.x)<size && (list[i].y-start.y<size))
        {
            if (list[i].y-start.y>-size/100)//REMOVE THIS IF THE PROBLEM IS NOT SOLVED
            {
                num[a]=i;
                a++;//len of the return list
            }
        }
    }
    
    //create the list with the right vertices
    vertex *retlist;
    retlist=(vertex*)malloc(sizeof(vertex)*(a+1));
    if (retlist==NULL)
    {
        printf("Can't allocate the memory");
        return 0;
    }
    //the first index is used only as an info container
    vertex infos;
    infos.index=a+1;
    retlist[0]=infos;
    
    //set the value for the return pointer
    for(i=1;i<=a;i++)
    {
        retlist[i]=list[num[i-1]];
    }
    
    return retlist;
}

//the function that pass the data to python
void return_funct_1(vertex lower,vertex highter)
{
    FILE* ret;
    ret=fopen("max_min.txt","w");
    if (ret==NULL)
    {
        printf("Error opening the file
");
        return;
    }
    
    fprintf(ret,"%i
",lower.index);
    fprintf(ret,"%i
",highter.index);
    fclose(ret);
}

//the function that pass the data to python
void return_funct_2(vertex *squarelist)
{
    FILE* ret;
    int i,len;
    ret=fopen("square_list.txt","w");
    if (ret==NULL)
    {
        printf("Error opening the file
");
        return;
    }
    len=squarelist[0].index;
    for(i=1;i<len;i++)
    {
        //return all the informations
        //fprintf(ret,"%i %f %f %f
",squarelist[i].index,squarelist[i].x,squarelist[i].y,squarelist[i].z);
        
        //just return the index(it's enought for the python script
        fprintf(ret,"%i
",squarelist[i].index);
    }
    fclose(ret);
}





//argv:
//function[1/2] number_of_vert(int) size_of_square(int) v_index(int) v_xcoord(float) v_ycoord(float) v_zcoord(float)...
int main(int argc, char *argv[])
{
    if(argc==1)
    {
        printf("%s need a path to a vectorlist file
",argv[0]);
        return 0;
    }
    
    FILE* input;
    input=fopen(argv[1],"r");
    if (input==NULL)
    {
        printf("Error opening the file
");
        return(0);
    }
    int func=0,i=0,a=0,u=0;
    char read;
    char* argument;
    argument=(char*)malloc(sizeof(char)*50);//i don't think the program will be used with more than 10^50 -1 vertices
    
    //get the first paramater in the file
    argument[0]=fgetc(input);
    argument[1]='\0';
    func=atoi(argument);
    
    
    //printf("Func retrived : %i
",func);//DA RIMUOVERE
    
    //skipp the space
    read=fgetc(input);
    
    //get the number of vertices;
    i=0;
    do {
        read=fgetc(input);
        argument[i]=read;
        i++;
    }while(read!=' ' && !feof(input) );
    //set the end of the string
    argument[i]='\0';
    
    //set the variable to the correct integer value;
    int vnumber=atoi(argument);
    
    //printf("Vnumber retrived : %i
",vnumber);//DA RIMUOVERE
    
    i=0;
    do {
        read=fgetc(input);
        argument[i]=read;
        i++;
    } while(read!=' ' && !feof(input));
    //set the end of the string
    argument[i]='\0';
    float sqsize=atof(argument);
    
    //printf("Sqsize retrived : %f
",sqsize);//DA RIMUOVERE
    
    vertex *list;
    //allocate memory in the array to fit the number of vertex needed
    list=(vertex*)malloc(sizeof(vertex)*vnumber);
    
    //control if the memory get allocated
    if (list==NULL)
    {
        printf("Can't allocate the memory");
        return 0;
    }
    
    //do the cycle for each vertex
    for(u=0;u<vnumber;u++)
    {
        //read the number and assign it to the proper value of the vertex
        for(a=0;a<4;a++)
        {
            i=0;
            do
            {
                read=fgetc(input);
                argument[i]=read;
                i++;
            } while(read!=' ' && !feof(input));
            argument[i]='\0';
            
            if(a==0)
                list[u].index=atoi(argument);
            if(a==1)
                list[u].x=atof(argument);
            if(a==2)
                list[u].y=atof(argument);
            if(a==3)
                list[u].z=atof(argument);
        }
        //printf("%i, %f, %f, %f
",list[u].index,list[u].x,list[u].y,list[u].z);
    }
    
    //printf("List retrived
");//DA RIMUOVERE
    //close the file
    fclose(input);
    
    if (func==1)
    {
        //let's find the lower and the highter
        //find the lowest vertex and the higtest vertex
        //printf("Enter Func 1
");//DA RIMUOVERE
        vertex lower;
        vertex highter;
        vertex *lohi;
        lohi=(vertex*)find_vertex(list, vnumber);
        lower=lohi[0];
        highter=lohi[1];
        free(lohi);
        return_funct_1(lower,highter);//the function that return the data to python
        
        //printf("Find Lower finished

");//DA RIMUOVERE
        return 1;
    }
    
    if(func==2)
    {
        //find the list to return
        vertex *lohi;
        lohi=(vertex*)find_vertex(list, vnumber);
        vertex lower;
        lower=lohi[0];
        free(lohi);
        return_funct_2(square_list_of_vertex(list,vnumber, lower, sqsize));//the function that return the data to python
        return 1;
    }
    
    printf("Function argument was wrong: nothing was done
");
    //system("pause");
}

I’m still working on this. Many things changed
-no more support in C, too problematic to compile on each system
-export function to export the separated mesh, zipped in 2 different ways
-many and various bug fixes, and internal changes

bl_info = {
    "name": "Mesh Separator",
    "author": "Zoffoli Francesco (Makers_F)",
    "version": (0,5),
    "blender": (2, 5, 6),
    "api": 35517,
    "location": "View3D > Toolshelf ",
    "description": "Subdivide the mesh in many tiles of given size." ,
    "category": "Object"}

import bpy
import os
from mathutils import Vector
from bpy.props import BoolProperty, StringProperty, FloatProperty
from math import pow
import gzip, tarfile

def compress_obj_file(filepath, file_name):
 with gzip.open(filepath + file_name + ".gz", mode = 'wb', compresslevel = 3) as new_file:
  new_file.write((open(filepath + file_name, 'rb')).read())
  new_file.close()

def SetProperties():
    bpy.types.Scene.separator_square_size = FloatProperty(name="Square Side", 
                                                    description="The lengh of the square side",
                                                    min = 0.01,
                                                    default = 9.0,
                                                    step = 100,
                                                    precision = 2)
 
    bpy.types.Scene.delete_file_if_already_exists = BoolProperty(name="Override files if they exist", 
                                                    description="Check if exixsts a file with the same name of the one that will be created and overwrite it if True", 
                                                    default=False)
    
    bpy.types.Scene.triangulate_mesh = BoolProperty(name="Triangulate the mesh when exporting", 
                                                    description="Check if you want to export the mesh triangulated.", 
                                                    default=False)
    
    bpy.types.Scene.compress_single_gzip_files = BoolProperty(name="Compress each file", 
                                                    description="Compress each single file inside a separate gzip file", 
                                                    default=False)
    
    bpy.types.Scene.compress_all_files_in_tarbz2 = BoolProperty(name="Compress all the files", 
                                                    description="Compress every file inside a single tar.bz2 archive", 
                                                    default=False)
                                                    
    bpy.types.Scene.exported_obj_path = StringProperty(name="Export Path",
                                                    description="Set the path where all the exported .obj files will be placed",
                                                    subtype='DIR_PATH'
                                                    )

def FindLowHigVertex(nomeID):
    low = bpy.data.objects[nomeID].data.vertices[0]
    hig = bpy.data.objects[nomeID].data.vertices[1]
    for v in bpy.data.objects[nomeID].data.vertices:
        if v.co[0]<=low.co[0] and v.co[1]<=low.co[1]:
            low = v
        elif v.co[0]>=hig.co[0] and v.co[1]>=hig.co[1]:
            hig = v
    return low , hig

def get_vertex_list_python(nomeID, size):
    low , hig = FindLowHigVertex(nomeID)
    for v in bpy.data.objects[nomeID].data.vertices:
        if v.co[0]-low.co[0]<=size and -size/100<=v.co[1]-low.co[1]<=size:
            v.select = True

def SepareMesh(size, nomeID):
    #deselect all vertices
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_all(action='DESELECT')
    bpy.ops.object.mode_set(mode='OBJECT')

    get_vertex_list_python(nomeID, size)

    #separe the vertex
    bpy.context.scene.objects.active=bpy.data.objects[nomeID]
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.separate(type="SELECTED")
    bpy.ops.object.mode_set(mode='OBJECT')
    return

def SetOrigin(size, nomeID):
    oblower, obhighter = FindLowHigVertex(nomeID)
    cursorx = oblower.co[0] + (obhighter.co[0] - oblower.co[0])/2 
    cursory = oblower.co[1] + (obhighter.co[1] - oblower.co[1])/2
    bpy.context.scene.cursor_location =  Vector((cursorx, cursory, 0))
    bpy.ops.object.select_all(action='DESELECT')
    bpy.ops.object.select_name(name=nomeID)
    bpy.context.scene.objects.active=bpy.data.objects[nomeID]
    bpy.ops.object.origin_set(type="ORIGIN_CURSOR")
 
class SepareMeshPanel(bpy.types.Panel):
    bl_idname = "OBJECT_PT_SepareMeshPanel"
    bl_label = "Separe Mesh"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    #set all the needed properies
    SetProperties()

    @classmethod
    #only if the object selected is a mesh object the menu will be displayed
    def poll(self, context):
        if context.object and context.object.type == "MESH":
            return 1
   
    def draw(self, context):
        layout = self.layout
        box1 = layout.box()

        row = box1.row(align=False)
        row.prop(context.scene, "separator_square_size")

        row = box1.row(align=False)
        row.operator("object.separe_mesh", text="Separe Mesh")

        box2 = layout.box()

        row = box2.row(align=False)
        row.prop(context.scene, "exported_obj_path")

        row = box2.row(align=False)
        row.prop(context.scene, "delete_file_if_already_exists")

        row = box2.row(align=False)
        row.prop(context.scene, "triangulate_mesh")
        
        row = box2.row(align=False)
        row.prop(context.scene, "compress_single_gzip_files")
        row.prop(context.scene, "compress_all_files_in_tarbz2")
        
        row = box2.row(align=False)
        row.operator("object.export_objects", text="Export Objects")

class SepareMeshOperator(bpy.types.Operator):
    bl_idname = "object.separe_mesh"
    bl_label = "Separe Mesh"
    bl_description = "Separe the mesh in tiles, change the origin and rename them"
    bl_options = {'REGISTER', 'UNDO'}
    context = bpy.context

    def execute(self, context):
        import bpy

        #get options
        size = context.scene.separator_square_size
        nomeID = context.active_object.name

        lower, highter = FindLowHigVertex(nomeID)

        tiles_height = int((highter.co[1] - lower.co[1])/size) +1 #round per excess
        tiles_width = int((highter.co[0] - lower.co[0])/size) + 1 
        
        lox=lower.co[0]
        loy=lower.co[1]
        hix=highter.co[0]
        hiy=highter.co[1]

        #SEPARE MESH
        print("The mesh will be divided in ", tiles_height ," rows and ", tiles_width , " columns for a total of ", tiles_height*tiles_width , " objects..")
        for w in range(tiles_width):
            for h in range(tiles_height):
                SepareMesh(size, nomeID)
        #the starting object remains with a 0 verts meshat the end of this process
        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.select_name(name=nomeID)
        bpy.ops.object.delete()
        #######################################
  
        #ORIGIN SET
        print("Setting the orgins..")
        #set the origin
        for obj in bpy.data.objects:
            #it the name of the object has the same name of the separed mesh followed by whatever
            if (obj.name.rfind(nomeID))==0 and obj.type=="MESH":
                #set the origin to the center of the square mesh
                SetOrigin(size, obj.name)
  
        #NAME SET
        print("Setting the names..")
        for obj in bpy.data.objects:
            #if the name of the object has the same name of the separed mesh followed by whatever
            if obj.name.rfind(nomeID)==0 and obj.type=="MESH":
                #struct of the object name:      "xsector ysector"
                firstpart=str(int((obj.location[0]-lox + size/2)/size)) +" "
                secondpart=str(int((obj.location[1]-loy + size/2)/size))
                obj.name=firstpart+secondpart
                obj.data.name=obj.name #set the same name for the mesh
        #######################################
        print("Finished")
        return{'FINISHED'}


(continue…)

class ExportObjectsOperator(bpy.types.Operator):
    bl_idname = "object.export_objects"
    bl_label = "Export Objects"
    bl_description = "Export the objects"
    bl_options = {'REGISTER', 'UNDO'}
    context = bpy.context
 
    def execute(self, context):
        if not os.path.exists(bpy.path.abspath(context.scene.exported_obj_path)):
            try:
                os.mkdir(context.scene.exported_obj_path)
            except:
                print("An error occoured while creating the directory")

        path = bpy.path.abspath(context.scene.exported_obj_path)
        print("Exporting to : " + path)
        for obj in bpy.data.objects:#add list from previous script if have time
            if obj.type=="MESH":
                bpy.ops.object.select_all(action='DESELECT')
                bpy.ops.object.select_name(name=obj.name)
                file_path = bpy.path.ensure_ext(path + obj.name, ".obj")
                #if the file exist overwrite or not
                if os.path.exists(file_path):
                    if context.scene.delete_file_if_already_exists:
                        try:
                            os.remove(file_path)
                        except:
                            print("Unable to remove the file")
                    else:
                        continue
    
            loc = obj.location    
            bpy.ops.object.location_clear()
            bpy.ops.export_scene.obj(filepath = file_path, 
                                    check_existing = True, 
                                    filter_glob = "*.obj;*.mtl", 
                                    use_selection = True, 
                                    use_all_scenes = False, 
                                    use_animation = False, 
                                    use_apply_modifiers = True, 
                                    use_rotate_x90 = True, 
                                    use_edges = True, 
                                    use_normals = False, 
                                    use_hq_normals = True, 
                                    use_uvs = True, 
                                    use_materials = True, 
                                    copy_images = False, 
                                    use_triangles = context.scene.triangulate_mesh, 
                                    use_vertex_groups = False, 
                                    use_nurbs = False, 
                                    use_blen_objects = True, 
                                    group_by_object = False, 
                                    group_by_material = False, 
                                    keep_vertex_order = False)
                                
            obj.location = loc
        print("Compressing..")
        if context.scene.compress_single_gzip_files:
            for file_name in os.listdir(path):
                if file_name[-3:]=="obj":
                    compress_obj_file(path, file_name)
                    os.remove(path + file_name)
        
        if context.scene.compress_all_files_in_tarbz2:
            archive = tarfile.open(path + "Compressed Terrain.tar.bz2", mode = "w:bz2")
            for file_name in os.listdir(path):
                if file_name[-3:]=="obj" or file_name[-3:]=="mtl" or file_name[-2:]=="gz":
                    archive.add(path + file_name, arcname= file_name)
                    os.remove(path + file_name)
            archive.close()
        
        print("Finished")
        return{'FINISHED'}

def register():
    bpy.utils.register_class(SepareMeshOperator)
    bpy.utils.register_class(ExportObjectsOperator)
    bpy.utils.register_class(SepareMeshPanel)

def unregister():
    bpy.utils.unregister_class(SepareMeshOperator)
    bpy.utils.unregister_class(ExportObjectsOperator)
    bpy.utils.unregister_class(SepareMeshPanel)
    del bpy.types.Scene.separator_square_size
    del bpy.types.Scene.delete_file_if_already_exists
    del bpy.types.Scene.triangulate_mesh
    del bpy.types.Scene.exported_obj_path
    del bpy.types.Scene.compress_single_gzip_files
    del bpy.types.Scene.compress_all_files_in_tarbz2
#register the two classes|| used while trying it in the text editor
if __name__ == "__main__":
    register()

(i needed to split the message because of the length, but you need to copy and past it in a single file)

Thanks for the script, I was just wondering how do I take a mesh of triangles and make each one sprarate? Is this what your script does?

Got it and worked on SVN of today :wink:
Just tried it on a subdivide cube … to make it triangulated … got an *.obj imported it and it was triangulated (but the ‘right’ part was ‘removed’)
Means I have to look too what you are doing :wink:

The Monkey head is not handled correct (subdivided once) … parts are missing (size 1 used) … :eek:

Hi got your C-script with python working something.
Esecuzione
The mesh will be divided in 1 objects
1 separated

Finito

But the it needs two input files. With reverse ingeneering I could see what they are used for. But easier if you tell me (us) and evtl. give suitable examples … :wink:

In any case, it was my first (second) time, executing a meaningful *.c in Blender
(the c was compiled using cygwin :wink: ).

Very awesome, this sounds like quite a magnificant piece!
Thank you very much for creating and sharing!

Maybe consider of releasing it as gpl 3 or higher? (now I didn’t see any license type)
This will help to prevent potential legal problems in the future and insures the free and open spirit?

If you don’t want to apply any license to this, is it ok to use it as gpl 3 or higher? This would help to be able to combine it with gpl 3 and higher projects. :wink: