LoginLogin
Might make SBS readonly: thread

How to Create a simple 3D Model in Blender and import it into Sim.3D on SmileBasic

Root / Submissions / [.]

seggiepantsCreated:
How to Create a simple 3D Model in Blender and import it into Sim.3D on SmileBasic Prerequisites: - Smile Basic - Sim.3D (key = HEAEP3) http://smilebasicsource.com/page?pid=1053 - PetitModem 1.3 or better recommended - Blender https://www.blender.org/ - An Art Program (I will be using the gimp https://www.gimp.org/) First off, lets use the art program. We want to make some reference images of the thing that we will be modeling. Usually you at minimum want a front, side and top view. Since the back is different than the front, I want one of those for our sample model too. To start, I printed some graph paper using http://www.printfreegraphpaper.com/, then made a few reference images of the space ship we will be making. Together it looked something like this. Unfortunately, I somehow made the side view too short and the front and back too big. Instead of trying to fix things, I just went with it. Graph paper really should have helped me keep everything in scale. When I was done I scanned it and opened it up in my art program. From here we want to chop it up and put the views in different files. If you can find good reference images on the internet you could skip this step. Notice I rotated the side view to how I expect I want to model it. They should look like the following once separated. I saved them as top.png, side.png, front.png, and back.png. That is all we need the art program for unless we do texture mapping, but there is no way SmileBasic has the power for that so we will skip it. It is now time to start up Blender and Open a new Project. I am currently using version 2.79b. This is what my desktop looks like. Before we begin, Blender relies a lot on shortcut keys. I use B for Box Select, A to Select/Deselect All, TAB to switch between object and edit modes, G for Grab/Move, and E for Extrude for the most part and DELETE to delete. You can use the Scroll button on your mouse to move in and out, and holding down the middle mouse button if you have ones lets you point at different things in the scene. One big part is the numeric keypad. You really want to be using Blender on a computer with a numeric keypad. The number keys and numeric keypad don’t do the same thing in Blender. (I think there is a way to use the number keys over your keypad, but I will leave that as an exercise for the user. Frankly I would rather go out and buy a USB numeric keypad if you are on a laptop without one.) Here are the functions on your numeric keypad. If you look at the screen shot above there is a view menu near the bottom left of the screen. If you pull that up you can switch views from there too. You can get the left view by holding down the control button and 3 on the number pad. Likewise you can get the back view with Control and 1 on the number pad. For the most part we will want orthographic view in the front/back, top/bottom, and left/right views and perspective in the free-hand views. What do I mean by Orthographic and Perspective? Perspective is a projection mode that will look like the real world where things diminish in size with distance. This unfortunately makes things hard to line up when modeling. That is why for the most part we will be just using orthographic mode which keeps parallel lines looking parallel unless you just want to check out how things look in normal 3D. Let’s switch to Top view (Num Pad 7), and then Orthographic mode (Num Pad 5). We want to use those views we made earlier so let’s set them up. Hit the tiny plus sign in the upper right of the view to get a window to pop up that looks like the following. Scroll down until you find background image. There is a lot of stuff in there. Click add image and select the file we saved for the top image earlier. Switch to the other views and repeat the same thing. Make sure for each one to open the axis box and have it only show on the axis you want (front/back, left/right, top/bottom). You can flip the axis horizonally or vertially if that helps. There are also some sliders to move it up/down or left/right a big. Now use the scroll wheel on your mouse to zoom in/out to make the image fit the screen. If you have cube let’s delete it so I can show you how to add a new one (it is a tutorial after all) just click on it, hit the DELETE key and choose Yes. So, next lets add a cube to the scene (exactly like the one we just deleted). Click on the create tab in the upper left and choose cube. Now click the G key (for Grab) and move it to the center. You may want to check it in the other views for practice too. If you look in the upper right you should see a tree-view that shows the cube, the lights, and camera. You can double click on the cube to give it a name like say spaceship. If you click on the cube the properties window should now show all of the cube’s many properties. There should be a transform group. Make sure x, y, and z are set to 0.00 for all three. That should make it perfectly centered. We want to do mirror modeling and by default the mirror point is the center. We will leave the default mirror point as-is. Now we want to cut the cube in half to prepare for mirror modeling. Hit [Tab] to go into switch between edit and object mode to go into edit mode. From the left side bar choose Loop Cut and slide from the Tools tab. Hold shift to constrain to the grid and split it vertically (I am in front view) From the bottom toolbar select face mode. Use B (for Box) to draw a box around the faces on the left side of the cube and delete them (DELETE key). You may need to switch views to get all of them. When you are done you should have a hollow half cube. Hit tab to go back into Object mode. Select the cube again using B if not selected. In the properties page under the scene tree. There should be a tab that looks like a wrench click on it to go to the modifiers tab and click add modifier. Then select mirror. We are mirroring across the x axis and merging points. There should now be a mirrored other half of the cube. Lets go back to the main view and go to top mode (make sure we are in orthographic mode still. Our cube isn’t the right size. Lets grab the front face of the cube (deselect All with A, then select the front face in face mode with B – face mode is in the area center bottom of the screen). Grab it with G then use the up down keys to move it up/down without wiggling it in the x or z axis. Repeat the same with the back of the cube. The model now looks something like this: At this point my left and right images don’t seem to line up. Time to flip them horizontally and adjust the reference image positions as best I can, we are just going to go with it since I can’t find a way to stretch them out to fit. I am not really a Blender guru I barely know enough to do this tutorial. Anyway, lets go back to tools then loop cut and slide for every horizontal division in the model. It should look like this afterward. Notice the new divisions. Note I added a couple for the window, but not for the v-point in the window, we will move that point instead. Now we need to grab those edges (from the point/edge/face selector switch to edge mode or just box select the points on the edge) and put them in place. I added another loop cut to prepare for the top fin, but otherwise it now looks like this. Now I want to select some of the side faces to extrude where the wings will connect. (Extrude the faces with the E key) When done, line the edges up again. There are a few 90 degree corners by the neck and engine exhaust. I want to make one vertical loop cut so I can square up the edges. I select and delete the unnecessary faces from the model. Now I have holes in my ship. Uh Oh. I can fix this by selecting four points that should form a face then hitting the F key. (F is for face) Looks like I went a little too far on the nosecone section, and I needed to add some faces back in. Since we seem to have the lines for the window almost in place, move them to match. Now we will need another vertical loop cut for the top fin. Move points as needed to match the fin. Select the faces making up the boxy area between the neck and exhaust and extrude it up a bit. Follow that up by selecting only the face that will be the tail fin and extrude it up with the E key. (Select them in top view and extrude with the keyboard in side view). With the tail fin extruded, move around the edges forming the fin to shape it as desired (use the G key - Grab). I left a hole behind the fin and had to select the empty points and fill it with a face. I want the exhaust to have an indent in the center so I moved those edges around a bit on the top (the bottom needs another loop cut first. We need one for the wings to so lets switch to side view and add that. I deleted some faces then extruded some points and linked them back together to add more to fix up the exhaust area. So that there is a square edge before the bottom of the model. The wings come out of the bottom edge of the ship. I want to move those edges a bit to make the wings wider. Select the one big face and extrude it out from the side of the ship. Now we have wings straight out. Grab the edges of the wings one at a time and move them down in front mode so they angle down. Switch back into top mode and try to move the edges around to match. Looks like we need a loop cut for the angle in the middle. I went to the side and started moving things around. I ended up spending a ton of time removing redundant geometry and moving points around. Sorry for the sudden change. But I think you see the bulk of how I model now. After that I did the same sort of thing with the back section. Finally I added a few materials for color selected faces and set a material. Ending up with this. This is probably too high of a polygon count for SmileBasic but I am going to use it. Save your model, we are about to get destructive Now lets finalize the mirror. Then we will need to merge a bunch of faces on the seam back together. Go back to the modifier and have it save out. Here it is de-mirrored and simplified. Save it as yet another file. I did have to delete some interior faces I didn’t want. Before we export to SmileBasic we want to do some clean up. I know I had to fix and re-export my tank model previously because the normals weren’t pointed out the right way. From the mesh menu at the bottom run through all of the following: cleanup \ delete loose Normals \ recalculate outside Faces \ Triangulate Faces (Sim.3D will only accept triangles.) Make sure the whole mesh is selected in edit mode each time. Your triangluated model should look like: We are ready to export it. Choose File\Export\WaveFront (obj). Under the options (left hand side). Uncheck everything but Triangulate faces, you can turn that on, but since we already triangluated it, it shouldn’t matter. You should be able to open the file in a text editor and see lots of text like the following. Mostly lines that start with v or f. # Blender v2.79 (sub 0) OBJ File: 'ship.simplified.blend' # www.blender.org v 0.185529 0.058296 6.023614 v 0.545001 1.233850 4.314751 v 0.714347 2.058296 -3.696200 v 1.371901 0.299531 -2.989002 Congratulations now you can transfer this file over with Petit Modem 1.3 to SmileBasic. I have a microphone and headphone jack on my PC. Normally I only end up plugging in the microphone part of my headphones. I put the microphone by the 3DS’s speaker, and lean my computer speaker over top of the 3DSs microphone and awkwardly and noisily move the files around. Theoretically you could type it all in manually if you have the patience and don’t mind double checking every line. Or you could beg someone to make a key for you. Once I get it over to SmileBasic I want to turn the binary file into a text file. I use something like the following.
VAR DATA%[0], I, J, TMP, CH$, RET$ = “”
LOAD “DAT:SPACESHIP.OBJ”, DATA%

