Skip to content
Snippets Groups Projects
import_x3d.py 86.9 KiB
Newer Older
  • Learn to ignore specific revisions
  •                                     child.parent = None
    
                                        extern_child = child.findSpecRecursive(extern_key)
    
                                        if extern_child:
                                            self.children.append(extern_child)
                                            extern_child.parent = self
    
                                            if DEBUG:
                                                print("\tEXTERNPROTO ID found!:", extern_key)
                                        else:
                                            print("\tEXTERNPROTO ID not found!:", extern_key)
    
                                # Watch it! - restore lines
                                lines[:] = lines_old
    
            return new_i
    
        def __parse(self, i, IS_PROTO_DATA=False):
            '''
            print('parsing at', i, end="")
            print(i, self.id, self.lineno)
            '''
            l = lines[i]
    
            if l == '[':
                # An anonymous list
                self.id = None
                i += 1
            else:
                words = []
    
                node_type, new_i = is_nodeline(i, words)
                if not node_type:  # fail for parsing new node.
                    print("Failed to parse new node")
                    raise ValueError
    
                if self.node_type == NODE_REFERENCE:
                    # Only assign the reference and quit
                    key = words[words.index('USE') + 1]
                    self.id = (words[0],)
    
                    self.reference = self.getDefDict()[key]
                    return new_i
    
                self.id = tuple(words)
    
                # fill in DEF/USE
                key = self.getDefName()
                if key != None:
                    self.getDefDict()[key] = self
    
                key = self.getProtoName()
                if not key:
                    key = self.getExternprotoName()
    
                proto_dict = self.getProtoDict()
                if key != None:
                    proto_dict[key] = self
    
                    # Parse the proto nodes fields
                    self.proto_node = vrmlNode(self, NODE_ARRAY, new_i)
                    new_i = self.proto_node.parse(new_i)
    
                    self.children.remove(self.proto_node)
    
                    # print(self.proto_node)
    
                    new_i += 1  # skip past the {
    
                else:  # If we're a proto instance, add the proto node as our child.
                    spec = self.getSpec()
                    try:
                        self.children.append(proto_dict[spec])
                        #pass
                    except:
                        pass
    
                    del spec
    
                del proto_dict, key
    
                i = new_i
    
            # print(self.id)
            ok = True
            while ok:
                if i >= len(lines):
                    return len(lines) - 1
    
                l = lines[i]
                # print('\tDEBUG:', i, self.node_type, l)
                if l == '':
                    i += 1
                    continue
    
                if l == '}':
                    if self.node_type != NODE_NORMAL:  # also ends proto nodes, we may want a type for these too.
                        print('wrong node ending, expected an } ' + str(i) + ' ' + str(self.node_type))
                        if DEBUG:
                            raise ValueError
                    ### print("returning", i)
                    return i + 1
                if l == ']':
                    if self.node_type != NODE_ARRAY:
                        print('wrong node ending, expected a ] ' + str(i) + ' ' + str(self.node_type))
                        if DEBUG:
                            raise ValueError
                    ### print("returning", i)
                    return i + 1
    
                node_type, new_i = is_nodeline(i, [])
                if node_type:  # check text\n{
                    child = vrmlNode(self, node_type, i)
                    i = child.parse(i)
    
                elif l == '[':  # some files have these anonymous lists
                    child = vrmlNode(self, NODE_ARRAY, i)
                    i = child.parse(i)
    
                elif is_numline(i):
                    l_split = l.split(',')
    
                    values = None
                    # See if each item is a float?
    
                    for num_type in (int, float):
                        try:
                            values = [num_type(v) for v in l_split]
                            break
                        except:
                            pass
    
                        try:
                            values = [[num_type(v) for v in segment.split()] for segment in l_split]
                            break
                        except:
                            pass
    
    
                    if values is None:  # dont parse
    
                        values = l_split
    
                    # This should not extend over multiple lines however it is possible
                    # print(self.array_data)
                    if values:
                        self.array_data.extend(values)
                    i += 1
                else:
                    words = l.split()
                    if len(words) > 2 and words[1] == 'USE':
                        vrmlNode(self, NODE_REFERENCE, i)
                    else:
    
                        # print("FIELD", i, l)
                        #
                        #words = l.split()
                        ### print('\t\ttag', i)
                        # this is a tag/
                        # print(words, i, l)
                        value = l
                        # print(i)
                        # javastrips can exist as values.
                        quote_count = l.count('"')
                        if quote_count % 2:  # odd number?
                            # print('MULTILINE')
                            while 1:
                                i += 1
                                l = lines[i]
                                quote_count = l.count('"')
                                if quote_count % 2:  # odd number?
                                    value += '\n' + l[:l.rfind('"')]
                                    break  # assume
                                else:
                                    value += '\n' + l
    
                        value_all = value.split()
    
                        def iskey(k):
    
                            if k[0] != '"' and k[0].isalpha() and k.upper() not in {'TRUE', 'FALSE'}:
    
                                return True
                            return False
    
                        def split_fields(value):
                            '''
                            key 0.0 otherkey 1,2,3 opt1 opt1 0.0
                                -> [key 0.0], [otherkey 1,2,3], [opt1 opt1 0.0]
                            '''
                            field_list = []
                            field_context = []
    
                            for j in range(len(value)):
                                if iskey(value[j]):
                                    if field_context:
                                        # this IS a key but the previous value was not a key, ot it was a defined field.
                                        if (not iskey(field_context[-1])) or ((len(field_context) == 3 and field_context[1] == 'IS')):
                                            field_list.append(field_context)
    
                                            field_context = [value[j]]
                                        else:
                                            # The last item was not a value, multiple keys are needed in some cases.
                                            field_context.append(value[j])
                                    else:
                                        # Is empty, just add this on
                                        field_context.append(value[j])
                                else:
                                    # Add a value to the list
                                    field_context.append(value[j])
    
                            if field_context:
                                field_list.append(field_context)
    
                            return field_list
    
                        for value in split_fields(value_all):
                            # Split
    
                            if value[0] == 'field':
                                # field SFFloat creaseAngle 4
                                self.proto_field_defs.append(value)
                            else:
                                self.fields.append(value)
                    i += 1
    
    
    def gzipOpen(path):
    
    Campbell Barton's avatar
    Campbell Barton committed
        import gzip
    
    Campbell Barton's avatar
    Campbell Barton committed
        try:
            data = gzip.open(path, 'r').read()
        except:
            pass
    
                filehandle = open(path, 'rU')
                data = filehandle.read()
                filehandle.close()
    
            except:
                pass
    
        return data
    
    
    def vrml_parse(path):
        '''
        Sets up the root node and returns it so load_web3d() can deal with the blender side of things.
        Return root (vrmlNode, '') or (None, 'Error String')
        '''
        data = gzipOpen(path)
    
    
            return None, 'Failed to open file: ' + path
    
        # Stripped above
        lines[:] = vrmlFormat(data)
    
        lines.insert(0, '{')
        lines.insert(0, 'dymmy_node')
        lines.append('}')
        # Use for testing our parsed output, so we can check on line numbers.
    
        '''
        ff = open('/tmp/test.txt', 'w')
        ff.writelines([l+'\n' for l in lines])
        ff.close()
        '''
    
        # Now evaluate it
        node_type, new_i = is_nodeline(0, [])
        if not node_type:
            return None, 'Error: VRML file has no starting Node'
    
        # Trick to make sure we get all root nodes.
        lines.insert(0, '{')
        lines.insert(0, 'root_node____')  # important the name starts with an ascii char
        lines.append('}')
    
        root = vrmlNode(None, NODE_NORMAL, -1)
        root.setRoot(path)  # we need to set the root so we have a namespace and know the path incase of inlineing
    
        # Parse recursively
        root.parse(0)
    
        # This prints a load of text
        if DEBUG:
            print(root)
    
        return root, ''
    
    
    # ====================== END VRML
    
    # ====================== X3d Support
    
    # Sane as vrml but replace the parser
    class x3dNode(vrmlNode):
        def __init__(self, parent, node_type, x3dNode):
            vrmlNode.__init__(self, parent, node_type, -1)
            self.x3dNode = x3dNode
    
        def parse(self, IS_PROTO_DATA=False):
            # print(self.x3dNode.tagName)
    
            define = self.x3dNode.getAttributeNode('DEF')
            if define:
                self.getDefDict()[define.value] = self
            else:
                use = self.x3dNode.getAttributeNode('USE')
                if use:
                    try:
                        self.reference = self.getDefDict()[use.value]
                        self.node_type = NODE_REFERENCE
                    except:
                        print('\tWarning: reference', use.value, 'not found')
                        self.parent.children.remove(self)
    
                    return
    
            for x3dChildNode in self.x3dNode.childNodes:
    
                if x3dChildNode.nodeType in {x3dChildNode.TEXT_NODE, x3dChildNode.COMMENT_NODE, x3dChildNode.CDATA_SECTION_NODE}:
    
                    continue
    
                node_type = NODE_NORMAL
                # print(x3dChildNode, dir(x3dChildNode))
                if x3dChildNode.getAttributeNode('USE'):
                    node_type = NODE_REFERENCE
    
                child = x3dNode(self, node_type, x3dChildNode)
                child.parse()
    
            # TODO - x3d Inline
    
        def getSpec(self):
            return self.x3dNode.tagName  # should match vrml spec
    
        def getDefName(self):
            data = self.x3dNode.getAttributeNode('DEF')
            if data:
                data.value  # XXX, return??
            return None
    
        # Other funcs operate from vrml, but this means we can wrap XML fields, still use nice utility funcs
        # getFieldAsArray getFieldAsBool etc
        def getFieldName(self, field, ancestry, AS_CHILD=False):
            # ancestry and AS_CHILD are ignored, only used for VRML now
    
            self_real = self.getRealNode()  # incase we're an instance
            field_xml = self.x3dNode.getAttributeNode(field)
            if field_xml:
                value = field_xml.value
    
                # We may want to edit. for x3d spesific stuff
                # Sucks a bit to return the field name in the list but vrml excepts this :/
                return value.split()
            else:
                return None
    
    
    def x3d_parse(path):
        '''
        Sets up the root node and returns it so load_web3d() can deal with the blender side of things.
        Return root (x3dNode, '') or (None, 'Error String')
        '''
    
        try:
            import xml.dom.minidom
        except:
            return None, 'Error, import XML parsing module (xml.dom.minidom) failed, install python'
    
        '''
        try:    doc = xml.dom.minidom.parse(path)
        except: return None, 'Could not parse this X3D file, XML error'
        '''
    
        # Could add a try/except here, but a console error is more useful.
        data = gzipOpen(path)
    
    
            return None, 'Failed to open file: ' + path
    
        doc = xml.dom.minidom.parseString(data)
    
        try:
            x3dnode = doc.getElementsByTagName('X3D')[0]
        except:
            return None, 'Not a valid x3d document, cannot import'
    
        root = x3dNode(None, NODE_NORMAL, x3dnode)
        root.setRoot(path)  # so images and Inline's we load have a relative path
        root.parse()
    
        return root, ''
    
    ## f = open('/_Cylinder.wrl', 'r')
    # f = open('/fe/wrl/Vrml/EGS/TOUCHSN.WRL', 'r')
    # vrml_parse('/fe/wrl/Vrml/EGS/TOUCHSN.WRL')
    #vrml_parse('/fe/wrl/Vrml/EGS/SCRIPT.WRL')
    '''
    import os
    files = os.popen('find /fe/wrl -iname "*.wrl"').readlines()
    files.sort()
    tot = len(files)
    for i, f in enumerate(files):
        #if i < 801:
        #   continue
    
        f = f.strip()
        print(f, i, tot)
        vrml_parse(f)
    '''
    
    # NO BLENDER CODE ABOVE THIS LINE.
    # -----------------------------------------------------------------------------------
    import bpy
    
    # import BPyImage
    # import BPySys
    # reload(BPySys)
    # reload(BPyImage)
    # import Blender
    # from Blender import Texture, Material, Mathutils, Mesh, Types, Window
    from mathutils import Vector, Matrix
    
    RAD_TO_DEG = 57.29578
    
    GLOBALS = {'CIRCLE_DETAIL': 16}
    
    
    def translateRotation(rot):
        ''' axis, angle '''
        return Matrix.Rotation(rot[3], 4, Vector(rot[:3]))
    
    
    def translateScale(sca):
        mat = Matrix()  # 4x4 default
        mat[0][0] = sca[0]
        mat[1][1] = sca[1]
        mat[2][2] = sca[2]
        return mat
    
    
    def translateTransform(node, ancestry):
        cent = node.getFieldAsFloatTuple('center', None, ancestry)  # (0.0, 0.0, 0.0)
        rot = node.getFieldAsFloatTuple('rotation', None, ancestry)  # (0.0, 0.0, 1.0, 0.0)
        sca = node.getFieldAsFloatTuple('scale', None, ancestry)  # (1.0, 1.0, 1.0)
        scaori = node.getFieldAsFloatTuple('scaleOrientation', None, ancestry)  # (0.0, 0.0, 1.0, 0.0)
        tx = node.getFieldAsFloatTuple('translation', None, ancestry)  # (0.0, 0.0, 0.0)
    
        if cent:
    
            cent_mat = Matrix.Translation(cent)
            cent_imat = cent_mat.inverted()
    
        else:
            cent_mat = cent_imat = None
    
        if rot:
            rot_mat = translateRotation(rot)
        else:
            rot_mat = None
    
        if sca:
            sca_mat = translateScale(sca)
        else:
            sca_mat = None
    
        if scaori:
            scaori_mat = translateRotation(scaori)
    
            scaori_imat = scaori_mat.inverted()
    
            tx_mat = Matrix.Translation(tx)
    
        else:
            tx_mat = None
    
        new_mat = Matrix()
    
        mats = [tx_mat, cent_mat, rot_mat, scaori_mat, sca_mat, scaori_imat, cent_imat]
        for mtx in mats:
            if mtx:
                new_mat = new_mat * mtx
    
        return new_mat
    
    
    def translateTexTransform(node, ancestry):
        cent = node.getFieldAsFloatTuple('center', None, ancestry)  # (0.0, 0.0)
        rot = node.getFieldAsFloat('rotation', None, ancestry)  # 0.0
        sca = node.getFieldAsFloatTuple('scale', None, ancestry)  # (1.0, 1.0)
        tx = node.getFieldAsFloatTuple('translation', None, ancestry)  # (0.0, 0.0)
    
        if cent:
            # cent is at a corner by default
    
            cent_mat = Matrix.Translation(Vector(cent).to_3d())
    
            cent_imat = cent_mat.inverted()
    
        else:
            cent_mat = cent_imat = None
    
        if rot:
            rot_mat = Matrix.Rotation(rot, 4, 'Z')  # translateRotation(rot)
        else:
            rot_mat = None
    
        if sca:
            sca_mat = translateScale((sca[0], sca[1], 0.0))
        else:
            sca_mat = None
    
        if tx:
    
            tx_mat = Matrix.Translation(Vector(tx).to_3d())
    
        else:
            tx_mat = None
    
        new_mat = Matrix()
    
        # as specified in VRML97 docs
        mats = [cent_imat, sca_mat, rot_mat, cent_mat, tx_mat]
    
        for mtx in mats:
            if mtx:
                new_mat = new_mat * mtx
    
        return new_mat
    
    
    # 90d X rotation
    import math
    MATRIX_Z_TO_Y = Matrix.Rotation(math.pi / 2.0, 4, 'X')
    
    
    
    def getFinalMatrix(node, mtx, ancestry, global_matrix):
    
    
        transform_nodes = [node_tx for node_tx in ancestry if node_tx.getSpec() == 'Transform']
        if node.getSpec() == 'Transform':
            transform_nodes.append(node)
        transform_nodes.reverse()
    
        if mtx is None:
            mtx = Matrix()
    
        for node_tx in transform_nodes:
            mat = translateTransform(node_tx, ancestry)
            mtx = mat * mtx
    
        # worldspace matrix
    
    
        return mtx
    
    
    def importMesh_IndexedFaceSet(geom, bpyima, ancestry):
        # print(geom.lineno, geom.id, vrmlNode.DEF_NAMESPACE.keys())
    
        ccw = geom.getFieldAsBool('ccw', True, ancestry)
        ifs_colorPerVertex = geom.getFieldAsBool('colorPerVertex', True, ancestry)  # per vertex or per face
        ifs_normalPerVertex = geom.getFieldAsBool('normalPerVertex', True, ancestry)
    
        # This is odd how point is inside Coordinate
    
        # VRML not x3d
        #coord = geom.getChildByName('coord') # 'Coordinate'
    
        coord = geom.getChildBySpec('Coordinate')  # works for x3d and vrml
    
        if coord:
            ifs_points = coord.getFieldAsArray('point', 3, ancestry)
        else:
            coord = []
    
        if not coord:
            print('\tWarnint: IndexedFaceSet has no points')
            return None, ccw
    
        ifs_faces = geom.getFieldAsArray('coordIndex', 0, ancestry)
    
        coords_tex = None
        if ifs_faces:  # In rare cases this causes problems - no faces but UVs???
    
            # WORKS - VRML ONLY
            # coords_tex = geom.getChildByName('texCoord')
            coords_tex = geom.getChildBySpec('TextureCoordinate')
    
            if coords_tex:
                ifs_texpoints = coords_tex.getFieldAsArray('point', 2, ancestry)
                ifs_texfaces = geom.getFieldAsArray('texCoordIndex', 0, ancestry)
    
                if not ifs_texpoints:
                    # IF we have no coords, then dont bother
                    coords_tex = None
    
        # WORKS - VRML ONLY
        # vcolor = geom.getChildByName('color')
        vcolor = geom.getChildBySpec('Color')
        vcolor_spot = None  # spot color when we dont have an array of colors
        if vcolor:
            # float to char
            ifs_vcol = [(0, 0, 0)]  # EEKADOODLE - vertex start at 1
            ifs_vcol.extend([col for col in vcolor.getFieldAsArray('color', 3, ancestry)])
            ifs_color_index = geom.getFieldAsArray('colorIndex', 0, ancestry)
    
            if not ifs_vcol:
                vcolor_spot = vcolor.getFieldAsFloatTuple('color', [], ancestry)
    
        # Convert faces into somthing blender can use
        edges = []
    
        # All lists are aligned!
        faces = []
        faces_uv = []  # if ifs_texfaces is empty then the faces_uv will match faces exactly.
        faces_orig_index = []  # for ngons, we need to know our original index
    
        if coords_tex and ifs_texfaces:
            do_uvmap = True
        else:
            do_uvmap = False
    
        # current_face = [0] # pointer anyone
    
        def add_face(face, fuvs, orig_index):
            l = len(face)
            if l == 3 or l == 4:
                faces.append(face)
                # faces_orig_index.append(current_face[0])
                if do_uvmap:
                    faces_uv.append(fuvs)
    
                faces_orig_index.append(orig_index)
            elif l == 2:
                edges.append(face)
            elif l > 4:
                for i in range(2, len(face)):
                    faces.append([face[0], face[i - 1], face[i]])
                    if do_uvmap:
                        faces_uv.append([fuvs[0], fuvs[i - 1], fuvs[i]])
                    faces_orig_index.append(orig_index)
            else:
                # faces with 1 verts? pfft!
                # still will affect index ordering
                pass
    
        face = []
        fuvs = []
        orig_index = 0
        for i, fi in enumerate(ifs_faces):
            # ifs_texfaces and ifs_faces should be aligned
            if fi != -1:
                # face.append(int(fi)) # in rare cases this is a float
                # EEKADOODLE!!!
                # Annoyance where faces that have a zero index vert get rotated. This will then mess up UVs and VColors
                face.append(int(fi) + 1)  # in rare cases this is a float, +1 because of stupid EEKADOODLE :/
    
                if do_uvmap:
                    if i >= len(ifs_texfaces):
                        print('\tWarning: UV Texface index out of range')
                        fuvs.append(ifs_texfaces[0])
                    else:
                        fuvs.append(ifs_texfaces[i])
            else:
                add_face(face, fuvs, orig_index)
                face = []
                if do_uvmap:
                    fuvs = []
                orig_index += 1
    
        add_face(face, fuvs, orig_index)
        del add_face  # dont need this func anymore
    
        bpymesh = bpy.data.meshes.new(name="XXX")
    
        # EEKADOODLE
        bpymesh.vertices.add(1 + (len(ifs_points)))
        bpymesh.vertices.foreach_set("co", [0, 0, 0] + [a for v in ifs_points for a in v])  # XXX25 speed
    
        # print(len(ifs_points), faces, edges, ngons)
    
        try:
            bpymesh.faces.add(len(faces))
            bpymesh.faces.foreach_set("vertices_raw", [a for f in faces for a in (f + [0] if len(f) == 3 else f)])  # XXX25 speed
        except KeyError:
    
            print("one or more vert indices out of range. corrupt file?")
    
            #for f in faces:
            #   bpymesh.faces.extend(faces, smooth=True)
    
    
    
        if len(bpymesh.faces) != len(faces):
            print('\tWarning: adding faces did not work! file is invalid, not adding UVs or vcolors')
            return bpymesh, ccw
    
        # Apply UVs if we have them
        if not do_uvmap:
            faces_uv = faces  # fallback, we didnt need a uvmap in the first place, fallback to the face/vert mapping.
        if coords_tex:
            #print(ifs_texpoints)
            # print(geom)
            uvlay = bpymesh.uv_textures.new()
    
            for i, f in enumerate(uvlay.data):
                f.image = bpyima
    
                fuv = faces_uv[i]  # uv indices
    
                for j, uv in enumerate(f.uv):
                    # print(fuv, j, len(ifs_texpoints))
                    try:
                        f.uv[j] = ifs_texpoints[fuv[j]]  # XXX25, speedup
                    except:
                        print('\tWarning: UV Index out of range')
                        f.uv[j] = ifs_texpoints[0]  # XXX25, speedup
    
        elif bpyima and len(bpymesh.faces):
            # Oh Bugger! - we cant really use blenders ORCO for for texture space since texspace dosnt rotate.
            # we have to create VRML's coords as UVs instead.
    
            # VRML docs
            '''
            If the texCoord field is NULL, a default texture coordinate mapping is calculated using the local
            coordinate system bounding box of the shape. The longest dimension of the bounding box defines the S coordinates,
            and the next longest defines the T coordinates. If two or all three dimensions of the bounding box are equal,
            ties shall be broken by choosing the X, Y, or Z dimension in that order of preference.
            The value of the S coordinate ranges from 0 to 1, from one end of the bounding box to the other.
            The T coordinate ranges between 0 and the ratio of the second greatest dimension of the bounding box to the greatest dimension.
            '''
    
            # Note, S,T == U,V
            # U gets longest, V gets second longest
            xmin, ymin, zmin = ifs_points[0]
            xmax, ymax, zmax = ifs_points[0]
            for co in ifs_points:
                x, y, z = co
                if x < xmin:
                    xmin = x
                if y < ymin:
                    ymin = y
                if z < zmin:
                    zmin = z
    
                if x > xmax:
                    xmax = x
                if y > ymax:
                    ymax = y
                if z > zmax:
                    zmax = z
    
            xlen = xmax - xmin
            ylen = ymax - ymin
            zlen = zmax - zmin
    
            depth_min = xmin, ymin, zmin
            depth_list = [xlen, ylen, zlen]
            depth_sort = depth_list[:]
            depth_sort.sort()
    
            depth_idx = [depth_list.index(val) for val in depth_sort]
    
            axis_u = depth_idx[-1]
            axis_v = depth_idx[-2]  # second longest
    
            # Hack, swap these !!! TODO - Why swap??? - it seems to work correctly but should not.
            # axis_u,axis_v = axis_v,axis_u
    
            min_u = depth_min[axis_u]
            min_v = depth_min[axis_v]
            depth_u = depth_list[axis_u]
            depth_v = depth_list[axis_v]
    
            depth_list[axis_u]
    
            if axis_u == axis_v:
                # This should be safe because when 2 axies have the same length, the lower index will be used.
                axis_v += 1
    
            uvlay = bpymesh.uv_textures.new()
    
            # HACK !!! - seems to be compatible with Cosmo though.
            depth_v = depth_u = max(depth_v, depth_u)
    
            bpymesh_vertices = bpymesh.vertices[:]
            bpymesh_faces = bpymesh.faces[:]
    
            for j, f in enumerate(uvlay.data):
                f.image = bpyima
                fuv = f.uv
                f_v = bpymesh_faces[j].vertices[:]  # XXX25 speed
    
                for i, v in enumerate(f_v):
                    co = bpymesh_vertices[v].co
                    fuv[i] = (co[axis_u] - min_u) / depth_u, (co[axis_v] - min_v) / depth_v
    
        # Add vcote
        if vcolor:
            # print(ifs_vcol)
            collay = bpymesh.vertex_colors.new()
    
            for f_idx, f in enumerate(collay.data):
                fv = bpymesh.faces[f_idx].vertices[:]
                if len(fv) == 3:  # XXX speed
                    fcol = f.color1, f.color2, f.color3
                else:
                    fcol = f.color1, f.color2, f.color3, f.color4
                if ifs_colorPerVertex:
                    for i, c in enumerate(fcol):
                        color_index = fv[i]  # color index is vert index
                        if ifs_color_index:
                            try:
                                color_index = ifs_color_index[color_index]
                            except:
                                print('\tWarning: per vertex color index out of range')
                                continue
    
                        if color_index < len(ifs_vcol):
                            c.r, c.g, c.b = ifs_vcol[color_index]
                        else:
                            #print('\tWarning: per face color index out of range')
                            pass
                else:
                    if vcolor_spot:  # use 1 color, when ifs_vcol is []
                        for c in fcol:
                            c.r, c.g, c.b = vcolor_spot
                    else:
                        color_index = faces_orig_index[f_idx]  # color index is face index
                        #print(color_index, ifs_color_index)
                        if ifs_color_index:
                            if color_index >= len(ifs_color_index):
                                print('\tWarning: per face color index out of range')
                                color_index = 0
                            else:
                                color_index = ifs_color_index[color_index]
                        try:
                            col = ifs_vcol[color_index]
                        except IndexError:
                            # TODO, look
                            col = (1.0, 1.0, 1.0)
                        for i, c in enumerate(fcol):
                            c.r, c.g, c.b = col
    
        # XXX25
        # bpymesh.vertices.delete([0, ])  # EEKADOODLE
    
        return bpymesh, ccw
    
    
    def importMesh_IndexedLineSet(geom, ancestry):
        # VRML not x3d
        #coord = geom.getChildByName('coord') # 'Coordinate'
        coord = geom.getChildBySpec('Coordinate')  # works for x3d and vrml
        if coord:
            points = coord.getFieldAsArray('point', 3, ancestry)
        else:
            points = []
    
        if not points:
            print('\tWarning: IndexedLineSet had no points')
            return None
    
        ils_lines = geom.getFieldAsArray('coordIndex', 0, ancestry)
    
        lines = []
        line = []
    
        for il in ils_lines:
            if il == -1:
                lines.append(line)
                line = []
            else:
                line.append(int(il))
        lines.append(line)
    
        # vcolor = geom.getChildByName('color') # blender dosnt have per vertex color
    
        bpycurve = bpy.data.curves.new('IndexedCurve', 'CURVE')
        bpycurve.dimensions = '3D'
    
        for line in lines:
            if not line:
                continue
    
    Campbell Barton's avatar
    Campbell Barton committed
            # co = points[line[0]]  # UNUSED
    
            nu = bpycurve.splines.new('POLY')
    
            nu.points.add(len(line) - 1)  # the new nu has 1 point to begin with
    
            for il, pt in zip(line, nu.points):
                pt.co[0:3] = points[il]
    
        return bpycurve
    
    
    def importMesh_PointSet(geom, ancestry):
        # VRML not x3d
        #coord = geom.getChildByName('coord') # 'Coordinate'
        coord = geom.getChildBySpec('Coordinate')  # works for x3d and vrml
        if coord:
            points = coord.getFieldAsArray('point', 3, ancestry)
        else:
            points = []
    
        # vcolor = geom.getChildByName('color') # blender dosnt have per vertex color
    
        bpymesh = bpy.data.meshes.new("XXX")
        bpymesh.vertices.add(len(points))
        bpymesh.vertices.foreach_set("co", [a for v in points for a in v])
    
    
        return bpymesh
    
    GLOBALS['CIRCLE_DETAIL'] = 12
    
    
    def bpy_ops_add_object_hack():  # XXX25, evil
        scene = bpy.context.scene
        obj = scene.objects[0]
        scene.objects.unlink(obj)
        bpymesh = obj.data
        bpy.data.objects.remove(obj)
        return bpymesh
    
    
    def importMesh_Sphere(geom, ancestry):
        diameter = geom.getFieldAsFloat('radius', 0.5, ancestry)
        # bpymesh = Mesh.Primitives.UVsphere(GLOBALS['CIRCLE_DETAIL'], GLOBALS['CIRCLE_DETAIL'], diameter)
    
        bpy.ops.mesh.primitive_uv_sphere_add(segments=GLOBALS['CIRCLE_DETAIL'],
                                             ring_count=GLOBALS['CIRCLE_DETAIL'],
                                             size=diameter,
                                             view_align=False,
                                             enter_editmode=False,
                                             )
    
        bpymesh = bpy_ops_add_object_hack()
    
        bpymesh.transform(MATRIX_Z_TO_Y)
        return bpymesh
    
    
    def importMesh_Cylinder(geom, ancestry):
        # bpymesh = bpy.data.meshes.new()
        diameter = geom.getFieldAsFloat('radius', 1.0, ancestry)
        height = geom.getFieldAsFloat('height', 2, ancestry)
    
        # bpymesh = Mesh.Primitives.Cylinder(GLOBALS['CIRCLE_DETAIL'], diameter, height)
    
        bpy.ops.mesh.primitive_cylinder_add(vertices=GLOBALS['CIRCLE_DETAIL'],
                                            radius=diameter,
                                            depth=height,
                                            cap_ends=True,
                                            view_align=False,
                                            enter_editmode=False,
                                            )
    
        bpymesh = bpy_ops_add_object_hack()
    
        bpymesh.transform(MATRIX_Z_TO_Y)
    
        # Warning - Rely in the order Blender adds verts
        # not nice design but wont change soon.
    
        bottom = geom.getFieldAsBool('bottom', True, ancestry)
        side = geom.getFieldAsBool('side', True, ancestry)
        top = geom.getFieldAsBool('top', True, ancestry)
    
        if not top:  # last vert is top center of tri fan.
            # bpymesh.vertices.delete([(GLOBALS['CIRCLE_DETAIL'] + GLOBALS['CIRCLE_DETAIL']) + 1])  # XXX25
            pass
    
        if not bottom:  # second last vert is bottom of triangle fan
            # XXX25
            # bpymesh.vertices.delete([GLOBALS['CIRCLE_DETAIL'] + GLOBALS['CIRCLE_DETAIL']])
            pass
    
        if not side:
            # remove all quads
            # XXX25
            # bpymesh.faces.delete(1, [f for f in bpymesh.faces if len(f) == 4])
            pass
    
        return bpymesh
    
    
    def importMesh_Cone(geom, ancestry):
        # bpymesh = bpy.data.meshes.new()
        diameter = geom.getFieldAsFloat('bottomRadius', 1.0, ancestry)
        height = geom.getFieldAsFloat('height', 2, ancestry)
    
        # bpymesh = Mesh.Primitives.Cone(GLOBALS['CIRCLE_DETAIL'], diameter, height)
    
        bpy.ops.mesh.primitive_cone_add(vertices=GLOBALS['CIRCLE_DETAIL'],
                                        radius=diameter,
                                        depth=height,
                                        cap_end=True,
                                        view_align=False,
                                        enter_editmode=False,
                                        )
    
        bpymesh = bpy_ops_add_object_hack()
    
        bpymesh.transform(MATRIX_Z_TO_Y)
    
        # Warning - Rely in the order Blender adds verts
        # not nice design but wont change soon.
    
        bottom = geom.getFieldAsBool('bottom', True, ancestry)