grow.py :  » Game-2D-3D » CGKit » cgkit-2.0.0alpha9 » utilities » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Game 2D 3D » CGKit 
CGKit » cgkit 2.0.0alpha9 » utilities » grow.py
#!/usr/bin/env python
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Python Computer Graphics Kit.
#
# The Initial Developer of the Original Code is Matthias Baas.
# Portions created by the Initial Developer are Copyright (C) 2004
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****

######################################################################
# grow v1.0.1
#
# "Grow" objects on the surface of another object.
# This tool generates uniformly distributed points on the surface
# of a mesh and calls a user defined function for each such point.
#
# http://cgkit.sourceforge.net
######################################################################

"""
This tool generates uniformly distributed point locations on the surface
of one or more objects and calls a user defined function for each such
point.

Usage: grow.py [Options] <Inputfiles>

The script does the following steps:

1) Load all input files in the given order (you may use any 3D file format
   that cgkit can read).
2) Initialize the RenderMan API using the "RIBname" parameter.
3) Load the geometry procedure file. The name of this file is determined by
   the "proc" parameter.
4) Generate the surface points on all objects that match the string
   "inputpattern" and call "proc" for each point. The number of points
   is specified with the "numpoints" parameter.

Parameters and their default values (the parameters are stored in the
cgkit Globals section):

Globals(

    # Name of the placement procedure to use. The procedure must be a
    # function with the specified name that is defined in a file with
    # the same name plus suffix ".py". The name may contain path
    # information if the procedure file is not in the current directory.
    # (Command line option: -p/--proc)
    
    proc = "grassproc",

    # Only objects whose name matches this pattern will receive surface points.
    # By default, all objects are considered.
    # (Command line option: -i/--inputpattern)
    
    inputpattern = "*",

    # The total number of points to generate.
    # (Command line option: -n/--numpoints)
    
    numpoints = 1000,

    # RIB output file name. If none is given, the output is written to stdout.
    # (Command line option: -r/--rib)
    
    RIBname = None,

    # Declare the result values of the placement procedure (only required when
    # the procedure wants the main program to call RiCurve()).
    # If the placement procedure returns None, then the procedure takes care
    # for the RIB output itself.
    
    resultdesc = ["P"]
    
)
"""

import sys, os.path, copy, time, optparse, fnmatch, random
import profile
from cgkit.all import *
from cgkit.ri import *

# VaryingVar
class VaryingVar:
    """Access a varying primitive variable.
    """
    
    def __init__(self, name, type, mult=1):
        """Constructor.

        name is the name of the primitive variable, type its type
        (FLOAT, NORMAL, ...) and mult its multiplicity.
        """

        self.name = name
        self.type = type
        self.mult = mult
        self.obj = None
        # The corresponding slot (this is already type checked)
        self.slot = None

        # The current three values
        self.values = None

    # init1
    def init1(self, obj):
        """Initialization part I.

        obj is the WorldObject whose variable should be accessed
        (actually the geom's variable).
        This method might change the geom and convert the variable
        to varying.
        """

        self.slot = None
        self.obj = None

        info = obj.geom.findVariable(self.name)
        # Var not found?
        if info==None:
            return False

        # Check the type
        if info[2]!=self.type:
            print >>sys.stderr, 'Variable "%s": Invalid type'%self.name
            return False

        # Check the multiplicity
        if info[3]!=self.mult:
            print >>sys.stderr, 'Variable "%s": Invalid multiplicty (required %d, got %d)'%(self.name, self.mult, info[3])
            return False

        # Is the variable stored as var/varfaces? Then convert to varying...
        finfo = obj.geom.findVariable("%sfaces"%self.name)
        if finfo!=None:
            obj.geom = convMeshAttr(obj.geom, self.name)
            # Update the var info...
            info = obj.geom.findVariable(self.name)

        # Facevarying? then convert to varying
        if info[1]==FACEVARYING:
            obj.geom = convFaceVarToVar(obj.geom)
            # Update the var info...
            info = obj.geom.findVariable(self.name)

        # Is it really varying?
        if info[1]!=VARYING:
            print >>sys.stderr, 'Variable "%s": Invalid storage class'%self.name
            return False

        self.obj = obj
        return True

    # init2
    def init2(self):
        """Initialization part II.

        Retrieve the slot that holds the variable values. Once this
        method is called, no init1() method may be called anymore
        (no matter if it's this or another variable) because this
        could change the geom so that the obtained slot is no longer valid.
        """
        if self.obj!=None:
            # Store the slot
            self.slot = self.obj.geom.slot(self.name)

    # retrieveValues
    def retrieveValues(self, i, j, k):
        """Get the three values at index i, j, k.
        """
        slot = self.slot
        self.values = (slot[i], slot[j], slot[k])

    # interpolateValue
    def interpolateValue(self, u, v, w):
        """
        """
        v1,v2,v3 = self.values
        return w*v1 + u*v2 + v*v3
        

