OcclusionBuffer.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2012 Lasse Oorni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "Camera.h"
  25. #include "Log.h"
  26. #include "OcclusionBuffer.h"
  27. #include <cstring>
  28. #include "DebugNew.h"
  29. namespace Urho3D
  30. {
  31. static const unsigned CLIPMASK_X_POS = 0x1;
  32. static const unsigned CLIPMASK_X_NEG = 0x2;
  33. static const unsigned CLIPMASK_Y_POS = 0x4;
  34. static const unsigned CLIPMASK_Y_NEG = 0x8;
  35. static const unsigned CLIPMASK_Z_POS = 0x10;
  36. static const unsigned CLIPMASK_Z_NEG = 0x20;
  37. OBJECTTYPESTATIC(OcclusionBuffer);
  38. OcclusionBuffer::OcclusionBuffer(Context* context) :
  39. Object(context),
  40. buffer_(0),
  41. width_(0),
  42. height_(0),
  43. numTriangles_(0),
  44. maxTriangles_(OCCLUSION_DEFAULT_MAX_TRIANGLES),
  45. cullMode_(CULL_CCW),
  46. depthHierarchyDirty_(true),
  47. nearClip_(0.0f),
  48. farClip_(0.0f)
  49. {
  50. }
  51. OcclusionBuffer::~OcclusionBuffer()
  52. {
  53. }
  54. bool OcclusionBuffer::SetSize(int width, int height)
  55. {
  56. // Force the height to an even amount of pixels for better mip generation
  57. if (height & 1)
  58. ++height;
  59. if (width == width_ && height == height_)
  60. return true;
  61. if (width <= 0 || height <= 0)
  62. return false;
  63. if (!IsPowerOfTwo(width))
  64. {
  65. LOGERROR("Width is not a power of two");
  66. return false;
  67. }
  68. width_ = width;
  69. height_ = height;
  70. // Reserve extra memory in case 3D clipping is not exact
  71. fullBuffer_ = new int[width * (height + 2) + 2];
  72. buffer_ = fullBuffer_.Get() + width + 1;
  73. mipBuffers_.Clear();
  74. // Build buffers for mip levels
  75. for (;;)
  76. {
  77. width = (width + 1) / 2;
  78. height = (height + 1) / 2;
  79. mipBuffers_.Push(SharedArrayPtr<DepthValue>(new DepthValue[width * height]));
  80. if (width <= OCCLUSION_MIN_SIZE && height <= OCCLUSION_MIN_SIZE)
  81. break;
  82. }
  83. LOGDEBUG("Set occlusion buffer size " + String(width_) + "x" + String(height_) + " with " +
  84. String(mipBuffers_.Size()) + " mip levels");
  85. CalculateViewport();
  86. return true;
  87. }
  88. void OcclusionBuffer::SetView(Camera* camera)
  89. {
  90. if (!camera)
  91. return;
  92. view_ = camera->GetInverseWorldTransform();
  93. projection_ = camera->GetProjection(false);
  94. viewProj_ = projection_ * view_;
  95. nearClip_ = camera->GetNearClip();
  96. farClip_ = camera->GetFarClip();
  97. CalculateViewport();
  98. }
  99. void OcclusionBuffer::SetMaxTriangles(unsigned triangles)
  100. {
  101. maxTriangles_ = triangles;
  102. }
  103. void OcclusionBuffer::SetCullMode(CullMode mode)
  104. {
  105. cullMode_ = mode;
  106. }
  107. void OcclusionBuffer::Reset()
  108. {
  109. numTriangles_ = 0;
  110. }
  111. void OcclusionBuffer::Clear()
  112. {
  113. if (!buffer_)
  114. return;
  115. Reset();
  116. int* dest = buffer_;
  117. int count = width_ * height_;
  118. while (count)
  119. {
  120. *dest++ = 0x7fffffff;
  121. --count;
  122. }
  123. depthHierarchyDirty_ = true;
  124. }
  125. bool OcclusionBuffer::Draw(const Matrix3x4& model, const void* vertexData, unsigned vertexSize, const void* indexData,
  126. unsigned indexSize, unsigned indexStart, unsigned indexCount)
  127. {
  128. const unsigned char* srcData = (const unsigned char*)vertexData;
  129. Matrix4 modelViewProj = viewProj_ * model;
  130. depthHierarchyDirty_ = true;
  131. // Theoretical max. amount of vertices if each of the 6 clipping planes doubles the triangle count
  132. Vector4 vertices[64 * 3];
  133. // 16-bit indices
  134. if (indexSize == sizeof(unsigned short))
  135. {
  136. const unsigned short* indices = ((const unsigned short*)indexData) + indexStart;
  137. const unsigned short* indicesEnd = indices + indexCount;
  138. while (indices < indicesEnd)
  139. {
  140. if (numTriangles_ >= maxTriangles_)
  141. return false;
  142. const Vector3& v0 = *((const Vector3*)(&srcData[indices[0] * vertexSize]));
  143. const Vector3& v1 = *((const Vector3*)(&srcData[indices[1] * vertexSize]));
  144. const Vector3& v2 = *((const Vector3*)(&srcData[indices[2] * vertexSize]));
  145. vertices[0] = ModelTransform(modelViewProj, v0);
  146. vertices[1] = ModelTransform(modelViewProj, v1);
  147. vertices[2] = ModelTransform(modelViewProj, v2);
  148. DrawTriangle(vertices);
  149. indices += 3;
  150. }
  151. }
  152. else
  153. {
  154. const unsigned* indices = ((const unsigned*)indexData) + indexStart;
  155. const unsigned* indicesEnd = indices + indexCount;
  156. while (indices < indicesEnd)
  157. {
  158. if (numTriangles_ >= maxTriangles_)
  159. return false;
  160. const Vector3& v0 = *((const Vector3*)(&srcData[indices[0] * vertexSize]));
  161. const Vector3& v1 = *((const Vector3*)(&srcData[indices[1] * vertexSize]));
  162. const Vector3& v2 = *((const Vector3*)(&srcData[indices[2] * vertexSize]));
  163. vertices[0] = ModelTransform(modelViewProj, v0);
  164. vertices[1] = ModelTransform(modelViewProj, v1);
  165. vertices[2] = ModelTransform(modelViewProj, v2);
  166. DrawTriangle(vertices);
  167. indices += 3;
  168. }
  169. }
  170. return true;
  171. }
  172. void OcclusionBuffer::BuildDepthHierarchy()
  173. {
  174. if (!buffer_)
  175. return;
  176. // Build the first mip level from the pixel-level data
  177. int width = (width_ + 1) / 2;
  178. int height = (height_ + 1) / 2;
  179. if (mipBuffers_.Size())
  180. {
  181. for (int y = 0; y < height; ++y)
  182. {
  183. int* src = buffer_ + (y * 2) * width_;
  184. DepthValue* dest = mipBuffers_[0].Get() + y * width;
  185. DepthValue* end = dest + width;
  186. if (y * 2 + 1 < height_)
  187. {
  188. int* src2 = src + width_;
  189. while (dest < end)
  190. {
  191. int minUpper = Min(src[0], src[1]);
  192. int minLower = Min(src2[0], src2[1]);
  193. dest->min_ = Min(minUpper, minLower);
  194. int maxUpper = Max(src[0], src[1]);
  195. int maxLower = Max(src2[0], src2[1]);
  196. dest->max_ = Max(maxUpper, maxLower);
  197. src += 2;
  198. src2 += 2;
  199. ++dest;
  200. }
  201. }
  202. else
  203. {
  204. while (dest < end)
  205. {
  206. dest->min_ = Min(src[0], src[1]);
  207. dest->max_ = Max(src[0], src[1]);
  208. src += 2;
  209. ++dest;
  210. }
  211. }
  212. }
  213. }
  214. // Build the rest of the mip levels
  215. for (unsigned i = 1; i < mipBuffers_.Size(); ++i)
  216. {
  217. int prevWidth = width;
  218. int prevHeight = height;
  219. width = (width + 1) / 2;
  220. height = (height + 1) / 2;
  221. for (int y = 0; y < height; ++y)
  222. {
  223. DepthValue* src = mipBuffers_[i - 1].Get() + (y * 2) * prevWidth;
  224. DepthValue* dest = mipBuffers_[i].Get() + y * width;
  225. DepthValue* end = dest + width;
  226. if (y * 2 + 1 < prevHeight)
  227. {
  228. DepthValue* src2 = src + prevWidth;
  229. while (dest < end)
  230. {
  231. int minUpper = Min(src[0].min_, src[1].min_);
  232. int minLower = Min(src2[0].min_, src2[1].min_);
  233. dest->min_ = Min(minUpper, minLower);
  234. int maxUpper = Max(src[0].max_, src[1].max_);
  235. int maxLower = Max(src2[0].max_, src2[1].max_);
  236. dest->max_ = Max(maxUpper, maxLower);
  237. src += 2;
  238. src2 += 2;
  239. ++dest;
  240. }
  241. }
  242. else
  243. {
  244. while (dest < end)
  245. {
  246. dest->min_ = Min(src[0].min_, src[1].min_);
  247. dest->max_ = Max(src[0].max_, src[1].max_);
  248. src += 2;
  249. ++dest;
  250. }
  251. }
  252. }
  253. }
  254. depthHierarchyDirty_ = false;
  255. }
  256. void OcclusionBuffer::ResetUseTimer()
  257. {
  258. useTimer_.Reset();
  259. }
  260. bool OcclusionBuffer::IsVisible(const BoundingBox& worldSpaceBox) const
  261. {
  262. if (!buffer_)
  263. return true;
  264. // Transform corners to projection space
  265. Vector4 vertices[8];
  266. vertices[0] = ModelTransform(viewProj_, worldSpaceBox.min_);
  267. vertices[1] = ModelTransform(viewProj_, Vector3(worldSpaceBox.max_.x_, worldSpaceBox.min_.y_, worldSpaceBox.min_.z_));
  268. vertices[2] = ModelTransform(viewProj_, Vector3(worldSpaceBox.min_.x_, worldSpaceBox.max_.y_, worldSpaceBox.min_.z_));
  269. vertices[3] = ModelTransform(viewProj_, Vector3(worldSpaceBox.max_.x_, worldSpaceBox.max_.y_, worldSpaceBox.min_.z_));
  270. vertices[4] = ModelTransform(viewProj_, Vector3(worldSpaceBox.min_.x_, worldSpaceBox.min_.y_, worldSpaceBox.max_.z_));
  271. vertices[5] = ModelTransform(viewProj_, Vector3(worldSpaceBox.max_.x_, worldSpaceBox.min_.y_, worldSpaceBox.max_.z_));
  272. vertices[6] = ModelTransform(viewProj_, Vector3(worldSpaceBox.min_.x_, worldSpaceBox.max_.y_, worldSpaceBox.max_.z_));
  273. vertices[7] = ModelTransform(viewProj_, worldSpaceBox.max_);
  274. // Apply a far clip relative bias
  275. for (unsigned i = 0; i < 8; ++i)
  276. vertices[i].z_ -= OCCLUSION_RELATIVE_BIAS;
  277. // Transform to screen space. If any of the corners cross the near plane, assume visible
  278. float minX, maxX, minY, maxY, minZ;
  279. if (vertices[0].z_ <= 0.0f)
  280. return true;
  281. Vector3 projected = ViewportTransform(vertices[0]);
  282. minX = maxX = projected.x_;
  283. minY = maxY = projected.y_;
  284. minZ = projected.z_;
  285. // Project the rest
  286. for (unsigned i = 1; i < 8; ++i)
  287. {
  288. if (vertices[i].z_ <= 0.0f)
  289. return true;
  290. projected = ViewportTransform(vertices[i]);
  291. if (projected.x_ < minX) minX = projected.x_;
  292. if (projected.x_ > maxX) maxX = projected.x_;
  293. if (projected.y_ < minY) minY = projected.y_;
  294. if (projected.y_ > maxY) maxY = projected.y_;
  295. if (projected.z_ < minZ) minZ = projected.z_;
  296. }
  297. // Expand the bounding box 1 pixel in each direction to be conservative and correct rasterization offset
  298. IntRect rect(
  299. (int)(minX - 1.5f), (int)(minY - 1.5f),
  300. (int)(maxX + 0.5f), (int)(maxY + 0.5f)
  301. );
  302. // If the rect is outside, let frustum culling handle
  303. if (rect.right_ < 0 || rect.bottom_ < 0)
  304. return true;
  305. if (rect.left_ >= width_ || rect.top_ >= height_)
  306. return true;
  307. // Clipping of rect
  308. if (rect.left_ < 0)
  309. rect.left_ = 0;
  310. if (rect.top_ < 0)
  311. rect.top_ = 0;
  312. if (rect.right_ >= width_)
  313. rect.right_ = width_ - 1;
  314. if (rect.bottom_ >= height_)
  315. rect.bottom_ = height_ - 1;
  316. // Convert depth to integer and apply final bias
  317. int z = (int)(minZ + 0.5f) - OCCLUSION_FIXED_BIAS;
  318. if (!depthHierarchyDirty_)
  319. {
  320. // Start from lowest mip level and check if a conclusive result can be found
  321. for (int i = mipBuffers_.Size() - 1; i >= 0; --i)
  322. {
  323. int shift = i + 1;
  324. int width = width_ >> shift;
  325. int left = rect.left_ >> shift;
  326. int right = rect.right_ >> shift;
  327. DepthValue* buffer = mipBuffers_[i].Get();
  328. DepthValue* row = buffer + (rect.top_ >> shift) * width;
  329. DepthValue* endRow = buffer + (rect.bottom_ >> shift) * width;
  330. bool allOccluded = true;
  331. while (row <= endRow)
  332. {
  333. DepthValue* src = row + left;
  334. DepthValue* end = row + right;
  335. while (src <= end)
  336. {
  337. if (z <= src->min_)
  338. return true;
  339. if (z <= src->max_)
  340. allOccluded = false;
  341. ++src;
  342. }
  343. row += width;
  344. }
  345. if (allOccluded)
  346. return false;
  347. }
  348. }
  349. // If no conclusive result, finally check the pixel-level data
  350. int* row = buffer_ + rect.top_ * width_;
  351. int* endRow = buffer_ + rect.bottom_ * width_;
  352. while (row <= endRow)
  353. {
  354. int* src = row + rect.left_;
  355. int* end = row + rect.right_;
  356. while (src <= end)
  357. {
  358. if (z <= *src)
  359. return true;
  360. ++src;
  361. }
  362. row += width_;
  363. }
  364. return false;
  365. }
  366. unsigned OcclusionBuffer::GetUseTimer()
  367. {
  368. return useTimer_.GetMSec(false);
  369. }
  370. inline Vector4 OcclusionBuffer::ModelTransform(const Matrix4& transform, const Vector3& vertex) const
  371. {
  372. return Vector4(
  373. transform.m00_ * vertex.x_ + transform.m01_ * vertex.y_ + transform.m02_ * vertex.z_ + transform.m03_,
  374. transform.m10_ * vertex.x_ + transform.m11_ * vertex.y_ + transform.m12_ * vertex.z_ + transform.m13_,
  375. transform.m20_ * vertex.x_ + transform.m21_ * vertex.y_ + transform.m22_ * vertex.z_ + transform.m23_,
  376. transform.m30_ * vertex.x_ + transform.m31_ * vertex.y_ + transform.m32_ * vertex.z_ + transform.m33_
  377. );
  378. }
  379. inline Vector3 OcclusionBuffer::ViewportTransform(const Vector4& vertex) const
  380. {
  381. float invW = 1.0f / vertex.w_;
  382. return Vector3(
  383. invW * vertex.x_ * scaleX_ + offsetX_,
  384. invW * vertex.y_ * scaleY_ + offsetY_,
  385. invW * vertex.z_ * OCCLUSION_Z_SCALE
  386. );
  387. }
  388. inline Vector4 OcclusionBuffer::ClipEdge(const Vector4& v0, const Vector4& v1, float d0, float d1) const
  389. {
  390. float t = d0 / (d0 - d1);
  391. return v0 + t * (v1 - v0);
  392. }
  393. inline bool OcclusionBuffer::CheckFacing(const Vector3& v0, const Vector3& v1, const Vector3& v2) const
  394. {
  395. if (cullMode_ == CULL_NONE)
  396. return true;
  397. float aX = v0.x_ - v1.x_;
  398. float aY = v0.y_ - v1.y_;
  399. float bX = v2.x_ - v1.x_;
  400. float bY = v2.y_ - v1.y_;
  401. float signedArea = aX * bY - aY * bX;
  402. if (cullMode_ == CULL_CCW)
  403. return signedArea < 0.0f;
  404. else
  405. return signedArea > 0.0f;
  406. }
  407. void OcclusionBuffer::CalculateViewport()
  408. {
  409. // Add half pixel offset due to 3D frustum culling
  410. scaleX_ = 0.5f * width_;
  411. scaleY_ = -0.5f * height_;
  412. offsetX_ = 0.5f * width_ + 0.5f;
  413. offsetY_ = 0.5f * height_ + 0.5f;
  414. projOffsetScaleX_ = projection_.m00_ * scaleX_;
  415. projOffsetScaleY_ = projection_.m11_ * scaleY_;
  416. }
  417. void OcclusionBuffer::DrawTriangle(Vector4* vertices)
  418. {
  419. unsigned clipMask = 0;
  420. unsigned andClipMask = 0;
  421. bool drawOk = false;
  422. Vector3 projected[3];
  423. // Build the clip plane mask for the triangle
  424. for (unsigned i = 0; i < 3; ++i)
  425. {
  426. unsigned vertexClipMask = 0;
  427. if (vertices[i].x_ > vertices[i].w_)
  428. vertexClipMask |= CLIPMASK_X_POS;
  429. if (vertices[i].x_ < -vertices[i].w_)
  430. vertexClipMask |= CLIPMASK_X_NEG;
  431. if (vertices[i].y_ > vertices[i].w_)
  432. vertexClipMask |= CLIPMASK_Y_POS;
  433. if (vertices[i].y_ < -vertices[i].w_)
  434. vertexClipMask |= CLIPMASK_Y_NEG;
  435. if (vertices[i].z_ > vertices[i].w_)
  436. vertexClipMask |= CLIPMASK_Z_POS;
  437. if (vertices[i].z_ < 0.0f)
  438. vertexClipMask |= CLIPMASK_Z_NEG;
  439. clipMask |= vertexClipMask;
  440. if (!i)
  441. andClipMask = vertexClipMask;
  442. else
  443. andClipMask &= vertexClipMask;
  444. }
  445. // If triangle is fully behind any clip plane, can reject quickly
  446. if (andClipMask)
  447. return;
  448. // Check if triangle is fully inside
  449. if (!clipMask)
  450. {
  451. projected[0] = ViewportTransform(vertices[0]);
  452. projected[1] = ViewportTransform(vertices[1]);
  453. projected[2] = ViewportTransform(vertices[2]);
  454. if (CheckFacing(projected[0], projected[1], projected[2]))
  455. {
  456. DrawTriangle2D(projected);
  457. drawOk = true;
  458. }
  459. }
  460. else
  461. {
  462. bool triangles[64];
  463. // Initial triangle
  464. triangles[0] = true;
  465. unsigned numTriangles = 1;
  466. if (clipMask & CLIPMASK_X_POS)
  467. ClipVertices(Vector4(-1.0f, 0.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  468. if (clipMask & CLIPMASK_X_NEG)
  469. ClipVertices(Vector4(1.0f, 0.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  470. if (clipMask & CLIPMASK_Y_POS)
  471. ClipVertices(Vector4(0.0f, -1.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  472. if (clipMask & CLIPMASK_Y_NEG)
  473. ClipVertices(Vector4(0.0f, 1.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  474. if (clipMask & CLIPMASK_Z_POS)
  475. ClipVertices(Vector4(0.0f, 0.0f, -1.0f, 1.0f), vertices, triangles, numTriangles);
  476. if (clipMask & CLIPMASK_Z_NEG)
  477. ClipVertices(Vector4(0.0f, 0.0f, 1.0f, 0.0f), vertices, triangles, numTriangles);
  478. // Draw each accepted triangle
  479. for (unsigned i = 0; i < numTriangles; ++i)
  480. {
  481. if (triangles[i])
  482. {
  483. unsigned index = i * 3;
  484. projected[0] = ViewportTransform(vertices[index]);
  485. projected[1] = ViewportTransform(vertices[index + 1]);
  486. projected[2] = ViewportTransform(vertices[index + 2]);
  487. if (CheckFacing(projected[0], projected[1], projected[2]))
  488. {
  489. DrawTriangle2D(projected);
  490. drawOk = true;
  491. }
  492. }
  493. }
  494. }
  495. if (drawOk)
  496. ++numTriangles_;
  497. }
  498. void OcclusionBuffer::ClipVertices(const Vector4& plane, Vector4* vertices, bool* triangles, unsigned& numTriangles)
  499. {
  500. unsigned num = numTriangles;
  501. for (unsigned i = 0; i < num; ++i)
  502. {
  503. if (triangles[i])
  504. {
  505. unsigned index = i * 3;
  506. float d0 = plane.DotProduct(vertices[index]);
  507. float d1 = plane.DotProduct(vertices[index + 1]);
  508. float d2 = plane.DotProduct(vertices[index + 2]);
  509. // If all vertices behind the plane, reject triangle
  510. if (d0 < 0.0f && d1 < 0.0f && d2 < 0.0f)
  511. {
  512. triangles[i] = false;
  513. continue;
  514. }
  515. // If 2 vertices behind the plane, create a new triangle in-place
  516. else if (d0 < 0.0f && d1 < 0.0f)
  517. {
  518. vertices[index] = ClipEdge(vertices[index], vertices[index + 2], d0, d2);
  519. vertices[index + 1] = ClipEdge(vertices[index + 1], vertices[index + 2], d1, d2);
  520. }
  521. else if (d0 < 0.0f && d2 < 0.0f)
  522. {
  523. vertices[index] = ClipEdge(vertices[index], vertices[index + 1], d0, d1);
  524. vertices[index + 2] = ClipEdge(vertices[index + 2], vertices[index + 1], d2, d1);
  525. }
  526. else if (d1 < 0.0f && d2 < 0.0f)
  527. {
  528. vertices[index + 1] = ClipEdge(vertices[index + 1], vertices[index], d1, d0);
  529. vertices[index + 2] = ClipEdge(vertices[index + 2], vertices[index], d2, d0);
  530. }
  531. // 1 vertex behind the plane: create one new triangle, and modify one in-place
  532. else if (d0 < 0.0f)
  533. {
  534. unsigned newIdx = numTriangles * 3;
  535. triangles[numTriangles] = true;
  536. ++numTriangles;
  537. vertices[newIdx] = ClipEdge(vertices[index], vertices[index + 2], d0, d2);
  538. vertices[newIdx + 1] = vertices[index] = ClipEdge(vertices[index], vertices[index + 1], d0, d1);
  539. vertices[newIdx + 2] = vertices[index + 2];
  540. }
  541. else if (d1 < 0.0f)
  542. {
  543. unsigned newIdx = numTriangles * 3;
  544. triangles[numTriangles] = true;
  545. ++numTriangles;
  546. vertices[newIdx + 1] = ClipEdge(vertices[index + 1], vertices[index], d1, d0);
  547. vertices[newIdx + 2] = vertices[index + 1] = ClipEdge(vertices[index + 1], vertices[index + 2], d1, d2);
  548. vertices[newIdx] = vertices[index];
  549. }
  550. else if (d2 < 0.0f)
  551. {
  552. unsigned newIdx = numTriangles * 3;
  553. triangles[numTriangles] = true;
  554. ++numTriangles;
  555. vertices[newIdx + 2] = ClipEdge(vertices[index + 2], vertices[index + 1], d2, d1);
  556. vertices[newIdx] = vertices[index + 2] = ClipEdge(vertices[index + 2], vertices[index], d2, d0);
  557. vertices[newIdx + 1] = vertices[index + 1];
  558. }
  559. }
  560. }
  561. }
  562. // Code based on Chris Hecker's Perspective Texture Mapping series in the Game Developer magazine
  563. // Also available online at http://chrishecker.com/Miscellaneous_Technical_Articles
  564. /// %Gradients of a software rasterized triangle.
  565. struct Gradients
  566. {
  567. /// Construct from vertices.
  568. Gradients(const Vector3* vertices)
  569. {
  570. float invdX = 1.0f / (((vertices[1].x_ - vertices[2].x_) *
  571. (vertices[0].y_ - vertices[2].y_)) -
  572. ((vertices[0].x_ - vertices[2].x_) *
  573. (vertices[1].y_ - vertices[2].y_)));
  574. float invdY = -invdX;
  575. dInvZdX_ = invdX * (((vertices[1].z_ - vertices[2].z_) * (vertices[0].y_ - vertices[2].y_)) -
  576. ((vertices[0].z_ - vertices[2].z_) * (vertices[1].y_ - vertices[2].y_)));
  577. dInvZdY_ = invdY * (((vertices[1].z_ - vertices[2].z_) * (vertices[0].x_ - vertices[2].x_)) -
  578. ((vertices[0].z_ - vertices[2].z_) * (vertices[1].x_ - vertices[2].x_)));
  579. dInvZdXInt_ = (int)dInvZdX_;
  580. }
  581. /// Integer horizontal gradient.
  582. int dInvZdXInt_;
  583. /// Horizontal gradient.
  584. float dInvZdX_;
  585. /// Vertical gradient.
  586. float dInvZdY_;
  587. };
  588. /// %Edge of a software rasterized triangle.
  589. struct Edge
  590. {
  591. /// Construct from gradients and top & bottom vertices.
  592. Edge(const Gradients& gradients, const Vector3& top, const Vector3& bottom, int topY)
  593. {
  594. float slope = (bottom.x_ - top.x_) / (bottom.y_ - top.y_);
  595. float yPreStep = (float)(topY + 1) - top.y_;
  596. float xPreStep = slope * yPreStep;
  597. x_ = (int)((xPreStep + top.x_) * OCCLUSION_X_SCALE + 0.5f);
  598. xStep_ = (int)(slope * OCCLUSION_X_SCALE + 0.5f);
  599. invZ_ = (int)(top.z_ + xPreStep * gradients.dInvZdX_ + yPreStep * gradients.dInvZdY_ + 0.5f);
  600. invZStep_ = (int)(slope * gradients.dInvZdX_ + gradients.dInvZdY_ + 0.5f);
  601. }
  602. /// X coordinate.
  603. int x_;
  604. /// X coordinate step.
  605. int xStep_;
  606. /// Inverse Z.
  607. int invZ_;
  608. /// Inverse Z step.
  609. int invZStep_;
  610. };
  611. void OcclusionBuffer::DrawTriangle2D(const Vector3* vertices)
  612. {
  613. int top, middle, bottom;
  614. bool middleIsRight;
  615. // Sort vertices in Y-direction
  616. if (vertices[0].y_ < vertices[1].y_)
  617. {
  618. if (vertices[2].y_ < vertices[0].y_)
  619. {
  620. top = 2; middle = 0; bottom = 1;
  621. middleIsRight = true;
  622. }
  623. else
  624. {
  625. top = 0;
  626. if (vertices[1].y_ < vertices[2].y_)
  627. {
  628. middle = 1; bottom = 2;
  629. middleIsRight = true;
  630. }
  631. else
  632. {
  633. middle = 2; bottom = 1;
  634. middleIsRight = false;
  635. }
  636. }
  637. }
  638. else
  639. {
  640. if (vertices[2].y_ < vertices[1].y_)
  641. {
  642. top = 2; middle = 1; bottom = 0;
  643. middleIsRight = false;
  644. }
  645. else
  646. {
  647. top = 1;
  648. if (vertices[0].y_ < vertices[2].y_)
  649. {
  650. middle = 0; bottom = 2;
  651. middleIsRight = false;
  652. }
  653. else
  654. {
  655. middle = 2; bottom = 0;
  656. middleIsRight = true;
  657. }
  658. }
  659. }
  660. int topY = (int)vertices[top].y_;
  661. int middleY = (int)vertices[middle].y_;
  662. int bottomY = (int)vertices[bottom].y_;
  663. // Check for degenerate triangle
  664. if (topY == bottomY)
  665. return;
  666. Gradients gradients(vertices);
  667. Edge topToMiddle(gradients, vertices[top], vertices[middle], topY);
  668. Edge topToBottom(gradients, vertices[top], vertices[bottom], topY);
  669. Edge middleToBottom(gradients, vertices[middle], vertices[bottom], middleY);
  670. // The triangle is clockwise, so if bottom > middle then middle is right
  671. if (middleIsRight)
  672. {
  673. // Top half
  674. int* row = buffer_ + topY * width_;
  675. int* endRow = buffer_ + middleY * width_;
  676. while (row < endRow)
  677. {
  678. int invZ = topToBottom.invZ_;
  679. int* dest = row + (topToBottom.x_ >> 16);
  680. int* end = row + (topToMiddle.x_ >> 16);
  681. while (dest < end)
  682. {
  683. if (invZ < *dest)
  684. *dest = invZ;
  685. invZ += gradients.dInvZdXInt_;
  686. ++dest;
  687. }
  688. topToBottom.x_ += topToBottom.xStep_;
  689. topToBottom.invZ_ += topToBottom.invZStep_;
  690. topToMiddle.x_ += topToMiddle.xStep_;
  691. row += width_;
  692. }
  693. // Bottom half
  694. row = buffer_ + middleY * width_;
  695. endRow = buffer_ + bottomY * width_;
  696. while (row < endRow)
  697. {
  698. int invZ = topToBottom.invZ_;
  699. int* dest = row + (topToBottom.x_ >> 16);
  700. int* end = row + (middleToBottom.x_ >> 16);
  701. while (dest < end)
  702. {
  703. if (invZ < *dest)
  704. *dest = invZ;
  705. invZ += gradients.dInvZdXInt_;
  706. ++dest;
  707. }
  708. topToBottom.x_ += topToBottom.xStep_;
  709. topToBottom.invZ_ += topToBottom.invZStep_;
  710. middleToBottom.x_ += middleToBottom.xStep_;
  711. row += width_;
  712. }
  713. }
  714. else
  715. {
  716. // Top half
  717. int* row = buffer_ + topY * width_;
  718. int* endRow = buffer_ + middleY * width_;
  719. while (row < endRow)
  720. {
  721. int invZ = topToMiddle.invZ_;
  722. int* dest = row + (topToMiddle.x_ >> 16);
  723. int* end = row + (topToBottom.x_ >> 16);
  724. while (dest < end)
  725. {
  726. if (invZ < *dest)
  727. *dest = invZ;
  728. invZ += gradients.dInvZdXInt_;
  729. ++dest;
  730. }
  731. topToMiddle.x_ += topToMiddle.xStep_;
  732. topToMiddle.invZ_ += topToMiddle.invZStep_;
  733. topToBottom.x_ += topToBottom.xStep_;
  734. row += width_;
  735. }
  736. // Bottom half
  737. row = buffer_ + middleY * width_;
  738. endRow = buffer_ + bottomY * width_;
  739. while (row < endRow)
  740. {
  741. int invZ = middleToBottom.invZ_;
  742. int* dest = row + (middleToBottom.x_ >> 16);
  743. int* end = row + (topToBottom.x_ >> 16);
  744. while (dest < end)
  745. {
  746. if (invZ < *dest)
  747. *dest = invZ;
  748. invZ += gradients.dInvZdXInt_;
  749. ++dest;
  750. }
  751. middleToBottom.x_ += middleToBottom.xStep_;
  752. middleToBottom.invZ_ += middleToBottom.invZStep_;
  753. topToBottom.x_ += topToBottom.xStep_;
  754. row += width_;
  755. }
  756. }
  757. }
  758. }