-
|
|
combine multiple models at load time and rotate, translate as one
|
Hi,
I would like to ask a new question, I still couldn't find a solution to my other problem "building a kitchen cabinet" after 2 days.
So here is the new question, how can we combine multiple pieces of objects (models) to act as a single model for translation (rotate, position) purposes?
that's it , if I can find the answer to this question, I will have my problem solved? Appreciate all help, thank you.
PS : I'm using ModelViewerControl from Shawn's sample. Combining must be done at load time or run time, not in 3ds max cause model accesories will be decided by the app's user.
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Each piece will be a different piece if they are not modeled as one solid object. Your task will be to create a world matrix for each piece to place it correctly.
If you have, for example, two pieces. Cabinet and Door, and they must be separate models, then how you put them together will, again, depend on how each was modeled.
To illustrate,
A: If Cabinet is modeled so that its center point is at 000 and then the door is modeled relative to the cabinet, it might be offset in the Z dimesion by half the width of the cabinet when you model it. Thus, when exported, its offset relative to the cabinet center is built into the models vertices.
B: Or, the door might not be modeled relative to the cabinet and may be itself centered on 000. Thus, when drawn, it would be halfway inside the cabinet.
If option A: is how the parts are modeled then you will have to mesh.Draw() each part using the same general World matrix so that each is rotated and translated appropriately. You need not worry about offsetting each part.
If option B: is how the parts are modeled, then you will still have to mesh.Draw() each part,but you will have to compensate for the offset of each part relative to where it should be given a fixed point on the cabinet. So, each part such as "Door" would have to have its own matrix (e.g., matrix.Identity) with its offset to the cabinet's local origin in that matrix's translation row (row 4). Then, you'd multiply that matrix by the cabinet's world matrix to place the door on the cabinet.
Or, if you understand the file formats (much better than I) you might be able to write a custom importer to take each part and combine them during load.
Best,
Byron
..shaders make you feel... powerful, or very very stupid. http://drjbn.spaces.live.com/
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Thank you Byron, I admire your understanding of the problem and you described it beautifully proving full grasp of the situation. As you described if I load door and case as in
SceneViewerControl.m_model[i] = content.Load<Model>("case") // note the array
SceneViewerControl.m_model[i] = content.Load<Model>("door")
the door IS halfway inside the cabinet, but I understand that somehow, the part I dont understand is and where the real problem occurs is when whole cabinet is rotated. Then parts go all in wrong directions and dont rotate as one, that's why I want to make the cabinet into a single entity, what can I do to correct this?
1- make a class clsCabinet and manually position items (case, door, handle) relative to cabinet itself then transform cabinet to its final place in the scene
but how should I construct such a class? use arrays , Lists for each model (cabinet door handle) to be combined into one cabinet
and most importantly, which method to use to load the class into SceneViewerControl
you see above I use the shown method for content.Load, m_model[] is a Model property I declared inside SceneViewerControl
I guess this is an unusual problem for xna framework or I'm not smart enough to comprehend it fast enough, either way I thank you very much.
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Everything Byron has said is spot on. I would just like to add an option C to his list: Have the cabinet sport a bone structure so you could simply attach the door to the end of the bone. A simple system of one or two bones should be sufficient. The bone could act as the hinge for rotation (swing open/close door) or for pulling open/push close a drawer. Because the bone system will be a part of the cabinet assembly, attaching the door to the bone automatically orients itself properly. The animation could be done ahead of time in max or you could take control of the bone matrices manually. As Byron said, it is important to know how the extra doors will be modeled, and in this case you'll need to ensure that the bone attachment point is set properly. Essentially, this is no different from attaching a weapon to a characters hand, so you should be able to find more info here or on the web about this technique.
-Ron XNA Neophyte
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
N E D Gold:Essentially, this is no different from attaching a weapon to a characters hand, so you should be able to find more info here or on the web about this technique.
yes I think so too, another example could be the accessories on a car model, thanks I'm looking further into this bone technique now.
UPDATE : I guess this whole thing is out of my league for putting together as a combined model, bones are not the answer I think to what I want to do, in fact I dont know what will do what I want. Which is a kitchen cabinet with 5-6 changeable parameters as seen fit by the app's user (door, handle models, moldings, countertop) or lack of those on the cabinet. I can build all of this when cabinet is at 0 rotation, as soon as you rotate it some, all pieces are gone down the drain (lose position). The best I can come up with is to ask for paid help, some kind of demo built for me. I will post in help wanted section. Thanks to people who tried to help.
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
hang in there! Post your draw code (use the "format code block" button, fourth from the right). I'm sure that this can get worked out here, If you can place everything so you've got a decent cabinet till it gets rotated, its probably something simple like a transform out of order or something like that.
Best,
Byron
..shaders make you feel... powerful, or very very stupid. http://drjbn.spaces.live.com/
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Byron, thank you, my draw code is below, a helpful picture is at http://www.kitchendesigned.com/images/cabinets.jpg
private void DrawModel()
{
GraphicsDevice.Clear(Color.CornflowerBlue);
for (int i = 0; i < modelCount; i++)
{
if (modelCount == 0) return;//no model to draw
if (m_model[i] == null) continue;
Matrix world = Matrix.CreateScale(m_scale[i].X ,m_scale[i].Y ,m_scale[i].Z ) *
Matrix.CreateFromYawPitchRoll(MathHelper.ToRadians(m_rotations[i].X),
MathHelper.ToRadians(m_rotations[i].Y),
MathHelper.ToRadians(m_rotations[i].Z)) *
Matrix.CreateTranslation(m_position[i].X, m_position[i].Y, m_position[i].Z);
// Look up the absolute bone transforms for this model.
boneTransforms = new Matrix[m_model[i].Bones.Count];
m_model[i].CopyAbsoluteBoneTransformsTo(boneTransforms);
// Draw the model.
foreach (ModelMesh mesh in m_model[i].Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = boneTransforms[mesh.ParentBone.Index] * world;
effect.View = view;
effect.Projection = projection;
effect.EnableDefaultLighting();
effect.SpecularPower = 16;
effect.Texture = m_texture[i];
effect.TextureEnabled = true;
}
mesh.Draw();
}
}
}
my content load code is below , loaded in a for loop foreach part of the cabinet
SceneViewerControl.m_model[i] = content.Load<Model>(Application.StartupPath + "\\v2\\xna\\Models\\" + nod.OC_fKatalogMain + "\\" + nod.OC_fKatalogSub + "\\" + nod.OC_PicName);
SceneViewerControl.m_MalzName[i] = nod.OC_MalzemeName;
//scale
SceneViewerControl.m_scale[i].X = nod.Width * 0.01f;//since model is 100 cm in 3dmax, we divide by 0.01, so we use mm for measurement
SceneViewerControl.m_scale[i].Y = nod.OC_M_Yuks * 0.01f;
SceneViewerControl.m_scale[i].Z = nod.Height * 0.01f;
//rotate
SceneViewerControl.m_rotations[i].X = -nod.GlobalRotation;
SceneViewerControl.m_rotations[i].Y = nod.OC_rotx;//one arkaya, pitch
SceneViewerControl.m_rotations[i].Z = nod.OC_rotz;//yana , yaw
//translate
SceneViewerControl.m_position[i].X = (int)nod.GlobalBounds.Left + (nod.Width / 2);
SceneViewerControl.m_position[i].Y = (int)nod.OC_M_YerdenAlt;
SceneViewerControl.m_position[i].Z = (int)nod.GlobalBounds.Top + (nod.Height / 2);
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Please take a look at the Simple Animation tutorial because it closely resembles what you're after. The tutorial shows how to grab the bones of a tank model and individually rotate wheels, turret, etc. Use this example as a basis for your project.
-Ron XNA Neophyte
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
N E D Gold:Please take a look at the Simple Animation tutorial because it closely resembles what you're after. The tutorial shows how to grab the bones of a tank model and individually rotate wheels, turret, etc. Use this example as a basis for your project.
I am not after animating anything on the cabinet, it's more like I'm after adding parts to cabinet (door, handle, etc), if there is a door or handle. Then again the cabinet may not have a door or handle, this is determined at load time, so I can't have a full featured cabinet modelled and simply animate it, cause it's not what I want. I need to be able to add pieces to another "main model" which is cabinet case. Thanks for your suggestion though, I will check it out. I am totally lost.
UPDATE :
I found this somewhere on this forum, could this be what I'm looking for, thank you
A technique often used to attach equipment to a player model is bind points. The simple explanation is that you just include additional bones in your model, then at run-time attach equipment models to these bones. For example, you can include an extra bone in the player model's hand and render the player's sword at that bone's position/orientation each frame. The net effect is the sword is "attached" to the player's hand. Doing it this way, there is no need to modify any models at run-time, you just extract bone data for rendering.
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Well, try looking at this differently. Suppose you did have a fully featured cabinet model. To draw the cabinet normally, you would loop through each mesh, set up the World/View/Proj and then mesh.Draw(). Well... I suggest that you intervene slightly by looking at the mesh piece that is about to be drawn, and substitute it with something else (or not). This way, as you loop through the pieces, and you can decided to draw or not draw a door or handle or whatever AND you can swap in a completely different mesh to draw which means multiple doors, etc. I wanted you to look at the Simple Animation tutorial because I feel it best illustrates how to access these components.
-Ron XNA Neophyte
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
mysdn:
UPDATE :
I found this somewhere on this forum, could this be what I'm looking for, thank you
A technique often used to attach equipment to a player model is bind points. The simple explanation is that you just include additional bones in your model, then at run-time attach equipment models to these bones. For example, you can include an extra bone in the player model's hand and render the player's sword at that bone's position/orientation each frame. The net effect is the sword is "attached" to the player's hand. Doing it this way, there is no need to modify any models at run-time, you just extract bone data for rendering.
Yes, that is what I was talking about for Option C.
What you may not know is that you do not have to create a skeleton for your model. When you iload your model in XNA, a bone is created for each mesh part. If you open up the tank in Max, you will see that there are no 'bones' in the model, but there are a bunch of objects that will turn into bones when the model is loaded.
-Ron XNA Neophyte
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
N E D Gold:Yes, that is what I was talking about for Option C.
thanks, would you work with me for a couple days till we got this thing working, I'm willing to pay you $100, half up front, half upon completion.
If you think this can be done with bones, please reply.
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Sure, no problem. I'm a tad busy tonight, but I can give this my full attention tomorrow. How may I contact you?
-Ron XNA Neophyte
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
N E D Gold:Sure, no problem.
thank you,
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
mysdn:Matrix world = Matrix.CreateScale(m_scale[i].X ,m_scale[i].Y ,m_scale[i].Z ) *
Matrix.CreateFromYawPitchRoll(MathHelper.ToRadians(m_rotations[i].X),
MathHelper.ToRadians(m_rotations[i].Y),
MathHelper.ToRadians(m_rotations[i].Z)) *
Matrix.CreateTranslation(m_position[i].X, m_position[i].Y, m_position[i].Z);
You want to parts to rotate with the rest of the cabinet. So, you want to
1: Scale it up to the size you want
2: Place the part relative to the others
3: Then rotate it all, after all the parts are placed in relation to each other.
So, you'll have
World = Matrix.Scale * Matrix.Translation * Matrix.CreatefromYawPitchRoll. The order of multiplication matters here.
What you are doing is rotating the piece on its local coordinates, then moving it out into the world. You need to place it in the world of your box first, then rotate it, that way its rotating in the box's world, so to speak.
Best,
Byron
..shaders make you feel... powerful, or very very stupid. http://drjbn.spaces.live.com/
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Byron Nelson:What you are doing is rotating the piece on its local coordinates, then moving it out into the world. You need to place it in the world of your box first, then rotate it, that way its rotating in the box's world, so to speak.
this is it, I changed world matrix according to your suggestion and the case (made of 4 boxes, one for each side) and the door (5 pieces) now stays in one piece as a cabinet after rotation, but the cabinet still loses position, pushed back or to the right.
thank you, I think this is the root of the problem, no need to make bones or anything like that. This is a simple case of me totally understanding translation matrices in xna. Without this knowledge I can't go far. Appreciate your help. But why is it pushed back? I guess this is a classic orbit problem, as rotation angle increases, so does position loss. Something's still wrong with the multiplication of matrices.
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
If all is working together except the floor, then its hard to say where the error is.
// Look up the absolute bone transforms for this model.
boneTransforms = new Matrix[m_model[i].Bones.Count];
m_model[i].CopyAbsoluteBoneTransformsTo(boneTransforms);
// Draw the model.
foreach (ModelMesh mesh in m_model[i].Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = boneTransforms[mesh.ParentBone.Index] * world;
effect.View = view;
effect.Projection = projection;
effect.EnableDefaultLighting();
effect.SpecularPower = 16;
effect.Texture = m_texture[i];
effect.TextureEnabled = true;
}
It could be something in the floor model. Imagine I create a model of a line. I want a line of 1 unit long, centered at 000. One end might be at
-.5,0,0 and the other at .5,0,0. I save my file and export it to an X or FBX file. These files will contain lots of thing, and one thing it will contain is a "bone" for my line. All that is, is a transformation matrix that is to be applied to the vertices in the model when rendered.
That's what is going on here effect.World = boneTransforms[mesh.ParentBone.Index] * world;
In the case of my line, which is modelled at 000, the bone is an identity matrix, multiplying by the bone transform doesn't change anything.
Now, imagine when I model my line I want it to be 1 unit wide, and 1 unit down on Y. I model a line (-.5,0,0 and .5,0,0) then I move it down on my screen 1 unit. It looks like a line at -.5,-1,0 to .5,-1,0, but internally it is still a line at -.5,0,0 to .5,0,0 with a transform matrix that moves it down 1 unit. If I export the file now, thats what I will get. The two vertices (-.5,0,0 and .5,0,0) and the "bone" transform matrix that moves it down 1 unit.
But, if in my modelling app I "freeze transforms" then internally my line is represented as -.5,-1,0 and .5,-1,0 and the bone transform is an identity matrix. If you export that file, thats what you'll get.
It could be that your floor was exported using the first scenario where it has a non-identity transform matrix. So, when you get your coordinates worked out to place your floor, then you multiply by its bone, it gets moved again. YOu might set a flag and when you draw the floor box, don't use the boneTransform multiplication & see if it behaves properly. If thats the case, then you can fix your floor in your modeling app.
Just a guess,
Best
Byron
..shaders make you feel... powerful, or very very stupid. http://drjbn.spaces.live.com/
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Byron Nelson:If all is working together except the floor, then its hard to say where the error is.
Thanks Byron,
I dont think there is an error with the floor. The cabinet case, wall and floor are rendered from cube.xnb which was exported from Max centered at 0,0,0.
//wall translate
SceneViewerControl.m_position[i].X = frmWall.iWall2Length / 2;
SceneViewerControl.m_position[i].Y = 0;
SceneViewerControl.m_position[i].Z = -7.5f;
//floor translate
SceneViewerControl.m_position[i].X = FloorWidth / 2;
SceneViewerControl.m_position[i].Y = 0;
SceneViewerControl.m_position[i].Z = FloorDepth / 2;
new Draw method
for (int i = 0; i < modelCount; i++)
{
if (modelCount == 0) return;//no model to draw
if (m_model[i] == null) continue;
Matrix world = Matrix.CreateScale(m_scale[i].X ,m_scale[i].Y ,m_scale[i].Z ) *
Matrix.CreateTranslation(m_position[i].X, m_position[i].Y, m_position[i].Z)*
Matrix.CreateFromYawPitchRoll(MathHelper.ToRadians(m_rotations[i].X),
MathHelper.ToRadians(m_rotations[i].Y),
MathHelper.ToRadians(m_rotations[i].Z));
// Look up the absolute bone transforms for this model.
boneTransforms = new Matrix[m_model[i].Bones.Count];
m_model[i].CopyAbsoluteBoneTransformsTo(boneTransforms);
// Draw the model.
foreach (ModelMesh mesh in m_model[i].Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = boneTransforms[mesh.ParentBone.Index] * world;
effect.View = view;
effect.Projection = projection;
effect.EnableDefaultLighting();
effect.SpecularPower = 16;
effect.Texture = m_texture[i];
effect.TextureEnabled = true;
}
mesh.Draw();
I remind you when I place 2 cabinets only the rotated one is off place, the non rotated cabinet is exactly where it is supposed to be. How can that be that only the rotated meshes go off position?, thanks again, I'm honestly struggling with this for 3 days now.
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
mysdn: //wall translate
SceneViewerControl.m_position[i].X = frmWall.iWall2Length / 2;
SceneViewerControl.m_position[i].Y = 0;
SceneViewerControl.m_position[i].Z = -7.5f;
//floor translate
SceneViewerControl.m_position[i].X = FloorWidth / 2;
SceneViewerControl.m_position[i].Y = 0;
SceneViewerControl.m_position[i].Z = FloorDepth / 2;
Can you put your entire project up somewhere that I can grab it to take a looK? Its puzzling that it works for the walls,door & top,but gets the floor out of line (or is the floor being put where you want it and the rest is "together" but not being placed exactly where it should be?)
Best,
Byron
..shaders make you feel... powerful, or very very stupid. http://drjbn.spaces.live.com/
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
I just sent you an email offering to lend a hand. No payment necessary. Looks like you might be close already, but I'm here if you need me.
-Ron XNA Neophyte
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Special thanks go to Ned and Byron for their helping hand in this
I have just uploaded the sample project I designed for this problem, it uses exact same methods and algoritms for all calculations.
http://www.kitchendesigned.com/download/RotationSample.zip
oh my God, what else to say I dont know, such a simple problem , so much inadequate knowledge on my part, the rest is up to your recommendations, thanks a lot
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Okay. Like Byron said, Translation x Rotation is not the same as Rotation x Translation. Think of the planet rotating about its axis, but also rotates around the sun. The proper order is to rotate around the planet axis, translate into position, and then rotate around its orbit.
Your code will orbit the cabinet as it stands. Unfortunately, because of the way you construct your geometry, you can't just swap the order of operations and expect it to work. Your scene depends on moving the parts into proper position first, so if you were to try to rotate first, the pieces get all messed up.
The answer is to construct your cabinet around 0,0,0 NOT its final position. Once this is done, you can do your rotation and then translation.
Best,
-Ron
-Ron XNA Neophyte
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
Ron,
it's funny that you tell me to construct my cabinet at 0,0,0 then rotate and translate to final position, you tell me this after some 40 or so posts, this is what I used to do for a year and a half for my cabinets in Povray, create them at 0,0,0 then rotate-position. I guess Byron will come to the same conclusion as you did. My only question now would be this,
What do you mean by create cabinet at 0,0,0 ? How would you construct the cabinet? thank you.
ibrahim
Turkey
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
I just finished re-writing your cabinet construction technique and emailed you the new file. Basically, what I did was take your cabinet width and created the left and right panels at (-width/2,0,0) and (+width/2,0,0). Repeat similar for top/bottom and front/back. Also had to add an additional translation to move the assembly into its final world position.
-Ron XNA Neophyte
|
|
-
|
|
Re: combine multiple models at load time and rotate, translate as one
|
You don't need to merge your models per se, you could just position each part model relative to each other according to some structure recipe (perhaps xml), and then after they've been placed in their relative locations at the origin, move them again by the structure's position.
Code wise:
Create two transformation matrices, one to transform the components of
the structure to their relative position and rotations, and one to
place and rotate the structure Globally.
The transformation for any piece of the structure is then Relative * Global
Each transform would be made of a rotation matrix * translation matrix.
For the sake of draw complexity, you'd probably want to use instancing for each piece, that way if there are 18 doors you can draw them with a single draw. This way the number of draw calls in a scene would not be a function of how many structures you have, on screen, but how many unique pieces you have on screen. This could in many cases make drawing a scene faster, especially if you have a large number of different structures that are made of some number of the same 10 pieces, in different arrangements.
I don't think merging them into one model really gives you as much advantage.
|
|
|