TweetFollow Us on Twitter

3D For Free Using the Mac's Standard Apps

Volume Number: 19 (2003)
Issue Number: 5
Column Tag: Programming

3D For Free Using the Mac's Standard Apps

Converting raw 3D text files to QuickDraw 3D's 3DMF format using AppleScript

by Tom Djajadiningrat

Summary

This article introduces you to the basics of 3D files in general and the 3D Metafile (3DMF), QuickDraw 3D's native 3D format, in particular. It shows how you can use AppleScript to easily convert a raw 3D text file into a 3DMF readable by the QuickDraw 3D Viewer or any other QuickDraw 3D compatible application. With this knowledge you can make hand-written data or data exported by a spreadsheet or database application suitable for visualisation as standalone 3D models.

Introduction

The combined power of the suite of goodies that comes standard with your Mac is really quite amazing. One such goody is the QuickDraw 3D Viewer. The QuickDraw 3D Viewer makes it possible to do basic viewing of 3D models that are formatted as 3D Metafiles (3DMF), QuickDraw 3D's native file format. Since the QuickDraw 3D Viewer is supported by SimpleText, which comes standard with a Mac, files written in 3DMF format can be viewed on any PowerMacintosh running QuickDraw 3D. Maybe you would like to visualise your spreadsheet or database data as standalone 3D models or perhaps you would like to write your own low-polygon count models by hand rather than use a 3D modeller. To achieve this you could make the spreadsheet or database application export complete 3DMFs. Often though it is quite awkward to get the formatting right. And though it is possible to write complete 3DMFs by hand, you of course want to reduce repetitive chores, such as adding brackets, to save time and reduce errors. Here we take the approach of writing minimalistic 3D files which are then post-processed into 3DMF. So how do we do this post-processing? Enter two other Macintosh standard goodies: AppleScript and the ScriptMaker application. Although it is seldom used for this express purpose, AppleScript is actually quite good at reading and writing text files. Another nice feature of AppleScript is the ease with which we can make a droplet: a script which performs certain actions on documents which are dropped on to it. This allows us to convert multiple files with the ease of drag and drop.

QuickDraw 3D's 3DMF Format

3D models can be described using vertices and faces. A vertex is a point in 3D space described by x, y and z co-ordinates. A face is a polygon of which each corner point is a vertex. To prevent unpredictable results a face should be planar: all its vertices should lie in one plane. Figure 1 shows a technical sketch of a table which consists of a square face (the table top) and eight triangular faces (the legs). Figure 2 shows the corresponding 3D model in the QuickDraw 3D Viewer.


Figure 1: Technical sketch of a table


Figure 2: The 3D model in the QuickDraw 3D Viewer

