Floor generator script (work in progress)

see script below for explanation

"""
floor maker script *** work in progress ***
goal is to make something that looks like floor generator for 3dsmax
would require a multimaterial and ability to assign material IDs to objects
to work properly and ultimately randomization of the map color per object
which is currently not possible vray as far as I know.

Script will generate floor planks and add random text coordinates to each plank
but will do that within the maps size.
Right now a texture size of 2000 x 300 units is chosen. So the smaller the
planks are, the more it can be randomized

other possibilities to include:
- making the planks not straight, but this will require denser meshes
- rotation (180 degrees) of textures for more quasi uniqueness
- a check to avoid seams on short ends to not come to close together
***********************************
* script written by Gijs de Zwart *
* www.studiogijs.nl               *
* April, 2016                     *
***********************************
"""

import Rhino
import rhinoscriptsyntax as rs
import scriptcontext as sc
import random
import math

def createMesh(vertices,face_vertices):
    mesh = Rhino.Geometry.Mesh()
    for a, b, c in vertices: mesh.Vertices.Add(a, b, c)
    for face in face_vertices:
        if len(face)<4:
            mesh.Faces.AddFace(face[0], face[1], face[2])
        else:
            mesh.Faces.AddFace(face[0], face[1], face[2], face[3])
    return mesh


def addPlank(plane, width, length):
    
    #define texture size in world units here
    texWidth = 2000
    texHeight = 300
    
    rect=Rhino.Geometry.Rectangle3d(plane,width,length)
    pts=[rect.Corner(i) for i in range(0,4)]
    pts2=[]
    for i in range(0,4):
        pts2.append(Rhino.Geometry.Point3d(0,0,20))
    for i in range(0,len(pts2)):#modify points
        pts2[i].X=pts[i].X
        pts2[i].Y=pts[i].Y
    for pt in pts2:#add points to original points
        pts.append(pt)
        
    faceVertices = []
    faceVertices.append((0,3,2,1))
    faceVertices.append((0,1,5,4))
    faceVertices.append((1,2,6,5))
    faceVertices.append((2,3,7,6))
    faceVertices.append((3,0,4,7))
    faceVertices.append((4,5,6,7))
    
    
    mesh=createMesh(pts,faceVertices)
    mesh.Unweld(.3,True)
    dx=Rhino.Geometry.Interval(0,texWidth)
    dy=Rhino.Geometry.Interval(0,texHeight)
    dz=Rhino.Geometry.Interval(0,20)
    #add mesh to document to apply a texture map
    newmesh = sc.doc.Objects.AddMesh(mesh)
    randX=(texHeight-width)*random.uniform(0,1)
    randY=(texWidth-length)*random.uniform(0,1)
    texplane=Rhino.Geometry.Plane.WorldXY
    texplane.Origin = plane.Origin
    texplane.Rotate(math.pi/2,plane.ZAxis)
    texplane.OriginY+=length-texWidth+randY
    texplane.OriginX+=texHeight-randX
    texmap = Rhino.Render.TextureMapping.CreatePlaneMapping(texplane,dx,dy,dz)    
    table = Rhino.RhinoDoc.ActiveDoc.Objects
    Rhino.DocObjects.Tables.ObjectTable.ModifyTextureMapping(table,newmesh,1,texmap)

def randomFloor():
    floorwidth = rs.GetReal("enter floor width size",10000)
    floorlength = rs.GetReal("enter floor length size",10000)
    width=rs.GetReal("enter plank width",140)
    maxlength = rs.GetReal("enter plank max length",2000)
    minlength = rs.GetReal("enter plank min length",700)
    gapwidth = rs.GetReal("enter gap width", 2)
    plane = Rhino.Geometry.Plane.WorldXY
    i=0
    meshes=[]
    temp=0
    while i<floorwidth:
        j=0
        plane.OriginY=0
        while j<floorlength:
            
            randomlength = random.randint(minlength,maxlength)
            if j==0:
                startlength=random.randint(2,20)*maxlength/20
                while temp==startlength:
                    startlength=random.randint(3,20)*maxlength/20
                temp=startlength
                addPlank(plane, width, startlength)
                j+=startlength+gapwidth
                
            elif randomlength<floorlength-j:
                #if (floorlength-j)/2<randomlength:
                    #randomlength-=300
                addPlank(plane, width, randomlength)
                j+=randomlength+gapwidth
                
            else:
                addPlank(plane, width, floorlength-j)
                j=floorlength
                
            plane.OriginY=j
            
        plane.OriginX+=width+gapwidth
        i+=width
    floor=[sc.doc.Objects.AddMesh(mesh) for mesh in meshes]
    sc.doc.Views.Redraw()
            
    
randomFloor()

