[DEV] add normal debug and basic correst display of a LowPoly tree

This commit is contained in:
Edouard DUPIN 2016-11-13 23:22:33 +01:00
parent a0132c0090
commit 0a474afb7e
18 changed files with 296 additions and 37 deletions

View File

@ -545,17 +545,17 @@ def write_file(filepath,
)) # vert, uv, normal
face_vert_index += len(f_v)
else: # No UV's
# export the normals :
# export the normals:
if f_smooth: # Smoothed, use vertex normals
for vi, v in f_v:
fw(" %d//%d" % (
fw(" %d/%d" % (
v.index + totverts-1,
globalNormals[veckey3d(v.normal)]-1,
))
else: # No smoothing, face normals
no = globalNormals[veckey3d(f.normal)]
for vi, v in f_v:
fw(" %d//%d" % (v.index + totverts-1, no-1))
fw(" %d/%d" % (v.index + totverts-1, no-1))
fw('|')
fw('\n')
# Write edges. ==> did not know what it is ...

49
data/material3D.frag Normal file
View File

@ -0,0 +1,49 @@
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif
struct DirectionalLight {
vec3 direction;
vec3 halfplane;
vec4 ambientColor;
vec4 diffuseColor;
vec4 specularColor;
};
struct Material {
vec4 ambientFactor;
vec4 diffuseFactor;
vec4 specularFactor;
float shininess;
};
// Light
uniform DirectionalLight EW_directionalLight;
// Material
uniform Material EW_material;
varying vec3 v_ecNormal;
void main(void) {
// Normalize v_ecNormal
vec3 ecNormal = v_ecNormal / length(v_ecNormal);
float ecNormalDotLightDirection = max(0.0, dot(ecNormal, EW_directionalLight.direction));
float ecNormalDotLightHalfplane = max(0.0, dot(ecNormal, EW_directionalLight.halfplane));
// Calculate ambient light
vec4 ambientLight = EW_directionalLight.ambientColor * EW_material.ambientFactor;
// Calculate diffuse light
vec4 diffuseLight = ecNormalDotLightDirection * EW_directionalLight.diffuseColor * EW_material.diffuseFactor;
// Calculate specular light
vec4 specularLight = EW_directionalLight.specularColor * EW_material.specularFactor;
if (ecNormalDotLightHalfplane > 0.0) {
specularLight = pow(ecNormalDotLightHalfplane, EW_material.shininess) * EW_directionalLight.specularColor * EW_material.specularFactor;
specularLight = EW_directionalLight.specularColor * EW_material.specularFactor;
}
vec4 light = ambientLight + diffuseLight + specularLight;
gl_FragColor = light;
}

21
data/material3D.vert Normal file
View File

@ -0,0 +1,21 @@
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif
// Input:
attribute vec3 EW_coord3d;
attribute vec3 EW_normal;
uniform mat4 EW_MatrixTransformation;
uniform mat4 EW_MatrixPosition;
// output:
varying vec3 v_ecNormal;
void main(void) {
gl_Position = EW_MatrixTransformation * EW_MatrixPosition * vec4(EW_coord3d, 1.0);
mat4 MatrixPosition = EW_MatrixPosition;
MatrixPosition[3][0] = 0.0;
MatrixPosition[3][1] = 0.0;
MatrixPosition[3][2] = 0.0;
v_ecNormal = vec3(MatrixPosition * vec4(EW_normal, 1.0) );
}

View File

@ -184,6 +184,10 @@ void ege::Element::drawDebug(ememory::SharedPtr<ewol::resource::Colored3DObject>
*/
}
void ege::Element::drawNormalDebug(ememory::SharedPtr<ewol::resource::Colored3DObject> _draw, ememory::SharedPtr<ege::Camera> _camera) {
// nothing to do ...
}

View File

@ -167,9 +167,16 @@ namespace ege {
public:
/**
* @brief Debug display of the current element
* @param[in,out] draw Basic system to draw the debug shape and informations
* @param[in,out] _draw Basic system to draw the debug shape and informations
* @param[in] _camera Current camera for display
*/
virtual void drawDebug(ememory::SharedPtr<ewol::resource::Colored3DObject> _draw, ememory::SharedPtr<ege::Camera> _camera);
/**
* @brief Debug display of the current element normal face
* @param[in,out] _draw Basic system to draw the debug shape and informations
* @param[in] _camera Current camera for display
*/
virtual void drawNormalDebug(ememory::SharedPtr<ewol::resource::Colored3DObject> _draw, ememory::SharedPtr<ege::Camera> _camera);
/**
* @brief get the theoric position. Sometimes, the element has move due to an explosion or something else, then its real position in not the one that woult it be at the end ...

View File

@ -330,6 +330,22 @@ void ege::ElementPhysic::drawDebug(ememory::SharedPtr<ewol::resource::Colored3DO
drawShape(m_shape, _draw, transformationMatrix, EwolVertices);
}
void ege::ElementPhysic::drawNormalDebug(ememory::SharedPtr<ewol::resource::Colored3DObject> _draw, ememory::SharedPtr<ege::Camera> _camera) {
if( m_body != nullptr
&& m_mesh != nullptr
&& m_body->getMotionState() ) {
//EGE_INFO("element pos = " << getPosition());
btScalar mmm[16];
btDefaultMotionState* myMotionState = (btDefaultMotionState*)m_body->getMotionState();
myMotionState->m_graphicsWorldTrans.getOpenGLMatrix(mmm);
mat4 transformationMatrix(mmm);
transformationMatrix.transpose();
m_mesh->drawNormal(transformationMatrix, _draw);
}
}
void ege::ElementPhysic::draw(int32_t _pass) {
if (m_elementInPhysicsSystem == false) {
return;

View File

@ -87,6 +87,7 @@ namespace ege {
* @brief draw the current life of the element
*/
// virtual void drawLife(const ememory::SharedPtr<ewol::resource::Colored3DObject>& _draw, const ememory::SharedPtr<ege::Camera>& _camera);
virtual void drawNormalDebug(ememory::SharedPtr<ewol::resource::Colored3DObject> _draw, ememory::SharedPtr<ege::Camera> _camera);
// TODO : Remove this ...
protected:
vec3 m_theoricPosition;

View File

@ -133,8 +133,10 @@ void ege::resource::Mesh::draw(mat4& _positionMatrix,
m_GLprogram->uniformMatrix(m_GLMatrixPosition, _positionMatrix);
// position :
m_GLprogram->sendAttributePointer(m_GLPosition, m_verticesVBO, MESH_VBO_VERTICES);
// Texture :
m_GLprogram->sendAttributePointer(m_GLtexture, m_verticesVBO, MESH_VBO_TEXTURE);
// Texture (if needed):
if (m_listUV.size() != 0) {
m_GLprogram->sendAttributePointer(m_GLtexture, m_verticesVBO, MESH_VBO_TEXTURE);
}
// position :
if (m_normalMode != normalModeNone) {
m_GLprogram->sendAttributePointer(m_GLNormal, m_verticesVBO, MESH_VBO_VERTICES_NORMAL);
@ -162,7 +164,7 @@ void ege::resource::Mesh::draw(mat4& _positionMatrix,
continue;
}
m_materials[m_listFaces.getKey(kkk)]->draw(m_GLprogram, m_GLMaterial);
if (m_checkNormal == false) {
if (true) { // TODO : understand why the optimisation does not work at all ... : if (m_checkNormal == false) {
gale::openGL::drawElements(m_materials[m_listFaces.getKey(kkk)]->getRenderModeOpenGl(), m_listFaces.getValue(kkk).m_index);
#ifdef DISPLAY_NB_VERTEX_DISPLAYED
nbElementDraw += m_listFaces.getValue(kkk).m_index.size();
@ -240,6 +242,71 @@ void ege::resource::Mesh::draw(mat4& _positionMatrix,
EGE_VERBOSE("draw Mesh : " << m_name << " ( end )");
}
void ege::resource::Mesh::drawNormal(mat4& _positionMatrix,
ememory::SharedPtr<ewol::resource::Colored3DObject> _draw) {
etk::Color<float> tmpColor(0.0, 1.0, 0.0, 1.0);
std::vector<vec3> vertices;
// generate element in 2 pass :
// - create new index dependeng a vertex is a unique componenet of position, texture, normal
// - the index list generation (can be dynamic ... (TODO later)
for (int32_t kkk=0; kkk<m_listFaces.size(); kkk++) {
// clean faces indexes :
int32_t nbIndicInFace = 3;
switch (m_materials[m_listFaces.getKey(kkk)]->getRenderMode()) {
case gale::openGL::renderMode::triangle:
case gale::openGL::renderMode::triangleStrip:
case gale::openGL::renderMode::triangleFan:
nbIndicInFace = 3;
break;
case gale::openGL::renderMode::line:
case gale::openGL::renderMode::lineStrip:
case gale::openGL::renderMode::lineLoop:
nbIndicInFace = 2;
break;
case gale::openGL::renderMode::point:
nbIndicInFace = 1;
break;
case gale::openGL::renderMode::quad:
case gale::openGL::renderMode::quadStrip:
nbIndicInFace = 4;
break;
case gale::openGL::renderMode::polygon:
nbIndicInFace = 3;
break;
}
FaceIndexing& tmpFaceList = m_listFaces.getValue(kkk);
for (size_t iii=0; iii<tmpFaceList.m_faces.size() ; iii++) {
switch(m_normalMode) {
case normalModeVertex:
{
// dsplay normal for each vertice ... (TODO: not tested ...)
for(size_t indice=0 ; indice<nbIndicInFace; indice++) {
vec3 position = m_listVertex[tmpFaceList.m_faces[iii].m_vertex[indice]];
vec3 normal = m_listVertexNormal[tmpFaceList.m_faces[iii].m_normal[indice]];
vertices.push_back(position);
vertices.push_back(position+normal/4);
}
} break;
case normalModeFace:
{
vec3 center(0,0,0);
for(size_t indice=0 ; indice<nbIndicInFace; indice++) {
vec3 position = m_listVertex[tmpFaceList.m_faces[iii].m_vertex[indice]];
center += position;
}
center /= float(nbIndicInFace);
vec3 normal = m_listFacesNormal[tmpFaceList.m_faces[iii].m_normal[0]];
vertices.push_back(center);
vertices.push_back(center+normal/4);
} break;
case normalModeNone:
break;
}
}
}
_draw->drawLine(vertices, tmpColor, _positionMatrix);
}
// normal calculation of the normal face is really easy :
// TODO : Use it for multiple Material interface
void ege::resource::Mesh::calculateNormaleFace(const std::string& _materialName) {
@ -259,7 +326,12 @@ void ege::resource::Mesh::calculateNormaleFace(const std::string& _materialName)
// for all case, We use only the 3 vertex for quad element, in theory 3D modeler export element in triangle if it is not a real plane.
vec3 normal = btCross(m_listVertex[it.m_vertex[0]]-m_listVertex[it.m_vertex[1]],
m_listVertex[it.m_vertex[1]]-m_listVertex[it.m_vertex[2]]);
m_listFacesNormal.push_back(normal.normalized());
if (normal == vec3(0,0,0)) {
EGE_ERROR("Null vertor for a face ... " << m_listVertex[it.m_vertex[0]] << " " << m_listVertex[it.m_vertex[1]] << " " << m_listVertex[it.m_vertex[2]]);
m_listFacesNormal.push_back(vec3(1,0,0));
} else {
m_listFacesNormal.push_back(normal.normalized());
}
}
}
}
@ -304,8 +376,10 @@ void ege::resource::Mesh::calculateNormaleEdge(const std::string& _materialName)
void ege::resource::Mesh::generateVBO() {
// calculate the normal of all faces if needed
if (m_normalMode != ege::resource::Mesh::normalModeNone) {
if ( m_normalMode != ege::resource::Mesh::normalModeNone
&& m_listFacesNormal.size() == 0) {
// when no normal detected == > auto generate Face normal ....
EGE_ERROR("Calculate normal face ... in case ????");
calculateNormaleFace(m_listFaces.getKeys()[0]);
}
EGE_WARNING("Generate VBO for nb faces layers: " << m_listFaces.size() << " list layer=" << etk::to_string(m_listFaces.getKeys()));
@ -355,7 +429,7 @@ void ege::resource::Mesh::generateVBO() {
} else {
color = etk::color::white;
}
// get µNormal
// get Normal
vec3 normal;
switch(m_normalMode) {
case normalModeVertex:

View File

@ -12,6 +12,7 @@
#include <gale/resource/Shader.hpp>
#include <gale/resource/Program.hpp>
#include <gale/resource/VirtualBufferObject.hpp>
#include <ewol/resource/Colored3DObject.hpp>
#include <ege/Light.hpp>
#include <ege/Material.hpp>
#include <ege/resource/tools/Face.hpp>
@ -30,12 +31,12 @@ namespace ege {
class Mesh : public gale::Resource {
public:
static ememory::SharedPtr<ege::resource::Mesh> createGrid(int32_t _lineCount,
const vec3& _position=vec3(0,0,0),
float _size=1.0f,
const std::string& _materialName="basics");
const vec3& _position=vec3(0,0,0),
float _size=1.0f,
const std::string& _materialName="basics");
static ememory::SharedPtr<ege::resource::Mesh> createCube(float _size=1.0f,
const std::string& _materialName="basics",
const etk::Color<float>& _color=etk::color::white);
const std::string& _materialName="basics",
const etk::Color<float>& _color=etk::color::white);
public:
/**
* @not_in_doc
@ -74,7 +75,10 @@ namespace ege {
ememory::SharedPtr<gale::resource::VirtualBufferObject> m_verticesVBO;
protected:
Mesh();
void init(const std::string& _fileName="---", const std::string& _shaderName="DATA:textured3D2.prog");
void init(const std::string& _fileName="---",
//const std::string& _shaderName="DATA:textured3D2.prog"
const std::string& _shaderName="DATA:material3D.prog"
);
public:
virtual ~Mesh();
DECLARE_RESOURCE_NAMED_FACTORY(Mesh);
@ -86,6 +90,9 @@ namespace ege {
bool _enableDepthUpdate = true) {
draw(_positionMatrix, _enableDepthTest, _enableDepthUpdate);
}
// For debug only ...
void drawNormal(mat4& _positionMatrix,
ememory::SharedPtr<ewol::resource::Colored3DObject> _draw);
void generateVBO();
private:
void calculateNormaleFace(const std::string& _materialName);

View File

@ -145,11 +145,11 @@ bool ege::resource::Mesh::loadEMF(const std::string& _fileName) {
// get the fileSize ...
int32_t size = fileName.fileSize();
if (size == 0 ) {
EGE_ERROR("No data in the file named=\"" << fileName << "\"");
EGE_ERROR("No data in the file named='" << fileName << "'");
return false;
}
if (fileName.fileOpenRead() == false) {
EGE_ERROR("Can not find the file name=\"" << fileName << "\"");
EGE_ERROR("Can not find the file name='" << fileName << "'");
return false;
}
char inputDataLine[2048];
@ -174,6 +174,7 @@ bool ege::resource::Mesh::loadEMF(const std::string& _fileName) {
ememory::SharedPtr<ege::Material> material;
// physical shape:
ememory::SharedPtr<ege::PhysicsShape> physics;
bool haveUVMapping = false;
while (1) {
int32_t level = countIndent(fileName);
if (level == 0) {
@ -217,6 +218,7 @@ bool ege::resource::Mesh::loadEMF(const std::string& _fileName) {
EGE_VERBOSE(" Vertex ...");
} else if(strncmp(inputDataLine, "UV-mapping", 10) == 0) {
currentMode = EMFModuleMeshUVMapping;
haveUVMapping = true;
EGE_VERBOSE(" UV-mapping ...");
} else if(strncmp(inputDataLine, "Normal(vertex)", 14) == 0) {
currentMode = EMFModuleMeshNormalVertex;
@ -296,6 +298,7 @@ bool ege::resource::Mesh::loadEMF(const std::string& _fileName) {
break;
}
case EMFModuleMeshNormalFace: {
EGE_ERROR("Change mode in face mode ...");
m_normalMode = normalModeFace;
vec3 normal(0,0,0);
// find the face Normal list.
@ -342,19 +345,26 @@ bool ege::resource::Mesh::loadEMF(const std::string& _fileName) {
vertexIndex[0] = 0;
vertexIndex[1] = 0;
vertexIndex[2] = 0;
uvIndex[0] = 0;
uvIndex[1] = 0;
uvIndex[2] = 0;
uvIndex[0] = -1;
uvIndex[1] = -1;
uvIndex[2] = -1;
normalIndex[0] = 0;
normalIndex[1] = 0;
normalIndex[2] = 0;
sscanf(inputDataLine, "%d/%d/%d %d/%d/%d %d/%d/%d",
&vertexIndex[0], &uvIndex[0], &normalIndex[0],
&vertexIndex[1], &uvIndex[1], &normalIndex[1],
&vertexIndex[2], &uvIndex[2], &normalIndex[2] );
if (haveUVMapping == true) {
sscanf(inputDataLine, "%d/%d/%d %d/%d/%d %d/%d/%d",
&vertexIndex[0], &uvIndex[0], &normalIndex[0],
&vertexIndex[1], &uvIndex[1], &normalIndex[1],
&vertexIndex[2], &uvIndex[2], &normalIndex[2] );
} else {
sscanf(inputDataLine, "%d/%d %d/%d %d/%d",
&vertexIndex[0], &normalIndex[0],
&vertexIndex[1], &normalIndex[1],
&vertexIndex[2], &normalIndex[2] );
}
m_listFaces.getValue(meshFaceMaterialID).m_faces.push_back(Face(vertexIndex[0], uvIndex[0], normalIndex[0],
vertexIndex[1], uvIndex[1], normalIndex[1],
vertexIndex[2], uvIndex[2], normalIndex[2]));
vertexIndex[1], uvIndex[1], normalIndex[1],
vertexIndex[2], uvIndex[2], normalIndex[2]));
/*
EGE_DEBUG("face :" << vertexIndex[0] << "/" << uvIndex[0] << "/" << normalIndex[0] <<
" " << vertexIndex[1] << "/" << uvIndex[1] << "/" << normalIndex[1] <<

View File

@ -39,6 +39,9 @@ ege::widget::Scene::Scene() :
propertyDebugPhysic(this, "debugPhysic",
false,
"Display debug of the physic interface"),
propertyDebugNormal(this, "debugMeshNormal",
false,
"Display debug of the mesh normal of each face"),
propertyDebugApplication(this, "debugApplication",
false,
"Display debug of the application"),
@ -133,6 +136,13 @@ void ege::widget::Scene::onDraw() {
}
}
}
if (propertyDebugNormal.get() == true) {
// Draw debug ... (Object)
for (int32_t iii=m_displayElementOrdered.size()-1; iii >= 0; iii--) {
m_displayElementOrdered[iii].element->drawNormalDebug(m_debugDrawProperty, camera);
}
}
if (propertyDebugApplication.get() == true) {
// Draw debug ... (User)
signalDisplayDebug.emit(m_debugDrawProperty);

View File

@ -38,6 +38,7 @@ namespace ege {
esignal::Signal<ememory::SharedPtr<ewol::resource::Colored3DObject>/*, ememory::SharedPtr<ege::Camera>*/> signalDisplayDebug; //!< emit a signal to the application to draw the debug (@ref setDebugPhysic)
// properties
eproperty::Value<bool> propertyDebugPhysic; //!< display Physic Debug
eproperty::Value<bool> propertyDebugNormal; //!< display mesh normal Debug
eproperty::Value<bool> propertyDebugApplication; //!< display Application Debug
protected:
ememory::SharedPtr<ege::Environement> m_env;

View File

@ -64,6 +64,7 @@ def configure(target, my_module):
'ege/Ray.cpp',
])
my_module.copy_path('data/ParticuleMesh.*')
my_module.copy_path('data/material3D.*')
my_module.add_depend(['ewol', 'bullet-physics', 'echrono'])
my_module.add_flag('c++', [
'-Wno-write-strings',

View File

@ -41,7 +41,7 @@ static ememory::SharedPtr<ege::resource::Mesh> createViewBoxStar() {
out->addMaterial("basics", material);
//material->setImageSize(ivec2(size,size));
egami::Image* myImage = material->get();
if (nullptr == myImage) {
if (myImage == nullptr) {
return out;
}
myImage->clear(etk::color::black);
@ -92,11 +92,12 @@ static ememory::SharedPtr<ege::Element> createTree(const ememory::SharedPtr<ege:
void appl::Windows::init() {
ewol::widget::Windows::init();
getObjectManager().periodicCall.connect(sharedFromThis(), &appl::Windows::onCallbackPeriodicUpdateCamera);
// TODO : Auto mode : getObjectManager().periodicCall.connect(sharedFromThis(), &appl::Windows::onCallbackPeriodicUpdateCamera);
m_env = ege::Environement::create();
// Create basic Camera
m_camera = ememory::makeShared<ege::camera::View>(vec3(30,30,-100), vec3(0,0,0));
m_camera = ememory::makeShared<ege::camera::View>(vec3(30,30,-100), vec3(0,0,1));
m_camera->setEye(vec3(100*std::sin(m_angleTetha),100*std::cos(m_angleTetha),80*std::cos(m_anglePsy)));
m_env->addCamera("basic", m_camera);
ememory::SharedPtr<ege::widget::Scene> tmpWidget = ege::widget::Scene::create();
@ -126,6 +127,8 @@ void appl::Windows::init() {
}
}
m_env->propertyStatus.set(ege::gameStart);
tmpWidget->propertyDebugPhysic.set(false);
tmpWidget->propertyDebugNormal.set(false);
}
@ -134,7 +137,58 @@ void appl::Windows::onCallbackPeriodicUpdateCamera(const ewol::event::Time& _eve
offset += 0.01;
static float offset2 = 0;
offset2 += 0.003;
m_camera->setEye(vec3(100*std::sin(offset),100*std::cos(offset),40*std::cos(offset2)));
m_camera->setEye(vec3(10*std::sin(offset),10*std::cos(offset),4*std::cos(offset2)));
}
bool appl::Windows::onEventInput(const ewol::event::Input& _event) {
static float ploppp=1;
if (_event.getId() == 1) {
} else if (_event.getId() == 4) {
ploppp += 0.001f;
m_camera->setEye(vec3(100*std::sin(m_angleTetha),100*std::cos(m_angleTetha),80*std::cos(m_anglePsy))*ploppp);
} else if (_event.getId() == 5) {
ploppp -= 0.001f;
if (ploppp == 0) {
ploppp = 1.0f;
}
m_camera->setEye(vec3(100*std::sin(m_angleTetha),100*std::cos(m_angleTetha),80*std::cos(m_anglePsy))*ploppp);
} else if (_event.getId() == 3) {
if (_event.getStatus() == gale::key::status::down) {
m_oldScreenPos = relativePosition(_event.getPos());
return true;
} else if (_event.getStatus() == gale::key::status::move) {
vec2 pos = relativePosition(_event.getPos());
m_angleTetha -= (m_oldScreenPos.x()-pos.x())*0.05f;
m_anglePsy += (m_oldScreenPos.y()-pos.y())*0.01f;
m_camera->setEye(vec3(100*std::sin(m_angleTetha),100*std::cos(m_angleTetha),80*std::cos(m_anglePsy))*ploppp);
m_oldScreenPos = relativePosition(_event.getPos());
return true;
}
} else if (_event.getId() == 2) {
if (_event.getStatus() == gale::key::status::down) {
m_oldScreenPos = relativePosition(_event.getPos());
return true;
} else if (_event.getStatus() == gale::key::status::move) {
vec2 pos = relativePosition(_event.getPos())*0.2;
pos -= m_oldScreenPos*0.2;
float cameraAngle = m_camera->getTetha();
vec3 newPos = vec3(std::sin(cameraAngle)*pos.x() + std::cos(cameraAngle)*pos.y(),
std::cos(cameraAngle)*pos.x() + std::sin(cameraAngle)*pos.y(),
0);
APPL_ERROR("apply offset = " << newPos << " from pos=" << pos << " angle=" << cameraAngle);
newPos += m_camera->getTarget();
newPos.setMin(vec3(200,200,200));
newPos.setMax(vec3(-200,-200,-200));
m_camera->setTarget(newPos);
m_oldScreenPos = relativePosition(_event.getPos());
return true;
}
} else if (_event.getId() == 10) {
m_camera->setAngle(m_camera->getAngle() + 0.01f);
} else if (_event.getId() == 11) {
m_camera->setAngle(m_camera->getAngle() - 0.01f);
}
return false;
}

View File

@ -24,6 +24,10 @@ namespace appl {
virtual ~Windows() { };
private:
void onCallbackPeriodicUpdateCamera(const ewol::event::Time& _event);
bool onEventInput(const ewol::event::Input& _event);
float m_angleTetha;
float m_anglePsy;
vec2 m_oldScreenPos;
};
}

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long