2
0

OcclusionBuffer.cpp 29 KB

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