FOR I = 0 TO LEN(DATA%) - 1
 TMP = DATA%[I]
 FOR J = 0 TO 3
  CH$ = CHR$(TMP AND &HFF)
  TMP = TMP >> 8
  IF CH$ != “” THEN RET$ = RET$ + CH$
 NEXT J
NEXT I

PRINT RET$
SAVE “TXT:SPACESHIP.OBJ”, RET$
For some reason mine shows upside down and backwards but at least it works, and that is easy enough to fix in Sim.3D. Now, the last bit of glue needed is to load the 3D model into something that Sim.3D can use. Lucky for us, the WaveFront OBJ format is about as simple as I can find. It is a text format (hence the transform to text utility listed above), and should have a line of data per line. We will also be skipping everything I don’t care about. Unfortunately, SmileBasic doesn’t like to load files line by line. It wants to get everything at once in a big string, so we will have to adapt to that. But back to the OBJ file format (note this is not the same as OBJ files that a C/C++ compiler will build). So lets look at a sample OBJ file. This one is a simple triangulated cube. It is probably simple enough to type in if you want. # Blender v2.79 (sub 0) OBJ File: '' # www.blender.org v 1.000000 -1.000000 -1.000000 v 1.000000 -1.000000 1.000000 v -1.000000 -1.000000 1.000000 v -1.000000 -1.000000 -1.000000 v 1.000000 1.000000 -0.999999 v 0.999999 1.000000 1.000001 v -1.000000 1.000000 1.000000 v -1.000000 1.000000 -1.000000 s off f 2 4 1 f 8 6 5 f 5 2 1 f 6 3 2 f 3 8 4 f 1 8 5 f 2 3 4 f 8 7 6 f 5 6 2 f 6 7 3 f 3 7 8 f 1 4 8 The first two lines start with #, that marks the line as a comment just like ‘ or REM does in SmileBasic. You can ignore those, it just says that Blender generated the file. After that we have a line that starts with v. V is for Vertex, they will define points in the model. You can see that each point is separated by a space and is just a number. So the first point is at (1, -1, -1). We need to keep track of these points, so save them into an array as we go. There are other vertex attributes such as vt for texture coordinates, or vn for vertext normals (Sim.3D is going to be using winding order if I am not mistaken). We will need to code our loader to ignore row types we don’t have a handler for. After all we wouldn’t want to crash on the next line s off. What does s off do? It tells the loader to turn smooth shading off. We don’t really have that option so we will ignore the line. The next set of rows all define a face. Since we triangulated this cube, they are all triangles and just have three numbers. Each number is a one based index into the vertices we read through previously. So the first triangle is made of point 2 to point 4 and then back to point 1, or (1, -1, 1) - (-1, -1, -1) – (1, -1, -1). Unfortunately, there is a bit of variation here, each point could really look something like 1/2/3. If you see something like that the first section is the vertex, the second is the texture coordinate, and the third is the normal. Since we turned off all the options we don’t see that. There is a lot more too it, but we will really only be looking at vertices and faces, and ignoring everything else. This loader only supports a single number for the vertex index. I will leave it as an exercise to the reader if they want to load more possibilities (just split it on / before casting to a number). So, in our loader function, I mentioned that not being able to read things in a line oriented manner would make things harder. Instead we need a few helper functions, Is_WhiteSpace() to check if a character is white space which separates the parts of a line (I am just counting space, tab, carriage return (character 10) and line feed (character 13)). Get_Token will take the current index into the file and read until it hits non-whitespace then return the text between there until either you see more white space or reach the end of the file. It returns the text found and the new index into the file. Finally I needed one more Skip_To_EOL which will advance the pointer to the next line in the file. From there the loader function just reads through the file. I have an array called vert#. When I find a vertex, I add the x, y, and z coordinates to that array. I also have X1, Y1, and Z1 arrays. I assume triangles and read a fixed A, B, and C index (subtracting by 1 to make it zero indexed). Then use those to look up the right vertices in the vert# array, placing those points in the X1, Y1, and Z1 arrays. Finally I use Sim.3D’s ADDOBJ function to add it to the world and return the object reference. If the object isn’t rotated correctly I can do something like ROTOBJ Player, 0, 180, 180 (Sim.3D is degree based by the way). The last tricky part is the first line in your code should call the Sim.3D engine through PRGEDIT. So a sample program to load our newly transferred over 3D model in Sim.3D should look something like this:
PRGEDIT 3 EXEC "PRG3:3D.ENGINE"