# SurfacePointGenerator
class SurfacePointGenerator:
    """Generate random points on a TriMesh.

    The points are uniformly distributed over the entire mesh.

    Usage:

    srf = SurfacePointGenerator(obj, num)
    for t in srf.triangles():
        for P,N,s,t in srf.surfacePoints(t):
            ...do something with P and N, for example:...
            drawMarker(P, size=0.01)
            drawLine(P, P+0.1*N)
            ...this is called num times...
    
    """
    
    def __init__(self, obj):
        """Constructor.
        
        obj: A list of TriMeshes
        """
        
        self.obj = worldObject(obj)
        self._area = 0
        self._triangles = []
        
        self._preprocess()

    # getArea
    def getArea(self):
        """Return the surface area of the object."""
        return self._area

    # triangles
    def triangles(self, num):
        """Iterate over all triangles.

        This method yields a tuple that describes one triangle.
        This tuple can be passed to surfacePoints() to iterate over
        a random number of points on that surface.

        num: The total number of surface points to be generated
        """
        A = self._area
        facecount = 0
        # "Error" made in assigning points to triangles (if one triangle
        # didn't receive any points, the chances for the next triangle
        # will be increased. This prevents clustering at the end of the
        # triangle list when only a few total points should be created)
        err = 0.0
        for a,b,c,Na,Nb,Nc,sta,stb,stc,Af in self._triangles:
            facecount+=1
#            print >>sys.stderr, "\015%d/%d"%(facecount, len(obj.faces)),
        
            # Calculate the number of points n for this triangle...
            n = int(round(num*Af/A + err))
            err += (num*Af/A)-n
            A -= Af
            num -= n

            if n==0:
                continue

            yield facecount,n,a,b,c,Na,Nb,Nc,sta,stb,stc

    # surfacePoints
    def surfacePoints(self, trispec):
        """Iterate over a random number of points on a triangle.

        Yields a tuple (P, N) where P is the position on the surface
        and N the (interpolated) surface normal.
        """
        facecount,n,a,b,c,Na,Nb,Nc,sta,stb,stc = trispec
        sa,ta = sta
        sb,tb = stb
        sc,tc = stc

        rnd = random.random
        ab = b-a
        ac = c-a
        for i in range(n):
            # Select a random position on the triangle
            while 1:
                u = rnd()
                v = rnd()
                if u+v<1.0:
                    break
            w = 1.0-u-v

            # P: Grass blade position   N: Interpolated normal
            P = a + u*ab + v*ac
            N = (w*Na + u*Nb + v*Nc).normalize()
            s = w*sa + u*sb + v*sc
            t = w*ta + u*tb + v*tc
            yield P,N,s,t
        

    # _preprocess
    def _preprocess(self):
        """Preprocessing the object.

        Computes self._triangles, self._area
        """

        varN = VaryingVar("N", NORMAL)
        varN.init1(self.obj)
                 
        varst = VaryingVar("st", FLOAT, 2)
        varst.init1(self.obj)

        varN.init2()
        varst.init2()
        N_slot = varN.slot
        st_slot = varst.slot

        # Extract triangles and determine surface area A of obj    
        triangles = []
        A = 0.0
        obj = self.obj
        L = obj.worldtransform
        L3 = L.getMat3()
        sta = [0.0, 0.0]
        stb = [0.0, 0.0]
        stc = [0.0, 0.0]
        for i,j,k in obj.faces:
            # Get vertices
            a = L*obj.verts[i]
            b = L*obj.verts[j]
            c = L*obj.verts[k]
            # Get the normals
            if N_slot!=None:
                Na = (L3*N_slot[i]).normalize()
                Nb = (L3*N_slot[j]).normalize()
                Nc = (L3*N_slot[k]).normalize()
            else:
                ab = b-a
                ac = c-a
                Na = (ab.cross(ac)).normalize()
                Nb = Na
                Nc = Na
            # Get the tex coords
            if st_slot!=None:
                sta = st_slot[i]
                stb = st_slot[j]
                stc = st_slot[k]
            # Surface area of this triangle
            Af = 0.5*((b-a).cross(c-a)).length()
            # Add to total surface area
            A += Af
            # Store vertices and triangle surface area for later use
            triangles.append((a,b,c, Na,Nb,Nc, sta,stb,stc, Af))

        self._triangles = triangles
        self._area = A
        print >>sys.stderr, "Surface area:",A
        
######################################################################  

# time2str
def time2str(t):
    """Convert a time value (in seconds) into a string.
    """
    if t<60:
        return "%1.0fs"%t
    else:
        min = int(t/60)
        sec = int(t%60)
        if min<60:
            return "%dmin %ds"%(min,sec)
        else:
            h = int(min/60)
            min = int(min%60)
            return "%dh %dmin %ds"%(h,min,sec)

