Jump to content

View more

Image of the Day

Full Syncs #screenshotsaturday favourites https://t.co/i1Flnwcg3l #xbox #ps4 https://t.co/m0v2F1SxGs
IOTD | Top Screenshots

The latest, straight to your Inbox.

Subscribe to GameDev.net's newsletters to receive the latest updates and exclusive content.


Sign up now

Planning on using Blender

2: Adsense
  • You cannot reply to this topic
18 replies to this topic

#1 the incredible smoker   Members   

483
Like
3Likes
Like

Posted 16 March 2017 - 11:10 AM

Hi, i was planning on using Blender in the future, is it bad compared to 3dmax ?, i,m used to 3dMax.

The good thing is that it supports .X files for use with DirectX.

 

thanks


S T O P   C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70  Working on : LevelContainer class & LevelEditor


#2 Scouting Ninja   Members   

3436
Like
9Likes
Like

Posted 16 March 2017 - 04:02 PM

*
POPULAR

You can think of a 3D engine as having four major factors: 3D modeling, Animation, Rendering and Useability.

In 3D modeling, Max is first, Blender second and Maya third.

Blender has all the basic tools and advance tools. Some of it's special tools are unique to Blender, because they are unstable and can cause crashes(although it's rare). The tools Max has that Blender doesn't is special tools that are maybe used by a 3D modeler once a year.

The types of modeling Max and Blender specialize in is also different. Blender is Sub-D modeling focused with great support for other types of modeling. Spline and Boolean modeling is what Max is great for.

Blender's Boolean modeling tools are very hard to use, it's spline modeling doesn't work with the Boolean. This could be a temporary problem as Blender developers are working on a better layers system, that will allow for better Boolean tools.

Blender's Auto UV is as bad as any Auto UV, Blender has ALL UV functions, there is payed software that I know off that doesn't. Blender's Auto LOD tools are also as bad as most of the standard stuff, in fact Simplygon is the best.

3D sculpting in Blender is the same as in most 3D software, it just can't compete to 3D sculpting software like Zbrush. Still you can make great levels with the sculpting tool and it has a unique feel to it. It's 3D painting tools are average.

 

In Animation Max is first, Maya second and Blender third.

Blender supports all the basics and advance tools, it doesn't have any special tools that I know about.

Rigging can be a pain in Blender, however the worst is the way blender allows animations to effect and change other animations. For example if you had a walking man on frame 1-24 and a jumping animation on frame 100-132 then the jump could have small effects on the walking animation. To prevent this brake animations into NLA actions, the actions are also easier to export and work with.

It works for game animations, although there is a lot of room for improvement.

 

Max is best at Rendering, Maya is second, Cinema 4d third and Blender fourth or maybe fifth.

Blender uses Cycles as it's rendering engine, it can produces very realistic results however it is among the slowest renders I have ever worked with. You also need a understanding of real world lighting and materials to get a good result. You will only use this for movies or posters so it doesn't matter. I just want to point out, that render artists do make top quality renders with it; some even better than Max or Maya renders.

The default render in Blender is a outdated OpenGL renderer, check that it is on when using Blender. It's more than whats needed to model and bake textures. At the moment it doesn't have BPR, I hope it has BPR with the next release. There are addons to allow BPR materials in Blender if you need them.

 

Useability, Maya is first, Max then a bunch of other software, with Blender dead last.

Blender works completely different from any 3D software you have ever used. When I am using Blender my screen looks like this:Fyrp11x.png?1

The main thing about Blender's interface is that it isn't on screen, it's on your keyboard; like a game. The buttons, sliders, number inputs and menus are just your training wheels or setup tools.

When using normal software it's advised you ignore shortcuts, because they are just one more thing to learn. In Blender the shortcuts is key to using Blender, don't think of Blender as having shortcuts think of it as Blender's input; when playing a game you don't think of WASD as shortcuts for walking.

Blender has menus, and in the menu will be the shortcut key for you to learn. Sometimes you need to hover the mouse to read the shortcut. Some shortcuts will open menus at the mouse pointer, like W_key (Specials), Ctrl_V(Vertices) Ctrl_E(Edge), Ctrl_F(Face); these are all near the WASD keys and are the most used.

With the correct addon enabled Space will act as a search tool, allowing you to find complex task fast.

To learn how Blender works start with the Blender Wiki, read it like a manual. If you are unwilling to do this just search how to do X on the internet. Blender can do everything other software can, it just doesn't have a convenient menu button for it.

 

Some major things people who don't read the wiki miss:

Spoiler

Because professinals mostly use shortcuts after years of modeling, Blender's interface can be considered a great tool for them. Modeling in Blender is faster than in any other 3D modeling software.

 

Hi, i was planning on using Blender in the future, is it bad compared to 3dmax ?, i,m used to 3dMax.

If Max and Maya where free, then I would say that there is no point in using Blender.

Because Blender provides you with all the 3D modeling tools you need and want, can do what animations you need for games, renders amazing images even if slowly, all of this for FREE with no strings attached. I can without a doubt say that Max and Maya aren't worth the price they are charging. So no it isn't bad when compared to Max.

Even if money wasn't a problem there are so many other software that you still need after the 3D modeling tool, like a 2D software, 3D sculpting sofware and material software; getting the most important one for free is a nice treat.

Blender is on par with other 3D software, even if harder to learn, it isn't like Photoshop vs Gimp.


Edited by Scouting Ninja, 16 March 2017 - 04:11 PM.


#3 Ben3d   Members   

165
Like
1Likes
Like

Posted 16 March 2017 - 06:00 PM

My opinion : Blender is better, but it takes time to learn it. 3dstudio max is easier to learn, more intuitive. It is a classical approach, and you wont be lost.

 

In Blender I save a lot of time. One of the strong features amongts others, is that you can lay down UVs with ease and speed.



#4 Postie   Members   

1525
Like
1Likes
Like

Posted 17 March 2017 - 02:59 AM

Pressing G_Key (grab) twice in edit mode enables edge slide.

Well, I just learnt something new. Thanks! :D


Currently working on an open world survival RPG - For info check out my Development blog: ByteWrangler

#5 the incredible smoker   Members   

483
Like
1Likes
Like

Posted 17 March 2017 - 05:24 AM

Hi, thanks for the reply`s.

Looks like i can use Blender without problems when reading Scouting Ninja`s post.

The UV coordinates has unwrap ?, that is very nice, if i have to, i will fill in the UV coordinates manually since i only do low-poly stuff.

The boolean is also not perfect in 3dMax, still have to remove sudden vertices sometime, it is a very nice inspiring usable feature for the rest, max aint perfect also, especially if you look to the price.

There are always other methods to make the same without boolean, and will be more perfect, so i can live with that.

I,m surely i going to use Blender, if i have some question i will post them in this forum.

thanks


S T O P   C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70  Working on : LevelContainer class & LevelEditor


#6 Hodgman   Moderators   

50335
Like
3Likes
Like

Posted 17 March 2017 - 07:18 AM

The good thing is that it supports .X files for use with DirectX.

The X file format is part of the D3DX library, which is used by the example code for D3D9. It's not a common or good format for games.



#7 the incredible smoker   Members   

483
Like
0Likes
Like

Posted 18 March 2017 - 10:02 AM

Hi, i know Hodgman, i like to make a very simple game for once, nothing more.

Microsoft makes all that stuff in dll, so you cannot see the actual codes and maths.

By example they have this nice function also : CheckIntersect.

 

I was planning on using all the DirectX stuff, i dont know how to make all functions myself,

i better wanto avoid all, only i dont know how ?

Ok a 3d fileformat aint that hard to make.

I,m also gonna use the fixed pipeline, i realy have no clue else what to do, i,m a total beginner with the goal of making some games.

I only wanto invest 1 year for all music/artwork/programming.

 

The big problem is that i cannot optimize the functions i use : adding integer math / adding lookuptables.


Edited by the incredible smoker, 18 March 2017 - 10:05 AM.

S T O P   C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70  Working on : LevelContainer class & LevelEditor


#8 BBeck   Members   

788
Like
0Likes
Like

Posted 20 March 2017 - 05:02 AM

Which program is better is a matter of opinion. I prefer Blender because it doesn't cost as much as a used car and I do tutorials where I want people to be able to follow along without having to go buy thousands of dollars worth of software.
 
I used Max for awhile. I started with Max before I knew how to do much, mostly following tutorials. I wouldn't mind using it again as long as I don't have to buy it.
 
But I really love Blender. There might be other software out there that does things better. There's a lot of software I've never tried. I had a lot of trouble with animation in Blender, but that may be simply because I haven't learned to use it well yet. However, when I worked in Poser years ago it seemed like animation was an order of magnitude easier.
 
So far, animation is really the only issue I've ever had with Blender. I'm not sure how much better Max is at it as it's been many years since I've done any animation in Max and I think that was just a tutorial or two. I think Poser was easier for humanoid animation than either of them, if I recall correctly. Even my Poser experience was several years ago.
 
I'm enrolled in a 3D art program where I'm learning 3D modeling and we're using Blender for the class. My DeviantArt site shows some of the finals I turned in for the class including a bucket, Mons Meg, and a Cold Blast Lamp. I did the modeling in Blender, then I exported them to Substance for painting/texturing, then I imported them back into Blender to render them in Cycles. The backgrounds are HDRI environment maps. Basically, they are 360 degree spherical photographs. Blender Cycles uses the photo for lighting, which is a big part of what makes everything looks so realistic.
http://virtuallyprogramming.deviantart.com/
 
If you are coding DirectX, you may want to check out my simple DX 11 engine on my website. The entire Visual Studio project is there for download including all art assets and such. For example, the Python code for the Blender exporter is in the download. I made a Blender model exporter written in Python (forgive my Python code as I literally taught myself Python just for this project - the exporter code works, but has room for improvement) and the source code is there to load the model file and create a model object from it. It should work well for rigid animation, although it doesn't support skinned animation. But it's modifiable so you could make it do whatever you like. There is a video of my OpenGL engine on my website. You can watch that and get an idea of what the DX engine does without having to actually download the DX project. The OGL engine is basically the same except I never got around to adding the code for models and all the models in the OGL code are hard coded. My modeling skills weren't all that great back when I programmed this stuff; they've gotten a lot better in the past year as I've been in this art program.
 
I think a lot of people use ASSIMP to load models and I think it supports a lot of different formats. I assume it supports skinned animation as well.
 
I started with XNA and then went straight to DX11. So, I skipped DX9, but I guess it natively supports .X files? They took the whole model class out and such in DX11, so it no longer natively supports much of anything although I think it will natively support .DDS images.


Edited by BBeck, 20 March 2017 - 05:37 AM.


#9 samoth   Members   

9424
Like
0Likes
Like

Posted 20 March 2017 - 09:03 AM

Blender is something you will hate with a passion, until you finally, after many hours of pain, get used to its perverse user interface and keyboard shortcuts.

