Exemplo n.º 1
0
 def __init__(self):
 
     self.textures={}
     for k in texMap:
         v=texMap[k]
         tex=loader.loadTexture("gfx/%s"%v)
         tex.setWrapU(Texture.WM_clamp)
         tex.setWrapV(Texture.WM_clamp)
         self.textures[k]=tex
     
     self.viewPoint=(0,0,0)
     self.cubeVtxSrc=[
         # 14 vertices per cube mapped
         # so that UV coords utilize
         # the typical cube unwrap:
         #
         #   ####  <-- square texture
         #   ##U#
         #   BLFR
         #   ##D#    #=unused
         #
         # This form uses 14 vertices per cube since
         # the extra (worldspace overlapped) vertices
         # map different U,V coordinates at the same
         # worldspace coordinates, depending on face
         #   
         # x    y    z    u    v
         (1.00,1.00,1.00,0.00,0.50), # A
         (1.00,1.00,0.00,0.00,0.25), # B
         (0.00,1.00,1.00,0.25,0.50), # C
         (0.00,1.00,0.00,0.25,0.25), # D
         (0.00,0.00,1.00,0.50,0.50), # E
         (0.00,0.00,0.00,0.50,0.25), # F
         (1.00,0.00,1.00,0.75,0.50), # G
         (1.00,0.00,0.00,0.75,0.25), # H
         (1.00,1.00,1.00,1.00,0.50), # I
         (1.00,1.00,0.00,1.00,0.25), # J
         (0.00,1.00,1.00,0.50,0.75), # K
         (1.00,1.00,1.00,0.75,0.75), # L
         (0.00,1.00,0.00,0.50,0.00), # M
         (1.00,1.00,0.00,0.75,0.00)  # N
         ]
     self.triSrc=[
         #
         #  Faces (QUAD/TRIPAIR)
         #  using RHR vertex ordering for
         #  correct face surface normals
         #
         #    front: EFHG/EFH+HGE
         #     rear: CABD/CAB+BDC
         #     left: CDFE/CDF+FEC
         #    right: GHJI/GHJ+JIG
         #    upper: KEGL/KEG+GLK
         #    lower: FMNH/FMN+NHF
         #
         "EFH","HGE",
         "CAB","BDC",
         "CDF","FEC",
         "GHJ","JIG",
         "KEG","GLK",
         "FMN","NHF"
         ]
     #
     # setup cube
     #
     # one cube node will be generated per non-air block
     # since different block id's potentially refer to
     # different textures and thus must be separate nodes
     # of the scene graph.
     #
     #   1. vertices
     self.cubeVtx=GeomVertexData('blockCube',GeomVertexFormat.getV3t2(),Geom.UHStatic)
     self.cubeVtx.setNumRows(len(self.cubeVtxSrc))
     vtxW=GeomVertexWriter(self.cubeVtx,'vertex')
     txcW=GeomVertexWriter(self.cubeVtx,'texcoord')
     for vertex in self.cubeVtxSrc:
         vtxW.addData3f(*vertex[0:3])
         txcW.addData2f(*vertex[3:5])
     #   2. mesh
     self.cubeMesh=GeomTriangles(Geom.UHStatic)
     for tri in self.triSrc:
         for triV in tri:
             triVtxId=ord(triV)-65 # changea 'A'-'N' to 0-13
             self.cubeMesh.addVertex(triVtxId)
         self.cubeMesh.close_primitive()
     #   3. geometry (primitive+vertex pair)
     self.cubeGeom=Geom(self.cubeVtx)
     self.cubeGeom.addPrimitive(self.cubeMesh)
