Files
GTASource/rage/scaleform/Src/GFxPlayer/GFxMesh.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1776 lines
61 KiB
C++

/**********************************************************************
Filename : GFxMesh.cpp
Content : Mesh structure implementation for shape characters.
Created :
Authors :
Copyright : (c) 2005-2006 Scaleform Corp. All Rights Reserved.
Patent Pending. Contact Scaleform for more information.
Notes :
Licensees may use this file in accordance with the valid Scaleform
Commercial License Agreement provided with the software.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
**********************************************************************/
#include "GFile.h"
#include "GFxLog.h"
#include "GFxStream.h"
#include "GFxMesh.h"
#include "GFxShape.h"
#include "GFxRenderGen.h"
#include "GFxDisplayContext.h"
#include "GFxScale9Grid.h"
#include "AMP/GFxAmpViewStats.h"
#include <float.h>
#include <stdlib.h>
#if defined(GFC_OS_WIN32)
#include <windows.h>
#endif
#include "GHeapNew.h"
// Utility.
// Dump the given coordinate GArray into the given GFxStream.
static void GFx_WriteCoordArray(GFile* out, const GArrayLH<SInt16>& PtArray)
{
UPInt n = PtArray.GetSize();
out->WriteSInt32(SInt32(n));
for (UPInt i = 0; i < n; i++)
{
out->WriteUInt16((UInt16) PtArray[i]);
}
}
// Read the coordinate GArray data from the GFxStream into *PtArray.
static void GFx_ReadCoordArray(GFile* in, GArrayLH<SInt16>* PtArray)
{
int n = in->ReadSInt32();
PtArray->Resize(n);
for (int i = 0; i < n; i ++)
{
(*PtArray)[i] = (SInt16) in->ReadUInt16();
}
}
//
// ***** GFxMesh
//
GFxMesh::GFxMesh()
{
StyleCount= 0;
Styles[0] = (UInt)GFxPathConsts::Style_None;
#ifndef GFC_NO_FXPLAYER_EDGEAA
Styles[1] = (UInt)GFxPathConsts::Style_None;
Styles[2] = (UInt)GFxPathConsts::Style_None;
FillType = GRenderer::GFill_Color;
#endif
}
void GFxMesh::AddTriangles(UInt style, const GTessellator &sourceTess)
{
// Assign style.
if (TriangleIndices.GetSize() == 0)
{
Styles[0] = style;
StyleCount = 1;
}
else
{ // Styles must match.
GASSERT(Styles[0] == style);
}
UInt triangleCount = sourceTess.GetNumTriangles();
UInt index = (UInt)TriangleIndices.GetSize();
UInt triangle;
TriangleIndices.Resize(index + triangleCount * 3);
for(triangle = 0; triangle<triangleCount; triangle++)
{
// Fetch triangle indices for each triangle
const GTessellator::TriangleType& t = sourceTess.GetTriangle(triangle);
TriangleIndices[index++] = (SInt16) t.v1;
TriangleIndices[index++] = (SInt16) t.v2;
TriangleIndices[index++] = (SInt16) t.v3;
}
}
#ifndef GFC_NO_FXPLAYER_EDGEAA
// Sets the styles
void GFxMesh::SetEdgeAAStyles(UInt styleCount, UInt style0, UInt style1, UInt style2)
{
StyleCount = styleCount;
Styles[0] = style0;
Styles[1] = style1;
Styles[2] = style2;
}
void GFxMesh::SetGouraudFillType(GRenderer::GouraudFillType gft)
{
FillType = gft;
}
#endif
void GFxMesh::Clear()
{
TriangleIndicesCache.ReleaseData(GRenderer::Cached_Index);
TriangleIndices.Clear();
StyleCount= 0;
Styles[0] = (UInt)GFxPathConsts::Style_None;
#ifndef GFC_NO_FXPLAYER_EDGEAA
Styles[1] = (UInt)GFxPathConsts::Style_None;
Styles[2] = (UInt)GFxPathConsts::Style_None;
FillType = GRenderer::GFill_Color;
#endif
}
void GFxMesh::Display(const GFxDisplayContext& dc,
const GFxFillStyle* pfills, UInt fillsSize,
UInt vertexStartIndex, UInt vertexCount, Float scaleMultiplier,
const GRenderer::Matrix* imgAdjustMatrices,
bool useEdgeAA)
{
// #ifdef GFX_AMP_SERVER
// ScopeFunctionTimer displayTimer(dc.pStats, NativeCodeSwdHandle, Func_GFxMesh_Display, Amp_Profile_Level_Low);
// #endif
GRenderer *prenderer = dc.GetRenderer();
// Pass GFxMesh to renderer.
if (TriangleIndices.GetSize() > 0)
{
const GRenderer::Matrix* imgAdjustMatrix;
#ifndef GFC_NO_FXPLAYER_EDGEAA
if (useEdgeAA)
{
// Compute & apply style
GRenderer::FillTexture ft[3];
GRenderer::FillTexture* pft[3] = {0, 0, 0};
if (fillsSize)
{
// In case of Font shapes, size can be 0.
UInt styleIndex;
for (styleIndex = 0; styleIndex < StyleCount; styleIndex++)
{
imgAdjustMatrix =
imgAdjustMatrices ?
&imgAdjustMatrices[Styles[styleIndex]] : 0;
if (Styles[styleIndex] != GFxPathConsts::Style_None &&
pfills[Styles[styleIndex]].GetFillTexture(&ft[styleIndex],
dc, scaleMultiplier,
imgAdjustMatrix))
{
pft[styleIndex] = &ft[styleIndex];
}
}
}
// Apply style textures.
prenderer->FillStyleGouraud(FillType, pft[0], pft[1], pft[2]);
}
else
#else
// useEdgeAA must always be zero if this is compiled
GASSERT(useEdgeAA == 0);
GUNUSED2(fillsSize,useEdgeAA);
#endif
// else (!useEdgeAA)
{
// No EdgeAA: single style.
GASSERT(GetStyle() <= fillsSize);
if (fillsSize)
{
imgAdjustMatrix =
imgAdjustMatrices ?
&imgAdjustMatrices[GetStyle()] : 0;
pfills[GetStyle()].Apply(dc, scaleMultiplier, imgAdjustMatrix);
}
}
// Draw triangles.
GRenderer::CacheProvider cp(&TriangleIndicesCache);
prenderer->SetIndexData(&TriangleIndices[0], (int)TriangleIndices.GetSize(), GRenderer::Index_16, &cp);
prenderer->DrawIndexedTriList(vertexStartIndex, 0, vertexCount, 0, (int)(TriangleIndices.GetSize() / 3));
}
}
// Dump our data to *out.
void GFxMesh::OutputCachedData(GFile* pout)
{
GUNUSED(pout);
// GFx_WriteCoordArray(out, TriangleIndices);
}
// Slurp our data from *out.
void GFxMesh::InputCachedData(GFile* pin)
{
GUNUSED(pin);
// GFx_ReadCoordArray(in, &TriangleIndices);
}
//
// ***** GFxCachedStroke
//
// Default constructor, for GArray<>.
GFxCachedStroke::GFxCachedStroke()
{
Style = GFxPathConsts::Style_None;
#ifndef GFC_NO_FXPLAYER_STROKER
pCachedMesh = 0;
CachedWidth = -4.0f;
CachedScaleFlag = 0;
CachedStrokeMatrixSx = 0;
CachedStrokeMatrixSy = 0;
#ifdef GFC_BUILD_DEBUG
RangeOverflowWarned = 0;
#endif
EdgeAADisabled = 0;
UsedStrokerAA = 0;
#ifndef GFC_NO_FXPLAYER_EDGEAA
EdgeAA = 0;
AllowCxformAddAlpha = 0;
#endif
#endif
}
GFxCachedStroke::~GFxCachedStroke()
{
#ifndef GFC_NO_FXPLAYER_STROKER
VerticesCache.ReleaseData(GRenderer::Cached_Vertex);
CoordsCache.ReleaseData(GRenderer::Cached_Vertex);
if (pCachedMesh)
delete pCachedMesh;
#endif
}
void GFxCachedStroke::AddShapePathVertices(const GCompoundShape& shape, UInt pathIndex, Float scaleMultiplier)
{
GASSERT(pathIndex < shape.GetNumPaths());
const GCompoundShape::SPath& p = shape.GetPath(pathIndex);
if (Coords.GetSize() == 0)
Style = p.GetLineStyle();
else
{
// All paths added must share a single line style.
GASSERT(Style == p.GetLineStyle());
}
UInt vertexCount = p.GetNumVertices();
UInt startIndex = (UInt)Coords.GetSize();
UInt j;
Coords.Resize(startIndex + vertexCount * 2);
// Copy the vertices from the path.
for (j = 0; j < vertexCount; j++)
{
Coords[startIndex + j*2 + 0] = (SInt16) (p.GetVertex(j).x * scaleMultiplier);
Coords[startIndex + j*2 + 1] = (SInt16) (p.GetVertex(j).y * scaleMultiplier);
}
// And store the count.
CoordCounts.PushBack(vertexCount * 2);
}
// Render this line strip in the given style.
// Returns true if a stroke was rendered
bool GFxCachedStroke::Display(GFxDisplayContext &context, const GFxLineStyle& style,
const Matrix &mat, Float scaleMultiplier, Float errorTolerance,
bool cxformAddAlpha, bool canBlend, bool scale9Grid)
{
GASSERT(Coords.GetSize() > 1);
GASSERT((Coords.GetSize() & 1) == 0);
const GFxRenderConfig& rconfig = *context.GetRenderConfig();
#ifdef GFC_NO_FXPLAYER_STROKER
GUNUSED(mat);
GUNUSED4(canBlend,cxformAddAlpha,errorTolerance,scaleMultiplier);
GUNUSED(scale9Grid);
#else
GFxRenderGen* rg = context.pMeshCacheManager->GetRenderGen();
// Width * 20.
Float strokeScaleFactor = 1.0f; // Multiplier applied to scale width.
Float resultScaleFactor = 1.0f; // Resulting scale factor which would include a matrix.
Float width = style.GetWidth();
bool matrixPreMultiply = 1;
bool useCachedStroke = 0;
Float sx, sy;
Float rot, rotf;
Float screenSpaceWidth;
bool useStrokerAA = !canBlend;
// If line style has non-255 alpha values, no stroker AA.
if (style.CanBlend())
useStrokerAA = 0;
# ifdef GFC_NO_FXPLAYER_EDGEAA
GUNUSED(cxformAddAlpha);
# else
bool useEdgeAA = rconfig.IsUsingEdgeAA();
if (style.HasComplexFill() && !rconfig.IsEdgeAATextured())
useEdgeAA = 0;
# endif // GFC_NO_FXPLAYER_EDGEAA
// Hairline rendering occurs in Flash under two conditions:
// 1) The original width value is 0.
// 2) The final computed width after transforms is <= 1. This means that
// lines are never actually drawn with a narrower width.
// In our implementation, hairline rendering can also be forced with RF_StrokeHairline flag.
if ((rconfig.GetStrokeRenderFlags() == GFxRenderConfig::RF_StrokeHairline) || (style.GetWidth() == 0))
goto render_hairline_stroke;
sx = scale9Grid ? (Float)1 : (Float)mat.GetXScale();
sy = scale9Grid ? (Float)1 : (Float)mat.GetYScale();
switch(style.GetScaling())
{
case GFxLineStyle::LineScaling_None:
strokeScaleFactor = 1.0f;
resultScaleFactor = sqrtf(sx*sx + sy*sy) * (Float)GFC_MATH_SQRT1_2;
break;
case GFxLineStyle::LineScaling_Normal:
resultScaleFactor = sqrtf(sx*sx + sy*sy) * (Float)GFC_MATH_SQRT1_2;
if (rconfig.GetStrokeRenderFlags() != GFxRenderConfig::RF_StrokeCorrect && !scale9Grid)
{
// In normal mode, simply use original scale factor;
// it will be scaled anisotropically by matrix.
matrixPreMultiply = 0;
strokeScaleFactor = 1.0f;
}
else
{
strokeScaleFactor = resultScaleFactor;
}
break;
// Try to mimic vertical and horizontal stroke scaling calculation.
case GFxLineStyle::LineScaling_Vertical:
{
// Note that these H/V scaling equations are not always correct, just most of the time;
// they model the axis-aligned cases and a lot of the rotation/scaling behavior.
// Use stroke_rotation_v8.swf for testing.
rot = (Float)mat.GetRotation();
rotf = G_Max<Float>(cosf(rot - (Float)GFC_MATH_PI_4),0) * (Float) GFC_MATH_SQRT2;
Float sx2 = sx * sinf(rot);
Float sy2 = sy * cosf(rot);
strokeScaleFactor = rotf * sqrtf(sx2*sx2 + sy2*sy2);
resultScaleFactor = strokeScaleFactor;
}
break;
case GFxLineStyle::LineScaling_Horizontal:
{
rot = (Float)mat.GetRotation();
rotf = G_Max<Float>(cosf(rot + (Float)GFC_MATH_PI_4),0) * (Float) GFC_MATH_SQRT2;
Float sx2 = sx * cosf(rot);
Float sy2 = sy * sinf(rot);
strokeScaleFactor = rotf * sqrtf(sx2*sx2 + sy2*sy2);
resultScaleFactor = strokeScaleFactor;
}
break;
default:
break;
}
screenSpaceWidth = width * resultScaleFactor * context.GetPixelScale();
// Blended connection points bigger then 1 pixel (20 twips) are definitely noticeable,
// so we can not use StrokerAA for them.
if (screenSpaceWidth <= rconfig.GetStrokerAAWidth() * 20.0f)
{
// Stroker AA can be used for relatively narrow lines where overlap is
// not a visible problem.
useStrokerAA = 1;
}
//useStrokerAA = 0; // To enforce regular stroker
//useStrokerAA = 1; // To enforce StrokerAA
// If less then one pixel - use hairline.
if (!matrixPreMultiply && screenSpaceWidth < 20.0f)
{
// Approximate hairline stroke by setting screen-space width to 1.
// [Actual hairline is slower and does not provide smooth AA look].
width = 20.0f / (resultScaleFactor * context.GetPixelScale());
}
// Compute the new width
width *= strokeScaleFactor;
// See if we already have a correctly cached mesh.
if (pCachedMesh)
{
// If we require "correct scale", cached mesh must have matched that requirement.
// EdgeAA start must also match (if disabling/enabling EdgeAA, tessellation is recomputed).
if ((matrixPreMultiply == CachedScaleFlag) &&
(!UsedStrokerAA || (useStrokerAA == UsedStrokerAA))
// (useStrokerAA == UsedStrokerAA)
# ifndef GFC_NO_FXPLAYER_EDGEAA
&& (useEdgeAA == EdgeAA) &&
(!cxformAddAlpha || AllowCxformAddAlpha)
# endif
)
{
// Widths must be within 1/5 pixel now = 4 twips.
if ((width>(CachedWidth - 2)) && (width < (CachedWidth + 2)))
{
if (!matrixPreMultiply)
{
// For Edge AA we technically would have to re-calculate this when width edge
// ratio changes; however, this is not necessary because GFxCachedStroke is a
// part of a mesh which is already discriminated based on scale.
useCachedStroke = 1;
}
else
{
// Allow up to 1/2 pixel error due to scale.
// Since shape is (64K / multiplier) pixels wide, allow that much tolerance
Float scaleTolerance = scaleMultiplier * (1.0f / 128000.0f);
// Matrix scale and aspect ratio must match.
// Note that a flip in the sign of scale is allowed (only occurs in GetXScale of our matrix impl).
if ( (sy >= (CachedStrokeMatrixSy - scaleTolerance)) && (sy <= (CachedStrokeMatrixSy + scaleTolerance)) &&
(((sx >= (CachedStrokeMatrixSx - scaleTolerance)) && (sx <= (CachedStrokeMatrixSx + scaleTolerance))) ||
((sx >= (-CachedStrokeMatrixSx - scaleTolerance)) && (sx <= (-CachedStrokeMatrixSx + scaleTolerance)))) )
useCachedStroke = 1;
// Note: Correct stroking cache performance could be better if we allowed ourselves
// to cache multiple strokes for different scale {sx,sy} pairs (right now some scenes
// are more expensive because some strokes are rebuilt every frame if they are
// simultaneously displayed at different scales).
}
}
}
if (!useCachedStroke)
{
VerticesCache.ReleaseData(GRenderer::Cached_Vertex);
pCachedMesh->Clear();
Vertices.Clear();
}
}
else
{
if ((pCachedMesh = GHEAP_AUTO_NEW(this) GFxMesh)==0)
return false;
}
if (!useCachedStroke)
{
// Set scale/rotate/skew only matrix.
Matrix m, mInv;
# ifndef GFC_NO_FXPLAYER_EDGEAA
bool needEdgeAA = rconfig.IsUsingEdgeAA() && !EdgeAADisabled;
if (style.HasComplexFill() && !rconfig.IsEdgeAATextured())
needEdgeAA = 0;
// use EdgeAA without cxform add alpha if it isn't supported anyway
if (cxformAddAlpha && !rconfig.HasVertexFormat(GRenderer::Vertex_XY16iCF32))
{
if (rconfig.HasCxformAddAlpha())
needEdgeAA = 0;
else
cxformAddAlpha = 0;
}
# endif
GASSERT(Vertices.GetSize() == 0);
if (matrixPreMultiply)
{
m.M_[0][0] = mat.M_[0][0];
m.M_[1][0] = mat.M_[1][0];
m.M_[0][1] = mat.M_[0][1];
m.M_[1][1] = mat.M_[1][1];
mInv.SetInverse(m);
}
// Compute the stroked mesh
//GCompoundShape lineShape;
// Populate line shape
UInt i, j, c, index;
GPointF pt;
rg->Shape1.Clear();
for(j = 0, index = 0; j<CoordCounts.GetSize(); j++)
{
c = CoordCounts[j];
rg->Shape1.BeginPath(-1, -1, Style);
if (matrixPreMultiply)
{
for (i = 0; i<c; i+=2, index+=2)
{
// Add vertex transformed by our M
m.Transform(&pt, GPointF(Coords[index], Coords[index + 1]));
rg->Shape1.AddVertex(pt.x, pt.y);
}
}
else
{
for (i = 0; i<c; i+=2, index+=2)
rg->Shape1.AddVertex(Coords[index], Coords[index + 1]);
}
}
// Use StrokerAA instead of Stroker+Tessellator if possible.
if (useStrokerAA)
{
#ifndef GFC_NO_FXPLAYER_STROKERAA
rg->StrokerAA.Clear(); //GStrokerAA strokerAA;
rg->StrokerAA.SetStartLineCap(GFx_SWFToFxStroke_LineCap(style.GetStartCap()));
rg->StrokerAA.SetEndLineCap(GFx_SWFToFxStroke_LineCap(style.GetEndCap()));
rg->StrokerAA.SetLineJoin(GFx_SWFToFxStroke_LineJoin(style.GetJoin()));
if (style.GetJoin() == GFxLineStyle::LineJoin_Miter)
rg->StrokerAA.SetMiterLimit(style.GetMiterSize());
rg->StrokerAA.SetCurveTolerance(scaleMultiplier * errorTolerance);
# ifndef GFC_NO_FXPLAYER_EDGEAA
if (needEdgeAA)
{
// AA Width is half a pixel.
GCoordType aaWidth = scaleMultiplier * 20.f / context.GetPixelScale();
if (!matrixPreMultiply)
{
// If no matrix multiply used (i.e. this is Normal Stroke),
// un-scale width by the matrix transform scale.
aaWidth /= resultScaleFactor;
}
rg->StrokerAA.SetAntiAliasWidth(aaWidth);
rg->StrokerAA.SetSolidWidth(G_Max<Float>(0.0f, width * scaleMultiplier - aaWidth ));
}
else
# endif
{
rg->StrokerAA.SetAntiAliasWidth(0.0f); // No AA.
rg->StrokerAA.SetSolidWidth(width * scaleMultiplier);
}
// Generate stroke triangles.
rg->StrokerAA.Tessellate(rg->Shape1, Style);
rg->NumTessellatedShapes++;
////@TODO: Remove
////Test for the "long rays" erroneously produced by the stroker
//Float x1, y1, x2, y2;
//rg->Shape1.PerceiveBounds(&x1, &y1, &x2, &y2);
//x1 -= rg->StrokerAA.GetAntiAliasWidth() + rg->StrokerAA.GetSolidWidth() * 5;
//y1 -= rg->StrokerAA.GetAntiAliasWidth() + rg->StrokerAA.GetSolidWidth() * 5;
//x2 += rg->StrokerAA.GetAntiAliasWidth() + rg->StrokerAA.GetSolidWidth() * 5;
//y2 += rg->StrokerAA.GetAntiAliasWidth() + rg->StrokerAA.GetSolidWidth() * 5;
//for(unsigned jj = 0; jj < rg->StrokerAA.GetNumVertices(); jj++)
//{
// const GStrokerAA::VertexType& v = rg->StrokerAA.GetVertex(jj);
// if(v.x < x1 || v.x > x2 || v.y < y1 || v.y > y2)
// {
// FILE* fd = fopen("stroke", "at");
// fprintf(fd, "mc.lineStyle(%f);\n", width * scaleMultiplier / 20);
// fprintf(fd, "mc.moveTo(%f, %f);\n", rg->Shape1.GetVertex(0).x/20, rg->Shape1.GetVertex(0).y/20);
// for(unsigned kk = 1; kk < rg->Shape1.GetNumVertices(); kk++)
// {
// fprintf(fd, "mc.lineTo(%f, %f);\n", rg->Shape1.GetVertex(kk).x/20, rg->Shape1.GetVertex(kk).y/20);
// }
// fclose(fd);
// break;
// }
//}
//===================
# ifndef GFC_NO_FXPLAYER_EDGEAA
// Antialiased version of StrokerAA use.
if (needEdgeAA)
{
// Determine the color that will be used for sides ahead of time.
UInt32 fillColor, outsideColor;
UInt32 fillFactor = 0, outsideFactor = 0;
AllowCxformAddAlpha = cxformAddAlpha;
// Currently needs swapping. TBD.
GColor c = style.GetColor();
GColor nc = c;
nc.SetBlue(c.GetRed() ); // Swap G & B
nc.SetRed( c.GetBlue());
fillColor = nc.Raw;
if (AllowCxformAddAlpha || style.HasComplexFill())
{
Vertices.SetFormat(GRenderer::Vertex_XY16iCF32);
// In this vertex format Alpha comes from Factor.Alpha.
// Use fill texture everywhere.
if (style.HasComplexFill())
{
// Color is irrelevant, use texture channels.
fillFactor = 0xFFFFFFFF;
outsideFactor = 0x00FFFFFF;
}
else
{
// Always use colors, factor only provides alpha.
fillFactor = 0xFF000000;
outsideFactor = 0x00000000;
}
// No alpha masking: avoid double blending.
outsideColor = fillColor;
}
else
{
Vertices.SetFormat(GRenderer::Vertex_XY16iC32);
outsideColor = fillColor & 0x00FFFFFF; // No alpha outside.
}
pCachedMesh->SetEdgeAAStyles(1, 0, (UInt)GFxPathConsts::Style_None, (UInt)GFxPathConsts::Style_None);
pCachedMesh->SetGouraudFillType(style.HasComplexFill() ?
GRenderer::GFill_1Texture : GRenderer::GFill_Color);
if (rg->StrokerAA.GetNumVertices() <= 0xFFFE)
{
Vertices.Resize(rg->StrokerAA.GetNumVertices());
if (matrixPreMultiply)
{
for(j = 0; j < rg->StrokerAA.GetNumVertices(); j++)
{
const GStrokerAA::VertexType& vpt = rg->StrokerAA.GetVertex(j);
// Transform back
mInv.Transform(&pt, GPointF(vpt.x, vpt.y));
BoundCheckPoint(&pt, sx, sy);
GFxVertexRef v = Vertices.GetVertexRef(j);
UInt32 c = (vpt.id == -1) ? outsideColor : fillColor;
UInt32 f = (vpt.id == -1) ? outsideFactor : fillFactor;
v.InitVertex(pt.x, pt.y, c);
v.SetFactor(f);
}
}
else
{
for(j = 0; j < rg->StrokerAA.GetNumVertices(); j++)
{
const GStrokerAA::VertexType& vpt = rg->StrokerAA.GetVertex(j);
GFxVertexRef v = Vertices.GetVertexRef(j);
UInt32 c = (vpt.id == -1) ? outsideColor : fillColor;
UInt32 f = (vpt.id == -1) ? outsideFactor : fillFactor;
v.InitVertex(vpt.x, vpt.y, c);
v.SetFactor(f);
}
}
// We are using EdgeAA.
EdgeAA = 1;
}
}
else
# endif // #ifndef GFC_NO_FXPLAYER_EDGEAA
// Non-antialiased StrokerAA use.
{
Vertices.SetFormat(GRenderer::Vertex_XY16i);
Vertices.Resize(rg->StrokerAA.GetNumVertices());
// Use style 0 everywhere in stroke mesh.
pCachedMesh->SetStyle(0);
if (matrixPreMultiply)
{
for(j = 0; j < rg->StrokerAA.GetNumVertices(); j++)
{
const GStrokerAA::VertexType& vpt = rg->StrokerAA.GetVertex(j);
// Transform back
mInv.Transform(&pt, GPointF(vpt.x, vpt.y));
BoundCheckPoint(&pt, sx, sy);
Vertices.GetVertexRef(j).InitVertex(pt.x, pt.y);
}
}
else
{
for(j = 0; j < rg->StrokerAA.GetNumVertices(); j++)
{
const GStrokerAA::VertexType& vpt = rg->StrokerAA.GetVertex(j);
Vertices.GetVertexRef(j).InitVertex(vpt.x, vpt.y);
}
}
}
// Copy the triangles into the mesh.
if (Vertices.GetSize())
{
for(j = 0; j < rg->StrokerAA.GetNumTriangles(); j++)
{
const GStrokerAA::TriangleType& t = rg->StrokerAA.GetTriangle(j);
pCachedMesh->AddTriangle(UInt16(t.v1), UInt16(t.v2), UInt16(t.v3));
}
}
// Tess Statistics.
context.AddTessTriangles(pCachedMesh->GetTriangleCount());
#endif //#ifndef GFC_NO_FXPLAYER_STROKERAA
}
else // if (!useStrokerAA)
{
rg->Tessellator.Clear();//GTessellator strokeTess;
rg->Stroker.Clear(); //GStroker stroker;
rg->Shape2.Clear(); //GCompoundShape strokeShape;
// If we use AA_OuterEdges we need to modify stroke width;
GCoordType edgeAAWidth = scaleMultiplier * -10.f / context.GetPixelScale();
if (!matrixPreMultiply)
{
// If no matrix multiply used (i.e. this is Normal Stroke),
// un-scale width by the matrix transform scale.
edgeAAWidth /= resultScaleFactor;
}
// *** Tessellate and render as stroke
GCoordType strokeWidth = width * scaleMultiplier;
#ifndef GFC_NO_FXPLAYER_EDGEAA
if (needEdgeAA)
{
strokeWidth += edgeAAWidth * 2;
if (strokeWidth < 1)
strokeWidth = 1;
}
#endif
rg->Stroker.SetLineJoin(GFx_SWFToFxStroke_LineJoin(style.GetJoin()));
rg->Stroker.SetStartLineCap(GFx_SWFToFxStroke_LineCap(style.GetStartCap()));
rg->Stroker.SetEndLineCap(GFx_SWFToFxStroke_LineCap(style.GetEndCap()));
rg->Stroker.SetWidth(strokeWidth);
// Miter size is 1/2 the multiple of width, so pass it directly with no multipliers.
// NOTE: Stroker does not like non-default miter limits if we are not doing MiterJoin.
if (style.GetJoin() == GFxLineStyle::LineJoin_Miter)
rg->Stroker.SetMiterLimit(style.GetMiterSize());
rg->Shape2.SetCurveTolerance(scaleMultiplier * errorTolerance);
rg->Stroker.GenerateStroke(rg->Shape1, Style, rg->Shape2);
//// Test stroke. This thing just draws the stroke outline.
//for(UInt k = 0; k < rg->Shape2.GetNumPaths(); k++)
//{
// GFxCachedStroke lineStrip;
// lineStrip.SetShapePathVertices(rg->Shape2, k, 1.0f);
// lineStrip.Display(prenderer, lineStyles[style], 1.0f);
//}
// *** Triangulation of the stroke multi-path
// Setup for optimal stroke triangulation
rg->Tessellator.SetFillRule(GTessellator::FillNonZero);
// It all became redundant with Tessellator v2
//rg->Tessellator.SetReduceWinding(true);
//rg->Tessellator.SetProcessJunctions(false);
//rg->Tessellator.SetPreserveStyleCoherence(false);
//rg->Tessellator.SetRoundOffScale(1/16.0f); // [fixes bug in scale, does round-off]
bool optFlag = rconfig.IsOptimizingTriangles();
rg->Tessellator.SetDirOptimization(true);
rg->Tessellator.SetCoherenceOptimization(optFlag);
rg->Tessellator.SetMonotoneOptimization(optFlag);
rg->Tessellator.SetPilotTriangulation(optFlag);
rg->Tessellator.Monotonize(rg->Shape2, 1);
rg->NumTessellatedShapes++;
# ifdef GFX_GENERATE_STROKE_SHAPE_FILE
{
FILE* fd = fopen("strokes", "at");
fprintf(fd, "=======BeginShape\n");
unsigned i, j;
for(i = 0; i < rg->Shape2.GetNumPaths(); i++)
{
const GCompoundShape::SPath& path = rg->Shape2.GetPath(i);
const GPointType& p0 = path.GetVertex(0);
fprintf(fd, "Path 0 -1 -1 %f %f\n", p0.x, p0.y);
for(j = 1; j < path.GetNumVertices(); j++)
{
const GPointType& p = path.GetVertex(j);
fprintf(fd, "Line %f %f\n", p.x, p.y);
}
fprintf(fd, "<-------EndPath\n");
}
fprintf(fd, "!======EndShape\n");
fclose(fd);
}
# endif
if (rg->Tessellator.GetNumVertices())
{
// If renderer cached vertices, we must release them.
VerticesCache.ReleaseData(GRenderer::Cached_Vertex);
# ifndef GFC_NO_FXPLAYER_EDGEAA
// EdgeAA starts out at 0, in case it fails.
// TBD: Do we need to check the renderer for caps?
EdgeAA = 0;
// Edge AA enabled
if (needEdgeAA)
{
// Hack to assign correct color channel in shapes.
GFxFillStyleArrayTemp strokeFillStyles;
if (style.HasComplexFill())
strokeFillStyles.PushBack(*style.GetComplexFill());
else
{
GFxFillStyle strokeFillStyle;
strokeFillStyle.SetColor(style.GetColor());
strokeFillStyles.PushBack(strokeFillStyle);
}
rg->EdgeAA_Gen.ProcessAndSortEdges(rg->Tessellator,
&strokeFillStyles[0], strokeFillStyles.GetSize(),
edgeAAWidth, GEdgeAA::AA_OuterEdges,
style.HasComplexFill() ? 0x00FFFFFF : 0); // Always mix texture for gradient stroke.
// If over 65K vertices limit, can't use AA for now.
if (rg->EdgeAA_Gen.GetVertexCount() <= 0xFFFE)
{
bool edgeGenSuccess = 0;
AllowCxformAddAlpha = cxformAddAlpha;
if (AllowCxformAddAlpha || style.HasComplexFill())
Vertices.SetFormat(GRenderer::Vertex_XY16iCF32);
else
Vertices.SetFormat(GRenderer::Vertex_XY16iC32);
if (matrixPreMultiply)
edgeGenSuccess = rg->EdgeAA_Gen.GenerateSolidMesh(&Vertices, pCachedMesh, mInv);
else
edgeGenSuccess = rg->EdgeAA_Gen.GenerateSolidMesh(&Vertices, pCachedMesh, 1.0f);
// Tess Statistics.
context.AddTessTriangles(pCachedMesh->GetTriangleCount());
// Done, use AA.
if (edgeGenSuccess)
{
EdgeAA = 1;
// Set edgeAA style, so that rendering actually applies the texture.
if (style.HasComplexFill())
{
pCachedMesh->SetEdgeAAStyles(1, 0, (UInt)GFxPathConsts::Style_None, (UInt)GFxPathConsts::Style_None);
pCachedMesh->SetGouraudFillType(GRenderer::GFill_1Texture);
}
}
}
}
# endif // #ifndef GFC_NO_FXPLAYER_EDGEAA
if (!IsUsingEdgeAA())
{
Vertices.SetFormat(GRenderer::Vertex_XY16i);
Vertices.Resize(rg->Tessellator.GetNumVertices());
if (matrixPreMultiply)
{
for(j = 0; j < rg->Tessellator.GetNumVertices(); j++)
{
const GPointType& vpt = rg->Tessellator.GetVertex(j);
// Transform back
mInv.Transform(&pt, GPointF(vpt.x, vpt.y));
BoundCheckPoint(&pt, sx, sy);
Vertices.GetVertexRef(j).InitVertex(pt.x, pt.y);
}
}
else
{
for(j = 0; j < rg->Tessellator.GetNumVertices(); j++)
{
const GPointType& vpt = rg->Tessellator.GetVertex(j);
Vertices.GetVertexRef(j).InitVertex(vpt.x, vpt.y);
}
}
// We have to create it here because it accumulates the triangles.
for(j = 0; j < rg->Tessellator.GetNumMonotones(); j++)
{
rg->Tessellator.TriangulateMonotone(j);
pCachedMesh->AddTriangles(0, rg->Tessellator); // Use a fake style, 0
}
// Tess Statistics.
context.AddTessTriangles(pCachedMesh->GetTriangleCount());
} // if (!EdgeAA)
} // if (rg->Tessellator.GetNumVertices())
} // else if (!useStrokerAA)
// Update cache matching constants.
UsedStrokerAA = useStrokerAA;
CachedWidth = width;
CachedScaleFlag = matrixPreMultiply;
CachedStrokeMatrixSx = sx;
CachedStrokeMatrixSy = sy;
#ifndef GFC_NO_FXPLAYER_EDGEAA
AllowCxformAddAlpha = cxformAddAlpha;
#endif
}
// Draw the mesh.
if (Vertices.GetSize())
{
GRenderer::CacheProvider cp(&VerticesCache);
Vertices.ApplyToRenderer(rconfig.GetRenderer(), &cp);
// Handle complex fill styles.
GFxFillStyle strokeFillStyle;
GFxFillStyle* pfill = 0;
UInt fillsSize = 0;
Float multiplier = 1.0f;
if (style.HasComplexFill())
{
pfill = style.GetComplexFill();
fillsSize = 1;
// Note: scaleMultiplier only applies to fills; it needs to be correct so that
// complex fills for stroke show up right.
multiplier = 1.0f / scaleMultiplier;
}
else if (!IsUsingEdgeAA())
{
pfill = &strokeFillStyle;
fillsSize = 1;
strokeFillStyle.SetColor(style.GetColor());
}
else
{
// Note that at this point null pfill is ok, because it would indicate
// that colors come from vertices, as expected for EdgeAA. If that is
// so vertex format can NOT be Vertex_XY16i.
GASSERT(Vertices.GetFormat() != GRenderer::Vertex_XY16i);
}
pCachedMesh->Display(context, pfill, fillsSize,
0, Vertices.GetSize(),
multiplier, (const GRenderer::Matrix*)0,
IsUsingEdgeAA());
return true;
}
return false;
// We get here if we only render hairline.
render_hairline_stroke: ;
#endif // GFC_NO_FXPLAYER_STROKER
// Draw line strips for hairline.
style.Apply(rconfig.GetRenderer());
UInt j, index;
GRenderer::CacheProvider cp(&CoordsCache);
rconfig.GetRenderer()->SetVertexData(&Coords[0], (int)(Coords.GetSize() / 2), GRenderer::Vertex_XY16i, &cp);
for(index = 0, j = 0; j<CoordCounts.GetSize(); j++)
{
rconfig.GetRenderer()->DrawLineStrip(index >> 1, (CoordCounts[j] >> 1) - 1);
index += CoordCounts[j];
}
return false;
}
#ifndef GFC_NO_FXPLAYER_STROKER
// Do a bounds check/clamp on a point, displaying warning.
void GFxCachedStroke::BoundCheckPoint(GPointF *ppt, Float sx, Float sy)
{
GPointF &pt = *ppt;
#ifdef GFC_BUILD_DEBUG
if ((pt.x > 32767.0f) || (pt.y > 32767.0f) || (pt.y < -32768.0f) || (pt.x < -32768.0f))
{
if (!RangeOverflowWarned)
{
GFC_DEBUG_WARNING4(1,
"Stroker target range overflow: (x,y) = (%g,%g). (Sx,Sy) = (%g,%g)",
pt.x, pt.y, sx, sy);
RangeOverflowWarned = 1;
}
// This clamping should be faster for debug
if (pt.x > 32767.0f) pt.x = 32767.0f;
if (pt.x < -32768.0f) pt.x = -32768.0f;
if (pt.y > 32767.0f) pt.y = 32767.0f;
if (pt.y < -32768.0f) pt.y = -32768.0f;
}
#else
GUNUSED2(sx,sy);
// Safety clamping for the case above. CPU should have an fabsf() op.
if (fabsf(pt.x) > 32767.0f)
pt.x = (pt.x > 32767.0f) ? 32767.0f : -32768.0f;
if (fabsf(pt.y) > 32767.0f)
pt.y = (pt.y > 32767.0f) ? 32767.0f : -32768.0f;
#endif
}
#endif // #ifndef GFC_NO_FXPLAYER_STROKER
// Dump our data to *out.
void GFxCachedStroke::OutputCachedData(GFile* out)
{
out->WriteSInt32(Style);
GFx_WriteCoordArray(out, Coords);
}
// Slurp our data from *out.
void GFxCachedStroke::InputCachedData(GFile* in)
{
Style = in->ReadSInt32();
GFx_ReadCoordArray(in, &Coords);
}
UPInt GFxCachedStroke::GetNumBytes() const
{
UPInt numBytes = 0;
numBytes += Coords.GetNumBytes();
numBytes += CoordCounts.GetNumBytes();
#ifndef GFC_NO_FXPLAYER_STROKER
if (pCachedMesh)
numBytes += pCachedMesh->GetNumBytes();
numBytes += Vertices.GetNumBytes();
#endif
return numBytes;
}
//
// ***** GFxMeshSet
//
// Tessellate the shape's paths into a different GFxMesh for each fill style.
GFxMeshSet::GFxMeshSet(Float screenPixelSize, Float curveError, bool aaDisabled, bool optTriangles)
{
ScreenPixelSize = screenPixelSize;
PixelCurveError = curveError;
ScaleMultiplier = 1.0f;
ScaleMultiplierInv = 1.0f;
EdgeAADisabled = aaDisabled;
#ifndef GFC_NO_FXPLAYER_EDGEAA
EdgeAA = 0;
EdgeAAFailed = 0;
AllowCxformAddAlpha = 0;
#endif
OptimizeTriangles = optTriangles;
Locked = 0;
memset(KeyData, 0, sizeof(KeyData));
pInstance = 0;
pTexture9Grids = 0;
pImgAdjustMatrices = 0;
NumStrokes = 0;
}
GFxMeshSet::~GFxMeshSet()
{
VerticesCache.ReleaseData(GRenderer::Cached_Vertex);
FreeKeyData();
delete pTexture9Grids;
delete pImgAdjustMatrices;
}
void GFxMeshSet::AllocKeyData(UInt len)
{
if (len != KeyData[1])
{
FreeKeyData();
if (len > 2*sizeof(void*) - 2)
{
void* p = GHEAP_AUTO_ALLOC(this, len); // Was: GStatRG_MeshFill_Mem
memcpy(KeyData + sizeof(void*), &p, sizeof(void*));
}
KeyData[1] = UInt8(len);
}
}
void GFxMeshSet::FreeKeyData()
{
if (KeyData[1] > 2*sizeof(void*) - 2)
{
void* p;
memcpy(&p, KeyData + sizeof(void*), sizeof(void*));
GFREE(p);
}
}
UInt GFxMeshSet::GetMeshKeySize() const
{
return (KeyData[1] > 2*sizeof(void*) - 2) ? KeyData[1] : 0;
}
// Throw our meshes at the renderer.
void GFxMeshSet::Display(GFxDisplayParams& params, bool scale9Grid)
{
// #ifdef GFX_AMP_SERVER
// ScopeFunctionTimer displayTimer(params.Context.pStats, NativeCodeSwdHandle, Func_GFxMeshSet_Display, Amp_Profile_Level_Low);
// #endif
NumStrokes = 0;
GASSERT(PixelCurveError > 0);
GRenderer* prenderer = params.Context.GetRenderer();
// Setup transforms.
GRenderer::Matrix m(params.Mat);
m.PrependScaling(ScaleMultiplierInv);
prenderer->SetCxform(params.Cx);
// Determine if blendMode/cxform can cause forced blending.
bool canBlend = 0;
if (params.Blend > GRenderer::Blend_Layer)
canBlend = 1;
else if (params.Cx.M_[3][0] < 0.997f)
canBlend = 1;
UInt ishape, imesh, istrip;
bool needMeshData = 1;
// 5.0 is quarter of a pixel
Float strokeOffset = 5.0f / params.Context.GetPixelScale();
for (ishape = 0, imesh=0, istrip=0 ; ishape < Shapes.GetSize(); ishape++)
{
UInt i;
if (Shapes[ishape].Texture9GridIdx != ~0U)
{
//prenderer->SetMatrix(m);
GFxTexture9Grid& t9g = (*pTexture9Grids)[Shapes[ishape].Texture9GridIdx];
GASSERT(params.pFillStyles && t9g.FillStyleIdx < params.FillStylesNum);
t9g.Display(params.Context, params.pFillStyles[t9g.FillStyleIdx], m);
needMeshData = 1;
}
else
{
if (needMeshData)
{
GRenderer::CacheProvider cp(&VerticesCache);
if (Vertices.GetSize())
Vertices.ApplyToRenderer(prenderer, &cp);
else if (cp.GetCachedData(prenderer))
prenderer->SetVertexData(0, 0, GRenderer::Vertex_None, &cp);
prenderer->SetMatrix(m);
needMeshData = 0;
// Can technically destroy buffer if this is returned.
//if (cp.CanDiscardData())
//{
// Vertices.Clear();
// Set flag
//}
}
const GRenderer::Matrix* imgAdjust = 0;
if (pImgAdjustMatrices)
imgAdjust = &(*pImgAdjustMatrices)[0];
// Dump meshes into renderer, one GFxMesh per style.
for (i = 0; (i < Shapes[ishape].MeshCount) && (imesh < Meshes.GetSize()); i++, imesh++)
{
Meshes[imesh].Display(params.Context, params.FillStylesNum ? &params.pFillStyles[0] : 0,
(UInt)params.FillStylesNum,
Shapes[ishape].VertexStartIndex, Shapes[ishape].VertexCount,
ScaleMultiplierInv, imgAdjust,
IsUsingEdgeAA());
}
// Dump strokes/line-strips into renderer.
if (Shapes[ishape].StrokeCount)
{
// HACK: Quarter pixel translate for strokes.
// Flash studio puts all lines at pixel edges by default, which is wrong
// because it would cause them to be blurry. To adjusts for that, it snaps
// horizontal and vertical lines "up", causing incorrect shapes.
// Since we don't do that, adjust lines forward by quarter pixel.
// (No adjustment could would cause lines to disappear, while 0.5 adjustment
// may produce sand and bleed-through on the bottom-right of shapes).
GRenderer::Matrix strokeMat(m);
strokeMat.M_[0][2] += strokeOffset;
strokeMat.M_[1][2] += strokeOffset;
prenderer->SetMatrix(strokeMat);
for (i = 0; (i < Shapes[ishape].StrokeCount) && (istrip < Strokes.GetSize()); i++, istrip++)
{
UInt style = Strokes[istrip].GetStyle();
GASSERT(params.pLineStyles && style < params.LineStylesNum);
if (Strokes[istrip].Display(params.Context, params.pLineStyles[style], params.Mat,
ScaleMultiplier, PixelCurveError, GetAllowCxformAddAlpha(), canBlend,
scale9Grid))
{
++NumStrokes;
}
}
needMeshData = 1;
}
}
}
prenderer->SetVertexData(0, 0, GRenderer::Vertex_None);
prenderer->SetIndexData(0, 0, GRenderer::Index_None);
}
// Hints at bounds, before adding any shapes.
// Can be used to determine the scale precision multiplier.
void GFxMeshSet::SetShapeBounds(const GRectF &r, Float strokeExtent)
{
// Determine the maximum value of rectangle scale.
Float maxf = G_Max( G_Max(G_Abs(r.Left), G_Abs(r.Right)),
G_Max(G_Abs(r.Top), G_Abs(r.Bottom)) );
// Convert scale [0.0f - maxf] to [0.0f - 32767]
// Avoid divide by zero
if (maxf < 0.01f)
ScaleMultiplier = 1.0f;
else
{
// Correct: maxf += 2.0f + strokeExtent;
// MA - TBD
// NOTE: strokeExtent*2 is a fudge multiplier (should not be here)
// The reason for it is when the object scales disproportionately, while
// generating a correct stroke, inverse-transformed coordinates may produce
// a very wide stroke, which will go out of bounds in the *other* axis coordinate.
// A correct solution would be to use FP, to not do the inverse transform on
// stroke, or handle stroke separately from shape.
// MA: Should GetIntersectionMiterLimit be multiplied by width? What is it then?
Float edgeAAAdjust = 0.0f;
#ifndef GFC_NO_FXPLAYER_EDGEAA
edgeAAAdjust = GEdgeAA::GetIntersectionMiterLimit()*2;
#endif
maxf += G_Max(2000.0f, 50.0f + strokeExtent * 2 + edgeAAAdjust);
ScaleMultiplier = 32766.0f / maxf;
}
ScaleMultiplierInv = 1.0f / ScaleMultiplier;
}
void GFxMeshSet::AddTessellatedShape(const GCompoundShape &shape,
const GFxFillStyle* pfills,
UPInt fillsNum,
GFxDisplayContext* context,
const GFxRenderConfig& rconfig)
{
UInt i, j;
// Sub-shape we will produce.
MeshSubShape subShape;
GPtr<GFxRenderGen> rg;
if(context)
rg = context->pMeshCacheManager->GetRenderGen();
else
rg = *GNEW GFxRenderGen(GMemory::GetGlobalHeap());
subShape.Texture9GridIdx = ~0U;
// *** Tessellate a shape.
// Setup the tessellator for shape triangulation
rg->Tessellator.Clear();
rg->Tessellator.SetFillRule(shape.IsNonZeroFill() ? GTessellator::FillNonZero : GTessellator::FillEvenOdd);
// It all became redundant with Tessellator v2
//rg->Tessellator.SetReduceWinding(false);
//rg->Tessellator.SetProcessJunctions(false);
//rg->Tessellator.SetPreserveStyleCoherence(false);
// Setup optimization
rg->Tessellator.SetDirOptimization(true);
rg->Tessellator.SetCoherenceOptimization(OptimizeTriangles);
rg->Tessellator.SetMonotoneOptimization(OptimizeTriangles);
rg->Tessellator.SetPilotTriangulation(OptimizeTriangles);
// Add 1 to styles to conform to the existing policy.
// The tessellator uses zero-based styles, while the policy
// is to use minus-one-based.
rg->Tessellator.Monotonize(shape, 1);
rg->NumTessellatedShapes++;
// Right now, 65K limit
if (rg->Tessellator.GetNumVertices() > 0xFFFE)
{
GFC_DEBUG_WARNING(1, "GFxMeshSet::AddTessellatedShape failed - more then 65K vertices generated");
return;
}
// *** Generate and Add Meshes
// Sort the monotone pieces by their style
// in order to reduce the number of switches between colors
rg->Tessellator.SortMonotonesByStyle();
#ifdef GFC_NO_FXPLAYER_EDGEAA
GUNUSED3(pfills, fillsNum, rconfig);
#else
bool useEdgeAA = 0;
bool hasVF_CF32 = rconfig.HasVertexFormat(GRenderer::Vertex_XY16iCF32);
if (rconfig.IsUsingEdgeAA() && !EdgeAADisabled)
{
useEdgeAA = 1;
if (AllowCxformAddAlpha && rconfig.HasCxformAddAlpha() && !hasVF_CF32)
useEdgeAA = 0;
else if (!rconfig.IsEdgeAATextured())
for (i = 0; i < fillsNum; i++)
if (pfills[i].GetType() != GFxFill_Solid)
{
useEdgeAA = 0;
break;
}
}
if (useEdgeAA)
{
useEdgeAA = 0;
// Try to generate edge AA
// GFxEdgeAAGenerator aaGen(rg->Tessellator, pfills, fillsNum);
//rg->EdgeAA_Gen.Clear();
rg->VertexArray.Clear(); // GFxVertexArray newVertices(GMemory::GetHeapByAddress(this));
UInt meshCount = 0;
rg->EdgeAA_Gen.ProcessAndSortEdges(rg->Tessellator, pfills, fillsNum,
ScreenPixelSize * -0.5f, GEdgeAA::AA_AllEdges, 0);
GASSERT(rconfig.IsEdgeAATextured() || !rg->EdgeAA_Gen.IsUsingTextures());
// If over 65K vertices limit, can't use AA for now.
// Also textured EdgeAA must be supported by renderer.
if ((Vertices.GetSize() + rg->EdgeAA_Gen.GetVertexCount()) <= 0xFFFE)
{
if (Vertices.GetFormat() != GRenderer::Vertex_None)
{
rg->VertexArray.SetFormat(Vertices.GetFormat());
}
else
{
// If previous Vertices are empty, determine buffer format.
GRenderer::VertexFormat vf = GRenderer::Vertex_XY16iC32;
if (hasVF_CF32)
{
if (AllowCxformAddAlpha)
vf = GRenderer::Vertex_XY16iCF32;
else
{
for (UPInt i = 0; i < fillsNum; i++)
if (pfills[i].GetType() != 0)
{
vf = GRenderer::Vertex_XY16iCF32;
break;
}
}
// If in larger format, we support Add-Alpha by default.
if (vf == GRenderer::Vertex_XY16iCF32)
AllowCxformAddAlpha = 1;
}
rg->VertexArray.SetFormat(vf);
}
// Generate meshes and vertices.
if (!rg->EdgeAA_Gen.IsUsingTextures() || (rg->VertexArray.GetFormat() == GRenderer::Vertex_XY16iC32))
{
// Will always have solid vertices only.
Meshes.Resize(Meshes.GetSize()+1);
meshCount++;
rg->EdgeAA_Gen.GenerateSolidMesh(&rg->VertexArray, &Meshes.Back(), ScaleMultiplier);
}
else
{
GASSERT(rg->VertexArray.GetFormat() == GRenderer::Vertex_XY16iCF32);
// Can have solid or textured vertices. Both must go into a CF buffer.
meshCount = rg->EdgeAA_Gen.GenerateTexturedMeshes(&rg->VertexArray, &Meshes, ScaleMultiplier);
}
if (context)
{
// Tess statistics.
UInt count = 0;
for (count =0; count < meshCount; count++)
context->AddTessTriangles(Meshes[Meshes.GetSize()-1-count].GetTriangleCount());
}
// If new vertices can't use AA for now, restore meshes to original size.
if (Vertices.GetSize() + rg->VertexArray.GetSize() > 0xFFFE)
{
// Didn't fit in, restore to prev state.
Meshes.Resize(Meshes.GetSize() - meshCount);
}
else
{
// Size ok, add extra vertices.
subShape.VertexStartIndex = Vertices.GetSize();
subShape.VertexCount = rg->VertexArray.GetSize();
subShape.MeshCount = meshCount;
if (Vertices.GetFormat() == GRenderer::Vertex_None)
Vertices.SetFormat(rg->VertexArray.GetFormat());
Vertices.AppendVertices(rg->VertexArray);
// Done, use AA.
useEdgeAA = 1;
}
}
else
{
EdgeAAFailed = 1;
}
}
if (useEdgeAA)
EdgeAA = 1;
#endif // #ifndef GFC_NO_FXPLAYER_EDGEAA
// If we did not use EdgeAA, generate normal meshes
if (!IsUsingEdgeAA())
{
subShape.VertexStartIndex = Vertices.GetSize();
subShape.VertexCount = rg->Tessellator.GetNumVertices();
if (Vertices.GetFormat() == GRenderer::Vertex_None)
Vertices.SetFormat(GRenderer::Vertex_XY16i);
GASSERT(Vertices.GetFormat() == GRenderer::Vertex_XY16i);
Vertices.Resize(subShape.VertexStartIndex + subShape.VertexCount);
if (Vertices.GetSize() == subShape.VertexStartIndex + subShape.VertexCount)
{
for (i=0, j = subShape.VertexStartIndex; i<subShape.VertexCount; i++, j++)
{
const GPointType& pt = rg->Tessellator.GetVertex(i);
Vertices.GetVertexRef(j).InitVertex(pt.x * ScaleMultiplier, pt.y * ScaleMultiplier);
}
}
else
{
return;
}
// The main loop of emitting the triangles. Iterate
// through the monotone pieces and emit triangles.
for(i = 0; i < rg->Tessellator.GetNumMonotones(); i++)
{
// Get the info about the i-th monotone.
// Well, basically m.style is all you need.
const GTessellator::MonotoneType& m = rg->Tessellator.GetMonotone(i);
// The tessellator uses zero-based styles, so, make it minus-one-based
// to conform to the existing policy.
int style = (int)m.style - 1;
// Triangulate the i-th monotone polygon. After this call
// you can iterate through the triangles for this particular monotone.
rg->Tessellator.TriangulateMonotone(i);
// If same style add to the last mesh, otherwise, to new one.
if ((i == 0) || ((int)Meshes.Back().GetStyle() != style))
{
Meshes.Resize(Meshes.GetSize()+1);
subShape.MeshCount++;
}
Meshes.Back().AddTriangles(style, rg->Tessellator);
// Tess statistics.
if (context)
context->AddTessTriangles(rg->Tessellator.GetNumTriangles());
}
}
// *** Generate Path line strips
// Now the line part.
GArray<bool> pathsMatched;
pathsMatched.Resize(shape.GetNumPaths());
for(i = 0; i < shape.GetNumPaths(); i++)
pathsMatched[i] = 0;
for(i = 0; i < shape.GetNumPaths(); i++)
{
if (pathsMatched[i])
continue;
const GCompoundShape::SPath& p = shape.GetPath(i);
if(p.GetLineStyle() >= 0 && p.GetNumVertices() > 1)
{
// Add a line strip.
Strokes.Resize(Strokes.GetSize() + 1);
subShape.StrokeCount++;
// Copy the vertices from the path into the strip.
Strokes.Back().SetEdgeAADisabled(EdgeAADisabled);
Strokes.Back().AddShapePathVertices(shape, i, ScaleMultiplier);
// Here we accumulate all paths with the same style so that we can render
// them as a single whole stroke. It's not a caprice, it's the way
// Flash renders the strokes. That is, it combines all strokes
// with the same style in order to correctly rasterize overlapping
// lines. Otherwise the overlaps will be visible on translucent strokes.
//
// Combine other paths with the same style.
for(j = i+1; j < shape.GetNumPaths(); j++)
{
const GCompoundShape::SPath& p2 = shape.GetPath(j);
if(p2.GetLineStyle() == p.GetLineStyle())
{
// Mark so that we don't add this path again
pathsMatched[j] = 1;
if (p2.GetLineStyle() >= 0 && p2.GetNumVertices() > 1)
{
// Copy the vertices from the path into the strip.
Strokes.Back().AddShapePathVertices(shape, j, ScaleMultiplier);
}
}
}
}
}
// Add a sub-shape. This is significant to ensure that
// separate sub-shapes are drawn on top of each other.
Shapes.PushBack(subShape);
}
void GFxMeshSet::AddTexture9Grid(const GFxTexture9Grid& t9g)
{
MeshSubShape subShape;
if (pTexture9Grids == 0)
pTexture9Grids = GHEAP_AUTO_NEW(this) GArrayLH<GFxTexture9Grid>;
subShape.MeshCount = 0;
subShape.StrokeCount = 0;
subShape.VertexStartIndex = 0;
subShape.VertexCount = 0;
subShape.Texture9GridIdx = (UInt)pTexture9Grids->GetSize();
pTexture9Grids->PushBack(t9g);
Shapes.PushBack(subShape);
#ifndef GFC_NO_FXPLAYER_EDGEAA
EdgeAA = !EdgeAADisabled;
#endif
}
// Dump our data to the output GFxStream.
void GFxMeshSet::OutputCachedData(GFile* out)
{
out->WriteFloat(ScreenPixelSize);
UInt i;
UInt meshCount = (UInt)Meshes.GetSize();
out->WriteUInt32(meshCount);
for (i = 0; i < meshCount; i++)
{
Meshes[i].OutputCachedData(out);
}
UInt lineCount = (UInt)Strokes.GetSize();
out->WriteUInt32(lineCount);
for (i = 0; i < lineCount; i++)
{
Strokes[i].OutputCachedData(out);
}
UInt shapeCount = (UInt)Shapes.GetSize();
out->WriteSInt32(shapeCount);
for (i = 0; i < shapeCount; i++)
{
out->WriteUInt32(Shapes[i].MeshCount);
out->WriteUInt32(Shapes[i].StrokeCount);
}
}
// Grab our data from the input GFxStream.
void GFxMeshSet::InputCachedData(GFile* in)
{
ScreenPixelSize = in->ReadFloat();
UInt i;
UInt meshCount = in->ReadUInt32();
Meshes.Resize(meshCount);
for (i = 0; i < meshCount; i++)
{
Meshes[i].InputCachedData(in);
}
UInt lineCount = in->ReadUInt32();
Strokes.Resize(lineCount);
for (i = 0; i < lineCount; i++)
{
Strokes[i].InputCachedData(in);
}
UInt shapeCount = in->ReadUInt32();
Shapes.Resize(shapeCount);
for (i = 0; i < shapeCount; i++)
{
Shapes[i].MeshCount = in->ReadUInt32();
Shapes[i].StrokeCount = in->ReadUInt32();
}
}
void GFxMeshSet::SetMeshKey(KeyCategoryType keyCat, const void* keyData, UInt len, GFxCharacter* inst)
{
KeyData[0] = (UInt8)keyCat;
pInstance = inst;
AllocKeyData(len);
if (len <= 2*sizeof(void*)-2)
{
memcpy(KeyData + 2, keyData, len);
}
else
{
void* p;
memcpy(&p, KeyData + sizeof(void*), sizeof(void*));
memcpy(p, keyData, len);
}
}
bool GFxMeshSet::MeshKeyFits(KeyCategoryType keyCat, const void* keyData, UInt len) const
{
if (keyCat != KeyData[0] || len != KeyData[1])
return false;
if (len <= 2*sizeof(void*)-2)
return !memcmp(keyData, KeyData + 2, len);
void* p;
memcpy(&p, KeyData + sizeof(void*), sizeof(void*));
return !memcmp(p, keyData, len);
}
void GFxMeshSet::SetImgAdjustMatrices(const GArray<GFxScale9GridInfo::ImgAdjust>& adj)
{
if (pImgAdjustMatrices == 0)
pImgAdjustMatrices = GHEAP_AUTO_NEW(this) GArrayLH<GRenderer::Matrix>;
pImgAdjustMatrices->Resize(adj.GetSize());
for (UInt i = 0; i < adj.GetSize(); ++i)
(*pImgAdjustMatrices)[i] = adj[i].Matrix;
}
UPInt GFxMeshSet::GetNumBytes() const
{
UPInt numBytes = 0;
numBytes += Meshes.GetNumBytes();
numBytes += Strokes.GetNumBytes();
numBytes += Vertices.GetNumBytes();
numBytes += Shapes.GetNumBytes();
numBytes += GetMeshKeySize();
if (pTexture9Grids)
numBytes += pTexture9Grids->GetNumBytes();
if (pImgAdjustMatrices)
numBytes += pImgAdjustMatrices->GetNumBytes();
UPInt i;
for(i = 0; i < Meshes.GetSize(); ++i)
numBytes += Meshes[i].GetNumBytes();
for(i = 0; i < Strokes.GetSize(); ++i)
numBytes += Strokes[i].GetNumBytes();
return numBytes;
}
UInt32 GFxMeshSet::GetNumStrokes() const
{
// UInt32 numStrokes = 0;
// for (UPInt i = 0; i < Shapes.GetSize(); ++i)
// {
// numStrokes += Shapes[i].StrokeCount;
// }
// return numStrokes;
return NumStrokes;
}