At that point, Blender suddenly becomes the most awesome modelling software you have ever used (I'm not really interested in raytracing, so the fact that the Cycles renderer is about as poor as it can be performance-wise is not an issue for me, and I couldn't say anything about Blender's built-in game engine either).

Tasks that seem to be trivial but are excruciatingly difficult with most modelling software are surprisingly easy in Blender, if you know how to use it. But... plan to invest a few weeks of learning.

#10 the incredible smoker   Members   

483
Like
0Likes
Like

Posted 20 March 2017 - 10:43 AM

I started with XNA and then went straight to DX11. So, I skipped DX9, but I guess it natively supports .X files? They took the whole model class out and such in DX11, so it no longer natively supports much of anything.

 

Oh, does D3DXMesh still exists ?, do you have to figure out now yourself how it works ?, thanks for the examples then.

I still use DX9, i wanto upgrade to a better version maybe if its better, i still use XP, visual C++ 2005, can i install a better DirectX with that ?

I dont wanto download a free compiler since i have professional package and can legally sell with this eventually.


S T O P   C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70  Working on : LevelContainer class & LevelEditor


#11 Hodgman   Moderators   

50335
Like
1Likes
Like

Posted 20 March 2017 - 05:28 PM

Oh, does D3DXMesh still exists ?

Nope. The D3DX library was not maintained after D3D9. It's used by the example code for D3D9 only, it's not a "real" part of DirectX.



#12 BBeck   Members   

788
Like
0Likes
Like

Posted 21 March 2017 - 05:12 AM

In addition to what Hodgman said, I believe they moved a lot of the functionality out to the DirectX Took Kit. Looks to me like they took a lot of that stuff out between DX10 and DX11 including Sprite and Model support. I think they did DXTK as a way to give it back to those who wanted it. I believe a lot of pro-game developers were basically writing their own anyway and they wanted to offload it.

 

I haven't tried DXTK myself. When I was doing DX11, I was bound and determined to figure out how to do it all myself. So, I built my own model class, exporter, and importer. Then I serialized the class to save the binary data out to disk. I would have gone on to make it support skinned animation. I got as far as doing a test run using XNA to play back Blender humanoid animation data as stick figures, but then decided to start doing OpenGL instead and had to start all over learning OGL. Then I got side tracked again with learning 3D modeling. Learning Vulkan is on my agenda but really before any of that I have to get my new computer built as the parts have been sitting here collecting dust since Christmas.

 

I never used D3DXMesh directly. I think what I was doing in XNA years ago was calling it to get the job done behind the scenes.

 

There's also ASSIMP for models although I don't know exactly how much it does for you since I haven't tried it. I've thought about trying to work ASSIMP into my OGL engine rather than rewriting my DX model class to support OGL. I can see some advantages to both and so I'm still not sure which direction I'll go with that if I ever get the time.

 

I actually have a bit of deprecated code in my DX11 engine. Some of it is probably because I wrote it for Windows 7 and MS no longer really supports that wanting everyone to use Win10. Apparently, there's no "good" way to get keyboard input in DX11 and hasn't been in DX for a long time. I'm using DX8 code to communicate with the Keyboard rather than going through the event loop. I think some of the error handling was deprecated code as well. I tried to avoid anything deprecated, but there seemed no good way to do some things in DX11 for Win7 without using deprecated code.

 

So, I'm guessing you could actually include D3DXMesh in DX11 or DX12 code.

 

Really, all a model is is a vertex buffer and an index buffer. The hard part is done in the modeling software to build all those vertices and assign them data. I made mine to import sub-meshes so that the model could be in different parts. That's mostly to support rigid animation, so that the parts can be animated separately. But I put it all in a single vertex buffer.

 

If you support skinned animation, it gets quite a bit more complex. You have to have all the skinning data such as vertex groups and weighting information to know how much each vertex is assigned to each bone in the armature. And you have to have the armature data itself (bones), which is a bit different from the sub-meshes having their own matrices in rigid animation. Then of course, there's all the animation data for the armature and calculating frames between the key frames.

I'm not sure if D3DXMesh supported animation. I'm betting it did because I seem to remember doing a tutorial once where I got a model to animate back in the DX9 days and back then I had no clue what I was doing. In fact, I failed to learn DX9 after a few years of effort. XNA came along and that's when I actually started figuring everything out and in XNA the model class did not natively support skinned animation. Then I went into DX11 without ever really learning DX9.



#13 Josh Green   Members   

106
Like
0Likes
Like

Posted 21 March 2017 - 08:27 AM

Agreed with what most people are saying here with the two main points.

  • Blender is cost effective and produces similar results (has a great community and lots of plugins).
  • Using Blender solely for that format is perhaps not the best use since it is not commonly used now.

Learn to create games through our vibrant platform of easy-to-follow video training material. Getting recognition of the knowledge and skills you gain from the courses is essential. We can provide you with globally recognised City & Guilds accredited certificates.

www.polygoncollege.com


#14 the incredible smoker   Members   

483
Like
0Likes
Like

Posted 21 March 2017 - 08:49 AM

Thanks for the reply`s

For the toolkit i see no download for XP & Win7, maybe i stay with old trusty DX9,

 

only it would be very handy to make stuff myself, i might understand things better for optimizing.

That means i have to make a blender export plugin, i bet there are enough examples for that in C.

 

I use vertexbuffer for the LevelMesh, only indexbuffer i dont know exact how it works, it seems i need it for the models.

And skinned animation is something i would like, only i dont wanto get involved in these seperate shader files for animations,

i want everything done in 1 source code file, not learn a new shader language on top, i dont know if that is possible ?


S T O P   C R I M E !

Visual Pro 2005 C++ DX9 Cubase VST 3.70  Working on : LevelContainer class & LevelEditor


#15 BBeck   Members   

788
Like
0Likes
Like

Posted Today, 03:27 AM

I think you have to use Python to write a Blender exporter. If you find a way to do it in C, let me know, because I pretty much hate Python. Then again, I only learned enough Python to write a Blender exporter. So, I imagine my code has plenty of room for improvement. But here is my Blender Python model data exporter:

 

import bpy


def ProcessChildren(f, ParentMesh, VerticesInModel, FacesInModel):  #Get all data for sub-meshes.
    VerticesSoFar = VerticesInModel
    FacesSoFar = FacesInModel
    NumberOfChildren = len(ParentMesh.children)
    if NumberOfChildren > 0:
        for ChildMesh in ParentMesh.children:
            HasUVCoordinates = ChildMesh.data.uv_layers.active
            HasVertexColors = ChildMesh.data.vertex_colors.active
            CountOfVertices = 0
            f.write("Name:%s\n" % ChildMesh.name)
            f.write("Parent:%s\n" % ChildMesh.parent.name)
            VerticesSoFar = VerticesSoFar + len(ChildMesh.data.vertices)
            ChildMesh.data.update(calc_tessface=True)
            FacesSoFar = FacesSoFar + len(ChildMesh.data.tessfaces)
            #Materials
            SubMeshMaterialNo = 0
            for Material in ChildMesh.material_slots:
                f.write("M:%d" % SubMeshMaterialNo)
                f.write("-%s" % Material.name)
                f.write(" T:%s" % Material.material.active_texture.image.name)
                f.write(" C:%s\n" % str(tuple(Material.material.diffuse_color)))
            #WorldMatrix
            WorldMatrixRowNumber = 0
            for WorldMatrixRow in ChildMesh.matrix_world.row:
                f.write("WMR%d" % WorldMatrixRowNumber)
                f.write(":%s\n" % str(tuple(WorldMatrixRow)))
                WorldMatrixRowNumber = WorldMatrixRowNumber + 1
            #Faces.
            FaceCount = 0
            for Face in ChildMesh.data.polygons:
                f.write("F:%d" % FaceCount)
                f.write(" M:%s" % str(Face.material_index))
                f.write(" N:({:f}".format(Face.normal[0]))
                f.write(",{:f}".format(Face.normal[1]))
                f.write(",{:f})".format(Face.normal[2]))
                f.write(" S:%s\n" % str(Face.use_smooth)[0])
                FaceCount = FaceCount + 1
                for Vert, Loop in zip(Face.vertices, Face.loop_indices):
                    f.write("P:%s" % str(tuple(ChildMesh.data.vertices[Vert].co)))
                    if HasUVCoordinates is not None:
                        f.write(", U:%s" % str(tuple(ChildMesh.data.uv_layers.active.data[Loop].uv)))
                    else:
                        f.write(", U:(0.0,0.0)")
                    f.write(", N:%s" % str(tuple(ChildMesh.data.vertices[Vert].normal)))
                    if HasVertexColors is not None:
                        f.write(", C:%s" % str(tuple(ChildMesh.data.vertex_colors.active.data[Vert].color)))
                    else:
                        f.write(", C:(0.0,0.0,0.0,0.0)")
                    f.write("\n")
            #Call child's children.
            VerticesSoFar, FacesSoFar = ProcessChildren(f, ChildMesh, VerticesSoFar, FacesSoFar)
    return VerticesSoFar, FacesSoFar

def write_some_data(context, filepath):
    HasUVCoordinates = bpy.context.active_object.data.uv_layers.active
    HasVertexColors = bpy.context.active_object.data.vertex_colors.active
    print("Exporting data...")
    bpy.context.active_object.data.update(calc_tessface=True)         #calc_tessface=True must be set before working with faces.
    
    f = open(filepath, 'w', encoding='utf-8')
    if bpy.context.active_object != None and bpy.context.active_object.parent == None and bpy.context.active_object.type == 'MESH':
        CountOfVertices = 0
        NumberOfMaterialsUsed = 0
        FacesInModel = 0
        VerticesInModel = len(bpy.context.active_object.data.vertices)
        FacesInModel = len(bpy.context.active_object.data.tessfaces)
        f.write("Punc!It Text\n")
        f.write("Rigid Animation\n")
        f.write("Name:%s\n" % bpy.context.active_object.name)
        f.write("Parent:None\n")
        #Materials
        MaterialNo = 0
        for Material in bpy.context.active_object.material_slots:
            f.write("M:%d" % MaterialNo)
            f.write("-%s" % Material.name)
            f.write(" T:%s" % Material.material.active_texture.image.name)
            f.write(" C:%s\n" % str(tuple(Material.material.diffuse_color)))
        #WorldMatrix
        WorldMatrixRowNumber = 0
        for WorldMatrixRow in bpy.context.active_object.matrix_world.row:
            f.write("WMR%d" % WorldMatrixRowNumber)
            f.write(":%s\n" % str(tuple(WorldMatrixRow)))
            WorldMatrixRowNumber = WorldMatrixRowNumber + 1
        #Faces.
        FaceCount = 0
        for Face in bpy.context.active_object.data.polygons:
            f.write("F:%d" % FaceCount)
            f.write(" M:%s" % str(Face.material_index))
            f.write(" N:({:f}".format(Face.normal[0]))
            f.write(",{:f}".format(Face.normal[1]))
            f.write(",{:f})".format(Face.normal[2]))
            f.write(" S:%s\n" % str(Face.use_smooth)[0])
            FaceCount = FaceCount + 1
            for Vert, Loop in zip(Face.vertices, Face.loop_indices):
                f.write("P:%s" % str(tuple(bpy.context.active_object.data.vertices[Vert].co)))
                if HasUVCoordinates is not None:
                    f.write(", U:%s" % str(tuple(bpy.context.active_object.data.uv_layers.active.data[Loop].uv)))
                else:
                    f.write(", U:(0.0,0.0)")
                f.write(", N:%s" % str(tuple(bpy.context.active_object.data.vertices[Vert].normal)))
                if HasVertexColors is not None:
                    f.write(", C:%s" % str(tuple(bpy.context.active_object.data.vertex_colors.active.data[Vert].color)))
                else:
                    f.write(", C:(0.0,0.0,0.0,0.0)")
                f.write("\n")
        #Children.
        VerticesInModel, FacesInModel = ProcessChildren(f, bpy.context.active_object, VerticesInModel, FacesInModel)
        #Footer
        f.write("Total Unique Vertices:%d\n" % VerticesInModel)
        f.write("Total Faces:%d\n" % FacesInModel)
        f.write("Total Materials:%d" % NumberOfMaterialsUsed)
    else:
        f.write("You must select the root mesh before exporting the model!")
    f.close()
    
    return {'FINISHED'}


# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator


class ExportSomeData(Operator, ExportHelper):
    """This appears in the tooltip of the operator and in the generated docs"""
    bl_idname = "export_test.some_data"  # important since its how bpy.ops.import_test.some_data is constructed
    bl_label = "Export Button"

    # ExportHelper mixin class uses this
    filename_ext = ".txt"

    filter_glob = StringProperty(
            default="*.txt",
            options={'HIDDEN'},
            )

    def execute(self, context):
        return write_some_data(context, self.filepath)


# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
    self.layout.operator(ExportSomeData.bl_idname, text="Custom Exporter Test")


def register():
    bpy.utils.register_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.append(menu_func_export)


def unregister():
    bpy.utils.unregister_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.remove(menu_func_export)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.export_test.some_data('INVOKE_DEFAULT')

 

This is a toy car model I made in Blender and exported. This is what the output looks like:

Punc!It Text
Rigid Animation
Name:CarBody
Parent:None
M:0-CarMaterial T:SimplifiedCarBody.png C:(1.0, 1.0, 1.0)
WMR0:(1.0, 0.0, 0.0, 0.0)
WMR1:(0.0, 1.0, 0.0, 0.0)
WMR2:(0.0, 0.0, 1.0, 0.0)
WMR3:(0.0, 0.0, 0.0, 1.0)
F:0 M:0 N:(-0.000000,-1.000000,0.000000) S:F
P:(0.6159690022468567, -1.0, 1.3930187225341797), U:(0.38228708505630493, 0.8303871154785156), N:(0.5597094893455505, -0.46266061067581177, 0.6874599456787109), C:(1.0, 1.0, 0.0)
P:(-0.6159691214561462, -0.9999999403953552, 1.3930187225341797), U:(0.6177127957344055, 0.8303872346878052), N:(-0.5597094893455505, -0.46266061067581177, 0.6874599456787109), C:(0.9686274528503418, 1.0, 0.003921568859368563)
P:(-0.6159691214561462, -0.9999999403953552, 0.6069812774658203), U:(0.617712676525116, 0.9806004762649536), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(1.0, 1.0, 0.0)
P:(0.6159690022468567, -1.0, 0.6069812774658203), U:(0.3822869658470154, 0.9806003570556641), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.9333333373069763, 1.0, 0.04313725605607033)
F:1 M:0 N:(-1.000000,0.000000,0.000000) S:F
P:(-0.6159690618515015, 0.01772703416645527, 0.9793898463249207), U:(0.8122022747993469, 0.9094325304031372), N:(-0.7674794793128967, 0.08630634844303131, 0.635181725025177), C:(0.929411768913269, 1.0, 0.003921568859368563)
P:(-0.6159690022468567, 0.7281867861747742, 1.0621156692504883), U:(0.9479727745056152, 0.8936232924461365), N:(-0.6028626561164856, 0.35294654965400696, 0.7155064344406128), C:(0.929411768913269, 1.0, 0.003921568859368563)
P:(-0.6159690022468567, 1.0, 0.6069812774658203), U:(0.9999170303344727, 0.9806004762649536), N:(-0.5541856288909912, 0.7238990664482117, -0.4108401834964752), C:(0.929411768913269, 1.0, 0.0)
P:(-0.6159690618515015, 0.1418156772851944, 0.6069812774658203), U:(0.8359159827232361, 0.9806005954742432), N:(-0.7070833444595337, 0.0, -0.7070833444595337), C:(0.9333333373069763, 1.0, 0.05098039284348488)
F:2 M:0 N:(0.000000,0.858546,0.512737) S:F
P:(-0.6159690022468567, 0.7281867861747742, 1.0621156692504883), U:(0.5792591571807861, 0.008944153785705566), N:(-0.6028626561164856, 0.35294654965400696, 0.7155064344406128), C:(0.929411768913269, 1.0, 0.003921568859368563)
P:(0.6159691214561462, 0.7281866669654846, 1.0621156692504883), U:(0.5792591571807861, 0.2862038016319275), N:(0.6028626561164856, 0.35294654965400696, 0.7155064344406128), C:(0.929411768913269, 1.0, 0.0)
P:(0.6159691214561462, 0.9999999403953552, 0.6069812774658203), U:(0.45994994044303894, 0.28620368242263794), N:(0.5541856288909912, 0.7238990664482117, -0.4108401834964752), C:(1.0, 1.0, 0.0)
P:(-0.6159690022468567, 1.0, 0.6069812774658203), U:(0.45994997024536133, 0.008944228291511536), N:(-0.5541856288909912, 0.7238990664482117, -0.4108401834964752), C:(0.929411768913269, 1.0, 0.0)
F:3 M:0 N:(1.000000,-0.000000,-0.000000) S:F
P:(0.6159690618515015, 0.017726941034197807, 0.9793898463249207), U:(0.18779747188091278, 0.9094322323799133), N:(0.7674794793128967, 0.08630634844303131, 0.635181725025177), C:(0.929411768913269, 1.0, 0.0)
P:(0.6159690022468567, -1.0, 1.3930187225341797), U:(0.38228708505630493, 0.8303871154785156), N:(0.5597094893455505, -0.46266061067581177, 0.6874599456787109), C:(1.0, 1.0, 0.0)
P:(0.6159690022468567, -1.0, 0.6069812774658203), U:(0.3822869658470154, 0.9806003570556641), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.9333333373069763, 1.0, 0.04313725605607033)
P:(0.6159690618515015, 0.14181558787822723, 0.6069812774658203), U:(0.16408395767211914, 0.9806002378463745), N:(0.7070833444595337, 0.0, -0.7070833444595337), C:(1.0, 1.0, 0.0)
F:4 M:0 N:(0.000000,-0.000000,-1.000000) S:F
P:(0.6159690618515015, 0.14181558787822723, 0.6069812774658203), U:(0.2668073773384094, 0.28620344400405884), N:(0.7070833444595337, 0.0, -0.7070833444595337), C:(1.0, 1.0, 0.0)
P:(-0.6159690618515015, 0.1418156772851944, 0.6069812774658203), U:(0.2668076157569885, 0.008944153785705566), N:(-0.7070833444595337, 0.0, -0.7070833444595337), C:(0.9333333373069763, 1.0, 0.05098039284348488)
P:(-0.6159690022468567, 1.0, 0.6069812774658203), U:(0.45994997024536133, 0.008944228291511536), N:(-0.5541856288909912, 0.7238990664482117, -0.4108401834964752), C:(0.929411768913269, 1.0, 0.0)
P:(0.6159691214561462, 0.9999999403953552, 0.6069812774658203), U:(0.45994994044303894, 0.28620368242263794), N:(0.5541856288909912, 0.7238990664482117, -0.4108401834964752), C:(1.0, 1.0, 0.0)
F:5 M:0 N:(0.000000,0.376516,0.926410) S:F
P:(0.6159690618515015, 0.017726941034197807, 0.9793898463249207), U:(0.7402355074882507, 0.28620395064353943), N:(0.7674794793128967, 0.08630634844303131, 0.635181725025177), C:(0.929411768913269, 1.0, 0.0)
P:(-0.6159690618515015, 0.01772703416645527, 0.9793898463249207), U:(0.740235447883606, 0.008944094181060791), N:(-0.7674794793128967, 0.08630634844303131, 0.635181725025177), C:(0.929411768913269, 1.0, 0.003921568859368563)
P:(-0.6159691214561462, -0.9999999403953552, 1.3930187225341797), U:(0.9874796867370605, 0.008944094181060791), N:(-0.5597094893455505, -0.46266061067581177, 0.6874599456787109), C:(0.9686274528503418, 1.0, 0.003921568859368563)
P:(0.6159690022468567, -1.0, 1.3930187225341797), U:(0.9874798059463501, 0.28620395064353943), N:(0.5597094893455505, -0.46266061067581177, 0.6874599456787109), C:(1.0, 1.0, 0.0)
F:6 M:0 N:(-1.000000,0.000000,0.000000) S:F
P:(-0.6159691214561462, -0.9999999403953552, 1.3930187225341797), U:(0.6177127957344055, 0.8303872346878052), N:(-0.5597094893455505, -0.46266061067581177, 0.6874599456787109), C:(0.9686274528503418, 1.0, 0.003921568859368563)
P:(-0.6159690618515015, 0.01772703416645527, 0.9793898463249207), U:(0.8122022747993469, 0.9094325304031372), N:(-0.7674794793128967, 0.08630634844303131, 0.635181725025177), C:(0.929411768913269, 1.0, 0.003921568859368563)
P:(-0.6159690618515015, 0.1418156772851944, 0.6069812774658203), U:(0.8359159827232361, 0.9806005954742432), N:(-0.7070833444595337, 0.0, -0.7070833444595337), C:(0.9333333373069763, 1.0, 0.05098039284348488)
P:(-0.6159691214561462, -0.9999999403953552, 0.6069812774658203), U:(0.617712676525116, 0.9806004762649536), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(1.0, 1.0, 0.0)
F:7 M:0 N:(1.000000,-0.000000,-0.000000) S:F
P:(0.6159691214561462, 0.7281866669654846, 1.0621156692504883), U:(0.052026983350515366, 0.8936232924461365), N:(0.6028626561164856, 0.35294654965400696, 0.7155064344406128), C:(0.929411768913269, 1.0, 0.0)
P:(0.6159690618515015, 0.017726941034197807, 0.9793898463249207), U:(0.18779747188091278, 0.9094322323799133), N:(0.7674794793128967, 0.08630634844303131, 0.635181725025177), C:(0.929411768913269, 1.0, 0.0)
P:(0.6159690618515015, 0.14181558787822723, 0.6069812774658203), U:(0.16408395767211914, 0.9806002378463745), N:(0.7070833444595337, 0.0, -0.7070833444595337), C:(1.0, 1.0, 0.0)
P:(0.6159691214561462, 0.9999999403953552, 0.6069812774658203), U:(8.296234591398388e-05, 0.9806005954742432), N:(0.5541856288909912, 0.7238990664482117, -0.4108401834964752), C:(1.0, 1.0, 0.0)
F:8 M:0 N:(0.000000,-0.000000,-1.000000) S:F
P:(0.6159690022468567, -1.0, 0.6069812774658203), U:(0.009831011295318604, 0.2862032651901245), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.9333333373069763, 1.0, 0.04313725605607033)
P:(-0.6159691214561462, -0.9999999403953552, 0.6069812774658203), U:(0.00983119010925293, 0.008943945169448853), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(1.0, 1.0, 0.0)
P:(-0.6159690618515015, 0.1418156772851944, 0.6069812774658203), U:(0.2668076157569885, 0.008944153785705566), N:(-0.7070833444595337, 0.0, -0.7070833444595337), C:(0.9333333373069763, 1.0, 0.05098039284348488)
P:(0.6159690618515015, 0.14181558787822723, 0.6069812774658203), U:(0.2668073773384094, 0.28620344400405884), N:(0.7070833444595337, 0.0, -0.7070833444595337), C:(1.0, 1.0, 0.0)
F:9 M:0 N:(-0.000000,-0.115658,0.993289) S:F
P:(0.6159691214561462, 0.7281866669654846, 1.0621156692504883), U:(0.5792591571807861, 0.2862038016319275), N:(0.6028626561164856, 0.35294654965400696, 0.7155064344406128), C:(0.929411768913269, 1.0, 0.0)
P:(-0.6159690022468567, 0.7281867861747742, 1.0621156692504883), U:(0.5792591571807861, 0.008944153785705566), N:(-0.6028626561164856, 0.35294654965400696, 0.7155064344406128), C:(0.929411768913269, 1.0, 0.003921568859368563)
P:(-0.6159690618515015, 0.01772703416645527, 0.9793898463249207), U:(0.740235447883606, 0.008944094181060791), N:(-0.7674794793128967, 0.08630634844303131, 0.635181725025177), C:(0.929411768913269, 1.0, 0.003921568859368563)
P:(0.6159690618515015, 0.017726941034197807, 0.9793898463249207), U:(0.7402355074882507, 0.28620395064353943), N:(0.7674794793128967, 0.08630634844303131, 0.635181725025177), C:(0.929411768913269, 1.0, 0.0)
Name:WheelBkPort
Parent:CarBody
M:0-BackPortMaterial T:SimplifiedCarFrontPort.png C:(0.800000011920929, 0.800000011920929, 0.800000011920929)
WMR0:(1.0, 0.0, 0.0, -0.9599999785423279)
WMR1:(0.0, 1.0, 0.0, -1.0)
WMR2:(0.0, 0.0, 1.0, 0.30000001192092896)
WMR3:(0.0, 0.0, 0.0, 1.0)
F:0 M:0 N:(-0.000000,-1.000000,0.000000) S:F
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(1.0000817775726318, 0.4998322129249573), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.750124990940094, 0.4998321533203125), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.7501248121261597, 0.2498754858970642), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(1.0000817775726318, 0.24987530708312988), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:1 M:0 N:(-1.000000,0.000000,0.000000) S:F
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.750124990940094, 0.4998321533203125), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.5001685619354248, 0.4998323321342468), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.5001682043075562, 0.24987581372261047), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.7501248121261597, 0.2498754858970642), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:2 M:0 N:(0.000000,1.000000,0.000000) S:F
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.5001685619354248, 0.4998323321342468), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(0.2502121329307556, 0.49983274936676025), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(0.2502116858959198, 0.24987617135047913), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.5001682043075562, 0.24987581372261047), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
F:3 M:0 N:(1.000000,-0.000000,0.000000) S:F
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(0.2502121329307556, 0.49983274936676025), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(0.0002555549144744873, 0.49983328580856323), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(0.0002550482749938965, 0.24987667798995972), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(0.2502116858959198, 0.24987617135047913), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:4 M:0 N:(0.000000,-0.000000,-1.000000) S:F
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(1.0000817775726318, 0.24987530708312988), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.7501248121261597, 0.2498754858970642), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.7501246929168701, -8.171796798706055e-05), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(1.0000818967819214, -8.183717727661133e-05), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:5 M:0 N:(0.000000,-0.000000,1.000000) S:F
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(1.0000817775726318, 0.7497890591621399), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.750124990940094, 0.7497889399528503), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.750124990940094, 0.4998321533203125), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(1.0000817775726318, 0.4998322129249573), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
Name:WheelBkStarboard
Parent:CarBody
M:0-BackStarboardMaterial T:SimplifiedCarFrontPort.png C:(0.800000011920929, 0.800000011920929, 0.800000011920929)
WMR0:(1.0, 0.0, 0.0, 0.949999988079071)
WMR1:(0.0, 1.0, 0.0, -1.0)
WMR2:(0.0, 0.0, 1.0, 0.30000001192092896)
WMR3:(0.0, 0.0, 0.0, 1.0)
F:0 M:0 N:(-0.000000,-1.000000,0.000000) S:F
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(1.0009549856185913, 0.49895909428596497), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.7509981393814087, 0.4989590346813202), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.7509979009628296, 0.2490023672580719), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(1.0009549856185913, 0.24900218844413757), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:1 M:0 N:(-1.000000,0.000000,0.000000) S:F
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.7509981393814087, 0.4989590346813202), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.5010417103767395, 0.4989592134952545), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.5010412931442261, 0.24900269508361816), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.7509979009628296, 0.2490023672580719), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:2 M:0 N:(0.000000,1.000000,0.000000) S:F
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.5010417103767395, 0.4989592134952545), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(0.2510852813720703, 0.49895963072776794), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(0.2510848045349121, 0.24900305271148682), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.5010412931442261, 0.24900269508361816), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
F:3 M:0 N:(1.000000,-0.000000,0.000000) S:F
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(0.2510852813720703, 0.49895963072776794), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(0.0011286735534667969, 0.4989601671695709), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(0.001128166913986206, 0.2490035593509674), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(0.2510848045349121, 0.24900305271148682), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:4 M:0 N:(0.000000,-0.000000,-1.000000) S:F
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(1.0009549856185913, 0.24900218844413757), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.7509979009628296, 0.2490023672580719), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.75099778175354, -0.0009548366069793701), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(1.0009549856185913, -0.0009549558162689209), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:5 M:0 N:(0.000000,-0.000000,1.000000) S:F
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(1.0009549856185913, 0.7489159107208252), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.7509981393814087, 0.7489157915115356), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.7509981393814087, 0.4989590346813202), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(1.0009549856185913, 0.49895909428596497), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
Name:WheelFtPort
Parent:CarBody
M:0-FrontPortMaterial T:SimplifiedCarFrontPort.png C:(0.800000011920929, 0.800000011920929, 0.800000011920929)
WMR0:(1.0, 0.0, 0.0, -0.9599999785423279)
WMR1:(0.0, 1.0, 0.0, 0.9999998211860657)
WMR2:(0.0, 0.0, 1.0, 0.30000001192092896)
WMR3:(0.0, 0.0, 0.0, 1.0)
F:0 M:0 N:(-0.000000,-1.000000,0.000000) S:F
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(0.25004422664642334, 0.25004151463508606), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.5000007748603821, 0.2500423192977905), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.5000001788139343, 0.49999886751174927), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(0.2500433921813965, 0.49999815225601196), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:1 M:0 N:(-1.000000,0.000000,0.000000) S:F
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.5000007748603821, 0.2500423192977905), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.7499570846557617, 0.2500428855419159), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.7499568462371826, 0.4999992251396179), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.5000001788139343, 0.49999886751174927), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:2 M:0 N:(0.000000,1.000000,0.000000) S:F
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.7499570846557617, 0.2500428855419159), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(0.9999133348464966, 0.2500429153442383), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(0.999913215637207, 0.49999916553497314), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.7499568462371826, 0.4999992251396179), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
F:3 M:0 N:(1.000000,-0.000000,0.000000) S:F
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(8.738040924072266e-05, 0.2500404715538025), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(0.25004422664642334, 0.25004151463508606), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(0.2500433921813965, 0.49999815225601196), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(8.654594421386719e-05, 0.49999719858169556), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:4 M:0 N:(0.000000,-0.000000,-1.000000) S:F
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(0.9999133944511414, 0.749955415725708), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.7499570846557617, 0.7499555945396423), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.7499568462371826, 0.4999992251396179), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(0.999913215637207, 0.49999916553497314), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:5 M:0 N:(0.000000,-0.000000,1.000000) S:F
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(0.9999133348464966, 0.2500429153442383), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.7499570846557617, 0.2500428855419159), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.7499572038650513, 8.666515350341797e-05), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(0.9999133944511414, 8.678436279296875e-05), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
Name:FtPortPropeller
Parent:WheelFtPort
M:0-FrontPortPropelerMaterial T:SimplifiedCarFrontPortProp.png C:(0.800000011920929, 0.800000011920929, 0.800000011920929)
WMR0:(1.0, 0.0, 0.0, -1.25)
WMR1:(0.0, 1.0, 0.0, 1.0)
WMR2:(0.0, 0.0, 1.0, 0.3333300054073334)
WMR3:(0.0, 0.0, 0.0, 1.0)
F:0 M:0 N:(-1.000000,0.000000,0.000000) S:F
P:(-0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.501366138458252, 0.5319569706916809), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.05822812020778656, 0.5319639444351196), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.05822690576314926, 0.4211801588535309), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.5013640522956848, 0.42117220163345337), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
F:1 M:0 N:(0.000000,1.000000,-0.000000) S:F
P:(-0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.05822812020778656, 0.5319639444351196), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.0028362085577100515, 0.5319638848304749), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.0028361277654767036, 0.4211793541908264), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.05822690576314926, 0.4211801588535309), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
F:2 M:0 N:(1.000000,0.000000,-0.000000) S:F
P:(0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.9999024271965027, 0.5319446325302124), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.5567590594291687, 0.531956136226654), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.5567563772201538, 0.4211706221103668), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.999899685382843, 0.4211588203907013), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
F:3 M:0 N:(0.000000,-1.000000,0.000000) S:F
P:(0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.5567590594291687, 0.531956136226654), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.501366138458252, 0.5319569706916809), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.5013640522956848, 0.42117220163345337), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.5567563772201538, 0.4211706221103668), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
F:4 M:0 N:(-0.000000,0.000000,-1.000000) S:F
P:(-0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.0554872564971447, 9.955011773854494e-05), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.05822690576314926, 0.4211801588535309), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.0028361277654767036, 0.4211793541908264), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(9.761026012711227e-05, 9.761026012711227e-05), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
F:5 M:0 N:(-0.000000,0.000000,1.000000) S:F
P:(0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.005591780412942171, 0.953059732913971), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.0028362085577100515, 0.5319638848304749), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.05822812020778656, 0.5319639444351196), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.060983531177043915, 0.9530596733093262), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
Name:WheelFtStarboard
Parent:CarBody
M:0-FrontStarboardMaterial T:SimplifiedCarFrontPort.png C:(0.800000011920929, 0.800000011920929, 0.800000011920929)
WMR0:(1.0, 0.0, 0.0, 0.949999988079071)
WMR1:(0.0, 1.0, 0.0, 1.0)
WMR2:(0.0, 0.0, 1.0, 0.30000001192092896)
WMR3:(0.0, 0.0, 0.0, 1.0)
F:0 M:0 N:(-0.000000,-1.000000,0.000000) S:F
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(0.7499573230743408, 0.25004249811172485), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.9999132752418518, 0.25004273653030396), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.999913215637207, 0.49999862909317017), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(0.7499569654464722, 0.4999988079071045), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:1 M:0 N:(-1.000000,0.000000,0.000000) S:F
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(8.830868318909779e-05, 0.2500384449958801), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.2500448524951935, 0.2500401735305786), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.2500433325767517, 0.49999675154685974), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(8.658744627609849e-05, 0.4999949634075165), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:2 M:0 N:(0.000000,1.000000,0.000000) S:F
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.2500448524951935, 0.2500401735305786), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(0.5000011920928955, 0.250041663646698), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(0.5000001192092896, 0.4999980628490448), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.2500433325767517, 0.49999675154685974), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
F:3 M:0 N:(1.000000,-0.000000,0.000000) S:F
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(0.5000011920928955, 0.250041663646698), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(0.7499573230743408, 0.25004249811172485), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(0.7499569654464722, 0.4999988079071045), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(0.5000001192092896, 0.4999980628490448), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:4 M:0 N:(0.000000,-0.000000,-1.000000) S:F
P:(0.29999998211860657, -0.30000004172325134, -0.30000001192092896), U:(0.7499569654464722, 0.4999988079071045), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, -0.30000001192092896), U:(0.999913215637207, 0.49999862909317017), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, -0.30000001192092896), U:(0.9999135136604309, 0.7499545216560364), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.11372549086809158, 0.003921568859368563, 1.0)
P:(0.30000004172325134, 0.29999998211860657, -0.30000001192092896), U:(0.7499575614929199, 0.7499548196792603), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
F:5 M:0 N:(0.000000,-0.000000,1.000000) S:F
P:(0.30000004172325134, 0.29999998211860657, 0.30000001192092896), U:(0.7499576210975647, 8.658744627609849e-05), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.29999998211860657, 0.30000004172325134, 0.30000001192092896), U:(0.9999135136604309, 8.686506771482527e-05), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
P:(-0.30000004172325134, -0.29999998211860657, 0.30000001192092896), U:(0.9999132752418518, 0.25004273653030396), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.12941177189350128, 0.0235294122248888, 1.0)
P:(0.29999998211860657, -0.30000004172325134, 0.30000001192092896), U:(0.7499573230743408, 0.25004249811172485), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.10980392247438431, 0.0, 1.0)
Name:FtStarboardPropeller
Parent:WheelFtStarboard
M:0-FrontStarboardPropMaterial T:SimplifiedCarFrontPortProp.png C:(0.800000011920929, 0.800000011920929, 0.800000011920929)
WMR0:(1.0, 0.0, 0.0, 1.25)
WMR1:(0.0, 1.0, 0.0, 1.0)
WMR2:(0.0, 0.0, 1.0, 0.3333300054073334)
WMR3:(0.0, 0.0, 0.0, 1.0)
F:0 M:0 N:(-1.000000,0.000000,0.000000) S:F
P:(-0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.9997372627258301, 0.5350660681724548), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.5566012263298035, 0.5350320935249329), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.5566093921661377, 0.42424821853637695), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.999745786190033, 0.4242819547653198), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
F:1 M:0 N:(0.000000,1.000000,-0.000000) S:F
P:(-0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.5566012263298035, 0.5350320935249329), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.501209557056427, 0.5350278615951538), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.5012171864509583, 0.4242442846298218), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.5566093921661377, 0.42424821853637695), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
F:2 M:0 N:(1.000000,0.000000,-0.000000) S:F
P:(0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.501209557056427, 0.5350278615951538), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.05807733163237572, 0.5349979400634766), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.05808385834097862, 0.42421552538871765), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.5012171864509583, 0.4242442846298218), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
F:3 M:0 N:(0.000000,-1.000000,0.000000) S:F
P:(0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.05807733163237572, 0.5349979400634766), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.0026867054402828217, 0.5349938869476318), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.0026956163346767426, 0.4242139160633087), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.05808385834097862, 0.42421552538871765), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
F:4 M:0 N:(-0.000000,0.000000,-1.000000) S:F
P:(-0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.0026956163346767426, 0.4242139160633087), N:(-0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(-5.901232361793518e-05, 0.0031646303832530975), N:(-0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, 0.20000000298023224, -0.05000000074505806), U:(0.055326636880636215, 0.0031646303832530975), N:(0.5773491859436035, 0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, -0.20000000298023224, -0.05000000074505806), U:(0.05808385834097862, 0.42421552538871765), N:(0.5773491859436035, -0.5773491859436035, -0.5773491859436035), C:(0.0,0.0,0.0,0.0)
F:5 M:0 N:(-0.000000,0.000000,1.000000) S:F
P:(0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.05807733163237572, 0.5349979400634766), N:(0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.06080096587538719, 0.956089973449707), N:(0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, 0.20000000298023224, 0.05000000074505806), U:(0.005409713834524155, 0.9560858607292175), N:(-0.5773491859436035, 0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
P:(-0.02500000037252903, -0.20000000298023224, 0.05000000074505806), U:(0.0026867054402828217, 0.5349938869476318), N:(-0.5773491859436035, -0.5773491859436035, 0.5773491859436035), C:(0.0,0.0,0.0,0.0)
Total Unique Vertices:60
Total Faces:46
Total Materials:0

 

There is no advantage to exporting to a binary format unless that binary format can be loaded straight into your data types, which at the very least would require a much greater knowledge of Python. What you really want to do is load your model class with this data and then serialize it so that the binary model file perfectly matches your model class data and thus is most efficient for loading into the model class to load a model. So, whatever you export, it's probably going to get converted; so it might as well be human readable.

 

This format is similar in some ways to the OBJ format which might be a good one to learn as well.

 

I called this file format the PUNC file format for Position, UV, Normal, Color as that's the data in the vertices. The first line allows you to verify that it is a PUNC file. The second line identifies the type of PUNC file as I was planning on a separate format for skinned animation from the rigid animation format. Then it goes into a loop of model parts/meshes. The first line in the loop is the sub-mesh name which in this case is the body of the car. If the sub-mesh has a parent because it was parented in Blender the second line in the loop will identify which sub-mesh is it's parent. (I believe there are two ways to parent something in Blender and one works for this and the other doesn't, but I can't remember off the top of my head what that is.)  The CarBody has no parent, which makes it the root mesh and it will be the parent of all the others (which are wheels).

 

The next line identifies the material used for the sub-mesh. I think this allows multiple materials per sub-mesh assigning each a number starting from 0. Then the T identifies any texture image file used for that material and C identifies any color used for the entire material.

 

After that it has the WMR lines. This is a world/object matrix for the sub-mesh. I think WMR stood for "World Matrix Row". It's just a 4 by 4 world matrix. These matrices are the relative positions and offsets of each sub-mesh from each other as assigned in Blender. In this case, this positions the wheels relative to the car body. And the body is an identity matrix basically placing it at the world origin.

 

Then it goes into another sub-loop describing "faces". This is going to use a triangle list rather than a triangle strip or triangle fan. So, it's basically a list of triangles. But Blender basically does things in faces rather than in triangles. So, the first face has four vertices. You have to figure out how to break this up into triangles. I assume all Blender faces have co-planar triangles, which means it should not matter how you assign the edges to triangulate the face. I usually triangulate the model in Blender before exporting. I've only tested this format a little and so there may still be some bugs. I think the most complicated model I tested with was Blender's Suzanne monkey.

 

But for each face it gives the material number which references the texture and color at the beginning. Then it defines a face normal. And I think the S value was for flat shading or smooth shading allowing some faces to be smooth and others faceted in the same sub-mesh. As I get better at modeling, I am appreciating that more. Then it's a vertex list of all the vertices in that face, their positions, UV coordinates, vertex normals, and colors. Note that you may not always use all of these values. UV coordinates and vertex colors are often mutually exclusive. You tend to use one or the other, but not both. You can use both, which is why it's like this. But usually you either use a texture or vertex colors and not both at the same time.

 

So, then it just repeats that loop going through every face of the sub-mesh and expecting you to break the faces up into triangles. The list ends when you hit the name of the next sub-mesh. I don't think the sub-meshes are in parent-child order and so you may have to read them in and then figure out which is the parent and which is the child.

 

The file ends when it lists the "Total Unique Vertices". Looks like that total number of materials is broken. I don't think I actually used those last 3 lines when reading the data back in. But that's basically all there is to it. It allows you to build a vertex buffer. And there's enough info there to build an index buffer if you want. My code loads the entire model and all its sub-meshes into a single vertex buffer and then uses offsets in the buffer for each sub-mesh.

In my C++ code, I used two classes for the model. I have a Model Compiler class that reads this file and I have a RigidModel class that it reads the data into. The model class looks like this:

RigidModel.h

#pragma once
#include <d3d11.h>
#include <DirectXMath.h>
#include <DxErr.h>
#include <vector>        //NOT actual vectors. These are dynamic arrays.
#include "TextureClass.h"
#include "ShaderClass.h"

struct PUNCVERTEX{ DirectX::XMFLOAT3 Position; DirectX::XMFLOAT2 UV; DirectX::XMFLOAT3 Normal; DirectX::XMFLOAT4 Color; };    //Describes the structure of our vertices.


//__declspec(align(16)) class MeshPart
class MeshPart
{
public:
    MeshPart();
    ~MeshPart();
    void Shutdown();
    void AddChild(MeshPart* Child);
    void Draw(ID3D11DeviceContext* GraphicsDeviceContext, ID3D11Buffer* VertexBuffer, ID3D11Buffer* IndexBuffer,
        DirectX::CXMMATRIX View, DirectX::CXMMATRIX Projection, ShaderClass Shader, XMFLOAT3 DiffuseLightDirection,
        XMFLOAT4 AmbientLightColor, XMFLOAT4 DiffuseLightColor, DirectX::CXMMATRIX FamilyWorld);
    std::string Name;
    DirectX::XMFLOAT4X4 World;
    unsigned int FirstVertexInBuffer;
    unsigned int FirstIndexInBuffer;
    unsigned int VerticesInThisMeshPart;
    unsigned int IndicesInThismeshPart;
    TextureClass* Texture;
    std::vector<MeshPart*> Children;
};


class RigidModel
{
public:
    RigidModel();
    ~RigidModel();
    bool RigidModel::BuildModelFromBuffer(PUNCVERTEX* VertexArray, int VerticesInModel,
        int* IndexArray, int IndicesInModel,
        ID3D11Device* GraphicsDevice, ID3D11DeviceContext* GraphicsDeviceContext);
    void Draw(ID3D11DeviceContext* GraphicsDeviceContext, CXMMATRIX View, CXMMATRIX Projection, ShaderClass Shader,
        XMFLOAT3 DiffuseLightDirection, XMFLOAT4 AmbientLightColor, XMFLOAT4 DiffuseLightColor);
    void Shutdown();
    std::string Name;
    MeshPart Root;
private:
    int VerticesInMesh;                            //Number of vertices used in the mesh.
    int IndicesInMesh;                            //Number of indices used in the index buffer.
    ID3D11Buffer* VertexBuffer;                 //The pointer to the vertex buffer
    ID3D11Buffer* IndexBuffer;                    //The pointer to the index buffer.
};

 

RigidModel.cpp]/b]

#include "RigidModel.h"


RigidModel::RigidModel()
{
}


RigidModel::~RigidModel()
{
    Shutdown();
}


bool RigidModel::BuildModelFromBuffer(PUNCVERTEX* VertexArray, int VerticesInModel,
    int* IndexArray, int IndicesInModel,
    ID3D11Device* GraphicsDevice, ID3D11DeviceContext* GraphicsDeviceContext)
{
    D3D11_BUFFER_DESC VertexBufferDescription;                //The settings for the vertex buffer.
    D3D11_SUBRESOURCE_DATA VertexBufferSubResourceData;        //How a sub-resource is to be
    D3D11_BUFFER_DESC IndexBufferDescription;                //The settings for the index buffer.
    D3D11_SUBRESOURCE_DATA IndexBufferSubResourceData;        //How a sub-resource is to be
    bool ModelCreatedSuccessfully = false;


    if (VertexBuffer == nullptr)    //Don't try and create a vertex buffer for the object if it's already created.
    {
        VerticesInMesh = VerticesInModel;                                            //We might want this info later so remember it.
        ZeroMemory(&VertexBufferDescription, sizeof(VertexBufferDescription));        //Initialize memory to zeros.
        VertexBufferDescription.Usage = D3D11_USAGE_DYNAMIC;                        //Read-write access type for CPU and GPU
        VertexBufferDescription.ByteWidth = sizeof(PUNCVERTEX) * VerticesInMesh;    //Total size of the vertex buffer.
        VertexBufferDescription.BindFlags = D3D11_BIND_VERTEX_BUFFER;                //Buffer is a vertex buffer.
        VertexBufferDescription.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;            //Allow CPU to write in buffer.

        VertexBufferSubResourceData.pSysMem = VertexArray;
        VertexBufferSubResourceData.SysMemPitch = NULL;
        VertexBufferSubResourceData.SysMemSlicePitch = NULL;


        if (SUCCEEDED(GraphicsDevice->CreateBuffer(&VertexBufferDescription, &VertexBufferSubResourceData, &VertexBuffer)))    //Create the vertex buffer or stop if it fails.
        {
            IndicesInMesh = IndicesInModel;
            ZeroMemory(&IndexBufferDescription, sizeof(IndexBufferDescription));
            IndexBufferDescription.Usage = D3D11_USAGE_DEFAULT;
            IndexBufferDescription.ByteWidth = sizeof(int) * IndicesInMesh;            //Total size of the index buffer.
            IndexBufferDescription.BindFlags = D3D11_BIND_INDEX_BUFFER;            //Buffer is an index buffer.
            IndexBufferDescription.CPUAccessFlags = NULL;    
            IndexBufferDescription.MiscFlags = NULL;


            IndexBufferSubResourceData.pSysMem = IndexArray;
            IndexBufferSubResourceData.SysMemPitch = NULL;
            IndexBufferSubResourceData.SysMemSlicePitch = NULL;
            if (SUCCEEDED(GraphicsDevice->CreateBuffer(&IndexBufferDescription, &IndexBufferSubResourceData, &IndexBuffer)))    //Create the vertex buffer or stop if it fails.
            {
                ModelCreatedSuccessfully = true;
            }
        }
    }

    return ModelCreatedSuccessfully;
}


void RigidModel::Shutdown()
{
    if (VertexBuffer != nullptr)
    {
        VertexBuffer->Release();
        VertexBuffer = nullptr;
    }

    Root.Shutdown();
}

void RigidModel::Draw(ID3D11DeviceContext* GraphicsDeviceContext, DirectX::CXMMATRIX View, DirectX::CXMMATRIX Projection, ShaderClass Shader,
    XMFLOAT3 DiffuseLightDirection, XMFLOAT4 AmbientLightColor, XMFLOAT4 DiffuseLightColor)
{
    Root.Draw(GraphicsDeviceContext, VertexBuffer, IndexBuffer, View, Projection, Shader, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor, DirectX::XMMatrixIdentity());
}

MeshPart::MeshPart()
{
    XMStoreFloat4x4(&World, DirectX::XMMatrixIdentity());
    Name.clear();
    Texture = nullptr;
    FirstVertexInBuffer = 0;
    VerticesInThisMeshPart = 0;
    IndicesInThismeshPart = 0;
}

MeshPart::~MeshPart()
{
    Shutdown();
}


void MeshPart::Shutdown()
{
    //Shutdown all children.
    for (auto Child : Children)
    {
        Child->Shutdown();
        //_aligned_free(Child);
        delete Child;
        Child = nullptr;
    }
    Children.clear();
    //Then Shutdown self.
    if (Texture != nullptr)
    {
        delete Texture;
        Texture = nullptr;
    }
}


void MeshPart::AddChild(MeshPart* Child)
{
    Children.push_back(Child);
}


void MeshPart::Draw(ID3D11DeviceContext* GraphicsDeviceContext, ID3D11Buffer* VertexBuffer, ID3D11Buffer* IndexBuffer,
    DirectX::CXMMATRIX View, DirectX::CXMMATRIX Projection, ShaderClass Shader,    XMFLOAT3 DiffuseLightDirection,
    XMFLOAT4 AmbientLightColor, XMFLOAT4 DiffuseLightColor, DirectX::CXMMATRIX FamilyWorld)
{
    UINT Stride = sizeof(PUNCVERTEX);    //Stride is the size of 1 vertex in memory.
    UINT Offset = 0;
    UINT IndexOffset = 0;
    DirectX::XMMATRIX RelationalWorld;

    RelationalWorld = XMLoadFloat4x4(&World) * FamilyWorld;    //This makes the submesh's world matrix relative to its parent which is relevant to its parent and so on.
    if (Texture == nullptr)
    {
        Shader.Settings(GraphicsDeviceContext, RelationalWorld, View, Projection, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor, nullptr, 0.0f);
    }
    else
    {
        Shader.Settings(GraphicsDeviceContext, RelationalWorld, View, Projection, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor, Texture->ResourceView(), 2.0f);
    }
    //Offset = FirstVertexInBuffer * sizeof(PUNCVERTEX);        //Offset is in bytes. FirstVertexInBuffer is in vertices.
    //IndexOffset = FirstIndexInBuffer * sizeof(int);
    GraphicsDeviceContext->IASetVertexBuffers(0, 1, &VertexBuffer, &Stride, &Offset);    //Select which vertex buffer to display.
    GraphicsDeviceContext->IASetIndexBuffer(IndexBuffer, DXGI_FORMAT_R32_UINT, IndexOffset);
    GraphicsDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);    //Select which primtive type we are using.
    GraphicsDeviceContext->DrawIndexed(IndicesInThismeshPart, FirstIndexInBuffer, FirstVertexInBuffer);        //Not using DrawIndexed. Draw the Bottom.


    if (Children.size() > 0)
    {
        for (auto Child : Children)
        {
            Child->Draw(GraphicsDeviceContext, VertexBuffer, IndexBuffer, View, Projection, Shader, DiffuseLightDirection, AmbientLightColor, DiffuseLightColor, RelationalWorld);
        }
    }
}

 

And this class loads the data into the RigidModel class from the Python export:

ModelCompiler.h

#pragma once
#include <fstream>
#include <sstream>
#include "RigidModel.h"
#include "StringConversions.h"


class Face
{
public:
    int CountVertices();
    int CountTriangles();
    Face();
    Face(std::string Header);
    void AddVertex(std::string CodedVertexString);
    PUNCVERTEX GetVertex(int VertexNumber);
private:
    unsigned int Number;
    unsigned int MaterialNumber;
    DirectX::XMFLOAT3 FaceNormal;
    bool SmoothShaded;
    std::vector<PUNCVERTEX> VerticesInFace;
};


class Material
{
public:
    Material();
    //Material(int No, std::string TextureFile, DirectX::XMFLOAT4 RGBColor);
    int Number;
    std::string Name;
    std::string Texture;
    DirectX::XMFLOAT4 Color;
};


class MeshPiece
{
public:
    std::string Name;
    std::string Parent;
    DirectX::XMFLOAT4X4 World;
    std::vector<Material> Materials;
    std::vector<PUNCVERTEX> Vertices;
    std::vector<Face> Faces;
    std::vector<PUNCVERTEX> MeshPiece::GetVertices();
private:
    void GetVerticesFromFaces();
};


class ModelCompiler
{
public:
    ModelCompiler();
    ~ModelCompiler();
    bool Build(RigidModel &Model, std::string FileName, ID3D11Device* GraphicsDevice, ID3D11DeviceContext* GraphicsDeviceContext);
    bool CreateModel(RigidModel &Model, std::vector<MeshPiece> SubMeshList, std::string TexturesFolder, ID3D11Device* GraphicsDevice,
        ID3D11DeviceContext* GraphicsDeviceContext);
    bool SaveToDisk(RigidModel &ModelToSave, PUNCVERTEX* VertexArray, int VerticesInModel, int* IndexArray, int IndicesInModel);
private:
    std::vector<MeshPiece> SubMeshes;
    Material CreateMaterial(std::string UnparsedText);
    void GetChildren(MeshPart* Parent, std::vector<PUNCVERTEX> &VertexBufferBuildList,
        std::vector<int> &IndexBufferBuildList, int &NumberOfVerticesAdded, int &NumberOfIndicesAdded,
        std::vector<MeshPiece>, std::string TexturesFolder, ID3D11Device* GraphicsDevice);
    void GetModelBuffers(PUNCVERTEX* &VertexBufferPtr, int &TotalNumberOfVertices,
        int* &IndexBufferPtr, int &TotalNumberOfIndices, std::vector<PUNCVERTEX> &VertexList);
    void GetPartBuffers(std::vector<PUNCVERTEX> &PartVertices, std::vector<PUNCVERTEX> &VertexBufferBuildList,
        std::vector<int> &IndexBufferBuildList, int &NumberOfVerticesAdded, int &NumberOfIndicesAdded);
    bool VerticesAreEqual(PUNCVERTEX A, PUNCVERTEX B);
    void SaveChildrenToDisk(int NumberOfChildren, MeshPart &Part, std::ofstream &ModelFile);
};

 

[b]ModelCompiler.cpp

#include "ModelCompiler.h"


ModelCompiler::ModelCompiler()
{
}


ModelCompiler::~ModelCompiler()
{
    //Free all the memory we allocated for the submeshes.
    for (auto Part : SubMeshes)
    {
        delete &Part;
    }
}


bool ModelCompiler::Build(RigidModel &Model, std::string FileName, ID3D11Device* GraphicsDevice, ID3D11DeviceContext* GraphicsDeviceContext)
{
    bool ModelCreatedSuccessfully = false;
    bool LoadedCorrectly = false;
    StringConversions StringConverter;
    std::ifstream InputFile(FileName);
    std::string LineOfText;
    unsigned int LineNumber = 0;
    MeshPiece* SubMesh = nullptr;
    std::string MatrixRow1 = "";
    std::string MatrixRow2 = "";
    std::string MatrixRow3 = "";
    


    std::getline(InputFile, LineOfText);
    if (LineOfText == "Punc!It Text")
    {
        //LineNumber++;
        std::getline(InputFile, LineOfText);
        if (LineOfText == "Rigid Animation")
        {
            //LineNumber++;
            std::getline(InputFile, LineOfText);
            while (!InputFile.eof())
            {
                if (LineOfText.substr(0, 5) == "Name:")
                {
                    if (SubMesh != nullptr) SubMeshes.push_back(*SubMesh);        //Every time you read a name it is a new submesh in the model so store the one just built.
                    SubMesh = new MeshPiece();                                //Start to build a new submesh.
                    SubMesh->Name = LineOfText.substr(5, LineOfText.length() - 5);    //Get the name of the submesh.
                    std::getline(InputFile, LineOfText);
                }
                else if (LineOfText.substr(0, 2) == "P:")
                {
                    //Line should already be processed as part of face, so just ignore it and go on.
                    std::getline(InputFile, LineOfText);
                }
                else if (LineOfText.substr(0, 2) == "F:")
                {
                    Face NewFace(LineOfText);
                    std::getline(InputFile, LineOfText);
                    while (LineOfText.substr(0, 2) == "P:")
                    {
                        NewFace.AddVertex(LineOfText);
                        std::getline(InputFile, LineOfText);
                    }
                    SubMesh->Faces.push_back(NewFace);
                }
                else if (LineOfText.substr(0, 2) == "M:")
                {
                    Material NewMaterial;
                    NewMaterial = CreateMaterial(LineOfText);
                    SubMesh->Materials.push_back(NewMaterial);
                    std::getline(InputFile, LineOfText);
                }
                else if (LineOfText.substr(0, 5) == "WMR0:")
                {
                    MatrixRow1 = LineOfText;
                    std::getline(InputFile, LineOfText);
                }
                else if (LineOfText.substr(0, 5) == "WMR1:")
                {
                    MatrixRow2 = LineOfText;
                    std::getline(InputFile, LineOfText);
                }
                else if (LineOfText.substr(0, 5) == "WMR2:")
                {
                    MatrixRow3 = LineOfText;
                    std::getline(InputFile, LineOfText);
                }
                else if (LineOfText.substr(0, 5) == "WMR3:")
                {
                    DirectX::XMStoreFloat4x4(&SubMesh->World, StringConverter.StringsToMatrix(MatrixRow1, MatrixRow2, MatrixRow3, LineOfText));
                    MatrixRow1.clear(); MatrixRow2.clear(); MatrixRow3.clear();
                    std::getline(InputFile, LineOfText);
                }
                else if (LineOfText.substr(0, 7) == "Parent:")
                {
                    SubMesh->Parent = LineOfText.substr(7, LineOfText.size() - 7);
                    std::getline(InputFile, LineOfText);
                }
                else if (LineOfText.substr(0, 22) == "Total Unique Vertices:")
                {
                    if (SubMesh != nullptr) SubMeshes.push_back(*SubMesh);    //EoF so a new Name: line will not be reached but we still need to save the submesh.
                    std::getline(InputFile, LineOfText);
                }
                else std::getline(InputFile, LineOfText);
            }
            InputFile.close();
            int LastSlash = FileName.find_last_of("/");
            int FileExtensionStart = FileName.find(".txt");
            Model.Name = FileName.substr(LastSlash + 1, FileExtensionStart - LastSlash -1);

            ModelCreatedSuccessfully = CreateModel(Model, SubMeshes, "./Models/", GraphicsDevice, GraphicsDeviceContext);
        }
    }
    return ModelCreatedSuccessfully;
}


Material ModelCompiler::CreateMaterial(std::string UnparsedText)
{
    Material NewMaterial;
    StringConversions StringConverter;
    std::vector<std::string> Delimiters;
    std::vector<std::string> ParsedStrings;
    std::string ColorValues;

    Delimiters.push_back("M:");
    Delimiters.push_back("-");
    Delimiters.push_back("T:");
    Delimiters.push_back("C:(");
    Delimiters.push_back(")");
    StringConverter.Parse(UnparsedText, Delimiters, ParsedStrings, true);
    Delimiters.clear();

    NewMaterial.Number = std::stoi(ParsedStrings[0]);
    NewMaterial.Name = ParsedStrings[1];
    NewMaterial.Texture = ParsedStrings[2];
    ColorValues = ParsedStrings[3];
    Delimiters.push_back(",");
    ParsedStrings.clear();
    StringConverter.Parse(ColorValues, Delimiters, ParsedStrings, false);
    NewMaterial.Color.x = std::stof(ParsedStrings[0]);    //Red
    NewMaterial.Color.y = std::stof(ParsedStrings[1]);    //Green
    NewMaterial.Color.z = std::stof(ParsedStrings[2]);    //Blue
    NewMaterial.Color.w = 1.0f;    //Set alpha to 100%. Blender does not export an alpha value.
    return NewMaterial;
}


bool ModelCompiler::CreateModel(RigidModel &Model, std::vector<MeshPiece> SubMeshList, std::string TexturesFolder, ID3D11Device* GraphicsDevice,
    ID3D11DeviceContext* GraphicsDeviceContext)
{
    bool ModelCreatedSuccessfully = false;
    std::vector<MeshPart> PartsTree;
    PUNCVERTEX* VertexBufferPtr = nullptr;
    int* IndexBufferPtr = nullptr;
    int NumberOfIndices = 0;
    int TotalNumberOfVertices = 0;
    int TotalNumberOfIndices = 0;
    std::vector<PUNCVERTEX> BuildList;
    std::vector<PUNCVERTEX> VertexList;
    std::vector<int> IndexList;
    int Root = -1;

    MeshPart* RootMeshPart = &Model.Root;
    std::vector<MeshPiece>::iterator MeshListIterator;
    TextureClass* NewTexture;
    bool ExitLoop = false;


    MeshListIterator = SubMeshList.begin();
    while (!ExitLoop)
    {
        
        if (MeshListIterator->Parent == "None")
        {
            Root = (int) (MeshListIterator - SubMeshList.begin());    //Store the index withing the vector to the root mesh of the model.
            ExitLoop = true;
        }
        if (MeshListIterator == SubMeshList.end()) ExitLoop = true;
            else MeshListIterator++;
    }
    
    RootMeshPart->Name = SubMeshList[Root].Name;
    RootMeshPart->World = SubMeshList[Root].World;
    if (SubMeshList[Root].Materials.size() > 0)    //No materials means there are no textures.
    {
        NewTexture = new TextureClass();
        NewTexture->Load(TexturesFolder + SubMeshList[Root].Materials[0].Texture, GraphicsDevice);    //Only handles 1 material. Probably need to change this.
        RootMeshPart->Texture = NewTexture;
    }
    RootMeshPart->FirstVertexInBuffer = 0;
    BuildList = SubMeshList[Root].GetVertices();
    
    GetPartBuffers(BuildList, VertexList, IndexList, TotalNumberOfVertices, TotalNumberOfIndices);
    
    RootMeshPart->VerticesInThisMeshPart = (int) TotalNumberOfVertices;
    RootMeshPart->IndicesInThismeshPart = (int)TotalNumberOfIndices;
    
    GetChildren(RootMeshPart, VertexList, IndexList, TotalNumberOfVertices, TotalNumberOfIndices, SubMeshList, TexturesFolder, GraphicsDevice);

    DirectX::XMMATRIX RotatedWorld;
    RotatedWorld = DirectX::XMLoadFloat4x4(&RootMeshPart->World);
    RotatedWorld *= DirectX::XMMatrixRotationX(-DirectX::XM_PIDIV2);    //Conversion to correct Blender world coordinate system difference to DX coordinate system.
    DirectX::XMStoreFloat4x4(&RootMeshPart->World, RotatedWorld);
    
    VertexBufferPtr = new PUNCVERTEX[TotalNumberOfVertices];
    IndexBufferPtr = new int[TotalNumberOfIndices];

    ZeroMemory(VertexBufferPtr, sizeof(PUNCVERTEX) * TotalNumberOfVertices);
    for (int VertexNumber = 0; VertexNumber < TotalNumberOfVertices; VertexNumber++)
    {
        VertexBufferPtr[VertexNumber] = VertexList[VertexNumber];
    }

    ZeroMemory(IndexBufferPtr, sizeof(int) * TotalNumberOfIndices);
    for (int IndexNumber = 0; IndexNumber < TotalNumberOfIndices; IndexNumber++)
    {
        IndexBufferPtr[IndexNumber] = IndexList[IndexNumber];
    }

    ModelCreatedSuccessfully = Model.BuildModelFromBuffer(VertexBufferPtr, TotalNumberOfVertices,
        IndexBufferPtr, TotalNumberOfIndices, GraphicsDevice, GraphicsDeviceContext);

    //Save Model to File.
    if (ModelCreatedSuccessfully)
        ModelCreatedSuccessfully = SaveToDisk(Model, VertexBufferPtr, TotalNumberOfVertices, IndexBufferPtr, TotalNumberOfIndices);

    delete[] VertexBufferPtr;    //Vertex buffer has been copied to an ID3D11Buffer and this array we allocated is no longer needed after that.
    VertexBufferPtr = nullptr;
    delete[] IndexBufferPtr;
    IndexBufferPtr = nullptr;

    return ModelCreatedSuccessfully;
}


bool ModelCompiler::SaveToDisk(RigidModel &ModelToSave, PUNCVERTEX* VertexArray, int VerticesInModel,
    int* IndexArray, int IndicesInModel)
{
    bool SavedProperly = false;
    //std::string ModelFileName = ModelToSave.Name;
    std::ofstream ModelFile;
    const char* FileID{"Rigid Model File"};

    ModelFile.open("CompiledModel.RMF", std::ios_base::binary);
    ModelFile.write(FileID, 16);
    ModelFile.write((char*)&VerticesInModel, sizeof(VerticesInModel));
    ModelFile.write((char*)VertexArray, sizeof(PUNCVERTEX)*VerticesInModel);
    ModelFile.write((char*)&IndicesInModel, sizeof(IndicesInModel));
    ModelFile.write((char*)IndexArray, sizeof(int)*IndicesInModel);
    ModelFile.write(ModelToSave.Name.c_str(), ModelToSave.Name.size()+1);    //Write the string with the null terminator.
    //Write Root.
    ModelFile.write(ModelToSave.Root.Name.c_str(), ModelToSave.Root.Name.size() + 1);    //Write root node name with the string null terminator.
    ModelFile.write((char*)&ModelToSave.Root.FirstVertexInBuffer, sizeof(unsigned int));
    ModelFile.write((char*)&ModelToSave.Root.VerticesInThisMeshPart, sizeof(unsigned int));
    ModelFile.write((char*)&ModelToSave.Root.FirstIndexInBuffer, sizeof(unsigned int));
    ModelFile.write((char*)&ModelToSave.Root.IndicesInThismeshPart, sizeof(unsigned int));
    ModelFile.write((char*)&ModelToSave.Root.World._11, sizeof(float));    //World Matrix.
    ModelFile.write((char*)&ModelToSave.Root.World._12, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._13, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._14, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._21, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._22, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._23, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._24, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._31, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._32, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._33, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._34, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._41, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._42, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._43, sizeof(float));
    ModelFile.write((char*)&ModelToSave.Root.World._44, sizeof(float));
    ModelFile.write(ModelToSave.Root.Texture->GetFileName().c_str(), ModelToSave.Root.Texture->GetFileName().size() + 1);
    int NumberOfChildren = ModelToSave.Root.Children.size();
    ModelFile.write((char*)&NumberOfChildren, sizeof(unsigned int));
    
    SaveChildrenToDisk(NumberOfChildren, ModelToSave.Root, ModelFile);

    ModelFile.close();    
    SavedProperly = true;

    return SavedProperly;
}


void ModelCompiler::SaveChildrenToDisk(int NumberOfChildren, MeshPart &Part, std::ofstream &ModelFile)
{
    if (NumberOfChildren > 0)
    {
        for (auto Child : Part.Children)
        {
            ModelFile.write(Child->Name.c_str(), Child->Name.size() + 1);    //Write the string with the null terminator.
            ModelFile.write((char*)&Child->FirstVertexInBuffer, sizeof(unsigned int));
            ModelFile.write((char*)&Child->VerticesInThisMeshPart, sizeof(unsigned int));
            ModelFile.write((char*)&Child->FirstIndexInBuffer, sizeof(unsigned int));
            ModelFile.write((char*)&Child->IndicesInThismeshPart, sizeof(unsigned int));
            ModelFile.write((char*)&Child->World._11, sizeof(float));    //World Matrix.
            ModelFile.write((char*)&Child->World._12, sizeof(float));
            ModelFile.write((char*)&Child->World._13, sizeof(float));
            ModelFile.write((char*)&Child->World._14, sizeof(float));
            ModelFile.write((char*)&Child->World._21, sizeof(float));
            ModelFile.write((char*)&Child->World._22, sizeof(float));
            ModelFile.write((char*)&Child->World._23, sizeof(float));
            ModelFile.write((char*)&Child->World._24, sizeof(float));
            ModelFile.write((char*)&Child->World._31, sizeof(float));
            ModelFile.write((char*)&Child->World._32, sizeof(float));
            ModelFile.write((char*)&Child->World._33, sizeof(float));
            ModelFile.write((char*)&Child->World._34, sizeof(float));
            ModelFile.write((char*)&Child->World._41, sizeof(float));
            ModelFile.write((char*)&Child->World._42, sizeof(float));
            ModelFile.write((char*)&Child->World._43, sizeof(float));
            ModelFile.write((char*)&Child->World._44, sizeof(float));
            ModelFile.write(Child->Texture->GetFileName().c_str(), Child->Texture->GetFileName().size() + 1);
            int NumberOfGrandChildren = Child->Children.size();
            ModelFile.write((char*)&NumberOfGrandChildren, sizeof(unsigned int));

            SaveChildrenToDisk(NumberOfGrandChildren, *Child, ModelFile);

        }
    }
}
 

bool ModelCompiler::VerticesAreEqual(PUNCVERTEX A, PUNCVERTEX B)
{
    return A.Position.x == B.Position.x
        && A.Position.y == B.Position.y
        && A.Position.z == B.Position.z
        && A.UV.x == B.UV.x
        && A.UV.y == B.UV.y
        && A.Normal.x == B.Normal.x
        && A.Normal.y == B.Normal.y
        && A.Normal.z == B.Normal.z
        && A.Color.x == B.Color.x
        && A.Color.y == B.Color.y
        && A.Color.z == B.Color.z
        && A.Color.w == B.Color.w;
}


void ModelCompiler::GetChildren(MeshPart* Parent, std::vector<PUNCVERTEX> &VertexBufferBuildList,
    std::vector<int> &IndexBufferBuildList, int &NumberOfVerticesAdded, int &NumberOfIndicesAdded,
    std::vector<MeshPiece> Meshs, std::string TexturesFolder, ID3D11Device* GraphicsDevice)
{
    std::vector<PUNCVERTEX> VerticesOfSubMesh;

    for (auto Mesh : Meshs)
    {
        if (Mesh.Parent == Parent->Name)
        {
            MeshPart* FoundChild = new MeshPart();
            FoundChild->Name = Mesh.Name;
            DirectX::XMMATRIX ParentsWorld = DirectX::XMLoadFloat4x4(&Parent->World);
            ParentsWorld = DirectX::XMMatrixInverse(nullptr, ParentsWorld);
            DirectX::XMMATRIX LocalWorld = DirectX::XMLoadFloat4x4(&Mesh.World);
            ParentsWorld = LocalWorld * ParentsWorld;
            DirectX::XMStoreFloat4x4(&FoundChild->World, ParentsWorld); //Multiplying by an inverse is basically subtracting one matrix from the other. Make children relative to parents.
    
            if (Mesh.Materials.size() > 0)
            {
                TextureClass* NewTexture = new TextureClass();
                NewTexture->Load(TexturesFolder + Mesh.Materials[0].Texture, GraphicsDevice);    //Only handles 1 material. Probably need to change this.
                FoundChild->Texture = NewTexture;
            }
            else FoundChild->Texture = nullptr;
            FoundChild->FirstVertexInBuffer = NumberOfVerticesAdded;
            FoundChild->FirstIndexInBuffer = NumberOfIndicesAdded;
            VerticesOfSubMesh = Mesh.GetVertices();

            GetPartBuffers(VerticesOfSubMesh, VertexBufferBuildList, IndexBufferBuildList, NumberOfVerticesAdded, NumberOfIndicesAdded);

            FoundChild->VerticesInThisMeshPart = NumberOfVerticesAdded - FoundChild->FirstVertexInBuffer;
            FoundChild->IndicesInThismeshPart = NumberOfIndicesAdded - FoundChild->FirstIndexInBuffer;

            GetChildren(FoundChild, VertexBufferBuildList, IndexBufferBuildList, NumberOfVerticesAdded,
                NumberOfIndicesAdded, Meshs, TexturesFolder, GraphicsDevice);

            Parent->AddChild(FoundChild);
            FoundChild = nullptr;
        }
    }
}


void ModelCompiler::GetPartBuffers(std::vector<PUNCVERTEX> &PartVertices, std::vector<PUNCVERTEX> &VertexBufferBuildList, std::vector<int> &IndexBufferBuildList,
    int &NumberOfVerticesAdded, int &NumberOfIndicesAdded)
{
    bool VertexFoundInList = false;
    int VertexFoundInPosition = -1;
    int NewPartIndex = 0;
    int IndicesPreviouslyAdded = NumberOfIndicesAdded;
    std::vector<PUNCVERTEX> TempBuildList;

    for (auto UnprocessedVertex : PartVertices)
    {
        //Find Vertex in VerticesOfSubMesh.
        int ListPosition = -1;

        //for (auto FinalVertex : VertexBufferBuildList)
        for (auto FinalVertex : TempBuildList)
        {
            ListPosition++;
            if (VerticesAreEqual(FinalVertex, UnprocessedVertex))
            {
                VertexFoundInList = true;
                VertexFoundInPosition = ListPosition;
            }
        }
        if (!VertexFoundInList)    //Not in the new list, add it.
        {
            VertexBufferBuildList.push_back(UnprocessedVertex);
            TempBuildList.push_back(UnprocessedVertex);
            IndexBufferBuildList.push_back(NewPartIndex);
            NumberOfVerticesAdded++;
            NumberOfIndicesAdded++;
            NewPartIndex++;
        }
        else    //In the new list already, just add its position to the index list.
        {
            IndexBufferBuildList.push_back(VertexFoundInPosition);
            NumberOfIndicesAdded++;
            VertexFoundInList = false;
        }
        VertexFoundInPosition = -1;
    }
}



std::vector<PUNCVERTEX> MeshPiece::GetVertices()
{
    Vertices.clear();
    GetVerticesFromFaces();
    return Vertices;
}


void MeshPiece::GetVerticesFromFaces()
{
    int NumberOfTrianglesInFace = 0;

    for (auto Polygon : Faces)
    {
        NumberOfTrianglesInFace = Polygon.CountTriangles();

        switch (NumberOfTrianglesInFace)
        {
            case 1:
                Vertices.push_back(Polygon.GetVertex(2));
                Vertices.push_back(Polygon.GetVertex(1));
                Vertices.push_back(Polygon.GetVertex(0));
                break;
            case 2:
                Vertices.push_back(Polygon.GetVertex(2));
                Vertices.push_back(Polygon.GetVertex(1));
                Vertices.push_back(Polygon.GetVertex(0));

                Vertices.push_back(Polygon.GetVertex(0));
                Vertices.push_back(Polygon.GetVertex(3));
                Vertices.push_back(Polygon.GetVertex(2));
                break;
            case 3:
                Vertices.push_back(Polygon.GetVertex(2));
                Vertices.push_back(Polygon.GetVertex(1));
                Vertices.push_back(Polygon.GetVertex(0));

                Vertices.push_back(Polygon.GetVertex(0));
                Vertices.push_back(Polygon.GetVertex(3));
                Vertices.push_back(Polygon.GetVertex(2));

                Vertices.push_back(Polygon.GetVertex(0));
                Vertices.push_back(Polygon.GetVertex(4));
                Vertices.push_back(Polygon.GetVertex(3));
                break;
            case 4:
                Vertices.push_back(Polygon.GetVertex(2));
                Vertices.push_back(Polygon.GetVertex(1));
                Vertices.push_back(Polygon.GetVertex(0));

                Vertices.push_back(Polygon.GetVertex(0));
                Vertices.push_back(Polygon.GetVertex(3));
                Vertices.push_back(Polygon.GetVertex(2));

                Vertices.push_back(Polygon.GetVertex(0));
                Vertices.push_back(Polygon.GetVertex(4));
                Vertices.push_back(Polygon.GetVertex(3));

                Vertices.push_back(Polygon.GetVertex(0));
                Vertices.push_back(Polygon.GetVertex(5));
                Vertices.push_back(Polygon.GetVertex(4));
                break;
            default:
                //Unhandled exception with too many triangles to count
                break;
        }
    }
}


Face::Face()
{
    Number = 0;
    MaterialNumber = 0;
    FaceNormal = DirectX::XMFLOAT3(0.f, 0.f, 0.f);
    SmoothShaded = false;
    VerticesInFace.clear();
}

Face::Face(std::string Header)
{
    StringConversions StringConverter;
    std::vector<std::string> Delimiters;
    std::vector<std::string> ParsedStrings;

    Delimiters.push_back("F:");
    Delimiters.push_back("M:");
    Delimiters.push_back("N:(");
    Delimiters.push_back(") S:");
    Delimiters.push_back(",");
    StringConverter.Parse(Header, Delimiters, ParsedStrings, true);
    Number = std::stoul(ParsedStrings[0]);
    MaterialNumber = std::stoul(ParsedStrings[1]);
    FaceNormal.x = std::stof(ParsedStrings[2]);
    FaceNormal.y = std::stof(ParsedStrings[3]);
    FaceNormal.z = std::stof(ParsedStrings[4]);
    if (ParsedStrings[5] == "T") SmoothShaded = true;
        else SmoothShaded = false;
}


void Face::AddVertex(std::string CodedVertexString)
{
    StringConversions StringConverter;
    std::vector<std::string> Delimiters;
    std::vector<std::string> ParsedStrings;
    PUNCVERTEX NewVertex;
    DirectX::XMFLOAT2 TempUV;
    DirectX::XMFLOAT3 Vec3To4;


    ZeroMemory(&NewVertex, sizeof(NewVertex));
    Delimiters.push_back("P:(");
    Delimiters.push_back("), U:(");
    Delimiters.push_back("), N:(");
    Delimiters.push_back("), C:(");

    StringConverter.Parse(CodedVertexString, Delimiters, ParsedStrings, true);
    NewVertex.Position = StringConverter.StringToVector3(ParsedStrings[0]);
    TempUV = StringConverter.StringToVector2(ParsedStrings[1]);        //Store UV coordinates to temp value because Blender has the V(which is y) value inversed.
    NewVertex.UV.x = TempUV.x;
    NewVertex.UV.y = 1.0f - TempUV.y;
    NewVertex.Normal = StringConverter.StringToVector3(ParsedStrings[2]);
    Vec3To4 = StringConverter.StringToVector3(ParsedStrings[3]);
    NewVertex.Color = DirectX::XMFLOAT4(Vec3To4.x, Vec3To4.y, Vec3To4.z, 1.0f);    //Could be a bug because not stipping ) at the end of line.

    VerticesInFace.push_back(NewVertex);
}


int Face::CountVertices()
{
    return (int) VerticesInFace.size();
}


int Face::CountTriangles()
{
    int FaceVertices = (int) VerticesInFace.size();
    int TrianglesInFace = 0;

    if (FaceVertices > 2)
    {
        if (FaceVertices < 7)   //There could be more but we'll limit it for now.
        {
            if (FaceVertices == 3) TrianglesInFace = 1;
            if (FaceVertices == 4) TrianglesInFace = 2;
            if (FaceVertices == 5) TrianglesInFace = 3;
            if (FaceVertices == 6) TrianglesInFace = 4;
        }
    }

    return TrianglesInFace;
}


PUNCVERTEX Face::GetVertex(int VertexNumber)
{
    return VerticesInFace[VertexNumber];
}

Material::Material(void){}

 

The entire project is on my website for download including the Python script and the textures for the model. The Python above is the abreviated version. The Python file in the project file has a bunch of other Python scripts that may prove useful in understanding the Blender data and how it is organized. I found getting info on the Blender data to be next to impossible. I actually "discovered" the Blender data layout through trial and error by just fishing around until I figured it out over a couple of months. Fortunately, Blender is really good about helping you on these fishing expeditions. Still, a lot of what's in the full Python file is stuff that I fished around and found and will likely be useful in figuring the layout of the data.

http://virtuallyprogramming.com/DirectX11/Tutorials/Tutorials.html



#16 BBeck   Members   

788
Like
0Likes
Like

Posted Today, 03:44 AM

If I understood you right, you said you don't know how an index buffer works. It's pretty straight forward. The index buffer is just a list of which vertices to process in what order. This allows your vertices in your vertex buffer to be used out of order and multiple times. For example, a quad (rectangle) made up of two triangles, might have an edge where two of the vertices are identical in both triangles. They are shared vertices. So, instead of specifying 6 vertices for the two triangles you only have to specify 4 and two of them get re-used in both triangles.

 

In that case, your vertex buffer might looks something like: 0,1,2,2,1,4

 

The graphics card knows that every 3 vertices makes a triangle. So, the first triangle is 0,1,2 and the second is 2,1,4 where vertices 2 and 1 are used in both triangles. Then your vertex buffer only has 4 vertices instead of six making your vertex buffer a whole lot smaller and saving memory on the graphics card. For a 4 byte integer (I believe it is) you saved a vertex which in the PUNC case is something like 11 four byte floats I believe. It can save a huge amount of memory.

 

But all it really is is a list of which vertices to process in what order. Every 3 values is a triangle. At least, I believe that's the way it works with a Triangle List(It's been probably a year since I've written any graphics code). In a triangle strip, I believe every vertex is a new triangle and it uses the last two vertices defined to form the three sides of the triangle. In that case, all the triangles are connected by definition. So, it only takes one vertex (plus the previous two) to define a triangle. I would love to use triangle strips in my model (because they are substantially more efficient), but I doubt it's even possible. I gave up on that a long time ago and just use a triangle list.

 

Oh. Also be aware of your "winding". Back face culling will remove the backside of the triangle, if it is turned on. It determines which side is the backside by whether the vertices are defined clockwise or counter-clockwise. So, the order you define them in is important. You want back face culling on because it saves the graphics card from having to draw back faces that will never be seen by the camera such as the inside of an object. With an index buffer, you define your order using the index buffer. But index buffers are optional. Without an index buffer your order would be determined in the vertex buffer. Turn back face culling off for debugging, but otherwise you want it on.


Edited by BBeck, Today, 03:50 AM.


#17 BBeck   Members   

788
Like
0Likes
Like

Posted Today, 04:05 AM

Now that I think about it, some may find it useful to see my entire Python script collection. Like I said, the only way I could figure out Blender scripting was to just fish around and try things out. There was little to no instructions I could find on how Blender data is organized. These scripts are things I saved while fishing around that pulled certain types of data from Blender. I think there's some stuff in there for skinned animation as I see some references to armatures.

 

I wrote a program that extracted the animation data from Blender using the basic humanoid armature that is in Blender. I created the animations in Blender using the basic humanoid armature and then my script exported those animations to a text file. I then loaded the file into an XNA program where I played them back as stick figures. It worked. The stick figures moved just like the armatures in the Blender animations.

 

Looks like the code for that is on my website here:

http://virtuallyprogramming.com/XNATutorials/ThreeDTutorials/SecondTutorial/SecondTutorial.html

But it is an XNA project, not a DX project. Still, that should not matter if you are just interested in the Blender exporter. The Blender exporter should be the same regardless of whether it is XNA or DX. Wait, I was able to find what appears to be the Blender exporter to export the animations. I'll make that a separate post.

 

#Python scripts and code to output the model data and also experimenting.
#The final script in this file is the one used to produce the models for this NewFormat code.
bpy.data.objects['CarBody'].data.vertices[0].co
bpy.data.objects['CarBody'].data.vertices[0].normal
bpy.data.objects['CarBody'].data.vertices[0].index
bpy.data.objects['CarBody'].matrix_world


Must have "Textured Solid" selected in the "Shading" menu of the N-menu (Properties) to see colors.

bpy.context.active_object.name
bpy.context.active_object.type
bpy.context.active_object.type == 'MESH' #Verify selected object is a mesh rather than camera, etc.
bpy.context.active_object.active_material.name #Active Material is what's selected and not what the mesh uses.
len(bpy.data.materials) #Number of Materials Used.
bpy.data.materials[1].diffuse_color  #Color of material 1.
bpy.data.materials[0].name   #Name of Material Zero.
bpy.data.materials[0].use_textures[0]  #True or False.
bpy.data.materials[0].texture_slots['MyTexture'].texture.image.name
bpy.context.active_object.active_material.diffuse_color
bpy.context.active_object.data.vertices[0].co
bpy.context.active_object.data.vertex_colors.active.data[0].color
bpy.context.active_object.data.tessfaces[0].use_smooth    #smooth or flat shading.
bpy.data.objects['WheelFtPort'].data.materials[0].texture_slots[0].texture.image.name
bpy.context.active_object.active_material.texture_slots[0].texture.image


for clr in bpy.context.active_object.data.vertex_colors.active.data: print(clr.color)

bpy.context.active_object.children[3].data.tessfaces[0].material_index

#Material Index points back to the Material Slot # of the mesh?

##################################################################
ob = bpy.context.active_object


for face in ob.data.polygons:
                for vert, loop in zip(face.vertices, face.loop_indices):
                    for item in ob.data.vertices[vert].normal:  # normal
                        print("Normal: %d" % item)
                    for item in ob.data.vertices[vert].co:  # vertex
                        print("Vertex: %d" % item)
                    for item in (ob.data.uv_layers.active.data[loop].uv if ob.data.uv_layers.active is not None else (0.0, 0.0)):  # uv
                        print("UV: %d" % item)


##################################################################
# List Faces
for Face in bpy.context.active_object.data.polygons:
    for Vert, Loop in zip(Face.vertices, Face.loop_indices):
        print(Vert)
    print("\n")


# List Face UVs
for Face in bpy.context.active_object.data.polygons:
    for Vert, Loop in zip(Face.vertices, Face.loop_indices):
        print(str(tuple(bpy.context.active_object.data.uv_layers.active.data[Loop].uv)))
    print("\n")


# List Face PUNC
# Appears to be accurate, but greatly multiples the number of vertices.
for Face in bpy.context.active_object.data.polygons:
    for Vert, Loop in zip(Face.vertices, Face.loop_indices):
        print("P:%s" % str(tuple(bpy.context.active_object.data.vertices[Vert].co)))
        print("U:%s" % str(tuple(bpy.context.active_object.data.uv_layers.active.data[Loop].uv)))
        print("N:%s" % str(tuple(bpy.context.active_object.data.vertices[Vert].normal)))
        print("C:%s" % str(tuple(bpy.context.active_object.data.vertex_colors.active.data[Vert].color)))
    print("\n")


#print("X:%s" % str(tuple(bpy.context.active_object.data.uv_layers.active.data[Vert].uv))) #wrong


# List Face PUNC
# Appears to be accurate, but greatly multiples the number of vertices.
MatNo = 0
for Material in bpy.context.active_object.material_slots:
    print("M:", MatNo, " Name:", Material.name, " Texture:", Material.material.active_texture.name, " Image:", Material.material.active_texture.image.name)
    MatNo = MatNo + 1

for Face in bpy.context.active_object.data.polygons:
    print("Face Normal:", round(Face.normal[0],7), ",", round(Face.normal[1],7), ",", round(Face.normal[2],7))
    print("Face Normal:{:f}".format(Face.normal[0]), ",", "{:f}".format(Face.normal[1]), ",", "{:f}".format(Face.normal[2]))
    print("Face Normal:{0}".format(tuple(Face.normal)))
    print("Material:%d" % Face.material_index)
    for Vert, Loop in zip(Face.vertices, Face.loop_indices):
        Pos = str(tuple(bpy.context.active_object.data.vertices[Vert].co))
        UV = str(tuple(bpy.context.active_object.data.uv_layers.active.data[Loop].uv))
        Norm =str(tuple(bpy.context.active_object.data.vertices[Vert].normal))
        Col =str(tuple(bpy.context.active_object.data.vertex_colors.active.data[Vert].color))
        print("P:", Pos, " U:", UV, " N:", Norm, " C:", Col)
    print("\n")


#############################################################

#Count triangles
bpub
len(bpy.context.active_object.data.tessfaces)

#############################################################

#List vertices of each quad

for Face in bpy.context.active_object.data.polygons:
    for Vert, Loop in zip(Face.vertices, Face.loop_indices):
        print("Vertex:", Vert)
    print("Material:", Face.material_index)
#############################################################

#Another way to list the vertices of each quad
bpy.context.active_object.data.update(calc_tessface=True)
print(len(bpy.context.active_object.data.tessfaces))

for Triangles in bpy.context.active_object.data.tessfaces:
    print("Material: %d Vertices:" % Triangles.material_index, end='')
    for Verts in Triangles.vertices:
        print(" %d" % Verts, end='')
    print()

for Triangles in bpy.context.active_object.data.tessfaces:
    print("M:%d F:" % Triangles.material_index, end='')
    print(str(tuple(Triangles.vertices)))

#############################################################
bpy.context.active_object.name
bpy.context.active_object.data.vertices[0].co
bpy.context.active_object.data.vertices[0].index
bpy.context.active_object.data.vertices[0].normal
bpy.context.active_object.data.vertex_colors.active.data[0].color
bpy.context.active_object.data.uv_layers.active.data[0].uv



print(bpy.context.active_object.data.vertex_colors.active) #None if empty.
print(bpy.context.active_object.data.uv_layers.active) #None if empty.

bpy.context.active_object.children[0].data.vertex_colors.active.data[0].color #Color of the first child.

#detect if coordinates are present.
HasUVCoordinates = bpy.context.active_object.data.uv_layers.active
if HasUVCoordinates is None:
    print("True")




####################################################################
import bpy

def ProcessChildren(ParentMesh, ParentLabel, VerticesInModel, FacesInModel):  #Get all data for sub-meshes.
    VerticesSoFar = VerticesInModel
    FacesSoFar = FacesInModel
    NumberOfChildren = len(ParentMesh.children)
    if NumberOfChildren > 0:
        ChildLabel = ParentLabel + "==>"
        for ChildMesh in ParentMesh.children:
            print(ChildLabel, ChildMesh.name)
            VerticesSoFar = VerticesSoFar + len(ChildMesh.data.vertices)
            ChildMesh.data.update(calc_tessface=True)
            FacesSoFar = FacesSoFar + len(ChildMesh.data.tessfaces)
            VerticesSoFar, FacesSoFar = ProcessChildren(ChildMesh, ChildLabel, VerticesSoFar, FacesSoFar)
    return VerticesSoFar, FacesSoFar

if bpy.context.active_object.type == 'MESH':
    print(bpy.context.active_object.name)
    bpy.context.active_object.data.update(calc_tessface=True)
    VerticesInModel = len(bpy.context.active_object.data.vertices)
    FacesInModel = len(bpy.context.active_object.data.tessfaces)
    VerticesInModel, FacesInModel = ProcessChildren(bpy.context.active_object, "", VerticesInModel, FacesInModel)
    print("Total Vertices:%d" % VerticesInModel)
    print("Total Faces:%d" % FacesInModel)



####################################################################
#There is an assumption that there can only be one material/texture per mesh.
#Make sure the parent object is selected in ObjectMode and not EditMode.
import bpy

def ProcessChildren(ParentMesh, VerticesInModel, FacesInModel):  #Get all data for sub-meshes.
    VerticesSoFar = VerticesInModel
    FacesSoFar = FacesInModel
    NumberOfChildren = len(ParentMesh.children)
    if NumberOfChildren > 0:
        for ChildMesh in ParentMesh.children:
            HasUVCoordinates = ChildMesh.data.uv_layers.active
            HasVertexColors = ChildMesh.data.vertex_colors.active
            CountOfVertices = 0
            print("Name:%s" % ChildMesh.name)
            print("Parent:%s" % ChildMesh.parent.name)
            VerticesSoFar = VerticesSoFar + len(ChildMesh.data.vertices)
            ChildMesh.data.update(calc_tessface=True)
            FacesSoFar = FacesSoFar + len(ChildMesh.data.tessfaces)
            #Vertices.
            for Vertex in ChildMesh.data.vertices:
                print("P:%s" % str(tuple(Vertex.co)))
                if HasUVCoordinates is not None:
                    print(", U:%s" % str(ChildMesh.data.uv_layers.active.data[CountOfVertices].uv))
                else:
                    print(", U:(0.0,0.0)")
                print(", N:%s" % str(tuple(Vertex.normal)))
                if HasVertexColors is not None:
                    print(", C:%s" % str(tuple(ChildMesh.data.vertex_colors.active.data[CountOfVertices].color)))
                else:
                    print(", C:(0.0,0.0,0.0,0.0)")
                print("\n")
                CountOfVertices = CountOfVertices + 1
            #Faces.
            for Face in ChildMesh.data.tessfaces:
                print("F:%s" % str(tuple(Face.vertices)))
                print(" M:%s" % str(Face.material_index))
                print(" S:%s\n" % str(Face.use_smooth))
            #Call child's children.
            VerticesSoFar, FacesSoFar = ProcessChildren(ChildMesh, VerticesSoFar, FacesSoFar)
    return VerticesSoFar, FacesSoFar

if bpy.context.active_object != None and bpy.context.active_object.parent == None and bpy.context.active_object.type == 'MESH':
    print("Punc!It Text\n")
    HasUVCoordinates = bpy.context.active_object.data.uv_layers.active
    HasVertexColors = bpy.context.active_object.data.vertex_colors.active
    NumberOfMaterialsUsed = 0
    CountOfVertices = 0
    #Name.
    print("Name:%s" % bpy.context.active_object.name)
    print("Parent:Root")
    bpy.context.active_object.data.update(calc_tessface=True)
    VerticesInModel = len(bpy.context.active_object.data.vertices)
    FacesInModel = len(bpy.context.active_object.data.tessfaces)
    #Vertices.
    for Vertex in bpy.context.active_object.data.vertices:
        print("P:%s" % str(tuple(Vertex.co)))
        if HasUVCoordinates is not None:
            print(", U:%s" % str(bpy.context.active_object.data.uv_layers.active.data[CountOfVertices].uv))
        else:
            print(", U:(0.0,0.0)")
        print(", N:%s" % str(tuple(Vertex.normal)))
        if HasVertexColors is not None:
            print(", C:%s" % str(tuple(bpy.context.active_object.data.vertex_colors.active.data[CountOfVertices].color)))
        else:
            print(", C:(0.0,0.0,0.0,0.0)")
        print("\n")
        CountOfVertices = CountOfVertices + 1
    #Faces.
    for Face in bpy.context.active_object.data.tessfaces:
        print("F:%s" % str(tuple(Face.vertices)))
        print(" M:%s" % str(Face.material_index))
        print(" S:%s\n" % str(Face.use_smooth))
    #Children.
    VerticesInModel, FacesInModel = ProcessChildren(bpy.context.active_object, VerticesInModel, FacesInModel)
    #Materials.
    NumberOfMaterialsUsed = len(bpy.data.materials)
    if NumberOfMaterialsUsed > 0:
        MaterialNumber = 0
        for Material in bpy.data.materials:
            print("M:%d" % MaterialNumber)
            print(" T:%s" % bpy.data.materials[0].texture_slots['MyTexture'].texture.image.name)
            print(" C:%s\n" % str(tuple(bpy.context.active_object.data.vertex_colors.active.data[CountOfVertices].color)))
            MaterialNumber = MaterialNumber + 1
    else:
        print("M:None\n");
    print("Total Vertices:%d" % VerticesInModel)
    print("Total Faces:%d" % FacesInModel)
    print("Total Materials:%d" % NumberOfMaterialsUsed)
else:
    print("You must select the root object before running this script!")

####################################################################
import bpy

def ProcessChildren(f, ParentMesh, VerticesInModel, FacesInModel):  #Get all data for sub-meshes.
    VerticesSoFar = VerticesInModel
    FacesSoFar = FacesInModel
    NumberOfChildren = len(ParentMesh.children)
    if NumberOfChildren > 0:
        for ChildMesh in ParentMesh.children:
            HasUVCoordinates = ChildMesh.data.uv_layers.active
            HasVertexColors = ChildMesh.data.vertex_colors.active
            CountOfVertices = 0
            f.write("Name:%s\n" % ChildMesh.name)
            f.write("Parent:%s\n" % ChildMesh.parent.name)
            VerticesSoFar = VerticesSoFar + len(ChildMesh.data.vertices)
            ChildMesh.data.update(calc_tessface=True)
            FacesSoFar = FacesSoFar + len(ChildMesh.data.tessfaces)
            #Materials
            SubMeshMaterialNo = 0
            for Material in ChildMesh.material_slots:
                f.write("M:%d" % SubMeshMaterialNo)
                f.write(" T:%s" % Material.material.texture_slots[0].texture.image.name)
                f.write(" C:%s\n" % str(tuple(Material.material.diffuse_color)))
            #WorldMatrix
            WorldMatrixRowNumber = 0
            for WorldMatrixRow in ChildMesh.matrix_world.row:
                f.write("WMR%d" % WorldMatrixRowNumber)
                f.write(":%s\n" % str(tuple(WorldMatrixRow)))
                WorldMatrixRowNumber = WorldMatrixRowNumber + 1
            #Vertices.
            for Vertex in ChildMesh.data.vertices:
                f.write("P:%s" % str(tuple(Vertex.co)))
                if HasUVCoordinates is not None:
                    f.write(", U:%s" % str(tuple(ChildMesh.data.uv_layers.active.data[CountOfVertices].uv)))
                else:
                    f.write(", U:(0.0,0.0)")
                f.write(", N:%s" % str(tuple(Vertex.normal)))
                if HasVertexColors is not None:
                    f.write(", C:%s" % str(tuple(ChildMesh.data.vertex_colors.active.data[CountOfVertices].color)))
                else:
                    f.write(", C:(0.0,0.0,0.0,0.0)")
                f.write("\n")
                CountOfVertices = CountOfVertices + 1
            #Faces.
            for Face in ChildMesh.data.tessfaces:
                f.write("F:%s" % str(tuple(Face.vertices)))
                f.write(" M:%s" % str(Face.material_index))
                f.write(" S:%s\n" % str(Face.use_smooth)[0])
            #Call child's children.
            VerticesSoFar, FacesSoFar = ProcessChildren(f, ChildMesh, VerticesSoFar, FacesSoFar)
    return VerticesSoFar, FacesSoFar

def write_some_data(context, filepath):
    HasUVCoordinates = bpy.context.active_object.data.uv_layers.active
    HasVertexColors = bpy.context.active_object.data.vertex_colors.active
    print("Exporting data...")
    bpy.context.active_object.data.update(calc_tessface=True)         #calc_tessface=True must be set before working with faces.
    
    f = open(filepath, 'w', encoding='utf-8')
    if bpy.context.active_object != None and bpy.context.active_object.parent == None and bpy.context.active_object.type == 'MESH':
        CountOfVertices = 0
        NumberOfMaterialsUsed = 0
        FacesInModel = 0
        VerticesInModel = len(bpy.context.active_object.data.vertices)
        FacesInModel = len(bpy.context.active_object.data.tessfaces)
        f.write("Punc!It Text\n")
        f.write("Rigid Animation\n")
        f.write("Name:%s\n" % bpy.context.active_object.name)
        f.write("Parent:None\n")
        #Materials
        SubMeshMaterialNo = 0
        for Material in bpy.context.active_object.material_slots:
            f.write("M:%d" % SubMeshMaterialNo)
            f.write(" T:%s" % Material.material.texture_slots[0].texture.image.name)
            f.write(" C:%s\n" % str(tuple(Material.material.diffuse_color)))
        #WorldMatrix
        WorldMatrixRowNumber = 0
        for WorldMatrixRow in bpy.context.active_object.matrix_world.row:
            f.write("WMR%d" % WorldMatrixRowNumber)
            f.write(":%s\n" % str(tuple(WorldMatrixRow)))
            WorldMatrixRowNumber = WorldMatrixRowNumber + 1
        #Vertices.
        for Vertex in bpy.context.active_object.data.vertices:
            f.write("P:%s" % str(tuple(Vertex.co)))
            if HasUVCoordinates is not None:
                f.write(", U:%s" % str(tuple(bpy.context.active_object.data.uv_layers.active.data[CountOfVertices].uv)))
            else:
                f.write(", U:(0.0,0.0)")
            f.write(", N:%s" % str(tuple(Vertex.normal)))
            if HasVertexColors is not None:
                f.write(", C:%s" % str(tuple(bpy.context.active_object.data.vertex_colors.active.data[CountOfVertices].color)))
            else:
                f.write(", C:(0.0,0.0,0.0,0.0)")
            f.write("\n")
            CountOfVertices = CountOfVertices + 1
        #Faces.
        for Face in bpy.context.active_object.data.tessfaces:
            f.write("F:%s" % str(tuple(Face.vertices)))
            f.write(" M:%s" % str(Face.material_index))
            f.write(" S:%s\n" % str(Face.use_smooth)[0])  #T if smooth shading & F if flat shading.
        #Children.
        VerticesInModel, FacesInModel = ProcessChildren(f, bpy.context.active_object, VerticesInModel, FacesInModel)
        #Materials.
        #NumberOfMaterialsUsed = len(bpy.data.materials)
        #if NumberOfMaterialsUsed > 0:
        #    MaterialNumber = 0
        #    for Material in bpy.data.materials:
        #        f.write("M:%d" % MaterialNumber)
        #        f.write(" T:%s" % bpy.data.materials[MaterialNumber].texture_slots[0].texture.image.name)
        #        f.write(" C:%s\n" % str(tuple(bpy.data.materials[MaterialNumber].diffuse_color)))
        #        MaterialNumber = MaterialNumber + 1
        #else:
        #    f.write("M:None\n")
        #File Footer.
        f.write("Total Vertices:%d\n" % VerticesInModel)
        f.write("Total Faces:%d\n" % FacesInModel)
        f.write("Total Materials:%d" % NumberOfMaterialsUsed)
    else:
        f.write("You must select the root mesh before exporting the model!")
    f.close()
    
    return {'FINISHED'}


# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator


class ExportSomeData(Operator, ExportHelper):
    """This appears in the tooltip of the operator and in the generated docs"""
    bl_idname = "export_test.some_data"  # important since its how bpy.ops.import_test.some_data is constructed
    bl_label = "Export Button"

    # ExportHelper mixin class uses this
    filename_ext = ".txt"

    filter_glob = StringProperty(
            default="*.txt",
            options={'HIDDEN'},
            )

    def execute(self, context):
        return write_some_data(context, self.filepath)


# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
    self.layout.operator(ExportSomeData.bl_idname, text="Custom Exporter Test")


def register():
    bpy.utils.register_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.append(menu_func_export)


def unregister():
    bpy.utils.unregister_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.remove(menu_func_export)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.export_test.some_data('INVOKE_DEFAULT')
=================================================================================

import bpy


def ProcessChildren(f, ParentMesh, VerticesInModel, FacesInModel):  #Get all data for sub-meshes.
    VerticesSoFar = VerticesInModel
    FacesSoFar = FacesInModel
    NumberOfChildren = len(ParentMesh.children)
    if NumberOfChildren > 0:
        for ChildMesh in ParentMesh.children:
            HasUVCoordinates = ChildMesh.data.uv_layers.active
            HasVertexColors = ChildMesh.data.vertex_colors.active
            CountOfVertices = 0
            f.write("Name:%s\n" % ChildMesh.name)
            f.write("Parent:%s\n" % ChildMesh.parent.name)
            VerticesSoFar = VerticesSoFar + len(ChildMesh.data.vertices)
            ChildMesh.data.update(calc_tessface=True)
            FacesSoFar = FacesSoFar + len(ChildMesh.data.tessfaces)
            #Materials
            SubMeshMaterialNo = 0
            for Material in ChildMesh.material_slots:
                f.write("M:%d" % SubMeshMaterialNo)
                f.write("-%s" % Material.name)
                f.write(" T:%s" % Material.material.active_texture.image.name)
                f.write(" C:%s\n" % str(tuple(Material.material.diffuse_color)))
            #WorldMatrix
            WorldMatrixRowNumber = 0
            for WorldMatrixRow in ChildMesh.matrix_world.row:
                f.write("WMR%d" % WorldMatrixRowNumber)
                f.write(":%s\n" % str(tuple(WorldMatrixRow)))
                WorldMatrixRowNumber = WorldMatrixRowNumber + 1
            #Faces.
            FaceCount = 0
            for Face in ChildMesh.data.polygons:
                f.write("F:%d" % FaceCount)
                f.write(" M:%s" % str(Face.material_index))
                f.write(" N:({:f}".format(Face.normal[0]))
                f.write(",{:f}".format(Face.normal[1]))
                f.write(",{:f})".format(Face.normal[2]))
                f.write(" S:%s\n" % str(Face.use_smooth)[0])
                FaceCount = FaceCount + 1
                for Vert, Loop in zip(Face.vertices, Face.loop_indices):
                    f.write("P:%s" % str(tuple(ChildMesh.data.vertices[Vert].co)))
                    if HasUVCoordinates is not None:
                        f.write(", U:%s" % str(tuple(ChildMesh.data.uv_layers.active.data[Loop].uv)))
                    else:
                        f.write(", U:(0.0,0.0)")
                    f.write(", N:%s" % str(tuple(ChildMesh.data.vertices[Vert].normal)))
                    if HasVertexColors is not None:
                        f.write(", C:%s" % str(tuple(ChildMesh.data.vertex_colors.active.data[Vert].color)))
                    else:
                        f.write(", C:(0.0,0.0,0.0,0.0)")
                    f.write("\n")
            #Call child's children.
            VerticesSoFar, FacesSoFar = ProcessChildren(f, ChildMesh, VerticesSoFar, FacesSoFar)
    return VerticesSoFar, FacesSoFar

def write_some_data(context, filepath):
    HasUVCoordinates = bpy.context.active_object.data.uv_layers.active
    HasVertexColors = bpy.context.active_object.data.vertex_colors.active
    print("Exporting data...")
    bpy.context.active_object.data.update(calc_tessface=True)         #calc_tessface=True must be set before working with faces.
    
    f = open(filepath, 'w', encoding='utf-8')
    if bpy.context.active_object != None and bpy.context.active_object.parent == None and bpy.context.active_object.type == 'MESH':
        CountOfVertices = 0
        NumberOfMaterialsUsed = 0
        FacesInModel = 0
        VerticesInModel = len(bpy.context.active_object.data.vertices)
        FacesInModel = len(bpy.context.active_object.data.tessfaces)
        f.write("Punc!It Text\n")
        f.write("Rigid Animation\n")
        f.write("Name:%s\n" % bpy.context.active_object.name)
        f.write("Parent:None\n")
        #Materials
        MaterialNo = 0
        for Material in bpy.context.active_object.material_slots:
            f.write("M:%d" % MaterialNo)
            f.write("-%s" % Material.name)
            f.write(" T:%s" % Material.material.active_texture.image.name)
            f.write(" C:%s\n" % str(tuple(Material.material.diffuse_color)))
        #WorldMatrix
        WorldMatrixRowNumber = 0
        for WorldMatrixRow in bpy.context.active_object.matrix_world.row:
            f.write("WMR%d" % WorldMatrixRowNumber)
            f.write(":%s\n" % str(tuple(WorldMatrixRow)))
            WorldMatrixRowNumber = WorldMatrixRowNumber + 1
        #Faces.
        FaceCount = 0
        for Face in bpy.context.active_object.data.polygons:
            f.write("F:%d" % FaceCount)
            f.write(" M:%s" % str(Face.material_index))
            f.write(" N:({:f}".format(Face.normal[0]))
            f.write(",{:f}".format(Face.normal[1]))
            f.write(",{:f})".format(Face.normal[2]))
            f.write(" S:%s\n" % str(Face.use_smooth)[0])
            FaceCount = FaceCount + 1
            for Vert, Loop in zip(Face.vertices, Face.loop_indices):
                f.write("P:%s" % str(tuple(bpy.context.active_object.data.vertices[Vert].co)))
                if HasUVCoordinates is not None:
                    f.write(", U:%s" % str(tuple(bpy.context.active_object.data.uv_layers.active.data[Loop].uv)))
                else:
                    f.write(", U:(0.0,0.0)")
                f.write(", N:%s" % str(tuple(bpy.context.active_object.data.vertices[Vert].normal)))
                if HasVertexColors is not None:
                    f.write(", C:%s" % str(tuple(bpy.context.active_object.data.vertex_colors.active.data[Vert].color)))
                else:
                    f.write(", C:(0.0,0.0,0.0,0.0)")
                f.write("\n")
        #Children.
        VerticesInModel, FacesInModel = ProcessChildren(f, bpy.context.active_object, VerticesInModel, FacesInModel)
        #Footer
        f.write("Total Unique Vertices:%d\n" % VerticesInModel)
        f.write("Total Faces:%d\n" % FacesInModel)
        f.write("Total Materials:%d" % NumberOfMaterialsUsed)
    else:
        f.write("You must select the root mesh before exporting the model!")
    f.close()
    
    return {'FINISHED'}


# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator


class ExportSomeData(Operator, ExportHelper):
    """This appears in the tooltip of the operator and in the generated docs"""
    bl_idname = "export_test.some_data"  # important since its how bpy.ops.import_test.some_data is constructed
    bl_label = "Export Button"

    # ExportHelper mixin class uses this
    filename_ext = ".txt"

    filter_glob = StringProperty(
            default="*.txt",
            options={'HIDDEN'},
            )

    def execute(self, context):
        return write_some_data(context, self.filepath)


# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
    self.layout.operator(ExportSomeData.bl_idname, text="Custom Exporter Test")


def register():
    bpy.utils.register_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.append(menu_func_export)


def unregister():
    bpy.utils.unregister_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.remove(menu_func_export)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.export_test.some_data('INVOKE_DEFAULT')
==========================================================================================

bpy.context.active_object.modifiers[0].type
bpy.context.active_object.modifiers[0].object.data.bones[0].name
bpy.context.active_object.modifiers[0].object.data.bones['upper_arm.R'].children[0].name
bpy.context.active_object.modifiers['Armature'].object.data.name
bpy.context.active_object.vertex_groups[0].name


#18 BBeck   Members   

788
Like
0Likes
Like

Posted Today, 04:13 AM

Here is the Blender exporter for exporting skinned animations. Skinned animation takes more than this data. Namely, it takes the "skinning" data. That is, when you skin a model you are assigning vertices to a bone in the armature and when you weight paint, you assign the percentage that each bone influences the vertex. So, all that data has to be exported. I would want to export that data separately; I would export this animation data exactly like this and then export the model as a separate file which would include the skinning data to describe which bones the vertex is influenced by and the percentage weight. I never got around to writing that exporter unfortunately. Learning skinning and weighting in Blender will help understand how to write that exporter and use the data in code.

 

But this exporter worked to export the armature animation. And having the animation data separate from the model allows the animation data to be re-used on many models. So, a walk animation could be used on any humanoid model that uses the same armature. Since the humanoid armature in Blender is standard, you could reuse the data a lot.

 

#script to export annimations
import bpy


def write_some_data(context, filepath):
    print("Exporting data...")
    #bpy.context.active_object.data.update(calc_tessface=True)         #calc_tessface=True must be set before working with faces.
    
    f = open(filepath, 'w', encoding='utf-8')
    if bpy.context.active_object != None and bpy.context.active_object.parent == None and bpy.context.active_object.type == 'MESH':
        ModelsArmature = bpy.context.active_object.find_armature()
        f.write("Punc!It Armature Animation\n")
        f.write("Armature Name:%s\n" % ModelsArmature.name)
        for Animation in bpy.data.actions:
            ModelsArmature.animation_data.action = Animation
            f.write("Action:%s\n" % Animation.name)
            
            FirstFrame = bpy.context.scene.frame_start
            LastFrame = bpy.context.scene.frame_end
            f.write("Start:%d\n" % FirstFrame)
            f.write("End:%d\n" % LastFrame)
            bpy.context.scene.frame_set(FirstFrame)
            
            NumberOfKeyFrames = len(Animation.fcurves[0].keyframe_points) #KeyFrames in animation
            for KeyFramePoint in Animation.fcurves[0].keyframe_points:
                KeyFrame = KeyFramePoint.co.x
                bpy.context.scene.frame_set(KeyFrame)                
                f.write("Frame:%d\n" % KeyFrame)
                for Bone in ModelsArmature.pose.bones:
                    f.write("Bone:%s\n" % Bone.name)
                    if (Bone.parent == None):
                        f.write("Parent:None\n")
                    else:
                        f.write("Parent:%s\n" % Bone.parent.name)
                    f.write("Head:%s\n" % str(tuple(Bone.head)))
                    f.write("Tail:%s\n" % str(tuple(Bone.tail)))
                    f.write("Length:%s\n" % str(Bone.length))
                    WorldMatrixRowNumber = 0
                    for WorldMatrixRow in Bone.matrix.row:
                        f.write("WMR:%d" % WorldMatrixRowNumber)
                        f.write(":%s\n" % str(tuple(WorldMatrixRow)))
                        WorldMatrixRowNumber = WorldMatrixRowNumber + 1
                    BasisMatrixRowNumber = 0
                    for BasisMatrixRow in Bone.matrix_basis:
                        f.write("BMR:%d" % BasisMatrixRowNumber)
                        f.write(":%s\n" % str(tuple(BasisMatrixRow)))
                        BasisMatrixRowNumber = BasisMatrixRowNumber + 1
    else:
        f.write("You must select the root mesh before exporting the model!")
    f.close()
    
    return {'FINISHED'}


# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator


class ExportSomeData(Operator, ExportHelper):
    """This appears in the tooltip of the operator and in the generated docs"""
    bl_idname = "export_test.some_data"  # important since its how bpy.ops.import_test.some_data is constructed
    bl_label = "Export Button"

    # ExportHelper mixin class uses this
    filename_ext = ".txt"

    filter_glob = StringProperty(
            default="*.txt",
            options={'HIDDEN'},
            )

    def execute(self, context):
        return write_some_data(context, self.filepath)


# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
    self.layout.operator(ExportSomeData.bl_idname, text="Custom Exporter Test")


def register():
    bpy.utils.register_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.append(menu_func_export)


def unregister():
    bpy.utils.unregister_class(ExportSomeData)
    bpy.types.INFO_MT_file_export.remove(menu_func_export)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.export_test.some_data('INVOKE_DEFAULT')


#19 BBeck   Members   

788
Like
0Likes
Like

Posted Today, 04:18 AM

That should produce a file something like this (This is the short version as the actual data file was too long to post here - you can get the whole thing off my website by downloading the "Armature Animation example" which is an XNA project but contains the Python script and an animation file called "RoboGuyAnimationFile"):

Punc!It Armature Animation
Armature Name:Armature
Action:MyBindPose
Start:1
End:100
Frame:1
Bone:hips
Parent:None
Head:(0.0, 0.0551999993622303, 1.0098999738693237)
Tail:(0.0, 0.017199985682964325, 1.1836999654769897)
Length:0.1779057000841996
WMR:0:(1.0, 0.0, 0.0, 0.0)
WMR:1:(0.0, -0.2135963886976242, -0.9769219756126404, 0.0551999993622303)
WMR:2:(0.0, 0.9769219756126404, -0.2135963886976242, 1.0098999738693237)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:spine
Parent:hips
Head:(0.0, 0.017199985682964325, 1.1836999654769897)
Tail:(0.0, 0.0003999844193458557, 1.3417999744415283)
Length:0.15899010307891734
WMR:0:(1.0, 0.0, 0.0, 0.0)
WMR:1:(0.0, -0.10566695779561996, -0.9944015741348267, 0.017199985682964325)
WMR:2:(0.0, 0.9944015741348267, -0.10566695779561996, 1.1836999654769897)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:ribs
Parent:spine
Head:(0.0, 0.00039998392458073795, 1.3417999744415283)
Tail:(0.0, 0.011400014162063599, 1.6582000255584717)
Length:0.316591208061175
WMR:0:(1.0, 0.0, 0.0, 0.0)
WMR:1:(0.0, 0.034745220094919205, -0.9993961453437805, 0.00039998392458073795)
WMR:2:(0.0, 0.9993961453437805, 0.034745220094919205, 1.3417999744415283)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:neck
Parent:ribs
Head:(0.0, 0.011400015093386173, 1.6582000255584717)
Tail:(0.0, -0.024699967354536057, 1.7812999486923218)
Length:0.1282840587955442
WMR:0:(1.0, 0.0, 0.0, 0.0)
WMR:1:(0.0, -0.28140658140182495, -0.9595885872840881, 0.011400015093386173)
WMR:2:(0.0, 0.9595885872840881, -0.28140658140182495, 1.6582000255584717)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:head
Parent:neck
Head:(0.0, -0.024699965491890907, 1.7812999486923218)
Tail:(0.0, -0.024699928238987923, 1.9347000122070312)
Length:0.153400063514714
WMR:0:(1.0, 0.0, 0.0, 0.0)
WMR:1:(0.0, 2.455568903769745e-07, -0.9999999403953552, -0.024699965491890907)
WMR:2:(0.0, 0.9999999403953552, 2.455568903769745e-07, 1.7812999486923218)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:shoulder.L
Parent:ribs
Head:(0.018300000578165054, -0.06839998811483383, 1.6051000356674194)
Tail:(0.16940002143383026, 0.02050001174211502, 1.6050000190734863)
Length:0.1753123994652116
WMR:0:(0.5070948600769043, 0.8618901968002319, 0.000529240642208606, 0.018300000578165054)
WMR:1:(-0.8618903160095215, 0.5070948004722595, 0.00022575189359486103, -0.06839998811483383)
WMR:2:(-7.374442793661729e-05, -0.0005706493393518031, 0.9999998807907104, 1.6051000356674194)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:upper_arm.L
Parent:shoulder.L
Head:(0.19530001282691956, 0.026700008660554886, 1.5845999717712402)
Tail:(0.30880507826805115, 0.0885000005364418, 1.326655626296997)
Length:0.28850983386843326
WMR:0:(-0.9150146842002869, 0.39341846108436584, 0.08927633613348007, 0.19530001282691956)
WMR:1:(-0.003204085398465395, 0.21420414745807648, -0.9767836928367615, 0.026700008660554886)
WMR:2:(-0.40340808033943176, -0.8940573334693909, -0.19473931193351746, 1.5845999717712402)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(0.7932483553886414, 0.5946226716041565, 0.13107618689537048, 0.0)
BMR:1:(-0.5949065089225769, 0.8027327656745911, -0.04130832478404045, 0.0)
BMR:2:(-0.12978202104568481, -0.045210305601358414, 0.9905112981796265, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:forearm.L
Parent:upper_arm.L
Head:(0.30880507826805115, 0.0885000005364418, 1.326655626296997)
Tail:(0.30192404985427856, 0.049199994653463364, 1.0668660402297974)
Length:0.2628354390933617
WMR:0:(-0.9995909333229065, -0.02618001215159893, 0.011519014835357666, 0.30880507826805115)
WMR:1:(-0.007477705366909504, -0.14952321350574493, -0.9887299537658691, 0.0885000005364418)
WMR:2:(0.027607256546616554, -0.9884114861488342, 0.1492663025856018, 1.326655626296997)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(0.9359229207038879, 0.3483165502548218, -0.05219045653939247, 0.0)
BMR:1:(-0.34817326068878174, 0.9373520016670227, 0.012107320129871368, 0.0)
BMR:2:(0.05313801020383835, 0.006839803420007229, 0.9985637664794922, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:hand.L
Parent:forearm.L
Head:(0.30192404985427856, 0.049199994653463364, 1.0668660402297974)
Tail:(0.2952873706817627, 0.04119998961687088, 0.9873819351196289)
Length:0.0801608916878344
WMR:0:(-0.0008265578071586788, -0.08279184997081757, -0.996566653251648, 0.30192404985427856)
WMR:1:(0.9949796199798584, -0.09979938715696335, 0.007465818431228399, 0.049199994653463364)
WMR:2:(-0.10007485002279282, -0.9915573000907898, 0.08245860785245895, 1.0668660402297974)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:palm.01.L
Parent:hand.L
Head:(0.3039860129356384, 0.022399989888072014, 1.0296443700790405)
Tail:(0.2986827790737152, 0.0050999801605939865, 0.9624106884002686)
Length:0.0696260194040096
WMR:0:(-0.00568762794137001, -0.0761675164103508, -0.9970789551734924, 0.3039860129356384)
WMR:1:(0.9685460329055786, -0.24847060441970825, 0.013455983251333237, 0.022399989888072014)
WMR:2:(-0.24876970052719116, -0.9656403064727783, 0.07518486678600311, 1.0296443700790405)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_index.01.L
Parent:palm.01.L
Head:(0.2986827790737152, 0.0050999801605939865, 0.9624106884002686)
Tail:(0.2806694805622101, 0.001299968920648098, 0.9213048815727234)
Length:0.045040052882204376
WMR:0:(0.0950925275683403, -0.3999395966529846, -0.9115952253341675, 0.2986827790737152)
WMR:1:(0.9865608215332031, -0.08436965197324753, 0.13992759585380554, 0.0050999801605939865)
WMR:2:(-0.1328735500574112, -0.9126502871513367, 0.3865416646003723, 0.9624106884002686)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_index.02.L
Parent:finger_index.01.L
Head:(0.2806694805622101, 0.0012999688042327762, 0.9213048815727234)
Tail:(0.2655388414859772, -0.00030004081781953573, 0.8975337743759155)
Length:0.028223426563902844
WMR:0:(0.08646058291196823, -0.5361015200614929, -0.8397141695022583, 0.2806694805622101)
WMR:1:(0.9888078570365906, -0.056690819561481476, 0.13800522685050964, 0.0012999688042327762)
WMR:2:(-0.12158889323472977, -0.8422480225563049, 0.5251997709274292, 0.9213048815727234)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_index.03.L
Parent:finger_index.02.L
Head:(0.2655388414859772, -0.00030004081781953573, 0.8975337743759155)
Tail:(0.24943029880523682, 0.0005999515415169299, 0.881397008895874)
Length:0.02281864004935344
WMR:0:(0.11580153554677963, -0.7059381008148193, -0.6987428069114685, 0.2655388414859772)
WMR:1:(0.9914401769638062, 0.03944113105535507, 0.1244625449180603, -0.00030004081781953573)
WMR:2:(-0.06030363589525223, -0.7071748375892639, 0.7044626474380493, 0.8975337743759155)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:thumb.01.L
Parent:palm.01.L
Head:(0.28035271167755127, 0.021399999037384987, 1.0403861999511719)
Tail:(0.2600117325782776, 0.0014999918639659882, 1.0098435878753662)
Length:0.04174466275624186
WMR:0:(0.8725852370262146, -0.48727157711982727, -0.034081779420375824, 0.28035271167755127)
WMR:1:(-0.23309919238090515, -0.47670814394950867, 0.8475931882858276, 0.021399999037384987)
WMR:2:(-0.42925503849983215, -0.7316528558731079, -0.5295511484146118, 1.0403861999511719)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:thumb.02.L
Parent:thumb.01.L
Head:(0.2600117325782776, 0.0014999917475506663, 1.0098435878753662)
Tail:(0.24852174520492554, -0.00570001220330596, 0.9792690873146057)
Length:0.03344637428703545
WMR:0:(0.9349398016929626, -0.3435346186161041, 0.08872213214635849, 0.2600117325782776)
WMR:1:(-0.1703493595123291, -0.21526996791362762, 0.9615820050239563, 0.0014999917475506663)
WMR:2:(-0.3112374246120453, -0.9141350984573364, -0.2597854435443878, 1.0098435878753662)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:thumb.03.L
Parent:thumb.02.L
Head:(0.24852174520492554, -0.00570001220330596, 0.9792690873146057)
Tail:(0.24306833744049072, -0.009800013154745102, 0.9596298933029175)
Length:0.02079056529957112
WMR:0:(0.9595236778259277, -0.2623019218444824, 0.10252860188484192, 0.24852174520492554)
WMR:1:(-0.1573072373867035, -0.19720466434955597, 0.9676594138145447, -0.00570001220330596)
WMR:2:(-0.2335997223854065, -0.944620668888092, -0.23048460483551025, 0.9792690873146057)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:palm.02.L
Parent:hand.L
Head:(0.3061484098434448, 0.038899991661310196, 1.0252189636230469)
Tail:(0.3019583523273468, 0.02769998088479042, 0.9580886363983154)
Length:0.06818707837033267
WMR:0:(-0.014553106389939785, -0.06144958361983299, -0.9980041980743408, 0.3061484098434448)
WMR:1:(0.9864088296890259, -0.1642541140317917, -0.004270466044545174, 0.038899991661310196)
WMR:2:(-0.1636638641357422, -0.9845023155212402, 0.06300473213195801, 1.0252189636230469)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_middle.01.L
Parent:palm.02.L
Head:(0.3019583523273468, 0.02769998274743557, 0.9580886363983154)
Tail:(0.2784064710140228, 0.023399967700242996, 0.9147172570228577)
Length:0.049540466204234135
WMR:0:(0.03002334013581276, -0.4754068851470947, -0.8792536854743958, 0.3019583523273468)
WMR:1:(0.9929408431053162, -0.08679802715778351, 0.08083651959896088, 0.02769998274743557)
WMR:2:(-0.11474771052598953, -0.8754739761352539, 0.4694449007511139, 0.9580886363983154)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_middle.02.L
Parent:finger_middle.01.L
Head:(0.2784064710140228, 0.023399967700242996, 0.9147172570228577)
Tail:(0.256862610578537, 0.021799959242343903, 0.8908450603485107)
Length:0.032195958187134936
WMR:0:(-0.016263922676444054, -0.6691475510597229, -0.7429516315460205, 0.2784064710140228)
WMR:1:(0.9985017776489258, -0.049695923924446106, 0.022901061922311783, 0.023399967700242996)
WMR:2:(-0.052245840430259705, -0.741466224193573, 0.6689532399177551, 0.9147172570228577)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_middle.03.L
Parent:finger_middle.02.L
Head:(0.256862610578537, 0.021799959242343903, 0.8908450603485107)
Tail:(0.23979225754737854, 0.02159995399415493, 0.8758766055107117)
Length:0.022704439982939768
WMR:0:(-0.01572253555059433, -0.7518513798713684, -0.659145176410675, 0.256862610578537)
WMR:1:(0.9998658895492554, -0.008809125982224941, -0.01380158681422472, 0.021799959242343903)
WMR:2:(0.004570264369249344, -0.6592739820480347, 0.7518890500068665, 0.8908450603485107)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:palm.03.L
Parent:hand.L
Head:(0.3055240213871002, 0.05449999123811722, 1.0256550312042236)
Tail:(0.30269524455070496, 0.05209998041391373, 0.955956220626831)
Length:0.06979746575807515
WMR:0:(-0.040807563811540604, -0.040528569370508194, -0.9983448386192322, 0.3055240213871002)
WMR:1:(0.9986307621002197, -0.034385357052087784, -0.03942333161830902, 0.05449999123811722)
WMR:2:(-0.03273067623376846, -0.9985866546630859, 0.0418761782348156, 1.0256550312042236)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_ring.01.L
Parent:palm.03.L
Head:(0.30269524455070496, 0.05209998041391373, 0.955956220626831)
Tail:(0.27694419026374817, 0.04989996924996376, 0.9193423390388489)
Length:0.044816661755981785
WMR:0:(-0.05744895339012146, -0.5745863914489746, -0.8164253234863281, 0.30269524455070496)
WMR:1:(0.9981566071510315, -0.049089159816503525, -0.03568858653306961, 0.05209998041391373)
WMR:2:(-0.019571460783481598, -0.8169706463813782, 0.5763472318649292, 0.955956220626831)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_ring.02.L
Parent:finger_ring.01.L
Head:(0.27694419026374817, 0.04989996924996376, 0.9193423390388489)
Tail:(0.25495731830596924, 0.0493999607861042, 0.8963721990585327)
Length:0.03180094145881558
WMR:0:(-0.06333208084106445, -0.691390335559845, -0.7197003960609436, 0.27694419026374817)
WMR:1:(0.997233510017395, -0.01572306454181671, -0.07264977693557739, 0.04989996924996376)
WMR:2:(0.038913462311029434, -0.7223105430603027, 0.6904733180999756, 0.9193423390388489)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_ring.03.L
Parent:finger_ring.02.L
Head:(0.25495731830596924, 0.0493999607861042, 0.8963721990585327)
Tail:(0.2385593205690384, 0.049799952656030655, 0.8877090811729431)
Length:0.01855003867316774
WMR:0:(-0.054464541375637054, -0.8839878439903259, -0.46432676911354065, 0.25495731830596924)
WMR:1:(0.9873839020729065, 0.021562909707427025, -0.156869575381279, 0.0493999607861042)
WMR:2:(0.14868304133415222, -0.4670126736164093, 0.8716609477996826, 0.8963721990585327)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:palm.04.L
Parent:hand.L
Head:(0.3034741282463074, 0.06959999352693558, 1.0283842086791992)
Tail:(0.29747551679611206, 0.0762999877333641, 0.9541244506835938)
Length:0.07480230557451074
WMR:0:(-0.003450550138950348, -0.08019282668828964, -0.9967735409736633, 0.3034741282463074)
WMR:1:(0.9959237575531006, 0.08956936001777649, -0.010653658770024776, 0.06959999352693558)
WMR:2:(0.09013469517230988, -0.9927470684051514, 0.07955678552389145, 1.0283842086791992)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_pinky.01.L
Parent:palm.04.L
Head:(0.29747551679611206, 0.0762999877333641, 0.9541244506835938)
Tail:(0.27759575843811035, 0.07649997621774673, 0.9345836043357849)
Length:0.027876324430510892
WMR:0:(-0.12287505716085434, -0.7131419777870178, -0.6901670098304749, 0.29747551679611206)
WMR:1:(0.9831879138946533, 0.007174238096922636, -0.18245655298233032, 0.0762999877333641)
WMR:2:(0.13506880402565002, -0.7009831666946411, 0.7002708315849304, 0.9541244506835938)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_pinky.02.L
Parent:finger_pinky.01.L
Head:(0.27759575843811035, 0.07649997621774673, 0.9345836043357849)
Tail:(0.2601030468940735, 0.0769999697804451, 0.9202945828437805)
Length:0.022592500656735275
WMR:0:(-0.11284443736076355, -0.7742713093757629, -0.6227120757102966, 0.27759575843811035)
WMR:1:(0.9785446524620056, 0.022131117060780525, -0.2048439085483551, 0.07649997621774673)
WMR:2:(0.172386035323143, -0.6324669718742371, 0.755161464214325, 0.9345836043357849)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_pinky.03.L
Parent:finger_pinky.02.L
Head:(0.2601030468940735, 0.0769999697804451, 0.9202945828437805)
Tail:(0.24699437618255615, 0.07719997316598892, 0.9129698276519775)
Length:0.015017632562992945
WMR:0:(-0.10453005135059357, -0.8728853464126587, -0.4765971899032593, 0.2601030468940735)
WMR:1:(0.9713146686553955, 0.013317709788680077, -0.23742561042308807, 0.0769999697804451)
WMR:2:(0.21359246969223022, -0.48774388432502747, 0.8464540839195251, 0.9202945828437805)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:shoulder.R
Parent:ribs
Head:(-0.018300000578165054, -0.06839998811483383, 1.6051000356674194)
Tail:(-0.16940002143383026, 0.02050001174211502, 1.6050000190734863)
Length:0.1753123994652116
WMR:0:(0.5070948600769043, -0.8618901968002319, -0.000529240642208606, -0.018300000578165054)
WMR:1:(0.8618903160095215, 0.5070948004722595, 0.00022575189359486103, -0.06839998811483383)
WMR:2:(7.374442793661729e-05, -0.0005706493393518031, 0.9999998807907104, 1.6051000356674194)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:upper_arm.R
Parent:shoulder.R
Head:(-0.19530001282691956, 0.026700008660554886, 1.5845999717712402)
Tail:(-0.3024795651435852, 0.0885000005364418, 1.3239638805389404)
Length:0.28850973550906783
WMR:0:(-0.924582302570343, -0.3714936375617981, -0.08449970185756683, -0.19530001282691956)
WMR:1:(0.0032040609512478113, 0.21420414745807648, -0.9767836928367615, 0.026700008660554886)
WMR:2:(0.3809690773487091, -0.9033876061439514, -0.19685900211334229, 1.5845999717712402)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(0.7781602144241333, -0.6133349537849426, -0.1352292150259018, 0.0)
BMR:1:(0.6136394739151001, 0.7883368134498596, -0.04440385103225708, 0.0)
BMR:2:(0.13384060561656952, -0.04842866212129593, 0.9898188710212708, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:forearm.R
Parent:upper_arm.R
Head:(-0.3024795651435852, 0.0885000005364418, 1.3239637613296509)
Tail:(-0.30127403140068054, 0.049199994653463364, 1.064085841178894)
Length:0.26283545642833384
WMR:0:(-0.9999554753303528, 0.004586691502481699, -0.008256259374320507, -0.3024795651435852)
WMR:1:(0.007477679755538702, -0.14952321350574493, -0.9887299537658691, 0.0885000005364418)
WMR:2:(-0.005769494455307722, -0.9887475967407227, 0.14948229491710663, 1.3239637613296509)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(0.9512011408805847, -0.3051568865776062, 0.04577907174825668, 0.0)
BMR:1:(0.30504775047302246, 0.9522894620895386, 0.009522203356027603, 0.0)
BMR:2:(-0.04650069400668144, 0.004907271824777126, 0.9989061951637268, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:hand.R
Parent:forearm.R
Head:(-0.30127403140068054, 0.049199994653463364, 1.064085841178894)
Tail:(-0.2963748872280121, 0.04119998961687088, 0.9844757318496704)
Length:0.08016090818859924
WMR:0:(0.0013592976611107588, 0.06111632287502289, 0.9981299042701721, -0.30127403140068054)
WMR:1:(-0.9949796199798584, -0.09979937970638275, 0.007465844042599201, 0.049199994653463364)
WMR:2:(0.10006904602050781, -0.9931290149688721, 0.06067381799221039, 1.064085841178894)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:palm.01.R
Parent:hand.R
Head:(-0.30414843559265137, 0.022399989888072014, 1.0269181728363037)
Tail:(-0.30031484365463257, 0.005099982023239136, 0.9595847129821777)
Length:0.0696260117714473
WMR:0:(-0.00025309206102974713, 0.05505960434675217, 0.9984832406044006, -0.30414843559265137)
WMR:1:(-0.9685460329055786, -0.24847058951854706, 0.013456009328365326, 0.022399989888072014)
WMR:2:(0.24883460998535156, -0.9670735001564026, 0.05339062586426735, 1.0269181728363037)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_index.01.R
Parent:palm.01.R
Head:(-0.30031484365463257, 0.005099981091916561, 0.9595847129821777)
Tail:(-0.2832036018371582, 0.0011999702546745539, 0.9180952906608582)
Length:0.04504860536824087
WMR:0:(0.09713990241289139, 0.379839688539505, 0.9199380874633789, -0.30031484365463257)
WMR:1:(-0.9863735437393188, -0.08657345920801163, 0.1399010419845581, 0.005099981091916561)
WMR:2:(0.1327822059392929, -0.9209924936294556, 0.366254061460495, 0.9595847129821777)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_index.02.R
Parent:finger_index.01.R
Head:(-0.2832036018371582, 0.0011999703710898757, 0.9180952906608582)
Tail:(-0.26859575510025024, -0.000300038605928421, 0.8939993977546692)
Length:0.028217924589995197
WMR:0:(0.0909094512462616, 0.5176796913146973, 0.8507311940193176, -0.2832036018371582)
WMR:1:(-0.989000141620636, -0.0531579852104187, 0.1380321979522705, 0.0011999703710898757)
WMR:2:(0.11667963862419128, -0.8539217114448547, 0.5071527361869812, 0.9180952906608582)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_index.03.R
Parent:finger_index.02.R
Head:(-0.26859575510025024, -0.0003000386350322515, 0.8939993977546692)
Tail:(-0.2528434991836548, 0.0005999548593536019, 0.877514660358429)
Length:0.022818635404477372
WMR:0:(0.11709101498126984, 0.6903250813484192, 0.713961660861969, -0.26859575510025024)
WMR:1:(-0.9914400577545166, 0.03944117948412895, 0.1244625598192215, -0.0003000386350322515)
WMR:2:(0.05776016414165497, -0.7224237322807312, 0.689034104347229, 0.8939993977546692)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:thumb.01.R
Parent:palm.01.R
Head:(-0.28028616309165955, 0.021399999037384987, 1.0371413230895996)
Tail:(-0.26061710715293884, 0.0014999937266111374, 1.0061618089675903)
Length:0.04174460765326611
WMR:0:(0.8817521929740906, 0.4711759388446808, 0.022508161142468452, -0.28028616309165955)
WMR:1:(0.23309920728206635, -0.4767081141471863, 0.8475931882858276, 0.021399999037384987)
WMR:2:(0.41009530425071716, -0.7421203851699829, -0.5301691889762878, 1.0371413230895996)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:thumb.02.R
Parent:thumb.01.R
Head:(-0.26061710715293884, 0.0014999930281192064, 1.0061618089675903)
Tail:(-0.24979761242866516, -0.005700009874999523, 0.975343644618988)
Length:0.03344638632734788
WMR:0:(0.9415143728256226, 0.3234878182411194, -0.09437473118305206, -0.26061710715293884)
WMR:1:(0.1703493744134903, -0.21526993811130524, 0.9615820050239563, 0.0014999930281192064)
WMR:2:(0.29074400663375854, -0.9214198589324951, -0.25778576731681824, 1.0061618089675903)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:thumb.03.R
Parent:thumb.02.R
Head:(-0.24979761242866516, -0.0057000103406608105, 0.975343644618988)
Tail:(-0.24477443099021912, -0.009800011292099953, 0.955590009689331)
Length:0.020790585669949638
WMR:0:(0.9643967747688293, 0.241608664393425, -0.10753796994686127, -0.24979761242866516)
WMR:1:(0.15730726718902588, -0.19720463454723358, 0.9676594138145447, -0.0057000103406608105)
WMR:2:(0.21258790791034698, -0.9501240253448486, -0.2281903773546219, 0.975343644618988)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:palm.02.R
Parent:hand.R
Head:(-0.3064069449901581, 0.038899991661310196, 1.0225409269332886)
Tail:(-0.3036840260028839, 0.02769998274743557, 0.9553350806236267)
Length:0.06818709750150699
WMR:0:(-0.010975182056427002, 0.0399332270026207, 0.9991422295570374, -0.3064069449901581)
WMR:1:(-0.9864088296890259, -0.1642540991306305, -0.004270440433174372, 0.038899991661310196)
WMR:2:(0.16394269466400146, -0.9856095910072327, 0.04119318351149559, 1.0225409269332886)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_middle.01.R
Parent:palm.02.R
Head:(-0.3036840260028839, 0.02769998274743557, 0.9553350806236267)
Tail:(-0.2810850143432617, 0.023299969732761383, 0.9114596843719482)
Length:0.04954922641941893
WMR:0:(0.03160826116800308, 0.4560922086238861, 0.8893712162971497, -0.3036840260028839)
WMR:1:(-0.992764949798584, -0.08880080282688141, 0.08082223683595657, 0.02769998274743557)
WMR:2:(0.11583929508924484, -0.8854911923408508, 0.44998544454574585, 0.9553350806236267)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_middle.02.R
Parent:finger_middle.01.R
Head:(-0.2810850143432617, 0.023299969732761383, 0.9114596843719482)
Tail:(-0.2600676715373993, 0.0217999629676342, 0.887122631072998)
Length:0.032191161553918914
WMR:0:(-0.01309398002922535, 0.6528918147087097, 0.757338285446167, -0.2810850143432617)
WMR:1:(-0.9986511468887329, -0.046596869826316833, 0.022904489189386368, 0.023299969732761383)
WMR:2:(0.050243765115737915, -0.7560167908668518, 0.6526212692260742, 0.9114596843719482)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_middle.03.R
Parent:finger_middle.02.R
Head:(-0.2600676715373993, 0.0217999629676342, 0.887122631072998)
Tail:(-0.2433283030986786, 0.02159995771944523, 0.8717849254608154)
Length:0.02270444166396787
WMR:0:(-0.01581859029829502, 0.7372732758522034, 0.6754097938537598, -0.2600676715373993)
WMR:1:(-0.9998659491539001, -0.008809060789644718, -0.013801590539515018, 0.0217999629676342)
WMR:2:(-0.004225800279527903, -0.6755375266075134, 0.7373137474060059, 0.887122631072998)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:palm.03.R
Parent:hand.R
Head:(-0.30577319860458374, 0.054399993270635605, 1.022963285446167)
Tail:(-0.3044673204421997, 0.05209998041391373, 0.9532193541526794)
Length:0.06979406371022387
WMR:0:(-0.04005623981356621, 0.01871049590408802, 0.9990224242210388, -0.30577319860458374)
WMR:1:(-0.9986790418624878, -0.032954249531030655, -0.039425238966941833, 0.054399993270635605)
WMR:2:(0.03218439221382141, -0.9992818832397461, 0.020005786791443825, 1.022963285446167)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_ring.01.R
Parent:palm.03.R
Head:(-0.3044673204421997, 0.05209998041391373, 0.9532193541526794)
Tail:(-0.2795220613479614, 0.04989997297525406, 0.9160518050193787)
Length:0.044816656419072176
WMR:0:(-0.057007744908332825, 0.5566065907478333, 0.8288182616233826, -0.3044673204421997)
WMR:1:(-0.9981566667556763, -0.04908903315663338, -0.035688575357198715, 0.05209998041391373)
WMR:2:(0.020821411162614822, -0.8293249011039734, 0.5583789348602295, 0.9532193541526794)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
Bone:finger_ring.02.R
Parent:finger_ring.01.R
Head:(-0.2795220613479614, 0.04989997297525406, 0.9160518050193787)
Tail:(-0.2580421268939972, 0.0493999682366848, 0.8926069140434265)
Length:0.03180095127129806
WMR:0:(-0.06416677683591843, 0.6754500865936279, 0.7346089482307434, -0.2795220613479614)
WMR:1:(-0.9972335696220398, -0.015722934156656265, -0.0726497694849968, 0.04989997297525406)
WMR:2:(-0.037521060556173325, -0.7372384071350098, 0.6745902895927429, 0.9160518050193787)
WMR:3:(0.0, 0.0, 0.0, 1.0)
BMR:0:(1.0, 0.0, 0.0, 0.0)
BMR:1:(0.0, 1.0, 0.0, 0.0)
BMR:2:(0.0, 0.0, 1.0, 0.0)
BMR:3:(0.0, 0.0, 0.0, 1.0)
...

Edited by BBeck, Today, 04:20 AM.