• Content count

  • Joined

  • Last visited

Community Reputation

826 Good

About BBeck

  • Rank

Personal Information

  • Location
  • Interests
  1. Probably what you want to do is learn GLSL or HLSL or both. Most of what OpenGL and DirectX do is make calls to the shader to do the actual work on the graphics card. The shader code is more "graphics card level" than DX or OGL themselves. When you understand the shader programming you'll have a pretty deep understanding of the graphics card.   There are some things outside of the shader, such as setting any sort of render state and defining buffers and such, but most of that is about getting ready to call the shader.   This book is a highly technical discussion of the graphics card programming. I would not recommend reading it unless you are at least at an intermediate level on the subject and unfortunately it is DX11 and thus starting to get a little dated. I think DX12 and Vulkan get into an even lower level of what is going on inside the graphics card.   Maybe a better place to start is with learning GLSL and HLSL and some of the math behind shader programming. I have a video series on YouTube that teaches how to write a basic Blinn-Phong shader in HLSL. This is a strong foundation for any other type of shader you plan on writing after that. The series stops just short of normal mapping, which would have been the next step. Unless you are interested, you could probably skip the intro video in the HLSL series and jump straight to the Triangles video. If you don't know Linear Algebra, you probably want to watch the matrix and vector videos as linear algebra is huge part of how things actually get drawn.   Remember that what you are actually doing is drawing a still motion frame. Things will make a little better sense if you realize that and understand that you are not doing full motion. Rather you are drawing one frame, then drawing another, and another and another, and it's actually the sequence of still motion frames that gives the impression of animation.   Oh, and you may also want to check out as a source of learning OGL. It gets pretty deep into teaching graphics including delving into the subject of PBR.
  2. The first pictures are really too small for me to see. But I can tell they have heavy specular lighting applied. That's a Blinn-Phong shader. If that's what you are wanting, it is relatively easy. But the question remains how to get it that way in your game engine. The answer is entirely dependent on what game engine you are using. I mean, I can give you the math, but the math may do you no good in Unity, for example, unless you are writing your own Unity shaders. And it may just be a matter of telling your engine "apply 30% specular" instead of anything you really have to do.   If you really want your models to "shine" though, you should probably be using PBR like Substance. That's a "whole 'nother level". Maybe all you need is specular. If you want to know about the math, I have a whole video series on HLSL that covers the math fairly in depth.
  3. I spent an entire weekend on the Oculus Rift in probably about 10 hour sessions each with zero motion sickness running off a GTX1080.   I had the first developer kit and the commercial Oculus Rift is a big improvement over that. With the DK1, I played with it for maybe a couple days and experienced a bit of a headache after spending about a full hour with it. I'm not overly prone to motion sickness but have been sea-sick before. I didn't play with the DK1 long enough to really get sick but in the few sessions I did spend with it I had only minor issues I was not even certain were caused by the VR. Still, I did experience what might be seen as some mild motion sickness after an hour or so.   I agree with Tbiblopper's statement that "Motion sickness usually happens when your eyes and your inner ear disagree".   I've heard several theories on what causes motion sickness with VR and how an artificial nose or something would make all the difference. But I think the mismatch between the inner ear and the eyes is really the key. And low frame rates seem to be the most likely culprit to me.   Regardless, I've got about 20 hours of playing Skyrim (a game not even designed remotely for VR) through VorpX without even the slightest hint of motion sickness. I've even spent a little time on horse-back which I thought for certain would cause some motion sickness because the motion is pretty crazy, and nothing.
  4. I don't have time to read through the code right now, but what determines whether you rotate on the global or local axis is the order of multiplication between the world matrix and the rotation matrix. Switch the order of multiplication if it's not working the way you want.   I happen to be on the wrong computer right now where I can't get to my own source code to find an example, but you may want to download and look at my DX11 3D "engine". That code does local rotations. It's on my website at and has the full VS project including shader code, Python Blender exporter code to export models, art assets, etc. available in one download file. So, there's a lot to look at there for examples. And with a bit of work it should compile although it's coded for Windows 7 and not Windows 8 or Windows 10 (which have to be coded slightly different). Still rotations are the same no matter what environment you are in.   You may also want to watch my vector and matrix videos on my YouTube channel probably in that order if you are not super familiar with those Linear Algebra topics.   I wish I were on my computer where I could copy and paste a code example, but it would have been from the code I linked above anyway. But the bottom line is that you just have to reverse the order of multiplication. When that is not possible, usually because you are using someone else's library that doesn't allow you to do the math yourself, there is a work around that works fine. And that is to move the object to the center (origin), do the rotation, and then move it back to where it was. Works like a charm and you do it in a single frame, so it is undetectable to the user. That always works too.
  5. Interesting article. I'm still a bit aways from getting up to the level of coding HDR stuff, but I've done some photography over the years and thus understand the subject of light somewhat.   I never imagined HDR for games would have the same complications that exist in the real world with lighting like exposure and white balance. Very informative.
  6. These days its all Unity and Unreal SDK. You should probably check those out. I think Unreal uses C++ as a scripting language. That's the modern thing anyway.   It depends a lot on what your end goals are and what your interests are. For me, it was too easy to get lazy with Unity and buy everything, which kind of defeats the whole purpose when you haven't created anything but merely assembled other peoples' stuff. I missed learning about low level stuff which is why I went back to C++ and OGL 4.5 and have been happy with my decision. But for me it's more about learning rather than trying to produce a commercial game.   Personally, I'm into OpenGL (4.5) right now after doing a little DX11. I think modern OGL is a pretty good learning platform if you find the right teachers. Check out if you think you want to get back into OpenGL.   You mentioned you were not familiar with shaders and the last time you were really into graphics programming was before the big shader revolution. You may want to check out my HLSL series on YouTube. You may want to skip the first video of the series and start with the "Triangles" video since the first video just shows the XNA code that I used to call the shader and explains the differences between that and doing HLSL with DX11.   Of course OGL uses GLSL and not HLSL, but most of what is in the video is math more than it is language specific. Not to mention I have written pretty much an identical shader to the one at the end of the video in GLSL and tried to use the same layout and variable names for comparison.   I'll see if I can find a link to a recent post where I posted that shader code.   But the whole series is designed to watch in order. It starts with the most simple 3D shader you can possible have: the silhouette shader. And then each video adds more and more code until it is turned into a Blinn-Phong shader that supports texturing which is pretty much the thing you want to learn to start learning 3D shaders. That gives you a platform to go on and learn more on your own once you kind of see what's going on. The next video would have been to add normal mapping. The series takes you right up to that point where that would have been the next step. I even did a test program to kind of prepare to make that video and never made it.   Also, there is a lot of vector and matrix algebra in shader programming and modern 3D graphics. If you are not big on Linear Algebra, you may want to watch my Vector and Matrix videos on my channel. It might help to watch those before the HLSL series because the shader uses vectors and matrices quite a bit.   On my website, there is a working 3D code example that makes use of the shader and you can download the source code and see how the GLSL is called and such.   EDIT: Couldn't find my GLSL code where I've posted it here recently. Must have been on the OGL forum where I posted it. Well here it is anyway. This is the GLSL translation of the HLSL shader you end up with at the end of my video series except this shader added fog that I didn't do in the video series. This is actually a Blinn or a Phong shader depending on which lines you comment out, but I think the video explains that better than I could here.   BlinnPhong.vrt #version 450 core layout (location = 0) in vec3 Pos; layout (location = 1) in vec2 UV; layout (location = 2) in vec3 Normal; layout (location = 3) in vec4 Color; uniform mat4 WorldMatrix; uniform mat4 ViewMatrix; uniform mat4 ProjectionMatrix; smooth out vec2 TextureCoordinates; smooth out vec3 VertexNormal; smooth out vec4 RGBAColor; smooth out vec4 PositionRelativeToCamera; out vec3 WorldSpacePosition; void main() {     gl_Position = WorldMatrix * vec4(Pos, 1.0f);                //Apply object's world matrix.     WorldSpacePosition =;                        //Save the position of the vertex in the 3D world just calculated. Convert to vec3 because it will be used with other vec3's.     gl_Position = ViewMatrix * gl_Position;                        //Apply the view matrix for the camera.     PositionRelativeToCamera = gl_Position;     gl_Position = ProjectionMatrix * gl_Position;                //Apply the Projection Matrix to project it on to a 2D plane.     TextureCoordinates = UV;                                    //Pass through the texture coordinates to the fragment shader.     VertexNormal = mat3(WorldMatrix) * Normal;                    //Rotate the normal according to how the model is oriented in the 3D world.     RGBAColor = Color;                                            //Pass through the color to the fragment shader. };   BlinnPhong.frg #version 450 core in vec2 TextureCoordinates; in vec3 VertexNormal; in vec4 RGBAColor; in float FogFactor; in vec4 PositionRelativeToCamera; in vec3 WorldSpacePosition; layout (location = 0) out vec4 OutputColor; uniform vec4 AmbientLightColor; uniform vec3 DiffuseLightDirection; uniform vec4 DiffuseLightColor; uniform vec3 CameraPosition; uniform float SpecularPower; uniform vec4 FogColor; uniform float FogStartDistance; uniform float FogMaxDistance; uniform bool UseTexture; uniform sampler2D Texture0; vec4 BlinnSpecular(in vec3 LightDirection, in vec4 LightColor, in vec3 PixelNormal, in vec3 CameraDirection, in float SpecularPower) {     vec3 HalfwayNormal;     vec4 SpecularLight;     float SpecularHighlightAmount;     HalfwayNormal = normalize(LightDirection + CameraDirection);     SpecularHighlightAmount = pow(clamp(dot(PixelNormal, HalfwayNormal), 0.0, 1.0), SpecularPower);     SpecularLight = SpecularHighlightAmount * LightColor;     return SpecularLight; } vec4 PhongSpecular(in vec3 LightDirection, in vec4 LightColor, in vec3 PixelNormal, in vec3 CameraDirection, in float SpecularPower) {     vec3 ReflectedLightDirection;         vec4 SpecularLight;     float SpecularHighlightAmount;     ReflectedLightDirection = 2.0 * PixelNormal * clamp(dot(PixelNormal, LightDirection), 0.0, 1.0) - LightDirection;     SpecularHighlightAmount = pow(clamp(dot(ReflectedLightDirection, CameraDirection), 0.0, 1.0), SpecularPower);     SpecularLight = SpecularHighlightAmount * LightColor;          return SpecularLight; } void main() {     vec3 LightDirection;     float DiffuseLightPercentage;     vec4 SpecularColor;     vec3 CameraDirection;    //Float3 because the w component really doesn't belong in a 3D vector normal.     vec4 AmbientLight;     vec4 DiffuseLight;     vec4 InputColor;          if (UseTexture)     {         InputColor = texture(Texture0, TextureCoordinates);     }     else     {         InputColor = RGBAColor; // vec4(0.0, 0.0, 0.0, 1.0);     }     LightDirection = -normalize(DiffuseLightDirection);    //Normal must face into the light, rather than WITH the light to be lit up.     DiffuseLightPercentage = max(dot(VertexNormal, LightDirection), 0.0);    //Percentage is based on angle between the direction of light and the vertex's normal.     DiffuseLight = clamp((DiffuseLightColor * InputColor) * DiffuseLightPercentage, 0.0, 1.0);    //Apply only the percentage of the diffuse color. Saturate clamps output between 0.0 and 1.0.     CameraDirection = normalize(CameraPosition - WorldSpacePosition);    //Create a normal that points in the direction from the pixel to the camera.     if (DiffuseLightPercentage == 0.0f)     {         SpecularColor  = vec4(0.0f, 0.0f, 0.0f, 1.0f);     }     else     {         //SpecularColor = BlinnSpecular(LightDirection, DiffuseLightColor, normalize(VertexNormal), CameraDirection, SpecularPower);         SpecularColor = PhongSpecular(LightDirection, DiffuseLightColor, normalize(VertexNormal), CameraDirection, SpecularPower);     }     float FogDensity = 0.01f;     float LOG2 = 1.442695f;     float FogFactor = exp2(-FogDensity * FogDensity * PositionRelativeToCamera.z * PositionRelativeToCamera.z * LOG2);     FogFactor = 1 - FogFactor;     //float FogFactor = clamp((FogMaxDistance - PositionRelativeToCamera.z)/(FogMaxDistance - FogStartDistance), 0.0, 1.0);          OutputColor = RGBAColor * (AmbientLightColor * InputColor) + DiffuseLight + SpecularColor;     OutputColor = mix (OutputColor, FogColor, FogFactor);     //OutputColor = vec4(0.0f, 0.5f, 0.0f, 1.0f); };  
  7.   It's matrix algebra. So, yes, it's basically a simple transformation. Why would it not be recommended, especially for first-person 3D games?   This is basically how the graphics card works. Most all 3D shaders are going to want a projection, view, and world/object matrix to draw an object. When I can, I keep as close to those matrices as possible. In some cases, I have to use other storage, but as much as possible I store the data in what will be these three matrices that get sent to the graphics card as requirements to draw.   In the case above, I will draw two cubes: Cube and Cube2. Cube2 is a child of Cube and orbits its parent as an attachment. I could have that relationship be direct but I wanted to change the orbit without directly affecting either cube directly. So, I created a matrix between them as the origin of Cube2. So, now Cube is the grandparent, the pivot point is the parent, and Cube2 is the child. These could have been 3 objects that are all parented together like a body, an upper arm, and a lower arm. This is basically how "bones" or a humanoid armature works that controls a 3D humanoid model. All the bones are basically just matrices that work pretty much exactly like this. I have a code example where I export Blender animation data of the standard humanoid armature used to animate humanoid models in Blender. I exported a handful of animations and played the animations back in my code as stick figures in the example code. Pretty much all of that is linked matrices like this; every bone of the armature is parent-child matrices like this.   Granted, none of this is working directly with the camera, but the view matrix is basically just another world/object matrix other than it does everything backwards (it's inverted).   A lot of times, quaternions will be used instead of matrices. A quaternion is basically the same thing as a 3 by 3 matrix in that it contains an orientation. A 4 by 4 matrix has the advantage of also being able to contain the position simultaneously, which the quaternion cannot do. The quaternion, in this case however, has the advantage of being able to natively do SLERP (Spherical Linear IntERPolation). SLEPR is the "cousin" of LERP (Linear IntERPolation) which is basically just a weighted average between two numbers or pieces of data. The only way I know how to do SLERP is to decompose the matrix and once you pull the data out of it you can do the SLERP and then put the data back into the matrix, which is pretty ugly in terms of CPU cycles and such. Quaternions handle SLERP natively without decomposing and are thus quite superior to matrices for SLERP.   SLERP is needed for keyframe animation. When you have your skinned model like the humanoid model I mention above, the animation data only contains key frames. Those are poses of the model at given frames. Your code has to create the poses of the model between the keyframes to transition the model from one key frame to the next. The poses are basically just matrices of the bones of the model. Or you can use quaternions, plus a position, to store the same bone data. Either way you have to SLERP the orientation values between poses and quaternions, as far as I can tell, handle that far more elegantly. But it's basically the same thing between quaternions and matrices other than that. You can certainly use matrices, but then you have to decompose them and recompose them every time you do an interpolation, and skinned animation play back is a whole lot of interpolating.   But as long as you don't have to interpolate values between values such as finding the matrix 30% between two other matrices, then I would consider matrices superior to quaternions since the graphics card is going to require you to submit a matrix, not a quaternion when it draws the object. At least, I haven't come across a shader yet that looks for quaternions as input, but perhaps they are out there and I just haven't seen it yet. As far as any shader I've seen yet, you would have to convert every last one of those quaternions into 3 by 3 and then 4 by 4 matrices before drawing.   As far as chaining matrices like in the above code example, I don't see any reason why not. How else would you do skinned animation (other than a quaternion of course which is basically the same thing for the purpose of game programming)?   Actually, it is available in the code you posted. That's what this is since it's declared public: public:     glm::mat4 View() const { return glm::lookAt(m_Position, m_Target + m_Position, m_Up); } The view matrix is the "model" matrix of the camera, which is part of the point I've been driving at. It's inverted compared to normal "model" matrices. But it's still for all practical purposes the same thing. You can either invert it in order to treat it like any other model matrix, or you can simply understand that it is backwards and perform all operations using it backwards. I like inverting it myself.
  8. In this case, the inverted matrix only exists for a single frame. Any discrepancies in the math would generally only exist for less than a 60th of a second before the inverted matrix is thrown away and completely rebuilt from scratch in the next frame. But you can generally avoid inverting by simply understanding that all the math must be done "backwards" on a view matrix, which is really all the inversion is doing; instead of doing the math backwards, inverting it makes the matrix backwards so that "forward running" math works on it. Kind of like adding a positive number to a negative number compared to subtracting a number from another number, same end result.
  9. To attach a weapon to the camera. Get the view matrix. Invert it to turn it into a world/object matrix. Multiply it times the weapon's world/object matrix. And use the results instead of the weapon's world/object matrix to draw it. This will make the weapon a child of the camera and thus attach it. It will make the weapon's position and orientation relative to the camera. You may have to reverse the order of multiplication if it doesn't work right. Order of multiplication will determine if it is a local or global axis.   Although, what you probably want is a "pivot point"/"attachment point" matrix in between the two so that you can control the point relative to the camera where the weapon is actually attached and even rotate the pivot point to rotate the weapon rather than rotating the weapon itself. That would be inverse view times the pivot point matrix times the weapon's world/object matrix and use the result to draw the weapon. Again, you might have to reverse the order of multiplication to get the desired result. The pivot point becomes an origin relative to the camera's position and orientation which is relative to the world origin.And the weapon becomes relative to the pivot point.   That's similar to what I have going on in this code:         //Yellow cube controls.         if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_I && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)             Cube.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, 0.05f)));         if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_K && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)             Cube.Transform(glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -0.05f)));         if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_L && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)             Cube.Transform(glm::rotate(glm::mat4(), glm::radians<float>(-1), glm::vec3(0.0f, 1.0f, 0.0f)));         if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_J && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)             Cube.Transform(glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)));                 if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_Y && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)             Cube2Pivot =   glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 0.0f, 1.0f)) * Cube2Pivot;         if (OperatingSystem.Keyboard.KeyPressed == GLFW_KEY_H && OperatingSystem.Keyboard.ActionPressed != GLFW_RELEASE)             Cube2Pivot = glm::rotate(glm::mat4(), glm::radians<float>(-1), glm::vec3(0.0f, 0.0f, 1.0f)) * Cube2Pivot;         Cube2Pivot = Cube2Pivot * glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f));         Cube2.WorldMatrix = Cube.WorldMatrix * Cube2Pivot * Cube2World; There I have a cube attached to another cube (rather than the camera) through a pivot/attachment point. So, every frame I set the second cube's world/object matrix equal to the primary cube's times the pivot's times the matrix I maintain for the second cube. Cube2World is the matrix I change for the attached object and Cube2.WorldMatrix is what I actually use to draw the second cube.   You can make chains with as many links in them as you want such as this. Each link is relative to the last.
  10. I have never worked on a project like that, but if I were to start on something like that, I think I would want to learn about economics in general. For that you might want to read "Naked Economics: Undressing the Dismal Science" it is a really good book that explains how everything boils down to supply and demand regardless of whether money is involved.
  11.   That makes sense. FBX seems to be the most popular format out there. I used it a lot in XNA and then in when I did a short stint in Unity.   In my particular case, I don't ever plan on supporting Maya or Max. Blender is all I plan on using for modelling in the foreseeable future. So, tying myself closely to Blender feels right for me in my particular case. The intent of my engine is for it to be a private engine for my own personal use and to post the source code for it for anyone who wants to look it over as an example or get some ideas from it. But if one wanted to support many modelling programs and/or made use of different modelling programs, using the FBX SDK makes a lot of sense. If one were making a commercial engine, it would almost be a necessity. People expect you to support FBX. I would expect it in a commercial engine and would probably not use the engine if it didn't support FBX.   When I was deciding what to do with my engine, I considered Assimp, the FBX SDK, and writing a Blender exporter. I first decided to use the FBX SDK and spent some pretty serious time looking the FBX SDK over. It was my first time having to write a model importer like that. I realized it was going to take a month or so for me to figure out the FBX SDK. For one thing, the FBX format is a "kitchen sink" format. As I imagine you know, it is not a format for game models at all; it is a format much like the Blend file in Blender that encompasses everything. It's for making movies, it's for saving your entire project, it's for saving your lighting, your animation, your cameras, the animation of your cameras, your shaders and so forth and 90% of what can be saved in an FBX file is just garbage from a game programmer's perspective. Most of the time, I just want the model data, not a light that my game engine doesn't know how to use, or a shader built into the modelling program that won't be there in my engine.   Anyway, I realized it was going to take me a month or more to figure out the FBX SDK and how to use it and that instead I could spend that month digging into Blender's data and figuring out how to extract the raw data directly from Blender. So, I abandoned the idea of the FBX SDK. And in my case, I am glad I did. It was a great learning experience working with the raw Blender data to see how Blender is organizing things internally as well as to see exactly what it takes to extract raw Blender data into my engine and turn it into a 3D model. It has given me a tremendous amount of confidence in approaching any other format in the future or using any other library or SDK. Which is why I'm thinking about trying Assimp next. And at some point, I might want to try the FBX SDK again.   The FBX format is certainly extremely popular and supporting it really does give you a lot of options.
  12. If your faces are triangles, then it's self evident that there will be 120 edges for 40 faces. I'm not sure what exactly you are working with, but I would start from this premise. A better question is, "Why wouldn't your 40 faces have 120 edges?" The number of vertices may tell you nothing of the number of triangles. If you have an index buffer, vertices can be reused without sharing an edge.   I would recommend reducing the problem. For example, make it two faces and try and solve the problem with just two faces and however many vertices that takes. If you can solve the simple problem, then add more to it and solve that until you get back to the original problem.
  13. I would recommend going through the tutorials at as that's going to get you started in the right direction for an OGL engine. There's a ton of stuff to learn for a 3D game engine and expect it to take years to get the basics down.   While I agree with L. Spiro's general premise - that the point of making an engine is to learn and you defeat that purpose if you take too many short-cuts, and I greatly respect her experience as a veteran game maker, felipefsdev is basically correct; GLFW saves you from learning the deep internals of Linux, MacOS, WIndows, etc. And learning operating system program for every operating system out there is not learning game programming.   I've done it both ways. When I learned DirectX 11 I tried not to use any libraries other than C++ (and thus STL) and DirectX. I wrote the code to handle the Windows OS. But the code was not at all cross platform compatible; I mostly only knew what I was doing because I had many years before spent a year or so learning deep Windows programming. The code is very locked into a specific version of Windows, Windows 7. It's not compatible with Win8, Win10, or Windows 3.1. (It would probably run fine on Vista and maybe XP. It might even run as legacy code in Win8/10.) Although, it probably would not be a huge rewrite to rewrite it for Win10 and such, but then it would be locked into that and not Win7 unless you included the code for both. This path of doing OS level stuff is not an easy one and has basically nothing to do with game programming; plan on forgetting entirely about game programming and focusing on OS programming for at least a year or two and that's if you plan on only supporting Windows in your engine. I have no idea what you would have to go through to add Linux or MacOS support to your engine. I didn't even try to support anything outside of Win32 such as Win8 or Win10. So, I consider something like GLFW that handles many versions of Windows, Linux, and MacOS to be a real boon and welcome companion so that I can get back to game programming.  The Visual Studio Project file for the DX engine is available for download on my website.   I also did not use a library for 3D models, but rather wrote my own Blender exporter in Python and the supporting C++ code to load the data and turn it into a 3D model. That source code and all the associated files are in the download. (Or you can look at this thread where I incessantly ranted on and on about my Python Blender exporter and posted the code.)   When I went to OpenGL 4.5, I decided to take another tack. One of the advantages of OGL is that it is cross platform. I have various reasons for wanting to do OGL, but a big selling point is the cross platform compatibility. To get that, you either have to learn some pretty serious OS programming in every OS and spend a few years on each OS before you even begin to learn about anything else, or use a library that handles that for you. Right from the start, OGL lends itself to using libraries. I decided to just go with that philosophy and embrace it and thus have been a lot more likely to use libraries as I begin to put my OGL engine together.   Of course, we are talking about a very simple 3D engine. A video of where I am at on the OGL engine is available for download on my website. I haven't gotten the Blender model support into the OGL version yet and really both are still very much a work in progress although I don't expect to get back to the DX engine in the foreseeable future and even the OGL engine is on hold right now while I study 3D modeling and maybe Vulkan sometime in the future. I've debated on whether to bring in the model support code from the DX engine or use a library like Assimp. Doing it myself was an excellent learning experience and still has much left to teach me. But I may try out Assimp and see what that's like.   For my OGL 4.5 engine, I used GLFW, GLEW, GLM, and FreeImage libraries. That's pretty much a minimum for doing 3D OGL. GLFW keeps you from having to learn a mountain of OS stuff that has basically nothing to do with game programming. GLEW handles all the OGL extensions which I hear is a real pain to handle and again has little to do with game programming. I have some complaints about GLM and I am a little tempted to write my own math library, but it's pretty standard and if I'm doing tutorials it's probably best to stick to something people can download that is tried and tested rather than bogging them down with a math library of my own creation. Plus, I make due with GLM. It works. It gets the job done with few issues even if I do have to force it to my will by using it in non-standard ways. FreeImage is pretty much a requirement. I tried another photograph library that is popular but could not get it to work with C++ as it seemed to be more oriented for C programs. FreeImage plays nice with my C++ OOP code. And OGL doesn't seem to natively support any type of image files. DirectX at least natively supported .DDS files. I really don't want to have to study the JPEG standard and learn to write code to compress and uncompress JPEG files just to support one image format. That's 6 months I could be spending learning game programming instead of learning one image format. Ok, maybe it would not take quite that long, but it would certainly take some time to decode JPEG. I'm not even sure if the info is out there. Seems some of those formats like MPeg are closely guarded secrets.   For example, the .FBX model format is owned by Autodesk and they aren't sharing it with anyone. They have a library you can download that will load and save .FBX files. And some seem to have somewhat successfully guessed the format. But the format is a closely guarded secret and any changes they make will require re-guessing how they did it if you try to work with the file format directly. I considered learning their library just to support .FBX files and decided if I was going to have to spend that much time learning something, I might as well learn Python and how to write an exporter in Blender to create my own model file format. So, that's what I did. (The exporter is in the DX project file download as well.) If I had to do it again, I might learn the .OBJ format although I don't know if it supports skinned animation (neither does mine for that matter, but when you write your own, you can always add to it).   Anyway, all the source code for my OGL 4.5 and DX11 engines are available for download on my website at and you are welcome to download them, study them, or otherwise use them as you see fit. It's certainly nothing to compare to Unity or Unreal. In fact it's pretty bare bones. But it's enough code to put textured models on the screen, animate them, and move the camera through the scene while taking keyboard and game controller input. It doesn't support sound yet. There's still much work to be done, but it's a start. You can't make 3D games until you can do at least that much.   Also, going down this OGL road, you are going to have to learn GLSL probably sooner rather than later. There's a Blinn-Phong shader included in my engine that will get you through the first couple of years that you can just copy and paste. I have a whole video series on HLSL (the DX version of GLSL). My GLSL shader is basically the same shader for OGL. It's probably worth your time to watch the HLSL series to learn GLSL; much of it is math that will be the same whether it is HLSL or GLSL. And with a copy of the shader code in GLSL, you should be able to mostly follow along.   If you don't know linear algebra pretty well, you maybe should check out my Vector and Matrices videos before attempting the HLSL videos. There is a lot of vector and matrix math in shaders and game programming in general.   And I would recommend learning OGL 4.5 before Vulkan. I might point out that I don't know Vulkan; it's on my somewhat near term agenda to learn it. I've only read one book on the subject and I'm looking forward to it, but it's more complex than OGL and there are fewer resources for learning it. OGL 4.5 is a long ways from being abandoned or obsolete. And it seems to me that learning modern OGL first gives you the advantage of learning is a somewhat easier platform as well as being able to code in both once you learn Vulkan. I'm not discouraging you from learning Vulkan but learning OGL as your first step into the world of bare metal graphics programming is going to be an uphill slog. Learning to "hike" with OGL before you try and climb the Matterhorn, might help keep you from giving up. OGL by itself as well as 3D graphics programming by itself is a long arduous journey. There's plenty to learn without making it extra difficult. And you can always come back and pick up more; Vulkan will still be there waiting and so will OS programming if you want to learn to write low level Linux code or learn to support every version of Windows ever made. And the mysteries of the JPEG file format are not going anywhere either.   DX11 is very comparable to OGL 4.5. DX12 is supposed to be very comparable to Vulkan although I've only read books about either and compiled a tutorial program or two. I find OGL to be easier because of all the libraries, but the cross platform compatibility really makes the libraries a huge boon. And really, the only library that I would say at all "cheats you out of a game programming education" is Assimp. Even that might be a good idea when you are first learning. DX has a math library pretty much built into it. So, it's not a huge cheat to use a math library. And GLFW (or GLUT) and GLEW are considered "par for the course" with OGL. I think you would be hard pressed to find anyone who doesn't use them.   Good luck with the engine project! There's a lot to learn. For this path, you have to appreciate the path itself. It will take you so long to travel down it, the only way to make it is to enjoy every step of the journey rather than making it a race to the finish. It is the slow and difficult path less traveled.
  14. 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) ...
  15. 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...")         #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" %         for Animation in             ModelsArmature.animation_data.action = Animation             f.write("Action:%s\n" %                          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 =                 bpy.context.scene.frame_set(KeyFrame)                                 f.write("Frame:%d\n" % KeyFrame)                 for Bone in ModelsArmature.pose.bones:                     f.write("Bone:%s\n" %                     if (Bone.parent == None):                         f.write("Parent:None\n")                     else:                         f.write("Parent:%s\n" %                     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')