1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "BsLightProbes.h" |
4 | #include "Renderer/BsLightProbeVolume.h" |
5 | #include "RenderAPI/BsGpuBuffer.h" |
6 | #include "BsRendererView.h" |
7 | #include "BsRenderBeastIBLUtility.h" |
8 | #include "Mesh/BsMesh.h" |
9 | #include "RenderAPI/BsVertexDataDesc.h" |
10 | #include "Material/BsGpuParamsSet.h" |
11 | #include "Renderer/BsRendererUtility.h" |
12 | #include "Renderer/BsSkybox.h" |
13 | #include "Utility/BsRendererTextures.h" |
14 | |
15 | namespace bs { namespace ct |
16 | { |
17 | TetrahedraRenderParamDef gTetrahedraRenderParamDef; |
18 | |
19 | TetrahedraRenderMat::TetrahedraRenderMat() |
20 | { |
21 | mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDepthBufferTex" , mDepthBufferTex); |
22 | |
23 | SAMPLER_STATE_DESC pointSampDesc; |
24 | pointSampDesc.minFilter = FO_POINT; |
25 | pointSampDesc.magFilter = FO_POINT; |
26 | pointSampDesc.mipFilter = FO_POINT; |
27 | pointSampDesc.addressMode.u = TAM_CLAMP; |
28 | pointSampDesc.addressMode.v = TAM_CLAMP; |
29 | pointSampDesc.addressMode.w = TAM_CLAMP; |
30 | |
31 | SPtr<SamplerState> pointSampState = SamplerState::create(pointSampDesc); |
32 | |
33 | if(mParams->hasSamplerState(GPT_FRAGMENT_PROGRAM, "gDepthBufferSamp" )) |
34 | mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gDepthBufferSamp" , pointSampState); |
35 | else if(mParams->hasSamplerState(GPT_FRAGMENT_PROGRAM, "gDepthBufferTex" )) |
36 | mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gDepthBufferTex" , pointSampState); |
37 | |
38 | mParamBuffer = gTetrahedraRenderParamDef.createBuffer(); |
39 | mParams->setParamBlockBuffer("Params" , mParamBuffer); |
40 | } |
41 | |
42 | void TetrahedraRenderMat::execute(const RendererView& view, const SPtr<Texture>& sceneDepth, const SPtr<Mesh>& mesh, |
43 | const SPtr<RenderTexture>& output) |
44 | { |
45 | BS_RENMAT_PROFILE_BLOCK |
46 | |
47 | const TextureProperties& texProps = sceneDepth->getProperties(); |
48 | |
49 | Vector2I texSize(texProps.getWidth(), texProps.getHeight()); |
50 | gTetrahedraRenderParamDef.gDepthTexSize.set(mParamBuffer, texSize); |
51 | |
52 | mDepthBufferTex.set(sceneDepth); |
53 | mParams->setParamBlockBuffer("PerCamera" , view.getPerViewBuffer()); |
54 | |
55 | RenderAPI& rapi = RenderAPI::instance(); |
56 | rapi.setRenderTarget(output); |
57 | |
58 | bind(); |
59 | gRendererUtility().draw(mesh); |
60 | } |
61 | |
62 | void TetrahedraRenderMat::getOutputDesc(const RendererView& view, POOLED_RENDER_TEXTURE_DESC& colorDesc, |
63 | POOLED_RENDER_TEXTURE_DESC& depthDesc) |
64 | { |
65 | const RendererViewProperties& viewProps = view.getProperties(); |
66 | UINT32 width = viewProps.target.viewRect.width; |
67 | UINT32 height = viewProps.target.viewRect.height; |
68 | UINT32 numSamples = viewProps.target.numSamples; |
69 | |
70 | colorDesc = POOLED_RENDER_TEXTURE_DESC::create2D(PF_R16U, width, height, TU_RENDERTARGET, numSamples); |
71 | depthDesc = POOLED_RENDER_TEXTURE_DESC::create2D(PF_D32, width, height, TU_DEPTHSTENCIL, numSamples); |
72 | } |
73 | |
74 | TetrahedraRenderMat* TetrahedraRenderMat::getVariation(bool msaa, bool singleSampleMSAA) |
75 | { |
76 | if (msaa) |
77 | { |
78 | if (singleSampleMSAA) |
79 | return get(getVariation<true, true>()); |
80 | |
81 | return get(getVariation<true, false>()); |
82 | } |
83 | |
84 | return get(getVariation<false, false>()); |
85 | } |
86 | |
87 | IrradianceEvaluateParamDef gIrradianceEvaluateParamDef; |
88 | |
89 | IrradianceEvaluateMat::IrradianceEvaluateMat() |
90 | :mGBufferParams(GPT_FRAGMENT_PROGRAM, mParams) |
91 | { |
92 | mSkyOnly = mVariation.getBool("SKY_ONLY" ); |
93 | |
94 | mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSkyIrradianceTex" , mParamSkyIrradianceTex); |
95 | mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gAmbientOcclusionTex" , mParamAmbientOcclusionTex); |
96 | |
97 | if(!mSkyOnly) |
98 | { |
99 | mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex" , mParamInputTex); |
100 | mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSHCoeffs" , mParamSHCoeffsTexture); |
101 | mParams->getBufferParam(GPT_FRAGMENT_PROGRAM, "gTetrahedra" , mParamTetrahedraBuffer); |
102 | mParams->getBufferParam(GPT_FRAGMENT_PROGRAM, "gTetFaces" , mParamTetFacesBuffer); |
103 | } |
104 | |
105 | mParamBuffer = gIrradianceEvaluateParamDef.createBuffer(); |
106 | mParams->setParamBlockBuffer("Params" , mParamBuffer); |
107 | } |
108 | |
109 | void IrradianceEvaluateMat::execute(const RendererView& view, const GBufferTextures& gbuffer, |
110 | const SPtr<Texture>& lightProbeIndices, const LightProbesInfo& lightProbesInfo, const Skybox* skybox, |
111 | const SPtr<Texture>& ambientOcclusion, const SPtr<RenderTexture>& output) |
112 | { |
113 | BS_RENMAT_PROFILE_BLOCK |
114 | |
115 | const RendererViewProperties& viewProps = view.getProperties(); |
116 | |
117 | mGBufferParams.bind(gbuffer); |
118 | |
119 | float skyBrightness = 1.0f; |
120 | SPtr<Texture> skyIrradiance; |
121 | if (skybox != nullptr) |
122 | { |
123 | skyIrradiance = skybox->getIrradiance(); |
124 | skyBrightness = skybox->getBrightness(); |
125 | } |
126 | |
127 | if(skyIrradiance == nullptr) |
128 | skyIrradiance = RendererTextures::defaultIndirect; |
129 | |
130 | mParamSkyIrradianceTex.set(skyIrradiance); |
131 | mParamAmbientOcclusionTex.set(ambientOcclusion); |
132 | |
133 | RenderSurfaceMask loadMask = RT_COLOR0; |
134 | if(!mSkyOnly) |
135 | { |
136 | mParamInputTex.set(lightProbeIndices); |
137 | mParamSHCoeffsTexture.set(lightProbesInfo.shCoefficients); |
138 | mParamTetrahedraBuffer.set(lightProbesInfo.tetrahedra); |
139 | mParamTetFacesBuffer.set(lightProbesInfo.faces); |
140 | } |
141 | else |
142 | { |
143 | // No need to load depth/stencil when rendering light probes as we'll be using a newly created intermediate |
144 | // depth buffer |
145 | loadMask |= RT_DEPTH_STENCIL; |
146 | } |
147 | |
148 | gIrradianceEvaluateParamDef.gSkyBrightness.set(mParamBuffer, skyBrightness); |
149 | gIrradianceEvaluateParamDef.gNumTetrahedra.set(mParamBuffer, lightProbesInfo.numTetrahedra); |
150 | mParamBuffer->flushToGPU(); |
151 | |
152 | mParams->setParamBlockBuffer("PerCamera" , view.getPerViewBuffer()); |
153 | |
154 | // Render |
155 | RenderAPI& rapi = RenderAPI::instance(); |
156 | rapi.setRenderTarget(output, FBT_DEPTH | FBT_STENCIL, loadMask); |
157 | |
158 | bind(); |
159 | |
160 | gRendererUtility().drawScreenQuad(Rect2(0.0f, 0.0f, (float)viewProps.target.viewRect.width, |
161 | (float)viewProps.target.viewRect.height)); |
162 | |
163 | rapi.setRenderTarget(nullptr); |
164 | } |
165 | |
166 | IrradianceEvaluateMat* IrradianceEvaluateMat::getVariation(bool msaa, bool singleSampleMSAA, bool skyOnly) |
167 | { |
168 | if(skyOnly) |
169 | { |
170 | if (msaa) |
171 | { |
172 | if (singleSampleMSAA) |
173 | return get(getVariation<true, true, true>()); |
174 | |
175 | return get(getVariation<true, false, true>()); |
176 | } |
177 | |
178 | return get(getVariation<false, false, true>()); |
179 | } |
180 | else |
181 | { |
182 | if (msaa) |
183 | { |
184 | if (singleSampleMSAA) |
185 | return get(getVariation<true, true, false>()); |
186 | |
187 | return get(getVariation<true, false, false>()); |
188 | } |
189 | |
190 | return get(getVariation<false, false, false>()); |
191 | } |
192 | } |
193 | |
194 | /** Hash value generator for std::pair<INT32, INT32>. */ |
195 | struct pair_hash |
196 | { |
197 | size_t operator()(const std::pair<INT32, INT32>& key) const |
198 | { |
199 | size_t hash = 0; |
200 | bs::bs_hash_combine(hash, key.first); |
201 | bs::bs_hash_combine(hash, key.second); |
202 | |
203 | return hash; |
204 | } |
205 | }; |
206 | |
207 | /** Information about a single tetrahedron, for use on the GPU. */ |
208 | struct TetrahedronDataGPU |
209 | { |
210 | UINT32 indices[4]; |
211 | Vector2I offsets[4]; |
212 | Matrix3x4 transform; |
213 | }; |
214 | |
215 | /** Information about a single tetrahedron face, for use on the GPU. */ |
216 | struct TetrahedronFaceDataGPU |
217 | { |
218 | Vector4 corners[3]; |
219 | Vector4 normals[3]; |
220 | UINT32 isQuadratic; |
221 | float padding[3]; |
222 | }; |
223 | |
224 | LightProbes::LightProbes() |
225 | :mTetrahedronVolumeDirty(false), mMaxCoefficientRows(0), mMaxTetrahedra(0), mMaxFaces(0), mNumValidTetrahedra(0) |
226 | { } |
227 | |
228 | void LightProbes::notifyAdded(LightProbeVolume* volume) |
229 | { |
230 | UINT32 handle = (UINT32)mVolumes.size(); |
231 | |
232 | VolumeInfo info; |
233 | info.volume = volume; |
234 | info.isDirty = true; |
235 | |
236 | mVolumes.push_back(info); |
237 | volume->setRendererId(handle); |
238 | |
239 | notifyDirty(volume); |
240 | } |
241 | |
242 | void LightProbes::notifyDirty(LightProbeVolume* volume) |
243 | { |
244 | UINT32 handle = volume->getRendererId(); |
245 | mVolumes[handle].isDirty = true; |
246 | |
247 | mTetrahedronVolumeDirty = true; |
248 | } |
249 | |
250 | void LightProbes::notifyRemoved(LightProbeVolume* volume) |
251 | { |
252 | UINT32 handle = volume->getRendererId(); |
253 | |
254 | LightProbeVolume* lastVolume = mVolumes.back().volume; |
255 | UINT32 lastHandle = lastVolume->getRendererId(); |
256 | |
257 | if (handle != lastHandle) |
258 | { |
259 | // Swap current last element with the one we want to erase |
260 | std::swap(mVolumes[handle], mVolumes[lastHandle]); |
261 | lastVolume->setRendererId(handle); |
262 | } |
263 | |
264 | // Erase last (empty) element |
265 | mVolumes.erase(mVolumes.end() - 1); |
266 | |
267 | mTetrahedronVolumeDirty = true; |
268 | } |
269 | |
270 | void LightProbes::updateProbes() |
271 | { |
272 | if (!mTetrahedronVolumeDirty) |
273 | return; |
274 | |
275 | // Move all coefficients into the global buffer |
276 | UINT32 numRows = 0; |
277 | for(auto& entry : mVolumes) |
278 | { |
279 | SPtr<Texture> localTexture = entry.volume->getCoefficientsTexture(); |
280 | numRows += localTexture->getProperties().getHeight(); |
281 | } |
282 | |
283 | if(numRows > mMaxCoefficientRows) |
284 | resizeCoefficientTexture(numRows + 4); |
285 | |
286 | UINT32 rowIdx = 0; |
287 | for(auto& entry : mVolumes) |
288 | { |
289 | TEXTURE_COPY_DESC copyDesc; |
290 | copyDesc.dstPosition = Vector3I(0, rowIdx, 0); |
291 | |
292 | SPtr<Texture> localTexture = entry.volume->getCoefficientsTexture(); |
293 | localTexture->copy(mProbeCoefficientsGPU, copyDesc); |
294 | |
295 | rowIdx += localTexture->getProperties().getHeight(); |
296 | } |
297 | |
298 | // Gather all positions |
299 | UINT32 bufferOffset = 0; |
300 | rowIdx = 0; |
301 | for(auto& entry : mVolumes) |
302 | { |
303 | const Vector<LightProbeInfo>& infos = entry.volume->getLightProbeInfos(); |
304 | const Vector<Vector3>& positions = entry.volume->getLightProbePositions(); |
305 | |
306 | UINT32 numProbes = entry.volume->getNumActiveProbes(); |
307 | |
308 | if (numProbes == 0) |
309 | continue; |
310 | |
311 | const Transform& tfrm = entry.volume->getTransform(); |
312 | Vector3 offset = tfrm.getPosition(); |
313 | Quaternion rotation = tfrm.getRotation(); |
314 | |
315 | for (UINT32 i = 0; i < numProbes; i++) |
316 | { |
317 | Vector3 localPos = positions[i]; |
318 | Vector3 transformedPos = rotation.rotate(localPos) + offset; |
319 | mTempTetrahedronPositions.push_back(transformedPos); |
320 | |
321 | mTempTetrahedronBufferIndices.push_back(bufferOffset + infos[i].bufferIdx); |
322 | |
323 | Vector2I offset = IBLUtility::getSHCoeffXYFromIdx(infos[i].bufferIdx, 3); |
324 | mTempTetrahedronBufferOffsets.push_back(offset); |
325 | } |
326 | |
327 | SPtr<Texture> localTexture = entry.volume->getCoefficientsTexture(); |
328 | rowIdx += localTexture->getProperties().getHeight(); |
329 | bufferOffset += (UINT32)positions.size(); |
330 | } |
331 | |
332 | mTetrahedronInfos.clear(); |
333 | |
334 | Vector<TetrahedronFaceData> outerFaces; |
335 | generateTetrahedronData(mTempTetrahedronPositions, mTetrahedronInfos, outerFaces, true); |
336 | |
337 | // Find valid tetrahedrons |
338 | UINT32 numTetrahedra = (UINT32)mTetrahedronInfos.size(); |
339 | |
340 | bool* validTets = (bool*)bs_stack_alloc(sizeof(bool) * numTetrahedra); |
341 | mNumValidTetrahedra = 0; |
342 | for (UINT32 i = 0; i < (UINT32)mTetrahedronInfos.size(); i++) |
343 | { |
344 | const TetrahedronData& entry = mTetrahedronInfos[i]; |
345 | |
346 | const Vector3& P1 = mTempTetrahedronPositions[entry.volume.vertices[0]]; |
347 | const Vector3& P2 = mTempTetrahedronPositions[entry.volume.vertices[1]]; |
348 | const Vector3& P3 = mTempTetrahedronPositions[entry.volume.vertices[2]]; |
349 | const Vector3& P4 = mTempTetrahedronPositions[entry.volume.vertices[3]]; |
350 | |
351 | Vector3 E1 = P1 - P4; |
352 | Vector3 E2 = P2 - P4; |
353 | Vector3 E3 = P3 - P4; |
354 | |
355 | // If tetrahedron is co-planar just ignore it, shader will use some other nearby one instead. We can't |
356 | // handle coplanar tetrahedrons because the matrix is not invertible, and for nearly co-planar ones the |
357 | // math breaks down because of precision issues. |
358 | validTets[i] = fabs(Vector3::dot(Vector3::normalize(Vector3::cross(E1, E2)), E3)) > 0.0001f; |
359 | |
360 | if (validTets[i]) |
361 | mNumValidTetrahedra++; |
362 | } |
363 | |
364 | UINT32 numValidFaces = 0; |
365 | for(auto& entry : outerFaces) |
366 | { |
367 | if (validTets[entry.tetrahedron]) |
368 | numValidFaces++; |
369 | } |
370 | |
371 | // Generate a mesh out of all the tetrahedron triangles |
372 | // Note: Currently the entire volume is rendered as a single large mesh, which will isn't optimal as we can't |
373 | // perform frustum culling. A better option would be to split the mesh into multiple smaller volumes, do |
374 | // frustum culling and possibly even sort by distance from camera. |
375 | UINT32 numVertices = mNumValidTetrahedra * 4 * 3 + numValidFaces * 9 * 3; |
376 | |
377 | SPtr<VertexDataDesc> vertexDesc = bs_shared_ptr_new<VertexDataDesc>(); |
378 | vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION); |
379 | vertexDesc->addVertElem(VET_UINT1, VES_TEXCOORD); |
380 | |
381 | SPtr<MeshData> meshData = MeshData::create(numVertices, numVertices, vertexDesc); |
382 | auto posIter = meshData->getVec3DataIter(VES_POSITION); |
383 | auto idIter = meshData->getDWORDDataIter(VES_TEXCOORD); |
384 | UINT32* indices = meshData->getIndices32(); |
385 | |
386 | // Insert inner tetrahedron triangles |
387 | UINT32 tetIdx = 0; |
388 | for (UINT32 i = 0; i < (UINT32)mTetrahedronInfos.size(); i++) |
389 | { |
390 | if (!validTets[i]) |
391 | continue; |
392 | |
393 | const Tetrahedron& volume = mTetrahedronInfos[i].volume; |
394 | |
395 | Vector3 center(BsZero); |
396 | for(UINT32 j = 0; j < 4; j++) |
397 | center += mTempTetrahedronPositions[volume.vertices[j]]; |
398 | |
399 | center /= 4.0f; |
400 | |
401 | static const UINT32 Permutations[4][3] = |
402 | { |
403 | { 0, 1, 2 }, |
404 | { 0, 1, 3 }, |
405 | { 0, 2, 3 }, |
406 | { 1, 2, 3 } |
407 | }; |
408 | |
409 | for(UINT32 j = 0; j < 4; j++) |
410 | { |
411 | Vector3 A = mTempTetrahedronPositions[volume.vertices[Permutations[j][0]]]; |
412 | Vector3 B = mTempTetrahedronPositions[volume.vertices[Permutations[j][1]]]; |
413 | Vector3 C = mTempTetrahedronPositions[volume.vertices[Permutations[j][2]]]; |
414 | |
415 | // Make sure the triangle is clockwise, facing away from the center |
416 | Vector3 e0 = A - C; |
417 | Vector3 e1 = B - C; |
418 | |
419 | Vector3 normal = e0.cross(e1); |
420 | if (normal.dot(A - center) > 0.0f) |
421 | std::swap(B, C); |
422 | |
423 | posIter.addValue(A); |
424 | posIter.addValue(B); |
425 | posIter.addValue(C); |
426 | |
427 | idIter.addValue(tetIdx); |
428 | idIter.addValue(tetIdx); |
429 | idIter.addValue(tetIdx); |
430 | |
431 | indices[0] = tetIdx * 4 * 3 + j * 3 + 0; |
432 | indices[1] = tetIdx * 4 * 3 + j * 3 + 1; |
433 | indices[2] = tetIdx * 4 * 3 + j * 3 + 2; |
434 | |
435 | indices += 3; |
436 | } |
437 | |
438 | tetIdx++; |
439 | } |
440 | |
441 | // Generate an edge map for outer faces (required for step below) |
442 | struct Edge |
443 | { |
444 | UINT32 vertInner[2]; |
445 | UINT32 vertOuter[2]; |
446 | UINT32 face[2]; |
447 | }; |
448 | |
449 | FrameUnorderedMap<std::pair<INT32, INT32>, Edge, pair_hash> edgeMap; |
450 | for(UINT32 i = 0; i < (UINT32)outerFaces.size(); i++) |
451 | { |
452 | if (!validTets[outerFaces[i].tetrahedron]) |
453 | continue; |
454 | |
455 | for (UINT32 j = 0; j < 3; ++j) |
456 | { |
457 | UINT32 v0 = outerFaces[i].innerVertices[j]; |
458 | UINT32 v1 = outerFaces[i].innerVertices[(j + 1) % 3]; |
459 | |
460 | // Keep the same ordering so other faces can find the same edge |
461 | if (v0 > v1) |
462 | std::swap(v0, v1); |
463 | |
464 | auto iterFind = edgeMap.find(std::make_pair((INT32)v0, (INT32)v1)); |
465 | if (iterFind != edgeMap.end()) |
466 | { |
467 | iterFind->second.face[1] = i; |
468 | } |
469 | else |
470 | { |
471 | Edge edge; |
472 | edge.vertInner[0] = outerFaces[i].innerVertices[j]; |
473 | edge.vertInner[1] = outerFaces[i].innerVertices[(j + 1) % 3]; |
474 | edge.vertOuter[0] = outerFaces[i].outerVertices[j]; |
475 | edge.vertOuter[1] = outerFaces[i].outerVertices[(j + 1) % 3]; |
476 | edge.face[0] = i; |
477 | edge.face[1] = -1; |
478 | |
479 | edgeMap.insert(std::make_pair(std::make_pair((INT32)v0, (INT32)v1), edge)); |
480 | } |
481 | } |
482 | } |
483 | |
484 | // Generate front and back triangles for extruded outer faces |
485 | UINT32 faceIdx = 0; |
486 | for(UINT32 i = 0; i < (UINT32)outerFaces.size(); i++) |
487 | { |
488 | if (!validTets[outerFaces[i].tetrahedron]) |
489 | continue; |
490 | |
491 | const TetrahedronFaceData& entry = outerFaces[i]; |
492 | |
493 | static const UINT32 Permutations[2][3] = { {0, 1, 2 }, { 3, 4, 5} }; |
494 | |
495 | // Make sure the triangle is clockwise, facing away from the center |
496 | Vector3 center(BsZero); |
497 | for (UINT32 k = 0; k < 3; k++) |
498 | { |
499 | center += mTempTetrahedronPositions[entry.innerVertices[k]]; |
500 | center += mTempTetrahedronPositions[entry.outerVertices[k]]; |
501 | } |
502 | |
503 | center /= 6.0f; |
504 | |
505 | for(UINT32 j = 0; j < 2; ++j) |
506 | { |
507 | UINT32 idxA = Permutations[j][0]; |
508 | UINT32 idxB = Permutations[j][1]; |
509 | UINT32 idxC = Permutations[j][2]; |
510 | |
511 | idxA = idxA > 2 ? entry.outerVertices[idxA - 3] : entry.innerVertices[idxA]; |
512 | idxB = idxB > 2 ? entry.outerVertices[idxB - 3] : entry.innerVertices[idxB]; |
513 | idxC = idxC > 2 ? entry.outerVertices[idxC - 3] : entry.innerVertices[idxC]; |
514 | |
515 | Vector3 A = mTempTetrahedronPositions[idxA]; |
516 | Vector3 B = mTempTetrahedronPositions[idxB]; |
517 | Vector3 C = mTempTetrahedronPositions[idxC]; |
518 | |
519 | Vector3 e0 = A - C; |
520 | Vector3 e1 = B - C; |
521 | |
522 | Vector3 normal = e0.cross(e1); |
523 | if (normal.dot(A - center) > 0.0f) |
524 | std::swap(A, B); |
525 | |
526 | posIter.addValue(A); |
527 | posIter.addValue(B); |
528 | posIter.addValue(C); |
529 | |
530 | idIter.addValue(tetIdx + faceIdx); |
531 | idIter.addValue(tetIdx + faceIdx); |
532 | idIter.addValue(tetIdx + faceIdx); |
533 | |
534 | indices[0] = tetIdx * 4 * 3 + faceIdx * 2 * 3 + j * 3 + 0; |
535 | indices[1] = tetIdx * 4 * 3 + faceIdx * 2 * 3 + j * 3 + 1; |
536 | indices[2] = tetIdx * 4 * 3 + faceIdx * 2 * 3 + j * 3 + 2; |
537 | |
538 | indices += 3; |
539 | } |
540 | |
541 | faceIdx++; |
542 | } |
543 | |
544 | // Generate sides for extruded outer faces |
545 | UINT32 sideIdx = 0; |
546 | for(auto& entry : edgeMap) |
547 | { |
548 | const Edge& edge = entry.second; |
549 | |
550 | for (UINT32 i = 0; i < 2; i++) |
551 | { |
552 | const TetrahedronFaceData& face = outerFaces[edge.face[i]]; |
553 | |
554 | // Make sure the triangle is clockwise, facing away from the center |
555 | Vector3 center(BsZero); |
556 | for (UINT32 k = 0; k < 3; k++) |
557 | { |
558 | center += mTempTetrahedronPositions[face.innerVertices[k]]; |
559 | center += mTempTetrahedronPositions[face.outerVertices[k]]; |
560 | } |
561 | |
562 | center /= 6.0f; |
563 | |
564 | static const UINT32 Permutations[2][3] = { {0, 1, 2 }, { 1, 2, 3} }; |
565 | for(UINT32 j = 0; j < 2; ++j) |
566 | { |
567 | UINT32 idxA = Permutations[j][0]; |
568 | UINT32 idxB = Permutations[j][1]; |
569 | UINT32 idxC = Permutations[j][2]; |
570 | |
571 | idxA = idxA > 1 ? edge.vertOuter[idxA - 2] : edge.vertInner[idxA]; |
572 | idxB = idxB > 1 ? edge.vertOuter[idxB - 2] : edge.vertInner[idxB]; |
573 | idxC = idxC > 1 ? edge.vertOuter[idxC - 2] : edge.vertInner[idxC]; |
574 | |
575 | Vector3 A = mTempTetrahedronPositions[idxA]; |
576 | Vector3 B = mTempTetrahedronPositions[idxB]; |
577 | Vector3 C = mTempTetrahedronPositions[idxC]; |
578 | |
579 | Vector3 e0 = A - C; |
580 | Vector3 e1 = B - C; |
581 | |
582 | Vector3 normal = e0.cross(e1); |
583 | if (normal.dot(A - center) > 0.0f) |
584 | std::swap(A, B); |
585 | |
586 | posIter.addValue(A); |
587 | posIter.addValue(B); |
588 | posIter.addValue(C); |
589 | |
590 | idIter.addValue(tetIdx + edge.face[i]); |
591 | idIter.addValue(tetIdx + edge.face[i]); |
592 | idIter.addValue(tetIdx + edge.face[i]); |
593 | |
594 | indices[0] = tetIdx * 4 * 3 + faceIdx * 2 * 3 + sideIdx * 2 * 3 + j * 3 + 0; |
595 | indices[1] = tetIdx * 4 * 3 + faceIdx * 2 * 3 + sideIdx * 2 * 3 + j * 3 + 1; |
596 | indices[2] = tetIdx * 4 * 3 + faceIdx * 2 * 3 + sideIdx * 2 * 3 + j * 3 + 2; |
597 | |
598 | indices += 3; |
599 | } |
600 | |
601 | sideIdx++; |
602 | } |
603 | } |
604 | |
605 | // Generate "caps" on the end of the extruded volume |
606 | UINT32 capIdx = 0; |
607 | for(UINT32 i = 0; i < (UINT32)outerFaces.size(); i++) |
608 | { |
609 | if (!validTets[outerFaces[i].tetrahedron]) |
610 | continue; |
611 | |
612 | const TetrahedronFaceData& entry = outerFaces[i]; |
613 | |
614 | Vector3 A = mTempTetrahedronPositions[entry.outerVertices[0]]; |
615 | Vector3 B = mTempTetrahedronPositions[entry.outerVertices[1]]; |
616 | Vector3 C = mTempTetrahedronPositions[entry.outerVertices[2]]; |
617 | |
618 | // Make sure the triangle is clockwise, facing toward the center |
619 | const Tetrahedron& tet = mTetrahedronInfos[entry.tetrahedron].volume; |
620 | |
621 | Vector3 center(BsZero); |
622 | for(UINT32 j = 0; j < 4; j++) |
623 | center += mTempTetrahedronPositions[tet.vertices[j]]; |
624 | |
625 | center /= 4.0f; |
626 | |
627 | Vector3 e0 = A - C; |
628 | Vector3 e1 = B - C; |
629 | |
630 | Vector3 normal = e0.cross(e1); |
631 | if (normal.dot(A - center) < 0.0f) |
632 | std::swap(B, C); |
633 | |
634 | posIter.addValue(A); |
635 | posIter.addValue(B); |
636 | posIter.addValue(C); |
637 | |
638 | idIter.addValue(-1); |
639 | idIter.addValue(-1); |
640 | idIter.addValue(-1); |
641 | |
642 | indices[0] = tetIdx * 4 * 3 + faceIdx * 8 * 3 + capIdx * 3 + 0; |
643 | indices[1] = tetIdx * 4 * 3 + faceIdx * 8 * 3 + capIdx * 3 + 1; |
644 | indices[2] = tetIdx * 4 * 3 + faceIdx * 8 * 3 + capIdx * 3 + 2; |
645 | |
646 | indices += 3; |
647 | capIdx++; |
648 | } |
649 | |
650 | mVolumeMesh = Mesh::create(meshData); |
651 | |
652 | // Map vertices to actual SH coefficient indices, and write GPU buffer with tetrahedron information |
653 | if ((mNumValidTetrahedra + numValidFaces) > mMaxTetrahedra) |
654 | { |
655 | UINT32 newSize = Math::divideAndRoundUp(mNumValidTetrahedra + numValidFaces, 64U) * 64U; |
656 | resizeTetrahedronBuffer(newSize); |
657 | } |
658 | |
659 | TetrahedronDataGPU* dst = (TetrahedronDataGPU*)mTetrahedronInfosGPU->lock(0, mTetrahedronInfosGPU->getSize(), |
660 | GBL_WRITE_ONLY_DISCARD); |
661 | |
662 | // Write inner tetrahedron data |
663 | for (UINT32 i = 0; i < (UINT32)mTetrahedronInfos.size(); i++) |
664 | { |
665 | if (!validTets[i]) |
666 | continue; |
667 | |
668 | TetrahedronData& entry = mTetrahedronInfos[i]; |
669 | |
670 | Vector2I offsets[4]; |
671 | for(UINT32 j = 0; j < 4; ++j) |
672 | { |
673 | entry.volume.vertices[j] = mTempTetrahedronBufferIndices[entry.volume.vertices[j]]; |
674 | offsets[j] = mTempTetrahedronBufferOffsets[entry.volume.vertices[j]]; |
675 | } |
676 | |
677 | memcpy(dst->indices, entry.volume.vertices, sizeof(UINT32) * 4); |
678 | memcpy(dst->offsets, &offsets, sizeof(offsets)); |
679 | memcpy(&dst->transform, &entry.transform, sizeof(float) * 12); |
680 | |
681 | dst++; |
682 | } |
683 | |
684 | // Write extruded face data |
685 | for (UINT32 i = 0; i < (UINT32)outerFaces.size(); i++) |
686 | { |
687 | if (!validTets[outerFaces[i].tetrahedron]) |
688 | continue; |
689 | |
690 | const TetrahedronFaceData& entry = outerFaces[i]; |
691 | |
692 | UINT32 indices[4]; |
693 | Vector2I offsets[4]; |
694 | for(UINT32 j = 0; j < 3; j++) |
695 | { |
696 | indices[j] = mTempTetrahedronBufferIndices[entry.innerVertices[j]]; |
697 | offsets[j] = mTempTetrahedronBufferOffsets[entry.innerVertices[j]]; |
698 | } |
699 | |
700 | indices[3] = -1; |
701 | |
702 | memcpy(dst->indices, indices, sizeof(UINT32) * 4); |
703 | memcpy(dst->offsets, offsets, sizeof(offsets)); |
704 | memcpy(&dst->transform, &entry.transform, sizeof(float) * 12); |
705 | |
706 | dst++; |
707 | } |
708 | |
709 | mTetrahedronInfosGPU->unlock(); |
710 | |
711 | // Write data specific to faces |
712 | if (numValidFaces > mMaxFaces) |
713 | { |
714 | UINT32 newSize = Math::divideAndRoundUp(numValidFaces, 64U) * 64U; |
715 | resizeTetrahedronFaceBuffer(newSize); |
716 | } |
717 | |
718 | TetrahedronFaceDataGPU* faceDst = (TetrahedronFaceDataGPU*)mTetrahedronFaceInfosGPU->lock(0, |
719 | mTetrahedronFaceInfosGPU->getSize(), GBL_WRITE_ONLY_DISCARD); |
720 | |
721 | for (UINT32 i = 0; i < (UINT32)outerFaces.size(); i++) |
722 | { |
723 | if (!validTets[outerFaces[i].tetrahedron]) |
724 | continue; |
725 | |
726 | const TetrahedronFaceData& entry = outerFaces[i]; |
727 | |
728 | for (UINT32 j = 0; j < 3; j++) |
729 | { |
730 | faceDst->corners[j] = mTempTetrahedronPositions[entry.innerVertices[j]]; |
731 | faceDst->normals[j] = entry.normals[j]; |
732 | } |
733 | |
734 | faceDst->isQuadratic = entry.quadratic ? 1 : 0; |
735 | faceDst++; |
736 | } |
737 | |
738 | mTetrahedronFaceInfosGPU->unlock(); |
739 | |
740 | bs_stack_free(validTets); |
741 | |
742 | mTempTetrahedronPositions.clear(); |
743 | mTempTetrahedronBufferIndices.clear(); |
744 | mTetrahedronVolumeDirty = false; |
745 | } |
746 | |
747 | bool LightProbes::hasAnyProbes() const |
748 | { |
749 | for(auto& entry : mVolumes) |
750 | { |
751 | UINT32 numProbes = entry.volume->getNumActiveProbes(); |
752 | if (numProbes > 0) |
753 | return true; |
754 | } |
755 | |
756 | return false; |
757 | } |
758 | |
759 | LightProbesInfo LightProbes::getInfo() const |
760 | { |
761 | LightProbesInfo info; |
762 | info.shCoefficients = mProbeCoefficientsGPU; |
763 | info.tetrahedra = mTetrahedronInfosGPU; |
764 | info.faces = mTetrahedronFaceInfosGPU; |
765 | info.tetrahedraVolume = mVolumeMesh; |
766 | info.numTetrahedra = mNumValidTetrahedra; |
767 | |
768 | return info; |
769 | } |
770 | |
771 | void LightProbes::resizeTetrahedronBuffer(UINT32 count) |
772 | { |
773 | static constexpr UINT32 ELEMENT_SIZE = Math::divideAndRoundUp((UINT32)sizeof(TetrahedronDataGPU), 4U); |
774 | |
775 | GPU_BUFFER_DESC desc; |
776 | desc.type = GBT_STANDARD; |
777 | desc.elementSize = 0; |
778 | desc.elementCount = count * ELEMENT_SIZE; |
779 | desc.usage = GBU_STATIC; |
780 | desc.format = BF_32X4U; |
781 | |
782 | mTetrahedronInfosGPU = GpuBuffer::create(desc); |
783 | mMaxTetrahedra = count; |
784 | } |
785 | |
786 | void LightProbes::resizeTetrahedronFaceBuffer(UINT32 count) |
787 | { |
788 | static constexpr UINT32 ELEMENT_SIZE = Math::divideAndRoundUp((UINT32)sizeof(TetrahedronFaceDataGPU), 4U); |
789 | |
790 | GPU_BUFFER_DESC desc; |
791 | desc.type = GBT_STANDARD; |
792 | desc.elementSize = 0; |
793 | desc.elementCount = count * ELEMENT_SIZE; |
794 | desc.usage = GBU_STATIC; |
795 | desc.format = BF_32X4F; |
796 | |
797 | mTetrahedronFaceInfosGPU = GpuBuffer::create(desc); |
798 | mMaxFaces = count; |
799 | } |
800 | |
801 | void LightProbes::resizeCoefficientTexture(UINT32 numRows) |
802 | { |
803 | TEXTURE_DESC desc; |
804 | desc.width = 4096; |
805 | desc.height = numRows; |
806 | desc.usage = TU_LOADSTORE | TU_RENDERTARGET; |
807 | desc.format = PF_RGBA32F; |
808 | |
809 | SPtr<Texture> newTexture = Texture::create(desc); |
810 | if (mProbeCoefficientsGPU) |
811 | mProbeCoefficientsGPU->copy(newTexture); |
812 | |
813 | mProbeCoefficientsGPU = newTexture; |
814 | mMaxCoefficientRows = numRows; |
815 | } |
816 | |
817 | void LightProbes::generateTetrahedronData(Vector<Vector3>& positions, Vector<TetrahedronData>& tetrahedra, |
818 | Vector<TetrahedronFaceData>& faces, bool ) |
819 | { |
820 | bs_frame_mark(); |
821 | { |
822 | TetrahedronVolume volume = Triangulation::tetrahedralize(positions); |
823 | |
824 | if (generateExtrapolationVolume) |
825 | { |
826 | // Add geometry so we can handle the case when the interpolation position falls outside of the tetrahedra |
827 | // volume. We use this geometry to project the position to the nearest face. |
828 | UINT32 numOuterFaces = (UINT32)volume.outerFaces.size(); |
829 | |
830 | // Calculate face normals for outer faces |
831 | //// Make an edge map |
832 | struct Edge |
833 | { |
834 | INT32 faces[2]; |
835 | INT32 oppositeVerts[2]; |
836 | }; |
837 | |
838 | FrameUnorderedMap<std::pair<INT32, INT32>, Edge, pair_hash> edgeMap; |
839 | for (UINT32 i = 0; i < numOuterFaces; ++i) |
840 | { |
841 | for (UINT32 j = 0; j < 3; ++j) |
842 | { |
843 | INT32 v0 = volume.outerFaces[i].vertices[j]; |
844 | INT32 v1 = volume.outerFaces[i].vertices[(j + 1) % 3]; |
845 | |
846 | // Keep the same ordering so other faces can find the same edge |
847 | if (v0 > v1) |
848 | std::swap(v0, v1); |
849 | |
850 | auto iterFind = edgeMap.find(std::make_pair(v0, v1)); |
851 | if (iterFind != edgeMap.end()) |
852 | { |
853 | iterFind->second.faces[1] = i; |
854 | iterFind->second.oppositeVerts[1] = (j + 2) % 3; |
855 | } |
856 | else |
857 | { |
858 | Edge edge; |
859 | edge.faces[0] = i; |
860 | edge.oppositeVerts[0] = (j + 2) % 3; |
861 | |
862 | edgeMap.insert(std::make_pair(std::make_pair(v0, v1), edge)); |
863 | } |
864 | } |
865 | } |
866 | |
867 | //// Generate face normals |
868 | struct FaceVertex |
869 | { |
870 | Vector3 normal = Vector3::ZERO; |
871 | UINT32 outerIdx = -1; |
872 | }; |
873 | |
874 | FrameVector<Vector3> faceNormals(volume.outerFaces.size()); |
875 | for (UINT32 i = 0; i < (UINT32)volume.outerFaces.size(); ++i) |
876 | { |
877 | const Vector3& v0 = positions[volume.outerFaces[i].vertices[0]]; |
878 | const Vector3& v1 = positions[volume.outerFaces[i].vertices[1]]; |
879 | const Vector3& v2 = positions[volume.outerFaces[i].vertices[2]]; |
880 | |
881 | Vector3 e0 = v1 - v0; |
882 | Vector3 e1 = v2 - v0; |
883 | |
884 | // Make sure the normal is facing away from the center |
885 | const Tetrahedron& tet = volume.tetrahedra[volume.outerFaces[i].tetrahedron]; |
886 | |
887 | Vector3 center(BsZero); |
888 | for(UINT32 j = 0; j < 4; j++) |
889 | center += positions[tet.vertices[j]]; |
890 | |
891 | center /= 4.0f; |
892 | |
893 | Vector3 normal = Vector3::normalize(e0.cross(e1)); |
894 | if (normal.dot(v0 - center) < 0.0f) |
895 | normal = -normal; |
896 | |
897 | faceNormals[i] = normal; |
898 | } |
899 | |
900 | //// Generate vertex normals |
901 | FrameUnorderedMap<INT32, FaceVertex> faceVertices; |
902 | for (auto& entry : edgeMap) |
903 | { |
904 | const Edge& edge = entry.second; |
905 | |
906 | auto accumulateNormalForEdgeVertex = [&](UINT32 v0Idx, UINT32 v1Idx) |
907 | { |
908 | auto iter = faceVertices.insert(std::make_pair(v0Idx, FaceVertex())); |
909 | |
910 | FaceVertex& accum = iter.first->second; |
911 | const Vector3& v0 = positions[v0Idx]; |
912 | |
913 | auto accumulateNormalForFace = [&](INT32 faceIdx, INT32 v2LocIdx) |
914 | { |
915 | const TetrahedronFace& face = volume.outerFaces[faceIdx]; |
916 | |
917 | // Vertices on the face, that aren't the vertex we're calculating the normal for |
918 | const Vector3& v1 = positions[v1Idx]; |
919 | const Vector3& v2 = positions[face.vertices[v2LocIdx]]; |
920 | |
921 | // Weight the contribution to the normal based on the angle spanned by the triangle |
922 | Vector3 e0 = Vector3::normalize(v1 - v0); |
923 | Vector3 e1 = Vector3::normalize(v2 - v0); |
924 | |
925 | float weight = acos(e0.dot(e1)); |
926 | accum.normal += weight * faceNormals[faceIdx]; |
927 | }; |
928 | |
929 | accumulateNormalForFace(edge.faces[0], entry.second.oppositeVerts[0]); |
930 | accumulateNormalForFace(edge.faces[1], entry.second.oppositeVerts[1]); |
931 | }; |
932 | |
933 | accumulateNormalForEdgeVertex(entry.first.first, entry.first.second); |
934 | accumulateNormalForEdgeVertex(entry.first.second, entry.first.first); |
935 | } |
936 | |
937 | for (auto& entry : faceVertices) |
938 | entry.second.normal.normalize(); |
939 | |
940 | // For each face vertex, generate an outer vertex along its normal |
941 | static const float = 5.0f; |
942 | for(auto& entry : faceVertices) |
943 | { |
944 | entry.second.outerIdx = (UINT32)positions.size(); |
945 | |
946 | Vector3 outerPos = positions[entry.first] + entry.second.normal * ExtrapolationDistance; |
947 | positions.push_back(outerPos); |
948 | } |
949 | |
950 | // Generate face data |
951 | for (UINT32 i = 0; i < numOuterFaces; ++i) |
952 | { |
953 | const TetrahedronFace& face = volume.outerFaces[i]; |
954 | |
955 | TetrahedronFaceData faceData; |
956 | faceData.tetrahedron = face.tetrahedron; |
957 | |
958 | for (UINT32 j = 0; j < 3; j++) |
959 | { |
960 | const FaceVertex& faceVertex = faceVertices[face.vertices[j]]; |
961 | |
962 | faceData.innerVertices[j] = face.vertices[j]; |
963 | faceData.outerVertices[j] = faceVertex.outerIdx; |
964 | faceData.normals[j] = faceVertex.normal; |
965 | } |
966 | |
967 | // Add a link on the source tetrahedron to the face data |
968 | Tetrahedron& innerTet = volume.tetrahedra[face.tetrahedron]; |
969 | for(UINT32 j = 0; j < 4; j++) |
970 | { |
971 | if (innerTet.neighbors[j] == -1) |
972 | { |
973 | // Note: Not searching for opposite neighbor here. If tet. has multiple free faces then we |
974 | // can't just pick the first one |
975 | innerTet.neighbors[j] = (UINT32)volume.tetrahedra.size() + (UINT32)faces.size(); |
976 | break; |
977 | } |
978 | } |
979 | |
980 | // We need a way to project a point outside the tetrahedron volume onto an outer face, then calculate |
981 | // triangle's barycentric coordinates. Use use the per-vertex normals to extrude the triangle face into |
982 | // infinity. |
983 | |
984 | // Our point can be represented as: |
985 | // p == a (p0 + t*v0) + b (p1 + t*v1) + c (p2 + t*v2) |
986 | // |
987 | // where a, b and c are barycentric coordinates, |
988 | // p0, p1, p2 are the corners of the face |
989 | // v0, v1, v2 are the vertex normals, per corner |
990 | // t is the distance from the triangle to the point |
991 | // |
992 | // Essentially we're calculating the corners of a bigger triangle that's "t" units away from the |
993 | // face, and its corners lie along the per-vertex normals. Point "p" will lie on that triangle, for which |
994 | // we can then calculate barycentric coordinates normally. |
995 | // |
996 | // First we substitute: c = 1 - a - b |
997 | // p == a (p0 + t v0) + b (p1 + t v1) + (1 - a - b) (p2 + t v2) |
998 | // p == a (p0 + t v0) + b (p1 + t v1) + (p2 + t v2) - a (p2 + t v2) - b (p2 + t v2) |
999 | // p == a (p0 - p2 + t v0 - t v2) + b (p1 - p2 + t v1 - t v2) + (p2 + t v2) |
1000 | // |
1001 | // And move everything to one side: |
1002 | // p - p2 - t v2 == a (p0 - p2 + t ( v0 - v2)) + b (p1 - p2 + t ( v1 - v2)) |
1003 | // a (p0 - p2 + t ( v0 - v2)) + b (p1 - p2 + t ( v1 - v2)) - (p - p2 - t v2) == 0 |
1004 | // |
1005 | // We rewrite it using: |
1006 | // Ap = p0 - p2 |
1007 | // Av = v0 - v2 |
1008 | // Bp = p1 - p2 |
1009 | // Bv = v1 - v2 |
1010 | // Cp = p - p2 |
1011 | // Cv = -v2 |
1012 | // |
1013 | // Which yields: |
1014 | // a (Ap + t Av) + b (Bp + t Bv) - (Cp + t Cv) == 0 |
1015 | // |
1016 | // Which can be written in matrix form: |
1017 | // |
1018 | // M = {Ap + t Av, Bp + t Bv, Cp + t Cv} |
1019 | // a 0 |
1020 | // M * [ b ] = [0] |
1021 | // -1 0 |
1022 | // |
1023 | // From that we can tell that matrix M cannot be inverted, because if we multiply the zero vector with the |
1024 | // inverted matrix the result would be zero, and not [a, b, -1]. Since the matrix cannot be inverted |
1025 | // det(M) == 0. |
1026 | // |
1027 | // We can use that fact to calculate "t". After we have "t" we can calculate barycentric coordinates |
1028 | // normally. |
1029 | // |
1030 | // Solving equation det(M) == 0 yields a cubic in form: |
1031 | // p t^3 + q t^2 + r t + s = 0 |
1032 | // |
1033 | // We'll convert this to monic form, by dividing by p: |
1034 | // t^3 + q/p t^2 + r/p t + s/p = 0 |
1035 | // |
1036 | // Or if p ends up being zero, we end up with a quadratic instead: |
1037 | // q t^2 + r t + s = 0 |
1038 | // |
1039 | // We want to create a matrix that when multiplied with the position, yields us the three coefficients, |
1040 | // which we can then use to solve for "t". For this we create a 4x3 matrix, where each row represents |
1041 | // a solution for one of the coefficients. We factor contributons to each coefficient whether they depend on |
1042 | // position x, y, z, or don't depend on position (row columns, in that order respectively). |
1043 | |
1044 | const Vector3& p0 = positions[faceData.innerVertices[0]]; |
1045 | const Vector3& p1 = positions[faceData.innerVertices[1]]; |
1046 | const Vector3& p2 = positions[faceData.innerVertices[2]]; |
1047 | |
1048 | const Vector3& v0 = faceVertices[faceData.innerVertices[0]].normal; |
1049 | const Vector3& v1 = faceVertices[faceData.innerVertices[1]].normal; |
1050 | const Vector3& v2 = faceVertices[faceData.innerVertices[2]].normal; |
1051 | |
1052 | float p = |
1053 | v2.x * v1.y * v0.z - |
1054 | v1.x * v2.y * v0.z - |
1055 | v2.x * v0.y * v1.z + |
1056 | v0.x * v2.y * v1.z + |
1057 | v1.x * v0.y * v2.z - |
1058 | v0.x * v1.y * v2.z; |
1059 | |
1060 | float qx = -v1.y * v0.z + v2.y * v0.z + v0.y * v1.z - v2.y * v1.z - v0.y * v2.z + v1.y * v2.z; |
1061 | float qy = v1.x * v0.z - v2.x * v0.z - v0.x * v1.z + v2.x * v1.z + v0.x * v2.z - v1.x * v2.z; |
1062 | float qz = -v1.x * v0.y + v2.x * v0.y + v0.x * v1.y - v2.x * v1.y - v0.x * v2.y + v1.x * v2.y; |
1063 | float qw = v2.y * v1.z * p0.x - v1.y * v2.z * p0.x - v2.y * v0.z * p1.x + v0.y * v2.z * p1.x + |
1064 | v1.y * v0.z * p2.x - v0.y * v1.z * p2.x - v2.x * v1.z * p0.y + v1.x * v2.z * p0.y + |
1065 | v2.x * v0.z * p1.y - v0.x * v2.z * p1.y - v1.x * v0.z * p2.y + v0.x * v1.z * p2.y + |
1066 | v2.x * v1.y * p0.z - v1.x * v2.y * p0.z - v2.x * v0.y * p1.z + v0.x * v2.y * p1.z + |
1067 | v1.x * v0.y * p2.z - v0.x * v1.y * p2.z; |
1068 | |
1069 | float rx = v1.z * p0.y - v2.z * p0.y - v0.z * p1.y + v2.z * p1.y + v0.z * p2.y - v1.z * p2.y - |
1070 | v1.y * p0.z + v2.y * p0.z + v0.y * p1.z - v2.y * p1.z - v0.y * p2.z + v1.y * p2.z; |
1071 | float ry = -v1.z * p0.x + v2.z * p0.x + v0.z * p1.x - v2.z * p1.x - v0.z * p2.x + v1.z * p2.x + |
1072 | v1.x * p0.z - v2.x * p0.z - v0.x * p1.z + v2.x * p1.z + v0.x * p2.z - v1.x * p2.z; |
1073 | float rz = v1.y * p0.x - v2.y * p0.x - v0.y * p1.x + v2.y * p1.x + v0.y * p2.x - v1.y * p2.x - |
1074 | v1.x * p0.y + v2.x * p0.y + v0.x * p1.y - v2.x * p1.y - v0.x * p2.y + v1.x * p2.y; |
1075 | float rw = v2.z * p1.x * p0.y - v1.z * p2.x * p0.y - v2.z * p0.x * p1.y + v0.z * p2.x * p1.y + |
1076 | v1.z * p0.x * p2.y - v0.z * p1.x * p2.y - v2.y * p1.x * p0.z + v1.y * p2.x * p0.z + |
1077 | v2.x * p1.y * p0.z - v1.x * p2.y * p0.z + v2.y * p0.x * p1.z - v0.y * p2.x * p1.z - |
1078 | v2.x * p0.y * p1.z + v0.x * p2.y * p1.z - v1.y * p0.x * p2.z + v0.y * p1.x * p2.z + |
1079 | v1.x * p0.y * p2.z - v0.x * p1.y * p2.z; |
1080 | |
1081 | float sx = -p1.y * p0.z + p2.y * p0.z + p0.y * p1.z - p2.y * p1.z - p0.y * p2.z + p1.y * p2.z; |
1082 | float sy = p1.x * p0.z - p2.x * p0.z - p0.x * p1.z + p2.x * p1.z + p0.x * p2.z - p1.x * p2.z; |
1083 | float sz = -p1.x * p0.y + p2.x * p0.y + p0.x * p1.y - p2.x * p1.y - p0.x * p2.y + p1.x * p2.y; |
1084 | float sw = p2.x * p1.y * p0.z - p1.x * p2.y * p0.z - p2.x * p0.y * p1.z + |
1085 | p0.x * p2.y * p1.z + p1.x * p0.y * p2.z - p0.x * p1.y * p2.z; |
1086 | |
1087 | faceData.transform[0][0] = qx; |
1088 | faceData.transform[0][1] = qy; |
1089 | faceData.transform[0][2] = qz; |
1090 | faceData.transform[0][3] = qw; |
1091 | |
1092 | faceData.transform[1][0] = rx; |
1093 | faceData.transform[1][1] = ry; |
1094 | faceData.transform[1][2] = rz; |
1095 | faceData.transform[1][3] = rw; |
1096 | |
1097 | faceData.transform[2][0] = sx; |
1098 | faceData.transform[2][1] = sy; |
1099 | faceData.transform[2][2] = sz; |
1100 | faceData.transform[2][3] = sw; |
1101 | |
1102 | // Unused |
1103 | faceData.transform[3][0] = 0.0f; |
1104 | faceData.transform[3][1] = 0.0f; |
1105 | faceData.transform[3][2] = 0.0f; |
1106 | faceData.transform[3][3] = 0.0f; |
1107 | |
1108 | if (fabs(p) > 0.00001f) |
1109 | { |
1110 | faceData.transform = faceData.transform * (1.0f / p); |
1111 | faceData.quadratic = false; |
1112 | } |
1113 | else // Quadratic |
1114 | { |
1115 | faceData.quadratic = true; |
1116 | } |
1117 | |
1118 | faces.push_back(faceData); |
1119 | } |
1120 | } |
1121 | else |
1122 | { |
1123 | for (UINT32 i = 0; i < (UINT32)volume.outerFaces.size(); ++i) |
1124 | { |
1125 | const TetrahedronFace& face = volume.outerFaces[i]; |
1126 | TetrahedronFaceData faceData; |
1127 | |
1128 | for (UINT32 j = 0; j < 3; j++) |
1129 | { |
1130 | faceData.innerVertices[j] = face.vertices[j]; |
1131 | faceData.outerVertices[j] = -1; |
1132 | faceData.normals[j] = Vector3::ZERO; |
1133 | } |
1134 | |
1135 | faceData.tetrahedron = face.tetrahedron; |
1136 | faceData.transform = Matrix4::IDENTITY; |
1137 | faceData.quadratic = false; |
1138 | |
1139 | faces.push_back(faceData); |
1140 | } |
1141 | } |
1142 | |
1143 | // Generate matrices |
1144 | UINT32 numOutputTets = (UINT32)volume.tetrahedra.size(); |
1145 | tetrahedra.reserve(numOutputTets); |
1146 | |
1147 | //// For inner tetrahedrons |
1148 | for(UINT32 i = 0; i < (UINT32)numOutputTets; ++i) |
1149 | { |
1150 | TetrahedronData entry; |
1151 | entry.volume = volume.tetrahedra[i]; |
1152 | |
1153 | // Generate a matrix that can be used for calculating barycentric coordinates |
1154 | // To determine a point within a tetrahedron, using barycentric coordinates, we use: |
1155 | // P = (P1 - P4) * a + (P2 - P4) * b + (P3 - P4) * c + P4 |
1156 | // |
1157 | // Where P1, P2, P3, P4 are the corners of the tetrahedron. |
1158 | // |
1159 | // Expanded for each coordinate this is: |
1160 | // x = (x1 - x4) * a + (x2 - x4) * b + (x3 - x4) * c + x4 |
1161 | // y = (y1 - y4) * a + (y2 - y4) * b + (y3 - y4) * c + y4 |
1162 | // z = (z1 - z4) * a + (z2 - z4) * b + (z3 - z4) * c + z4 |
1163 | // |
1164 | // In matrix form this is: |
1165 | // a |
1166 | // P = [P1 - P4, P2 - P4, P3 - P4, P4] [b] |
1167 | // c |
1168 | // 1 |
1169 | // |
1170 | // Solved for barycentric coordinates: |
1171 | // a |
1172 | // [b] = Minv * P |
1173 | // c |
1174 | // 1 |
1175 | // |
1176 | // Where Minv is the inverse of the matrix above. |
1177 | |
1178 | const Vector3& P1 = positions[volume.tetrahedra[i].vertices[0]]; |
1179 | const Vector3& P2 = positions[volume.tetrahedra[i].vertices[1]]; |
1180 | const Vector3& P3 = positions[volume.tetrahedra[i].vertices[2]]; |
1181 | const Vector3& P4 = positions[volume.tetrahedra[i].vertices[3]]; |
1182 | |
1183 | Vector3 E1 = P1 - P4; |
1184 | Vector3 E2 = P2 - P4; |
1185 | Vector3 E3 = P3 - P4; |
1186 | |
1187 | Matrix4 mat; |
1188 | mat.setColumn(0, Vector4(E1, 0.0f)); |
1189 | mat.setColumn(1, Vector4(E2, 0.0f)); |
1190 | mat.setColumn(2, Vector4(E3, 0.0f)); |
1191 | mat.setColumn(3, Vector4(P4, 1.0f)); |
1192 | |
1193 | entry.transform = mat.inverse(); |
1194 | |
1195 | tetrahedra.push_back(entry); |
1196 | } |
1197 | } |
1198 | bs_frame_clear(); |
1199 | } |
1200 | }} |
1201 | |