View Issue Details

IDProjectCategoryView StatusLast Update
0002828DarkRadiantScriptingpublic11.10.2012 19:04
Reportertels Assigned Togreebo  
PrioritynormalSeverityfeatureReproducibilityN/A
Status closedResolutionfixed 
Product Version1.6.1 
Target Version1.7.0Fixed in Version1.7.0 
Summary0002828: ASE export could skip caulk
DescriptionSometimes you want to export a brush, but leave one face off (f.i. because it is embedded into another brush/patch). However, even if you set that face to caulk or nodraw, it is exported as it's own geometry object.

While it can sometimes make sense to export nodraw (to later skin it in a different shader), I am not so sure if it makes sense to ever export caulk.

So it would be nice if there was a checkbox that asks "Export nodraw faces" and/or "Export caulked faces" and when these are toggled, skips any faces with that shader.
TagsNo tags attached.
Attached Files
ase_export.py (15,443 bytes)   
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****

#TODO:

# Set the command name so that DarkRadiant recognises this file
__commandName__ = 'aseExport2'
__commandDisplayName__ = 'Export ASE... (2)'

# The actual algorithm called by DarkRadiant is contained in the execute() function
def execute():
    script = "Dark Radiant ASCII Scene Export (*.ase)"
    author = "Richard Bartlett, some additions by greebo and tels"
    version = "0.7"

    # Check if we have a valid selection

    selectionInfo = GlobalSelectionSystem.getSelectionInfo()

    # Don't allow empty selections or selected components only
    if selectionInfo.totalCount == 0 or selectionInfo.totalCount == selectionInfo.componentCount:
        errMsg = GlobalDialogManager.createMessageBox('No selection', 'Nothing selected, cannot run exporter.', Dialog.ERROR)
        errMsg.run()
        return

    shaderlist = []
    geomlist = []

    # simple linear triangulation of n-sided poly

    def triangulate(pointset):
        tris = []
        for count in range(1, len(pointset) - 1):
            tris.append([pointset[0], pointset[count], pointset[count + 1]])
        return tris

    # skin patch matrix with tris

    def skinmatrix(pointset, width, height):
        tris = []
        for h in range(height-1):
            for w in range(width-1):
                tris.append([pointset[w+(h*width)], pointset[w+1+(h*width)], pointset[w+width+(h*width)]])
                tris.append([pointset[w+1+(h*width)], pointset[w+1+width+(h*width)], pointset[w+width+(h*width)]])
        return tris

    def processBrush(brushnode):
        verts = []
        faces = []

        numfaces = brushnode.getNumFaces()
        for index in range(numfaces):
            facenode = brushnode.getFace(index)
            shader = facenode.getShader()

            # Tels: skip if caulk and no caulk should be exported
            if (shader == 'textures/common/caulk') and (int(GlobalRegistry.get('user/scripts/aseExport/exportcaulk'))) == 0:
                continue

            if not shader in shaderlist:
                shaderlist.append(shader)
            winding = facenode.getWinding()
            tris = triangulate([x+len(verts) for x in range(len(winding))])
            for x in tris:
                x.append(shaderlist.index(shader))
                faces.append(x)
            for x in reversed(winding):
                verts.append([x.vertex.x(), x.vertex.y(), x.vertex.z(), x.texcoord.x(), x.texcoord.y() * -1, x.normal.x(), x.normal.y(), x.normal.z()])

        geomlist.append([verts, faces])
        return

    def processPatch(patchnode):
        verts = []
        faces = []

        shader = patchnode.getShader()

        # Tels: skip if caulk and no caulk should be exported
        if shader == 'textures/common/caulk' and int(GlobalRegistry.get('user/scripts/aseExport/exportcaulk')) == 0:
            return

        if not shader in shaderlist:
            shaderlist.append(shader)
        mesh = patchnode.getTesselatedPatchMesh()
        for x in mesh.vertices:
            verts.append([x.vertex.x(), x.vertex.y(), x.vertex.z(), x.texcoord.x(), x.texcoord.y() * -1, x.normal.x(), x.normal.y(), x.normal.z()])
        tris = skinmatrix([x for x in range(len(verts))], mesh.width, mesh.height)
        for x in tris:
            x.append(shaderlist.index(shader))
            faces.append(x)

        geomlist.append([verts, faces])
        return

    # Branch on primitive nodes
    def processPrimitive(scenenode):
        if scenenode.isBrush():
            processBrush(scenenode.getBrush())
        elif scenenode.isPatch():
            processPatch(scenenode.getPatch())
        return

    # Traversor class to visit child primitives of entities
    class nodeVisitor(SceneNodeVisitor):
        def pre(self, scenenode):
            # Brush?
            if scenenode.isBrush():
                processBrush(scenenode.getBrush())
            # Patch?
            elif scenenode.isPatch():
                processPatch(scenenode.getPatch())

            # Traverse all child nodes, regardless of type
            return 1

    class dataCollector(SelectionVisitor):
        def visit(self, scenenode):

            if scenenode.getNodeType() == 'primitive':
                processPrimitive(scenenode)
            elif scenenode.isEntity():
                # greebo: Found an entity, this could be a func_static or similar
                # Traverse children of this entity using a new walker
                nodewalker = nodeVisitor()
                scenenode.traverse(nodewalker)
            else:
                print('WARNING: unsupported node type selected. Skipping: ' + scenenode.getNodeType())

    # Dialog
    dialog = GlobalDialogManager.createDialog(script + 'v' + version)

    # Add an entry box and remember the handle
    fileHandle = dialog.addEntryBox("Filename:")
    dialog.setElementValue(fileHandle, GlobalRegistry.get('user/scripts/aseExport/recentFilename'))

    # Add an entry box and remember the handle
    pathHandle = dialog.addPathEntry("Save path:", True)
    dialog.setElementValue(pathHandle, GlobalRegistry.get('user/scripts/aseExport/recentPath'))

    # Add a checkbox
    centerObjectsHandle = dialog.addCheckbox("Center objects at 0,0,0 origin")
    dialog.setElementValue(centerObjectsHandle, GlobalRegistry.get('user/scripts/aseExport/centerObjects'))

    # Add another checkbox
    exportCaulkHandle = dialog.addCheckbox("Export caulked faces")
    dialog.setElementValue(exportCaulkHandle, GlobalRegistry.get('user/scripts/aseExport/exportcaulk'))

    if dialog.run() == Dialog.OK:
        fullpath = dialog.getElementValue(pathHandle) + '/' + dialog.getElementValue(fileHandle)
        if not fullpath.endswith('.ase'):
            fullpath = fullpath + '.ase'

        # Save the path for later use
        GlobalRegistry.set('user/scripts/aseExport/recentFilename', dialog.getElementValue(fileHandle))
        GlobalRegistry.set('user/scripts/aseExport/recentPath', dialog.getElementValue(pathHandle))
        GlobalRegistry.set('user/scripts/aseExport/centerObjects', dialog.getElementValue(centerObjectsHandle))
        GlobalRegistry.set('user/scripts/aseExport/exportcaulk', dialog.getElementValue(exportCaulkHandle))

        try:
            file = open(fullpath, 'r')
            file.close()
            prompt = GlobalDialogManager.createMessageBox('Warning', 'The file ' + fullpath + ' already exists. Do you wish to overwrite it?', Dialog.ASK)
            if prompt.run() == Dialog.YES:
                overwrite = True
            else:
                overwrite = False
        except IOError:
            overwrite = True

        if overwrite:

            # Tels: Only collect the data if we are going to export it
            walker = dataCollector()
            GlobalSelectionSystem.foreachSelected(walker)

            # greebo: Check if we should center objects at the 0,0,0 origin
            if int(dialog.getElementValue(centerObjectsHandle)) == 1:
                #center objects at 0,0,0
                xlist = []
                ylist = []
                zlist = []
                for item in geomlist:
                    for vert in item[0]:
                        xlist.append(vert[0])
                        ylist.append(vert[1])
                        zlist.append(vert[2])
                xcenter=(max(xlist)+min(xlist))/2
                ycenter=(max(ylist)+min(ylist))/2
                zcenter=(max(zlist)+min(zlist))/2
                for item in geomlist:
                    for vert in item[0]:
                        vert[0] = vert[0] - xcenter
                        vert[1] = vert[1] - ycenter
                        vert[2] = vert[2] - zcenter

            # split objects that do not share the same texture on all faces
            for x in geomlist:
                texlist = []
                for data in x[1]:
                    texlist.append(data[3])
                if len(set(texlist)) > 1:
                    temp = []
                    for texture in set(texlist):
                        vertlist = []
                        facelist = []
                        for data in x[1]:
                            if data[3] == texture:
                                facelist.append(data)
                        usedverts = []
                        for face in facelist:
                            usedverts.extend(face[0:3])
                        usedverts = list(set(usedverts))
                        for index in usedverts:
                            vertlist.append(x[0][index])
                        newfacelist = []
                        for face in facelist:
                            newfacelist.append([vertlist.index(x[0][face[0]]),vertlist.index(x[0][face[1]]),vertlist.index(x[0][face[2]]),face[3]])
                        temp.append([vertlist, newfacelist])
                    del geomlist[geomlist.index(x)]
                    geomlist.extend(temp)

            scene = '''\t*SCENE_FILENAME "{0}"
\t*SCENE_FIRSTFRAME 0
\t*SCENE_LASTFRAME 100
\t*SCENE_FRAMESPEED 30
\t*SCENE_TICKSPERFRAME 160
\t*SCENE_BACKGROUND_STATIC 0.0000\t0.0000\t0.0000
\t*SCENE_AMBIENT_STATIC 0.0000\t0.0000\t0.0000'''.format(GlobalMap.getMapName())

            materials = str()
            for x in shaderlist:
                materials = materials + '''\t*MATERIAL {0} {{
\t\t*MATERIAL_NAME "{1}"
\t\t*MATERIAL_CLASS "Standard"
\t\t*MATERIAL_AMBIENT 0.5882\t0.5882\t0.5882
\t\t*MATERIAL_DIFFUSE 0.5882\t0.5882\t0.5882
\t\t*MATERIAL_SPECULAR 0.9000\t0.9000\t0.9000
\t\t*MATERIAL_SHINE 0.1000
\t\t*MATERIAL_SHINESTRENGTH 0.0000
\t\t*MATERIAL_TRANSPARENCY 0.0000
\t\t*MATERIAL_WIRESIZE 1.0000
\t\t*MATERIAL_SHADING Blinn
\t\t*MATERIAL_XP_FALLOFF 0.0000
\t\t*MATERIAL_SELFILLUM 0.0000
\t\t*MATERIAL_FALLOFF In
\t\t*MATERIAL_XP_TYPE Filter
\t\t*MAP_DIFFUSE {{
\t\t\t*MAP_NAME "{2}"
\t\t\t*MAP_CLASS "Bitmap"
\t\t\t*MAP_SUBNO 1
\t\t\t*MAP_AMOUNT 1.0000
\t\t\t*BITMAP "\\\\purgatory\\purgatory\\doom\\base\{2}"
\t\t\t*MAP_TYPE Screen
\t\t\t*UVW_U_OFFSET 0.0000
\t\t\t*UVW_V_OFFSET 0.0000
\t\t\t*UVW_U_TILING 1.0000
\t\t\t*UVW_V_TILING 1.0000
\t\t\t*UVW_ANGLE 0.0000
\t\t\t*UVW_BLUR 1.0000
\t\t\t*UVW_BLUR_OFFSET 0.0000
\t\t\t*UVW_NOUSE_AMT 1.0000
\t\t\t*UVW_NOISE_SIZE 1.0000
\t\t\t*UVW_NOISE_LEVEL 1
\t\t\t*UVW_NOISE_PHASE 0.0000
\t\t\t*BITMAP_FILTER Pyramidal
\t\t}}
\t}}
'''.format(shaderlist.index(x), x, x.replace('/','\\'))

            geomobjects = str()

            for x in geomlist:
                # x[0] = vertices
                # vert[0] - vert[2] = vertex coords
                # vert[3] - vert[4] = texture coords
                # vert[5] - vert[7] = normal
                # x[1] = faces
                vertlist = str()
                for count, data in enumerate(x[0]):
                    vertlist = vertlist + '''\t\t\t*MESH_VERTEX {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(count, data[0], data[1], data[2])
                facelist = str()
                for count, data in enumerate(x[1]):
                    facelist = facelist + '''\t\t\t*MESH_FACE     {0}:  A:   {1} B:   {2} C:     {3} AB:       0 BC:    0 CA:    0\t *MESH_SMOOTHING 1 \t*MESH_MTLID {4}\n'''.format(count, data[0], data[1], data[2], data[3])
                tvertlist = str()
                for count, data in enumerate(x[0]):
                    tvertlist = tvertlist + '''\t\t\t*MESH_TVERT {0}\t{1: 10.4f}\t{2: 10.4f}\t0.0000\n'''.format(count, data[3], data[4])
                tfacelist = str()
                for count, data in enumerate(x[1]):
                    tfacelist = tfacelist + '''\t\t\t*MESH_TFACE {0}\t{1}\t{2}\t{3}\n'''.format(count, data[0], data[1], data[2])
                cfacelist = str()
                for count, data in enumerate(x[1]):
                    cfacelist = cfacelist + '''\t\t\t*MESH_CFACE {0}\t0\t0\t0\n'''.format(count)

                normals = str()
                for count, data in enumerate(x[1]):
                    normals += '''\t\t\t*MESH_FACENORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(count, x[0][data[0]][5], x[0][data[0]][6], x[0][data[0]][7]) # greebo: use first vertex normal as face normal
                    normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[0], x[0][data[0]][5], x[0][data[0]][6], x[0][data[0]][7])
                    normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[1], x[0][data[1]][5], x[0][data[1]][6], x[0][data[1]][7])
                    normals += '''\t\t\t\t*MESH_VERTEXNORMAL {0}\t{1: 10.4f}\t{2: 10.4f}\t{3: 10.4f}\n'''.format(data[2], x[0][data[2]][5], x[0][data[2]][6], x[0][data[2]][7])

                if len(x[1]) == 0:
                    continue

                geomobjects = geomobjects + '''*GEOMOBJECT {{
\t*NODE_NAME "{0}"
\t*NODE_TM {{
\t\t*NODE_NAME "{0}"
\t\t*INHERIT_POS 0 0 0
\t\t*INHERIT_ROT 0 0 0
\t\t*INHERIT_SCL 0 0 0
\t\t*TM_ROW0 1.0000\t0.0000\t0.0000
\t\t*TM_ROW1 0.0000\t1.0000\t0.0000
\t\t*TM_ROW2 0.0000\t0.0000\t1.0000
\t\t*TM_ROW3 0.0000\t0.0000\t0.0000
\t\t*TM_POS 0.0000\t0.0000\t0.0000
\t\t*TM_ROTAXIS 0.0000\t0.0000\t0.0000
\t\t*TM_ROTANGLE 0.0000
\t\t*TM_SCALE 1.0000\t1.0000\t1.0000
\t\t*TM_SCALEAXIS 0.0000\t0.0000\t0.0000
\t\t*TM_SCALEAXISANG 0.0000
\t}}
\t*MESH {{
\t\t*TIMEVALUE 0
\t\t*MESH_NUMVERTEX {1}
\t\t*MESH_NUMFACES {2}
\t\t*MESH_VERTEX_LIST {{
{3}\t\t}}
\t\t*MESH_FACE_LIST {{
{4}\t\t}}
\t\t*MESH_NUMTVERTEX {5}
\t\t*MESH_TVERTLIST {{
{6}\t\t}}
\t\t*MESH_NUMTVFACES {7}
\t\t*MESH_TFACELIST {{
{8}\t\t}}
\t\t*MESH_NUMCVERTEX 1
\t\t*MESH_CVERTLIST {{
\t\t\t*MESH_VERTCOL 0\t1.0000\t1.0000\t1.0000
\t\t}}
\t\t*MESH_NUMCVFACES {9}
\t\t*MESH_CFACELIST {{
{10}\t\t}}
\t\t*MESH_NORMALS {{
{11}\t\t}}
\t}}
\t*PROP_MOTIONBLUR 0
\t*PROP_CASTSHADOW 1
\t*PROP_RECVSHADOW 1
\t*MATERIAL_REF {12}
}}\n'''.format('mesh' + str(geomlist.index(x)), \
        len(x[0]), \
        len(x[1]), \
        vertlist, \
        facelist, \
        len(x[0]), \
        tvertlist, \
        len(x[1]), \
        tfacelist, \
        len(x[1]), \
        cfacelist, \
        normals, \
        x[1][0][3]) # material reference from first face

                data = '''*3DSMAX_ASCIIEXPORT\t200
*COMMENT "{0} v{1}"
*SCENE {{
{2}
}}
*MATERIAL_LIST {{
\t*MATERIAL_COUNT {3}
{4}}}
{5}'''.format(script, version, scene, len(shaderlist), materials, geomobjects)

                # Write the compiled data to the output file
                file = open(fullpath, 'w')
                file.write(data)
                file.close()

# __executeCommand__ evaluates to true after DarkRadiant has successfully initialised
if __executeCommand__:
    execute()
ase_export.py (15,443 bytes)   

Activities

tels

tels

28.07.2011 17:16

reporter   ~0003959

Last edited: 28.07.2011 17:17

I've attached an updated ASE exporter script that implements this feature. There is a new checkbox, if unchecked, any patch or face of a brush with "textures/common/caulk" on it will be skipped.

Please tell me if this suffices and can be merged, or if I need to work further on this.

Issue History

Date Modified Username Field Change
28.07.2011 16:41 tels New Issue
28.07.2011 17:15 tels File Added: ase_export.py
28.07.2011 17:16 tels Note Added: 0003959
28.07.2011 17:17 tels Note Edited: 0003959
31.07.2011 05:30 greebo Assigned To => greebo
31.07.2011 05:30 greebo Status new => assigned
31.07.2011 05:30 greebo Status assigned => resolved
31.07.2011 05:30 greebo Fixed in Version => 1.7.0
31.07.2011 05:30 greebo Resolution open => fixed
31.07.2011 05:30 greebo Product Version => 1.6.1
31.07.2011 05:30 greebo Target Version => 1.7.0
11.10.2012 19:04 greebo Status resolved => closed