VAR CR$ = CHR$(13), LF$ = CHR$(10)
VAR TAB$ = CHR$(9)
VAR GAME_OVER% = FALSE

VAR PLAYER = LOAD_OBJ("SPACESHIP.OBJ", "SPACESHIP", RGB(255, 255, 255))

ROTOBJ PLAYER, 0, 180, 180 

WHILE GAME_OVER% == FALSE 
 VSYNC
 CLS
 PRINT "FPS ";FPS
 CONTROLS
 IF CAMRY<-90 THEN CAMRY=-90
 IF CAMRY>90 THEN CAMRY=90
 RENDER
WEND

DEF CONTROLS
 VAR X, Y, Z
 VAR BTN
 BTN=BUTTON()
 
 DEC CAMRX,STX*6*COSCAMRZ
 DEC CAMRY,STY*6*COSCAMRZ
 IF BTN AND #UP THEN CAMDIST=CAMDIST/1.05
 IF BTN AND #DOWN THEN CAMDIST=CAMDIST*1.05
 IF BTN AND #X THEN
  GETCAMRAY OUT X,Y,Z
  CAMX=CAMX+X/2
  CAMY=CAMY+Y/2
  CAMZ=CAMZ+Z/2
 ENDIF

 IF BTN AND #B THEN
  GETCAMRAY OUT X,Y,Z
  CAMX=CAMX-X/2
  CAMY=CAMY-Y/2
  CAMZ=CAMZ-Z/2
 ENDIF

 IF BTN AND #Y THEN GAME_OVER% = TRUE
