View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0002828 | DarkRadiant | Scripting | public | 28.07.2011 16:41 | 11.10.2012 19:04 |
Reporter | tels | Assigned To | greebo | ||
Priority | normal | Severity | feature | Reproducibility | N/A |
Status | closed | Resolution | fixed | ||
Product Version | 1.6.1 | ||||
Target Version | 1.7.0 | Fixed in Version | 1.7.0 | ||
Summary | 0002828: ASE export could skip caulk | ||||
Description | Sometimes 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. | ||||
Tags | No 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() | ||||
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. |
|
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 |