//+------------------------------------------------------------------+ //| DXUtils.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2000-2025, MetaQuotes Ltd." #property link "https://www.mql5.com" //--- #include "DXMath.mqh" #include "DXData.mqh" //--- data types in Wavefront OBJ file format #define OBJ_DATA_UNKNOWN 0 #define OBJ_DATA_V 1 #define OBJ_DATA_VT 2 #define OBJ_DATA_VN 3 #define OBJ_DATA_F 4 //+------------------------------------------------------------------+ //| OBJFaceType | //+------------------------------------------------------------------+ struct OBJFaceType { int total; int v[4]; int t[4]; int n[4]; }; //+------------------------------------------------------------------+ //| Transforms right to left hand system, or backward | //| TVertex must have | //| DXVector4 normal and DXVector2 tcoord members | //+------------------------------------------------------------------+ template void DXInverseWinding(TVertex &vertices[],uint &indices[]) { //--- proccess vertices uint count=ArraySize(vertices); for(uint i=0; i0) faces[idx].v[i]=(int)StringToInteger(str_parsed_faces[0]); if(elements>1) faces[idx].t[i]=(int)StringToInteger(str_parsed_faces[1]); if(elements>2) faces[idx].n[i]=(int)StringToInteger(str_parsed_faces[2]); } //--- debug message if(show_debug) printf("%face d: %d vertex(%d,%d,%d,%d) texture(%d,%d,%d,%d) normal(%d,%d,%d,%d)",idx+1, faces[idx].v[0],faces[idx].v[1],faces[idx].v[2],faces[idx].v[3], faces[idx].t[0],faces[idx].t[1],faces[idx].t[2],faces[idx].t[3], faces[idx].n[0],faces[idx].n[1],faces[idx].n[2],faces[idx].n[3]); //--- break; } default: { break; } } } //--- close the file FileClose(file_handle); //--- if(show_debug) { printf("File %s loaded successfully. Total lines: %d",filename,total_lines); printf("total v_positions=%d",total_positions); printf("total v_normals=%d",total_normals); printf("total v_tcoords=%d",total_tcoords); printf("total faces=%d",total_faces); } //--- return(true); } //+------------------------------------------------------------------+ //| Load static 3D models from Maya 2011 | //| https://en.wikibooks.org/wiki/DirectX/10.0/Direct3D/Loading_Maya | //| TVertex must have | //| DXVector4 position, normal and DXVector2 tcoord members | //+------------------------------------------------------------------+ template bool DXLoadObjModel(const string filename,TVertex &vertices[],uint &indices[],float scale=1.0f) { //--- intermediate data arrays DXVector4 v_positions[]; DXVector2 v_tcoords[]; DXVector4 v_normals[]; OBJFaceType faces[]; //--- load data if(!DXLoadObjData(filename,v_positions,v_tcoords,v_normals,faces,false)) { printf("Error loading model data from %s",filename); return(false); } //--- int total_v_positions=ArraySize(v_positions); int total_v_tcoords =ArraySize(v_tcoords); int total_v_normals =ArraySize(v_normals); int total_faces =ArraySize(faces); //--- check faces if(total_faces==0) { printf("No model data."); return(false); } //--- check consistency of the indices int vertices_count=0; int indices_count =0; bool split_vertices=false; for(int i=0; i4) { printf("Error in %d face, face vertices count is %d",i,faces[i].total); return(false); } //--- for(int j=0; jtotal_v_positions) { printf("Error in %d face, %d vertext index is %d. Total posiitons=%d",i,j,faces[i].v[j],total_v_positions); return(false); } if(total_v_tcoords>0) { if(faces[i].t[j]<=0 || faces[i].t[j]>total_v_tcoords) { printf("Error in %d face, %d tcoord index is %d. Total tcoords=%d",i,j,faces[i].v[j],total_v_positions); return(false); } if(faces[i].t[j]!=faces[i].v[j]) split_vertices=true; } if(total_v_normals>0) { if(faces[i].n[j]<=0 || faces[i].n[j]>total_v_normals) { printf("Error in %d face, %d normal index is %d. Total normals=%d",i,j,faces[i].v[j],total_v_positions); return(false); } if(faces[i].n[j]!=faces[i].v[j]) split_vertices=true; } } //--- calc counts vertices_count+=faces[i].total; if(faces[i].total<4) indices_count+=3; else indices_count+=6; } printf("Data consistency checked."); //--- prepare arrays if(!split_vertices) vertices_count=total_v_positions; ArrayResize(vertices,vertices_count); ArrayResize(indices, indices_count); int v_idx=0,i_idx=0; for(int i=0; i bool DXComputeBox(const DXVector3 &from,const DXVector3 &to,TVertex &vertices[],uint &indices[]) { //--- prepare arrays const int faces=6; if(ArrayResize(vertices,4*faces)!=4*faces) return(false); //--- set indices uint ind[]= {0,1,2, 2,3,0, 4,5,6, 6,7,4, 8,9,10, 10,11,8, 12,13,14, 14,15,12, 16,17,18, 18,19,16, 20,21,22, 22,23,20}; ArrayResize(indices,0); if(ArrayCopy(indices,ind)!=ArraySize(ind)) return(false); //--- prepare boundaries float left=from.x,right=to.x,bottom=from.y,top=to.y,near=from.z,far=to.z; if(from.x>to.x) { right=from.x; left= to.x; } if(from.y>to.y) { top= from.y; bottom=to.y; } if(from.z>to.z) { far=from.z; near=to.z; } //--- left face vertices[0].position=DXVector4(left,top, far, 1.0); vertices[1].position=DXVector4(left,top, near,1.0); vertices[2].position=DXVector4(left,bottom,near,1.0); vertices[3].position=DXVector4(left,bottom,far, 1.0); for(int i=0; i<4; i++) vertices[i].normal=DXVector4(-1.0,0.0,0.0,0.0); //--- right face vertices[4].position=DXVector4(right,top, near,1.0); vertices[5].position=DXVector4(right,top, far, 1.0); vertices[6].position=DXVector4(right,bottom,far, 1.0); vertices[7].position=DXVector4(right,bottom,near,1.0); for(int i=4; i<8; i++) vertices[i].normal=DXVector4(1.0,0.0,0.0,0.0); //--- front face vertices[8].position =DXVector4(left, top, near,1.0); vertices[9].position =DXVector4(right,top, near,1.0); vertices[10].position=DXVector4(right,bottom,near,1.0); vertices[11].position=DXVector4(left, bottom,near,1.0); for(int i=8; i<12; i++) vertices[i].normal=DXVector4(0.0,0.0,-1.0,0.0); //--- back face vertices[12].position=DXVector4(right,top, far,1.0); vertices[13].position=DXVector4(left, top, far,1.0); vertices[14].position=DXVector4(left, bottom,far,1.0); vertices[15].position=DXVector4(right,bottom,far,1.0); for(int i=12; i<16; i++) vertices[i].normal=DXVector4(0.0,0.0,1.0,0.0); //--- top face vertices[16].position=DXVector4(left, top,far, 1.0); vertices[17].position=DXVector4(right,top,far, 1.0); vertices[18].position=DXVector4(right,top,near,1.0); vertices[19].position=DXVector4(left, top,near,1.0); for(int i=16; i<20; i++) vertices[i].normal=DXVector4(0.0,1.0,0.0,0.0); //--- bottom face vertices[20].position=DXVector4(left, bottom,near,1.0); vertices[21].position=DXVector4(right,bottom,near,1.0); vertices[22].position=DXVector4(right,bottom,far, 1.0); vertices[23].position=DXVector4(left, bottom,far, 1.0); for(int i=20; i<24; i++) vertices[i].normal=DXVector4(0.0,-1.0,0.0,0.0); //--- texture coordinates for(int i=0; i bool DXComputeSphere(float radius,uint tessellation,TVertex &vertices[],uint &indices[]) { if(tessellation<3) tessellation=3; uint segments_y =tessellation; uint segments_xz=tessellation*2; //--- prepare arrays uint count=(segments_y+1)*(segments_xz+1); if(ArrayResize(vertices,count)!=count) return(false); count=6*segments_y*(segments_xz); if(ArrayResize(indices,count)!=count) return(false); //--- create rings of vertices at progressively higher latitudes. for(uint i=0,idx=0; i<=segments_y; i++) { DXVector2 tcoord=DXVector2(0.0f,1.0f-(float)i/segments_y); float latitude=(i*DX_PI/segments_y)-DX_PI_DIV2; float dy =(float)sin(latitude); float dxz=(float)cos(latitude); //--- create a single ring of vertices at this latitude. for(uint j=0; j<=segments_xz; j++,idx++) { float longitude=(j%segments_xz)*DX_PI_MUL2/segments_xz; //--- normal DXVector3 normal=DXVector3((float)sin(longitude)*dxz,dy,(float)cos(longitude)*dxz); vertices[idx].normal =DXVector4(normal.x,normal.y,normal.z,0.0); //--- position DXVec3Scale(normal,normal,radius); vertices[idx].position=DXVector4(normal.x,normal.y,normal.z,1.0); //--- texture coords tcoord.x = float(j)/segments_xz; vertices[idx].tcoord =tcoord; } } //--- fill the index buffer with triangles joining each pair of latitude rings. uint stride=segments_xz+1; uint idx=0; for(uint i=0; i bool DXComputeTorus(float outer_radius,float inner_radius,uint tessellation,TVertex &vertices[],uint &indices[]) { if(tessellation<3) tessellation=3; //--- prepare arrays uint count=(tessellation+1)*(tessellation+1); if(ArrayResize(vertices,count)!=count) return(false); count=6*tessellation*tessellation; if(ArrayResize(indices,count)!=count) return(false); //--- uint v=0,idx=0; uint stride=tessellation+1; //--- first we loop around the main ring of the torus. for(uint i=0; i<=tessellation; i++) { DXVector2 tcoord=DXVector2(float(i)/tessellation,0.0f); //--- create a transform matrix that will align geometry to slice perpendicularly though the current ring position. DXMatrix rotation,transform; DXMatrixRotationY(rotation,-1.0f*(i%tessellation)*DX_PI_MUL2/tessellation-DX_PI_DIV2); DXMatrixTranslation(transform,outer_radius,0.0f,0.0f); DXMatrixMultiply(transform,transform,rotation); //--- now we loop along the other axis, around the side of the tube. for(uint j=0; j<=tessellation; j++) { //--- calc normal and position float angle=(j%tessellation)*DX_PI_MUL2/tessellation+DX_PI; vertices[v].normal=DXVector4((float)cos(angle),(float)sin(angle),0.0f,0.0f); vertices[v].position=DXVector4(vertices[v].normal.x*inner_radius,vertices[v].normal.y*inner_radius,0.0f,1.0f); DXVec4Transform(vertices[v].normal, vertices[v].normal, transform); DXVec4Transform(vertices[v].position,vertices[v].position,transform); //--- calc texture coord tcoord.y=1-float(j)/tessellation; vertices[v].tcoord=tcoord; v++; //--- create indices for two triangles. if(i bool DXComputeCylinder(float radius,float height,uint tessellation,TVertex &vertices[],uint &indices[]) { return(DXComputeTruncatedCone(radius,radius,height,tessellation,vertices,indices)); } //+------------------------------------------------------------------+ //| Truncated Cone | //| TVertex must have | //| DXVector4 position, DXVector4 normal and DXVector2 tcoord members| //+------------------------------------------------------------------+ template bool DXComputeTruncatedCone(float radius_top,float radius_bottom,float height,uint tessellation,TVertex &vertices[],uint &indices[]) { if(tessellation<3) tessellation=3; //--- prepare arrays uint count=2*(tessellation+1)+2*tessellation; if(ArrayResize(vertices,count)!=count) return(false); count=6*tessellation+6*(tessellation-1); if(ArrayResize(indices,count)!=count) return(false); //--- prepare normal DXVector2 normal=DXVector2(height,radius_bottom-radius_top); DXVec2Normalize(normal,normal); float dy=height/2.0f; uint v=0,idx=0; uint stride=2; //--- create top and bottom rings of vertices for(uint i=0; i<=tessellation; i++) { float u=1.0f-(float)i/tessellation; float angle=(i*DX_PI_MUL2/tessellation); float dx=(float)sin(angle); float dz=(float)cos(angle); //--- vertices[v].normal =DXVector4(dx*normal.x,normal.y,dz*normal.x,0.0f); vertices[v].position=DXVector4(dx*radius_bottom,-dy,dz*radius_bottom,1.0f); vertices[v].tcoord =DXVector2(u,1.0f); v++; vertices[v].normal =vertices[v-1].normal; vertices[v].position=DXVector4(dx*radius_top,dy,dz*radius_top,1.0f); vertices[v].tcoord =DXVector2(u,0.0f); v++; //--- creater side surface if(i0 && i bool DXComputeCone(float radius,float height,uint tessellation,TVertex &vertices[],uint &indices[]) { if(tessellation<3) tessellation=3; //--- prepare arrays uint count=2*(tessellation+1)+tessellation; if(ArrayResize(vertices,count)!=count) return(false); count=3*tessellation+3*(tessellation-1); if(ArrayResize(indices,count)!=count) return(false); //--- prepare normal DXVector2 normal=DXVector2(height,radius); DXVec2Normalize(normal,normal); float dy=height/2.0f; uint v=0,idx=0; uint stride=2; //--- create top and bottom rings of vertices for(uint i=0; i<=tessellation; i++) { float u=1.0f-(float)i/tessellation; float angle=(i*DX_PI_MUL2/tessellation); float dx=(float)sin(angle); float dz=(float)cos(angle); //--- vertices[v].normal =DXVector4(dx*normal.x,normal.y,dz*normal.x,0.0f); vertices[v].position=DXVector4(dx*radius,-dy,dz*radius,1.0f); vertices[v].tcoord =DXVector2(u,1.0f); v++; vertices[v].normal =vertices[v-1].normal; vertices[v].position=DXVector4(0.0f,dy,0.0f,1.0f); vertices[v].tcoord =DXVector2(u,0.0f); v++; //--- creater side surface if(i0 && i bool DXComputeSurface(double &data[],uint data_width,uint data_height,double data_range, const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size, bool two_sided,bool use_normals, TVertex &vertices[],uint &indices[]) { //--- if(data_width<2 || data_height<2) return(false); //--- prepare arrays for vertices and triangles uint count=data_width*data_height*(two_sided?2:1); if(!ArrayResize(vertices,count)) return(false); count=6*(data_width-1)*(data_height-1)*(two_sided?2:1); if(!ArrayResize(indices,count)) return(false); //--- find min and max value float min_value=+FLT_MAX; float max_value=-FLT_MAX; for(uint j=0; jvalue) min_value=value; if(max_value=data_width-1) DXVec4Subtract(v1,vertices[j*data_width+i-1].position,vertices[j*data_width+i].position); else DXVec4Subtract(v1,vertices[j*data_width+i-1].position,vertices[j*data_width+i+1].position); //--- v2 if(j<=0) DXVec4Subtract(v2,vertices[j*data_width+i].position,vertices[(j+1)*data_width+i].position); else if(j>=data_height-1) DXVec4Subtract(v2,vertices[(j-1)*data_width+i].position,vertices[j*data_width+i].position); else DXVec4Subtract(v2,vertices[(j-1)*data_width+i].position,vertices[(j+1)*data_width+i].position); //--- normal DXVec4Cross(normal,v2,v1,DXVector4(0.0f,0.0f,0.0f,1.0f)); float inv_len=(float)(1.0/sqrt(normal.x*normal.x+normal.y*normal.y+normal.z*normal.z)); vertices[j*data_width+i].normal.x=normal.x*inv_len; vertices[j*data_width+i].normal.y=normal.y*inv_len; vertices[j*data_width+i].normal.z=normal.z*inv_len; vertices[j*data_width+i].normal.w=0.0f; } } } else { DXVector4 n=DXVector4(0.0f,0.0f,0.0f,0.0f); for(int i=0; i blue > light blue > green > yellow > red > dark red | //+---------------------------------------------------------------------+ void DXComputeColorJet(const float value,DXColor &cout) { float v=value*1.1f-0.05f; cout.r = fmin(fmax(v<0.75f ? 4*v-1.5f : 4.5f-4*v,0.0f),1.0f); cout.g = fmin(fmax(v<0.5f ? 4*v-0.5f : 3.5f-4*v,0.0f),1.0f); cout.b = fmin(fmax(v<0.25f ? 4*v+0.5f : 2.5f-4*v,0.0f),1.0f); cout.a = 1.0; } //+---------------------------------------------------------------------+ //| Computes hot to cold color scheme colors on [0;1] range | //| blue > light blue > green > yellow > red | //+---------------------------------------------------------------------+ void DXComputeColorColdToHot(const float value,DXColor &cout) { float v=2*value-1.0f; cout.r = fmin(fmax(2*v,0.0f),1.0f); cout.g = fmin(fmax(2.0f-2*fabs(v),0.0f),1.0f); cout.b = fmin(fmax(-2*v,0.0f),1.0f); cout.a = 1.0; } //+---------------------------------------------------------------------+ //| Computes red to green color scheme colors on [0;1] range | //| red > yellow > dark green | //+---------------------------------------------------------------------+ void DXComputeColorRedToGreen(const float value,DXColor &cout) { if(value<=0.5) { cout.r=1.0f; cout.g=DXScalarLerp(0.01f,0.95f,fmin(fmax(2*value,0.0f),1.0f)); } else { cout.r=DXScalarLerp(0.1f,1.0f, fmin(fmax(2.0f-2*value,0.0f),1.0f)); cout.g=DXScalarLerp(0.6f,0.95f,fmin(fmax(2.0f-2*value,0.0f),1.0f)); } cout.b=0.0f; cout.a=1.0f; } //+------------------------------------------------------------------+