/* export_cmod_plugin.a8s */ #plugin("object", "export", "Celestia", ".cmod_mesh"); #file($output, "text"); #return($result); string $ecsVersion; $ecsVersion = "v0.23 beta4"; /* edits: * * 002 variable version, modified headers & summary * 003 multiple materials per mesh * 004 scale colors * 005 skip shapes with 0 points * 006 fix bugs in 003 * 007 fix texture inversion * 008 display original material values, too * 009 invert specular power * 010 put parts counts at very end so tail shows them * 011 count vertices, show largest & avg mesh * 012 elminate extra write \n statements * 013 add + to output summary * 014 count triangles/shape * 015 count materials/shape * 016 -- omitted: normals left in * 017 undo 009: don't invert specularity * 018 use roughness for specularity, not spec weight * 019 print fp averages * 020 fix typos in 018 * 021 translate bumpmap filespec to normalmap: if you use it, you need it. * 022 edit for material texture structure changes in Anim8or v0.95b4 * 023 transform normals to world coordinates */ /* * * This script is a simple .cmod format export plugin. * adapted from Steve's .c and .obj exporters by s.ball sep06 * * creates the files .cmod_material and .cmod_mesh * which the user must concatenate. */ /* * Export scripts are not allowed to modify a project. Any * attempt to change a model is an error. * * Copyright 2006 R. Steven Glanville. Permission granted for * modification and use, including commercial use. Source * distribution must include this copyright. * */ /* * Directives to install script as export plugin in Object editor. * * Required directives: * * #plugin(object", "export", , ); * #file(, [ "text" | "binary" ]); * #return(); * * - string, including initial '.' * - comment for export file open dialog. * - name of file variable. Must be declared later. * - int variable with result. 1 = success, 0 = failure. * */ file $output; int $result; object $curObject; $curObject = project.curObject; shape $shape, $shapes[1], $childShapes[1]; tridata $tdata; int $numPoints, $numNormals, $numTexCoords, $numTriangles, $numMaterials; int $ii,$ij; point3 $point, $pointn, $normal; point2 $uv; float4x4 $transformMat, $normalTransformMat; int $numMaterial, $prevMaterial, $intspecMaterial; float $flspecMaterial; file $materialFile; string $mFileName, $matName; material $material; /* edit 022: material structure changed */ string $texName; texture $matTexture; string $names[0]; int $numTri; float $vRed, $vGreen, $vBlue, $vAvg, $vNum, $vDenom; int $totalVertices, $totalTriangles, $numEmpty, $numMatEmpty; int $maxVertices, $maxTriangles, $maxMaterials; $totalVertices = 0; $totalTriangles = 0; $numEmpty = 0; $numMatEmpty = 0; $maxVertices = 0; $maxTriangles = 0; $maxMaterials=0; /************************************************************ * Anim8or groups materials with meshes * Celestia requires all materials to be defined at the start * * Simulate this by writing a materials file (.cmod_material) * which the user will have to prepend to the .cmod_mesh file ************************************************************ */ /********************** * Create material file * material file must be initial part of final cmod file */ $mFileName = $output.GetDir() + $output.GetRoot() + "." + "cmod_material"; $materialFile.open($mFileName, "w"); /* keep track of number of materials that have been defined */ $prevMaterial = 0; $numMaterial = 0; /******************** * cmod header comments for cmod_material */ $materialFile.print("#celmodel__ascii\n\n"); $materialFile.print("#\n"); $materialFile.print("# Material file \"%s\"\n", $materialFile.GetName()); $materialFile.print("#\n"); $materialFile.print("# Written by Anim8or %s using",version); $materialFile.print(" export_cmod_plugin.a8s %s\n", $ecsVersion); $materialFile.print("# CMOD export script by S.Ball, Sept. 2006 et seq.\n"); $materialFile.print("#\n"); $materialFile.print("# Meshes are in %s\n", $output.GetName()); $materialFile.print("# Prepend this file to that one.\n\n"); $materialFile.print("#\n"); $materialFile.print("# Object \"%s\":\n", $curObject.name); $materialFile.print("#\n\n"); /******************** * header comments for cmod_mesh */ $materialFile.print("#\n"); $output.print("# Mesh file \"%s\"\n", $output.GetName()); $output.print("#\n"); $output.print("# Written by Anim8or %s using",version); $output.print(" export_cmod_plugin.a8s %s\n", $ecsVersion); $output.print("# CMOD export script by S.Ball, Sept. 2006 et seq.\n"); $output.print("#\n"); $output.print("# Materials are in %s\n",$materialFile.GetName()); $output.print("# Prepend that file to this one.\n\n"); $output.print("#\n"); $output.print("# Object \"%s\":\n", $curObject.name); $output.print("#\n\n"); /* This comment is by steve. I don't understand all that it implies ...seb */ /* The shapes in $shapes are processed in reverse order so */ /* reverse the array of values that GetShapes returns: */ /* maybe I could extract the materials during this push/pop * but they'd be in reverse order :-( ...seb */ $curObject.GetShapes($childShapes); $shapes.size = 0; while ($childShapes.size > 0) $shapes.push($childShapes.pop()); while ($shapes.size > 0) { $shape = $shapes.pop(); /* If $shape is a group push child shapes onto stack: */ if ($shape.GetKind() == SHAPE_KIND_GROUP) { $shape.GetShapes($childShapes); $output.print("# Group \"%s\" has %d children:\n", $shape.name, $childShapes.size); while ($childShapes.size > 0) { $shapes.push($childShapes.pop()); } } else if ($shape.GetKind() == SHAPE_KIND_PATH || $shape.GetKind() == SHAPE_KIND_MODIFIER || $shape.GetKind() == SHAPE_KIND_TEXT) { /* No 3D mesh to output. */ } else { /****************** * define an object */ $tdata = $shape.GetTriangleData(); $transformMat = $shape.GetGlobalTransform(); $normalTransformMat = $shape.GetGlobalNormalTransform(); /******************* * declare materials */ /* note: * Anim8or supports multiple materials per mesh * * Starting with v0.003, this script attempts to emit multiple trilists, * one for each material. */ /* prevMaterial used to calculate current material offset for lists */ $prevMaterial = $prevMaterial+$numMaterial; $numMaterials = $tdata.GetNumMaterials(); $numMaterial = $numMaterials; if ($numMaterial > $maxMaterials){$maxMaterials=$numMaterial;} $materialFile.print("\n# %s has %d material(s)\n\n", $shape.name, $numMaterials); for $ii = 0 to $numMaterials - 1 do { $material = $tdata.GetMaterial($ii); $materialFile.print("material # %d\n", $prevMaterial+$ii); /********************************* * bug workaround & optimization: * write no other info if this material is completely transparent */ if ($material.alpha == 0) { $materialFile.print(" opacity %.3g \n", $material.alpha); } else { $materialFile.print("\n"); /************ * diffuse surface texture * * alpha channel is opacity map in Celestia * Anim8or uses a separate opacity image, which this script ignores * * Anim8or does not directly support alpha channel opacity * (e.g. It has no png image support) */ /* patch 008: display original parameters, too */ $materialFile.print("# diffuse %.3g %.3g %.3g\n", $material.diffuse); $materialFile.print("# diffuse weight %.3g # Kd\n", $material.Kd); /* patch 004: apply Anim8or's color scale factor now: * Celestia won't */ $vRed = $material.diffuse.x*$material.Kd; $vGreen = $material.diffuse.y*$material.Kd; $vBlue = $material.diffuse.z*$material.Kd; $materialFile.print(" diffuse %.3g %.3g %.3g\n", $vRed, $vGreen,$vBlue); $materialFile.print(" opacity %.3g \n", $material.alpha); /* edit 022: revised texture name access */ $matTexture = $material.GetTexture(TEXTURE_DIFFUSE); $texName = $matTexture.name; if ( $texName != "" ) $materialFile.print(" texture0 \"%s.*\"\n\n", $texName); /************ * specular surface texture */ /* patch 008: display original parameters, too */ $materialFile.print("# specular %.3g %.3g %.3g\n", $material.specular); $materialFile.print("# specular weight %.3g # Ks\n", $material.Ks); /* 020 delete spurious specular output line */ /* patch 004: apply Anim8or's color scale factor now: * Celestia won't */ $vRed = $material.specular.x*$material.Ks; $vGreen = $material.specular.y*$material.Ks; $vBlue = $material.specular.z*$material.Ks; $materialFile.print(" specular %.3g %.3g %.3g\n", $vRed, $vGreen,$vBlue); /* patch 018: use roughness = PhongSize for specpower (range 1-100)*/ $intspecMaterial = (($material.roughness)*2.55); $materialFile.print(" specpower %d # (roughness)*2.55\n", $intspecMaterial); /* edit 022: revised texture name access */ $matTexture = $material.GetTexture(TEXTURE_SPECULAR); $texName = $matTexture.name; if ( $texName != "" ) $materialFile.print(" specularmap \"%s.*\"\n\n", $texName); /************ * emissive surface texture * * Celestia uses its alpha channel as its opacity to see * through an emissive surface texture. e.g. to see though * a "night lights" texture for the Earth to view * the underlying diffuse texture map. * * Anim8or does not support alpha channel opacity * (e.g. It has no png image support) */ /* patch 008: display original parameters, too */ $materialFile.print("# emissive %.3g %.3g %.3g\n", $material.emissive); $materialFile.print("# emissive weight %.3g # Ke\n", $material.Ke); $vRed = $material.emissive.x*$material.Ke; $vGreen = $material.emissive.y*$material.Ke; $vBlue = $material.emissive.z*$material.Ke; $materialFile.print(" emissive %.3g %.3g %.3g # product\n", $vRed, $vGreen,$vBlue); /* edit 022: revised texture name access */ $matTexture = $material.GetTexture(TEXTURE_EMISSIVE); $texName = $matTexture.name; if ( $texName != "" ) $materialFile.print(" emissivemap \"%s.*\"\n\n", $texName); /************ * bumpmap surface texture * assume that if you're using a bumpmap, * you know how to generate the equivalent normalmap * edit 021 */ /* edit 022: revised texture name access */ $matTexture = $material.GetTexture(TEXTURE_BUMPMAP); $texName = $matTexture.name; if ( $texName != "" ) $materialFile.print(" normalmap \"%s.*\"\n\n", $texName); } /* end of opacity !=0 else */ $materialFile.print("end_material\n\n"); } /* * Animator supports other surface textures * which Celestia does not * i.e. ambient & environment maps * so they aren't emitted. */ /*************************** * write this shape's mesh and vertices to .cmod_mesh file * * include vertex position, normal vector and texture coordinates * in each line of vertices */ /* patch 005: don't emit empty shapes */ $numPoints = $tdata.GetNumPoints(); if ( $numPoints <= 0 ) { $output.print("# Shape %s has 0 points: skipping.\n", $shape.name); $numEmpty = $numEmpty +1; } else { $output.print("\n mesh # %s\n\n", $shape.name); $output.print("vertexdesc\n"); $output.print(" position f3 normal f3 texcoord0 f2\n"); $output.print("end_vertexdesc\n\n"); $output.print("vertices %d\n",$numPoints); $totalVertices = $totalVertices + $numPoints; if ($numPoints > $maxVertices) {$maxVertices = $numPoints;} for $ii = 0 to $numPoints - 1 do { $point = $tdata.GetPoint($ii); $point = $transformMat.Project($point); $pointn = $tdata.GetNormal($ii); /* 023 transform normals to world coords */ $pointn = $normalTransformMat.Project($pointn); $uv = $tdata.GetTexCoord($ii); /* 007 textures are inverted */ $uv.y = 1.0 - $uv.y; $output.print("%.7g %.7g %.7g %.7g %.7g %.7g %.7g %.7g \n", $point, $pointn, $uv); } /**************** * draw triangles */ /* note: * Anim8or supports multiple materials per mesh * * Starting with v0.03, this script attempts to emit multiple trilists, * one for each material. */ $numTriangles = $tdata.GetNumTriangles(); if ($numTriangles > $maxTriangles) {$maxTriangles=$numTriangles;} $output.print("\n"); /* * 003: emit separate trilists the hard way: * count all triangles with iith material, then * list all triangles with iith material * */ /* * patch 006: was using wrong index; skip if none in trilist */ for $ii = 0 to $numMaterials - 1 do { /* count triangles using this material */ $numTri=0; for $ij = 0 to $numTriangles - 1 do { if ( $tdata.GetMatIndex($ij)==$ii) { $numTri=$numTri+1; } } if ( $numTri <= 0 ) { $output.print("\n# material %d has 0 points: skipping.\n", $ii+$prevMaterial); $numMatEmpty = $numMatEmpty +1; } else { $output.print("\n trilist %d %d\n", $ii+$prevMaterial, $numTri*3); for $ij = 0 to $numTriangles - 1 do { if ( $tdata.GetMatIndex($ij)==$ii) { $output.print(" %d %d %d\n", $tdata.GetIndex($ij*3), $tdata.GetIndex($ij*3 + 1), $tdata.GetIndex($ij*3 + 2)); $totalTriangles = $totalTriangles+1; } } } } /* end no points in material */ $names.push($shape.name); $output.print("\n end_mesh\n\n"); } /* end if <=0 points else */ } } /*********************** * list of shapes may be useful for performance estimates */ $output.print("\n###############################################\n"); for $ii = 0 to $names.size - 1 do $output.print("# %s\n", $names[$ii]); $output.print("#\n"); /* 010: parts at very end */ $output.print("# Object %s has\n",$curObject.name); $output.print("# %d materials (+ %d unused); %d vertices, %d triangles\n", $numMaterial+$prevMaterial, $numMatEmpty, $totalVertices, $totalTriangles); $vNum = $totalVertices; $vDenom = $names.size; $vAvg = $vNum/$vDenom; $output.print("# %d Shapes (+ %d with no vertices; max: %d, avg: %.2f,\n", $names.size, $numEmpty, $maxVertices, $vAvg); $vNum = $totalTriangles; $vDenom = $names.size; $vAvg = $vNum/$vDenom; $output.print("# max Triangles/Shape: %d, avg: %.2f)\n", $maxTriangles, $vAvg); $vNum = $numMaterial+$prevMaterial; $vDenom = $names.size; $vAvg = $vNum/$vDenom; $output.print("# max Materials/Shape: %d, avg: %.2f)\n", $maxMaterials, $vAvg); $output.print("###############################################\n\n"); $output.print("#--- End of file \"%s\"\n", $output.GetName()); $materialFile.print("#--- End of file \"%s\"\n", $materialFile.GetName()); $materialFile.close(); $result = 1; /* Report success. */