END


DEF LOAD_OBJ(FILE_NAME$, OBJ_NAME$, CLR%)
 VAR CR$ = CHR$(13), LF$ = CHR$(10)
 VAR TAB$ = CHR$(9)
 VAR I%, IDX% = 0, X#, Y#, Z#, A%, B%, C%
 VAR OBJ$
 VAR VERT#[0]
 VAR FACE%[0]
 VAR TOKEN$, MAX_LEN%
 VAR X1[0], Y1[0], Z1[0]
 
 LOAD "TXT:" + FILE_NAME$, FALSE OUT OBJ$
 MAX_LEN% = LEN(OBJ$)
 
 IDX% = 0
 WHILE IDX% < MAX_LEN%
  GET_TOKEN IDX%, OBJ$ OUT IDX%, TOKEN$
  IF TOKEN$ == "#" THEN
   'COMMENT, SKIP IT
   IDX% = SKIP_TO_EOL(IDX%, OBJ$)
  ELSEIF TOKEN$ == "v" OR TOKEN$ == "V" THEN
   'VERTEX
   GET_TOKEN IDX%, OBJ$ OUT IDX%, TOKEN$
   X# = VAL(TOKEN$)
   GET_TOKEN IDX%, OBJ$ OUT IDX%, TOKEN$
   Y# = VAL(TOKEN$)
   GET_TOKEN IDX%, OBJ$ OUT IDX%, TOKEN$
   Z# = VAL(TOKEN$)
   PUSH VERT#, X#
   PUSH VERT#, Y#
   PUSH VERT#, Z#
   'PRINT X#, Y#, Z#
   IDX% = SKIP_TO_EOL(IDX%, OBJ$)
  ELSEIF TOKEN$ == "f" OR TOKEN$ == "F" THEN
   'FACE (TRIANGLES ONLY PLEASE)
   GET_TOKEN IDX%, OBJ$ OUT IDX%, TOKEN$
   A% = VAL(TOKEN$) - 1
   GET_TOKEN IDX%, OBJ$ OUT IDX%, TOKEN$
   B% = VAL(TOKEN$) - 1
   GET_TOKEN IDX%, OBJ$ OUT IDX%, TOKEN$
   C% = VAL(TOKEN$) - 1
   PUSH FACE%, A%
   PUSH FACE%, B%
   PUSH FACE%, C%
   PUSH X1, VERT#[(A% * 3)]
   PUSH Y1, VERT#[(A% * 3) + 1]
   PUSH Z1, VERT#[(A% * 3) + 2]
   PUSH X1, VERT#[(B% * 3)]
   PUSH Y1, VERT#[(B% * 3) + 1]
   PUSH Z1, VERT#[(B% * 3) + 2]
   PUSH X1, VERT#[(C% * 3)]
   PUSH Y1, VERT#[(C% * 3) + 1]
   PUSH Z1, VERT#[(C% * 3) + 2]
   
   IDX% = SKIP_TO_EOL(IDX%, OBJ$)
  ELSE
   'DEFAULT, SKIP IT
   IDX% = SKIP_TO_EOL(IDX%, OBJ$)
  ENDIF
 WEND
 VAR T
 T = ADDOBJ(OBJ_NAME$, X1, Y1, Z1, CLR%)
 
 RETURN T