def makeTrack(segments):

	format=GeomVertexFormat.getV3t2()

	vdata=GeomVertexData('track', format, Geom.UHStatic)
	vertex=GeomVertexWriter(vdata, 'vertex')
	texcoord=GeomVertexWriter(vdata, 'texcoord')

	vdata2=GeomVertexData('startline', format, Geom.UHStatic)
	vertex2=GeomVertexWriter(vdata2, 'vertex')
	texcoord2=GeomVertexWriter(vdata2, 'texcoord')

	numVertices=0
	twat=0
	for segmentIndex in range(len(segments)):
		currentSegment=segments[segmentIndex]

		if segmentIndex==0:
			nextSegment=segments[segmentIndex+1]
			currentStartLineLeftPoint=Geometry.Point.CreateByInterpolation(currentSegment.getMidPoint(),currentSegment.getLeftPoint(),100,77.5);
			currentStartLineRightPoint=Geometry.Point.CreateByInterpolation(currentSegment.getMidPoint(),currentSegment.getRightPoint(),100,77.5);
			nextStartLineLeftPoint=Geometry.Point.CreateByInterpolation(nextSegment.getMidPoint(),nextSegment.getLeftPoint(),100,77.5);
			nextStartLineRightPoint=Geometry.Point.CreateByInterpolation(nextSegment.getMidPoint(),nextSegment.getRightPoint(),100,77.5);

			vertex2.addData3f(nextStartLineLeftPoint.getX(),nextStartLineLeftPoint.getY(),0)
			vertex2.addData3f(nextStartLineRightPoint.getX(),nextStartLineRightPoint.getY(),0)
			vertex2.addData3f(currentStartLineLeftPoint.getX(),currentStartLineLeftPoint.getY(),0)
			vertex2.addData3f(currentStartLineRightPoint.getX(),currentStartLineRightPoint.getY(),0)
			texcoord2.addData2f(0.0, 0.0)
			texcoord2.addData2f(12.0, 0.0)
			texcoord2.addData2f(0.0, 2.0)
			texcoord2.addData2f(12.0, 2.0)

		vertex.addData3f(currentSegment.leftPoint.getX(),currentSegment.leftPoint.getY(),0)
		vertex.addData3f(currentSegment.rightPoint.getX(),currentSegment.rightPoint.getY(),0)
		texcoord.addData2f(0.0, 1.0/8*float(twat))
		texcoord.addData2f(1.0, 1.0/8*float(twat))
		#print('vertex at '+str(segmentIndex)+' with v='+str(1.0/8*float(twat)))
		numVertices+=2

		twat=twat+1
		if twat==9:		
			#print('surplus at '+str(segmentIndex)+' with v=0.0')
			vertex.addData3f(currentSegment.leftPoint.getX(),currentSegment.leftPoint.getY(),0)
			vertex.addData3f(currentSegment.rightPoint.getX(),currentSegment.rightPoint.getY(),0)
			texcoord.addData2f(0.0, 0.0)
			texcoord.addData2f(1.0, 0.0)
			numVertices+=2
			twat=1

	#print('vertices created: '+str(numVertices))

	tris=GeomTriangles(Geom.UHDynamic)

	bob=0
	vertexOffset=0
	for index in range(len(segments)):

		vertexOffsets=[]
		for innerIndex in range(4):
			offset=vertexOffset+innerIndex
			if offset>numVertices-1:
				offset=offset-numVertices
			vertexOffsets.append(offset)

		tris.addVertex(vertexOffsets[0])
		tris.addVertex(vertexOffsets[1])
		tris.addVertex(vertexOffsets[3])
		tris.closePrimitive()

		tris.addVertex(vertexOffsets[0])
		tris.addVertex(vertexOffsets[3])
		tris.addVertex(vertexOffsets[2])
		tris.closePrimitive()

		vertexOffset+=2

		bob+=1
		if bob==8:
			vertexOffset+=2
			bob=0

	tris2=GeomTriangles(Geom.UHDynamic)
	tris2.addVertex(0)
	tris2.addVertex(3)
	tris2.addVertex(1)
	tris2.closePrimitive()

	tris2.addVertex(0)
	tris2.addVertex(2)
	tris2.addVertex(3)
	tris2.closePrimitive()

	track=Geom(vdata)
	track.addPrimitive(tris)

	startLine=Geom(vdata2)
	startLine.addPrimitive(tris2)

	snode=GeomNode('tarmac')
	snode.addGeom(track)

	snode2=GeomNode('startline')
	snode2.addGeom(startLine)

	returnValues={"tarmac":snode,"startline":snode2}
	return returnValues

	return snode
	def makeMountains():

		format=GeomVertexFormat.getV3t2()
		vdata=GeomVertexData('mountains', format, Geom.UHStatic)
		vertex=GeomVertexWriter(vdata, 'vertex')
		texcoord=GeomVertexWriter(vdata, 'texcoord')

		format2=GeomVertexFormat.getV3c4()
		vdata2=GeomVertexData('sky', format2, Geom.UHStatic)
		vertex2=GeomVertexWriter(vdata2, 'vertex')
		color2=GeomVertexWriter(vdata2, 'color')

		numQuads=32

		angle=0
		textureX=0
		angleAdd=math.pi*2/numQuads
		textureXAdd=1.0/numQuads
		currentQuad=0
		numVertices=0

		while currentQuad<numQuads:
			if currentQuad==0:

				vertexX=math.sin(angle)*3000
				vertexY=math.cos(angle)*3000
				vertex.addData3f(vertexX,vertexY,0.0)
				vertex.addData3f(vertexX,vertexY,360.0)

				texcoord.addData2f(1.0, 0.0)
				texcoord.addData2f(1.0, 1.0)

				numVertices=numVertices+2

			vertexX=math.sin(angle)*3000
			vertexY=math.cos(angle)*3000
		
			vertex.addData3f(vertexX,vertexY,0.0)
			vertex.addData3f(vertexX,vertexY,360.0)

			vertex2.addData3f(vertexX,vertexY,360.0)
			color2.addData4f(45.0/255.0,112.0/255.0,255.0/255.0,1.0)
			#color2.addData4f(1.0,1.0,1.0,1.0)

			#print('created vertex at '+str(vertexX)+','+str(vertexY)+',2')
			#print('created vertex at '+str(vertexX)+','+str(vertexY)+',0')

			texcoord.addData2f(textureX, 0.0)
			texcoord.addData2f(textureX, 1.0)
			#print('texturex is '+str(textureX))

			#print('creating vertices v'+str(numVertices)+' and v'+str(numVertices+1))

			numVertices=numVertices+2
			currentQuad=currentQuad+1
			textureX=textureX+textureXAdd
			angle=angle+angleAdd

		vertex2.addData3f(0.0,0.0,360.0)
		#color2.addData4f(1.0,1.0,1.0,1.0)
		color2.addData4f(45.0/255.0,112.0/255.0,255.0/255.0,1.0)

		currentQuad=0
		currentOffset=2
		tris=GeomTriangles(Geom.UHDynamic)

		#print('creating tris - numVertices is '+str(numVertices))

		while currentQuad<numQuads:

			vertexOffsets=[]
			for innerIndex in range(4):
				offset=currentOffset+innerIndex
				#print('comparing '+str(offset)+' with '+str(numVertices-1))
				if offset>numVertices-1:
					offset=offset-numVertices
				vertexOffsets.append(offset)

			#print('adding tri connecting v'+str(vertexOffsets[0])+', v'+str(vertexOffsets[1])+', v'+str(vertexOffsets[3]))
			#print('adding tri connecting v'+str(vertexOffsets[1])+', v'+str(vertexOffsets[3])+', v'+str(vertexOffsets[2]))

			tris.addVertex(vertexOffsets[0])
			tris.addVertex(vertexOffsets[2])
			tris.addVertex(vertexOffsets[1])
			tris.closePrimitive()

			tris.addVertex(vertexOffsets[1])
			tris.addVertex(vertexOffsets[2])
			tris.addVertex(vertexOffsets[3])
			tris.closePrimitive()

			currentOffset=currentOffset+2
			currentQuad=currentQuad+1

		tris2=GeomTriangles(Geom.UHDynamic)
		currentOffset=1

		numTris=numQuads
		currentTri=0
		while currentTri<numTris:

			tris2.addVertex(numTris)
			tris2.addVertex(currentTri)
			if currentTri==numTris-1:
				tris2.addVertex(0)
			else:
				tris2.addVertex(currentTri+1)
			tris2.closePrimitive()

			currentTri=currentTri+1

		mountains=Geom(vdata)
		mountains.addPrimitive(tris)

		sky=Geom(vdata2)
		sky.addPrimitive(tris2)

		snode=GeomNode('mountains')
		snode.addGeom(mountains)

		snode2=GeomNode('sky')
		snode2.addGeom(sky)

		returnValues={"mountains":snode,"sky":snode2}
		return returnValues
