/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ ////////////////////////////////////////////////////////////////////////////// // // // plDrawableGenerator Class Functions // // // //// Version History ///////////////////////////////////////////////////////// // // // 5.15.2001 mcn - Created. // // // ////////////////////////////////////////////////////////////////////////////// #include "hsTypes.h" #include "plDrawableGenerator.h" #include "plDrawableSpans.h" #include "plGeometrySpan.h" #include "hsFastMath.h" #include "plRenderLevel.h" #include "hsResMgr.h" #include "pnKeyedObject/plUoid.h" // Making light white and dark black by default, because this is really // redundant. The handling of what color unlit and fully lit map to is // encapsulated in the material used to draw the mesh. The caller // wants illumination values, and can handle on screen contrast // through the material. mf hsColorRGBA plDrawableGenerator::fLiteColor = { 1, 1, 1, 1 }; hsColorRGBA plDrawableGenerator::fDarkColor = { 0.0, 0.0, 0.0, 1 }; //// SetFauxLightColors ////////////////////////////////////////////////////// // Set the colors for the foux lighting on generated drawables void plDrawableGenerator::SetFauxLightColors( hsColorRGBA &lite, hsColorRGBA &dark ) { fLiteColor = lite; fDarkColor = dark; } //// IQuickShadeVerts //////////////////////////////////////////////////////// // Quickly shades vertices based on a fake directional light. Good for doing // faux shadings on proxy objects. void plDrawableGenerator::IQuickShadeVerts( UInt32 count, hsVector3 *normals, hsColorRGBA *colors, hsColorRGBA* origColors, const hsColorRGBA* multColor ) { hsVector3 lightDir; float scale; lightDir.Set( 1, 1, 1 ); lightDir.Normalize(); while( count-- ) { scale = ( normals[ count ] * lightDir ); // pretend there are two opposing directional lights, but the // one pointing downish is a little stronger. const hsScalar kReverseLight = -0.8f; if( scale < 0 ) scale = kReverseLight * scale; colors[ count ] = fLiteColor * scale + fDarkColor * ( 1.f - scale ); if( origColors ) colors[ count ] *= origColors[ count ]; if( multColor ) colors[ count ] *= *multColor; } } void plDrawableGenerator::IFillSpan( UInt32 vertCount, hsPoint3 *positions, hsVector3 *normals, hsPoint3 *uvws, UInt32 uvwsPerVtx, hsColorRGBA *origColors, hsBool fauxShade, const hsColorRGBA* multColor, UInt32 numIndices, UInt16 *indices, hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, plGeometrySpan* span ) { hsTArray myNormals; /// Calculate normals if we don't have them if( normals == nil ) { int i; hsVector3 normal, v1, v2; myNormals.SetCount( vertCount ); for( i = 0; i < vertCount; i++ ) myNormals[ i ].Set( 0, 0, 0 ); for( i = 0; i < numIndices; i += 3 ) { v1.Set( &positions[ indices[ i + 1 ] ], &positions[ indices[ i ] ] ); v2.Set( &positions[ indices[ i + 2 ] ], &positions[ indices[ i ] ] ); normal = v1 % v2; myNormals[ indices[ i ] ] += normal; myNormals[ indices[ i + 1 ] ] += normal; myNormals[ indices[ i + 2 ] ] += normal; } for( i = 0; i < vertCount; i++ ) myNormals[ i ].Normalize(); normals = myNormals.AcquireArray(); } if( uvws == nil ) uvwsPerVtx = 0; span->BeginCreate( material, localToWorld, plGeometrySpan::UVCountToFormat( (UInt8)uvwsPerVtx ) ); if( !origColors && !fauxShade ) span->AddVertexArray( vertCount, positions, normals, nil, uvws, uvwsPerVtx ); else { hsTArray colArray; hsColorRGBA* colors; if( fauxShade ) { colArray.SetCount(vertCount); IQuickShadeVerts( vertCount, normals, colArray.AcquireArray(), origColors, multColor ); colors = colArray.AcquireArray(); } else // just use the origColors { colors = origColors; } hsTArray tempColors; int i; UInt8 a, r, g, b; tempColors.SetCount( vertCount ); for( i = 0; i < vertCount; i++ ) { hsColorRGBA *color = &colors[ i ]; a = (UInt8)( color->a >= 1 ? 255 : color->a <= 0 ? 0 : color->a * 255.0 ); r = (UInt8)( color->r >= 1 ? 255 : color->r <= 0 ? 0 : color->r * 255.0 ); g = (UInt8)( color->g >= 1 ? 255 : color->g <= 0 ? 0 : color->g * 255.0 ); b = (UInt8)( color->b >= 1 ? 255 : color->b <= 0 ? 0 : color->b * 255.0 ); tempColors[ i ] = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | ( b ); } span->AddVertexArray( vertCount, positions, normals, tempColors.AcquireArray(), uvws, uvwsPerVtx ); } span->AddIndexArray( numIndices, indices ); span->EndCreate(); } //// RegenerateDrawable //////////////////////////////////////////////////////// // Static function that refills an existing drawable based on the vertex/index // data given. That data had better match the data the drawable was first filled // with (i.e. vertex/index count hsBool plDrawableGenerator::RegenerateDrawable( UInt32 vertCount, hsPoint3 *positions, hsVector3 *normals, hsPoint3 *uvws, UInt32 uvwsPerVtx, hsColorRGBA *origColors, hsBool fauxShade, const hsColorRGBA* multColor, UInt32 numIndices, UInt16 *indices, hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, UInt32 diIndex, plDrawableSpans *destDraw ) { plDISpanIndex spanList = destDraw->GetDISpans( diIndex ); if( spanList.GetCount() != 1 ) { hsAssert(false, "Don't know how to distribute this geometry over multiple spans"); return false; } plGeometrySpan* span = destDraw->GetGeometrySpan(spanList[0]); if( (span->fNumVerts != vertCount) ||(span->fNumIndices != numIndices) ) { hsAssert(false, "Mismatched data coming in for a refill"); return false; } IFillSpan( vertCount, positions, normals, uvws, uvwsPerVtx, origColors, fauxShade, multColor, numIndices, indices, material, localToWorld, blended, span ); destDraw->RefreshDISpans( diIndex ); return true; } //// GenerateDrawable //////////////////////////////////////////////////////// // Static function that creates a new drawable based on the vertex/index // data given. plDrawableSpans *plDrawableGenerator::GenerateDrawable( UInt32 vertCount, hsPoint3 *positions, hsVector3 *normals, hsPoint3 *uvws, UInt32 uvwsPerVtx, hsColorRGBA *origColors, hsBool fauxShade, const hsColorRGBA* multColor, UInt32 numIndices, UInt16 *indices, hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, hsTArray *retIndex, plDrawableSpans *toAddTo ) { plDrawableSpans *newDraw; hsTArray spanArray; plGeometrySpan *span; // Set up props on the new drawable if( toAddTo != nil ) newDraw = toAddTo; else { newDraw = TRACKED_NEW plDrawableSpans; // newDraw->SetNativeProperty( plDrawable::kPropVolatile, true ); if( blended ) { newDraw->SetRenderLevel(plRenderLevel(plRenderLevel::kBlendRendMajorLevel, plRenderLevel::kDefRendMinorLevel)); newDraw->SetNativeProperty( plDrawable::kPropSortSpans | plDrawable::kPropSortFaces, true ); } static int nameIdx = 0; char buff[256]; sprintf(buff, "%s_%d", "GenDrawable", nameIdx++); hsgResMgr::ResMgr()->NewKey( buff, newDraw, plLocation::kGlobalFixedLoc ); } // Create a temp plGeometrySpan spanArray.SetCount( 1 ); span = spanArray[ 0 ] = TRACKED_NEW plGeometrySpan; IFillSpan( vertCount, positions, normals, uvws, uvwsPerVtx, origColors, fauxShade, multColor, numIndices, indices, material, localToWorld, blended, span ); /// Now add the span to the new drawable, clear up the span's buffers and return! UInt32 trash = UInt32(-1); UInt32 idx = newDraw->AppendDISpans( spanArray, trash, false ); if( retIndex != nil ) retIndex->Append(idx); return newDraw; } //// GenerateSphericalDrawable /////////////////////////////////////////////// plDrawableSpans *plDrawableGenerator::GenerateSphericalDrawable( const hsPoint3& pos, hsScalar radius, hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, const hsColorRGBA* multColor, hsTArray *retIndex, plDrawableSpans *toAddTo, hsScalar qualityScalar ) { hsTArray points; hsTArray normals; hsTArray indices; hsTArray colors; hsPoint3 point; hsVector3 normal; int i, j, numDivisions, start; float angle, z, x, y, internRad; plDrawableSpans *drawable; numDivisions = (int)( radius * qualityScalar / 10.f ); if( numDivisions < 5 ) numDivisions = 5; else if( numDivisions > 30 ) numDivisions = 30; /// Generate points for( i = 0; i <= numDivisions; i++ ) { angle = (float)i * ( hsScalarPI ) / (float)numDivisions; hsFastMath::SinCosInRange( angle, internRad, z ); internRad *= radius; for( j = 0; j < numDivisions; j++ ) { angle = (float)j * ( 2 * hsScalarPI ) / (float)numDivisions; hsFastMath::SinCosInRange( angle, x, y ); point.Set( pos.fX + x * internRad, pos.fY + y * internRad, pos.fZ + z * radius ); normal.Set( x * internRad, y * internRad, z * radius ); normal.Normalize(); points.Append( point ); normals.Append( normal ); } } /// Generate indices for( i = 0, start = 0; i < numDivisions; i++, start += numDivisions ) { for( j = 0; j < numDivisions - 1; j++ ) { indices.Append( start + j ); indices.Append( start + j + 1 ); indices.Append( start + j + numDivisions + 1 ); indices.Append( start + j ); indices.Append( start + j + numDivisions + 1 ); indices.Append( start + j + numDivisions ); } indices.Append( start + j ); indices.Append( start ); indices.Append( start + numDivisions ); indices.Append( start + j ); indices.Append( start + numDivisions ); indices.Append( start + j + numDivisions ); } /// Create a drawable for it drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), nil, 0, nil, true, multColor, indices.GetCount(), indices.AcquireArray(), material, localToWorld, blended, retIndex, toAddTo ); return drawable; } //// GenerateBoxDrawable ///////////////////////////////////////////////////// plDrawableSpans *plDrawableGenerator::GenerateBoxDrawable( hsScalar width, hsScalar height, hsScalar depth, hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, const hsColorRGBA* multColor, hsTArray *retIndex, plDrawableSpans *toAddTo ) { hsVector3 xVec, yVec, zVec; hsPoint3 pt; xVec.Set( width, 0, 0 ); yVec.Set( 0, height, 0 ); zVec.Set( 0, 0, depth ); pt.Set( -width / 2.f, -height / 2.f, -depth / 2.f ); return GenerateBoxDrawable( pt, xVec, yVec, zVec, material, localToWorld, blended, multColor, retIndex, toAddTo ); } //// GenerateBoxDrawable ///////////////////////////////////////////////////// // Version that takes a corner and three vectors, for x, y and z edges. #define CALC_NORMAL( nA, xVec, yVec, zVec ) { hsVector3 n = (xVec) + (yVec) + (zVec); n = -n; n.Normalize(); nA.Append( n ); } plDrawableSpans *plDrawableGenerator::GenerateBoxDrawable( const hsPoint3 &corner, const hsVector3 &xVec, const hsVector3 &yVec, const hsVector3 &zVec, hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, const hsColorRGBA* multColor, hsTArray *retIndex, plDrawableSpans *toAddTo ) { hsTArray points; hsTArray normals; hsTArray indices; hsTArray colors; hsTArray uvws; hsPoint3 point; plDrawableSpans *drawable; float mults[ 8 ][ 3 ] = { { -1, -1, -1 }, { 1, -1, -1 }, { 1, 1, -1 }, { -1, 1, -1 }, { -1, -1, 1 }, { 1, -1, 1 }, { 1, 1, 1 }, { -1, 1, 1 } }; /// Generate points and normals points.Expand( 8 ); normals.Expand( 8 ); point = corner; points.Append( point ); point += xVec; points.Append( point ); point += yVec; points.Append( point ); point = corner + yVec; points.Append( point ); point = corner + zVec; points.Append( point ); point += xVec; points.Append( point ); point += yVec; points.Append( point ); point = corner + zVec + yVec; points.Append( point ); CALC_NORMAL( normals, xVec, yVec, zVec ); CALC_NORMAL( normals, -xVec, yVec, zVec ); CALC_NORMAL( normals, -xVec, -yVec, zVec ); CALC_NORMAL( normals, xVec, -yVec, zVec ); CALC_NORMAL( normals, xVec, yVec, -zVec ); CALC_NORMAL( normals, -xVec, yVec, -zVec ); CALC_NORMAL( normals, -xVec, -yVec, -zVec ); CALC_NORMAL( normals, xVec, -yVec, -zVec ); uvws.Expand( 8 ); uvws.Append( hsPoint3( 0.f, 1.f, 0.f ) ); uvws.Append( hsPoint3( 1.f, 1.f, 0.f ) ); uvws.Append( hsPoint3( 1.f, 0.f, 0.f ) ); uvws.Append( hsPoint3( 0.f, 0.f, 0.f ) ); uvws.Append( hsPoint3( 1.f, 1.f, 0.f ) ); uvws.Append( hsPoint3( 1.f, 0.f, 0.f ) ); uvws.Append( hsPoint3( 0.f, 0.f, 0.f ) ); uvws.Append( hsPoint3( 0.f, 1.f, 0.f ) ); /// Generate indices indices.Expand( 36 ); indices.Append( 0 ); indices.Append( 1 ); indices.Append( 2 ); indices.Append( 0 ); indices.Append( 2 ); indices.Append( 3 ); indices.Append( 1 ); indices.Append( 0 ); indices.Append( 4 ); indices.Append( 1 ); indices.Append( 4 ); indices.Append( 5 ); indices.Append( 2 ); indices.Append( 1 ); indices.Append( 5 ); indices.Append( 2 ); indices.Append( 5 ); indices.Append( 6 ); indices.Append( 3 ); indices.Append( 2 ); indices.Append( 6 ); indices.Append( 3 ); indices.Append( 6 ); indices.Append( 7 ); indices.Append( 0 ); indices.Append( 3 ); indices.Append( 7 ); indices.Append( 0 ); indices.Append( 7 ); indices.Append( 4 ); indices.Append( 7 ); indices.Append( 6 ); indices.Append( 5 ); indices.Append( 7 ); indices.Append( 5 ); indices.Append( 4 ); /// Create a drawable for it drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), uvws.AcquireArray(), 1, nil, true, multColor, indices.GetCount(), indices.AcquireArray(), material, localToWorld, blended, retIndex, toAddTo ); return drawable; } //// GenerateBoundsDrawable ////////////////////////////////////////////////// plDrawableSpans *plDrawableGenerator::GenerateBoundsDrawable( hsBounds3Ext *bounds, hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, const hsColorRGBA* multColor, hsTArray *retIndex, plDrawableSpans *toAddTo ) { hsTArray points; hsTArray normals; hsTArray indices; hsTArray colors; hsPoint3 point; hsVector3 normal; int i; plDrawableSpans *drawable; float mults[ 8 ][ 3 ] = { { -1, -1, -1 }, { 1, -1, -1 }, { 1, 1, -1 }, { -1, 1, -1 }, { -1, -1, 1 }, { 1, -1, 1 }, { 1, 1, 1 }, { -1, 1, 1 } }; /// Generate points and normals points.Expand( 8 ); normals.Expand( 8 ); hsPoint3 min = bounds->GetMins(); hsPoint3 max = bounds->GetMaxs(); for( i = 0; i < 8; i++ ) { points.Append( hsPoint3( mults[ i ][ 0 ] > 0 ? max.fX : min.fX, mults[ i ][ 1 ] > 0 ? max.fY : min.fY, mults[ i ][ 2 ] > 0 ? max.fZ : min.fZ ) ); normals.Append( hsVector3( mults[ i ][ 0 ], mults[ i ][ 1 ], mults[ i ][ 2 ] ) ); } /// Generate indices indices.Expand( 36 ); indices.Append( 0 ); indices.Append( 1 ); indices.Append( 2 ); indices.Append( 0 ); indices.Append( 2 ); indices.Append( 3 ); indices.Append( 1 ); indices.Append( 0 ); indices.Append( 4 ); indices.Append( 1 ); indices.Append( 4 ); indices.Append( 5 ); indices.Append( 2 ); indices.Append( 1 ); indices.Append( 5 ); indices.Append( 2 ); indices.Append( 5 ); indices.Append( 6 ); indices.Append( 3 ); indices.Append( 2 ); indices.Append( 6 ); indices.Append( 3 ); indices.Append( 6 ); indices.Append( 7 ); indices.Append( 0 ); indices.Append( 3 ); indices.Append( 7 ); indices.Append( 0 ); indices.Append( 7 ); indices.Append( 4 ); indices.Append( 7 ); indices.Append( 6 ); indices.Append( 5 ); indices.Append( 7 ); indices.Append( 5 ); indices.Append( 4 ); /// Create a drawable for it drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), nil, 0, nil, true, multColor, indices.GetCount(), indices.AcquireArray(), material, localToWorld, blended, retIndex, toAddTo ); return drawable; } //// GenerateConicalDrawable ///////////////////////////////////////////////// plDrawableSpans *plDrawableGenerator::GenerateConicalDrawable( hsScalar radius, hsScalar height, hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, const hsColorRGBA* multColor, hsTArray *retIndex, plDrawableSpans *toAddTo ) { hsVector3 direction; direction.Set( 0, 0, height ); return GenerateConicalDrawable( hsPoint3( 0, 0, 0 ), direction, radius, material, localToWorld, blended, multColor, retIndex, toAddTo ); } //// GenerateConicalDrawable ///////////////////////////////////////////////// plDrawableSpans *plDrawableGenerator::GenerateConicalDrawable( hsPoint3 &apex, hsVector3 &direction, hsScalar radius, hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, const hsColorRGBA* multColor, hsTArray *retIndex, plDrawableSpans *toAddTo ) { hsTArray points; hsTArray normals; hsTArray indices; hsTArray colors; hsPoint3 point; hsVector3 normal; int i, numDivisions; float angle, x, y; plDrawableSpans *drawable; numDivisions = (int)( radius / 4.f ); if( numDivisions < 6 ) numDivisions = 6; else if( numDivisions > 20 ) numDivisions = 20; /// First, we need a few more vectors--specifically, the x and y vectors for the cone's base hsPoint3 baseCenter = apex + direction; hsVector3 xVec, yVec; xVec.Set( 1, 0, 0 ); yVec = xVec % direction; if( yVec.MagnitudeSquared() == 0 ) { xVec.Set( 0, 1, 0 ); yVec = xVec % direction; hsAssert( yVec.MagnitudeSquared() != 0, "Weird funkiness when doing this!!!" ); } xVec = yVec % direction; xVec.Normalize(); yVec.Normalize(); /// Now generate points based on those points.Expand( numDivisions + 2 ); normals.Expand( numDivisions + 2 ); points.Append( apex ); normals.Append( -direction ); for( i = 0; i < numDivisions; i++ ) { angle = (float)i * ( hsScalarPI * 2.f ) / (float)numDivisions; hsFastMath::SinCosInRange( angle, x, y ); points.Append( baseCenter + ( xVec * x * radius ) + ( yVec * y * radius ) ); normals.Append( ( xVec * x ) + ( yVec * y ) ); } /// Generate indices indices.Expand( ( numDivisions + 1 + numDivisions - 2 ) * 3 ); for( i = 0; i < numDivisions - 1; i++ ) { indices.Append( 0 ); indices.Append( i + 2 ); indices.Append( i + 1 ); } indices.Append( 0 ); indices.Append( 1 ); indices.Append( numDivisions ); // Bottom cap for( i = 3; i < numDivisions + 1; i++ ) { indices.Append( i - 1 ); indices.Append( i ); indices.Append( 1 ); } /// Create a drawable for it drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), nil, 0, nil, true, multColor, indices.GetCount(), indices.AcquireArray(), material, localToWorld, blended, retIndex, toAddTo ); return drawable; } //// GenerateAxesDrawable //////////////////////////////////////////////////// plDrawableSpans *plDrawableGenerator::GenerateAxesDrawable( hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, const hsColorRGBA* multColor, hsTArray *retIndex, plDrawableSpans *toAddTo ) { hsTArray points; hsTArray normals; hsTArray colors; hsTArray indices; int i; float size = 15; plDrawableSpans *drawable; /// Generate points points.SetCount( 6 * 3 ); normals.SetCount( 6 * 3 ); colors.SetCount( 6 * 3 ); points[ 0 ].Set( 0, 0, 0 ); points[ 1 ].Set( size, -size * 0.1f, 0 ); points[ 2 ].Set( size, -size * 0.3f, 0 ); points[ 3 ].Set( size * 1.3f, 0, 0 ); points[ 4 ].Set( size, size * 0.3f, 0 ); points[ 5 ].Set( size, size * 0.1f, 0 ); for( i = 0; i < 6; i++ ) { points[ i + 6 ].fX = - points[ i ].fY; points[ i + 6 ].fY = points[ i ].fX; points[ i + 6 ].fZ = 0; points[ i + 12 ].fX = points[ i ].fY; points[ i + 12 ].fZ = points[ i ].fX; points[ i + 12 ].fY = 0; colors[ i ].Set( 1, 0, 0, 1 ); colors[ i + 6 ].Set( 0, 1, 0, 1 ); colors[ i + 12 ].Set( 0, 0, 1, 1 ); if( multColor ) colors[ i ] *= *multColor; } /// Generate indices indices.SetCount( 6 * 3 ); for( i = 0; i < 18; i += 6 ) { indices[ i ] = i + 0; indices[ i + 1 ] = i + 1; indices[ i + 2 ] = i + 5; indices[ i + 3 ] = i + 2; indices[ i + 4 ] = i + 3; indices[ i + 5 ] = i + 4; } /// Create a drawable for it drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), nil, 0, nil, true, multColor, indices.GetCount(), indices.AcquireArray(), material, localToWorld, blended, retIndex, toAddTo ); return drawable; } //// GeneratePlanarDrawable ////////////////////////////////////////////////// // Version that takes a corner and two vectors, for x and y edges. #define CALC_PNORMAL( nA, xVec, yVec ) { hsVector3 n = (xVec) % (yVec); n.Normalize(); nA.Append( n ); } plDrawableSpans *plDrawableGenerator::GeneratePlanarDrawable( const hsPoint3 &corner, const hsVector3 &xVec, const hsVector3 &yVec, hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, const hsColorRGBA* multColor, hsTArray *retIndex, plDrawableSpans *toAddTo ) { hsTArray points; hsTArray normals; hsTArray indices; hsTArray colors; hsTArray uvws; hsPoint3 point; plDrawableSpans *drawable; /// Generate points and normals points.Expand( 4 ); normals.Expand( 4 ); point = corner; points.Append( point ); point += xVec; points.Append( point ); point += yVec; points.Append( point ); point = corner + yVec; points.Append( point ); CALC_PNORMAL( normals, xVec, yVec ); CALC_PNORMAL( normals, xVec, yVec ); CALC_PNORMAL( normals, xVec, yVec ); CALC_PNORMAL( normals, xVec, yVec ); uvws.Expand( 4 ); uvws.Append( hsPoint3( 0.f, 1.f, 0.f ) ); uvws.Append( hsPoint3( 1.f, 1.f, 0.f ) ); uvws.Append( hsPoint3( 1.f, 0.f, 0.f ) ); uvws.Append( hsPoint3( 0.f, 0.f, 0.f ) ); /// Generate indices indices.Expand( 6 ); indices.Append( 0 ); indices.Append( 1 ); indices.Append( 2 ); indices.Append( 0 ); indices.Append( 2 ); indices.Append( 3 ); /// Create a drawable for it drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), uvws.AcquireArray(), 1, nil, true, multColor, indices.GetCount(), indices.AcquireArray(), material, localToWorld, blended, retIndex, toAddTo ); return drawable; }