END

DEF GET_TOKEN IDX%, TEXT$ OUT NEW_IDX%, TOKEN$
 VAR I%, MAX_LEN% = LEN(TEXT$), TEXT_LEN%
 IF IDX% >= LEN(TEXT$) THEN
  NEW_IDX% = IDX%
  TOKEN$ = ""
 ELSE
  NEW_IDX% = IDX%
  WHILE IS_WHITE_SPACE(MID$(TEXT$, NEW_IDX%, 1)) && (NEW_IDX% < MAX_LEN%)
   INC NEW_IDX%
  WEND
  TEXT_LEN% = 1
  WHILE (IS_WHITE_SPACE(MID$(TEXT$, NEW_IDX% + TEXT_LEN%, 1)) == FALSE) && ((NEW_IDX% + TEXT_LEN%) < MAX_LEN%)
   INC TEXT_LEN%
  WEND
  TOKEN$ = MID$(TEXT$, NEW_IDX%, TEXT_LEN%)
  NEW_IDX% = NEW_IDX% + TEXT_LEN%
 ENDIF
END

DEF IS_WHITE_SPACE(TEXT$)
 VAR I%, CH$
 
 FOR I% = 0 TO LEN(TEXT$) - 1
  CH$ = MID$(TEXT$, I%, 1)
  IF CH$ != CR$ && CH$ != LF$ AND CH$ != TAB$ AND CH$ != " " THEN
   RETURN FALSE
  ENDIF
 NEXT I%
 RETURN TRUE
END

DEF SKIP_TO_EOL(IDX%, TEXT$)
 VAR MAX_LEN% = LEN(TEXT$)
 VAR NEW_IDX% = IDX%
 
 WHILE MID$(TEXT$, NEW_IDX%, 1) != CR$ && MID$(TEXT$, NEW_IDX%, 1) != LF$ && NEW_IDX% < MAX_LEN%
  INC NEW_IDX%
 WEND
 
 RETURN NEW_IDX%