Exemplo n.º 4
0
class Mesh(NodePath):
    _formats = {
        (0, 0, 0): GeomVertexFormat.getV3(),
        (1, 0, 0): GeomVertexFormat.getV3c4(),
        (0, 1, 0): GeomVertexFormat.getV3t2(),
        (0, 0, 1): GeomVertexFormat.getV3n3(),
        (1, 0, 1): GeomVertexFormat.getV3n3c4(),
        (1, 1, 0): GeomVertexFormat.getV3c4t2(),
        (0, 1, 1): GeomVertexFormat.getV3n3t2(),
        (1, 1, 1): GeomVertexFormat.getV3n3c4t2(),
    }

    _modes = {
        'triangle': GeomTriangles,
        'tristrip': GeomTristrips,
        'ngon': GeomTrifans,
        'line': GeomLinestrips,
        'point': GeomPoints,
    }

    def __init__(self, vertices=None, triangles=None, colors=None, uvs=None, normals=None, static=True, mode='triangle',
                 thickness=1):
        super().__init__('mesh')

        self.vertices = vertices
        self.triangles = triangles
        self.colors = colors
        self.uvs = uvs
        self.normals = normals
        self.static = static
        self.mode = mode
        self.thickness = thickness

        for var in (
        ('vertices', vertices), ('triangles', triangles), ('colors', colors), ('uvs', uvs), ('normals', normals)):
            name, value = var
            if value is None:
                setattr(self, name, list())

        if self.vertices:
            self.vertices = [tuple(v) for v in self.vertices]
            self.generate()

    def generate(self):  # call this after setting some of the variables to update it
        if hasattr(self, 'geomNode'):
            self.geomNode.removeAllGeoms()

        static_mode = Geom.UHStatic if self.static else Geom.UHDynamic
        vertex_format = Mesh._formats[(bool(self.colors), bool(self.uvs), bool(self.normals))]
        vdata = GeomVertexData('name', vertex_format, static_mode)
        vdata.setNumRows(len(self.vertices))  # for speed
        self.geomNode = GeomNode('mesh')
        self.attachNewNode(self.geomNode)

        vertexwriter = GeomVertexWriter(vdata, 'vertex')
        for v in self.vertices:
            vertexwriter.addData3f((v[0], v[2], v[1]))  # swap y and z

        if self.colors:
            colorwriter = GeomVertexWriter(vdata, 'color')
            for c in self.colors:
                colorwriter.addData4f(c)

        if self.uvs:
            uvwriter = GeomVertexWriter(vdata, 'texcoord')
            for uv in self.uvs:
                uvwriter.addData2f(uv[0], uv[1])

        if self.normals != None:
            normalwriter = GeomVertexWriter(vdata, 'normal')
            for norm in self.normals:
                normalwriter.addData3f((norm[0], norm[2], norm[1]))

        if self.mode != 'line' or not self._triangles:
            prim = Mesh._modes[self.mode](static_mode)

            if self._triangles:
                if isinstance(self._triangles[0], int):
                    for t in self._triangles:
                        prim.addVertex(t)

                elif len(self._triangles[0]) >= 3:  # if tris are tuples like this: ((0,1,2), (1,2,3))
                    for t in self._triangles:
                        if len(t) == 3:
                            for e in t:
                                prim.addVertex(e)
                        elif len(t) == 4:  # turn quad into tris
                            prim.addVertex(t[0])
                            prim.addVertex(t[1])
                            prim.addVertex(t[2])
                            prim.addVertex(t[2])
                            prim.addVertex(t[3])
                            prim.addVertex(t[0])

            else:
                prim.addConsecutiveVertices(0, len(self.vertices))

            prim.close_primitive()
            geom = Geom(vdata)
            geom.addPrimitive(prim)
            self.geomNode.addGeom(geom)

        else:  # line with segments defined in triangles
            for line in self._triangles:
                prim = Mesh._modes[self.mode](static_mode)
                for e in line:
                    prim.addVertex(e)
                prim.close_primitive()
                geom = Geom(vdata)
                geom.addPrimitive(prim)
                self.geomNode.addGeom(geom)

        self.recipe = dedent(f'''
            Mesh(
                vertices={[tuple(e) for e in self.vertices]},
                triangles={self._triangles},
                colors={[tuple(e) for e in self.colors]},
                uvs={self.uvs},
                normals={[tuple(e) for e in self.normals]},
                static={self.static},
                mode="{self.mode}",
                thickness={self.thickness}
            )
        ''')
        # print('finished')

    def __add__(self, other):
        self.vertices += other.vertices
        self.triangles += other.triangles
        if other.colors:
            self.colors += other.colors
        else:
            self.colors += (color.white,) * len(other.vertices)

        self.normals += other.normals
        self.uvs += other.uvs

    def __copy__(self):
        return Mesh(self.vertices, self.triangles, self.colors, self.uvs, self.normals, self.static, self.mode,
                    self.thickness)

    @property
    def thickness(self):
        return self.getRenderModeThickness()

    @thickness.setter
    def thickness(self, value):
        self.setRenderModeThickness(value)

    @property
    def triangles(self):
        if self._triangles == None:
            self._triangles = [(i, i + 1, i + 2) for i in range(0, len(self.vertices), 3)]

        return self._triangles

    @triangles.setter
    def triangles(self, value):
        self._triangles = value

    def generate_normals(self, smooth=True):
        self.normals = list(generate_normals(self.vertices, self.triangles, smooth))
        self.generate()
        return self.normals

    def colorize(self, left=color.white, right=color.blue, down=color.red, up=color.green, back=color.white,
                 forward=color.white, smooth=True, world_space=True):
        colorize(self, left, right, down, up, back, forward, smooth, world_space)

    def project_uvs(self, aspect_ratio=1, direction='forward'):
        project_uvs(self, aspect_ratio)

    def clear(self, regenerate=True):
        self.vertices, self.triangles, self.colors, self.uvs, self.normals = list(), list(), list(), list(), list()
        if regenerate:
            self.generate()

    def save(self, name, path=application.asset_folder, filetype='ursinamesh'):
        if filetype == 'ursinamesh':
            if not '.' in name:
                name += '.ursinamesh'

            with open(path / name, 'w') as f:
                recipe = self.recipe.replace('LVector3f', '')
                f.write(recipe)

        elif filetype == 'obj':
            from ursina.mesh_importer import ursina_mesh_to_obj
            ursina_mesh_to_obj(self, name, path)
