New 3DSMax XML Exporter & PV3D XML Primitive

Intro

Well it happened again. We built an incredible 3DSMax model (actually 8 of them) to import into Papervision3D for a Google Maps project and we couldn’t get it to work using collada: collada worked in 7 (with a fix), kinda in 8, dead in 9…going going gone. That’s it….we’ve had it!

So as in the Blender case, we built an XML exporter for 3DSMax and Papervision3D XML primitive to suck up the data (We also built an importer for CS4 using drawTriangles). So sure it doesn’t import animation, and guess what…we don’t care! We’re doing all our animation in the Flash engine anyway. And in the book we show you how to build your own data driven animation engine using the Flash10 framework.

3DSMax XML Export

3DSMax XML Expo

YouTube

Demo

Source

Discussion

The exporter is basically a rewrite of the AS3 Geom Class Exporter written by Seraf aka Jerome Birembaut. But as opposed to exporting a class, it exports XML. The exporter is built in Max Script and is run typically as any Max Script is run.

To use the script:

1. In 3DS Max, tools tab, open the maxscript panel and click the “execute script” button.
2. Select the script. It is now displayed in the available scripts list.
3. Select it, a new “XML Exporter” appears.
4. Click the Export XML button.

To learn how to build the house in the demo above view the following Youtube links by Alex Green (one of my designers):

Part 1: http://youtube.com/watch?v=5rf0caQaol8

Part 2: http://youtube.com/watch?v=twZGqvQu3jc

Part 3: http://youtube.com/watch?v=KR7P3tRL72U

Part 4: http://youtube.com/watch?v=EoUzm_x2wP4

Part 5: http://youtube.com/watch?v=51zBUV8Fjn8

Part 6: http://youtube.com/watch?v=y4Fm0u-W88U

To see the 3DSMax Script and XML exporter code, click the more button below.

MaxScript

