 How to stretch a 3D model between two coordinates with python

It should be possible to get the shear factor directly without knowing the angle, by dividing the horizontal difference in position (i.e. X2 - X1) by the vertical difference (Z2 - Z1). It might also be the other way around and I’m not sure of the sign (subtraction order) either; you’ll have to try around a bit.

1 Like

I can only add that to implement your idea, you need to use procedural model generation. Which guarantees the correct geometry and texture mapping. It’s more complicated than it seems, you can’t just distort the model in the right direction.

1 Like

And how can I implement that, Actually I am a newbie with panda3d, is there any tutorial that can help me?
And which way do you think it’s complicated, you mean procedural model generation Or the way that guys said?

I may be mistaken, but I think that you can likely get at the least pretty close to the desired effect with shearing.

As to procedural generation, that is a rather more powerful tool–and perhaps a less fiddly one than shearing.

The manual discusses it here:
https://docs.panda3d.org/1.10/python/programming/internal-structures/procedural-generation/index

I’m not sure offhand of whether there are any tutorials on the subject, but perhaps a search of the forum will turn one up.

2 Likes

I think on the contrary, you need to take care of many things, for example, calculating the lighting normals and so on.

@Pythonic_Person

Check out this library. Based on it, you can learn about how procedural generation works.

2 Likes

Okay, that is a fair point.

Try shearing first. If that doesn’t give desirable results, you could indeed resort to procedural model generation. If you need help with that, you might want to check out my procedural geometry samples. Especially basic.py could be helpful.

2 Likes

Any ideas how to keep the original Z coordination without going the model down?

Well, the offset should be calculable (I think that it comes down to some trigonometry), so you could perform that calculation and then offset the object appropriately.

Judging from the screenshot in your first post, the origin of your fence model is at the bottom center, so the vertical offset needed to keep the sheared model from sinking into the ground should be equal to the shear factor multiplied by half the width of the fence model:

offset = abs(shear_factor) * fence_width * .5
fence_model.set_z(fence_model.get_z() + offset)
2 Likes

You don’t need the angle; all you need is the shear factor, which should be this (if fence_segment.get_length() gives the horizontal size of the fence segment):

shear_factor = height / fence_segment.get_length()  # ex: height = 5, segment = 40

So the code might work better like this:

if fence_segment != fence_segment: # has different Z
height = fence_segment - fence_segment
shear_factor = height / fence_segment.get_length()  # ex: height = 5, segment = 40
shear_mat = Mat4.shear_mat(0., shear_factor, 0.)
shear_mat.transpose_in_place()
fence_segment_model.set_mat(shear_mat * fence_segment_model.get_mat())
offset = abs(shear_factor) * fence_segment.get_length() * .3
fence_segment_model.set_z(fence_segment_model.get_z() + offset)
1 Like

Could it have something to do with the yaw rotation applied to the fences? Or perhaps something to do with the order of transformations applied to the fence segments.

I am sorry I was not able to reply sooner, but another way to approach the original problem is to consider that a 3x3 transformation matrix is composed of three X, Y, Z basis vectors, and the xyz coordinates of the model are essentially weightings of these vectors to produce the transformed position. If the fence model goes along the X axis, this means you could compose a matrix like so:

\begin{bmatrix} 1 & 0 & \frac{z}{w} \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end{bmatrix}

…where z is the amount by which to lift the fence (relative to the origin of the fence) and w is the width of the fence. This means that the local z coordinate of a given position in the model will be increased by this factor of the local x coordinate.

Here is a small sample.

from panda3d.core import *
from direct.directbase import TestStart

base.cam.set_y(-150)

# Fence segments are 30 units wide in x, 15 units high in z
cm = CardMaker('card')
cm.set_frame(0, 30, 0, 15)

card1 = render.attach_new_node(cm.generate())
card1.set_color((1, 0, 0, 1))
card1.set_mat(Mat4(Mat3(
# Next fence has z of 10, so the right edge of this is skewed up by 10
1, 0, 10 / 30,
0, 1, 0,
0, 0, 1,
), card1.get_pos()))

card2 = render.attach_new_node(cm.generate())
card2.set_color((0, 1, 0, 1))
card2.set_pos(30, 0, 10)
card2.set_mat(Mat4(Mat3(
# Next fence has z of -10, this one has z of 10,
# so the right edge of this one is skewed down by 20
1, 0, -20 / 30,
0, 1, 0,
0, 0, 1,
), card2.get_pos()))

card3 = render.attach_new_node(cm.generate())
card3.set_color((0, 0, 1, 1))
card3.set_pos(60, 0, -10)

base.run()
1 Like

Thank you so much, But what if the fence goes along not just the X-axis what about Y or even XY?

Well, how are your fence models oriented? Do you have multiple different models, each with its own orientation “baked in”, or do they all have the same initial orientation when they’re loaded and are they then rotated afterwards (using e.g. a call to set_hpr)? The latter would be easier to deal with.

Also, is the order in which the two points of a fence segment are given consistent? What I mean is, when looking straight at the model, is one point (e.g. fence_segment) always to the left and the other point to the right of the fence? If not, it might be hard to tell whether the shear factor for a particular fence needs to be negative or positive.

2 Likes

Ideally you have one fence segment, which goes into the X direction, and then you rotate it after applying the shear.

Otherwise, you’re making things unnecessarily difficult for yourself. The easiest then would be to apply the inverse of the rotation to align it in the X direction, then apply the shear matrix, then re-apply the original rotation matrix. The transforms can be combined using matrix multiplication.

2 Likes

I tried to make a shear before we rotating the model but am a fraid still didn’t work.
Maybe I did something not correctly.

If I’m not much mistaken, if you model your fence such that one end of it is at the origin (i.e. the zero-position), and then you always place your fence such that its origin is at point A, then you will always know which end is associated with point A and which with point B. This should allow you to calculate a shear-factor based on those two points without worrying about which way around the fence is.

What behaviour are you seeing when you try that approach?

What rdb described should, I think, work, so my inclination is to suspect that there is another problem that’s preventing it from doing so.

1 Like

Looking at that image, it seems that the shear factor is correct when the x-coordinate of point 1 is smaller than that of point 2, so it may be worth trying to negate the shear factor (multiply by -1) in the other case.

However, I do wonder if the heading angle is always correctly calculated. How do you calculate the “2D slope”?

1 Like

Same results as before.
Thank you Thaumaturge for your help I really appreciate it.
I solved by adding a condition if x_point_1 is smaller than other points will make the shear factor a positive otherwise negative.
as Epihaius said.

1 Like