Exemplo n.º 5
0
    def generate(
            self
    ):  # call this after setting some of the variables to update it
        if hasattr(self, 'geomNode'):
            self.geomNode.removeAllGeoms()

        static_mode = Geom.UHStatic if self.static else Geom.UHDynamic

        formats = {
            (0, 0, 0): GeomVertexFormat.getV3(),
            (1, 0, 0): GeomVertexFormat.getV3c4(),
            (0, 1, 0): GeomVertexFormat.getV3t2(),
            (0, 0, 1): GeomVertexFormat.getV3n3(),
            (1, 0, 1): GeomVertexFormat.getV3n3c4(),
            (1, 1, 0): GeomVertexFormat.getV3c4t2(),
            (0, 1, 1): GeomVertexFormat.getV3n3t2(),
            (1, 1, 1): GeomVertexFormat.getV3n3c4t2(),
        }

        vertex_format = formats[(bool(self.colors), bool(self.uvs),
                                 bool(self.normals))]
        vdata = GeomVertexData('name', vertex_format, static_mode)
        vdata.setNumRows(len(self.vertices))  # for speed

        vertexwriter = GeomVertexWriter(vdata, 'vertex')
        for v in self.vertices:
            vertexwriter.addData3f((v[0], v[2], v[1]))  # swap y and z

        if self.colors:
            colorwriter = GeomVertexWriter(vdata, 'color')
            for c in self.colors:
                colorwriter.addData4f(c)

        if self.uvs:
            uvwriter = GeomVertexWriter(vdata, 'texcoord')
            for uv in self.uvs:
                uvwriter.addData2f(uv[0], uv[1])

        if self.normals != None:
            normalwriter = GeomVertexWriter(vdata, 'normal')
            for norm in self.normals:
                normalwriter.addData3f((norm[0], norm[2], norm[1]))

        modes = {
            'triangle': GeomTriangles(static_mode),
            'tristrip': GeomTristrips(static_mode),
            'ngon': GeomTrifans(static_mode),
            'line': GeomLinestrips(static_mode),
            'point': GeomPoints(static_mode),
        }

        if self.mode != 'line' or not self._triangles:
            prim = modes[self.mode]

            if self._triangles:
                if isinstance(self._triangles[0], int):
                    for t in self._triangles:
                        prim.addVertex(t)

                elif len(
                        self._triangles[0]
                ) >= 3:  # if tris are tuples like this: ((0,1,2), (1,2,3))
                    for t in self._triangles:
                        if len(t) == 3:
                            for e in t:
                                prim.addVertex(e)
                        elif len(t) == 4:  # turn quad into tris
                            prim.addVertex(t[0])
                            prim.addVertex(t[1])
                            prim.addVertex(t[2])
                            prim.addVertex(t[2])
                            prim.addVertex(t[3])
                            prim.addVertex(t[0])

            else:
                prim.addConsecutiveVertices(0, len(self.vertices))

            prim.close_primitive()
            geom = Geom(vdata)
            geom.addPrimitive(prim)

        else:  # line with segments defnined in triangles
            for line in self._triangles:
                prim = modes[self.mode]
                for e in line:
                    prim.addVertex(e)
                prim.close_primitive()
                geom = Geom(vdata)
                geom.addPrimitive(prim)

        self.geomNode = GeomNode('mesh')
        self.geomNode.addGeom(geom)
        self.attachNewNode(self.geomNode)

        # if self.normals:
        #     self.normals = [tuple(e) for e in self.normals]
        self.recipe = dedent(f'''
            Mesh(
                vertices={[tuple(e) for e in self.vertices]},
                triangles={self._triangles},
                colors={[tuple(e) for e in self.colors]},
                uvs={self.uvs},
                normals={[tuple(e) for e in self.normals]},
                static={self.static},
                mode="{self.mode}",
                thickness={self.thickness}
            )
        ''')