————————————————————————————-
– Oigianal Author Seraf aka Jerome Birembaut – http://www.dreammania.net
– PV3D 2.0 support by mr.doob – http://www.mrdoob.com
– Rewrtiiten by Mike Lively to create CS4 XML Exporter
————————————————————————————-
utility XMLExporter “XMLExporter”
(
– Definition des variables
local ostream,
papervision2=”CS4 XML Exporter “,
engineName = #(papervision2),

papervision2HeaderFormat = “<?xml version=’1.0′ encoding=’utf-8′?>\n<myData>”,
tag1Format=”\n<primData>\n<vertex>”,
papervisionVertexFormat = “%,%,%,”,
tag2Format=”\n</vertex>\n<uvData>”,
papervisionUVFormat =”%,%,”,
tag3Format=”\n</uvData>\n<faces>”,
papervisionFaceFormat =”%,%,%,%,%,%,”,
tag4Format=”\n</faces>\n</primData>”,
papervision2FooterFormat =”\n</myData>”

– Definition de l’interface
group “Options”
(
bitmap the_bmp fileName:”ship.jpg”
hyperLink lab1 “www.dreammania.net” address:”http://www.dreammania.net&#8221; align:#center
hyperLink lab2 “extended by mr.doob” address:”http://mrdoob.com&#8221; align:#center
hyperLink lab3 “rewritten by Lively” address:”http://www.professionalpapervision.wordpress.com&#8221; align:#center

edittext classname_txt “XML :” text:”XMLfilename”
dropdownlist cbEngine “Engine” items:engineName
spinner scale “Scale :” range:[0.001,1000,1] type:#float
checkbox exportNormal “Export vertex normal” checked:false enabled:false

checkbox swapNormal “Swap face normal” checked:false
checkbox rounded “Rounded vertex coord” checked:false
)
on cbEngine selected i do
(
exportNormal.enabled = case cbEngine.selected of
(
papervision2: false
)

)
button btn_export “Export XML File”

– definition des function

————————————————————————————-
– This function exports all geometry object to the xml file
function ExportMesh meshObj name =
(
vertexFormat = case cbEngine.selected of
(
papervision2: papervisionVertexFormat
)
vertexNormalFormat = case cbEngine.selected of
(
papervision2: papervisionVertexFormat
)

tag2 = case cbEngine.selected of
(
papervision2: tag2Format

)
tag3 = case cbEngine.selected of
(
papervision2: tag3Format

)
UVFormat = case cbEngine.selected of
(
papervision2: papervisionUVFormat
)
faceFormat = case cbEngine.selected of
(
papervision2: papervisionFaceFormat
)

if exportNormal.enabled then (

– Vertex normals
if meshObj.numVerts > 0 and exportNormal.checked then
(

for i = 1 to meshObj.numVerts do
(
normal = GetNormal meshObj i
normal= normalize normal as point3
Format vertexNormalFormat normal.x normal.z normal.y to:ostream

)
)

)
–vertice

if meshObj.numVerts > 0 then
(
for i = 1 to meshObj.numVerts do
(
vert = ((GetVert meshObj i)-meshObj.pos)
if rounded.checked then Format vertexFormat ((ceil (vert.x*scale.value*10 ))/10 ) ((ceil (vert.z*scale.value*10 ))/10 ) ((ceil (vert.y*scale.value*10 ))/10 ) to:ostream
else Format vertexFormat (vert.x*scale.value ) (vert.z*scale.value ) (vert.y*scale.value ) to:ostream
)
)

Format tag2 to:ostream;
– Texture coords

if meshObj.numTVerts > 0 then
(
for i = 1 to meshObj.numTVerts do
(
uvw = GetTVert meshObj i
if cbEngine.selected == sandy then Format UVFormat uvw.x (1-uvw.y) to:ostream
else if cbEngine.selected == papervision then Format UVFormat uvw.x uvw.y to:ostream
else if cbEngine.selected == papervision2 then Format UVFormat uvw.x uvw.y to:ostream
else Format UVFormat uvw.x uvw.y to:ostream

)
)

Format tag3 to:ostream;

– Polygon

if meshObj.NumFaces > 0 then
(
for i = 1 to meshObj.numFaces do
(
poly = GetFace meshObj i
tvert = GetTVFace meshObj i
if swapNormal.checked then Format faceFormat (poly.x as integer -1) (poly.z as integer -1) (poly.y as integer -1) (tvert.x as integer -1) (tvert.z as integer -1) (tvert.y as integer -1) to:ostream
else Format faceFormat (poly.x as integer -1) (poly.y as integer -1) (poly.z as integer -1) (tvert.x as integer -1) (tvert.y as integer -1) (tvert.z as integer -1) to:ostream
)
)

)
————————————————————————————-
– This function is called once per node in the scene.
– A node in Max may be all sorts of things. We are only interested in geometry.
function ExportNode node =
(
– Create node and export class specific data
if SuperClassOf node == GeometryClass and ClassOf node == Editable_mesh then
ExportMesh node node.name
else if SuperClassOf node == GeometryClass then
(
– Build a mesh out of this object and save it
local temp = copy node
convertToMesh temp
ExportMesh temp node.name
delete temp
)
else — Not geometry.. could be a camera, light, etc.
return false

return true
)

————————————————————————————-
–export de la class
function ExportXMLFile =
(

header = case cbEngine.selected of
(
papervision2: papervision2HeaderFormat

)

tag1 = case cbEngine.selected of
(
papervision2: tag1Format

)

tag4 = case cbEngine.selected of
(
papervision2: tag4Format

)

footer = case cbEngine.selected of
(
papervision2: papervision2FooterFormat
)

– Header de la class
Format header classname_txt.text classname_txt.text to:ostream

Format tag1 to:ostream

for node in selection do
ExportNode node

Format tag4 to:ostream

Format tag1 to:ostream

for node in selection do
ExportNode node

Format tag4 to:ostream

Format footer to:ostream

)
————————————————————————————-
– Open an prepare a file handle for writing.
function GetSaveFileStream =
(
fname = GetSaveFileName filename:classname_txt.text types:”xml (*.xml)|*.xml|All Files(*.*)|*.*|”
if fname == undefined then
return undefined

ostream = CreateFile fname
if ostream == undefined then
(
MessageBox “Couldn’t open file for writing !”
return undefined
)

return ostream
)
————————————————————————————-
– This is the function called when the user activates the utility by pressing on
– the export button. It opens the file and calls the export routine.
on btn_export pressed do
(
ostream = GetSaveFileStream()
if ostream != undefined then
(
ExportXMLFile()
close ostream
)
)

)

XML 3DSMax Primitive