10 Likes

Great! I was just deciding how to do this and saw your script. I wanted to have variable plank widths so I put in a random and IF stmts to select between 4.75 to 6 " with 1/4" increments. Also, I dont have a rectangular layout so I am turning the resulting meshes into NURB boxes and then triming them to size. Another thing I want to do is prevent edge joints being closer than 6" on adjacent rows and some other layout suggestions. Any comments?

For now it’s probably easiest to turn on the control points of the generated mesh boxes and move some that are outside the boundary, and move some seams that are too close together. Because otherwise you will loose the texture mapping when turning them into nurbs boxes.

Is this script working for anyone else? I edit a new script and copy/paste this into the script but I’m getting all kinds of error messages, such as “expected end of statement”. I’ve used Rhino for 12 years but a total amateur with the script editor. I’m using Rhino 5…

Just tested it and it still works. Just to make sure: you choose EditPythonScript, not EditScript, because this is a python script.

1 Like

Thanks for clarifying, I wasn’t using Python and it’s working great now! Is there an easy way to apply texture mapping to the planks or is this a manual job?

Hey, new vray has multisubtext and random object id option, it is really simple to implement it… :wink:

random mapping is included in the script. If your wood texture is large enough, it will look as if each plank is different.

Anything new to know about this script and Rhino6 and Vray 3.6? Would really like to figure out how to have a more realistic plank floor with subtle randomness throught.

Thx

the script is not bound to VRay, it’s just making the geometry and applying the texture coordinates. So it should work with any renderer/version

Wow, this would be great man! I have found difficulties with the Floor generator for Max, especially when I had to export the texture. It seem that I couldn’t handle with the texture baking, which is needed to export texture and import it in Rhino.

Is there a small tutorial of it somewhere? :slight_smile:

I believe you are responding to Pitti’s question? Just select your objects and run VraySetObjectID, random then go to properties and select objects by ID and apply a material to it.
the multimaterial in Vray for Rhino is, as far as I know, only for proxy objects. Here at least, it doesn’t work to apply multimaterials to groups of objects that have their ID set.

1 Like

hellow there,

I’m not familiar with scripts. Can you give a small tutoria here ^^?

regards,
-CC

Hi Charles, what do you want to know exactly?

hi @Gijs,

