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 |