Exemplo n.º 6
0
class Mesh(NodePath):

    _formats = {
        (0, 0, 0): GeomVertexFormat.getV3(),
        (1, 0, 0): GeomVertexFormat.getV3c4(),
        (0, 1, 0): GeomVertexFormat.getV3t2(),
        (0, 0, 1): GeomVertexFormat.getV3n3(),
        (1, 0, 1): GeomVertexFormat.getV3n3c4(),
        (1, 1, 0): GeomVertexFormat.getV3c4t2(),
        (0, 1, 1): GeomVertexFormat.getV3n3t2(),
        (1, 1, 1): GeomVertexFormat.getV3n3c4t2(),
    }

    _modes = {
        'triangle': GeomTriangles,
        'tristrip': GeomTristrips,
        'ngon': GeomTrifans,
        'line': GeomLinestrips,
        'point': GeomPoints,
    }

    def __init__(self,
                 vertices=None,
                 triangles=None,
                 colors=None,
                 uvs=None,
                 normals=None,
                 static=True,
                 mode='triangle',
                 thickness=1):
        super().__init__('mesh')

        self.vertices = vertices
        self.triangles = triangles
        self.colors = colors
        self.uvs = uvs
        self.normals = normals
        self.static = static
        self.mode = mode
        self.thickness = thickness

        for var in (('vertices', vertices), ('triangles', triangles),
                    ('colors', colors), ('uvs', uvs), ('normals', normals)):
            name, value = var
            if value is None:
                setattr(self, name, list())

        if self.vertices:
            self.generate()

    def generate(
            self
    ):  # call this after setting some of the variables to update it
        if hasattr(self, 'geomNode'):
            self.geomNode.removeAllGeoms()

        static_mode = Geom.UHStatic if self.static else Geom.UHDynamic
        vertex_format = Mesh._formats[(bool(self.colors), bool(self.uvs),
                                       bool(self.normals))]
        vdata = GeomVertexData('name', vertex_format, static_mode)
        vdata.setNumRows(len(self.vertices))  # for speed
        if not hasattr(self, 'geomNode'):
            self.geomNode = GeomNode('mesh')
            self.attachNewNode(self.geomNode)

        vertexwriter = GeomVertexWriter(vdata, 'vertex')
        for v in self.vertices:
            vertexwriter.addData3f(*v)

        if self.colors:
            colorwriter = GeomVertexWriter(vdata, 'color')
            for c in self.colors:
                colorwriter.addData4f(c)

        if self.uvs:
            uvwriter = GeomVertexWriter(vdata, 'texcoord')
            for uv in self.uvs:
                uvwriter.addData2f(uv[0], uv[1])

        if self.normals != None:
            normalwriter = GeomVertexWriter(vdata, 'normal')
            for norm in self.normals:
                normalwriter.addData3f(*norm)

        if self.mode != 'line' or not self._triangles:
            self.indices = list()

            if self._triangles:
                if isinstance(self._triangles[0], int):
                    for t in self._triangles:
                        self.indices.append(t)

                elif len(
                        self._triangles[0]
                ) >= 3:  # if tris are tuples like this: ((0,1,2), (1,2,3))
                    for t in self._triangles:
                        if len(t) == 3:
                            self.indices.extend(t)

                        elif len(t) == 4:  # turn quad into tris
                            self.indices.extend(
                                [t[i] for i in (0, 1, 2, 2, 3, 0)])

            else:
                self.indices = [i for i in range(len(self.vertices))]

            prim = Mesh._modes[self.mode](static_mode)

            self.generated_vertices = [self.vertices[i] for i in self.indices]
            for v in self.indices:
                prim.addVertex(v)

            prim.close_primitive()
            geom = Geom(vdata)
            geom.addPrimitive(prim)
            self.geomNode.addGeom(geom)

        else:  # line with segments defined in triangles
            for line in self._triangles:
                prim = Mesh._modes[self.mode](static_mode)
                for e in line:
                    prim.addVertex(e)
                prim.close_primitive()
                geom = Geom(vdata)
                geom.addPrimitive(prim)
                self.geomNode.addGeom(geom)

        if self.mode == 'point':
            self.setTexGen(TextureStage.getDefault(),
                           TexGenAttrib.MPointSprite)
            # self.set_render_mode_perspective(True)

        # print('finished')

    @property
    def recipe(self):
        if hasattr(self, '_recipe'):
            return self._recipe

        return dedent(f'''
            Mesh(
                vertices={[tuple(e) for e in self.vertices]},
                triangles={self._triangles},
                colors={[tuple(e) for e in self.colors]},
                uvs={self.uvs},
                normals={[tuple(e) for e in self.normals]},
                static={self.static},
                mode="{self.mode}",
                thickness={self.thickness}
            )
        ''')

    @recipe.setter
    def recipe(self, value):
        self._recipe = value

    def __add__(self, other):
        self.vertices += other.vertices
        self.triangles += other.triangles
        if other.colors:
            self.colors += other.colors
        else:
            self.colors += (color.white, ) * len(other.vertices)

        self.normals += other.normals
        self.uvs += other.uvs

    def __deepcopy__(self, memo):
        m = Mesh(self.vertices, self.triangles, self.colors, self.uvs,
                 self.normals, self.static, self.mode, self.thickness)
        m.name = self.name
        return m

    @property
    def thickness(self):
        return self.getRenderModeThickness()

    @thickness.setter
    def thickness(self, value):
        self.setRenderModeThickness(value)

    @property
    def triangles(self):
        if self._triangles == None:
            self._triangles = [(i, i + 1, i + 2)
                               for i in range(0, len(self.vertices), 3)]

        return self._triangles

    @triangles.setter
    def triangles(self, value):
        self._triangles = value

    def generate_normals(self, smooth=True):
        self.normals = list(
            generate_normals(self.vertices, self.triangles, smooth))
        self.generate()
        return self.normals

    def colorize(self,
                 left=color.white,
                 right=color.blue,
                 down=color.red,
                 up=color.green,
                 back=color.white,
                 forward=color.white,
                 smooth=True,
                 world_space=True):
        colorize(self, left, right, down, up, back, forward, smooth,
                 world_space)

    def project_uvs(self, aspect_ratio=1, direction='forward'):
        project_uvs(self, aspect_ratio)

    def clear(self, regenerate=True):
        self.vertices, self.triangles, self.colors, self.uvs, self.normals = list(
        ), list(), list(), list(), list()
        if regenerate:
            self.generate()

    def save(self, name='', path=application.compressed_models_folder):
        if not application.compressed_models_folder.exists():
            application.compressed_models_folder.mkdir()

        if not name and hasattr(self, 'path'):
            name = self.path.stem
            if not '.' in name:
                name += '.ursinamesh'

        if name.endswith('ursinamesh'):
            with open(path / name, 'w') as f:
                # recipe = self.recipe.replace('LVector3f', '')
                f.write(self.recipe)
            print('saved .ursinamesh to:', path / name)

        elif name.endswith('.obj'):
            from ursina.mesh_importer import ursina_mesh_to_obj
            ursina_mesh_to_obj(self, name, path)

        elif name.endswith('.bam'):
            success = self.writeBamFile(path / name)
            print('saved .bam to:', path / name)