1#define _USE_MATH_DEFINES
7#include "EntityStore.h"
9#include "Utilities/Math.h"
10#include "Systems/Core/CameraSystem.h"
11#include "Systems/Core/MeshSystem.h"
12#include "Math/CrossSectionGenerator.h"
14#include "Resources/Mesh.h"
15#include "Resources/MeshManager.h"
16#include "Resources/MaterialManager.h"
17#include "Resources/FontManager.h"
18#include "Resources/DefaultMaterial.h"
20#include "Components/Core/MeshRenderComponent.h"
21#include "Components/Core/TextComponent.h"
22#include "Components/Core/SceneComponent.h"
23#include "Components/Core/MeshRenderComponent.h"
24#include "Components/Core/TransformComponent.h"
25#include "Components/Core/SubMeshRenderComponent.h"
26#include "Components/Appearance/MaterialComponent.h"
28#include "MultiphaseFlowComponent.h"
30#include <glm/gtx/compatibility.hpp>
36static const int NUM_OF_SEGMENTS = 24;
37static const int NUM_OF_SEGMENTS_SHADOW = 12;
38static std::vector<glm::vec3> CROSS_SECTION;
39static std::vector<glm::vec3> CROSS_SECTION_SHADOW;
41static const glm::vec3 WATER_COLOR_DEFAULT = glm::vec3(54, 121, 159) / 255.f;
42static const glm::vec3 OIL_COLOR_DEFAULT = glm::vec3(235, 201, 77) / 255.f;
43static const glm::vec3 GAS_COLOR_DEFAULT = glm::vec3(195, 195, 195) / 255.f;
44static const glm::vec3 SHADOW_COLOR = glm::vec3(0.3f, 0.3f, 0.3f);
45static const glm::vec3 INDICATOR_RING_COLOR = glm::vec3(143, 14, 14) / 255.f;
47static const float ANNULAR_FLOW_INCLINATION = 0.6f;
48static const float GAS_STRATIFIED_TRANSPARENCY = 0.5f;
50static const int GAS_STRATIFIED_TRANSITION_RANGE = 20;
52MultiphaseFlowComponent::MultiphaseFlowComponent() :
53 waterColor(WATER_COLOR_DEFAULT),
54 oilColor(OIL_COLOR_DEFAULT),
55 gasColor(GAS_COLOR_DEFAULT),
56 indicatorRingColor(INDICATOR_RING_COLOR)
58 if (CROSS_SECTION.empty())
60 CROSS_SECTION.resize(NUM_OF_SEGMENTS);
61 CrossSectionGenerator::generateCircularCrossection(CROSS_SECTION.data(),
static_cast<int>(NUM_OF_SEGMENTS), 1.f, 0, glm::pi<float>() * 2.0f,
false);
64 if (CROSS_SECTION_SHADOW.empty())
66 CROSS_SECTION_SHADOW.resize(NUM_OF_SEGMENTS_SHADOW);
67 CrossSectionGenerator::generateCircularCrossection(CROSS_SECTION_SHADOW.data(),
static_cast<int>(NUM_OF_SEGMENTS_SHADOW), 1.f, 0, glm::pi<float>() * 2.0f,
false);
71void MultiphaseFlowComponent::registerType()
74 Field(
Name(
"trajectoryPoints"), &MultiphaseFlowComponent::trajectoryPoints),
75 Field(
Name(
"radius"), &MultiphaseFlowComponent::radius),
76 Field(
Name(
"flowFraction"), &MultiphaseFlowComponent::flowFraction),
77 Field(
Name(
"wallValues"), &MultiphaseFlowComponent::wallValues),
78 Field(
Name(
"heightScale"), &MultiphaseFlowComponent::heightScale),
79 Field(
Name(
"radiusScale"), &MultiphaseFlowComponent::radiusScale),
80 Field(
Name(
"showWater"), &MultiphaseFlowComponent::showWater),
81 Field(
Name(
"showOil"), &MultiphaseFlowComponent::showOil),
82 Field(
Name(
"showGas"), &MultiphaseFlowComponent::showGas),
83 Field(
Name(
"unitScale"), &MultiphaseFlowComponent::unitScale),
84 Field(
Name(
"unitText"), &MultiphaseFlowComponent::unitText),
85 Field(
Name(
"updateNeeded"), &MultiphaseFlowComponent::updateNeeded),
86 Field(
Name(
"waterColor"), &MultiphaseFlowComponent::waterColor),
87 Field(
Name(
"oilColor"), &MultiphaseFlowComponent::oilColor),
88 Field(
Name(
"gasColor"), &MultiphaseFlowComponent::gasColor),
89 Field(
Name(
"fontSize"), &MultiphaseFlowComponent::fontSize),
90 Field(
Name(
"showAxialExtents"), &MultiphaseFlowComponent::showAxialExtents),
91 Field(
Name(
"showShadow"), &MultiphaseFlowComponent::showShadow),
92 Field(
Name(
"showGrid"), &MultiphaseFlowComponent::showGrid),
93 Field(
Name(
"centerMesh"), &MultiphaseFlowComponent::centerMesh),
94 Field(
Name(
"showIndicatorRing"), &MultiphaseFlowComponent::showIndicatorRing),
95 Field(
Name(
"indicatorRingPosition"), &MultiphaseFlowComponent::indicatorRingPosition)
99 Method(
Name(
"initialize"), &MultiphaseFlowComponent::initialize),
100 Method(
Name(
"update"), &MultiphaseFlowComponent::update),
103 DynamicComponent::registerDerivedType<MultiphaseFlowComponent>().setFields(fields).setMethods(methods);
106bool MultiphaseFlowComponent::isStratified(MeshType liquidType)
108 assert(liquidType != MeshType::Wall && liquidType != MeshType::Shadow);
110 return (liquidType == MeshType::StratifiedWater ||
111 liquidType == MeshType::StratifiedOil ||
112 liquidType == MeshType::StratifiedGas);
115bool MultiphaseFlowComponent::isAnnular(MeshType liquidType)
117 assert(liquidType != MeshType::Wall && liquidType != MeshType::Shadow);
119 return !isStratified(liquidType);
122unsigned int MultiphaseFlowComponent::getNumOfSegmentsInCrossSection(MeshType liquidType)
124 return liquidType != MeshType::Shadow ? NUM_OF_SEGMENTS : NUM_OF_SEGMENTS_SHADOW;
134 meshComp->
meshHandle = context->meshManager->create();
137 auto materialInstance = context->materialInstanceManager->createMaterialInstance(
MaterialHandle(mainMaterial));
139 meshRenderComp->
material = materialInstance;
141 updateMaterial(mesh, liquidType);
148 auto material = meshRenderComp->
material;
149 material->setOption(
"CullMode",
"Front");
152 glm::vec3 diffuseColor;
155 case MeshType::StratifiedWater:
156 diffuseColor = waterColor;
157 material->setOption(
"CullMode",
"Back");
158 material->setOption(
"DepthBiasEnabled",
"true");
159 material->setOption(
"DepthBiasConstant",
"0.5f");
160 material->setOption(
"DepthBiasSlope",
"0.5f");
162 case MeshType::AnnularWater:
163 diffuseColor = waterColor;
164 material->setOption(
"CullMode",
"Back");
166 case MeshType::StratifiedOil:
167 diffuseColor = oilColor;
168 material->setOption(
"CullMode",
"None");
170 case MeshType::AnnularOil:
171 diffuseColor = oilColor;
172 material->setOption(
"Transparency",
"On");
174 case MeshType::StratifiedGas:
175 diffuseColor = gasColor;
176 material->setOption(
"Transparency",
"On");
178 case MeshType::AnnularGas:
179 diffuseColor = gasColor;
182 diffuseColor = glm::vec3(1.f);
183 material->setVariant(
"EnablePerVertexColor",
true);
185 case MeshType::Shadow:
186 diffuseColor = SHADOW_COLOR;
187 material->setVariant(
"EnableLighting",
false);
188 material->setVariant(
"LightModel",
"BaseColor");
189 material->setOption(
"CullMode",
"None");
192 assert(
false &&
"Unknown liquid type");
197 material->setVec3Property(diffuseColorMaterialPropertyKey, diffuseColor);
198 material->setVec3Property(emissiveColorMaterialPropertyKey, diffuseColor * (liquidType != MeshType::Wall ? 0.3f : 0.05f));
199 material->setVec3Property(specularColorMaterialPropertyKey, liquidType != MeshType::Shadow ? diffuseColor : glm::vec3(0, 0, 0));
202void MultiphaseFlowComponent::initialize(
Context * ctx)
207 auto materialHandle = context->materialManager->loadMaterial(
"MultiphaseFlowMaterial.material");
209 context->materialManager->processLoading();
210 materialHandle->setChanged();
211 mainMaterial = materialHandle.resolve();
213 diffuseColorMaterialPropertyKey = materialHandle->getVec3Key(
"diffuseColor");
214 emissiveColorMaterialPropertyKey = materialHandle->getVec3Key(
"emissiveColor");
215 specularColorMaterialPropertyKey = materialHandle->getVec3Key(
"specularColor");
218 for (
int i = MeshType::First; i <= MeshType::Last; i++)
220 auto liquidType = MeshType(i);
221 createMeshEntity(meshEntity[i], liquidType);
228 createIndicatorRing();
234static void SetLineMaterial(
Context* context,
EntityPtr entity,
const glm::vec3& color,
float lineWidth = 1.f)
236 auto material = context->materialInstanceManager->createMaterialInstance(context->materialManager->getDefaultMaterial());
237 material->setPermutation(
"Line");
238 material->setVariant(
"EnableLighting",
false);
239 material->setBoolProperty(DefaultMaterial::EnableLighting,
false);
240 material->setFloatProperty(DefaultMaterial::LineWidth, lineWidth);
241 material->setVec4Property(DefaultMaterial::DiffuseColor, glm::vec4(color, 1));
244 meshRenderComp->
material = material;
248void MultiphaseFlowComponent::createGridEntity()
251 auto* meshComp = groundGridMeshEntity->getComponent<
MeshComponent>();
254 meshComp->
meshHandle = context->meshManager->create();
256 SetLineMaterial(context, groundGridMeshEntity, glm::vec3(0.6f, 0.6f, 0.6f));
259 auto* annotationMeshComp = annotationAxisMeshEntity->getComponent<
MeshComponent>();
262 annotationMeshComp->
meshHandle = context->meshManager->create();
264 SetLineMaterial(context, annotationAxisMeshEntity, glm::vec3(0.2f, 0.2f, 0.2f));
267void MultiphaseFlowComponent::createIndicatorRing()
270 auto* meshComp = indicatorRingEntity->getComponent<
MeshComponent>();
273 meshComp->
meshHandle = context->meshManager->create();
276 SetLineMaterial(context, indicatorRingEntity, indicatorRingColor, 5.f);
280void MultiphaseFlowComponent::createTextEntity()
282 for (
int i = AnnotationAxis::AxisFirst; i <= AnnotationAxis::AxisLast; i++)
285 auto* textComp = textEntity[i]->getComponent<
TextComponent>();
291 assert(currentFontSize != fontSize);
295void MultiphaseFlowComponent::updateFontSize()
297 if (currentFontSize != fontSize)
299 currentFontSize = fontSize;
301 for (
int i = AnnotationAxis::AxisFirst; i <= AnnotationAxis::AxisLast; i++)
303 auto* textComp = textEntity[i]->getComponent<
TextComponent>();
304 textComp->
fontHandle = context->fontManager->loadFont(
"Fonts/SourceSansPro-Regular.ttf", currentFontSize, NoResourceId);
309static float sCurve(
float a_fValue,
float a_fPower = 1.5f)
313 return (glm::pow(glm::clamp(a_fValue, 0.f, 1.f) * 2, a_fPower)) * 0.5f;
317 return -glm::pow(-(glm::clamp(a_fValue, 0.f, 1.f) - 1) * 2, a_fPower) * 0.5f + 1;
321static glm::vec3 wavelengthToColor(
float lambda)
328 glm::vec3 toColor(
float factor)
const
330 float const gamma = 0.8f;
332 for (
int i = 0; i < 3; ++i)
334 ci[i] = c[i] == 0 ? 0 : glm::round(255 * pow(c[i] * factor, gamma));
336 return glm::vec3(ci[0] / 255.f, ci[1] / 255.f, ci[2] / 255.f);
346 static float thresholds[] = { 380, 440, 490, 510, 580, 645, 780 };
348 for (
unsigned int i = 0; i <
sizeof(thresholds) /
sizeof(thresholds[0]); ++i)
350 float t1 = thresholds[i], t2 = thresholds[i + 1];
351 if (lambda < t1 || lambda >= t2)
359 color.c[i % 3] = (i < 5) ? (lambda - t2) / (t1 - t2) : 0.f;
360 color.c[2 - i / 2] = 1.f;
366 if (lambda >= 380 && lambda < 420)
368 factor = 0.3f + 0.7f * (lambda - 380) / (420 - 380);
370 else if (lambda >= 700 && lambda < 780)
372 factor = 0.3f + 0.7f * (780 - lambda) / (780 - 700);
374 return color.toColor(factor);
377glm::vec3 MultiphaseFlowComponent::wallValueToColor(
const ControlPoint& controlPoint)
379 static const float f1 = 1.f / 435.f;
380 static const float f2 = 1.f / 650.f;
381 float lambda = 1.f / (f1 - controlPoint.wall * (f1 - f2));
382 return wavelengthToColor(lambda);
385void MultiphaseFlowComponent::transformCrossection(
float annularRatio,
float alpha, std::vector<Vertex>& vertexBuffer,
const ControlPoint& controlPoint, MeshType liquidType,
bool isClosingCap )
388 float radius = controlPoint.radius;
389 float annularOilTransparency = 1.f;
390 if (liquidType == MeshType::AnnularOil && !isClosingCap)
392 float radiusAnnular = glm::sqrt(controlPoint.flow.oil + controlPoint.flow.gas);
393 radius *= glm::lerp(1.0f, radiusAnnular, annularRatio);
395 annularOilTransparency = glm::lerp(1.f, controlPoint.flow.oil, annularRatio);
398 if (liquidType == MeshType::AnnularGas)
400 float radiusAnnular = glm::sqrt(controlPoint.flow.gas);
401 radius *= glm::lerp(1.0f, radiusAnnular, annularRatio);
405 bool wallVisible = (wallValues.size() == trajectoryPoints.size());
406 if (wallVisible && liquidType != MeshType::Wall && liquidType != MeshType::Shadow)
412 const auto& crossSection = (liquidType != MeshType::Shadow ? CROSS_SECTION : CROSS_SECTION_SHADOW);
413 for (
size_t j = 0; j < crossSection.size(); ++j)
415 glm::vec3 positionN = controlPoint.rotation * crossSection[j];
416 glm::vec3 crossectionPointAnnular = crossSection[j];
419 glm::vec3 crossectionPointStratified = crossSection[j];
420 glm::vec3 crossectionPointStratifiedInverted = crossSection[j];
423 case MeshType::AnnularWater:
426 case MeshType::StratifiedWater:
428 if (crossectionPointStratified.x < controlPoint.flow.heightWaterOil)
430 crossectionPointStratified.x = controlPoint.flow.heightWaterOil;
431 crossectionPointStratified.y = crossectionPointStratified.y > 0.f ? controlPoint.flow.widthWaterOil : -controlPoint.flow.widthWaterOil;
435 if (crossectionPointStratifiedInverted.x > -controlPoint.flow.heightWaterOil)
437 crossectionPointStratifiedInverted.x = -controlPoint.flow.heightWaterOil;
438 crossectionPointStratifiedInverted.y = crossectionPointStratifiedInverted.y > 0.f ? controlPoint.flow.widthWaterOil : -controlPoint.flow.widthWaterOil;
442 case MeshType::StratifiedOil:
443 case MeshType::AnnularOil:
444 if (crossectionPointStratified.x < controlPoint.flow.heightOilGas)
446 crossectionPointStratified.x = controlPoint.flow.heightOilGas;
447 crossectionPointStratified.y = crossectionPointStratified.y > 0.f ? controlPoint.flow.widthOilGas : -controlPoint.flow.widthOilGas;
450 if (crossectionPointStratified.x > controlPoint.flow.heightWaterOil)
452 crossectionPointStratified.x = controlPoint.flow.heightWaterOil;
453 crossectionPointStratified.y = crossectionPointStratified.y > 0.f ? controlPoint.flow.widthWaterOil : -controlPoint.flow.widthWaterOil;
457 if (crossectionPointStratifiedInverted.x > -controlPoint.flow.heightOilGas)
459 crossectionPointStratifiedInverted.x = -controlPoint.flow.heightOilGas;
460 crossectionPointStratifiedInverted.y = crossectionPointStratifiedInverted.y > 0.f ? controlPoint.flow.widthOilGas : -controlPoint.flow.widthOilGas;
463 if (crossectionPointStratifiedInverted.x < -controlPoint.flow.heightWaterOil)
465 crossectionPointStratifiedInverted.x = -controlPoint.flow.heightWaterOil;
466 crossectionPointStratifiedInverted.y = crossectionPointStratifiedInverted.y > 0.f ? controlPoint.flow.widthWaterOil : -controlPoint.flow.widthWaterOil;
470 case MeshType::StratifiedGas:
471 case MeshType::AnnularGas:
472 if (crossectionPointStratified.x > controlPoint.flow.heightOilGas)
474 crossectionPointStratified.x = controlPoint.flow.heightOilGas;
475 crossectionPointStratified.y = crossectionPointStratified.y > 0.f ? controlPoint.flow.widthOilGas : -controlPoint.flow.widthOilGas;
479 if (crossectionPointStratifiedInverted.x < -controlPoint.flow.heightOilGas)
481 crossectionPointStratifiedInverted.x = -controlPoint.flow.heightOilGas;
482 crossectionPointStratifiedInverted.y = crossectionPointStratifiedInverted.y > 0.f ? controlPoint.flow.widthOilGas : -controlPoint.flow.widthOilGas;
486 case MeshType::Shadow:
495 if (liquidType == MeshType::StratifiedWater)
498 crossectionPointStratified = (controlPoint.invertedFlowRatio < 0.5f ? crossectionPointStratified : crossectionPointStratifiedInverted);
501 if (liquidType != MeshType::Shadow && liquidType != MeshType::Wall)
503 crossectionPointStratified = glm::lerp(crossectionPointStratified, crossectionPointStratifiedInverted, controlPoint.invertedFlowRatio);
507 glm::vec3 crossectionPoint = glm::lerp(crossectionPointStratified, crossectionPointAnnular, annularRatio);
510 glm::vec3 position = controlPoint.rotation * crossectionPoint;
514 position += controlPoint.position;
517 auto transparency = 1.f;
520 case MeshType::StratifiedWater:
522 case MeshType::AnnularWater:
524 case MeshType::StratifiedOil:
526 case MeshType::AnnularOil:
527 transparency = annularOilTransparency;
529 case MeshType::StratifiedGas:
530 transparency = alpha;
532 case MeshType::AnnularGas:
536 case MeshType::Shadow:
539 assert(
false &&
"Unknown liquid type");
547 normal = glm::normalize(positionN);
552 normal = controlPoint.direction;
556 vertexBuffer.push_back({ position, glm::vec4(-normal, transparency) });
560void MultiphaseFlowComponent::generateExtrusionVertices(std::vector<Vertex>& vertexBuffer, std::vector<unsigned int>& indexBuffer,
const std::vector<ControlPoint>& controlPoints, MeshType liquidType)
562 assert(controlPoints.size() >= 2);
564 bool isAnnular = MultiphaseFlowComponent::isAnnular(liquidType);
567 assert(vertexBuffer.size() == 0);
568 vertexBuffer.reserve(NUM_OF_SEGMENTS * controlPoints.size() + NUM_OF_SEGMENTS * 4);
571 std::vector<std::pair<int, int>> flowRanges;
573 for (
size_t i = 0; i < controlPoints.size(); i++)
575 bool isPointAnnularFlow = (controlPoints[i].annularFlowRatio > 0.f);
576 if (isPointAnnularFlow == isAnnular)
578 if (startRange == -1)
588 flowRanges.push_back(std::pair<int, int>(startRange, (
int)i));
596 flowRanges.push_back(std::pair<int, int>(startRange, (
int)controlPoints.size() - 1));
600 unsigned int segmentStart = 0;
601 for (
const auto& range : flowRanges)
603 int segmentRange = range.second - range.first;
604 if (segmentRange <= 0)
610 for (
int i = range.first; i <= range.second; i++)
612 const auto& controlPoint = controlPoints[i];
613 float annularRatio = controlPoint.annularFlowRatio;
616 if ((i == range.first || i == range.second) && i > 0 && i <
static_cast<int>(controlPoints.size()) - 1)
622 if (liquidType == MeshType::StratifiedGas)
625 for (
int j = 0; j <= GAS_STRATIFIED_TRANSITION_RANGE; j++)
627 int nextIndex = i + j;
628 if (nextIndex >=
static_cast<int>(controlPoints.size()))
632 const auto& nextCtrlPoint = controlPoints[nextIndex];
634 if (nextCtrlPoint.annularFlowRatio > 0.f)
636 ratioNext = 1.f -
static_cast<float>(j) /
static_cast<float>(GAS_STRATIFIED_TRANSITION_RANGE);
642 for (
int j = 0; j <= GAS_STRATIFIED_TRANSITION_RANGE; j++)
644 int prevIndex = i - j;
649 const auto& nextCtrlPoint = controlPoints[prevIndex];
651 if (nextCtrlPoint.annularFlowRatio > 0.f)
653 ratioPrev = 1.f -
static_cast<float>(j) /
static_cast<float>(GAS_STRATIFIED_TRANSITION_RANGE);
658 alpha = std::max(ratioNext, ratioPrev);
659 alpha = GAS_STRATIFIED_TRANSPARENCY + alpha * (1.f - GAS_STRATIFIED_TRANSPARENCY);
662 transformCrossection(annularRatio, alpha, vertexBuffer, controlPoint, liquidType);
665 assert(segmentRange > 0);
666 unsigned int segmentEnd = segmentStart + segmentRange;
667 assert(segmentStart < segmentEnd && segmentEnd);
670 for (
unsigned int offset = segmentStart; offset <= segmentEnd - 1; offset++)
672 const auto numOfSegments = getNumOfSegmentsInCrossSection(liquidType);
673 for (
unsigned int j = 0; j < numOfSegments - 1; j++)
675 indexBuffer.push_back((offset * numOfSegments) + j);
676 indexBuffer.push_back((offset * numOfSegments) + j + 1);
677 indexBuffer.push_back(((offset + 1) * numOfSegments) + j + 1);
679 indexBuffer.push_back((offset * numOfSegments) + j);
680 indexBuffer.push_back(((offset + 1) * numOfSegments) + j + 1);
681 indexBuffer.push_back(((offset + 1) * numOfSegments) + j);
684 indexBuffer.push_back((offset * numOfSegments) + numOfSegments - 1);
685 indexBuffer.push_back((offset * numOfSegments));
686 indexBuffer.push_back(((offset + 1) * numOfSegments));
688 indexBuffer.push_back((offset * numOfSegments) + numOfSegments - 1);
689 indexBuffer.push_back(((offset + 1) * numOfSegments));
690 indexBuffer.push_back(((offset + 1) * numOfSegments) + numOfSegments - 1);
693 segmentStart = segmentEnd + 1;
697 if (liquidType == MeshType::StratifiedOil ||
698 liquidType == MeshType::AnnularGas)
701 for (
const auto& range : flowRanges)
703 int segmentRange = range.second - range.first;
704 if (segmentRange <= 0)
711 generateClosingCaps(vertexBuffer, indexBuffer, controlPoints[range.first], liquidType);
714 if (range.second <
static_cast<int>(controlPoints.size()) - 1)
716 generateClosingCaps(vertexBuffer, indexBuffer, controlPoints[range.second], liquidType);
722 if (!flowRanges.empty() && liquidType != MeshType::Wall && liquidType != MeshType::Shadow)
724 if (flowRanges[0].first == 0)
726 generateClosingCaps(vertexBuffer, indexBuffer, controlPoints[0], liquidType);
729 if (flowRanges[flowRanges.size() - 1].second ==
static_cast<int>(controlPoints.size()) - 1)
731 generateClosingCaps(vertexBuffer, indexBuffer, controlPoints[controlPoints.size() - 1], liquidType);
736void MultiphaseFlowComponent::generateClosingCaps(std::vector<Vertex>& vertexBuffer, std::vector<unsigned int>& indexBuffer,
const ControlPoint& controlPoint, MeshType liquidType)
738 float ratio = (controlPoint.annularFlowRatio < 0.5f ? 0.f : 1.f);
741 if (liquidType == MeshType::StratifiedGas)
743 alpha = GAS_STRATIFIED_TRANSPARENCY;
746 if (liquidType == MeshType::Wall || isStratified(liquidType) || liquidType == MeshType::AnnularGas)
748 for (
int i = 0; i < 2; i++)
750 ControlPoint ctrlPoint = controlPoint;
754 ctrlPoint.direction *= -1;
757 const auto vertexBufferOffset =
static_cast<unsigned int>(vertexBuffer.size());
760 transformCrossection(ratio, alpha, vertexBuffer, ctrlPoint, liquidType,
true);
762 const auto numOfSegments = getNumOfSegmentsInCrossSection(liquidType);
763 for (
unsigned int j = 0; j < numOfSegments - 2; ++j)
765 indexBuffer.push_back(vertexBufferOffset);
768 indexBuffer.push_back(vertexBufferOffset + j + 2);
769 indexBuffer.push_back(vertexBufferOffset + j + 1);
773 indexBuffer.push_back(vertexBufferOffset + j + 1);
774 indexBuffer.push_back(vertexBufferOffset + j + 2);
780 if (liquidType == MeshType::AnnularWater || liquidType == MeshType::AnnularOil)
782 assert(isAnnular(liquidType));
784 const unsigned int offset =
static_cast<unsigned int>(vertexBuffer.size());
786 ControlPoint ctrlPoint1 = controlPoint;
787 ControlPoint ctrlPoint2 = controlPoint;
789 if (liquidType == MeshType::AnnularWater)
791 ctrlPoint2.radius *= glm::sqrt(ctrlPoint2.flow.oil + ctrlPoint2.flow.gas);
793 ctrlPoint1.direction *= -1;
794 ctrlPoint2.direction *= -1;
798 ctrlPoint1.radius *= glm::sqrt(ctrlPoint1.flow.oil + ctrlPoint1.flow.gas);
799 ctrlPoint2.radius *= glm::sqrt(ctrlPoint2.flow.gas);
803 transformCrossection(ratio, alpha, vertexBuffer, ctrlPoint1, liquidType,
true);
804 transformCrossection(ratio, alpha, vertexBuffer, ctrlPoint2, liquidType,
true);
806 const auto numOfSegments = getNumOfSegmentsInCrossSection(liquidType);
807 for (
unsigned int i = 0; i < 2; i++)
809 for (
unsigned int j = 0; j < numOfSegments; ++j)
811 indexBuffer.push_back(offset + j);
814 indexBuffer.push_back(offset + j + numOfSegments);
815 indexBuffer.push_back(j < numOfSegments - 1 ? offset + j + 1 : offset);
819 indexBuffer.push_back(j < numOfSegments - 1 ? offset + j + 1 : offset);
820 indexBuffer.push_back(offset + j + numOfSegments);
823 indexBuffer.push_back(offset + j + numOfSegments);
826 indexBuffer.push_back(j < numOfSegments - 1 ? offset + j + numOfSegments + 1 : offset + numOfSegments);
827 indexBuffer.push_back(j < numOfSegments - 1 ? offset + j + 1 : offset);
831 indexBuffer.push_back(j < numOfSegments - 1 ? offset + j + 1 : offset);
832 indexBuffer.push_back(j < numOfSegments - 1 ? offset + j + numOfSegments + 1 : offset + numOfSegments);
839 assert(!
"Unknown flow type");
843void MultiphaseFlowComponent::generateExtrusionVerticesWall(std::vector<WallVertex>& vertexBuffer, std::vector<unsigned int>& indexBuffer,
const std::vector<ControlPoint>& controlPoints, MeshType liquidType)
845 for (
const auto& controlPoint : controlPoints)
847 std::vector<Vertex> temp;
848 transformCrossection(1.f, 1.f, temp, controlPoint, liquidType);
851 for (
const auto& v : temp)
853 vertexBuffer.push_back({ v.position, v.normalAndOpacity, wallValueToColor(controlPoint) });
858 const auto numOfSegments = getNumOfSegmentsInCrossSection(liquidType);
859 for (
unsigned int offset = 0; offset < static_cast<unsigned int>(controlPoints.size()) - 1; offset++)
861 for (
unsigned int j = 0; j < numOfSegments - 1; j++)
863 indexBuffer.push_back((offset * numOfSegments) + j);
864 indexBuffer.push_back((offset * numOfSegments) + j + 1);
865 indexBuffer.push_back(((offset + 1) * numOfSegments) + j + 1);
867 indexBuffer.push_back((offset * numOfSegments) + j);
868 indexBuffer.push_back(((offset + 1) * numOfSegments) + j + 1);
869 indexBuffer.push_back(((offset + 1) * numOfSegments) + j);
872 indexBuffer.push_back((offset * numOfSegments) + numOfSegments - 1);
873 indexBuffer.push_back((offset * numOfSegments));
874 indexBuffer.push_back(((offset + 1) * numOfSegments));
876 indexBuffer.push_back((offset * numOfSegments) + numOfSegments - 1);
877 indexBuffer.push_back(((offset + 1) * numOfSegments));
878 indexBuffer.push_back(((offset + 1) * numOfSegments) + numOfSegments - 1);
882 if (liquidType == MeshType::Wall && flowFraction.size() != trajectoryPoints.size())
884 std::vector<Vertex> tempFirst;
885 std::vector<Vertex> tempLast;
886 std::vector<unsigned int> indexBufferFirst;
887 std::vector<unsigned int> indexBufferLast;
888 generateClosingCaps(tempFirst, indexBufferFirst, controlPoints[0], liquidType);
889 generateClosingCaps(tempLast, indexBufferLast, controlPoints[controlPoints.size() - 1], liquidType);
892 unsigned int offsetFirst =
static_cast<unsigned int>(vertexBuffer.size());
893 vertexBuffer.reserve(vertexBuffer.size() + tempFirst.size() + tempLast.size());
894 for (
const auto& v : tempFirst)
896 vertexBuffer.push_back({ v.position, v.normalAndOpacity, wallValueToColor(controlPoints[0]) });
898 for (
const auto& i : indexBufferFirst)
900 indexBuffer.push_back(i + offsetFirst);
903 unsigned int offsetLast =
static_cast<unsigned int>(vertexBuffer.size());
904 for (
const auto& v : tempLast)
906 vertexBuffer.push_back({ v.position, v.normalAndOpacity, wallValueToColor(controlPoints[controlPoints.size() - 1]) });
908 for (
const auto& i : indexBufferLast)
910 indexBuffer.push_back(i + offsetLast);
915static glm::mat4x4 shadowMatrix(glm::vec4 groundplane, glm::vec4 lightpos)
918 float dot = glm::dot(groundplane, lightpos);
920 glm::mat4x4 shadowMat;
921 shadowMat[0][0] = dot - lightpos.x * groundplane.x;
922 shadowMat[1][0] = 0.f - lightpos.x * groundplane.y;
923 shadowMat[2][0] = 0.f - lightpos.x * groundplane.z;
924 shadowMat[3][0] = 0.f - lightpos.x * groundplane.w;
926 shadowMat[0][1] = 0.f - lightpos.y * groundplane.x;
927 shadowMat[1][1] = dot - lightpos.y * groundplane.y;
928 shadowMat[2][1] = 0.f - lightpos.y * groundplane.z;
929 shadowMat[3][1] = 0.f - lightpos.y * groundplane.w;
931 shadowMat[0][2] = 0.f - lightpos.z * groundplane.x;
932 shadowMat[1][2] = 0.f - lightpos.z * groundplane.y;
933 shadowMat[2][2] = dot - lightpos.z * groundplane.z;
934 shadowMat[3][2] = 0.f - lightpos.z * groundplane.w;
936 shadowMat[0][3] = 0.f - lightpos.w * groundplane.x;
937 shadowMat[1][3] = 0.f - lightpos.w * groundplane.y;
938 shadowMat[2][3] = 0.f - lightpos.w * groundplane.z;
939 shadowMat[3][3] = dot - lightpos.w * groundplane.w;
944void MultiphaseFlowComponent::generateMultiphaseFlowMesh(
Mesh* mesh,
const std::vector<ControlPoint>& controlPoints, MeshType liquidType)
947 if (liquidType != MeshType::Wall && liquidType != MeshType::Shadow)
949 std::vector<Vertex> vertexBuffer;
950 std::vector<unsigned int> indexBuffer;
951 generateExtrusionVertices(vertexBuffer, indexBuffer, controlPoints, liquidType);
954 if (!vertexBuffer.empty())
956 if (liquidType == MeshType::StratifiedOil)
958 calculateNormals(vertexBuffer, indexBuffer);
961 mesh->
setVertexData(vertexBuffer.data(), vertexBuffer.size());
970 for (
auto& p : vertexBuffer)
972 geometryMin = glm::min(geometryMin, p.position);
973 geometryMax = glm::max(geometryMax, p.position);
978 std::vector<WallVertex> vertexBufferWall;
979 std::vector<unsigned int> indexBuffer;
980 generateExtrusionVerticesWall(vertexBufferWall, indexBuffer, controlPoints, liquidType);
982 if (liquidType == MeshType::Shadow)
985 float l = glm::max(glm::max(glm::max(geometryMax.x, geometryMax.y), geometryMax.z), 1.f);
986 glm::mat4x4 shadowMat = shadowMatrix(glm::vec4(0, 0, -1, geometryMin.z), glm::vec4(l* 1.5f, -l * 1.5f, l * 4.f, 1.f));
988 for (
auto& v : vertexBufferWall)
990 auto transformed = shadowMat * glm::vec4(v.position.x, v.position.y, v.position.z, 1);
991 v.position = glm::vec3(transformed.x / transformed.w, transformed.y / transformed.w, transformed.z / transformed.w);
996 if (!vertexBufferWall.empty())
998 mesh->
setVertexData(vertexBufferWall.data(), vertexBufferWall.size());
1008 if (liquidType == MeshType::Wall)
1010 for (
auto& p : vertexBufferWall)
1012 geometryMin = glm::min(geometryMin, p.position);
1013 geometryMax = glm::max(geometryMax, p.position);
1020static void stretch(std::vector<MultiphaseFlowComponent::TrajectoryPoint>& points)
1022 assert(points.size() >= 2);
1024 glm::vec3 direction = points[1].pos - points[0].pos;
1025 glm::vec3 dirPrev = glm::normalize(direction);
1026 glm::vec3 prevPoint;
1027 glm::vec3 trajectoryOffset(0, 0, 0);
1029 for (
size_t i = 0; i < points.size(); ++i)
1034 if (i <= points.size() - 2)
1036 direction = points[i + 1].pos - prevPoint;
1040 direction = points[i].pos - prevPoint;
1044 float dirLength = glm::length(direction);
1048 direction.x /= dirLength;
1049 direction.y /= dirLength;
1050 direction.z /= dirLength;
1053 prevPoint = points[i].pos;
1056 const float r = points[i].radius;
1060 auto dot = glm::dot(direction, dirPrev);
1061 dot = glm::clamp(glm::abs(dot), 0.f, 1.f);
1062 float angle = acos(dot);
1063 float ratio = angle / glm::half_pi<float>() * r * 0.65f;
1065 trajectoryOffset += (dirPrev + direction) * ratio;
1066 points[i].pos += trajectoryOffset;
1068 dirPrev = direction;
1076 CatmullRomSpline(T x0, T x1, T x2, T x3,
float t01,
float t12,
float t23,
bool x0HasValue,
bool x3HasValue)
1081 auto delta = x1 - x0;
1083 x0Scaled = x1 - delta * t12;
1087 auto delta = x2 - x1;
1088 x0Scaled = x1 - delta;
1094 auto delta = x2 - x3;
1096 x3Scaled = x2 - delta * t12;
1100 auto delta = x1 - x2;
1101 x3Scaled = x2 - delta;
1106 _c1 = -x0Scaled * 0.5f + x2 * 0.5f;
1107 _c2 = x0Scaled - x1 * 2.5f + x2 * 2.0f - x3Scaled * 0.5f;
1108 _c3 = -x0Scaled * 0.5f + x1 * 1.5f - x2 * 1.5f + x3Scaled * 0.5f;
1111 T Interpolate(
float t)
1113 return (((_c3 * t + _c2) * t + _c1) * t + _c0);
1123static void interpolate(std::vector<MultiphaseFlowComponent::TrajectoryPoint>& points)
1125 assert(points.size() >= 2);
1127 const float MAX_STEPS = glm::min(
static_cast<float>(50000 / points.size()), 20.f);
1128 const float MIN_STEPS = glm::max(glm::floor(MAX_STEPS / 4), 1.f);
1135 std::vector<MultiphaseFlowComponent::TrajectoryPoint> newPoints;
1136 size_t reservedSize = points.size() *
static_cast<size_t>((MAX_STEPS + MIN_STEPS) / 2.f);
1137 newPoints.reserve(reservedSize);
1139 glm::vec3 direction = points[1].pos - points[0].pos;
1140 glm::vec3 dirPrev = glm::normalize(direction);
1142 for (
size_t i = 0; i < points.size() - 1; i++)
1146 if (i <= points.size() - 2)
1148 direction = points[i + 1].pos - points[i - 1].pos;
1152 direction = points[i].pos - points[i - 1].pos;
1155 direction = glm::normalize(direction);
1157 bool hasFirstPoint = (i > 0);
1158 bool hasLastPoint = (i < points.size() - 2);
1160 glm::vec3 p0 = hasFirstPoint ? points[i - 1].pos : glm::vec3();
1161 glm::vec3 f0 = hasFirstPoint ? points[i - 1].flowFraction : glm::vec3();
1163 const auto& p1 = points[i].pos;
1164 const auto& f1 = points[i].flowFraction;
1166 glm::vec3 p2 = points[i + 1].pos;
1167 glm::vec3 f2 = points[i + 1].flowFraction;
1169 glm::vec3 p3 = hasLastPoint ? points[i + 2].pos : glm::vec3();
1170 glm::vec3 f3 = hasLastPoint ? points[i + 2].flowFraction : glm::vec3();
1172 float t01 = hasFirstPoint ? glm::distance(p0, p1) : 0;
1173 float t12 = glm::distance(p1, p2);
1174 float t23 = hasLastPoint ? glm::distance(p2, p3) : 0;
1177 auto dot = glm::dot(direction, dirPrev);
1178 auto angle = glm::acos(glm::clamp(glm::abs(dot), 0.f, 1.f));
1179 auto ratio = glm::clamp(angle / glm::half_pi<float>(), 0.f, 1.f);
1181 int steps =
static_cast<int>(glm::lerp(MIN_STEPS, MAX_STEPS, ratio));
1189 for (
int j = 0; j < steps; j++)
1191 float ratio_ =
static_cast<float>(j) /
static_cast<float>(steps);
1194 newPoint.pos = splineTrajectory.Interpolate(ratio_);
1195 newPoint.radius = glm::lerp(points[i].radius, points[i + 1].radius, ratio_);
1196 newPoint.flowFraction = splineFlowFraction.Interpolate(ratio_);
1197 newPoint.wallValue = glm::lerp(points[i].wallValue, points[i + 1].wallValue, ratio_);
1198 newPoint.md = glm::lerp(points[i].md, points[i + 1].md, ratio_);
1200 newPoints.push_back(newPoint);
1203 dirPrev = direction;
1207 newPoints.push_back(points[points.size() - 1]);
1210 std::swap(points, newPoints);
1213void MultiphaseFlowComponent::updateGroundGrid()
1216 if (groundGridMeshEntity)
1228 glm::vec3 center = (geometryMax + geometryMin) * 0.5f;
1229 float edgeLength = glm::max(geometryMax.x - geometryMin.x, geometryMax.y - geometryMin.y) * 1.5f;
1230 glm::vec3 corner = center - edgeLength * 0.5f;
1233 std::vector<PositionVertex> vertexBufferGrid;
1234 std::vector<unsigned int> indexBufferGrid;
1235 static const int NUM_OF_SECTORS = 10;
1236 for (
int j = 0; j < 2; j++)
1238 for (
int i = 0; i <= NUM_OF_SECTORS; i++)
1245 v0.position = glm::vec3(corner.x + edgeLength /
static_cast<float>(NUM_OF_SECTORS)*
static_cast<float>(i), corner.y, geometryMin.z * 1.01f);
1246 v1.position = glm::vec3(corner.x + edgeLength /
static_cast<float>(NUM_OF_SECTORS)*
static_cast<float>(i), corner.y + edgeLength, geometryMin.z * 1.01f);
1250 v0.position = glm::vec3(corner.x, corner.y + edgeLength /
static_cast<float>(NUM_OF_SECTORS)*
static_cast<float>(i), geometryMin.z * 1.01f);
1251 v1.position = glm::vec3(corner.x + edgeLength, corner.y + edgeLength /
static_cast<float>(NUM_OF_SECTORS)*
static_cast<float>(i), geometryMin.z * 1.01f);
1254 vertexBufferGrid.push_back(v0);
1255 vertexBufferGrid.push_back(v1);
1257 indexBufferGrid.push_back(
static_cast<unsigned int>(vertexBufferGrid.size()) - 2);
1258 indexBufferGrid.push_back(
static_cast<unsigned int>(vertexBufferGrid.size()) - 1);
1264 meshGrid->
setVertexData(vertexBufferGrid.data(), vertexBufferGrid.size());
1265 meshGrid->
setIndexes(std::move(indexBufferGrid));
1269void MultiphaseFlowComponent::updateIndicatorRing(
const std::vector<TrajectoryPoint>& trajectory)
1272 if (indicatorRingEntity)
1275 meshSceneCompoment->
setVisible(showIndicatorRing);
1279 if (!showIndicatorRing)
1285 assert(trajectory.size() > 1);
1286 glm::vec3 positionPoint;
1289 for (
size_t i = 1; i < trajectory.size(); i++)
1291 if (indicatorRingPosition < trajectory[i].md || i+1 == trajectory.size())
1293 auto ratio = (trajectory[i].md - indicatorRingPosition) / (trajectory[i].md - trajectory[i - 1].md);
1294 ratio = glm::clamp(ratio, 0.f, 1.f);
1296 positionPoint = glm::lerp(trajectory[i].pos, trajectory[i - 1].pos, ratio);
1297 radius = glm::lerp(trajectory[i].radius, trajectory[i - 1].radius, ratio) * 1.3f;
1298 rotation = glm::slerp(trajectory[i].rotation, trajectory[i - 1].rotation, ratio);
1304 std::vector<PositionVertex> vertexBuffer;
1305 std::vector<unsigned int> indexBuffer;
1306 static const int NUM_OF_STEPS = 32;
1308 auto angleStep = glm::two_pi<double>() /
static_cast<double>(NUM_OF_STEPS);
1310 for (
auto i = 0; i < NUM_OF_STEPS; i++)
1312 auto s =
static_cast<float>(sin(angle)) * radius;
1313 auto c =
static_cast<float>(cos(angle)) * radius;
1317 v.position = rotation * glm::vec3(s, c, 0);
1318 v.position += positionPoint;
1320 vertexBuffer.push_back(v);
1322 if (i == NUM_OF_STEPS - 1)
1324 indexBuffer.push_back(i);
1325 indexBuffer.push_back(0);
1329 indexBuffer.push_back(i);
1330 indexBuffer.push_back(i + 1);
1336 meshGrid->
setVertexData(vertexBuffer.data(), vertexBuffer.size());
1337 meshGrid->
setIndexes(std::move(indexBuffer));
1345void MultiphaseFlowComponent::updateAxisAnnotations()
1350 if (!showAxialExtents)
1356 for (
int i = AnnotationAxis::AxisFirst; i <= AnnotationAxis::AxisLast; i++)
1358 auto textComp = textEntity[i]->getComponent<
TextComponent>();
1359 textComp->
texts.clear();
1366 glm::vec3 center = (geometryMax + geometryMin) * 0.5f;
1367 float edgeLength = glm::max(geometryMax.x - geometryMin.x, geometryMax.y - geometryMin.y) * 1.5f;
1369 std::vector<PositionVertex> vertexBuffer;
1370 std::vector<unsigned int> indexBuffer;
1373 glm::vec3 trajectoryMin = trajectoryPoints[0];
1374 glm::vec3 trajectoryMax = trajectoryPoints[0];
1375 for (
const auto& p : trajectoryPoints)
1377 trajectoryMin = glm::min(trajectoryMin, p);
1378 trajectoryMax = glm::max(trajectoryMax, p);
1382 bool xShowAnnotation = glm::abs(trajectoryMax.x - trajectoryMin.x) > 0.1f;
1383 bool yShowAnnotation = glm::abs(trajectoryMax.y - trajectoryMin.y) > 0.1f;
1384 bool zShowAnnotation = glm::abs(trajectoryMax.z - trajectoryMin.z) > 0.1f;
1387 float xLinePosZ = geometryMax.z + edgeLength * 0.01f;
1388 if (xShowAnnotation)
1390 vertexBuffer.push_back({ glm::vec3(scaledTrajectoryMin.x, center.y, xLinePosZ) });
1391 vertexBuffer.push_back({ glm::vec3(scaledTrajectoryMax.x, center.y, xLinePosZ) });
1392 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 2);
1393 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 1);
1394 vertexBuffer.push_back({ glm::vec3(scaledTrajectoryMin.x, center.y, xLinePosZ - edgeLength * 0.005f) });
1395 vertexBuffer.push_back({ glm::vec3(scaledTrajectoryMin.x, center.y, xLinePosZ + edgeLength * 0.005f) });
1396 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 2);
1397 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 1);
1398 vertexBuffer.push_back({ glm::vec3(scaledTrajectoryMax.x, center.y, xLinePosZ - edgeLength * 0.005f) });
1399 vertexBuffer.push_back({ glm::vec3(scaledTrajectoryMax.x, center.y, xLinePosZ + edgeLength * 0.005f) });
1400 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 2);
1401 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 1);
1405 float yLinePosX = geometryMin.x - edgeLength * 0.01f;
1406 if (yShowAnnotation)
1408 vertexBuffer.push_back({ glm::vec3(yLinePosX, scaledTrajectoryMin.y, center.z) });
1409 vertexBuffer.push_back({ glm::vec3(yLinePosX, scaledTrajectoryMax.y, center.z) });
1410 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 2);
1411 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 1);
1412 vertexBuffer.push_back({ glm::vec3(yLinePosX - edgeLength * 0.005f, scaledTrajectoryMin.y, center.z) });
1413 vertexBuffer.push_back({ glm::vec3(yLinePosX + edgeLength * 0.005f, scaledTrajectoryMin.y, center.z) });
1414 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 2);
1415 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 1);
1416 vertexBuffer.push_back({ glm::vec3(yLinePosX - edgeLength * 0.005f, scaledTrajectoryMax.y, center.z) });
1417 vertexBuffer.push_back({ glm::vec3(yLinePosX + edgeLength * 0.005f, scaledTrajectoryMax.y, center.z) });
1418 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 2);
1419 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 1);
1423 float zLinePosX = geometryMax.x + edgeLength * 0.01f;
1424 if (zShowAnnotation)
1426 vertexBuffer.push_back({ glm::vec3(zLinePosX, center.y, scaledTrajectoryMin.z) });
1427 vertexBuffer.push_back({ glm::vec3(zLinePosX, center.y, scaledTrajectoryMax.z) });
1428 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 2);
1429 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 1);
1430 vertexBuffer.push_back({ glm::vec3(zLinePosX - edgeLength * 0.005f, center.y, scaledTrajectoryMin.z) });
1431 vertexBuffer.push_back({ glm::vec3(zLinePosX + edgeLength * 0.005f, center.y, scaledTrajectoryMin.z) });
1432 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 2);
1433 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 1);
1434 vertexBuffer.push_back({ glm::vec3(zLinePosX - edgeLength * 0.005f, center.y, scaledTrajectoryMax.z) });
1435 vertexBuffer.push_back({ glm::vec3(zLinePosX + edgeLength * 0.005f, center.y, scaledTrajectoryMax.z) });
1436 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 2);
1437 indexBuffer.push_back(
static_cast<unsigned int>(vertexBuffer.size()) - 1);
1441 mesh->
setVertexData(vertexBuffer.data(), vertexBuffer.size());
1446 TextComponent* textComp[AnnotationAxis::AxisLast + 1] = {
nullptr,
nullptr,
nullptr };
1447 for (
int i = AnnotationAxis::AxisFirst; i <= AnnotationAxis::AxisLast; i++)
1450 textComp[i]->
color = glm::vec4(0.f, 0.f, 0.f, 1.f);
1451 textComp[i]->
texts.clear();
1459 if (xShowAnnotation)
1461 std::stringstream xMinText;
1462 xMinText << static_cast<int>(floor(trajectoryMin.x * unitScale)) <<
" " << unitText;
1463 textComp[AnnotationAxis::X]->
texts.push_back(xMinText.str());
1464 textComp[AnnotationAxis::X]->
positions.push_back(glm::vec3(scaledTrajectoryMin.x, center.y, xLinePosZ + edgeLength * 0.01f));
1466 std::stringstream xMaxText;
1467 xMaxText << static_cast<int>(floor(trajectoryMax.x * unitScale)) <<
" " << unitText;
1468 textComp[AnnotationAxis::X]->
texts.push_back(xMaxText.str());
1469 textComp[AnnotationAxis::X]->
positions.push_back(glm::vec3(scaledTrajectoryMax.x, center.y, xLinePosZ + edgeLength * 0.01f));
1471 textComp[AnnotationAxis::X]->
texts.push_back(
"X");
1472 textComp[AnnotationAxis::X]->
positions.push_back(glm::vec3((scaledTrajectoryMax.x + scaledTrajectoryMin.x) / 2, center.y, xLinePosZ + edgeLength * 0.005f));
1476 if (yShowAnnotation)
1478 std::stringstream yMinText;
1479 yMinText << static_cast<int>(floor(trajectoryMin.y * unitScale)) <<
" " << unitText;
1480 textComp[AnnotationAxis::YMin]->
texts.push_back(yMinText.str());
1481 textComp[AnnotationAxis::YMin]->
positions.push_back(glm::vec3(yLinePosX - edgeLength * 0.01f, scaledTrajectoryMin.y, center.z));
1483 std::stringstream yMaxText;
1484 yMaxText << static_cast<int>(floor(trajectoryMax.y * unitScale)) <<
" " << unitText;
1485 textComp[AnnotationAxis::YMax]->
texts.push_back(yMaxText.str());
1486 textComp[AnnotationAxis::YMax]->
positions.push_back(glm::vec3(yLinePosX - edgeLength * 0.01f, scaledTrajectoryMax.y, center.z));
1488 textComp[AnnotationAxis::Y]->
texts.push_back(
"Y");
1489 textComp[AnnotationAxis::Y]->
positions.push_back(glm::vec3(yLinePosX + edgeLength * 0.005f, (scaledTrajectoryMin.y + scaledTrajectoryMax.y) / 2, center.z));
1493 if (zShowAnnotation)
1495 std::stringstream zMaxText;
1496 zMaxText <<
" " <<
static_cast<int>(ceil(trajectoryMax.z * unitScale)) <<
" " << unitText <<
" ";
1497 textComp[AnnotationAxis::Z]->
texts.push_back(zMaxText.str());
1498 textComp[AnnotationAxis::Z]->
positions.push_back(glm::vec3(zLinePosX + edgeLength * 0.005f, center.y, scaledTrajectoryMax.z));
1500 std::stringstream zMinText;
1501 zMinText <<
" " <<
static_cast<int>(floor(trajectoryMin.z * unitScale)) <<
" " << unitText <<
" ";
1502 textComp[AnnotationAxis::Z]->
texts.push_back(zMinText.str());
1503 textComp[AnnotationAxis::Z]->
positions.push_back(glm::vec3(zLinePosX + edgeLength * 0.005f, center.y, scaledTrajectoryMin.z));
1505 textComp[AnnotationAxis::Z]->
texts.push_back(
"Z");
1506 textComp[AnnotationAxis::Z]->
positions.push_back(glm::vec3(zLinePosX + edgeLength * 0.005f, center.y, (scaledTrajectoryMin.z + scaledTrajectoryMax.z) / 2));
1510 for (
int i = AnnotationAxis::AxisFirst; i <= AnnotationAxis::AxisLast; i++)
1516void MultiphaseFlowComponent::removeDuplicatedPoints(std::vector<TrajectoryPoint>& points)
1518 assert(!points.empty());
1519 std::vector<TrajectoryPoint> newPoints;
1520 newPoints.reserve(points.size());
1521 newPoints.push_back(points[0]);
1523 for (
size_t i = 1; i < points.size(); i++)
1525 if (points[i].pos != newPoints[newPoints.size() - 1].pos)
1527 newPoints.push_back(points[i]);
1531 points.swap(newPoints);
1534void MultiphaseFlowComponent::applyScaling(std::vector<TrajectoryPoint>& points)
1536 assert(!points.empty());
1538 float bottomHeight = points[0].pos.z;
1539 for (
auto& p : points)
1541 bottomHeight = glm::min(bottomHeight, p.pos.z);
1544 for (
auto& p : points)
1546 p.pos.z = (p.pos.z - bottomHeight) * heightScale;
1549 for (
auto& p : points)
1551 p.radius *= radiusScale;
1555void MultiphaseFlowComponent::center(std::vector<TrajectoryPoint>& points)
1557 assert(!points.empty());
1559 glm::vec3 min = points[0].pos;
1560 glm::vec3 max = points[0].pos;
1561 for (
auto& p : points)
1563 min = glm::min(min, p.pos);
1564 max = glm::max(max, p.pos);
1568 center.x = (min.x + max.x) / 2;
1569 center.y = (min.y + max.y) / 2;
1570 center.z = (min.z + max.z) / 2;
1572 for (
auto& p : points)
1578static float crosssectionAreaToHeight(
float alpha)
1580 static const float fac = glm::pow(1.5f * glm::pi<float>(), 1.f / 3.f);
1581 const float beta = glm::pi<float>() * alpha + fac * (1 - 2 * alpha + glm::pow(alpha, 1.f / 3.f) - glm::pow(1 - alpha, 1.f / 3.f));
1582 float res = 0.5f - 0.5f * glm::cos(beta);
1586static glm::quat myRotation(glm::vec3 dir, glm::vec3 up = glm::vec3(0, 1, 0))
1588 glm::vec3 x = glm::cross(up, dir);
1589 x = glm::normalize(x);
1591 glm::vec3 y = glm::cross(dir, x);
1592 y = glm::normalize(y);
1594 glm::mat3x3 cMatrix(x, y, dir);
1596 return glm::quat(cMatrix);
1599void MultiphaseFlowComponent::finalizeControlPoints(std::vector<ControlPoint>& controlPoints)
1601 static const int DISTRIBUTION_SIZE = 5;
1602 static float distributionTable[DISTRIBUTION_SIZE + 1];
1603 static bool distributionTableInitialized =
false;
1604 if (!distributionTableInitialized)
1606 for (
int x = 0; x <= DISTRIBUTION_SIZE; x++)
1608 float y = expf(-(x * x * 0.1f));
1609 distributionTable[x] = y;
1611 distributionTableInitialized =
true;
1614 for (
int i = 0; i < static_cast<int>(controlPoints.size()); i++)
1616 auto& controlPoint = controlPoints[i];
1619 controlPoint.annularFlowRatio = 0.f;
1622 for (
int j = -DISTRIBUTION_SIZE; j <= DISTRIBUTION_SIZE; j++)
1626 if (k >
static_cast<int>(controlPoints.size() - 1)) k =
static_cast<int>(controlPoints.size() - 1);
1628 auto& sibling = controlPoints[k];
1630 float ratio = distributionTable[j < 0 ? -j : j];
1631 controlPoint.annularFlowRatio += sibling.annularFlow ? ratio : 0.f;
1636 controlPoint.annularFlowRatio /= sum;
1637 controlPoint.annularFlowRatio = glm::clamp(controlPoint.annularFlowRatio, 0.f, 1.f);
1640 controlPoint.invertedFlowRatio = controlPoint.invertedFlow ? 1.f : 0.f;
1644 std::vector<std::pair<int, int>> annularRanges;
1646 for (
int i = 0; i < static_cast<int>(controlPoints.size()); i++)
1648 auto& controlPoint = controlPoints[i];
1649 if (start == -1 && controlPoint.annularFlowRatio > 0.f)
1651 start = glm::max(i - 1, 0);
1654 if (start >= 0 && controlPoint.annularFlowRatio == 0.f)
1657 annularRanges.push_back(std::pair<int, int>(start, i));
1663 annularRanges.push_back(std::pair<int, int>(start,
static_cast<int>(controlPoints.size() - 1)));
1667 for (
int i = 0; i < (int)annularRanges.size(); i++)
1669 auto& p0 = controlPoints[annularRanges[i].first];
1670 auto& p1 = controlPoints[annularRanges[i].second];
1672 float p0InvertedFlow = p0.invertedFlow ? 1.f : 0.f;
1673 float p1InvertedFlow = p1.invertedFlow ? 1.f : 0.f;
1675 int dist = annularRanges[i].second - annularRanges[i].first;
1678 for (
int j = 0; j <= dist; j++)
1680 float ratio =
static_cast<float>(j) /
static_cast<float>(dist);
1681 ratio = sCurve(ratio);
1682 ratio = glm::clamp(ratio, 0.f, 1.f);
1684 auto up = glm::lerp(p0.rotationUpVector, p1.rotationUpVector, ratio);
1686 int k = annularRanges[i].first + j;
1687 controlPoints[k].rotation = myRotation(controlPoints[k].direction, up);
1690 controlPoints[k].invertedFlowRatio = glm::lerp(p0InvertedFlow, p1InvertedFlow, ratio);
1696void MultiphaseFlowComponent::updateShadowVisibility()
1698 auto* cam = context->cameraSystem->getMainCamera();
1701 bool show = (transform->position.z > geometryMin.z) && (trajectoryPoints.size() == radius.size()) && showShadow;
1703 if (meshEntity[MeshType::Shadow])
1705 auto meshSceneCompoment = meshEntity[MeshType::Shadow]->getComponent<
MeshRenderComponent>();
1706 if (meshSceneCompoment)
1713void MultiphaseFlowComponent::updateMeshes()
1715 assert(updateNeeded);
1717 updateNeeded =
false;
1719 assert(flowFraction.empty() || flowFraction.size() == trajectoryPoints.size());
1720 assert(wallValues.empty() || wallValues.size() == trajectoryPoints.size());
1722 std::vector<TrajectoryPoint> trajectory(trajectoryPoints.size());
1724 for (
size_t i = 0; i < trajectoryPoints.size(); i++)
1726 auto& trajPoint = trajectory[i];
1727 trajPoint.pos = trajectoryPoints[i];
1728 trajPoint.radius = radius[i];
1729 trajPoint.flowFraction = !flowFraction.empty() ? flowFraction[i] : glm::vec3();
1730 trajPoint.wallValue = !wallValues.empty() ? wallValues[i] : 0.0f;
1734 md += glm::distance(trajectory[i - 1].pos, trajectory[i].pos);
1740 removeDuplicatedPoints(trajectory);
1747 applyScaling(trajectory);
1749 stretch(trajectory);
1750 interpolate(trajectory);
1751 stretch(trajectory);
1759 auto posOffset = trajectoryPoints[0] - trajectory[0].pos;
1760 for (
auto &v : trajectory)
1766 std::vector<ControlPoint> controlPoints;
1767 controlPoints.resize(trajectory.size());
1768 bool visualizeMultiphaseFlow = !flowFraction.empty();
1769 scaledTrajectoryMin = trajectory[0].pos;
1770 scaledTrajectoryMax = trajectory[0].pos;
1771 bool waterInMultiphaseFlow =
false;
1772 bool oilInMultiphaseFlow =
false;
1773 bool gasInMultiphaseFlow =
false;
1774 for (
size_t i = 0; i < trajectory.size(); i++)
1776 auto& controlPoint = controlPoints[i];
1777 controlPoint.position = trajectory[i].pos;
1778 controlPoint.radius = trajectory[i].radius;
1779 controlPoint.wall = glm::clamp(!wallValues.empty() ? trajectory[i].wallValue : 0.f, 0.f, 1.f);
1782 scaledTrajectoryMin = glm::min(scaledTrajectoryMin, trajectory[i].pos);
1783 scaledTrajectoryMax = glm::max(scaledTrajectoryMax, trajectory[i].pos);
1786 controlPoint.direction = trajectory[i == 0 ? 0 : i - 1].pos - trajectory[i < trajectory.size() - 1 ? i + 1 : i].pos;
1787 controlPoint.direction = glm::normalize(controlPoint.direction);
1790 auto d = glm::dot(controlPoint.direction, glm::vec3(0, 1, 0));
1791 auto right = glm::abs(d) > 0.95f ? glm::vec3(1, 0, 0) : glm::vec3(0, 1, 0);
1794 controlPoint.rotationUpVector = right;
1798 auto rotPos = myRotation(controlPoint.direction, right);
1799 auto rotNeg = myRotation(controlPoint.direction, -right);
1801 auto upPos = rotPos * glm::vec3(1, 0, 0);
1802 auto upNeg = rotNeg * glm::vec3(1, 0, 0);
1804 auto upPrev = controlPoints[i - 1].rotation * glm::vec3(1, 0, 0);
1806 auto dotPos = glm::dot(upPos, upPrev);
1807 auto dotNeg = glm::dot(upNeg, upPrev);
1809 controlPoint.rotationUpVector = right * (dotPos > dotNeg ? 1.0f : -1.0f);
1811 controlPoint.rotation = myRotation(controlPoint.direction, controlPoint.rotationUpVector);
1812 trajectory[i].rotation = controlPoint.rotation;
1815 auto& flowData = controlPoint.flow;
1816 flowData.water = visualizeMultiphaseFlow ? glm::max(trajectory[i].flowFraction.x, 0.f) : 0.f;
1817 flowData.oil = visualizeMultiphaseFlow ? glm::max(trajectory[i].flowFraction.y, 0.f) : 0.f;
1818 flowData.gas = visualizeMultiphaseFlow ? glm::max(trajectory[i].flowFraction.z, 0.f) : 0.f;
1821 float total = flowData.water + flowData.oil + flowData.gas;
1824 flowData.water /= total;
1825 flowData.oil /= total;
1826 flowData.gas /= total;
1830 if (flowData.water > 0.f) { waterInMultiphaseFlow =
true; }
1831 if (flowData.oil > 0.f) { oilInMultiphaseFlow =
true; }
1832 if (flowData.gas > 0.f) { gasInMultiphaseFlow =
true; }
1835 float h1 = crosssectionAreaToHeight(flowData.water);
1836 float h2 = crosssectionAreaToHeight(flowData.water + flowData.oil);
1838 flowData.heightWaterOil = -1 * (h1 - 0.5f) * 2.f;
1839 flowData.heightOilGas = -1 * (h2 - 0.5f) * 2.f;
1841 flowData.widthWaterOil = glm::sqrt(1 - (1 - h1 * 2) * (1 - h1 * 2));
1842 flowData.widthOilGas = glm::sqrt(1 - (1 - h2 * 2) * (1 - h2 * 2));
1845 glm::vec3 testVec = controlPoint.rotation * glm::vec3(1, 0, 0);
1846 controlPoint.invertedFlow = (testVec.z > 0.f);
1849 bool isAnnularFlow = (std::fabs(controlPoint.direction.z) >= ANNULAR_FLOW_INCLINATION);
1850 controlPoint.annularFlow = isAnnularFlow;
1854 finalizeControlPoints(controlPoints);
1857 geometryMin = glm::vec3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
1858 geometryMax = glm::vec3(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
1861 for (
int i = MeshType::First; i <= MeshType::Last; i++)
1863 auto liquidType = MeshType(i);
1866 Mesh* mesh = context->meshManager->get(meshEntity[i]->getComponent<MeshComponent>()->meshHandle);
1869 updateMaterial(meshEntity[i], liquidType);
1871 bool visible =
true;
1874 case MeshType::StratifiedWater:
1875 case MeshType::AnnularWater:
1876 visible = showWater && waterInMultiphaseFlow && visualizeMultiphaseFlow;
1878 case MeshType::StratifiedOil:
1879 case MeshType::AnnularOil:
1880 visible = showOil && oilInMultiphaseFlow && visualizeMultiphaseFlow;
1882 case MeshType::StratifiedGas:
1883 case MeshType::AnnularGas:
1884 visible = showGas && gasInMultiphaseFlow && visualizeMultiphaseFlow;
1886 case MeshType::Wall:
1887 visible = (wallValues.size() == trajectoryPoints.size() || !visualizeMultiphaseFlow);
1892 auto material = meshRenderComp->
material;
1893 material->setOption(
"CullMode", visualizeMultiphaseFlow ?
"Back" :
"Front");
1895 material->setVariant(
"EnableLighting", !visualizeMultiphaseFlow);
1896 material->setVariant(
"LightModel", visualizeMultiphaseFlow ?
"BaseColor" :
"Phong");
1899 case MeshType::Shadow:
1901 visible = (trajectoryPoints.size() == radius.size());
1905 assert(
false &&
"Unknown liquid type");
1911 generateMultiphaseFlowMesh(mesh, controlPoints, liquidType);
1921 updateAxisAnnotations();
1922 updateIndicatorRing(trajectory);
1925void MultiphaseFlowComponent::update()
1927 if (trajectoryPoints.size() < 2 || trajectoryPoints.size() != radius.size())
1932 if (showAxialExtents != currentShowAxialExtents ||
1933 showShadow != currentShowGrid ||
1934 showGrid != currentShowGrid ||
1935 centerMesh != currentCenterMesh ||
1936 showIndicatorRing != currentShowIndicatorRing)
1938 currentShowAxialExtents = showAxialExtents;
1939 currentShowGrid = showShadow;
1940 currentShowGrid = showGrid;
1941 currentCenterMesh = centerMesh;
1942 currentShowIndicatorRing = showIndicatorRing;
1943 updateNeeded =
true;
1952 updateShadowVisibility();
1956 auto* cam = context->cameraSystem->getMainCamera();
1958 auto* zTextComp = textEntity[AnnotationAxis::Z]->getComponent<
TextComponent>();
1961 auto* yMinTextComp = textEntity[AnnotationAxis::YMin]->getComponent<
TextComponent>();
1962 auto* yMaxTextComp = textEntity[AnnotationAxis::YMax]->getComponent<
TextComponent>();
1963 auto* yTextComp = textEntity[AnnotationAxis::Y]->getComponent<
TextComponent>();
1965 if (yMinTextComp !=
nullptr && yMaxTextComp !=
nullptr && yTextComp !=
nullptr)
1973void MultiphaseFlowComponent::calculateNormals(std::vector<Vertex>& vertexBuffer, std::vector<unsigned int>& indexBuffer)
1975 for (
size_t index = 0; index < indexBuffer.size() / 3; index++)
1977 auto i0 = indexBuffer[index * 3 + 0];
1978 auto i1 = indexBuffer[index * 3 + 1];
1979 auto i2 = indexBuffer[index * 3 + 2];
1981 auto& p0 = vertexBuffer[i0];
1982 auto& p1 = vertexBuffer[i1];
1983 auto& p2 = vertexBuffer[i2];
1985 auto p = p1.position - p0.position;
1986 auto q = p2.position - p0.position;
1987 auto s = p2.position - p1.position;
1989 if (glm::length(p) > 0.0001f &&
1990 glm::length(q) > 0.0001f &&
1991 glm::length(s) > 0.0001f)
1993 auto n = glm::cross(p, q);
1994 n = glm::normalize(n);
1996 p0.normalAndOpacity.x = n.x;
1997 p0.normalAndOpacity.y = n.y;
1998 p0.normalAndOpacity.z = n.z;
2000 p1.normalAndOpacity.x = n.x;
2001 p1.normalAndOpacity.y = n.y;
2002 p1.normalAndOpacity.z = n.z;
2004 p2.normalAndOpacity.x = n.x;
2005 p2.normalAndOpacity.y = n.y;
2006 p2.normalAndOpacity.z = n.z;
void setChanged()
Sets the component to the ComponentFlags::Changed state with carry.
ComponentType * getComponent() const
class Entity * getContainer() const
Get the container currently owning this component instance.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
class EntityStore * store
Entity store.
EntityPtr createChildEntity(const StringView &type, ComponentModel::Entity *parent, const StringView &name=StringView())
Create a new Entity, parenting it to the given parent.
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
MeshHandle meshHandle
Handle to a Mesh resource to use when rendering.
Renders the contents of a MeshComponent using the given materials.
MaterialInstanceHandle material
Material used to render the mesh.
constexpr void setVisible(bool visible)
Set the specific visibility.
Contains information on how the entity behaves in the scene.
Renders the given text(s) by generating sprites.
std::vector< glm::vec3 > positions
Offset positions for each of the strings in texts.
HorizontalJustification horizontalJustification
Horizontal justification of the text.
std::vector< std::string > texts
A set of text strings to render.
PositionMode positionMode
Positioning mode of the text.
glm::vec4 color
Single color value to apply to all text strings.
FontHandle fontHandle
Handle to the font resource to use when rendering text.
Field definition describing a single data member of a data structure.
Simple method definition.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
@ World
Position given in world space coordinates.
std::shared_ptr< ComponentModel::Entity > EntityPtr
Smart pointer for Entity access.
@ Right
The rightmost character is placed to the left of the screen position.
@ Left
The leftmost character is placed to the right of the screen position.
@ Center
Center the text on the screen position.
@ Top
Align the text towards the top, making the position the baseline of the text.
@ Color
Use color property from point set if present, fallback to basecolor.
Contains geometry calculations and generation.
Contains reflection support.
@ Default
Default material.
void setVec4Property(const VariableKey key, glm::vec4 value)
Set the vec4 property with the given key to value.
Meshes contain streams of vertex data in addition to index data and options defining geometry used fo...
void setIndexes(std::span< const uint32_t > collection)
Set the index data to the collection given.
void clear()
Clear all data from the Mesh, returning it to its initial non-indexed state with no streams and no su...
void setVertexData(Element *elements, size_t count)
Set vertex data.
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
Represents an unique name.