Ticket #1027: 1027_atlas_brush_04mar12.patch

File 1027_atlas_brush_04mar12.patch, 32.4 KB (added by vts, 15 months ago)

Updated; forgot to change last-modified dates on files

  • source/maths/BoundingBoxAligned.cpp

    diff --git a/source/maths/BoundingBoxAligned.cpp b/source/maths/BoundingBoxAligned.cpp
    index 2e0e706..80c25ef 100644
    a b  
    1 /* Copyright (C) 2011 Wildfire Games. 
     1/* Copyright (C) 2012 Wildfire Games. 
    22 * This file is part of 0 A.D. 
    33 * 
    44 * 0 A.D. is free software: you can redistribute it and/or modify 
    void CBoundingBoxAligned::Transform(const CMatrix3D& transform, CBoundingBoxOrie 
    217217// Intersect with the given frustum in a conservative manner 
    218218void CBoundingBoxAligned::IntersectFrustumConservative(const CFrustum& frustum) 
    219219{ 
     220    // if this bound is empty, it cannot be meaningfully intersected with a frustum 
     221    // see http://trac.wildfiregames.com/ticket/1027 
     222    ENSURE(!IsEmpty()); 
     223 
    220224    CBrush brush(*this); 
    221225    CBrush buf; 
    222226 
  • source/maths/BoundingBoxAligned.h

    diff --git a/source/maths/BoundingBoxAligned.h b/source/maths/BoundingBoxAligned.h
    index 43f0a5b..df83af6 100644
    a b public: 
    126126     * 
    127127     * @note While not in the spirit of this function's purpose, a no-op would be a correct 
    128128     * implementation of this function. 
     129     * @note Must not be called if this bound is empty 
    129130     * 
    130131     * @param frustum the frustum to intersect with 
    131132     */ 
  • source/maths/Brush.cpp

    diff --git a/source/maths/Brush.cpp b/source/maths/Brush.cpp
    index d6909cf..bd06280 100644
    a b  
    1 /* Copyright (C) 2009 Wildfire Games. 
     1/* Copyright (C) 2012 Wildfire Games. 
    22 * This file is part of 0 A.D. 
    33 * 
    44 * 0 A.D. is free software: you can redistribute it and/or modify 
    CBrush::CBrush(const CBoundingBoxAligned& bounds) 
    3838 
    3939    for(size_t i = 0; i < 8; ++i) 
    4040    { 
    41         m_Vertices[i][0] = bounds[(i & 1) ? 1 : 0][0]; 
    42         m_Vertices[i][1] = bounds[(i & 2) ? 1 : 0][1]; 
    43         m_Vertices[i][2] = bounds[(i & 4) ? 1 : 0][2]; 
     41        m_Vertices[i][0] = bounds[(i & 1) ? 1 : 0][0]; // X 
     42        m_Vertices[i][1] = bounds[(i & 2) ? 1 : 0][1]; // Y 
     43        m_Vertices[i][2] = bounds[(i & 4) ? 1 : 0][2]; // Z 
    4444    } 
    4545 
     46    // construct cube face indices, 5 vertex indices per face (start vertex included twice) 
     47 
    4648    m_Faces.resize(30); 
    4749 
    4850    m_Faces[0] = 0; m_Faces[1] = 1; m_Faces[2] = 3; m_Faces[3] = 2; m_Faces[4] = 0; // Z = min 
    void CBrush::Bounds(CBoundingBoxAligned& result) const 
    6971 
    7072/////////////////////////////////////////////////////////////////////////////// 
    7173// Cut the brush according to a given plane 
    72 struct SliceVertexInfo { 
    73     float d; // distance 
    74     size_t res; // index in result brush (or no_vertex if cut away) 
    75 }; 
    7674 
    77 struct NewVertexInfo { 
    78     size_t v1, v2; // adjacent vertices in original brush 
    79     size_t res; // index in result brush 
     75/// Holds information about what happens to a single vertex in a brush during a slicing operation. 
     76struct SliceOpVertexInfo 
     77{ 
     78    float planeDist;    ///< Signed distance from this vertex to the slicing plane. 
     79    size_t resIdx;      ///< Index of this vertex in the resulting brush (or NO_VERTEX if cut away) 
     80}; 
    8081 
    81     size_t neighb1, neighb2; // index into newv 
     82/// Holds information about a newly introduced vertex on an edge in a brush as the result of a slicing operation. 
     83struct SliceOpNewVertexInfo 
     84{ 
     85    /// Indices of adjacent edge vertices in original brush 
     86    size_t edgeIdx1, edgeIdx2; 
     87    /// Index of newly introduced vertex in resulting brush 
     88    size_t resIdx; 
     89    /// Index into SliceOpInfo.nvInfo; hold the indices of this new vertex's direct neighbours in the slicing plane face, 
     90    /// with no consistent winding direction around the face for either field (e.g., the neighb1 of X can point back to 
     91    /// X with either its neighb1 or neighb2). 
     92    size_t neighbIdx1, neighbIdx2; 
    8293}; 
    8394 
    84 struct SliceInfo { 
    85     std::vector<SliceVertexInfo> v; 
    86     std::vector<NewVertexInfo> newv; 
    87     size_t thisFaceNewVertex; // index into newv 
    88     const CBrush* original; 
     95/// Holds support information during a CBrush/CPlane slicing operation. 
     96struct SliceOpInfo 
     97{ 
    8998    CBrush* result; 
     99    const CBrush* original; 
     100 
     101    /// Holds information about what happens to each vertex in the original brush after the slice operation. 
     102    /// Same size as m_Vertices of the brush getting sliced. 
     103    std::vector<SliceOpVertexInfo> ovInfo; 
     104    /// Holds information about newly inserted vertices during a slice operation. 
     105    std::vector<SliceOpNewVertexInfo> nvInfo; 
     106    /// Indices into nvInfo; during the execution of the slicing algorithm, holds the previously inserted new vertex on  
     107    /// one of the edges of the face that's currently being evaluated for slice points, or NO_VERTEX if no such vertex  
     108    /// exists. 
     109    size_t thisFaceNewVertexIdx; 
    90110}; 
    91111 
    92112struct CBrush::Helper 
    93113{ 
    94     static size_t SliceNewVertex(SliceInfo& si, size_t v1, size_t v2); 
     114    /// Creates a new vertex between the given two vertices (indexed into the original brush). 
     115    /// Returns the index of the new vertex in the resulting brush. 
     116    static size_t SliceNewVertex(SliceOpInfo& sliceInfo, size_t v1, size_t v2); 
    95117}; 
    96118 
    97 // create a new vertex between the given two vertices (index into original brush) 
    98 // returns the index of the new vertex in the resulting brush 
    99 size_t CBrush::Helper::SliceNewVertex(SliceInfo& si, size_t v1, size_t v2) 
     119size_t CBrush::Helper::SliceNewVertex(SliceOpInfo& sliceOp, size_t edgeIdx1, size_t edgeIdx2) 
    100120{ 
     121    // check if a new vertex has already been inserted on this edge 
    101122    size_t idx; 
    102  
    103     for(idx = 0; idx < si.newv.size(); ++idx) 
     123    for(idx = 0; idx < sliceOp.nvInfo.size(); ++idx) 
    104124    { 
    105         if ((si.newv[idx].v1 == v1 && si.newv[idx].v2 == v2) || 
    106             (si.newv[idx].v1 == v2 && si.newv[idx].v2 == v1)) 
     125        if ((sliceOp.nvInfo[idx].edgeIdx1 == edgeIdx1 && sliceOp.nvInfo[idx].edgeIdx2 == edgeIdx2) || 
     126            (sliceOp.nvInfo[idx].edgeIdx1 == edgeIdx2 && sliceOp.nvInfo[idx].edgeIdx2 == edgeIdx1)) 
    107127            break; 
    108128    } 
    109129 
    110     if (idx >= si.newv.size()) 
     130    if (idx >= sliceOp.nvInfo.size()) 
    111131    { 
    112         NewVertexInfo nvi; 
    113         CVector3D newpos; 
    114         float inv = 1.0 / (si.v[v1].d - si.v[v2].d); 
    115  
    116         newpos = si.original->m_Vertices[v2]*(si.v[v1].d*inv) + 
    117              si.original->m_Vertices[v1]*(-si.v[v2].d*inv); 
    118  
    119         nvi.v1 = v1; 
    120         nvi.v2 = v2; 
    121         nvi.res = si.result->m_Vertices.size(); 
    122         nvi.neighb1 = no_vertex; 
    123         nvi.neighb2 = no_vertex; 
    124         si.result->m_Vertices.push_back(newpos); 
    125         si.newv.push_back(nvi); 
     132        // no previously inserted new vertex found on this edge; insert a new one 
     133        SliceOpNewVertexInfo nvi; 
     134        CVector3D newPos; 
     135         
     136        // interpolate between the two vertices based on their distance from the plane 
     137        float inv = 1.0 / (sliceOp.ovInfo[edgeIdx1].planeDist - sliceOp.ovInfo[edgeIdx2].planeDist); 
     138        newPos = sliceOp.original->m_Vertices[edgeIdx2] * ( sliceOp.ovInfo[edgeIdx1].planeDist * inv) + 
     139                 sliceOp.original->m_Vertices[edgeIdx1] * (-sliceOp.ovInfo[edgeIdx2].planeDist * inv); 
     140 
     141        nvi.edgeIdx1 = edgeIdx1; 
     142        nvi.edgeIdx2 = edgeIdx2; 
     143        nvi.resIdx = sliceOp.result->m_Vertices.size(); 
     144        nvi.neighbIdx1 = NO_VERTEX; 
     145        nvi.neighbIdx2 = NO_VERTEX; 
     146 
     147        sliceOp.result->m_Vertices.push_back(newPos); 
     148        sliceOp.nvInfo.push_back(nvi); 
    126149    } 
    127150 
    128     if (si.thisFaceNewVertex != no_vertex) 
     151    // at this point, 'idx' is the index into nvInfo of the vertex inserted onto the edge 
     152 
     153    if (sliceOp.thisFaceNewVertexIdx != NO_VERTEX) 
    129154    { 
    130         if (si.newv[si.thisFaceNewVertex].neighb1 == no_vertex) 
    131             si.newv[si.thisFaceNewVertex].neighb1 = idx; 
     155        // a vertex has been previously inserted onto another edge of this face; link them together as neighbours 
     156        // (using whichever one of the neighbIdx1 or -2 links is still available) 
     157 
     158        if (sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx1 == NO_VERTEX) 
     159            sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx1 = idx; 
    132160        else 
    133             si.newv[si.thisFaceNewVertex].neighb2 = idx; 
     161            sliceOp.nvInfo[sliceOp.thisFaceNewVertexIdx].neighbIdx2 = idx; 
    134162 
    135         if (si.newv[idx].neighb1 == no_vertex) 
    136             si.newv[idx].neighb1 = si.thisFaceNewVertex; 
     163        if (sliceOp.nvInfo[idx].neighbIdx1 == NO_VERTEX) 
     164            sliceOp.nvInfo[idx].neighbIdx1 = sliceOp.thisFaceNewVertexIdx; 
    137165        else 
    138             si.newv[idx].neighb2 = si.thisFaceNewVertex; 
     166            sliceOp.nvInfo[idx].neighbIdx2 = sliceOp.thisFaceNewVertexIdx; 
    139167 
    140         si.thisFaceNewVertex = no_vertex; 
     168        // a plane should slice a face only in two locations, so reset for the next face 
     169        sliceOp.thisFaceNewVertexIdx = NO_VERTEX; 
    141170    } 
    142171    else 
    143172    { 
    144         si.thisFaceNewVertex = idx; 
     173        // store the index of the inserted vertex on this edge, so that we can retrieve it when the plane slices 
     174        // this face again in another edge 
     175        sliceOp.thisFaceNewVertexIdx = idx; 
    145176    } 
    146177 
    147     return si.newv[idx].res; 
     178    return sliceOp.nvInfo[idx].resIdx; 
    148179} 
    149180 
    150181void CBrush::Slice(const CPlane& plane, CBrush& result) const 
    151182{ 
    152183    ENSURE(&result != this); 
    153184 
    154     SliceInfo si; 
     185    SliceOpInfo sliceOp; 
    155186 
    156     si.original = this; 
    157     si.result = &result; 
    158     si.thisFaceNewVertex = no_vertex; 
    159     si.newv.reserve(m_Vertices.size() / 2); 
     187    sliceOp.original = this; 
     188    sliceOp.result = &result; 
     189    sliceOp.thisFaceNewVertexIdx = NO_VERTEX; 
     190    sliceOp.ovInfo.resize(m_Vertices.size()); 
     191    sliceOp.nvInfo.reserve(m_Vertices.size() / 2); 
    160192 
    161193    result.m_Vertices.resize(0); // clear any left-overs 
    162194    result.m_Faces.resize(0); 
    163195    result.m_Vertices.reserve(m_Vertices.size() + 2); 
    164196    result.m_Faces.reserve(m_Faces.size() + 5); 
    165197 
    166     // Classify and copy vertices 
    167     si.v.resize(m_Vertices.size()); 
    168  
     198    // Copy vertices that weren't sliced away by the plane to the resulting brush. 
    169199    for(size_t i = 0; i < m_Vertices.size(); ++i) 
    170200    { 
    171         si.v[i].d = plane.DistanceToPlane(m_Vertices[i]); 
    172         if (si.v[i].d >= 0.0) 
     201        const CVector3D& vtx = m_Vertices[i];            // current vertex 
     202        SliceOpVertexInfo& vtxInfo = sliceOp.ovInfo[i];  // slicing operation info about current vertex 
     203 
     204        vtxInfo.planeDist = plane.DistanceToPlane(vtx); 
     205        if (vtxInfo.planeDist >= 0.0) 
    173206        { 
    174             si.v[i].res = result.m_Vertices.size(); 
    175             result.m_Vertices.push_back(m_Vertices[i]); 
     207            // positive side of the plane; not sliced away 
     208            vtxInfo.resIdx = result.m_Vertices.size(); 
     209            result.m_Vertices.push_back(vtx); 
    176210        } 
    177211        else 
    178212        { 
    179             si.v[i].res = no_vertex; 
     213            // other side of the plane; sliced away 
     214            vtxInfo.resIdx = NO_VERTEX; 
    180215        } 
    181216    } 
    182217 
    183     // Transfer faces 
    184     size_t firstInFace = no_vertex; // in original brush 
    185     size_t startInResultFaceArray = ~0u; 
     218    // Transfer faces. (Recall how faces are specified; see CBrush::m_Faces). The idea is to examine each face separately, 
     219    // and see where its edges cross the slicing plane (meaning that exactly one of the vertices of that edge was cut away). 
     220    // On those edges, new vertices are introduced where the edge intersects the plane, and the resulting brush's m_Faces  
     221    // array is updated to refer to the newly inserted vertices instead of the original one that got cut away. 
     222 
     223    size_t currentFaceStartIdx = NO_VERTEX; // index of the first vertex of the current face in the original brush 
     224    size_t resultFaceStartIdx = NO_VERTEX;  // index of the first vertex of the current face in the resulting brush 
    186225 
    187226    for(size_t i = 0; i < m_Faces.size(); ++i) 
    188227    { 
    189         if (firstInFace == no_vertex) 
     228        if (currentFaceStartIdx == NO_VERTEX) 
    190229        { 
    191             ENSURE(si.thisFaceNewVertex == no_vertex); 
     230            // starting a new face 
     231            ENSURE(sliceOp.thisFaceNewVertexIdx == NO_VERTEX); 
    192232 
    193             firstInFace = m_Faces[i]; 
    194             startInResultFaceArray = result.m_Faces.size(); 
     233            currentFaceStartIdx = m_Faces[i]; 
     234            resultFaceStartIdx = result.m_Faces.size(); 
    195235            continue; 
    196236        } 
    197237 
    198         size_t prev = m_Faces[i-1]; 
    199         size_t cur = m_Faces[i]; 
     238        size_t prevIdx = m_Faces[i-1];  // index of previous vertex in this face list 
     239        size_t curIdx = m_Faces[i];     // index of current vertex in this face list 
    200240 
    201         if (si.v[prev].res == no_vertex) 
     241        if (sliceOp.ovInfo[prevIdx].resIdx == NO_VERTEX) 
    202242        { 
    203             if (si.v[cur].res != no_vertex) 
     243            // previous face vertex got sliced away by the plane; see if the edge (prev,current) crosses the slicing plane 
     244            if (sliceOp.ovInfo[curIdx].resIdx != NO_VERTEX) 
    204245            { 
    205                 // re-entering the front side of the plane 
    206                 result.m_Faces.push_back(Helper::SliceNewVertex(si, prev, cur)); 
    207                 result.m_Faces.push_back(si.v[cur].res); 
     246                // re-entering the front side of the plane; insert vertex on intersection of plane and (prev,current) edge 
     247                result.m_Faces.push_back(Helper::SliceNewVertex(sliceOp, prevIdx, curIdx)); 
     248                result.m_Faces.push_back(sliceOp.ovInfo[curIdx].resIdx); 
    208249            } 
    209250        } 
    210251        else 
    211252        { 
    212             if (si.v[cur].res != no_vertex) 
     253            // previous face vertex didn't get sliced away; see if the edge (prev,current) crosses the slicing plane 
     254            if (sliceOp.ovInfo[curIdx].resIdx != NO_VERTEX) 
    213255            { 
    214                 // perfectly normal edge 
    215                 result.m_Faces.push_back(si.v[cur].res); 
     256                // perfectly normal edge; doesn't cross the plane 
     257                result.m_Faces.push_back(sliceOp.ovInfo[curIdx].resIdx); 
    216258            } 
    217259            else 
    218260            { 
    219                 // leaving the front side of the plane 
    220                 result.m_Faces.push_back(Helper::SliceNewVertex(si, prev, cur)); 
     261                // leaving the front side of the plane; insert vertex on intersection of plane and edge (prev, current) 
     262                result.m_Faces.push_back(Helper::SliceNewVertex(sliceOp, prevIdx, curIdx)); 
    221263            } 
    222264        } 
    223265 
    224         if (cur == firstInFace) 
     266        // if we're back at the first vertex of the current face, then we've completed the face 
     267        if (curIdx == currentFaceStartIdx) 
    225268        { 
    226             if (result.m_Faces.size() > startInResultFaceArray) 
    227                 result.m_Faces.push_back(result.m_Faces[startInResultFaceArray]); 
    228             firstInFace = no_vertex; // start a new face 
     269            // close the index loop 
     270            if (result.m_Faces.size() > resultFaceStartIdx) 
     271                result.m_Faces.push_back(result.m_Faces[resultFaceStartIdx]); 
     272 
     273            currentFaceStartIdx = NO_VERTEX; // start a new face 
    229274        } 
    230275    } 
    231276 
    232     ENSURE(firstInFace == no_vertex); 
     277    ENSURE(currentFaceStartIdx == NO_VERTEX); 
     278 
     279    // Create the face that lies in the slicing plane. Remember, all the intersections of the slicing plane with face 
     280    // edges of the brush have been stored in sliceOp.nvInfo by the SliceNewVertex function, and refer to their direct  
     281    // neighbours in the slicing plane face using the neighbIdx1 and neighbIdx2 fields (in no consistent winding order). 
    233282 
    234     // Create the face that lies in the slicing plane 
    235     if (si.newv.size()) 
     283    if (sliceOp.nvInfo.size()) 
    236284    { 
    237         size_t prev = 0; 
     285        // push the starting vertex 
     286        result.m_Faces.push_back(sliceOp.nvInfo[0].resIdx); 
     287         
     288        // At this point, there is no consistent winding order in the neighbX fields, so at each vertex we need to figure  
     289        // out whether neighb1 or neighb2 points 'onwards' along the face, according to an initially chosen winding direction. 
     290        // (or, equivalently, which one points back to the one we were just at). At each vertex, we then set neighb1 to be the  
     291        // one to point onwards, deleting any pointers which we no longer need to complete the trace. 
     292 
    238293        size_t idx; 
     294        size_t prev = 0; 
    239295 
    240         result.m_Faces.push_back(si.newv[0].res); 
    241         idx = si.newv[0].neighb2; 
    242         si.newv[0].neighb2 = no_vertex; 
     296        idx = sliceOp.nvInfo[0].neighbIdx2; // pick arbitrary starting direction 
     297        sliceOp.nvInfo[0].neighbIdx2 = NO_VERTEX; 
    243298 
    244299        while(idx != 0) 
    245300        { 
    246             ENSURE(idx < si.newv.size()); 
    247             if (idx >= si.newv.size()) 
     301            ENSURE(idx < sliceOp.nvInfo.size()); 
     302            if (idx >= sliceOp.nvInfo.size()) 
    248303                break; 
    249304 
    250             if (si.newv[idx].neighb1 == prev) 
     305            if (sliceOp.nvInfo[idx].neighbIdx1 == prev) 
    251306            { 
    252                 si.newv[idx].neighb1 = si.newv[idx].neighb2; 
    253                 si.newv[idx].neighb2 = no_vertex; 
     307                // neighb1 is pointing the wrong way; we want to normalize it to point onwards in the direction 
     308                // we initially chose, so swap it with neighb2 and delete neighb2 (no longer needed) 
     309                sliceOp.nvInfo[idx].neighbIdx1 = sliceOp.nvInfo[idx].neighbIdx2; 
     310                sliceOp.nvInfo[idx].neighbIdx2 = NO_VERTEX; 
    254311            } 
    255312            else 
    256313            { 
    257                 ENSURE(si.newv[idx].neighb2 == prev); 
    258  
    259                 si.newv[idx].neighb2 = no_vertex; 
     314                // neighb1 isn't pointing to the previous vertex, so neighb2 must be (otherwise a pair of vertices failed to 
     315                // get paired properly during face/plane slicing). 
     316                ENSURE(sliceOp.nvInfo[idx].neighbIdx2 == prev); 
     317                sliceOp.nvInfo[idx].neighbIdx2 = NO_VERTEX; 
    260318            } 
    261319 
    262             result.m_Faces.push_back(si.newv[idx].res); 
     320            result.m_Faces.push_back(sliceOp.nvInfo[idx].resIdx); 
    263321 
     322            // move to next vertex; neighb1 has been normalized to point onward 
    264323            prev = idx; 
    265             idx = si.newv[idx].neighb1; 
    266             si.newv[prev].neighb1 = no_vertex; 
     324            idx = sliceOp.nvInfo[idx].neighbIdx1; 
     325            sliceOp.nvInfo[prev].neighbIdx1 = NO_VERTEX; // no longer needed, we've moved on 
    267326        } 
    268327 
    269         result.m_Faces.push_back(si.newv[0].res); 
     328        // push starting vertex again to close the shape 
     329        result.m_Faces.push_back(sliceOp.nvInfo[0].resIdx); 
    270330    } 
    271331} 
    272332 
    273333 
     334 
    274335/////////////////////////////////////////////////////////////////////////////// 
    275336// Intersect with frustum by repeated slicing 
    276337void CBrush::Intersect(const CFrustum& frustum, CBrush& result) const 
    void CBrush::Intersect(const CFrustum& frustum, CBrush& result) const 
    287348    const CBrush* prev = this; 
    288349    CBrush* next; 
    289350 
     351    // Repeatedly slice this brush with each plane of the frustum, alternating between 'result' and 'buf' to  
     352    // save intermediate results. Set up the starting brush so that the final version always ends up in 'result'. 
     353 
    290354    if (frustum.GetNumPlanes() & 1) 
    291355        next = &result; 
    292356    else 
    void CBrush::Intersect(const CFrustum& frustum, CBrush& result) const 
    304368 
    305369    ENSURE(prev == &result); 
    306370} 
     371 No newline at end of file 
     372 
     373std::vector<CVector3D> CBrush::GetVertices() const  
     374{ 
     375    return m_Vertices; 
     376} 
     377 
     378void CBrush::GetFaces(std::vector<std::vector<size_t> >& out) const 
     379{ 
     380    // split the back-to-back faces into separate face vectors, so that they're in a  
     381    // user-friendlier format than the back-to-back vertex index array 
     382    // i.e. split 'x--xy------yz----z' into 'x--x', 'y-------y', 'z---z' 
     383 
     384    size_t faceStartIdx = 0; 
     385    while (faceStartIdx < m_Faces.size()) 
     386    { 
     387        // start new face 
     388        std::vector<size_t> singleFace; 
     389        singleFace.push_back(m_Faces[faceStartIdx]); 
     390 
     391        // step over all the values in the face until we hit the starting value again (which closes the face) 
     392        size_t j = faceStartIdx + 1; 
     393        while (j < m_Faces.size() && m_Faces[j] != m_Faces[faceStartIdx]) 
     394        { 
     395            singleFace.push_back(m_Faces[j]); 
     396            j++; 
     397        } 
     398 
     399        // each face must be closed by the same value that started it 
     400        ENSURE(m_Faces[faceStartIdx] == m_Faces[j]); 
     401 
     402        singleFace.push_back(m_Faces[j]); 
     403        out.push_back(singleFace); 
     404 
     405        faceStartIdx = j + 1; 
     406    } 
  • source/maths/Brush.h

    +}
    \ No newline at end of file
    diff --git a/source/maths/Brush.h b/source/maths/Brush.h
    index bc87383..26996d8 100644
    a b  
    1 /* Copyright (C) 2009 Wildfire Games. 
     1/* Copyright (C) 2012 Wildfire Games. 
    22 * This file is part of 0 A.D. 
    33 * 
    44 * 0 A.D. is free software: you can redistribute it and/or modify 
    public: 
    5959    void Bounds(CBoundingBoxAligned& result) const; 
    6060 
    6161    /** 
    62      * Slice: Cut the object along the given plane, resulting in a smaller (or even empty) 
    63      * brush representing the part of the object that lies in front of the plane. 
     62     * Slice: Cut the object along the given plane, resulting in a smaller (or even empty) brush representing  
     63     * the part of the object that lies in front of the plane (as defined by the positive direction of its  
     64     * normal vector). 
    6465     * 
    6566     * @param plane the slicing plane 
    6667     * @param result the resulting brush is stored here 
    public: 
    7576     */ 
    7677    void Intersect(const CFrustum& frustum, CBrush& result) const; 
    7778 
     79    /** 
     80     * Returns a copy of the vertices in this brush. Intended for testing purposes; you should not need to use 
     81     * this method directly. 
     82     */ 
     83    std::vector<CVector3D> GetVertices() const; 
     84 
     85    /** 
     86     * Writes a vector of the faces in this brush to @p out. Each face is itself a vector, listing the vertex indices  
     87     * that make up the face, starting and ending with the same index. Intended for testing purposes; you should not  
     88     * need to use this method directly. 
     89     */ 
     90    void GetFaces(std::vector<std::vector<size_t> >& out) const; 
     91 
    7892private: 
    79     static const size_t no_vertex = ~0u; 
     93    static const size_t NO_VERTEX = ~0u; 
    8094 
    8195    typedef std::vector<CVector3D> Vertices; 
    8296    typedef std::vector<size_t> FaceIndices; 
    8397 
     98    /// Collection of vertices that make up this shape. 
    8499    Vertices m_Vertices; 
     100 
     101    /// Holds the face definitions of this brush. Each face is a sequence of indices into m_Vertices that starts and ends with  
     102    /// the same vertex index, completing a loop through all the vertices that make up the face. This vector holds all the face 
     103    /// sequences back-to-back, thus looking something like 'x---xy--------yz--z' in the general case. 
    85104    FaceIndices m_Faces; 
    86105 
    87106    struct Helper; 
  • source/maths/tests/test_Bound.h

    diff --git a/source/maths/tests/test_Bound.h b/source/maths/tests/test_Bound.h
    index 54d3c73..282f54f 100644
    a b  
    1 /* Copyright (C) 2009 Wildfire Games. 
     1/* Copyright (C) 2012 Wildfire Games. 
    22 * This file is part of 0 A.D. 
    33 * 
    44 * 0 A.D. is free software: you can redistribute it and/or modify 
     
    1818#include "lib/self_test.h" 
    1919 
    2020#include "maths/BoundingBoxAligned.h" 
     21#include "maths/BoundingBoxOriented.h" 
    2122 
    2223class TestBound : public CxxTest::TestSuite  
    2324{ 
    2425public: 
    25     void test_empty() 
     26    void test_empty_aabb() 
    2627    { 
    2728        CBoundingBoxAligned bound; 
    2829        TS_ASSERT(bound.IsEmpty()); 
    public: 
    3233        TS_ASSERT(bound.IsEmpty()); 
    3334    } 
    3435 
     36    void test_empty_obb() 
     37    { 
     38        CBoundingBoxOriented bound; 
     39        TS_ASSERT(bound.IsEmpty()); 
     40        bound.m_Basis[0] = CVector3D(1,0,0); 
     41        bound.m_Basis[1] = CVector3D(0,1,0); 
     42        bound.m_Basis[2] = CVector3D(0,0,1); 
     43        bound.m_HalfSizes = CVector3D(1,2,3); 
     44        TS_ASSERT(!bound.IsEmpty()); 
     45        bound.SetEmpty(); 
     46        TS_ASSERT(bound.IsEmpty()); 
     47    } 
     48 
    3549    void test_extend_vector() 
    3650    { 
    3751        CBoundingBoxAligned bound; 
  • new file source/maths/tests/test_Brush.h

    diff --git a/source/maths/tests/test_Brush.h b/source/maths/tests/test_Brush.h
    new file mode 100644
    index 0000000..ffbe840
    - +  
     1/* Copyright (C) 2012 Wildfire Games. 
     2 * This file is part of 0 A.D. 
     3 * 
     4 * 0 A.D. is free software: you can redistribute it and/or modify 
     5 * it under the terms of the GNU General Public License as published by 
     6 * the Free Software Foundation, either version 2 of the License, or 
     7 * (at your option) any later version. 
     8 * 
     9 * 0 A.D. is distributed in the hope that it will be useful, 
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     12 * GNU General Public License for more details. 
     13 * 
     14 * You should have received a copy of the GNU General Public License 
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>. 
     16 */ 
     17 
     18#include "lib/self_test.h" 
     19 
     20#include "maths/Brush.h" 
     21#include "maths/BoundingBoxAligned.h" 
     22#include "graphics/Frustum.h" 
     23 
     24class TestBrush : public CxxTest::TestSuite  
     25{ 
     26public: 
     27    void setUp() 
     28    { 
     29        CxxTest::setAbortTestOnFail(true); 
     30    } 
     31 
     32    void tearDown() 
     33    { 
     34 
     35    } 
     36 
     37    void test_slice_plane_simple() 
     38    { 
     39        // slice a 1x1x1 cube vertically down the middle at z = 0.5, with the plane normal pointing towards the negative 
     40        // end of the Z axis (i.e., anything with Z lower than 0.5 is considered 'in front of' the plane and is kept) 
     41        CPlane plane(CVector4D(0, 0, -1, 0.5f)); 
     42        CBrush brush(CBoundingBoxAligned(CVector3D(0,0,0), CVector3D(1,1,1))); 
     43 
     44        CBrush result; 
     45        brush.Slice(plane, result); 
     46 
     47        // verify that the resulting brush consists of exactly our 8 expected, unique vertices 
     48        TS_ASSERT_EQUALS(8, result.GetVertices().size()); 
     49        size_t LBF = GetUniqueVertexIndex(result, CVector3D(0, 0, 0)); // left-bottom-front <=> XYZ 
     50        size_t RBF = GetUniqueVertexIndex(result, CVector3D(1, 0, 0)); // right-bottom-front 
     51        size_t RBB = GetUniqueVertexIndex(result, CVector3D(1, 0, 0.5f)); // right-bottom-back 
     52        size_t LBB = GetUniqueVertexIndex(result, CVector3D(0, 0, 0.5f)); // etc. 
     53        size_t LTF = GetUniqueVertexIndex(result, CVector3D(0, 1, 0)); 
     54        size_t RTF = GetUniqueVertexIndex(result, CVector3D(1, 1, 0)); 
     55        size_t RTB = GetUniqueVertexIndex(result, CVector3D(1, 1, 0.5f)); 
     56        size_t LTB = GetUniqueVertexIndex(result, CVector3D(0, 1, 0.5f)); 
     57 
     58        // verify that the brush contains the six expected planes (one of which is the slicing plane) 
     59        VerifyFacePresent(result, 5, LBF, RBF, RBB, LBB, LBF); // bottom face 
     60        VerifyFacePresent(result, 5, LTF, RTF, RTB, LTB, LTF); // top face 
     61        VerifyFacePresent(result, 5, LBF, LBB, LTB, LTF, LBF); // left face 
     62        VerifyFacePresent(result, 5, RBF, RBB, RTB, RTF, RBF); // right face 
     63        VerifyFacePresent(result, 5, LBF, RBF, RTF, LTF, LBF); // front face 
     64        VerifyFacePresent(result, 5, LBB, RBB, RTB, LTB, LBB); // back face 
     65    } 
     66 
     67private: 
     68    size_t GetUniqueVertexIndex(const CBrush& brush, const CVector3D& vertex, float eps = 1e-6f) 
     69    { 
     70        std::vector<CVector3D> vertices = brush.GetVertices(); 
     71 
     72        for (size_t i = 0; i < vertices.size(); ++i) 
     73        { 
     74            const CVector3D& v = vertices[i]; 
     75            if (fabs(v.X - vertex.X) < eps 
     76                && fabs(v.Y - vertex.Y) < eps 
     77                && fabs(v.Z - vertex.Z) < eps) 
     78                return i; 
     79        } 
     80 
     81        TS_FAIL("Vertex not found in brush"); 
     82        return ~0u; 
     83    } 
     84 
     85    void VerifyFacePresent(const CBrush& brush, int count, ...) 
     86    { 
     87        std::vector<size_t> face; 
     88 
     89        va_list args; 
     90        va_start(args, count); 
     91        for (int x = 0; x < count; ++x) 
     92            face.push_back(va_arg(args, size_t)); 
     93        va_end(args); 
     94 
     95        if (face.size() == 0) 
     96            return; 
     97 
     98        std::vector<std::vector<size_t> > faces; 
     99        brush.GetFaces(faces); 
     100 
     101        // the brush is free to use any starting vertex along the face, and to use any winding order, so have 'face'  
     102        // cycle through various starting values and see if any of them (or their reverse) matches one found in the brush. 
     103         
     104        for (size_t c = 0; c < face.size() - 1; ++c) 
     105        { 
     106            std::vector<std::vector<size_t> >::iterator it1 = std::find(faces.begin(), faces.end(), face); 
     107            if (it1 != faces.end()) 
     108                return; 
     109             
     110            // no match, try the reverse 
     111            std::vector<size_t> faceReverse = face; 
     112            std::reverse(faceReverse.begin(), faceReverse.end()); 
     113            std::vector<std::vector<size_t> >::iterator it2 = std::find(faces.begin(), faces.end(), faceReverse); 
     114            if (it2 != faces.end()) 
     115                return; 
     116 
     117            // no match, cycle it 
     118            face.erase(face.begin()); 
     119            face.push_back(face[0]); 
     120        } 
     121 
     122        TS_FAIL("Face not found in brush"); 
     123    } 
     124}; 
  • source/renderer/ShadowMap.cpp

    diff --git a/source/renderer/ShadowMap.cpp b/source/renderer/ShadowMap.cpp
    index 779366d..30685ce 100644
    a b void ShadowMap::AddShadowedBound(const CBoundingBoxAligned& bounds) 
    218218// projection and transformation matrices 
    219219void ShadowMapInternals::CalcShadowMatrices() 
    220220{ 
    221     CRenderer& renderer = g_Renderer; 
    222  
    223     float minZ = ShadowBound[0].Z; 
    224  
    225     ShadowBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum()); 
    226  
    227     // round off the shadow boundaries to sane increments to help reduce swim effect 
    228     float boundInc = 16.0f; 
    229     ShadowBound[0].X = floor(ShadowBound[0].X / boundInc) * boundInc; 
    230     ShadowBound[0].Y = floor(ShadowBound[0].Y / boundInc) * boundInc; 
    231     ShadowBound[1].X = ceil(ShadowBound[1].X / boundInc) * boundInc; 
    232     ShadowBound[1].Y = ceil(ShadowBound[1].Y / boundInc) * boundInc; 
    233  
    234     // minimum Z bound must not be clipped too much, because objects that lie outside 
    235     // the shadow bounds cannot cast shadows either 
    236     // the 2.0 is rather arbitrary: it should be big enough so that we won't accidently miss 
    237     // a shadow generator, and small enough not to affect Z precision 
    238     ShadowBound[0].Z = minZ - 2.0; 
    239  
    240     // Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering 
    241     CVector3D scale = ShadowBound[1] - ShadowBound[0]; 
    242     CVector3D shift = (ShadowBound[1] + ShadowBound[0]) * -0.5; 
    243  
    244     if (scale.X < 1.0) 
    245         scale.X = 1.0; 
    246     if (scale.Y < 1.0) 
    247         scale.Y = 1.0; 
    248     if (scale.Z < 1.0) 
    249         scale.Z = 1.0; 
    250  
    251     scale.X = 2.0 / scale.X; 
    252     scale.Y = 2.0 / scale.Y; 
    253     scale.Z = 2.0 / scale.Z; 
    254  
    255     // make sure a given world position falls on a consistent shadowmap texel fractional offset 
    256     float offsetX = fmod(ShadowBound[0].X - LightTransform._14, 2.0f/(scale.X*EffectiveWidth)); 
    257     float offsetY = fmod(ShadowBound[0].Y - LightTransform._24, 2.0f/(scale.Y*EffectiveHeight)); 
    258  
    259     LightProjection.SetZero(); 
    260     LightProjection._11 = scale.X; 
    261     LightProjection._14 = (shift.X + offsetX) * scale.X; 
    262     LightProjection._22 = scale.Y; 
    263     LightProjection._24 = (shift.Y + offsetY) * scale.Y; 
    264     LightProjection._33 = scale.Z; 
    265     LightProjection._34 = shift.Z * scale.Z + renderer.m_ShadowZBias; 
    266     LightProjection._44 = 1.0; 
    267  
    268  
    269     // Calculate texture matrix by creating the clip space to texture coordinate matrix 
    270     // and then concatenating all matrices that have been calculated so far 
    271     CMatrix3D lightToTex; 
    272     float texscalex = scale.X * 0.5f * (float)EffectiveWidth / (float)Width; 
    273     float texscaley = scale.Y * 0.5f * (float)EffectiveHeight / (float)Height; 
    274     float texscalez = scale.Z * 0.5f; 
    275  
    276     lightToTex.SetZero(); 
    277     lightToTex._11 = texscalex; 
    278     lightToTex._14 = (offsetX - ShadowBound[0].X) * texscalex; 
    279     lightToTex._22 = texscaley; 
    280     lightToTex._24 = (offsetY - ShadowBound[0].Y) * texscaley; 
    281     lightToTex._33 = texscalez; 
    282     lightToTex._34 = -ShadowBound[0].Z * texscalez; 
    283     lightToTex._44 = 1.0; 
    284  
    285     TextureMatrix = lightToTex * LightTransform; 
     221    if (!ShadowBound.IsEmpty()) 
     222    { 
     223        CRenderer& renderer = g_Renderer; 
     224        float minZ = ShadowBound[0].Z; 
     225 
     226        ShadowBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum()); 
     227 
     228        // round off the shadow boundaries to sane increments to help reduce swim effect 
     229        float boundInc = 16.0f; 
     230        ShadowBound[0].X = floor(ShadowBound[0].X / boundInc) * boundInc; 
     231        ShadowBound[0].Y = floor(ShadowBound[0].Y / boundInc) * boundInc; 
     232        ShadowBound[1].X = ceil(ShadowBound[1].X / boundInc) * boundInc; 
     233        ShadowBound[1].Y = ceil(ShadowBound[1].Y / boundInc) * boundInc; 
     234 
     235        // minimum Z bound must not be clipped too much, because objects that lie outside 
     236        // the shadow bounds cannot cast shadows either 
     237        // the 2.0 is rather arbitrary: it should be big enough so that we won't accidently miss 
     238        // a shadow generator, and small enough not to affect Z precision 
     239        ShadowBound[0].Z = minZ - 2.0; 
     240 
     241        // Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering 
     242        CVector3D scale = ShadowBound[1] - ShadowBound[0]; 
     243        CVector3D shift = (ShadowBound[1] + ShadowBound[0]) * -0.5; 
     244 
     245        if (scale.X < 1.0) 
     246            scale.X = 1.0; 
     247        if (scale.Y < 1.0) 
     248            scale.Y = 1.0; 
     249        if (scale.Z < 1.0) 
     250            scale.Z = 1.0; 
     251 
     252        scale.X = 2.0 / scale.X; 
     253        scale.Y = 2.0 / scale.Y; 
     254        scale.Z = 2.0 / scale.Z; 
     255 
     256        // make sure a given world position falls on a consistent shadowmap texel fractional offset 
     257        float offsetX = fmod(ShadowBound[0].X - LightTransform._14, 2.0f/(scale.X*EffectiveWidth)); 
     258        float offsetY = fmod(ShadowBound[0].Y - LightTransform._24, 2.0f/(scale.Y*EffectiveHeight)); 
     259 
     260        LightProjection.SetZero(); 
     261        LightProjection._11 = scale.X; 
     262        LightProjection._14 = (shift.X + offsetX) * scale.X; 
     263        LightProjection._22 = scale.Y; 
     264        LightProjection._24 = (shift.Y + offsetY) * scale.Y; 
     265        LightProjection._33 = scale.Z; 
     266        LightProjection._34 = shift.Z * scale.Z + renderer.m_ShadowZBias; 
     267        LightProjection._44 = 1.0; 
     268 
     269        // Calculate texture matrix by creating the clip space to texture coordinate matrix 
     270        // and then concatenating all matrices that have been calculated so far 
     271 
     272        float texscalex = scale.X * 0.5f * (float)EffectiveWidth / (float)Width; 
     273        float texscaley = scale.Y * 0.5f * (float)EffectiveHeight / (float)Height; 
     274        float texscalez = scale.Z * 0.5f; 
     275 
     276        CMatrix3D lightToTex; 
     277        lightToTex.SetZero(); 
     278        lightToTex._11 = texscalex; 
     279        lightToTex._14 = (offsetX - ShadowBound[0].X) * texscalex; 
     280        lightToTex._22 = texscaley; 
     281        lightToTex._24 = (offsetY - ShadowBound[0].Y) * texscaley; 
     282        lightToTex._33 = texscalez; 
     283        lightToTex._34 = -ShadowBound[0].Z * texscalez; 
     284        lightToTex._44 = 1.0; 
     285 
     286        TextureMatrix = lightToTex * LightTransform; 
     287    } 
     288    else 
     289    { 
     290        // TODO: what are sensible no-op values here? 
     291        LightProjection.SetIdentity(); 
     292        TextureMatrix = LightTransform; 
     293    } 
    286294} 
    287295 
    288296