package org.papervision3d.objects.primitives {

import flash.events.Event;
import flash.net.*;

import org.papervision3d.core.*;
import org.papervision3d.core.geom.*;
import org.papervision3d.core.geom.TriangleMesh3D;
import org.papervision3d.core.geom.renderables.Triangle3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.core.math.NumberUV;
import org.papervision3d.core.proto.*;

public class XML3DSPrimitive extends TriangleMesh3D {

public var verts :Array;
public var faceAr:Array;
public var uv :Array;

public var myFileGet:String;
public var xmlReq:URLRequest;
public var xmlLoader:URLLoader;
public var myData_xml:XML;
public var iFacNum:Number;

public function XML3DSPrimitive(myFileGet:String, material :MaterialObject3D=null, initObject:Object=null ) {
super( material, new Array(), new Array(),null, initObject );
//ve = this.geometry.vertices;
//fa = this.geometry.faces;

verts = this.geometry.vertices;
faceAr= this.geometry.faces;
uv = new Array();

var externalXML:XML;
var loader:URLLoader = new URLLoader();
var request:URLRequest = new URLRequest(myFileGet);

loader.load(request);

loader.addEventListener(Event.COMPLETE, onComplete);

function onComplete(event:Event):void
{
var loader:URLLoader = event.target as URLLoader;
if (loader != null)
{
externalXML = new XML(loader.data);

buildCylinder(externalXML);

}
else
{
trace(“loader is not a URLLoader!”);
}
}

this.geometry.ready = true;
}

private function buildCylinder(myData_xml:XML):void
{

verts = this.geometry.vertices;
faceAr= this.geometry.faces;
uv = new Array();

var vsplit:Array=myData_xml.primData[1].vertex.split(“,”);
var fsplit:Array=myData_xml.primData[1].faces.split(“,”);
var usplit:Array=myData_xml.primData[1].uvData.split(“,”);
//var nsplit:Array=myData_xml.primData[1].normals.split(“,”);

var j:int;
for (j=0; j<(vsplit.length-1)/3; j++) {

v(vsplit[j*3],vsplit[j*3+1],vsplit[j*3+2]);

}

//if statement for material or no material

var k:int;
for (k=0;k<(usplit.length-1)/2; k++) {

uv.push(new NumberUV(usplit[2*k], usplit[2*k+1]));

trace(usplit[2*k]+ “, “+ usplit[2*k+1]);

}

var n:int;
for (n=0;n<(fsplit.length-1)/6; n++) {

f(fsplit[n*6], fsplit[n*6+1], fsplit[n*6+2], fsplit[n*6+3], fsplit[n*6+4], fsplit[n*6+5]);

}

}

public function v(x:Number, y:Number, z:Number):void {
verts.push(new Vertex3D(x, y, z));
}

//Function for material on prims
public function f(vertexIndex1:Number, vertexIndex2:Number, vertexIndex3:Number, uv0:Number, uv1:Number, uv2:Number):void {

faceAr.push(new Triangle3D(this, [verts[vertexIndex1], verts[vertexIndex2], verts[vertexIndex3]], null, [ uv[uv0],uv[uv1],uv[uv2] ] ));

}

}
}
//And the glory of the LORD shall be revealed, and all flesh shall see it together: for the mouth of the LORD hath spoken it

About these ads

9 Responses to New 3DSMax XML Exporter & PV3D XML Primitive

  1. orgicus says:

    hi mike,

    how can I use your exporter with flash’s drawTrianles() ?

  2. Pradeek says:

    In the exported XML file, there seems to be the vertices data,faces data and the texture vertices data.But I don’t see the texture faces data. Am I missing something here?

  3. Mike Lively says:

    The texture is pulled in as an external bitmap from the images folder – just the same way it’s done in Papervision3D. Got to run. Let me know if you find it…

  4. Pradeek says:

    What I wanted to know was there are 4 different sets of data for rendering a 3D object – Vertices of a 3D object, Faces (or Indices), Texture Co-ordinates(uvData) and Faces for the textures(Integer values). I can’t seem to find the faces(or indices) for textures.These four sets of data are present in all major formats of 3D objects. I am working on a parser for Flash Player 10 now and ran across these data. I don’t seem to find them here though.Please clarify.

  5. Pradeek says:

    Another question.Sorry to bug you but you have two primData in your XML file but you are using only the second set and leaving out the first.Why is that?

    • Mike Lively says:

      These are all good questions and I appreciate you asking!

      XML: I had a few issues with Flex importing xml data – unless it sees more than one data item http services fails – so thus the double set of data – the solutions to XML problems like this can be found in Sas Jacobs book titled XML and E4x.

      Data Needed: Papervision3D and CS4 only need 3 data sets at minimum (plus an image) to create a 3D object. Vertices, UVData, and Triangle Face Numbers. If you look closely at the AS3 Geom Class code that I used to create this from, this is the only data they use – so that’s all I used (First Rule of Hacking Fast – make it look the same).

      But in addition, I have created two 3DSMax XML exporters (one that outputs normals and one that doesn’t). Will post that soon…

      Now in the Blender parser that I created, I did use the normal data (since that’s what the Blender class exporter used), but for the 3DSMax primitive I just followed what had already been done. It worked.

      For the CS4 parsers I have built they don’t use the normal data in either case (Blender or 3DSMax) and that was where I was going anyway…

      I’ll try to get some type of video up on this soon…but please feel free to ask as many questions as you want. And I will help you as much as I can.

      Good luck on your parsers…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: