Drawing Instanced Pipes Problem With Matrix Generation and Angles

Started by
8 comments, last by jollyjeffers 16 years, 4 months ago
Dear All, I've been tasked with drawing lots of pipes. Exciting I know. Basicly I'm being passed an array of points, and I draw the pipes based of those, treating each point as an end point of a pipe. I'm using an instance of a cylinder to do this, and my problem is in creating the matrix for each instance. Basicly I cant figure out why my rotation angles are not turning out right. I basicly need an XY and a YZ rotation angle, which I generate in the following manner : TempPipePoint.x = (Temp1->x + Temp2->x)/2; TempPipePoint.y = (Temp1->y + Temp2->y)/2; TempPipePoint.z = (Temp1->z + Temp2->z)/2; float fDx = Temp2->x - Temp1->x; float fDy = Temp2->y - Temp1->y; float fDz = Temp2->z - Temp1->z; TempPipePoint.anglexy = atan2( (fDy), (fDx) ) ; TempPipePoint.angleyz = -atan2( (fDz), (fDy) ) ; TempPipePoint.length = sqrt( (fDx) * (fDx) + (fDy) * (fDy) + (fDz) * (fDz) ); TempPipePoint.isnop = false; m_PipeList.push_back(TempPipePoint); m_iNumberOfPipes++; .... Then I create the matrix as below : RotX[0] = cos(xyrot ); RotX[1] = -sin(xyrot ); RotX[4] = sin(xyrot); RotX[5] = cos(xyrot ); RotY[5] = cos(yzrot ); RotY[6] = -sin(yzrot ); RotY[9] = sin(yzrot ); RotY[10] = cos(yzrot ); TempRotationMatrix *= RotX; TempRotationMatrix *= RotY; // Seperated For debugging, instead of building the entire matrix at once. This works as I would expect if the pipe points are all on the XY plane, but if I vary the Z cordinates the thing goes haywire, with the exception that it works ok for a pipe that has an end point on the origin, which is a bit odd. Does anyone have an idea what I'm doing wrong? Regards, Jesse
Advertisement
I didn't try to debug your code, but I do have a couple of suggestions.

First of all, is there any particular reason you're not using a math library, or at least a proper vector class? Writing out all those expressions by hand for each dimension (e.g. x = ..., y = ..., z = ...) is tedious and error-prone, and makes the code harder for others to read as well.

Now, personally I wouldn't bother with rotation matrices; rather, I'd just build the matrix directly from the cylinder direction vector. Pseudocode:
vector3 direction = normalize(point2 - point1);// This returns the index (0, 1, or 2) of the element with the least// absolute value:int i = index_of_least_magnitude(direction[0], direction[1], direction[2]);vector3 ref(0,0,0);ref = 1;vector3 side = normalize(cross(ref, direction));vector3 up = cross(direction, side);matrix44 m = matrix_from_basis_vectors(side, up, direction);m.set_translation(lerp(point1, point2, .5));
There are other ways you could do it as well (including the rotation method).

Of course if you want rounded connectors between each pipe segment, then things get a little more complicated :)
Ah hmm that does seem like a more elegant way. I'll give that a shot.
Yes the rounded connectors are going to be a problem. I have a solution for drawing the connectors, but I need to figure out an offset to translate by so the pipes are only touching at one point. You seem to have considered the problem before, did yo uhave a solution for that too ;-)?
Jesse
Quote:Original post by laeuchli
...did yo uhave a solution for that too ;-)?
Of course ;)

I don't have time to go into too much detail, but here's what I'd do.

First, I'd make the connectors a little thicker than the pipe sections so that I didn't have to worry about continuity (if continuity were required, I'd switch to a parallel transport frame, which is a different topic). Now, OpenGL doesn't have any built-in functionality for creating curved cylindrical sections, so you'd have to do that yourself. You'd probably also want to cap the ends so that there wouldn't be gaps between the pipe sections and the connectors.

The pipe sections you'd be able to position the same way you're doing now (or will be, once you get it working). However, you'll have to shorten the pipes at each end by some amount to make room for the connectors. We'll assume that no individual section is so short that its two connectors will run into each other.

That leaves positioning and orienting the connectors. The position of the connector is of course simply the endpoint shared by the two pipe sections it connects. The basis for the connector can be built easily from the endpoint and the two adjacent endpoints, e.g.:
vector3 x = normalize(next_endpoint - endpoint);vector3 y = normalize(prev_endpoint - endpoint);vector3 z = cross(x, y);
This assumes that your connector model is built in local space with one end pointing down the x axis, and the other pointing down the y axis.
Ahhh, the method you gave me for rotating the pipes worked. I'm afraid I'm going to have to burn you for a witch. Thanks again :). This method makes alot of sense and is alot more robust then what I was trying to do.

Since I may indeed have to offer continuity, I've been googling a bit on parallel transports, but other then a fairly sparse wikipedia page, and some prosey discriptions, I didnt find much. Should I go ahead and check out one of those differential geometery books listed on wikipedia or are you aware of any good online resources on this subject?

Once again thanks alot for your help.
Quote:Original post by laeuchli
Ahhh, the method you gave me for rotating the pipes worked. I'm afraid I'm going to have to burn you for a witch. Thanks again :). This method makes alot of sense and is alot more robust then what I was trying to do.

Since I may indeed have to offer continuity, I've been googling a bit on parallel transports, but other then a fairly sparse wikipedia page, and some prosey discriptions, I didnt find much. Should I go ahead and check out one of those differential geometery books listed on wikipedia or are you aware of any good online resources on this subject?

Once again thanks alot for your help.
I know of one good reference on the topic - an online .pdf on sweeping out shapes along curves that includes a discussion of the parallel transport frame - but I don't know if it's still available or where to find it.

I've discussed the PTF a few times on these forums - you might try searching the archives for 'jyk parallel transport frame', as I think I've explained it in detail on a couple of occasions.

It's not terribly complicated, but you will need a math library of some sort - doing it manually (an 'element at a time', so to speak) or via OpenGL function calls will be difficult at best.

If your pipes are required to be continuous, then the PTF is really your best bet. Any other approach will require hacks and workarounds to avoid rendering artifacts.

Is this a homework assignment? If so, that's probably all the help I should give :) If not though, I could probably direct you to some source code or additional references...
I wish it were a hw assignmnet so I could just decide to drop the class :P. This is unfortunatly for work.
Quote:Original post by laeuchli
I wish it were a hw assignmnet so I could just decide to drop the class :P. This is unfortunatly for work.
Oh :) Well, in that case, just let me know if you need more help. I've done this 'swept solid' thing a number of times (for various purposes), so I can certainly help if needed. (I'm not sure how much detail I'll be able to go into, just due to lack of time, but I'll do my best :)
I think you've given me lots to go on. Thanks for your help.
<Shamess-Plug>

'3D Pipes In Direct3D 10' is part of the way through solving this problem, including an instanced based renderer. As shown in some of my recent journal entries it's a bit buggered for normals, but I'm hoping to solve that next week when I have some free time...

fwiw: InstancedForwardRenderer::UpdateIndividualPipe():
    HRESULT InstancedForwardRenderer::UpdateIndividualPipe( PipeResources* p, const Utils::CoordinateList& coords, const UINT16& xSize, const UINT16& ySize, const UINT16& zSize, const UINT32& CurrLen )    {        // All we need to do here is update the per-instance        // information according to the latest pipe layout        // Scan each pipe in the simulation        HRESULT hr = S_OK;        D3DXVECTOR3 mid_point;        D3DXMATRIX mTemp;        D3DXMATRIX mFrom;        D3DXMATRIX mTo;        // 1] Get access to the per-instance data        // --------------------------------------        InstancedData *pInstance = NULL;        hr = p->m_pPerInstanceData->Map( D3D10_MAP_WRITE_DISCARD, 0, reinterpret_cast< void** >( &pInstance ) );        if( FAILED( hr ) )        {            ERR_OUT( L"Unable to Map() a per-instance data buffer." );            return hr;        }        // 2] For each segment that we have in the current pipe        // ----------------------------------------------------        for( UINT32 segment = 0; segment < CurrLen; ++segment )        {            // 3] Calculate the mid point for this segment            // -------------------------------------------            mid_point.x = ((2.0f * static_cast< float >( coords.at(segment).x ) + 1.0f) / (2.0f * static_cast< float >( xSize ))) - 0.5f;            mid_point.y = ((2.0f * static_cast< float >( coords.at(segment).y ) + 1.0f) / (2.0f * static_cast< float >( ySize ))) - 0.5f;            mid_point.z = ((2.0f * static_cast< float >( coords.at(segment).z ) + 1.0f) / (2.0f * static_cast< float >( zSize ))) - 0.5f;            // 4] Generate FROM blend matrix            // -----------------------------                // a] Compute incoming vector                Utils::IntegerVector InVec;                if( segment < 1 ) // Can't use (segment-1)<0 due to segment being UINT                {                    // special case: incoming vector at the first segment should just                    // be a copy of the outgoing vector as there are no previous segments                    InVec.x = coords.at( segment + 1 ).x - coords.at( segment ).x;                    InVec.y = coords.at( segment + 1 ).y - coords.at( segment ).y;                    InVec.z = coords.at( segment + 1 ).z - coords.at( segment ).z;                }                else                {                    // expected case: do a simple (to - from) vector calculation                    InVec.x = coords.at( segment ).x - coords.at( segment - 1 ).x;                    InVec.y = coords.at( segment ).y - coords.at( segment - 1 ).y;                    InVec.z = coords.at( segment ).z - coords.at( segment - 1 ).z;                }                // b] Compute rotation matrix                D3DXMATRIX mIn = GetRotationMatrixForVector( InVec );                // c] Composite the translation elements                D3DXMatrixIdentity( &mFrom );                                D3DXMatrixTranslation( &mTemp, mid_point.x, mid_point.y, mid_point.z );                D3DXMatrixMultiply( &mFrom, &mIn, &mTemp );            // 5] Generate TO blend matrix            // ---------------------------                // a] Compute outgoing vector                Utils::IntegerVector OutVec;                if( (segment + 1) >= coords.size() )                {                    // special case: outgoing vector at the last segment has to be the                    // same as the incoming vector as there are no further segments                    OutVec.x = coords.at( segment ).x - coords.at( segment - 1 ).x;                    OutVec.y = coords.at( segment ).y - coords.at( segment - 1 ).y;                    OutVec.z = coords.at( segment ).z - coords.at( segment - 1 ).z;                }                else                {                    // expected case: do a simple (to - from) vector calculation                    OutVec.x = coords.at( segment + 1 ).x - coords.at( segment ).x;                    OutVec.y = coords.at( segment + 1 ).y - coords.at( segment ).y;                    OutVec.z = coords.at( segment + 1 ).z - coords.at( segment ).z;                }                // b] Compute rotation matrix                D3DXMATRIX mOut = GetRotationMatrixForVector( OutVec );                // c] Composite the translation elements                D3DXMatrixIdentity( &mTo );                                D3DXMatrixTranslation( &mTemp, mid_point.x, mid_point.y, mid_point.z );                D3DXMatrixMultiply( &mTo, &mOut, &mTemp );            // 6] Write the actual data            // ------------------------            pInstance[segment].segment_weight = static_cast< float >( segment ) / static_cast< float >( CurrLen );            pInstance[segment].blend_from = mFrom;            pInstance[segment].blend_to = mTo;        }        // 7] Ensure we release the lock on the data        // -----------------------------------------        p->m_pPerInstanceData->Unmap();        return hr;    }    D3DXMATRIX InstancedForwardRenderer::GetRotationMatrixForVector( const Utils::IntegerVector& vec )    {        D3DXMATRIX mRet;        D3DXMatrixIdentity( &mRet );        // Handle vectors going in/out along the X axis        if( (vec.x != 0) && (vec.y == 0) && (vec.z == 0) )        {            if( vec.x < 0 )            {                D3DXMatrixRotationZ( &mRet, D3DX_PI / 2.0f );            }            else            {                D3DXMatrixRotationZ( &mRet, -D3DX_PI / 2.0f );            }        }        // Handle vectors going in/out along the Y axis        if( (vec.x == 0) && (vec.y != 0) && (vec.z == 0) )        {            if( vec.y < 0 )            {                D3DXMatrixScaling( &mRet, 1.0f, -1.0f, 1.0f );            }            else            {                /* NOP */            }        }        // Handle vectors going in/out along the Z axis        if( (vec.x == 0) && (vec.y == 0) && (vec.z != 0) )        {            if( vec.z < 0 )            {                D3DXMatrixRotationX( &mRet, -D3DX_PI / 2.0f );            }            else            {                D3DXMatrixRotationX( &mRet, D3DX_PI / 2.0f );            }        }        return mRet;    }


</Shamess-Plug>

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

This topic is closed to new replies.

Advertisement