END
If you type all of that in (or send it over from Petit Modem) you should find something like the following. I hope people found this useful. Thanks, SeggiePants Glossary:
  • Blender: A Open Source and free 3-D modeling program. It is hard to use but at least you aren’t a pirate.
  • .blend: The native file format for Blender files.
  • Edge: A line between two points, or a place where two planes meet. So if you had a cube, there would be an edge between a pair of connected corners.
  • Face: A loop of points that all lie on a plane. A triangle is always guaranteed to have all points lie on a plane, but a square isn’t (one of the points could form a bent triangle pair).
  • Model: An object you have created using vertices and faces.
  • Normal: A vector pointing perpendicularly out from a face. This is used in Sim.3D to make sure that only triangles facing the viewer are drawn. If you have your normal flipped a triangle may only be visible from inside of your model.
  • .obj: Wavefront OBJ file. A simple text based 3D model file format. See: https://en.wikipedia.org/wiki/Wavefront_.obj_file
  • Object: A collection of connected faces that forms a model or object you are trying to create.
  • Point: Describes a position in 3D space. It is usually a triplet of three numbers that describe the how far on the X, Y, or Z axis a point is from the center. This is for Cartesian coordinates, Polar coordinates are not covered but would be a length and some angles.
  • Vertex: Same as a point.
