BsLightProbes.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsLightProbes.h"
  4. #include "BsLightProbeVolume.h"
  5. #include "BsGpuBuffer.h"
  6. #include "BsRendererView.h"
  7. #include "BsRenderBeastIBLUtility.h"
  8. #include "BsMesh.h"
  9. #include "BsVertexDataDesc.h"
  10. #include "BsGpuParamsSet.h"
  11. #include "BsRendererUtility.h"
  12. #include "BsSkybox.h"
  13. #include "BsRendererTextures.h"
  14. namespace bs { namespace ct
  15. {
  16. TetrahedraRenderParamDef gTetrahedraRenderParamDef;
  17. ShaderVariation TetrahedraRenderMat::VAR_NoMSAA = ShaderVariation({
  18. ShaderVariation::Param("MSAA", false)
  19. });
  20. ShaderVariation TetrahedraRenderMat::VAR_MSAA = ShaderVariation({
  21. ShaderVariation::Param("MSAA", true)
  22. });
  23. TetrahedraRenderMat::TetrahedraRenderMat()
  24. {
  25. SPtr<GpuParams> params = mParamsSet->getGpuParams();
  26. params->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDepthBufferTex", mDepthBufferTex);
  27. if(params->hasSamplerState(GPT_FRAGMENT_PROGRAM, "gDepthBufferSamp"))
  28. {
  29. SAMPLER_STATE_DESC pointSampDesc;
  30. pointSampDesc.minFilter = FO_POINT;
  31. pointSampDesc.magFilter = FO_POINT;
  32. pointSampDesc.mipFilter = FO_POINT;
  33. pointSampDesc.addressMode.u = TAM_CLAMP;
  34. pointSampDesc.addressMode.v = TAM_CLAMP;
  35. pointSampDesc.addressMode.w = TAM_CLAMP;
  36. SPtr<SamplerState> pointSampState = SamplerState::create(pointSampDesc);
  37. params->setSamplerState(GPT_FRAGMENT_PROGRAM, "gDepthBufferSamp", pointSampState);
  38. }
  39. mParamBuffer = gTetrahedraRenderParamDef.createBuffer();
  40. mParamsSet->setParamBlockBuffer("Params", mParamBuffer, true);
  41. }
  42. void TetrahedraRenderMat::_initVariations(ShaderVariations& variations)
  43. {
  44. variations.add(VAR_NoMSAA);
  45. variations.add(VAR_MSAA);
  46. }
  47. void TetrahedraRenderMat::execute(const RendererView& view, const SPtr<Texture>& sceneDepth, const SPtr<Mesh>& mesh,
  48. const SPtr<RenderTexture>& output)
  49. {
  50. const RendererViewProperties& viewProps = view.getProperties();
  51. Vector4 NDCtoUV = view.getNDCToUV();
  52. if(mVariation.getBool("MSAA"))
  53. {
  54. NDCtoUV.x *= viewProps.viewRect.width;
  55. NDCtoUV.y *= viewProps.viewRect.height;
  56. NDCtoUV.z *= viewProps.viewRect.width;
  57. NDCtoUV.w *= viewProps.viewRect.height;
  58. }
  59. gTetrahedraRenderParamDef.gMatViewProj.set(mParamBuffer, viewProps.viewProjTransform);
  60. gTetrahedraRenderParamDef.gNDCToUV.set(mParamBuffer, NDCtoUV);
  61. gTetrahedraRenderParamDef.gNDCToDeviceZ.set(mParamBuffer, RendererView::getNDCZToDeviceZ());
  62. mDepthBufferTex.set(sceneDepth);
  63. mParamBuffer->flushToGPU();
  64. RenderAPI& rapi = RenderAPI::instance();
  65. rapi.setRenderTarget(output);
  66. gRendererUtility().setPass(mMaterial);
  67. gRendererUtility().setPassParams(mParamsSet);
  68. gRendererUtility().draw(mesh);
  69. }
  70. void TetrahedraRenderMat::getOutputDesc(const RendererView& view, POOLED_RENDER_TEXTURE_DESC& colorDesc,
  71. POOLED_RENDER_TEXTURE_DESC& depthDesc)
  72. {
  73. const RendererViewProperties& viewProps = view.getProperties();
  74. UINT32 width = viewProps.viewRect.width;
  75. UINT32 height = viewProps.viewRect.height;
  76. UINT32 numSamples = viewProps.numSamples;
  77. colorDesc = POOLED_RENDER_TEXTURE_DESC::create2D(PF_R16I, width, height, TU_RENDERTARGET, numSamples);
  78. depthDesc = POOLED_RENDER_TEXTURE_DESC::create2D(PF_D16, width, height, TU_DEPTHSTENCIL, numSamples);
  79. }
  80. TetrahedraRenderMat* TetrahedraRenderMat::getVariation(bool msaa)
  81. {
  82. if (msaa)
  83. return get(VAR_MSAA);
  84. return get(VAR_NoMSAA);
  85. }
  86. IrradianceEvaluateParamDef gIrradianceEvaluateParamDef;
  87. ShaderVariation IrradianceEvaluateMat::VAR_MSAA_Probes = ShaderVariation({
  88. ShaderVariation::Param("MSAA_COUNT", 2),
  89. ShaderVariation::Param("SKY_ONLY", false)
  90. });
  91. ShaderVariation IrradianceEvaluateMat::VAR_NoMSAA_Probes = ShaderVariation({
  92. ShaderVariation::Param("MSAA_COUNT", 1),
  93. ShaderVariation::Param("SKY_ONLY", false)
  94. });
  95. ShaderVariation IrradianceEvaluateMat::VAR_MSAA_Sky = ShaderVariation({
  96. ShaderVariation::Param("MSAA_COUNT", 2),
  97. ShaderVariation::Param("SKY_ONLY", true)
  98. });
  99. ShaderVariation IrradianceEvaluateMat::VAR_NoMSAA_Sky = ShaderVariation({
  100. ShaderVariation::Param("MSAA_COUNT", 1),
  101. ShaderVariation::Param("SKY_ONLY", true)
  102. });
  103. IrradianceEvaluateMat::IrradianceEvaluateMat()
  104. :mGBufferParams(mMaterial, mParamsSet)
  105. {
  106. mSkyOnly = mVariation.getBool("SKY_ONLY");
  107. SPtr<GpuParams> params = mParamsSet->getGpuParams();
  108. if(mSkyOnly)
  109. params->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSkyIrradianceTex", mParamSkyIrradianceTex);
  110. else
  111. {
  112. params->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mParamInputTex);
  113. params->getBufferParam(GPT_FRAGMENT_PROGRAM, "gSHCoeffs", mParamSHCoeffsBuffer);
  114. params->getBufferParam(GPT_FRAGMENT_PROGRAM, "gProbeVolumes", mParamVolumeBuffer);
  115. }
  116. mParamBuffer = gIrradianceEvaluateParamDef.createBuffer();
  117. mParamsSet->setParamBlockBuffer("Params", mParamBuffer, true);
  118. }
  119. void IrradianceEvaluateMat::_initVariations(ShaderVariations& variations)
  120. {
  121. variations.add(VAR_MSAA_Probes);
  122. variations.add(VAR_MSAA_Sky);
  123. variations.add(VAR_NoMSAA_Probes);
  124. variations.add(VAR_NoMSAA_Sky);
  125. }
  126. void IrradianceEvaluateMat::execute(const RendererView& view, const GBufferTextures& gbuffer,
  127. const SPtr<Texture>& lightProbeIndices, const SPtr<GpuBuffer>& shCoeffs, const SPtr<GpuBuffer>& volumes,
  128. const Skybox* skybox, const SPtr<RenderTexture>& output)
  129. {
  130. const RendererViewProperties& viewProps = view.getProperties();
  131. mGBufferParams.bind(gbuffer);
  132. float skyBrightness = 1.0f;
  133. if (mSkyOnly)
  134. {
  135. SPtr<Texture> skyIrradiance;
  136. if (skybox != nullptr)
  137. {
  138. skyIrradiance = skybox->getIrradiance();
  139. skyBrightness = skybox->getBrightness();
  140. }
  141. if(skyIrradiance == nullptr)
  142. skyIrradiance = RendererTextures::defaultIndirect;
  143. mParamSkyIrradianceTex.set(skyIrradiance);
  144. }
  145. else
  146. {
  147. mParamInputTex.set(lightProbeIndices);
  148. mParamSHCoeffsBuffer.set(shCoeffs);
  149. mParamVolumeBuffer.set(volumes);
  150. }
  151. gIrradianceEvaluateParamDef.gSkyBrightness.set(mParamBuffer, skyBrightness);
  152. mParamBuffer->flushToGPU();
  153. mParamsSet->setParamBlockBuffer("PerCamera", view.getPerViewBuffer(), true);
  154. // Render
  155. RenderAPI& rapi = RenderAPI::instance();
  156. rapi.setRenderTarget(output, 0, RT_COLOR0);
  157. gRendererUtility().setPass(mMaterial);
  158. gRendererUtility().setPassParams(mParamsSet);
  159. gRendererUtility().drawScreenQuad(Rect2(0.0f, 0.0f, (float)viewProps.viewRect.width,
  160. (float)viewProps.viewRect.height));
  161. rapi.setRenderTarget(nullptr);
  162. }
  163. IrradianceEvaluateMat* IrradianceEvaluateMat::getVariation(UINT32 msaaCount, bool skyOnly)
  164. {
  165. if(skyOnly)
  166. {
  167. if (msaaCount > 1)
  168. return get(VAR_MSAA_Sky);
  169. return get(VAR_NoMSAA_Sky);
  170. }
  171. else
  172. {
  173. if (msaaCount > 1)
  174. return get(VAR_MSAA_Probes);
  175. return get(VAR_NoMSAA_Probes);
  176. }
  177. }
  178. LightProbes::LightProbes()
  179. :mTetrahedronVolumeDirty(false), mMaxCoefficients(0), mMaxTetrahedra(0)
  180. { }
  181. void LightProbes::notifyAdded(LightProbeVolume* volume)
  182. {
  183. UINT32 handle = (UINT32)mVolumes.size();
  184. VolumeInfo info;
  185. info.volume = volume;
  186. info.isDirty = true;
  187. mVolumes.push_back(info);
  188. volume->setRendererId(handle);
  189. notifyDirty(volume);
  190. }
  191. void LightProbes::notifyDirty(LightProbeVolume* volume)
  192. {
  193. UINT32 handle = volume->getRendererId();
  194. mVolumes[handle].isDirty = true;
  195. mTetrahedronVolumeDirty = true;
  196. }
  197. void LightProbes::notifyRemoved(LightProbeVolume* volume)
  198. {
  199. UINT32 handle = volume->getRendererId();
  200. LightProbeVolume* lastVolume = mVolumes.back().volume;
  201. UINT32 lastHandle = lastVolume->getRendererId();
  202. if (handle != lastHandle)
  203. {
  204. // Swap current last element with the one we want to erase
  205. std::swap(mVolumes[handle], mVolumes[lastHandle]);
  206. lastVolume->setRendererId(handle);
  207. }
  208. // Erase last (empty) element
  209. mVolumes.erase(mVolumes.end() - 1);
  210. mTetrahedronVolumeDirty = true;
  211. }
  212. void LightProbes::updateProbes()
  213. {
  214. if (!mTetrahedronVolumeDirty)
  215. return;
  216. // Move all coefficients into the global buffer
  217. UINT32 numCoeffs = 0;
  218. for(auto& entry : mVolumes)
  219. {
  220. UINT32 numProbes = (UINT32)entry.volume->getLightProbePositions().size();
  221. numCoeffs += numProbes;
  222. }
  223. if(numCoeffs > mMaxCoefficients)
  224. {
  225. UINT32 newSize = Math::divideAndRoundUp(numCoeffs, 32U) * 32U;
  226. resizeCoefficientBuffer(newSize);
  227. }
  228. UINT8* dest = (UINT8*)mProbeCoefficientsGPU->lock(0, mProbeCoefficientsGPU->getSize(), GBL_WRITE_ONLY_DISCARD);
  229. for(auto& entry : mVolumes)
  230. {
  231. UINT32 numProbes = (UINT32)entry.volume->getLightProbePositions().size();
  232. SPtr<GpuBuffer> localBuffer = entry.volume->getCoefficientsBuffer();
  233. // Note: Some of the coefficients might still be dirty (unrendered). Check for this and write them as black?
  234. UINT32 size = numProbes * sizeof(LightProbeSHCoefficients);
  235. UINT8* src = (UINT8*)localBuffer->lock(0, size, GBL_READ_ONLY);
  236. memcpy(dest, src, size);
  237. localBuffer->unlock();
  238. dest += size;
  239. }
  240. mProbeCoefficientsGPU->unlock();
  241. // Gather all positions
  242. UINT32 bufferOffset = 0;
  243. for(auto& entry : mVolumes)
  244. {
  245. const Vector<LightProbeInfo>& infos = entry.volume->getLightProbeInfos();
  246. const Vector<Vector3>& positions = entry.volume->getLightProbePositions();
  247. UINT32 numProbes = entry.volume->getNumActiveProbes();
  248. if (numProbes == 0)
  249. continue;
  250. Vector3 offset = entry.volume->getPosition();
  251. Quaternion rotation = entry.volume->getRotation();
  252. for(UINT32 i = 0; i < numProbes; i++)
  253. {
  254. Vector3 localPos = positions[i];
  255. Vector3 transformedPos = rotation.rotate(localPos) + offset;
  256. mTempTetrahedronPositions.push_back(transformedPos);
  257. mTempTetrahedronBufferIndices.push_back(bufferOffset + infos[i].bufferIdx);
  258. }
  259. bufferOffset += (UINT32)positions.size();
  260. }
  261. mTetrahedronInfos.clear();
  262. UINT32 innerVertexCount = (UINT32)mTempTetrahedronPositions.size();
  263. generateTetrahedronData(mTempTetrahedronPositions, mTetrahedronInfos, false);
  264. // Generate a mesh out of all the tetrahedron triangles
  265. // Note: Currently the entire volume is rendered as a single large mesh, which will isn't optimal as we can't
  266. // perform frustum culling. A better option would be to split the mesh into multiple smaller volumes, do
  267. // frustum culling and possibly even sort by distance from camera.
  268. UINT32 numTetrahedra = (UINT32)mTetrahedronInfos.size();
  269. UINT32 numVertices = numTetrahedra * 4 * 3;
  270. UINT32 numIndices = numTetrahedra * 4 * 3;
  271. SPtr<VertexDataDesc> vertexDesc = bs_shared_ptr_new<VertexDataDesc>();
  272. vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
  273. vertexDesc->addVertElem(VET_UINT1, VES_TEXCOORD);
  274. SPtr<MeshData> meshData = MeshData::create(numVertices, numIndices, vertexDesc);
  275. auto posIter = meshData->getVec3DataIter(VES_POSITION);
  276. auto idIter = meshData->getDWORDDataIter(VES_TEXCOORD);
  277. UINT32 tetIdx = 0;
  278. for(auto& entry : mTetrahedronInfos)
  279. {
  280. const Tetrahedron& volume = entry.volume;
  281. Vector3 center(BsZero);
  282. for(UINT32 i = 0; i < 4; i++)
  283. center += mTempTetrahedronPositions[volume.vertices[i]];
  284. center /= 4.0f;
  285. static const UINT32 Permutations[4][3] =
  286. {
  287. { 0, 1, 2 },
  288. { 0, 1, 3 },
  289. { 0, 2, 3 },
  290. { 1, 2, 3 }
  291. };
  292. for(UINT32 i = 0; i < 4; i++)
  293. {
  294. Vector3 A = mTempTetrahedronPositions[volume.vertices[Permutations[i][0]]];
  295. Vector3 B = mTempTetrahedronPositions[volume.vertices[Permutations[i][1]]];
  296. Vector3 C = mTempTetrahedronPositions[volume.vertices[Permutations[i][2]]];
  297. // Make sure the triangle is clockwise
  298. Vector3 e0 = A - C;
  299. Vector3 e1 = B - C;
  300. Vector3 normal = e0.cross(e1);
  301. if (normal.dot(A - center) < 0.0f)
  302. std::swap(B, C);
  303. posIter.addValue(A);
  304. posIter.addValue(B);
  305. posIter.addValue(C);
  306. idIter.addValue(tetIdx);
  307. idIter.addValue(tetIdx);
  308. idIter.addValue(tetIdx);
  309. }
  310. tetIdx++;
  311. }
  312. mVolumeMesh = Mesh::create(meshData);
  313. // Map vertices to actual SH coefficient indices, and write GPU buffer with tetrahedron information
  314. if (numTetrahedra > mMaxTetrahedra)
  315. {
  316. UINT32 newSize = Math::divideAndRoundUp(numTetrahedra, 64U) * 64U;
  317. resizeTetrahedronBuffer(newSize);
  318. }
  319. TetrahedronDataGPU* dst = (TetrahedronDataGPU*)mTetrahedronInfosGPU->lock(0, mTetrahedronInfosGPU->getSize(),
  320. GBL_WRITE_ONLY_DISCARD);
  321. for (auto& entry : mTetrahedronInfos)
  322. {
  323. for(UINT32 i = 0; i < 4; ++i)
  324. {
  325. // Check for outer vertices, which have no SH data associated with them
  326. if (entry.volume.vertices[i] >= (INT32)innerVertexCount)
  327. entry.volume.vertices[i] = -1;
  328. else
  329. entry.volume.vertices[i] = mTempTetrahedronBufferIndices[i];
  330. }
  331. memcpy(dst->indices, entry.volume.vertices, sizeof(UINT32) * 4);
  332. memcpy(&dst->transform, &entry.transform, sizeof(float) * 12);
  333. dst++;
  334. }
  335. mTetrahedronInfosGPU->unlock();
  336. mTempTetrahedronPositions.clear();
  337. mTempTetrahedronBufferIndices.clear();
  338. mTetrahedronVolumeDirty = false;
  339. }
  340. bool LightProbes::hasAnyProbes() const
  341. {
  342. for(auto& entry : mVolumes)
  343. {
  344. UINT32 numProbes = entry.volume->getNumActiveProbes();
  345. if (numProbes > 0)
  346. return true;
  347. }
  348. return false;
  349. }
  350. void LightProbes::resizeTetrahedronBuffer(UINT32 count)
  351. {
  352. GPU_BUFFER_DESC desc;
  353. desc.type = GBT_STRUCTURED;
  354. desc.elementSize = sizeof(TetrahedronDataGPU);
  355. desc.elementCount = count;
  356. desc.usage = GBU_STATIC;
  357. desc.format = BF_UNKNOWN;
  358. mTetrahedronInfosGPU = GpuBuffer::create(desc);
  359. mMaxTetrahedra = count;
  360. }
  361. void LightProbes::resizeCoefficientBuffer(UINT32 count)
  362. {
  363. GPU_BUFFER_DESC desc;
  364. desc.type = GBT_STRUCTURED;
  365. desc.elementSize = sizeof(LightProbeSHCoefficients);
  366. desc.elementCount = count;
  367. desc.usage = GBU_STATIC;
  368. desc.format = BF_UNKNOWN;
  369. mProbeCoefficientsGPU = GpuBuffer::create(desc);
  370. mMaxCoefficients = count;
  371. }
  372. /** Hash value generator for std::pair<INT32, INT32>. */
  373. struct pair_hash
  374. {
  375. size_t operator()(const std::pair<INT32, INT32>& key) const
  376. {
  377. size_t hash = 0;
  378. bs::hash_combine(hash, key.first);
  379. bs::hash_combine(hash, key.second);
  380. return hash;
  381. }
  382. };
  383. void LightProbes::generateTetrahedronData(Vector<Vector3>& positions, Vector<TetrahedronData>& output,
  384. bool generateExtrapolationVolume)
  385. {
  386. bs_frame_mark();
  387. {
  388. TetrahedronVolume volume = Triangulation::tetrahedralize(positions);
  389. if (generateExtrapolationVolume)
  390. {
  391. // We don't want ot handle the case where the user looks up a position and it falls outside of the
  392. // tetrahedron volume, as the math for projecting the point onto the volume might be too slow for the
  393. // shader (which we need in order not to have a sharp cutoff in lighting where the volume ends). Therefore
  394. // we extend the tetrahedron volume to "infinity" (technically to some far away distance, but we treat it as
  395. // infinity when calculating barycentric coordinates) by adding new points along the outer face normals.
  396. UINT32 numOuterFaces = (UINT32)volume.outerFaces.size();
  397. // Calculate face normals for outer faces
  398. //// Make an edge map
  399. struct Edge
  400. {
  401. INT32 faces[2];
  402. INT32 oppositeVerts[2];
  403. };
  404. FrameUnorderedMap<std::pair<INT32, INT32>, Edge, pair_hash> edgeMap;
  405. for (UINT32 i = 0; i < numOuterFaces; ++i)
  406. {
  407. for (UINT32 j = 0; j < 3; ++j)
  408. {
  409. INT32 v0 = volume.outerFaces[i].vertices[j];
  410. INT32 v1 = volume.outerFaces[i].vertices[(j + 1) % 3];
  411. // Keep the same ordering so other faces can find the same edge
  412. if (v0 > v1)
  413. std::swap(v0, v1);
  414. auto iterFind = edgeMap.find(std::make_pair(v0, v1));
  415. if (iterFind != edgeMap.end())
  416. {
  417. iterFind->second.faces[1] = i;
  418. iterFind->second.oppositeVerts[1] = (j + 2) % 3;
  419. }
  420. else
  421. {
  422. Edge edge;
  423. edge.faces[0] = i;
  424. edge.oppositeVerts[0] = (j + 2) % 3;
  425. edgeMap.insert(std::make_pair(std::make_pair(v0, v1), edge));
  426. }
  427. }
  428. }
  429. //// Generate face normals
  430. FrameVector<Vector3> faceNormals(volume.outerFaces.size());
  431. for (UINT32 i = 0; i < (UINT32)volume.outerFaces.size(); ++i)
  432. {
  433. const Vector3& v0 = positions[volume.outerFaces[i].vertices[0]];
  434. const Vector3& v1 = positions[volume.outerFaces[i].vertices[1]];
  435. const Vector3& v2 = positions[volume.outerFaces[i].vertices[2]];
  436. Vector3 e0 = v1 - v0;
  437. Vector3 e1 = v2 - v0;
  438. faceNormals[i] = Vector3::normalize(e1.cross(e0));
  439. }
  440. //// Generate vertex normals
  441. struct FaceVertex
  442. {
  443. Vector3 normal = Vector3::ZERO;
  444. UINT32 outerIdx = -1;
  445. };
  446. FrameUnorderedMap<INT32, FaceVertex> faceVertices;
  447. for (auto& entry : edgeMap)
  448. {
  449. const Edge& edge = entry.second;
  450. auto accumulateNormalForEdgeVertex = [&](UINT32 v0Idx, UINT32 v1Idx)
  451. {
  452. auto iter = faceVertices.insert(std::make_pair(v0Idx, FaceVertex()));
  453. FaceVertex& accum = iter.first->second;
  454. const Vector3& v0 = positions[v0Idx];
  455. auto accumulateNormalForFace = [&](INT32 faceIdx, INT32 v2LocIdx)
  456. {
  457. const TetrahedronFace& face = volume.outerFaces[faceIdx];
  458. // Vertices on the face, that aren't the vertex we're calculating the normal for
  459. const Vector3& v1 = positions[v1Idx];
  460. const Vector3& v2 = positions[face.vertices[v2LocIdx]];
  461. // Weight the contribution to the normal based on the angle spanned by the triangle
  462. Vector3 e0 = Vector3::normalize(v1 - v0);
  463. Vector3 e1 = Vector3::normalize(v2 - v0);
  464. float weight = acos(e0.dot(e1));
  465. accum.normal += weight * faceNormals[faceIdx];
  466. };
  467. accumulateNormalForFace(edge.faces[0], entry.second.oppositeVerts[0]);
  468. accumulateNormalForFace(edge.faces[1], entry.second.oppositeVerts[1]);
  469. };
  470. accumulateNormalForEdgeVertex(entry.first.first, entry.first.second);
  471. accumulateNormalForEdgeVertex(entry.first.second, entry.first.first);
  472. }
  473. for (auto& entry : faceVertices)
  474. entry.second.normal.normalize();
  475. // For each face vertex, generate an outer vertex along its normal
  476. static const float ExtrapolationDistance = 1000.0f;
  477. for(auto& entry : faceVertices)
  478. {
  479. entry.second.outerIdx = (UINT32)positions.size();
  480. Vector3 outerPos = positions[entry.first] + entry.second.normal * ExtrapolationDistance;
  481. positions.push_back(outerPos);
  482. }
  483. // For each face, generate outer tetrahedrons
  484. Vector<Vector3> outerVolumeVerts;
  485. FrameVector<TetrahedronVolume> outerTetrahedra;
  486. for (UINT32 i = 0; i < numOuterFaces; ++i)
  487. {
  488. const TetrahedronFace& face = volume.outerFaces[i];
  489. UINT32 originalIndices[6];
  490. for (UINT32 j = 0; j < 3; j++)
  491. {
  492. const FaceVertex& faceVertex = faceVertices[face.vertices[j]];
  493. outerVolumeVerts.push_back(positions[face.vertices[j]]);
  494. outerVolumeVerts.push_back(positions[faceVertex.outerIdx]);
  495. originalIndices[j * 2 + 0] = face.vertices[j];
  496. originalIndices[j * 2 + 1] = faceVertex.outerIdx;
  497. }
  498. TetrahedronVolume outerVolume = Triangulation::tetrahedralize(outerVolumeVerts);
  499. UINT32 tetStartIdx = (UINT32)volume.tetrahedra.size();
  500. for (auto& entry : outerVolume.tetrahedra)
  501. {
  502. // Remap vertices back to global array
  503. for (UINT32 j = 0; j < 4; j++)
  504. entry.vertices[j] = originalIndices[entry.vertices[j]];
  505. // Remap neighbors to global array
  506. for (UINT32 j = 0; j < 4; j++)
  507. {
  508. if (entry.neighbors[j] != -1)
  509. {
  510. // Valid neighbor, map to global array
  511. entry.neighbors[j] += tetStartIdx;
  512. }
  513. }
  514. }
  515. // Connect the new volume to the original face
  516. for (auto& entry : outerVolume.outerFaces)
  517. {
  518. bool isValid = true;
  519. for (UINT32 j = 0; j < 3; j++)
  520. {
  521. if (entry.vertices[j] % 2 == 1)
  522. {
  523. isValid = false;
  524. break;
  525. }
  526. }
  527. if (!isValid)
  528. continue;
  529. Tetrahedron& outerTet = outerVolume.tetrahedra[entry.tetrahedron];
  530. UINT32 oppositeVert = -1;
  531. for (UINT32 j = 0; j < 4; j++)
  532. {
  533. if(outerTet.vertices[j] != entry.vertices[0] &&
  534. outerTet.vertices[j] != entry.vertices[1] &&
  535. outerTet.vertices[j] != entry.vertices[2])
  536. {
  537. oppositeVert = j;
  538. break;
  539. }
  540. }
  541. assert(outerTet.neighbors[oppositeVert] == -1);
  542. outerTet.neighbors[oppositeVert] = face.tetrahedron;
  543. Tetrahedron& innerTet = volume.tetrahedra[face.tetrahedron];
  544. for(UINT32 j = 0; j < 4; j++)
  545. {
  546. if (innerTet.neighbors[j] == -1)
  547. innerTet.neighbors[j] = entry.tetrahedron;
  548. }
  549. }
  550. outerTetrahedra.push_back(outerVolume);
  551. outerVolumeVerts.clear();
  552. // Add to global tetrahedra array
  553. for (auto& entry : outerVolume.tetrahedra)
  554. volume.tetrahedra.push_back(entry);
  555. }
  556. // Note: Not forming neighbor connections between outer tetrahedrons. Since we generate them separately
  557. // we can't guarantee they even exist.
  558. }
  559. // Generate matrices
  560. UINT32 numOutputTets = (UINT32)volume.tetrahedra.size();
  561. output.reserve(numOutputTets);
  562. // Insert tetrahedrons, generate matrices
  563. for(UINT32 i = 0; i < (UINT32)volume.tetrahedra.size(); ++i)
  564. {
  565. TetrahedronData entry;
  566. entry.volume = volume.tetrahedra[i];
  567. // Generate a matrix that can be used for calculating barycentric coordinates
  568. // To determine a point within a tetrahedron, using barycentric coordinates, we use:
  569. // P = (P1 - P4) * a + (P2 - P4) * b + (P3 - P4) * c + P4
  570. //
  571. // Where P1, P2, P3, P4 are the corners of the tetrahedron.
  572. //
  573. // Expanded for each coordinate this is:
  574. // x = (x1 - x4) * a + (x2 - x4) * b + (x3 - x4) * c + x4
  575. // y = (y1 - y4) * a + (y2 - y4) * b + (y3 - y4) * c + y4
  576. // z = (z1 - z4) * a + (z2 - z4) * b + (z3 - z4) * c + z4
  577. //
  578. // In matrix form this is:
  579. // a
  580. // P = [P1 - P4, P2 - P4, P3 - P4, P4] [b]
  581. // c
  582. // 1
  583. //
  584. // Solved for barycentric coordinates:
  585. // a
  586. // [b] = Minv * P
  587. // c
  588. // 1
  589. //
  590. // Where Minv is the inverse of the matrix above.
  591. const Vector3& P1 = positions[volume.tetrahedra[i].vertices[0]];
  592. const Vector3& P2 = positions[volume.tetrahedra[i].vertices[1]];
  593. const Vector3& P3 = positions[volume.tetrahedra[i].vertices[2]];
  594. const Vector3& P4 = positions[volume.tetrahedra[i].vertices[3]];
  595. Matrix4 mat;
  596. mat.setColumn(0, Vector4(P1 - P4, 0.0f));
  597. mat.setColumn(1, Vector4(P2 - P4, 0.0f));
  598. mat.setColumn(2, Vector4(P3 - P4, 0.0f));
  599. mat.setColumn(3, Vector4(P4, 1.0f));
  600. entry.transform = mat.inverse();
  601. output.push_back(entry);
  602. }
  603. }
  604. bs_frame_clear();
  605. }
  606. }}