@@ -64,17 +64,13 @@ void Game::Draw2DImageQuad(irr::video::IVideoDriver* driver, irr::video::ITextur
6464void Game::DrawSelectionLine (irr::video::S3DVertex* vec, bool stipple, irr::video::SColor color) {
6565 static const int edgeStart[] = { 0 , 1 , 3 , 2 };
6666 static const int edgeEnd[] = { 1 , 3 , 2 , 0 };
67- driver->setMaterial (matManager.mOutLine );
6867 const irr::core::matrix4 oldWorld = driver->getTransform (irr::video::ETS_WORLD );
6968 irr::core::vector3df wp[4 ];
7069 for (int i = 0 ; i < 4 ; ++i)
7170 oldWorld.transformVect (wp[i], vec[i].Pos );
7271 const irr::core::matrix4& view = driver->getTransform (irr::video::ETS_VIEW );
7372 irr::core::matrix4 pv = driver->getTransform (irr::video::ETS_PROJECTION );
7473 pv *= view;
75- irr::core::matrix4 invPv;
76- if (!pv.getInverse (invPv))
77- return ;
7874 const auto sz = driver->getCurrentRenderTargetSize ();
7975 if (sz.Width == 0 || sz.Height == 0 )
8076 return ;
@@ -83,6 +79,11 @@ void Game::DrawSelectionLine(irr::video::S3DVertex* vec, bool stipple, irr::vide
8379 const irr::f32 yScale = sz.Height / static_cast <irr::f32 >(GAME_WINDOW_HEIGHT );
8480 const irr::f32 pixelScale = (xScale < yScale) ? xScale : yScale;
8581 const irr::f32 halfThick = matManager.mOutLine .Thickness * pixelScale * 0 .2f ;
82+ if (halfThick <= 0 .0f )
83+ return ;
84+ irr::core::matrix4 invPv;
85+ if (!pv.getInverse (invPv))
86+ return ;
8687 struct ProjectedPoint {
8788 irr::core::vector2df screen;
8889 irr::f32 ndcZ;
@@ -105,39 +106,26 @@ void Game::DrawSelectionLine(irr::video::S3DVertex* vec, bool stipple, irr::vide
105106 const irr::f32 invW = 1 .0f / point[3 ];
106107 return irr::core::vector3df (point[0 ] * invW, point[1 ] * invW, point[2 ] * invW);
107108 };
108- std::vector<irr::video::S3DVertex> vertices;
109- std::vector<irr::u16 > indices;
110- vertices.reserve (128 );
111- indices.reserve (240 );
112109 constexpr size_t THICK_LINE_VERTICES_PER_SEGMENT = 8 ;
113- constexpr size_t MAX_THICK_LINE_BATCH_VERTICES = (size_t {1 } << (sizeof (irr::u16 ) * 8 )) - THICK_LINE_VERTICES_PER_SEGMENT ;
114- const auto drawBatch = [&]() {
115- if (indices.empty ())
116- return ;
117- driver->drawVertexPrimitiveList (vertices.data (), static_cast <irr::u32 >(vertices.size ()),
118- indices.data (), static_cast <irr::u32 >(indices.size () / 3 ),
119- irr::video::EVT_STANDARD , irr::scene::EPT_TRIANGLES );
120- vertices.clear ();
121- indices.clear ();
110+ constexpr size_t THICK_LINE_INDICES_PER_SEGMENT = 30 ;
111+ const auto lerpProjected = [](const ProjectedPoint& start, const ProjectedPoint& end, irr::f32 t) {
112+ ProjectedPoint result;
113+ result.screen = start.screen + (end.screen - start.screen ) * t;
114+ result.ndcZ = start.ndcZ + (end.ndcZ - start.ndcZ ) * t;
115+ return result;
122116 };
123- const auto appendThickLine = [&](const irr::core::vector3df& start, const irr::core::vector3df& end) {
124- if (halfThick <= 0 .0f ) return ;
125- ProjectedPoint ps, pe;
126- if (!projectPoint (start, ps) || !projectPoint (end, pe))
127- return ;
117+ const auto buildThickLine = [&](const ProjectedPoint& ps, const ProjectedPoint& pe,
118+ irr::video::S3DVertex* outVertices, irr::u16 * outIndices, irr::u16 base) {
128119 const auto screenDirection = pe.screen - ps.screen ;
129120 const irr::f32 screenLenSq = screenDirection.getLengthSQ ();
130- if (screenLenSq < 1e-4f ) return ;
131- if (vertices.size () > MAX_THICK_LINE_BATCH_VERTICES )
132- drawBatch ();
121+ if (screenLenSq < 1e-4f ) return false ;
133122 const irr::f32 invScreenLen = 1 .0f / std::sqrt (screenLenSq);
134123 const irr::core::vector2df dir = screenDirection * invScreenLen;
135124 const irr::core::vector2df perp (-dir.Y , dir.X );
136125 const irr::f32 feather = halfThick * 2 .0f ;
137126 const auto innerPerp = perp * halfThick;
138127 const auto outerPerp = perp * (halfThick + feather);
139128 const auto endFeather = dir * feather;
140- const auto base = static_cast <irr::u16 >(vertices.size ());
141129 static const irr::u16 idx[30 ] = {
142130 0 , 1 , 3 , 0 , 3 , 2 ,
143131 4 , 5 , 0 , 0 , 5 , 1 ,
@@ -148,55 +136,130 @@ void Game::DrawSelectionLine(irr::video::S3DVertex* vec, bool stipple, irr::vide
148136 const irr::core::vector3df normal (0 .0f , 0 .0f , -1 .0f );
149137 irr::video::SColor transparent = color;
150138 transparent.setAlpha (0 );
151- vertices.emplace_back (unprojectScreen (ps.screen + innerPerp, ps.ndcZ ), normal, color, irr::core::vector2df ());
152- vertices.emplace_back (unprojectScreen (pe.screen + innerPerp, pe.ndcZ ), normal, color, irr::core::vector2df ());
153- vertices.emplace_back (unprojectScreen (ps.screen - innerPerp, ps.ndcZ ), normal, color, irr::core::vector2df ());
154- vertices.emplace_back (unprojectScreen (pe.screen - innerPerp, pe.ndcZ ), normal, color, irr::core::vector2df ());
155- vertices.emplace_back (unprojectScreen (ps.screen - endFeather + outerPerp, ps.ndcZ ), normal, transparent, irr::core::vector2df ());
156- vertices.emplace_back (unprojectScreen (pe.screen + endFeather + outerPerp, pe.ndcZ ), normal, transparent, irr::core::vector2df ());
157- vertices.emplace_back (unprojectScreen (pe.screen + endFeather - outerPerp, pe.ndcZ ), normal, transparent, irr::core::vector2df ());
158- vertices.emplace_back (unprojectScreen (ps.screen - endFeather - outerPerp, ps.ndcZ ), normal, transparent, irr::core::vector2df ());
159- for (int i = 0 ; i < 30 ; ++i)
160- indices.push_back (base + idx[i]);
139+ outVertices[0 ] = irr::video::S3DVertex (unprojectScreen (ps.screen + innerPerp, ps.ndcZ ), normal, color, irr::core::vector2df ());
140+ outVertices[1 ] = irr::video::S3DVertex (unprojectScreen (pe.screen + innerPerp, pe.ndcZ ), normal, color, irr::core::vector2df ());
141+ outVertices[2 ] = irr::video::S3DVertex (unprojectScreen (ps.screen - innerPerp, ps.ndcZ ), normal, color, irr::core::vector2df ());
142+ outVertices[3 ] = irr::video::S3DVertex (unprojectScreen (pe.screen - innerPerp, pe.ndcZ ), normal, color, irr::core::vector2df ());
143+ outVertices[4 ] = irr::video::S3DVertex (unprojectScreen (ps.screen - endFeather + outerPerp, ps.ndcZ ), normal, transparent, irr::core::vector2df ());
144+ outVertices[5 ] = irr::video::S3DVertex (unprojectScreen (pe.screen + endFeather + outerPerp, pe.ndcZ ), normal, transparent, irr::core::vector2df ());
145+ outVertices[6 ] = irr::video::S3DVertex (unprojectScreen (pe.screen + endFeather - outerPerp, pe.ndcZ ), normal, transparent, irr::core::vector2df ());
146+ outVertices[7 ] = irr::video::S3DVertex (unprojectScreen (ps.screen - endFeather - outerPerp, ps.ndcZ ), normal, transparent, irr::core::vector2df ());
147+ for (size_t i = 0 ; i < THICK_LINE_INDICES_PER_SEGMENT ; ++i)
148+ outIndices[i] = base + idx[i];
149+ return true ;
161150 };
151+ driver->setMaterial (matManager.mOutLine );
162152 driver->setTransform (irr::video::ETS_WORLD , irr::core::matrix4 ());
153+ const auto drawFixedSegments = [&](const auto & getSegment) {
154+ irr::video::S3DVertex vertices[4 * THICK_LINE_VERTICES_PER_SEGMENT ];
155+ irr::u16 indices[4 * THICK_LINE_INDICES_PER_SEGMENT ];
156+ size_t vertexCount = 0 ;
157+ size_t indexCount = 0 ;
158+ for (int i = 0 ; i < 4 ; ++i) {
159+ ProjectedPoint ps, pe;
160+ if (!getSegment (i, ps, pe))
161+ continue ;
162+ if (buildThickLine (ps, pe, vertices + vertexCount, indices + indexCount, static_cast <irr::u16 >(vertexCount))) {
163+ vertexCount += THICK_LINE_VERTICES_PER_SEGMENT ;
164+ indexCount += THICK_LINE_INDICES_PER_SEGMENT ;
165+ }
166+ }
167+ if (indexCount)
168+ driver->drawVertexPrimitiveList (vertices, static_cast <irr::u32 >(vertexCount),
169+ indices, static_cast <irr::u32 >(indexCount / 3 ),
170+ irr::video::EVT_STANDARD , irr::scene::EPT_TRIANGLES );
171+ };
163172 if (!stipple) {
164- for (int i = 0 ; i < 4 ; ++i)
165- appendThickLine (wp[edgeStart[i]], wp[edgeEnd[i]]);
173+ drawFixedSegments ([&](int i, ProjectedPoint& ps, ProjectedPoint& pe) {
174+ return projectPoint (wp[edgeStart[i]], ps) && projectPoint (wp[edgeEnd[i]], pe);
175+ });
166176 } else if (gameConf.solid_selection_line ) {
167177 const irr::f32 t0 = (linePattern < 15 ) ? 0 .0f : (linePattern - 14 ) / 15 .0f ;
168178 const irr::f32 t1 = (linePattern < 15 ) ? (linePattern + 1 ) / 15 .0f : 1 .0f ;
169- for ( int i = 0 ; i < 4 ; ++i ) {
179+ drawFixedSegments ([&]( int i, ProjectedPoint& ps, ProjectedPoint& pe ) {
170180 const auto & s = wp[edgeStart[i]];
171181 const auto d = wp[edgeEnd[i]] - s;
172- appendThickLine (s + d * t0, s + d * t1);
173- }
182+ return projectPoint (s + d * t0, ps) && projectPoint ( s + d * t1, pe );
183+ });
174184 } else {
185+ constexpr size_t MAX_THICK_LINE_BATCH_VERTICES = size_t {1 } << (sizeof (irr::u16 ) * 8 );
186+ struct ProjectedEdge {
187+ ProjectedPoint start;
188+ ProjectedPoint end;
189+ irr::f32 screenLen;
190+ bool projected;
191+ };
192+ ProjectedEdge projectedEdges[4 ];
193+ irr::f32 totalProjectedLen = 0 .0f ;
194+ for (int i = 0 ; i < 4 ; ++i) {
195+ auto & edge = projectedEdges[i];
196+ edge.projected = projectPoint (wp[edgeStart[i]], edge.start ) && projectPoint (wp[edgeEnd[i]], edge.end );
197+ edge.screenLen = 1 .0f ;
198+ if (edge.projected ) {
199+ edge.screenLen = std::sqrt ((edge.start .screen - edge.end .screen ).getLengthSQ ());
200+ totalProjectedLen += edge.screenLen ;
201+ }
202+ }
203+ const size_t estimatedSegments = static_cast <size_t >(totalProjectedLen * 0 .5f ) + 4 ;
204+ size_t reserveVertices = estimatedSegments * THICK_LINE_VERTICES_PER_SEGMENT ;
205+ if (reserveVertices > MAX_THICK_LINE_BATCH_VERTICES )
206+ reserveVertices = MAX_THICK_LINE_BATCH_VERTICES ;
207+ std::vector<irr::video::S3DVertex> vertices;
208+ std::vector<irr::u16 > indices;
209+ vertices.reserve (reserveVertices);
210+ indices.reserve ((reserveVertices / THICK_LINE_VERTICES_PER_SEGMENT ) * THICK_LINE_INDICES_PER_SEGMENT );
211+ const auto drawBatch = [&]() {
212+ if (indices.empty ())
213+ return ;
214+ driver->drawVertexPrimitiveList (vertices.data (), static_cast <irr::u32 >(vertices.size ()),
215+ indices.data (), static_cast <irr::u32 >(indices.size () / 3 ),
216+ irr::video::EVT_STANDARD , irr::scene::EPT_TRIANGLES );
217+ vertices.clear ();
218+ indices.clear ();
219+ };
220+ const auto appendProjectedThickLine = [&](const ProjectedPoint& ps, const ProjectedPoint& pe) {
221+ if (vertices.size () + THICK_LINE_VERTICES_PER_SEGMENT > MAX_THICK_LINE_BATCH_VERTICES )
222+ drawBatch ();
223+ const auto base = static_cast <irr::u16 >(vertices.size ());
224+ const size_t oldVertexCount = vertices.size ();
225+ const size_t oldIndexCount = indices.size ();
226+ vertices.resize (oldVertexCount + THICK_LINE_VERTICES_PER_SEGMENT );
227+ indices.resize (oldIndexCount + THICK_LINE_INDICES_PER_SEGMENT );
228+ if (!buildThickLine (ps, pe, vertices.data () + oldVertexCount, indices.data () + oldIndexCount, base)) {
229+ vertices.resize (oldVertexCount);
230+ indices.resize (oldIndexCount);
231+ }
232+ };
175233 // Project edge endpoints to screen space to get pixel length for pattern tiling.
176234 // (1 pattern bit = 1 screen pixel, matching the original GL stipple behaviour.)
177235 irr::f32 patternCursor = 0 .0f ;
178236 for (int i = 0 ; i < 4 ; ++i) {
179- const auto & s = wp[edgeStart[i]];
180- const auto d = wp[edgeEnd[i]] - s;
181- ProjectedPoint ps, pe;
182- irr::f32 screenLen = 1 .0f ;
183- if (projectPoint (s, ps) && projectPoint (s + d, pe))
184- screenLen = std::sqrt ((ps.screen - pe.screen ).getLengthSQ ());
237+ const auto & edge = projectedEdges[i];
238+ const irr::f32 screenLen = edge.screenLen ;
185239 for (irr::f32 cursor = 0 .0f ; cursor < screenLen; ) {
186240 const int bit = static_cast <int >(patternCursor + cursor) & 0xf ;
187- if (!((stippleMask >> bit) & 1 )) { cursor += 1 .0f ; continue ; }
241+ if (!((stippleMask >> bit) & 1 )) {
242+ irr::f32 runEnd = cursor + 1 .0f ;
243+ while (runEnd < screenLen && !((stippleMask >> (static_cast <int >(patternCursor + runEnd) & 0xf )) & 1 ))
244+ runEnd += 1 .0f ;
245+ cursor = runEnd;
246+ continue ;
247+ }
188248 irr::f32 runEnd = cursor + 1 .0f ;
189249 while (runEnd < screenLen && ((stippleMask >> (static_cast <int >(patternCursor + runEnd) & 0xf )) & 1 ))
190250 runEnd += 1 .0f ;
191251 if (runEnd > screenLen)
192252 runEnd = screenLen;
193- appendThickLine (s + d * (cursor / screenLen), s + d * (runEnd / screenLen));
253+ if (edge.projected ) {
254+ appendProjectedThickLine (lerpProjected (edge.start , edge.end , cursor / screenLen),
255+ lerpProjected (edge.start , edge.end , runEnd / screenLen));
256+ }
194257 cursor = runEnd;
195258 }
196259 patternCursor = std::fmod (patternCursor + screenLen, 16 .0f );
197260 }
261+ drawBatch ();
198262 }
199- drawBatch ();
200263 driver->setTransform (irr::video::ETS_WORLD , oldWorld);
201264}
202265void Game::DrawSelectionLine (irr::gui::IGUIElement* element, int width, irr::video::SColor color) {
0 commit comments