Sample Model The spoiler section contains the text of the 3D model made for this tutorial.
Spoiler # Blender v2.79 (sub 0) OBJ File: 'ship.simplified.blend' # www.blender.org v 0.185529 0.058296 6.023614 v 0.545001 1.233850 4.314751 v 0.714347 2.058296 -3.696200 v 1.371901 0.299531 -2.989002 v 1.214928 2.058296 -2.811143 v 1.540320 0.058296 -2.082262 v 1.521111 2.058296 -0.445370 v 0.675655 0.058296 0.462635 v 0.839425 1.114969 3.738742 v 1.559528 0.058296 3.657409 v 0.887866 0.178350 2.719927 v 0.740738 1.678495 2.806175 v 0.736698 1.202971 3.955156 v 1.156146 0.058296 4.559874 v 1.496025 0.058296 0.158585 v 0.713800 2.058296 -3.696200 v 0.772340 2.058296 -2.998149 v 0.752217 2.058296 0.462635 v 0.232899 2.058296 1.985574 v 0.339688 0.616684 1.310989 v 0.772340 -0.662029 3.870839 v 0.236528 1.615195 3.748712 v 0.561045 -0.675369 4.479837 v 0.566198 -0.191417 -1.415282 v 0.599886 0.147642 -3.006434 v 0.049003 2.058296 -3.696200 v 0.059340 2.058296 -3.006434 v 0.057563 2.058296 -2.698096 v 0.804770 2.590715 -2.756637 v 0.753870 2.501786 -0.650167 v 0.057563 2.738603 -2.391290 v 0.053466 2.738603 -0.397221 v 0.057563 4.112556 -3.004901 v 0.053466 4.112556 -2.238051 v 0.714347 0.721217 -3.696200 v 0.328039 0.721217 4.561622 v 1.559528 0.721217 -2.989002 v 1.540320 0.721217 -2.082262 v 0.467026 1.165862 1.584504 v 1.136664 1.355577 0.462635 v 0.772340 0.721217 -2.998149 v 1.559528 0.736656 3.865836 v 1.194564 0.721217 2.806633 v 1.156146 0.721217 4.559874 v 1.496025 0.721217 0.158586 v 0.713800 0.721217 -3.696200 v 0.049003 0.721217 -3.696200 v 0.059340 0.729895 -3.006434 v 3.963805 -1.542426 -1.823088 v 4.910374 -1.515748 -5.953856 v 4.910374 -1.399740 -5.953856 v 3.963805 -1.426418 -1.823088 v 3.398713 -1.145025 -1.369309 v 3.398713 -0.903781 -1.369309 v 3.908571 -0.957139 -3.535781 v 3.908571 -1.198383 -3.535780 v -0.372662 0.058296 6.023614 v -0.732134 1.233850 4.314751 v -0.901480 2.058296 -3.696200 v -0.093566 1.873027 -3.696200 v -1.559033 0.299531 -2.989002 v -1.402061 2.058296 -2.811143 v -0.093566 1.873027 -3.008210 v -0.093566 2.058296 -2.698096 v -1.727452 0.058296 -2.082262 v -1.708244 2.058296 -0.445370 v -0.093566 2.058296 0.462635 v -0.093566 0.058296 0.462635 v -0.862788 0.058296 0.462635 v -0.093566 2.058296 2.270960 v -1.026558 1.114969 3.738742 v -0.093566 1.824857 3.380962 v -1.746661 0.058296 3.657409 v -1.074999 0.178350 2.719927 v -0.927870 1.678495 2.806175 v -0.923831 1.202971 3.955156 v -1.343279 0.058296 4.559874 v -1.683158 0.058296 0.158585 v -0.093566 0.058296 -0.397221 v -0.900932 2.058296 -3.696200 v -0.959473 2.058296 -2.998149 v -0.939350 2.058296 0.462635 v -0.420032 2.058296 1.985574 v -0.526821 0.616684 1.310989 v -0.959473 -0.662029 3.870839 v -0.423661 1.615195 3.748712 v -0.748178 -0.675369 4.479837 v -0.753331 -0.191417 -1.415282 v -0.093566 0.058296 -3.008210 v -0.787019 0.147642 -3.006434 v -0.236135 2.058296 -3.696200 v -0.246473 2.058296 -3.006434 v -0.244696 2.058296 -2.698096 v -0.991903 2.590715 -2.756637 v -0.093566 2.738603 -3.008210 v -0.941003 2.501786 -0.650167 v -0.093566 2.738603 -2.391290 v -0.093566 2.738603 -0.397221 v -0.244696 2.738603 -2.391290 v -0.240599 2.738603 -0.397221 v -0.244696 4.112556 -3.004901 v -0.240599 4.112556 -2.238051 v -0.901480 0.721217 -3.696200 v -0.515172 0.721217 4.561622 v -0.093566 0.890642 -3.696200 v -1.746661 0.721217 -2.989002 v -1.727452 0.721217 -2.082262 v -0.654159 1.165862 1.584504 v -1.323797 1.355577 0.462635 v -0.959473 0.721217 -2.998149 v -1.746661 0.736656 3.865836 v -1.381696 0.721217 2.806633 v -1.343279 0.721217 4.559874 v -1.683158 0.721217 0.158586 v -0.900932 0.721217 -3.696200 v -0.236135 0.721217 -3.696200 v -0.246473 0.729895 -3.006434 v -0.093566 0.899725 -3.008211 v -4.150938 -1.542426 -1.823088 v -5.097507 -1.515748 -5.953856 v -5.097507 -1.399740 -5.953856 v -4.150938 -1.426418 -1.823088 v -3.585846 -1.145025 -1.369309 v -3.585846 -0.903781 -1.369309 v -4.095704 -0.957139 -3.535781 v -4.095704 -1.198383 -3.535780 s off f 16 35 46 f 44 2 36 f 23 14 1 f 39 12 43 f 8 79 15 f 38 37 5 f 11 39 43 f 11 10 21 f 43 9 42 f 21 14 23 f 9 44 42 f 7 45 38 f 18 67 39 f 79 24 15 f 79 69 78 f 65 61 90 f 5 17 29 f 5 30 7 f 30 32 18 f 67 32 98 f 30 31 32 f 7 30 18 f 31 95 97 f 5 41 17 f 42 14 10 f 37 25 48 f 43 10 11 f 18 40 7 f 45 8 15 f 4 38 6 f 40 20 8 f 14 36 1 f 89 117 48 f 87 1 57 f 25 24 89 f 4 6 25 f 27 29 17 f 103 80 115 f 113 58 76 f 87 57 77 f 108 75 83 f 88 79 78 f 109 66 114 f 74 108 84 f 74 85 73 f 112 71 75 f 85 77 73 f 113 71 111 f 106 107 62 f 108 82 109 f 69 79 68 f 62 94 81 f 72 12 70 f 83 70 19 f 62 96 94 f 67 100 82 f 96 99 94 f 27 92 95 f 66 82 96 f 99 95 94 f 110 62 81 f 77 111 73 f 106 90 61 f 73 112 74 f 82 66 109 f 69 114 78 f 61 107 106 f 109 84 108 f 36 57 1 f 77 104 113 f 117 89 90 f 87 21 23 f 85 11 21 f 88 24 79 f 90 89 88 f 88 65 90 f 94 92 81 f 84 8 20 f 11 84 20 f 16 3 35 f 44 13 2 f 39 19 12 f 11 20 39 f 43 12 9 f 21 10 14 f 9 13 44 f 7 38 5 f 39 40 18 f 8 68 79 f 6 15 24 f 5 29 30 f 100 96 82 f 30 29 31 f 31 29 95 f 5 37 41 f 42 44 14 f 37 4 25 f 43 42 10 f 45 40 8 f 4 37 38 f 40 39 20 f 14 44 36 f 48 25 89 f 87 23 1 f 27 95 29 f 103 59 80 f 113 104 58 f 108 112 75 f 74 112 108 f 112 111 71 f 85 87 77 f 113 76 71 f 7 40 45 f 83 67 108 f 67 19 39 f 88 78 65 f 12 19 70 f 70 83 75 f 75 72 70 f 67 82 108 f 19 67 83 f 62 66 96 f 117 118 48 f 67 98 100 f 96 100 99 f 27 63 92 f 99 97 95 f 110 106 62 f 77 113 111 f 106 117 90 f 73 111 112 f 69 109 114 f 61 65 107 f 109 69 84 f 36 104 57 f 77 57 104 f 32 67 18 f 87 85 21 f 85 74 11 f 88 89 24 f 94 95 92 f 84 69 8 f 11 74 84 f 3 16 17 f 107 66 62 f 6 24 25 f 8 69 68 f 59 81 80 f 114 66 107 f 22 12 72 f 13 22 2 f 2 104 36 f 86 75 71 f 86 76 58 f 86 58 2 f 72 86 22 f 22 9 12 f 13 9 22 f 2 58 104 f 86 72 75 f 22 86 2 f 86 71 76 f 50 55 49 f 31 34 32 f 53 52 54 f 56 49 53 f 56 51 50 f 56 53 55 f 6 53 15 f 54 38 15 f 38 54 55 f 125 122 124 f 125 120 119 f 38 45 15 f 33 97 101 f 123 126 125 f 102 33 101 f 102 99 100 f 102 98 34 f 122 123 124 f 119 126 123 f 126 121 125 f 65 125 107 f 123 65 78 f 114 107 78 f 55 54 52 f 55 52 49 f 31 33 34 f 53 49 52 f 56 50 49 f 56 55 51 f 6 38 55 f 50 51 55 f 15 53 54 f 107 124 78 f 121 120 125 f 122 125 119 f 31 97 33 f 97 99 101 f 102 34 33 f 102 101 99 f 100 98 102 f 98 32 34 f 122 119 123 f 119 120 126 f 126 120 121 f 65 123 125 f 53 6 55 f 124 123 78 f 107 125 124 f 26 63 27 f 3 41 35 f 46 47 105 f 60 46 105 f 16 27 17 f 48 105 47 f 46 48 47 f 91 63 60 f 110 59 103 f 115 60 105 f 116 115 105 f 80 92 91 f 105 117 116 f 117 115 116 f 26 60 63 f 3 17 41 f 46 60 16 f 60 26 16 f 16 26 27 f 48 118 105 f 46 41 48 f 91 92 63 f 110 81 59 f 91 60 80 f 60 115 80 f 80 81 92 f 105 118 117 f 117 110 115 f 41 46 35 f 41 37 48 f 110 103 115 f 110 117 106

