OcclusionBuffer.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  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 "Profiler.h"
  28. #include "StringUtils.h"
  29. #include <cstring>
  30. #include "DebugNew.h"
  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. max_Triangles(OCCLUSION_DEFAULT_MAX_TRIANGLES),
  45. cullMode_(CULL_CCW),
  46. depthHierarchyDirty_(true),
  47. nearClip_(0.0f),
  48. farClip_(0.0f),
  49. depthBias_(0.0f)
  50. {
  51. }
  52. OcclusionBuffer::~OcclusionBuffer()
  53. {
  54. }
  55. bool OcclusionBuffer::SetSize(int width, int height)
  56. {
  57. if ((width == width_) && (height == height_))
  58. return true;
  59. if ((width <= 0) || (height <= 0))
  60. return false;
  61. if (!IsPowerOfTwo(width))
  62. {
  63. LOGERROR("Width is not a power of two");
  64. return false;
  65. }
  66. PROFILE(ResizeOcclusion);
  67. width_ = width;
  68. height_ = height;
  69. // Reserve extra memory in case 3D clipping is not exact
  70. fullBuffer_ = new int[width * (height + 2)];
  71. buffer_ = fullBuffer_.GetPtr() + 1 * width;
  72. mipBuffers_.Clear();
  73. // Build buffers for mip levels
  74. for (;;)
  75. {
  76. width = (width + 1) / 2;
  77. height = (height + 1) / 2;
  78. mipBuffers_.Push(SharedArrayPtr<DepthValue>(new DepthValue[width * height]));
  79. if ((width <= OCCLUSION_MIN_SIZE) && (height <= OCCLUSION_MIN_SIZE))
  80. break;
  81. }
  82. LOGINFO("Set occlusion buffer size " + ToString(width_) + "x" + ToString(height_) + " with " +
  83. ToString(mipBuffers_.Size()) + " mip levels");
  84. CalculateViewport();
  85. return true;
  86. }
  87. void OcclusionBuffer::SetView(Camera* camera)
  88. {
  89. if (!camera)
  90. return;
  91. view_ = camera->GetInverseWorldTransform();
  92. projection_ = camera->GetProjection();
  93. viewProj_ = projection_ * view_;
  94. nearClip_ = camera->GetNearClip();
  95. farClip_ = camera->GetFarClip();
  96. depthBias_ = farClip_ * 0.00002f;
  97. CalculateViewport();
  98. }
  99. void OcclusionBuffer::SetMaxTriangles(unsigned triangles)
  100. {
  101. max_Triangles = 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. PROFILE(ClearOcclusion);
  114. if (!buffer_)
  115. return;
  116. Reset();
  117. int* dest = buffer_;
  118. int count = width_ * height_;
  119. while (count)
  120. {
  121. *dest++ = 0x7fffffff;
  122. --count;
  123. }
  124. depthHierarchyDirty_ = true;
  125. }
  126. bool OcclusionBuffer::Draw(const Matrix4x3& model, const unsigned char* vertexData, unsigned vertexSize,
  127. const unsigned char* indexData, unsigned indexSize, unsigned indexStart, unsigned indexCount)
  128. {
  129. PROFILE(DrawOcclusion);
  130. Matrix4 modelViewProj = viewProj_ * model;
  131. depthHierarchyDirty_ = true;
  132. // Theoretical max. amount of vertices if each of the 6 clipping planes doubles the triangle count
  133. Vector4 vertices[64 * 3];
  134. // 16-bit indices
  135. if (indexSize == sizeof(unsigned short))
  136. {
  137. const unsigned short* indices = (const unsigned short*)indexData;
  138. for (unsigned i = indexStart; i < indexStart + indexCount; i += 3)
  139. {
  140. if (numTriangles_ >= max_Triangles)
  141. return false;
  142. const Vector3& v0 = *((const Vector3*)(&vertexData[indices[i] * vertexSize]));
  143. const Vector3& v1 = *((const Vector3*)(&vertexData[indices[i + 1] * vertexSize]));
  144. const Vector3& v2 = *((const Vector3*)(&vertexData[indices[i + 2] * vertexSize]));
  145. vertices[0] = ModelTransform(modelViewProj, v0);
  146. vertices[1] = ModelTransform(modelViewProj, v1);
  147. vertices[2] = ModelTransform(modelViewProj, v2);
  148. DrawTriangle(vertices);
  149. }
  150. }
  151. else
  152. {
  153. const unsigned* indices = (const unsigned*)indexData;
  154. for (unsigned i = indexStart; i < indexStart + indexCount; i += 3)
  155. {
  156. if (numTriangles_ >= max_Triangles)
  157. return false;
  158. const Vector3& v0 = *((const Vector3*)(&vertexData[indices[i] * vertexSize]));
  159. const Vector3& v1 = *((const Vector3*)(&vertexData[indices[i + 1] * vertexSize]));
  160. const Vector3& v2 = *((const Vector3*)(&vertexData[indices[i + 2] * vertexSize]));
  161. vertices[0] = ModelTransform(modelViewProj, v0);
  162. vertices[1] = ModelTransform(modelViewProj, v1);
  163. vertices[2] = ModelTransform(modelViewProj, v2);
  164. DrawTriangle(vertices);
  165. }
  166. }
  167. return true;
  168. }
  169. void OcclusionBuffer::BuildDepthHierarchy()
  170. {
  171. PROFILE(BuildDepthHierarchy);
  172. if (!buffer_)
  173. return;
  174. // Build the first mip level from the pixel-level data
  175. int width = (width_ + 1) / 2;
  176. int height = (height_ + 1) / 2;
  177. if (mipBuffers_.Size())
  178. {
  179. for (int y = 0; y < height; ++y)
  180. {
  181. int* src = buffer_ + (y * 2) * width_;
  182. DepthValue* dest = mipBuffers_[0].GetPtr() + y * width;
  183. DepthValue* end = dest + width;
  184. if (y * 2 + 1 < height_)
  185. {
  186. int* src2 = src + width_;
  187. while (dest < end)
  188. {
  189. int minUpper = Min(src[0], src[1]);
  190. int minLower = Min(src2[0], src2[1]);
  191. dest->min_ = Min(minUpper, minLower);
  192. int maxUpper = Max(src[0], src[1]);
  193. int maxLower = Max(src2[0], src2[1]);
  194. dest->max_ = Max(maxUpper, maxLower);
  195. src += 2;
  196. src2 += 2;
  197. ++dest;
  198. }
  199. }
  200. else
  201. {
  202. while (dest < end)
  203. {
  204. dest->min_ = Min(src[0], src[1]);
  205. dest->max_ = Max(src[0], src[1]);
  206. src += 2;
  207. ++dest;
  208. }
  209. }
  210. }
  211. }
  212. // Build the rest of the mip levels
  213. for (unsigned i = 1; i < mipBuffers_.Size(); ++i)
  214. {
  215. int prevWidth = width;
  216. int prevHeight = height;
  217. width = (width + 1) / 2;
  218. height = (height + 1) / 2;
  219. for (int y = 0; y < height; ++y)
  220. {
  221. DepthValue* src = mipBuffers_[i - 1].GetPtr() + (y * 2) * prevWidth;
  222. DepthValue* dest = mipBuffers_[i].GetPtr() + y * width;
  223. DepthValue* end = dest + width;
  224. if (y * 2 + 1 < prevHeight)
  225. {
  226. DepthValue* src2 = src + prevWidth;
  227. while (dest < end)
  228. {
  229. int minUpper = Min(src[0].min_, src[1].min_);
  230. int minLower = Min(src2[0].min_, src2[1].min_);
  231. dest->min_ = Min(minUpper, minLower);
  232. int maxUpper = Max(src[0].max_, src[1].max_);
  233. int maxLower = Max(src2[0].max_, src2[1].max_);
  234. dest->max_ = Max(maxUpper, maxLower);
  235. src += 2;
  236. src2 += 2;
  237. ++dest;
  238. }
  239. }
  240. else
  241. {
  242. while (dest < end)
  243. {
  244. dest->min_ = Min(src[0].min_, src[1].min_);
  245. dest->max_ = Max(src[0].max_, src[1].max_);
  246. src += 2;
  247. ++dest;
  248. }
  249. }
  250. }
  251. }
  252. depthHierarchyDirty_ = false;
  253. }
  254. bool OcclusionBuffer::IsVisible(const BoundingBox& worldSpaceBox) const
  255. {
  256. if (!buffer_)
  257. return true;
  258. Vector3 vertices[8];
  259. // Transform corners to view space. Note: do not directly transform the bounding box, as this would expand it unnecessarily
  260. vertices[0] = view_ * worldSpaceBox.min_;
  261. vertices[1] = view_ * Vector3(worldSpaceBox.max_.x_, worldSpaceBox.min_.y_, worldSpaceBox.min_.z_);
  262. vertices[2] = view_ * Vector3(worldSpaceBox.min_.x_, worldSpaceBox.max_.y_, worldSpaceBox.min_.z_);
  263. vertices[3] = view_ * Vector3(worldSpaceBox.max_.x_, worldSpaceBox.max_.y_, worldSpaceBox.min_.z_);
  264. vertices[4] = view_ * Vector3(worldSpaceBox.min_.x_, worldSpaceBox.min_.y_, worldSpaceBox.max_.z_);
  265. vertices[5] = view_ * Vector3(worldSpaceBox.max_.x_, worldSpaceBox.min_.y_, worldSpaceBox.max_.z_);
  266. vertices[6] = view_ * Vector3(worldSpaceBox.min_.x_, worldSpaceBox.max_.y_, worldSpaceBox.max_.z_);
  267. vertices[7] = view_ * worldSpaceBox.max_;
  268. // Project to screen. If any of the corners cross the near plane, assume visible
  269. float minX, maxX, minY, maxY, minZ;
  270. // Subtract a small bias to prevent self-occlusion artifacts
  271. // (need for bias results from different floating point transformations producing different errors)
  272. vertices[0].z_ -= depthBias_;
  273. if (vertices[0].z_ <= nearClip_)
  274. return true;
  275. // Project the first corner to initialize the rectangle
  276. float invW = 1.0f / (vertices[0].z_ * projection_.m32_ + projection_.m33_);
  277. minX = maxX = invW * (projOffsetScaleX_ * vertices[0].x_) + offsetX_;
  278. minY = maxY = invW * (projOffsetScaleY_ * vertices[0].y_) + offsetY_;
  279. minZ = invW * (projection_.m22_ * vertices[0].z_ + projection_.m23_);
  280. // Project the rest
  281. for (unsigned i = 1; i < 8; ++i)
  282. {
  283. // Subtract a small bias to prevent self-occlusion artifacts
  284. vertices[i].z_ -= depthBias_;
  285. if (vertices[i].z_ <= nearClip_)
  286. return true;
  287. float invW = 1.0f / (vertices[i].z_ * projection_.m32_ + projection_.m33_);
  288. float x = invW * (projOffsetScaleX_ * vertices[i].x_) + offsetX_;
  289. float y = invW * (projOffsetScaleY_ * vertices[i].y_) + offsetY_;
  290. float z = invW * (projection_.m22_ * vertices[i].z_ + projection_.m23_);
  291. if (x < minX) minX = x;
  292. if (x > maxX) maxX = x;
  293. if (y < minY) minY = y;
  294. if (y > maxY) maxY = y;
  295. if (z < minZ) minZ = 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 outside, can not test, so assume visible (should be frustum culled in that case)
  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. int z = ((int)(minZ * OCCLUSION_Z_SCALE));
  317. if (!depthHierarchyDirty_)
  318. {
  319. // Start from lowest mip level and check if a conclusive result can be found
  320. for (int i = mipBuffers_.Size() - 1; i >= 0; --i)
  321. {
  322. int shift = i + 1;
  323. int width = width_ >> shift;
  324. int left = rect.left_ >> shift;
  325. int right = rect.right_ >> shift;
  326. DepthValue* buffer = mipBuffers_[i].GetPtr();
  327. DepthValue* row = buffer + (rect.top_ >> shift) * width;
  328. DepthValue* endRow = buffer + (rect.bottom_ >> shift) * width;
  329. bool allOccluded = true;
  330. while (row <= endRow)
  331. {
  332. DepthValue* src = row + left;
  333. DepthValue* end = row + right;
  334. while (src <= end)
  335. {
  336. if (z <= src->min_)
  337. return true;
  338. if (z <= src->max_)
  339. allOccluded = false;
  340. ++src;
  341. }
  342. row += width;
  343. }
  344. if (allOccluded)
  345. return false;
  346. }
  347. }
  348. // If no conclusive result, finally check the pixel-level data
  349. int* row = buffer_ + rect.top_ * width_;
  350. int* endRow = buffer_ + rect.bottom_ * width_;
  351. while (row <= endRow)
  352. {
  353. int* src = row + rect.left_;
  354. int* end = row + rect.right_;
  355. while (src <= end)
  356. {
  357. if (z <= *src)
  358. return true;
  359. ++src;
  360. }
  361. row += width_;
  362. }
  363. return false;
  364. }
  365. inline Vector4 OcclusionBuffer::ModelTransform(const Matrix4& transform, const Vector3& vertex) const
  366. {
  367. return Vector4(
  368. transform.m00_ * vertex.x_ + transform.m01_ * vertex.y_ + transform.m02_ * vertex.z_ + transform.m03_,
  369. transform.m10_ * vertex.x_ + transform.m11_ * vertex.y_ + transform.m12_ * vertex.z_ + transform.m13_,
  370. transform.m20_ * vertex.x_ + transform.m21_ * vertex.y_ + transform.m22_ * vertex.z_ + transform.m23_,
  371. transform.m30_ * vertex.x_ + transform.m31_ * vertex.y_ + transform.m32_ * vertex.z_ + transform.m33_
  372. );
  373. }
  374. inline Vector3 OcclusionBuffer::ViewportTransform(const Vector4& vertex) const
  375. {
  376. float invW = 1.0f / vertex.w_;
  377. return Vector3(
  378. invW * vertex.x_ * scaleX_ + offsetX_,
  379. invW * vertex.y_ * scaleY_ + offsetY_,
  380. invW * vertex.z_ * OCCLUSION_Z_SCALE
  381. );
  382. }
  383. inline Vector4 OcclusionBuffer::ClipEdge(const Vector4& v0, const Vector4& v1, float d0, float d1) const
  384. {
  385. float t = d0 / (d0 - d1);
  386. return v0 + t * (v1 - v0);
  387. }
  388. inline bool OcclusionBuffer::CheckFacing(const Vector3& v0, const Vector3& v1, const Vector3& v2) const
  389. {
  390. if (cullMode_ == CULL_NONE)
  391. return true;
  392. float aX = v0.x_ - v1.x_;
  393. float aY = v0.y_ - v1.y_;
  394. float bX = v2.x_ - v1.x_;
  395. float bY = v2.y_ - v1.y_;
  396. float signedArea = aX * bY - aY * bX;
  397. if (cullMode_ == CULL_CCW)
  398. return signedArea < 0.0f;
  399. else
  400. return signedArea > 0.0f;
  401. }
  402. void OcclusionBuffer::CalculateViewport()
  403. {
  404. // Add half pixel offset due to 3D frustum culling
  405. scaleX_ = 0.5f * width_;
  406. scaleY_ = -0.5f * height_;
  407. offsetX_ = 0.5f * width_ + 0.5f;
  408. offsetY_ = 0.5f * height_ + 0.5f;
  409. projOffsetScaleX_ = projection_.m00_ * scaleX_;
  410. projOffsetScaleY_ = projection_.m11_ * scaleY_;
  411. }
  412. void OcclusionBuffer::DrawTriangle(Vector4* vertices)
  413. {
  414. unsigned clipMask = 0;
  415. unsigned andClipMask = 0;
  416. Vector3 projected[3];
  417. // Build the clip plane mask for the triangle
  418. for (unsigned i = 0; i < 3; ++i)
  419. {
  420. unsigned vertexClipMask = 0;
  421. if (vertices[i].x_ > vertices[i].w_)
  422. vertexClipMask |= CLIPMASK_X_POS;
  423. if (vertices[i].x_ < -vertices[i].w_)
  424. vertexClipMask |= CLIPMASK_X_NEG;
  425. if (vertices[i].y_ > vertices[i].w_)
  426. vertexClipMask |= CLIPMASK_Y_POS;
  427. if (vertices[i].y_ < -vertices[i].w_)
  428. vertexClipMask |= CLIPMASK_Y_NEG;
  429. if (vertices[i].z_ > vertices[i].w_)
  430. vertexClipMask |= CLIPMASK_Z_POS;
  431. if (vertices[i].z_ < 0.0f)
  432. vertexClipMask |= CLIPMASK_Z_NEG;
  433. clipMask |= vertexClipMask;
  434. if (!i)
  435. andClipMask = vertexClipMask;
  436. else
  437. andClipMask &= vertexClipMask;
  438. }
  439. // If triangle is fully behind any clip plane, can reject quickly
  440. if (andClipMask)
  441. return;
  442. // Check if triangle is fully inside
  443. if (!clipMask)
  444. {
  445. projected[0] = ViewportTransform(vertices[0]);
  446. projected[1] = ViewportTransform(vertices[1]);
  447. projected[2] = ViewportTransform(vertices[2]);
  448. if (CheckFacing(projected[0], projected[1], projected[2]))
  449. DrawTriangle2D(projected);
  450. }
  451. else
  452. {
  453. bool triangles[64];
  454. // Initial triangle
  455. triangles[0] = true;
  456. unsigned numTriangles = 1;
  457. if (clipMask & CLIPMASK_X_POS)
  458. ClipVertices(Vector4(-1.0f, 0.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  459. if (clipMask & CLIPMASK_X_NEG)
  460. ClipVertices(Vector4(1.0f, 0.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  461. if (clipMask & CLIPMASK_Y_POS)
  462. ClipVertices(Vector4(0.0f, -1.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  463. if (clipMask & CLIPMASK_Y_NEG)
  464. ClipVertices(Vector4(0.0f, 1.0f, 0.0f, 1.0f), vertices, triangles, numTriangles);
  465. if (clipMask & CLIPMASK_Z_POS)
  466. ClipVertices(Vector4(0.0f, 0.0f, -1.0f, 1.0f), vertices, triangles, numTriangles);
  467. if (clipMask & CLIPMASK_Z_NEG)
  468. ClipVertices(Vector4(0.0f, 0.0f, 1.0f, 0.0f), vertices, triangles, numTriangles);
  469. // Draw each accepted triangle
  470. for (unsigned i = 0; i < numTriangles; ++i)
  471. {
  472. if (triangles[i])
  473. {
  474. unsigned index = i * 3;
  475. projected[0] = ViewportTransform(vertices[index]);
  476. projected[1] = ViewportTransform(vertices[index + 1]);
  477. projected[2] = ViewportTransform(vertices[index + 2]);
  478. if (CheckFacing(projected[0], projected[1], projected[2]))
  479. DrawTriangle2D(projected);
  480. }
  481. }
  482. }
  483. }
  484. void OcclusionBuffer::ClipVertices(const Vector4& plane, Vector4* vertices, bool* triangles, unsigned& numTriangles)
  485. {
  486. unsigned num = numTriangles;
  487. for (unsigned i = 0; i < num; ++i)
  488. {
  489. if (triangles[i])
  490. {
  491. unsigned index = i * 3;
  492. float d0 = plane.DotProduct(vertices[index]);
  493. float d1 = plane.DotProduct(vertices[index + 1]);
  494. float d2 = plane.DotProduct(vertices[index + 2]);
  495. // If all vertices behind the plane, reject triangle
  496. if ((d0 < 0.0f) && (d1 < 0.0f) && (d2 < 0.0f))
  497. {
  498. triangles[i] = false;
  499. continue;
  500. }
  501. // If 2 vertices behind the plane, create a new triangle in-place
  502. else if ((d0 < 0.0f) && (d1 < 0.0f))
  503. {
  504. vertices[index] = ClipEdge(vertices[index], vertices[index + 2], d0, d2);
  505. vertices[index + 1] = ClipEdge(vertices[index + 1], vertices[index + 2], d1, d2);
  506. }
  507. else if ((d0 < 0.0f) && (d2 < 0.0f))
  508. {
  509. vertices[index] = ClipEdge(vertices[index], vertices[index + 1], d0, d1);
  510. vertices[index + 2] = ClipEdge(vertices[index + 2], vertices[index + 1], d2, d1);
  511. }
  512. else if ((d1 < 0.0f) && (d2 < 0.0f))
  513. {
  514. vertices[index + 1] = ClipEdge(vertices[index + 1], vertices[index], d1, d0);
  515. vertices[index + 2] = ClipEdge(vertices[index + 2], vertices[index], d2, d0);
  516. }
  517. // 1 vertex behind the plane: create one new triangle, and modify one in-place
  518. else if (d0 < 0.0f)
  519. {
  520. unsigned newIdx = numTriangles * 3;
  521. triangles[numTriangles] = true;
  522. ++numTriangles;
  523. vertices[newIdx] = ClipEdge(vertices[index], vertices[index + 1], d0, d1);
  524. vertices[newIdx + 1] = vertices[index + 1];
  525. vertices[newIdx + 2] = vertices[index] = ClipEdge(vertices[index], vertices[index + 2], d0, d2);
  526. }
  527. else if (d1 < 0.0f)
  528. {
  529. unsigned newIdx = numTriangles * 3;
  530. triangles[numTriangles] = true;
  531. ++numTriangles;
  532. vertices[newIdx + 1] = ClipEdge(vertices[index + 1], vertices[index + 2], d1, d2);
  533. vertices[newIdx + 2] = vertices[index + 2];
  534. vertices[newIdx] = vertices[index + 1] = ClipEdge(vertices[index + 1], vertices[index], d1, d0);
  535. }
  536. else if (d2 < 0.0f)
  537. {
  538. unsigned newIdx = numTriangles * 3;
  539. triangles[numTriangles] = true;
  540. ++numTriangles;
  541. vertices[newIdx + 2] = ClipEdge(vertices[index + 2], vertices[index], d2, d0);
  542. vertices[newIdx] = vertices[index];
  543. vertices[newIdx + 1] = vertices[index + 2] = ClipEdge(vertices[index + 2], vertices[index + 1], d2, d1);
  544. }
  545. }
  546. }
  547. }
  548. // Code based on Chris Hecker's Perspective Texture Mapping series in the Game Developer magazine
  549. // Also available online at http://chrishecker.com/Miscellaneous_Technical_Articles
  550. /// Gradients of a software rasterized triangle
  551. struct Gradients
  552. {
  553. /// Construct from vertices
  554. Gradients(const Vector3* vertices)
  555. {
  556. float invdX = 1.0f / (((vertices[1].x_ - vertices[2].x_) *
  557. (vertices[0].y_ - vertices[2].y_)) -
  558. ((vertices[0].x_ - vertices[2].x_) *
  559. (vertices[1].y_ - vertices[2].y_)));
  560. float invdY = -invdX;
  561. mDInvZdX = invdX * (((vertices[1].z_ - vertices[2].z_) * (vertices[0].y_ - vertices[2].y_)) -
  562. ((vertices[0].z_ - vertices[2].z_) * (vertices[1].y_ - vertices[2].y_)));
  563. mDInvZdY = invdY * (((vertices[1].z_ - vertices[2].z_) * (vertices[0].x_ - vertices[2].x_)) -
  564. ((vertices[0].z_ - vertices[2].z_) * (vertices[1].x_ - vertices[2].x_)));
  565. mDInvZdXInt = (int)mDInvZdX;
  566. }
  567. /// Integer horizontal gradient
  568. int mDInvZdXInt;
  569. /// Horizontal gradient
  570. float mDInvZdX;
  571. /// Vertical gradient
  572. float mDInvZdY;
  573. };
  574. /// Edge of a software rasterized triangle
  575. struct Edge
  576. {
  577. /// Construct from gradients and top & bottom vertices
  578. Edge(const Gradients& gradients, const Vector3& top, const Vector3& bottom, int topY)
  579. {
  580. float slope = (bottom.x_ - top.x_) / (bottom.y_ - top.y_);
  581. float yPreStep = (float)(topY + 1) - top.y_;
  582. float xPreStep = slope * yPreStep;
  583. x_ = (int)((xPreStep + top.x_) * OCCLUSION_X_SCALE);
  584. x_Step = (int)(slope * OCCLUSION_X_SCALE);
  585. mInvZ = (int)(top.z_ + xPreStep * gradients.mDInvZdX + yPreStep * gradients.mDInvZdY);
  586. mInvZStep = (int)(slope * gradients.mDInvZdX + gradients.mDInvZdY);
  587. }
  588. /// X coordinate
  589. int x_;
  590. /// X coordinate step
  591. int x_Step;
  592. /// Inverse Z
  593. int mInvZ;
  594. /// Inverse Z step
  595. int mInvZStep;
  596. };
  597. void OcclusionBuffer::DrawTriangle2D(const Vector3* vertices)
  598. {
  599. int top, middle, bottom;
  600. bool middleIsRight;
  601. numTriangles_++;
  602. // Sort vertices in Y-direction
  603. if (vertices[0].y_ < vertices[1].y_)
  604. {
  605. if (vertices[2].y_ < vertices[0].y_)
  606. {
  607. top = 2; middle = 0; bottom = 1;
  608. middleIsRight = true;
  609. }
  610. else
  611. {
  612. top = 0;
  613. if (vertices[1].y_ < vertices[2].y_)
  614. {
  615. middle = 1; bottom = 2;
  616. middleIsRight = true;
  617. }
  618. else
  619. {
  620. middle = 2; bottom = 1;
  621. middleIsRight = false;
  622. }
  623. }
  624. }
  625. else
  626. {
  627. if (vertices[2].y_ < vertices[1].y_)
  628. {
  629. top = 2; middle = 1; bottom = 0;
  630. middleIsRight = false;
  631. }
  632. else
  633. {
  634. top = 1;
  635. if (vertices[0].y_ < vertices[2].y_)
  636. {
  637. middle = 0; bottom = 2;
  638. middleIsRight = false;
  639. }
  640. else
  641. {
  642. middle = 2; bottom = 0;
  643. middleIsRight = true;
  644. }
  645. }
  646. }
  647. int topY = (int)vertices[top].y_;
  648. int middleY = (int)vertices[middle].y_;
  649. int bottoy_ = (int)vertices[bottom].y_;
  650. // Check for degenerate triangle
  651. if (topY == bottoy_)
  652. return;
  653. Gradients gradients(vertices);
  654. Edge topToMiddle(gradients, vertices[top], vertices[middle], topY);
  655. Edge topToBottom(gradients, vertices[top], vertices[bottom], topY);
  656. Edge middleToBottom(gradients, vertices[middle], vertices[bottom], middleY);
  657. // The triangle is clockwise, so if bottom > middle then middle is right
  658. if (middleIsRight)
  659. {
  660. // Top half
  661. int* row = buffer_ + topY * width_;
  662. int* endRow = buffer_ + middleY * width_;
  663. while (row < endRow)
  664. {
  665. int invZ = topToBottom.mInvZ;
  666. int* dest = row + (topToBottom.x_ >> 16);
  667. int* end = row + (topToMiddle.x_ >> 16);
  668. while (dest < end)
  669. {
  670. if (invZ < *dest)
  671. *dest = invZ;
  672. invZ += gradients.mDInvZdXInt;
  673. ++dest;
  674. }
  675. topToBottom.x_ += topToBottom.x_Step;
  676. topToBottom.mInvZ += topToBottom.mInvZStep;
  677. topToMiddle.x_ += topToMiddle.x_Step;
  678. row += width_;
  679. }
  680. // Bottom half
  681. row = buffer_ + middleY * width_;
  682. endRow = buffer_ + bottoy_ * width_;
  683. while (row < endRow)
  684. {
  685. int invZ = topToBottom.mInvZ;
  686. int* dest = row + (topToBottom.x_ >> 16);
  687. int* end = row + (middleToBottom.x_ >> 16);
  688. while (dest < end)
  689. {
  690. if (invZ < *dest)
  691. *dest = invZ;
  692. invZ += gradients.mDInvZdXInt;
  693. ++dest;
  694. }
  695. topToBottom.x_ += topToBottom.x_Step;
  696. topToBottom.mInvZ += topToBottom.mInvZStep;
  697. middleToBottom.x_ += middleToBottom.x_Step;
  698. row += width_;
  699. }
  700. }
  701. else
  702. {
  703. // Top half
  704. int* row = buffer_ + topY * width_;
  705. int* endRow = buffer_ + middleY * width_;
  706. while (row < endRow)
  707. {
  708. int invZ = topToMiddle.mInvZ;
  709. int* dest = row + (topToMiddle.x_ >> 16);
  710. int* end = row + (topToBottom.x_ >> 16);
  711. while (dest < end)
  712. {
  713. if (invZ < *dest)
  714. *dest = invZ;
  715. invZ += gradients.mDInvZdXInt;
  716. ++dest;
  717. }
  718. topToMiddle.x_ += topToMiddle.x_Step;
  719. topToMiddle.mInvZ += topToMiddle.mInvZStep;
  720. topToBottom.x_ += topToBottom.x_Step;
  721. row += width_;
  722. }
  723. // Bottom half
  724. row = buffer_ + middleY * width_;
  725. endRow = buffer_ + bottoy_ * width_;
  726. while (row < endRow)
  727. {
  728. int invZ = middleToBottom.mInvZ;
  729. int* dest = row + (middleToBottom.x_ >> 16);
  730. int* end = row + (topToBottom.x_ >> 16);
  731. while (dest < end)
  732. {
  733. if (invZ < *dest)
  734. *dest = invZ;
  735. invZ += gradients.mDInvZdXInt;
  736. ++dest;
  737. }
  738. middleToBottom.x_ += middleToBottom.x_Step;
  739. middleToBottom.mInvZ += middleToBottom.mInvZStep;
  740. topToBottom.x_ += topToBottom.x_Step;
  741. row += width_;
  742. }
  743. }
  744. }