I just want to know, how to import your script into Rhino, and use all the commands you provide. This is why a small demo/tutorial could help me throught this. Or maybe if you don’t have time, could you please link me references on how to import your type of script. (I don’t know if its python or C# or C…whatever ^^)

And from there I guess I will try the differents commands (which I think are : create mesh, add planks and randomfloor ?).

i’m really not familiar with this type of process.

regards,
-CC

Hi Charles,

The script is a python script. If you run the command
_EditPythonScript in Rhino, copy and paste the above script into a new script, and run it, it should look like a normal Rhino command.

After that you can also save it to a file. For example
RandomFloor.py

Say you saved this to c:\scripts\randomfloor.py

You can run it from a button by adding this line to the button:
! _-RunPythonScript "c:\scripts\randomfloor.py"

thank you @Gijs,

I manage to make it works, thanks to your instructions. I also do have couple questions :

  1. Would it be difficult to modifiy this script in orther to make it work on any surfaces (reference surfaces) ? ( I do have differentes surfaces / polysurfaces like ceilings)

  2. and a small distortion (random 3 axes rotation for the planks, in order to make the floor more realistic)

I could do all this throught grasshopper but its far less efficiente … I saw some trying to go throught this journey in this old post

I really apreciate that you share your script.

regards,
-CC

Nice to see that it’s useful :slight_smile:
To make this work on a surface would require another approach I guess, or do you mean that the size of the area to fill with planks would be controlled by a surface? Rectangular surfaces shouldn’t be too difficult but if it is other than that, it’s becoming complicated quickly.
As for the randomness:

"""
floor maker script *** work in progress ***
goal is to make something that looks like floor generator for 3dsmax
would require a multimaterial and ability to assign material IDs to objects
to work properly and ultimately randomization of the map color per object
which is currently not possible vray as far as I know.

Script will generate floor planks and add random text coordinates to each plank
but will do that within the maps size.
Right now a texture size of 2000 x 300 units is chosen. So the smaller the
planks are, the more it can be randomized

other possibilities to include:
- making the planks not straight, but this will require denser meshes
- rotation (180 degrees) of textures for more quasi uniqueness
- a check to avoid seams on short ends to not come to close together
***********************************
* script written by Gijs de Zwart *
* www.studiogijs.nl               *
* April, 2016                     *
* May, 2020 (random rotation)     *
***********************************
"""

import Rhino
import rhinoscriptsyntax as rs
import scriptcontext as sc
import random
import math

def createMesh(vertices,face_vertices):
    mesh = Rhino.Geometry.Mesh()
    for a, b, c in vertices: mesh.Vertices.Add(a, b, c)
    for face in face_vertices:
        if len(face)<4:
            mesh.Faces.AddFace(face[0], face[1], face[2])
        else:
            mesh.Faces.AddFace(face[0], face[1], face[2], face[3])
    return mesh


def addPlank(plane, width, length):
    
    #define texture size in world units here
    texWidth = 2000
    texHeight = 300
    
    rect=Rhino.Geometry.Rectangle3d(plane,width,length)
    pts=[rect.Corner(i) for i in range(0,4)]
    pts2=[]
    for i in range(0,4):
        pts2.append(Rhino.Geometry.Point3d(0,0,20))
    for i in range(0,len(pts2)):#modify points
        pts2[i].X=pts[i].X
        pts2[i].Y=pts[i].Y
    for pt in pts2:#add points to original points
        pts.append(pt)
        
    faceVertices = []
    faceVertices.append((0,3,2,1))
    faceVertices.append((0,1,5,4))
    faceVertices.append((1,2,6,5))
    faceVertices.append((2,3,7,6))
    faceVertices.append((3,0,4,7))
    faceVertices.append((4,5,6,7))
    
    
    mesh=createMesh(pts,faceVertices)
    mesh.Unweld(.3,True)
    dx=Rhino.Geometry.Interval(0,texWidth)
    dy=Rhino.Geometry.Interval(0,texHeight)
    dz=Rhino.Geometry.Interval(0,20)
    #add mesh to document to apply a texture map
    newmesh = sc.doc.Objects.AddMesh(mesh)
    randX=(texHeight-width)*random.uniform(0,1)
    randY=(texWidth-length)*random.uniform(0,1)
    texplane=Rhino.Geometry.Plane.WorldXY
    texplane.Origin = plane.Origin
    texplane.Rotate(math.pi/2,plane.ZAxis)
    texplane.OriginY+=length-texWidth+randY
    texplane.OriginX+=texHeight-randX
    texmap = Rhino.Render.TextureMapping.CreatePlaneMapping(texplane,dx,dy,dz)    
    table = Rhino.RhinoDoc.ActiveDoc.Objects
    Rhino.DocObjects.Tables.ObjectTable.ModifyTextureMapping(table,newmesh,1,texmap)
    center = rs.MeshAreaCentroid(newmesh)
    angle= random.uniform(-.5,.5)
    rs.RotateObject(newmesh, center, angle, copy=False)
    angle= random.uniform(-2,2)
    axis = Rhino.Geometry.Vector3d(0,1,0)
    rs.RotateObject(newmesh, center, angle, axis, copy=False)
    angle= random.uniform(-.25,.25)
    axis = Rhino.Geometry.Vector3d(1,0,0)
    rs.RotateObject(newmesh, center, angle, axis, copy=False)

def randomFloor():
    floorwidth = rs.GetReal("enter floor width size",10000)
    floorlength = rs.GetReal("enter floor length size",10000)
    width=rs.GetReal("enter plank width",140)
    maxlength = rs.GetReal("enter plank max length",2000)
    minlength = rs.GetReal("enter plank min length",700)
    gapwidth = rs.GetReal("enter gap width", 2)
    plane = Rhino.Geometry.Plane.WorldXY
    i=0
    meshes=[]
    temp=0
    while i<floorwidth:
        j=0
        plane.OriginY=0
        while j<floorlength:
            
            randomlength = random.randint(minlength,maxlength)
            if j==0:
                startlength=random.randint(2,20)*maxlength/20
                while temp==startlength:
                    startlength=random.randint(3,20)*maxlength/20
                temp=startlength
                addPlank(plane, width, startlength)
                j+=startlength+gapwidth
                
            elif randomlength<floorlength-j:
                #if (floorlength-j)/2<randomlength:
                    #randomlength-=300
                addPlank(plane, width, randomlength)
                j+=randomlength+gapwidth
                
            else:
                addPlank(plane, width, floorlength-j)
                j=floorlength
                
            plane.OriginY=j
            
        plane.OriginX+=width+gapwidth
        i+=width
    floor=[sc.doc.Objects.AddMesh(mesh) for mesh in meshes]
    sc.doc.Views.Redraw()
            
    
randomFloor()

hi @Gijs,

Yes, that exactly what I meant: the area controlled by a surface ! I know this would require a different aproach…

But also, it wood be a great workaround the definition of complex wood material in Vray. It would allows us to compose whatever wood floor / cladding / tiles, simply using a basic wood texture. If we manage to make this work, I could make a tutorial after to explain simply how it works :slight_smile:

-CC