This is really neat! A bit inconvenient but it's better than manually modeling in SB. I might use this for Switch if we get PetitModem and Sim.3D to work there.

Change the file extension to .txt so petit modem will save it as a text file automatically.

You should put the example .obj in a spoiler

Replying to:12Me21
Change the file extension to .txt so petit modem will save it as a text file automatically.
Good idea, I should have done that.

Replying to:12Me21
You should put the example .obj in a spoiler
That does look nicer.

Wow, that's really cool. Thanks for making it a tutorial!

Replying to:Nathaniel
This is really neat! A bit inconvenient but it's better than manually modeling in SB. I might use this for Switch if we get PetitModem and Sim.3D to work there.
PetitModem will probably be replaced with something like a program that sends a text file through a USB cable by sending the signals for key presses from a keyboard. In any case, we'll surely have a way to send text from a computer.

Replying to:Nathaniel
This is really neat! A bit inconvenient but it's better than manually modeling in SB. I might use this for Switch if we get PetitModem and Sim.3D to work there.
I think PetitIModem will still be used to send files from the Switch to PC as the USB keyboard is probably going to be one way. Even if SB is able to toggle caps lock on the keyboard, it's unlikely that we'll be able to control it with a program. We could, however, convert the program into B&W images, take screenshots of them, download them to PC, and convert them back into text. 12Me21 did something like this a while back.