# grow
def grow(objs, proc, num=10, resultdesc=["P"]):
    """Grow objects on the given object (TriMesh).
    
    """

    srfs = []
    totalarea = 0.0
    for obj in objs:
        print >>sys.stderr, 'Preprocessing object "%s"...'%obj.name
        srf = SurfacePointGenerator(obj)
        srfs.append(srf)
        totalarea += srf.getArea()

    print >>sys.stderr, "Total area:",totalarea

    # Determine the number of points for each object...
    nums = []
    err = 0.0
    for srf in srfs:
        Aobj = srf.getArea()
        # Calculate the number of points n for this object...
        n = int(round(num*Aobj/totalarea + err))
        nums.append(n)
        err += (num*Aobj/totalarea)-n
        totalarea -= Aobj
        num -= n
        
    total_n = 0
    for obj,srf,num in zip(objs, srfs, nums):
        print >>sys.stderr, 'Generating %d points on "%s"...'%(num,obj.name)
        for tri in srf.triangles(num):
            n = tri[1]
            total_n += n
            print >>sys.stderr, "\015%d..."%total_n,

            # Create a list with len(resultdesc) lists (one list per variable)
            valuelsts = []
            for i in range(len(resultdesc)):
                valuelsts.append([])

            # Iterate over the surface points...
            for P,N,s,t in srf.surfacePoints(tri):
                F = mat4(1).lookAt(P, P+N, up=vec3(0,0,1))
                res = proc(P=P, N=N, F=F, s=s, t=t)
                if res!=None:
                    for Lst, lst in zip(valuelsts, res):
                        Lst += lst

            if res!=None:
                params = {}
                for name, values in zip(resultdesc, valuelsts):
                    params[name] = values
                RiCurves(RI_CUBIC, n*[6], RI_NONPERIODIC, params)
                
        print >>sys.stderr, ""


######################################################################

# Default values
Globals(
    proc = "grassproc",
    inputpattern = "*",
    numpoints = 1000,
    RIBname = None,
    resultdesc = ["P"]
)

# Parse the command line
usage = "usage: %prog [options] inputfiles"
parser = optparse.OptionParser(usage)
parser.add_option("-p", "--proc", metavar="NAME", default=None,
                  help="Select the procedure")
parser.add_option("-n", "--numpoints", type="int", default=None,
                  help="Number of points to generate")
parser.add_option("-r", "--rib", metavar="NAME", default=None,
                  help="Output RIB name")
parser.add_option("-i", "--inputpattern", metavar="PATTERN", default=None,
                  help="Input pattern used for matching object names")
opts, args = parser.parse_args()

# No scene files? then exit
if len(args)==0:
    parser.print_help()
    sys.exit(0)

scene = getScene()

# Load the geometry...
for filename in args:
    print >>sys.stderr, 'Loading "%s"...'%filename
    load(filename)

# Initialize ri
RIBname = scene.getGlobal("RIBname")
if opts.rib!=None:
    RIBname = opts.rib

RiBegin(RIBname)
RiOption(RI_RIBOUTPUT, RI_VERSION, 0)

# Load the procedure file
procname = scene.getGlobal("proc")
if opts.proc!=None:
    procname = opts.proc
procfilename = "%s.py"%procname
print >>sys.stderr, 'Loading procedure file "%s"...'%procfilename
execfile(procfilename)

# Get a reference to the procedure
procname = os.path.basename(procname)
proc = eval("%s"%procname)

inputpattern = scene.getGlobal("inputpattern")
if opts.inputpattern!=None:
    inputpattern = opts.inputpattern
print >>sys.stderr, 'Input pattern: "%s"'%inputpattern

# Collect all geometry (convert to TriMesh if possible)...
meshes = []
for obj in scene.walkWorld():
    if obj.geom==None:
        continue
    if not fnmatch.fnmatch(obj.name, inputpattern):
        continue
    if not isinstance(obj.geom, TriMeshGeom):
        try:
            convertToTriMesh(obj)
#        except NotImplementedError:
        except:
            continue
    meshes.append(obj)

# No meshes? then exit
if len(meshes)==0:
    print >>sys.stderr, "No meshes available"
    sys.exit(1)

# Generate the surface points...

numpoints = scene.getGlobal("numpoints")
if opts.numpoints!=None:
    numpoints = opts.numpoints

desc = scene.getGlobal("resultdesc")

print >>sys.stderr, 'Output RIB file: "%s"'%RIBname
print >>sys.stderr, '#Surface points:',numpoints

t1 = time.time()
grow(objs=meshes, proc=proc, num=numpoints, resultdesc=desc)
#profile.run("grow(objs=meshes, proc=proc, num=numpoints, resultdesc=desc)", "profile.log")
t2 = time.time()
dt = t2-t1
print >>sys.stderr, "Generation time: %s"%time2str(dt)

RiEnd()



    
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.