Listing 1 shows a 3DMF file for this table. For our example we use the QuickDraw 3D mesh geometry, which is probably the easiest to understand way to describe a 3D model. The hash sign (#) marks a comment in a 3DMF. Everything on a line after a hash is ignored. The 3DMF here consists of two parts. The first part is the header, 3DMetafile ( 1 6 Normal toc> ). The second part is formed by a container which contains a single mesh object. Let's look at the header first. The header, formed by the first line of the file, tells us that we have a 3D Metafile for QuickDraw 3D 1.6. The major version number is 1 and the minor version number is 6. The third field, Normal, is the type of 3D Metafile. The fourth field of the header, toc>, is a file pointer to a table of contents which is non-existent in this 3DMF. Don't worry about the type of 3D Metafile and the table of contents parameters. You do not need to know their exact meaning to understand this article and later we explain where to read up on the full 3DMF specification.

Listing 1: a 3DMF file

table.3df
3DMetafile ( 1 6 Normal toc> )
#This is a table with a square top and four legs
Container (
   Mesh(
      20 #number of vertices
      1 1.5 -1 #0
      -1 1.5 -1 #1
      -1 1.5 1 #2
      1 1.5 1 #3
      1 0 1 #4
      0.9 1.5 0.6 #5
      0.9 1.5 0.9 #6
      0.6 1.5 0.9 #7
      1 0 -1 #8
      0.6 1.5 -0.9 #9
      0.9 1.5 -0.9 #10
      0.9 1.5 -0.6 #11
      -1 0 -1 #12
      -0.9 1.5 -0.6 #13
      -0.9 1.5 -0.9 #14
      -0.6 1.5 -0.9 #15
      -1 0 1 #16
      -0.6 1.5 0.9 #17
      -0.9 1.5 0.9 #18
      -0.9 1.5 0.6 #19
      9 #number of faces
      0 #number of contours
      4 0 1 2 3 #0
      3 4 5 6 #1
      3 4 6 7 #2
      3 8 9 10 #3
      3 8 10 11 #4
      3 12 13 14 #5
      3 12 14 15 #6
      3 16 17 18 #7
      3 16 18 19 #8
   )
   Container (
      AttributeSet ( )
      DiffuseColor (1.0 1.0 0.0) #r g b
   )
)

Now let's look at what takes place within the bracket of the container. The container contains a mesh and another container with a diffuse colour attribute in it. First the mesh description lists the number of vertices involved. In our case there are twenty vertices. What follows is a list of vertices which QuickDraw 3D sees as numbered from 0 to 19. Each vertex is described by three co-ordinates. For example, vertex number 0 has the co-ordinates x=1, y=1.5 and z=-1. Next is the number of faces, eight in this case, followed by the number of contours. A contour is a polygonal hole in a face. Since there are no holes in our table the number of contours is 0. The mesh description finishes with the eight faces. The first number of a face description is the number of vertices involved in the face, the remaining numbers specify these vertices. For example, the square table top is face number 0, which has four corners formed by the vertices 0, 1, 2 and 3. Finally, there is a diffuse colour attribute which applies to the whole mesh. It is specified in red, green and blue components, each ranging from 0.0 to 1.0.

The raw 3D file

It is very important to carefully consider what the raw 3D file should look like before you start writing a script to convert it to 3DMF. If you yourself export the file or write it by hand you are of course in complete control. If someone else does this for you--say an expert in low-polygon count models--you want to make sure this person formats the file in such a way that makes life as easy on you as possible. On the other hand, you may wish to respect this person's way of formatting files. Listing 2 shows an example of what a raw 3D file might look like. There are some differences between this raw 3D file and the 3DMF file we just discussed which make conversion pretty awkward, but it is understandable why the formatting of the raw 3D file is convenient for the person who writes the file by hand. Because of the vertex and face labels on each line it is immediately clear whether one is in the vertices or faces section when quickly scrolling through the file. Because of the commas it is possible to put spaces between a minus sign and a co-ordinate so that the minus signs line up vertically. This makes it easier to spot co-ordinates with the wrong sign. Both vertices and faces start rather than end with their index number so that the indices line up too. A face line does not include the number of vertices which saves work and reduces errors. Careful deliberation with all involved can safe much work and frustration.

Listing 2: A raw 3D file

table.txt
Description:This is a table with a square top and four legs
Vertices:20
Vertex: 0,  1, 1.5, -1
Vertex: 1, -1, 1.5, -1
Vertex: 2, -1, 1.5,  1
Vertex: 3,  1, 1.5,  1
Vertex: 4, 1,   0,   1
Vertex: 5, 0.9, 1.5, 0.6
Vertex: 6, 0.9, 1.5, 0.9
Vertex: 7, 0.6, 1.5, 0.9
Vertex: 8, 1,   0,   -1
Vertex: 9, 0.6, 1.5, -0.9
Vertex:10, 0.9, 1.5, -0.9
Vertex:11, 0.9, 1.5, -0.6
Vertex:12, -1,   0,   -1
Vertex:13, -0.9, 1.5, -0.6
Vertex:14, -0.9, 1.5, -0.9
Vertex:15, -0.6, 1.5, -0.9
Vertex:16, -1,   0,   1
Vertex:17, -0.6, 1.5, 0.9
Vertex:18, -0.9, 1.5, 0.9
Vertex:19, -0.9, 1.5, 0.6
Faces: 9
Face: 0, 0, 1, 2, 3
Face: 1, 4, 5, 6
Face: 2, 4, 6, 7
Face: 3, 8, 9, 10
Face: 4, 8, 10, 11
Face: 5, 12, 13, 14
Face: 6, 12, 14, 15
Face: 7, 16, 17, 18
Face: 8, 16, 18, 19
DiffuseColor:1.0 1.0 0.0

AppleScript

The next thing we do is to write an AppleScript to convert the raw 3D text file to 3DMF. In the folder <your harddisk>:Apple Extras:AppleScript you find the application ScriptMaker which you can use for writing AppleScripts. First we discuss the structure of our script, then we fill in the details.

The structure

Listing 3 shows the structure of our conversion script Convert to 3DMF. We use the on open construct so that we can use the script as a droplet. The file specifications of the files dropped onto the droplet appear in the list inDocList. We traverse the list inDocList through a repeat command and act on each file in turn. First we do the most basic of error checking. Within a try statement we check whether the file is of type 'TEXT'. If it is not, we bail out by throwing an error which leads to execution of the on error portion of the try statement. If it is a text file we call the parse handler. Within the parse handler we again have a try statement in which we try to extract the relevant information from the raw 3D text file and format it according to 3DMF standards. If all goes well we call the writeResultFile handler with as arguments the file specification of the raw 3D text file and the text of the 3DMF file to write. Within the writeResultFile handler we try to create a text file within the same directory as the raw 3D text file and the same name, except for the extension which we make 3df. Now that we have some idea of the structure of our script, let's turn our attention to the parse handler.

Listing 3: The structure of Convert to 3DMF

on open (inDocList)
      
   -- traverse the list of files that were dropped on the droplet
   repeat with theFilePath in inDocList
      
      try
         tell application "Finder"
            
            --minimal error checking:
            --check whether the file is of type text.
            --if it isn't, throw an error
            if (get file type of file theFilePath)<>"TEXT" then
               error "This is not a text file."
            end if
            
         end tell
         
         --if we get here the file type is "TEXT"
         --and we parse the file.
         parse(theFilePath)
         
      on error inErrorText
         --handle any errors that may occur.
         display dialog ("An error has occurred:" -
                                 & inErrorText)
      end try
      
   end repeat
      
end open
on parse(inFilePath)
   try
      --Here we try to read the raw 3D file,
      --extract the relevant information,
      --and format it into 3DMF format.
      <code omitted>
      --Call a handler to write the result to a file.
      writeResultFile(inFilePath, theOutputString)
      on error inErrorText
         display dialog ("An error has occurred:" & -
                                 inErrorText)
   end try
end parse
on writeResultFile(inFilePath, inOutputString)
   try
      --Here we try to create a new text file
      --in the same directory as the raw 3D file,
      --only with the extension '3df' instead of 'txt'.
      --and write our formatted result to it.
      <code omitted>
   on error inErrorText
      display dialog ("An error has occurred:" & inErrorText)
end writeResultFile

The Parse handler

For the code of the parse handler please refer to Listing 4. First we read all of the raw 3D text file into a list called theLineList using returns as delimiters. This means that each element of the list theLineList contains one line of the raw 3D text file. We then traverse this list of lines using a repeat statement and look at the start of each line. There are number of possibilities.

If a line starts with "description" we are dealing with the first line of the raw 3D text file which holds a description. Although this may seem superfluous as the file has a descriptive name, it can be a convenient location to store a long (>32 characters) description of the 3D object in the file. We ignore the word "description" and the colon and store the rest of the line in the string variable theDescription.

If a line starts with "vertices" we have come across the line which signals the start of the vertices description and which tells us the number of vertices in the file. We simply ignore this line. Instead of grabbing the number of vertices from this line we will count the vertices as we encounter them.

If a line starts with "faces" we are dealing with the line which signals the start of the faces description and which tells us the number of faces in the file. Again we ignore this line as we will also count the faces as we encounter them.

If a line starts with "vertex" we have a line with a vertex description. We ignore everything on this line up to and including the first comma which means that we eliminate the word "vertex" and the number of the vertex. The remainder of a vertex description line needs some filtering. We traverse this remainder looking at each character in turn. We replace the commas by spaces and eliminate superfluous spaces. For this last action we need to keep track of the last character using the variable theLastChar. For example, if there is a space between a minus sign and a comma we eliminate that space. Also, if there are multiple consecutive spaces we reduce them to a single space. The characters we wish to keep are appended to the variable theVerticesString. After each line we add a comment with the index number of the vertex. By the time we have finished parsing all the vertices theVerticesString holds nice, clean versions of all the vertices in the file.

If a line starts with "face" we are dealing with a face description. The filtering of a face description is pretty similar to that of a vertex description. Again we ignore everything on this line up to and including the first comma. This means that we have got rid of the word "face" and the number of vertices involved in the face. Unlike with a vertex line, we need not worry about decimal places. What we do need to do is count the number of vertices involved in a face. If a digit is preceded by a space or a comma we increment the variable theNumberOfVerticesInFace. The characters we wish to keep are appended to the variable theFacesString. After each line we add a comment with the index number of the face. By the time we have finished parsing the faces theFacesString holds a nicely formatted version of all the faces in the file.

If a line starts with "DiffuseColor" we are dealing with the colour specification. We ignore the word "DiffuseColor" and the colon and store the rest of the line in the string variable theColor.

If a line does not conform to any of the previous options, then we either have an empty line or a line that we do not currently support. These lines are simply ignored.

In its current form, the script does not cater for holes. The number of contours is simply set to 0.

The rest of the parse handler assembles the various parts into a string variable theOutputStr. All we need to do now is write our freshly formatted string theOutputStr to a file through the handler writeResultFile.

Listing 4: Parsing and formatting

parse
on parse(inFilePath)
   
   --initialise the number of vertices
   set theNumOfVerts to 0
   
   --initialise the number of faces
   set theNumOfFaces to 0
   
   --the string with vertex descriptions
   set theVertsStr to ""
   
   --the string with face descriptions
   set theFacesStr to ""
   
   try
      --Copy the content of the text file into a list.
      --Use return as the delimiter.
      --This makes each list element one line.
      copy (read inFilePath as list using delimiter return)-
               to theLineList
      
      -- Traverse the list of lines.
      repeat with i from -
         1 to the number of items in theLineList
         
         set theLine to item i of theLineList
         
         if theLine contains "Description" then
            --Skip the label "description" and copy the rest.
            set theDescription to characters -
               ((the offset of ":" in theLine) + 1) -
                  thru (the end of theLine) of theLine as string
            
         else if theLine contains "Vertices" then
            --Signals the start of the vertices list: do nothing
            
         else if theLine contains "Faces" then
            --Signals the start of the faces list: do nothing.
            
         else if theLine contains "Vertex" then
            --This is a vertex description      
            
            --Track the last character we read.
            set theLastChar to ""
            
            --Traverse the line from the comma to the end of the line
            repeat with j from ((the offset of "," in -
                  theLine) + 1) to the number of characters-
                  in theLine
               
               copy character j of theLine to theChar
               
               if "1234567890-." contains theChar then
                  --These are valid characters which we keep
                  set theVertsStr to theVertsStr & theChar
               else if theChar = "," then
                  --We replace commas by spaces
                  set theVertsStr to theVertsStr & " "
               else if theChar = " " then
                  if theLastChar = "" then
                     --Line starts with a space : don't copy the space
                  else if theLastChar = "-" then
                     --space preceded by a minus sign: don't copy the space 
                  else if theLastChar = " " then
                     --Two consecutive spaces: don't copy the " "
                  else if theLastChar = "," then
                     --space preceded by a comma: don't copy the space 
                  else
                     --This is a space we wish to keep.
                     set theVertsStr to theVertsStr & " "
                  end if
                  
               end if
               
               -- Keep track of the last character
               copy theChar to theLastChar
            end repeat
            
            --We append a comment with the vertex number to this line.
            set theVertsStr to theVertsStr & -
               " " & "#" & theNumOfVerts & return
            
            --Increment the number of vertices we have dealt with.
            set theNumOfVerts to theNumOfVerts + 1
            
            
         else if theLine contains "Face" then
            --This is a face description.
            
            set theCurrFaceStr to ""
            set theLastChar to ","
            set theNumOfVertsInFace to 0
            
            --Skip everything upto and including the first comma.
            --Traverse the line from the comma to the end of the line
            repeat with j from ((the offset of "," in -
                                 theLine) + 1) to -
                                 the number of characters in theLine
               
               copy character j of theLine to theChar
               
               if "1234567890" contains theChar then
                  --These are valid characters which we keep
                  copy theCurrFaceStr & theChar to -
                           theCurrFaceStr
                  
                  --If this valid character is preceded by a comma or a space.
                  --then it must be a new vertex number.
                  if theLastChar = "," or theLastChar = " " then
                     copy theNumOfVertsInFace + 1 to -
                        theNumOfVertsInFace
                  end if
                  
               else if theChar = "," then
                  --We replace commas by spaces
                  set theCurrFaceStr to theCurrFaceStr & " "
                  
               else if theChar = " " then
                  
                  if theLastChar = "" then
                     --Line starts with a space: don't copy the space
                  else if theLastChar = "-" then
                     --" " preceeded by a minus sign: don't copy the space
                  else if theLastChar = " " then
                     --Two consecutive space s: don't copy the " "
                  else if theLastChar = "," then
                     --space preceded by a comma: don't copy the space 
                  else
                     --This is a space we wish to keep.
                     set theCurrFaceStr to theCurrFaceStr & " "
                  end if
                  
               end if
               
               --Keep track of the last character
               set theLastChar to theChar
               
            end repeat
            
            --Build a line with a face description.
            set theCurrFaceStr to theNumOfVertsInFace & -
               " " & theCurrFaceStr & " " & "#" & theNumOfFaces
            
            --Build the description of all the faces.
            set theFacesStr to theFacesStr & -
               theCurrFaceStr & return
            
            --Increment the number of faces.
            set theNumOfFaces to theNumOfFaces + 1
            
         else if (item i of theLineList) -
            contains "DiffuseColor" then
            
            set theColor to characters -
               ((the offset of ":" in theLine) + 1) -
                  thru (the end of theLine) of theLine as string
            
         else
            --Probably an empty line or something unsupported.
            
         end if
         
      end repeat
      
      --There are no holes in our model so the number of contours is 0.
      set theNumOfContours to 0
      
      --The 3DMF header.
      set theHeader to "3DMetafile(1 6 Normal toc>)" & return
      
      --The description
      set theDescription to "#" & theDescription & return
      
      --The attribute set
      set theAttributeSet to "AttributeSet ( )" & return & -
         "DiffuseColor (" & theColor & ")" & " #r g b" &-
         return
      
      --We've got the parts, now assemble the 3DMF file
      --in the string variable theOutputStr.      
      set theOutputStr to -
         theHeader & -
         theDescription & -
         "Container (" & return & "Mesh(" & return & -
         theNumOfVerts & " #num of vertices" & return & -
         theVertsStr & -
         theNumOfFaces & " #num of faces" & return & -
         theNumOfContours & " #num of contours" & return & -
         theFacesStr & -
         ")" & return & -
         "Container (" & return & -
         theAttributeSet & -
         ")" & return & -
         ")"
      
      --Call a handler to write the result to a file.
      writeResultFile(inFilePath, theOutputStr)
      
      --Done!
      beep
      
   on error inErrorText
      display dialog ("An error has occurred: " & -
                              inErrorText)
   end try
   
end parse

The writeResultFile Handler

Now that we have the string theOutputStr all that remains to do is to write it to a text file. This happens in the handler writeResultFile (Listing 5). In the inFilePath parameter we pass the handler the file path of the raw 3D text file. We want to create a finished 3DMF in the same directory and with the same name as the raw 3D text file but with a different file extension, which we want to be .3df. If the raw text file has the file extension .txt we chop it off first. Using the newly created file path theOutfilePathStr we open a file with write permission. We then write our string inOutputStr to this file and set its type and creator. The type is 3DMF and here we use SimpleText as the creator though of course you can change the creator to the application of your choice. Finally, we close the file.

Listing 5: Writing our result to a file

writeResultFile
on writeResultFile(inFilePath, inOutputStr)
   
   --Cast the file path to a string.
   copy inFilePath as string to theFilePathStr
   
   --we write the resulting 3DMF file to the same directory as our original text file.   
   -- if the file path ends with .txt cut off the .txt
   if (theFilePathStr) ends with ".txt" then
      set theOutfilePathStr to (characters 1 thru -
         ((the offset of ".txt" in theFilePathStr) - 1) -
            of theFilePathStr) as string
   else
      set theOutfilePathStr to theFilePathStr
   end if
   
   --Append .3df to the file path in case of use by 'the evil empire'.
   set theOutfilePathStr to theOutfilePathStr & ".3df"
   
   --Create a file to write our result to.
   copy (open for access file theOutfilePathStr -
      with write permission) to theOutFileRef
   
   try
      
      --Write the output string to the file.
      write inOutputStr to theOutFileRef
      
      --Set type and creator.
      tell application "Finder"
         --Set the type to 3DMF, the file type of a 3D Metafile.
         set file type of file theOutfilePathStr to "3DMF"
         
         --We want the file to be openable by SimpleText (creator "ttxt").
         --But of course others are possible:
         --FormZ (creator "VTVS")
         --Geo3D (creator "3Tt*")
         --3DMFOptimizer (creator "OP20")
         set creator type of file theOutfilePathStr to "ttxt"
      end tell
      
      --Close the result file
      close access theOutFileRef
      
   on error inErrorText
      --Close the result file
      close access theOutFileRef
      display dialog ("An error has occurred: "-
                              &inErrorText)
      
   end try
   
end writeResultFile

Finishing and trying out our script

We finish our script by turning it into a droplet. Choose save as from ScriptMaker's file menu, enter the name Convert to 3DMF and choose classic applet from the structure popup menu. Now drop the raw 3D text file table.txt on the Convert to 3DMF droplet. After a brief pause the Mac beeps and a file called table.3df appears in the same directory as table.txt. Double click the file. SimpleText should open and the QuickDraw 3D Viewer should display our table as shown in Figure 2. If you are unsure how to use the controls of the QuickDraw 3D Viewer, check out Balloon Help as it is quite helpful. On some systems the file may not open correctly after a double click in the Finder and shows as a QuickTime movie instead. This can be solved by launching SimpleText and choosing table.3df from the File Open dialog box. As this is of course awkward if you would like to open multiple files, Listing 6 shows a small script for a droplet which correctly opens 3DMF files that are dropped onto it . Note that if you use a non-English language system, AppleScript may ask you to locate your localized copy of SimpleText which is hiding on your harddisk under a different name.

Listing 6: Open3DMF

on open (inDocList)
   
   repeat with theFileSpec in inDocList
      
      tell application "SimpleText"
         open theFileSpec
         activate
      end tell
      
   end repeat
   
end open

Discussion

In this article we showed you how you can get your data into QuickDraw 3D's 3D Metafile format. We barely scratched the surface but now that you are up and walking you can teach yourself how to run using the following pointers.

Quesa: QuickDraw 3D to the future?

Quesa (www.quesa.org) is the astounding open source effort to build a 3D graphics library which offers binary and source level compatibility with QuickDraw 3D. If you are hesitant to support QuickDraw 3D because of Apple's announcement to drop the technology, you may wish to investigate Quesa. Quesa is not only suitable for Mac OS8/9 and MacOSX, it is completely cross-platform with support for Windows and Linux. A port for Be is expected.

Learning more about 3DMF

You can find a well organised collection of links to QuickDraw 3D documentation, including the 3D Metafile 1.5 Reference documentation, at www.quesa.org/other/links.html. A good read on a rainy sunday afternoon, the 3D Metafile documentation tells you in approximately 250 pages everything there is to know about 3DMF. You should pay special attention to these two aspects of the 3D Metafile:

  • Alternative geometry types

    For our file we have used QuickDraw 3D's mesh geometry. The advantage of the mesh is that it is easy to understand and write and that there are many QuickDraw 3D calls for mesh editing. Its big disadvantage is that it is rather inefficient when it comes to rendering. If you are mainly interested in fast rendering you may wish to look into two other geometry types: the polyhedron and the trimesh.

    Another option is to use 3DMF Optimizer by Pangea Software to convert your meshes into trimeshes (www.pangeasoft.net/downloads.html).

  • Binary vs. ASCII

    So far we have been writing 3DMF ASCII files. There is also a binary 3DMF format. The binary format results in smaller files which can be read faster by QuickDraw 3D applications. Luckily, there are conversion utilities around which convert our 3DMF ASCII file to a 3DMF binary file. One such utility is Anatas by Stefan Huber (www.topoi.ch). It converts from ASCII 3DMF to binary 3DMF and vice versa.

Better tools for AppleScript

While ScriptMaker is a great freebie and quite adequate for writing small AppleScripts, you will start to notice its limitations as your scripts start to grow. There is no built-in search and replace, variable watching or step-by-step debugging. If you feel you need a better AppleScript development environment have a look around at Developer Depot (www.developerdepot.com). There you will find some professional AppleScript tools.

Better 3D tools

As the name implies the QuickDraw 3D Viewer is limited to viewing 3DMFs. It does not allow you to edit them. A far more capable freeware application is Geo3D by Stefan Huber which is a better viewer and adds some editing and animation features (www.topoi.ch). If you need a full-blown modelling, rendering and animation freeware package you may wish to consider Strata3D (www.strata.com/html/demos_updates.html). This is a stripped down version of what used to be Strata StudioPro. Finally, most commercial 3D applications edit 3DMFs.

Conclusion

This article showed you how to use AppleScript to take a raw 3D text file and convert it into the 3DMF file format, viewable by any QuickDraw 3D application. Part of the fun was that the technologies and applications that we used--QuickDraw 3D, the QuickDraw 3D Viewer, AppleScript and ScriptMaker--all come standard with your PowerMacintosh. You can of course push the boundaries further by trying to export 3D data from your favourite commercial database or spreadsheet application. Enjoy!


While walking about on the Geneva Motor Show, Tom spotted a Japanese concept car in which a display clearly showed a Macintosh alert box with an error of Type 2. As he does not find the prospect of having to install and configure Linux for car navigation, car audio and engine management particularly appealing, he can't wait for the release of OSX.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Dashlane 6.2027.0 - Password manager and...
Dashlane is an award-winning service that revolutionizes the online experience by replacing the drudgery of everyday transactional processes with convenient, automated simplicity - in other words,... Read more
ffWorks 2.1.5 - Convert multimedia files...
ffWorks, focused on simplicity, brings a fresh approach to the use of FFmpeg, allowing you to create ultra-high-quality movies without the need to write a single line of code on the command-line.... Read more
Dropbox 101.4.434 - Cloud backup and syn...
Dropbox for Mac is a file hosting service that provides cloud storage, file synchronization, personal cloud, and client software. It is a modern workspace that allows you to get to all of your files... Read more
1Password 7.6 - Powerful password manage...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
EyeTV 4.0.0 - Watch and record TV on you...
EyeTV brings a rich TV experience to your Mac. Watch live TV on your Mac. Pause, rewind, and record whenever you want. EyeTV gives you powerful control over what you watch and how you watch it. Put... Read more
Tidy Up 5.3.7 - Find duplicate files and...
Tidy Up is a full-featured duplicate finder and disk-tidiness utility. Features: Supports Lightroom: it is now possible to search and collect duplicates directly in the Lightroom library. Multiple... Read more
Pinegrow 5.97 - Mockup and design web pa...
Pinegrow (was Pinegrow Web Designer) is desktop app that lets you mockup and design webpages faster with multi-page editing, CSS and LESS styling, and smart components for Bootstrap, Foundation,... Read more
BlueStacks 4.210.0 - Run Android applica...
BlueStacks App Player lets you run your Android apps fast and fullscreen on your Mac. Feature comparison chart How to install Bluestacks on your Mac Go to MacUpdate and click the green "Download"... Read more
WhatsApp 2.2027.10 - Desktop client for...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
Art Text 4.0.1 - $29.99
Art Text is graphic design software specifically tuned for lettering, typography, text mockups and various artistic text effects. Supplied with a great variety of ready to use styles and materials,... Read more

Latest Forum Discussions

See All

Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
Steam Link Spotlight - Disco Elysium
Steam Link Spotlight is a feature where we look at PC games that play exceptionally well using the Steam Link app. Our last entry was Signs of the Sojourner Read about how it plays using Steam Link over here. | Read more »
Distract Yourself With These Great Mobil...
There’s a lot going on right now, and I don’t really feel like trying to write some kind of pithy intro for it. All I’ll say is lots of people have been coming together and helping each other in small ways, and I’m choosing to focus on that as I... | Read more »
Pokemon Go's July Community Day wil...
Pokemon Go developers have announced the details concerning the upcoming Gastly Community Day. This particular event was selected by the players of the game after the Gas Pokemon came in second place after a poll that decided which Pokemon would... | Read more »
Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
Detective Di is a point-and-click murder...
Detective Di is a point-and-click murder mystery set in Tang Dynasty-era China. You'll take on the role of China's best-known investigator, Di Renjie, as he solves a series of grisly murders that will ultimately lead him on a collision course with... | Read more »
Dissidia Final Fantasy Opera Omnia is se...
Dissidia Final Fantasy Opera Omnia, one of Square Enix's many popular mobile RPGs, has announced a plethora of in-game events that are set to take place over the summer. This will include several rewards, Free Multi Draws and more. [Read more] | Read more »
Sphaze is a neat-looking puzzler where y...
Sphaze is a neat-looking puzzler where you'll work to guide robots through increasingly elaborate mazes. It's set in a visually distinct world that's equal parts fantasy and sci-fi, and it's finally launched today for iOS and Android devices. [... | Read more »
Apple Arcade is in trouble
Yesterday, Bloomberg reported that Apple is disappointed in the performance of Apple Arcade and will be shifting their approach to the service by focusing on games that can retain subscribers and canceling other upcoming releases that don't fit... | Read more »
Pixel Petz, an inventive platform for de...
Pixel Petz has built up a sizeable player base thanks to its layered, easy-to-understand creative tools and friendly social experience. It revolves around designing, trading, and playing with a unique collection of pixel art pets, and it's out now... | Read more »

Price Scanner via MacPrices.net

B&H Photo offers $200-$300 discounts on A...
B&H Photo has new 16″ MacBook Pros on sale today for $200-$300 off Apple’s MSRP, starting at $2149. Expedited shipping is free to many addresses in the US: – 2019 16″ 2.6GHz 6-Core MacBook Pro... Read more
Clearance 2019 15″ MacBook Pros on sale today...
Amazon-owned Woot is blowing out Apple refurbished, clearance 2019 15″ MacBook Pros starting at only $1579 and up to $950 off Apple’s original MSRP. According to Woot, “These MacBooks are Refurbished... Read more
Apple Refurbished iMac Pros available for $35...
Amazon-owned Woot is selling Apple refurbished 27″ 3.2GHz 8-Core iMac Pros for $3599.99 shipped. That’s $1400 off Apple’s original MSRP for this model. According to Woot, these iMac Pros are “Factory... Read more
Clearance 2019 13″ 2.4GHz/256GB MacBook Pro o...
B&H Photo has dropped their price on the clearance 2019 13″ 2.4GHz/256GB Quad-Core Silver MacBook Pro by $500 off Apple’s original MSRP to a new low of only $1299. Expedited shipping is free to... Read more
$219 Apple AirPods Pro are back at Verizon, s...
Verizon has Apple AirPods Pro on sale again for a limited time for $219.99 on their online store. Their price is $30 off Apple’s MSRP, and it’s the lowest price we’ve seen for AirPods Pro. Available... Read more
Apple’s $779 13″ MacBook Air deal returns to...
Apple has clearance, Certified Refurbished, 2019 13″ MacBook Airs available again starting at $779. Each MacBook features a new outer case, comes with a standard Apple one-year warranty, and is... Read more
$200 13″ MacBook Pro discounts are back at Am...
Amazon has 2020 13″ 2.0GHz MacBook Pros on sale again today for $150-$200 off Apple’s MSRP. Shipping is free. Be sure to purchase the MacBook Pro from Amazon, rather than a third-party seller, and... Read more
Deal Alert! Apple AirPods with Wireless Charg...
Sams Club has Apple AirPods with Wireless Charging Case on sale on their online store for only $149.98 from July 6, 2020 to July 9, 2020. Their price is $50 off Apple’s MSRP, and it’s the lowest... Read more
Xfinity Mobile promo: Apple iPhone XS models...
Take $300 off the purchase of any Apple iPhone XS model at Xfinity Mobile while supplies last. Service plan required: – 64GB iPhone XS: $599.99 save $300 – 256GB iPhone XS: $749.99 save $300 – 512GB... Read more
New July 2020 promo at US Cellular: Switch an...
US Cellular has introduced a new July 2020 deal offering free 64GB Apple iPhone 11 smartphones to customers opening a new line of service. No trade-in required, and discounts are applied via monthly... Read more

Jobs Board

Product Manager, *Apple* Commercial Sales -...
Product Manager, Apple Commercial Sales Austin, TX, US Requisition Number:77652 As an Apple Product Manager for the Commercial Sales team at Insight, you Read more
Physical Therapist Assistant - *Apple* Hill...
Physical Therapist Assistant - Apple Hill Rehab - Full Time Tracking Code 62519 Job Description General Summary: Under the direct supervision of a licensed Physical Read more
Operating Room Assistant, *Apple* Hill Surg...
Operating Room Assistant, Apple Hill Surgical Center - Full Time, Day Shift, Monday - Saturday availability required Tracking Code 62363 Job Description Operating Read more
Perioperative RN - ( *Apple* Hill Surgical C...
Perioperative RN - ( Apple Hill Surgical Center) Tracking Code 60593 Job Description Monday - Friday - Full Time Days Possible Saturdays General Summary: Under the Read more
Product Manager, *Apple* Commercial Sales -...
Product Manager, Apple Commercial Sales Austin, TX, US Requisition Number:77652 As an Apple Product Manager for the Commercial Sales team at Insight, you Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.