Advertisement
Guest User

ffs

a guest
Jan 13th, 2014
260
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.28 KB | None | 0 0
  1. //.h
  2. using namespace std;
  3.  
  4. class Terrain
  5. {
  6. public:
  7.     struct InitInfo
  8.     {
  9.         wstring HeightmapFileName,
  10.             LayerMapFileName0,
  11.             LayerMapFileName1,
  12.             LayerMapFileName2,
  13.             LayerMapFileName3,
  14.             LayerMapFileName4,
  15.             BlendMapFileName;
  16.  
  17.         float HeightScale;
  18.         float HeightOffset;
  19.         UINT NumRows;
  20.         UINT NumCols;
  21.         float CellSpacing;
  22.     };
  23.  
  24. public:
  25.     Terrain();
  26.     ~Terrain();
  27.  
  28.     void Init(const InitInfo& info);
  29.  
  30.     float width()const;
  31.     float depth()const;
  32.  
  33.     float getHeight(float x, float y)const;
  34.     void SetDirectionToSun(const XMFLOAT3& v);
  35.     void draw(CXMMATRIX world, Camera& cam);
  36.  
  37.     UINT GetNumVertices()const { return mNumVertices; }
  38.  
  39. private:
  40.     void BuildFX();
  41.     void loadHeightMap();
  42.     void Smooth();
  43.     bool inBounds(UINT i, UINT j);
  44.     float average(UINT i, UINT j);
  45.     void BuildGeometry();
  46.     void BuildSubMesh(RECT &R, Vertex::Basic32* vertex);
  47. private:
  48.     struct SubMesh
  49.     {
  50.         ID3D11Buffer* VertexBuffer;
  51.         ID3D11Buffer* IndexBuffer;
  52.  
  53.         GeometryGenerator::MeshData mesh;
  54.         XNA::AxisAlignedBox box;
  55.  
  56.         const static int NUM_ROWS  = 33;
  57.         const static int NUM_COLS  = 33;
  58.         const static int NUM_TRIS  = (NUM_ROWS-1)*(NUM_COLS-1)*2;
  59.         const static int NUM_VERTS = NUM_ROWS*NUM_COLS;
  60.     };
  61.     InitInfo mInfo;
  62.     UINT mNumVertices;
  63.     UINT mNumFaces;
  64.  
  65.     vector<float> mHeightmap;
  66.     vector<SubMesh> mSubMesh;
  67.  
  68.  
  69.     ID3DX11Effect* mFX;
  70.  
  71.  
  72.     ID3D11Buffer* mVB;
  73.     ID3D11Buffer* mIB;
  74.  
  75.     ID3D11ShaderResourceView* mLayer0;
  76.     ID3D11ShaderResourceView* mLayer1;
  77.     ID3D11ShaderResourceView* mLayer2;
  78.     ID3D11ShaderResourceView* mLayer3;
  79.     ID3D11ShaderResourceView* mLayer4;
  80.     ID3D11ShaderResourceView* mBlendMap;
  81.  
  82.     ID3DX11EffectTechnique* mTech;
  83.     ID3DX11EffectMatrixVariable* mfxWVPVar;
  84.     ID3DX11EffectMatrixVariable* mfxWorldVar;
  85.     ID3DX11EffectVectorVariable* mfxDirToSunVar;
  86.     ID3DX11EffectShaderResourceVariable* mfxLayer0Var;
  87.     ID3DX11EffectShaderResourceVariable* mfxLayer1Var;
  88.     ID3DX11EffectShaderResourceVariable* mfxLayer2Var;
  89.     ID3DX11EffectShaderResourceVariable* mfxLayer3Var;
  90.     ID3DX11EffectShaderResourceVariable* mfxLayer4Var;
  91.     ID3DX11EffectShaderResourceVariable* mfxBlendMapVar;
  92.  
  93. };
  94. //.cpp
  95. #include "stdafx.h"
  96. #include <list>
  97.  
  98. Terrain::Terrain()
  99. {
  100.  
  101.     mVB = nullptr;
  102.     mIB = nullptr;
  103.    
  104.     mLayer0 = nullptr;
  105.     mLayer1 = nullptr;
  106.     mLayer2 = nullptr;
  107.     mLayer3 = nullptr;
  108.     mLayer4 = nullptr;
  109.     mBlendMap = nullptr;
  110.  
  111.     for (int i = 0; i < mSubMesh.size(); i++)
  112.     {
  113.         ReleaseCOM(mSubMesh[i].VertexBuffer);
  114.         ReleaseCOM(mSubMesh[i].IndexBuffer);
  115.     }
  116. }
  117.  
  118. Terrain::~Terrain()
  119. {
  120.     ReleaseCOM(mVB);
  121.     ReleaseCOM(mIB);
  122.     ReleaseCOM(mFX);
  123.  
  124.  
  125.     ReleaseCOM(mBlendMap);
  126.     ReleaseCOM(mLayer0);
  127.     ReleaseCOM(mLayer1);
  128.     ReleaseCOM(mLayer2);
  129.     ReleaseCOM(mLayer3);
  130.     ReleaseCOM(mLayer4);
  131.  
  132. }
  133.  
  134. float Terrain::width()const
  135. {
  136.     return (mInfo.NumCols-1)*mInfo.CellSpacing;
  137. }
  138.  
  139. float Terrain::depth()const
  140. {
  141.     return (mInfo.NumRows-1)*mInfo.CellSpacing;
  142. }
  143.  
  144. float Terrain::getHeight(float x, float z)const
  145. {
  146.     // Transform from terrain local space to "cell" space.
  147.     float c = (x + 0.5f*width()) /  mInfo.CellSpacing;
  148.     float d = (z - 0.5f*depth()) / -mInfo.CellSpacing;
  149.  
  150.     // Get the row and column we are in.
  151.     int row = (int)floorf(d);
  152.     int col = (int)floorf(c);
  153.  
  154.     // Grab the heights of the cell we are in.
  155.     // A*--*B
  156.     //  | /|
  157.     //  |/ |
  158.     // C*--*D
  159.     float A = mHeightmap[row*mInfo.NumCols + col];
  160.     float B = mHeightmap[row*mInfo.NumCols + col + 1];
  161.     float C = mHeightmap[(row+1)*mInfo.NumCols + col];
  162.     float D = mHeightmap[(row+1)*mInfo.NumCols + col + 1];
  163.  
  164.     // Where we are relative to the cell.
  165.     float s = c - (float)col;
  166.     float t = d - (float)row;
  167.  
  168.     // If upper triangle ABC.
  169.     if( s + t <= 1.0f)
  170.     {
  171.         float uy = B - A;
  172.         float vy = C - A;
  173.         return A + s*uy + t*vy;
  174.     }
  175.     else // lower triangle DCB.
  176.     {
  177.         float uy = C - D;
  178.         float vy = B - D;
  179.         return D + (1.0f-s)*uy + (1.0f-t)*vy;
  180.     }
  181. }
  182.  
  183. void Terrain::BuildFX()
  184. {
  185.     std::ifstream fin("Resources\\Shaders\\Terrain.fxo", std::ios::binary);
  186.  
  187.     fin.seekg(0, std::ios_base::end);
  188.     int size = (int)fin.tellg();
  189.     fin.seekg(0, std::ios_base::beg);
  190.     std::vector<char> compiledShader(size);
  191.  
  192.     fin.read(&compiledShader[0], size);
  193.     fin.close();
  194.    
  195.     HR(D3DX11CreateEffectFromMemory(&compiledShader[0], size,
  196.         0, pDevice, &mFX));
  197. }
  198.  
  199. void Terrain::Init(const InitInfo& initInfo)
  200. {
  201.     BuildFX();
  202.  
  203.     mTech          = mFX->GetTechniqueByName("TerrainTech");
  204.     mfxWVPVar      = mFX->GetVariableByName("gWVP")->AsMatrix();
  205.     mfxWorldVar    = mFX->GetVariableByName("gWorld")->AsMatrix();
  206.     mfxDirToSunVar = mFX->GetVariableByName("gDirToSunW")->AsVector();
  207.     mfxLayer0Var   = mFX->GetVariableByName("gLayer0")->AsShaderResource();
  208.     mfxLayer1Var   = mFX->GetVariableByName("gLayer1")->AsShaderResource();
  209.     mfxLayer2Var   = mFX->GetVariableByName("gLayer2")->AsShaderResource();
  210.     mfxLayer3Var   = mFX->GetVariableByName("gLayer3")->AsShaderResource();
  211.     mfxLayer4Var   = mFX->GetVariableByName("gLayer4")->AsShaderResource();
  212.     mfxBlendMapVar = mFX->GetVariableByName("gBlendMap")->AsShaderResource();
  213.  
  214.     mInfo = initInfo;
  215.  
  216.     mNumVertices = mInfo.NumRows*mInfo.NumCols;
  217.     mNumFaces    = (mInfo.NumRows-1)*(mInfo.NumCols-1)*2;
  218.  
  219.     loadHeightMap();
  220.     Smooth();
  221.  
  222.     BuildGeometry();
  223.  
  224.     HR(D3DX11CreateShaderResourceViewFromFile(pDevice, initInfo.LayerMapFileName0.c_str(), 0, 0, &mLayer0, 0));
  225.     HR(D3DX11CreateShaderResourceViewFromFile(pDevice, initInfo.LayerMapFileName1.c_str(), 0, 0, &mLayer1, 0));
  226.     HR(D3DX11CreateShaderResourceViewFromFile(pDevice, initInfo.LayerMapFileName2.c_str(), 0, 0, &mLayer2, 0));
  227.     HR(D3DX11CreateShaderResourceViewFromFile(pDevice, initInfo.LayerMapFileName3.c_str(), 0, 0, &mLayer3, 0));
  228.     HR(D3DX11CreateShaderResourceViewFromFile(pDevice, initInfo.LayerMapFileName4.c_str(), 0, 0, &mLayer4, 0));
  229.     HR(D3DX11CreateShaderResourceViewFromFile(pDevice, initInfo.BlendMapFileName.c_str(), 0, 0, &mBlendMap, 0));
  230. }
  231.  
  232. void Terrain::SetDirectionToSun(const XMFLOAT3& v)
  233. {
  234.     XMVECTOR temp = XMVectorSet(v.x, v.y, v.z, 0.0f);
  235.     mfxDirToSunVar->SetFloatVector((float*)&temp);
  236. }
  237.  
  238. void Terrain::draw(CXMMATRIX world, Camera& cam)
  239. {
  240.     pDeviceContext->IASetInputLayout(InputLayouts::Basic32);
  241.  
  242.     UINT stride = sizeof(Vertex::Basic32);
  243.     UINT offset = 0;
  244.  
  245.    
  246.     for (int a = 0; a < mSubMesh.size(); a++) {
  247.         pDeviceContext->IASetVertexBuffers(0, mSubMesh.size(), &mSubMesh[a].VertexBuffer, &stride, &offset);
  248.         pDeviceContext->IASetIndexBuffer(mSubMesh[a].IndexBuffer, DXGI_FORMAT_R32_UINT, 0);
  249.    
  250.  
  251.     XMMATRIX view = cam.View();
  252.     XMMATRIX proj = cam.Proj();
  253.  
  254.     XMMATRIX WVP = world * view * proj;
  255.  
  256.  
  257.     mfxWVPVar->SetMatrix((float*)&WVP);
  258.     mfxWorldVar->SetMatrix((float*)&world);
  259.  
  260.     mfxLayer0Var->SetResource(mLayer0);
  261.     mfxLayer1Var->SetResource(mLayer1);
  262.     mfxLayer2Var->SetResource(mLayer2);
  263.     mfxLayer3Var->SetResource(mLayer3);
  264.     mfxLayer4Var->SetResource(mLayer4);
  265.     mfxBlendMapVar->SetResource(mBlendMap);
  266.  
  267.     D3DX11_TECHNIQUE_DESC techDesc;
  268.     mTech->GetDesc(&techDesc);
  269.  
  270.     for(UINT i = 0; i < techDesc.Passes; ++i)
  271.     {
  272.         ID3DX11EffectPass* pass = mTech->GetPassByIndex(i);
  273.         pass->Apply(0, pDeviceContext);
  274.  
  275.         pDeviceContext->DrawIndexed(mSubMesh[a].mesh.Indices.size(), 0, 0);
  276.     }  
  277.  
  278.     }
  279. }
  280.  
  281. void Terrain::loadHeightMap()
  282. {
  283.     // A height for each vertex
  284.     std::vector<unsigned char> in( mInfo.NumRows * mInfo.NumCols );
  285.  
  286.     // Open the file.
  287.     std::ifstream inFile;
  288.     inFile.open(mInfo.HeightmapFileName.c_str(), std::ios_base::binary);
  289.  
  290.     if(inFile)
  291.     {
  292.         // Read the RAW bytes.
  293.         inFile.read((char*)&in[0], (std::streamsize)in.size());
  294.  
  295.         // Done with file.
  296.         inFile.close();
  297.     }
  298.  
  299.     // Copy the array data into a float array, and scale and offset the heights.
  300.     mHeightmap.resize(mInfo.NumRows * mInfo.NumCols, 0);
  301.     for(UINT i = 0; i < mInfo.NumRows * mInfo.NumCols; ++i)
  302.     {
  303.         mHeightmap[i] = (float)in[i] * mInfo.HeightScale + mInfo.HeightOffset;
  304.     }
  305. }
  306.  
  307.  
  308. void Terrain::Smooth()
  309. {
  310.     std::vector<float> dest( mHeightmap.size() );
  311.  
  312.     for(UINT i = 0; i < mInfo.NumRows; ++i)
  313.     {
  314.         for(UINT j = 0; j < mInfo.NumCols; ++j)
  315.         {
  316.             dest[i*mInfo.NumCols+j] = average(i,j);
  317.         }
  318.     }
  319.  
  320.     // Replace the old heightmap with the filtered one.
  321.     mHeightmap = dest;
  322. }
  323.  
  324.  
  325.  
  326. bool Terrain::inBounds(UINT i, UINT j)
  327. {
  328.     // True if ij are valid indices; false otherwise.
  329.     return
  330.         i >= 0 && i < mInfo.NumRows &&
  331.         j >= 0 && j < mInfo.NumCols;
  332. }
  333.  
  334. float Terrain::average(UINT i, UINT j)
  335. {
  336.     // Function computes the average height of the ij element.
  337.     // It averages itself with its eight neighbor pixels.  Note
  338.     // that if a pixel is missing neighbor, we just don't include it
  339.     // in the average--that is, edge pixels don't have a neighbor pixel.
  340.     //
  341.     // ----------
  342.     // | 1| 2| 3|
  343.     // ----------
  344.     // |4 |ij| 6|
  345.     // ----------
  346.     // | 7| 8| 9|
  347.     // ----------
  348.  
  349.     float avg = 0.0f;
  350.     float num = 0.0f;
  351.  
  352.     for(UINT m = i-1; m <= i+1; ++m)
  353.     {
  354.         for(UINT n = j-1; n <= j+1; ++n)
  355.         {
  356.             if( inBounds(m,n) )
  357.             {
  358.                 avg += mHeightmap[m*mInfo.NumCols + n];
  359.                 num += 1.0f;
  360.             }
  361.         }
  362.     }
  363.  
  364.     return avg / num;
  365. }
  366.  
  367.    
  368.  
  369. void Terrain::BuildGeometry()
  370. {
  371.     //=========================================
  372.     //               Build Vertices
  373.     //=========================================
  374.  
  375.     std::vector<Vertex::Basic32> vertices(mNumVertices);
  376.  
  377.  
  378.     float halfWidth = (mInfo.NumCols-1)*mInfo.CellSpacing*0.5f;
  379.     float halfDepth = (mInfo.NumRows-1)*mInfo.CellSpacing*0.5f;
  380.  
  381.     float du = 1.0f / (mInfo.NumCols-1);
  382.     float dv = 1.0f / (mInfo.NumRows-1);
  383.     for(UINT i = 0; i < mInfo.NumRows; ++i)
  384.     {
  385.         float z = halfDepth - i*mInfo.CellSpacing;
  386.         for(UINT j = 0; j < mInfo.NumCols; ++j)
  387.         {
  388.             float x = -halfWidth + j*mInfo.CellSpacing;
  389.  
  390.             float y = mHeightmap[i*mInfo.NumCols+j];
  391.             vertices[i*mInfo.NumCols+j].Pos    = XMFLOAT3(x, y, z);
  392.             vertices[i*mInfo.NumCols+j].Normal = XMFLOAT3(0.0f, 1.0f, 0.0f);
  393.  
  394.             // Stretch texture over grid.
  395.             vertices[i*mInfo.NumCols+j].Tex.x = j*du;
  396.             vertices[i*mInfo.NumCols+j].Tex.y = i*dv;
  397.         }
  398.     }
  399.  
  400.     // Estimate normals for interior nodes using central difference.
  401.     float invTwoDX = 1.0f / (2.0f*mInfo.CellSpacing);
  402.     float invTwoDZ = 1.0f / (2.0f*mInfo.CellSpacing);
  403.     for(UINT i = 2; i < mInfo.NumRows-1; ++i)
  404.     {
  405.         for(UINT j = 2; j < mInfo.NumCols-1; ++j)
  406.         {
  407.             float t = mHeightmap[(i-1)*mInfo.NumCols + j];
  408.             float b = mHeightmap[(i+1)*mInfo.NumCols + j];
  409.             float l = mHeightmap[i*mInfo.NumCols + j - 1];
  410.             float r = mHeightmap[i*mInfo.NumCols + j + 1];
  411.  
  412.             XMFLOAT3 tanZ(0.0f, (t-b)*invTwoDZ, 1.0f);
  413.             XMFLOAT3 tanX(1.0f, (r-l)*invTwoDX, 0.0f);
  414.  
  415.             XMVECTOR N;
  416.             N = XMVector3Cross(XMLoadFloat3(&tanZ), XMLoadFloat3(&tanX));
  417.             N = XMVector3Normalize(N);
  418.    
  419.             XMStoreFloat3(&vertices[i*mInfo.NumCols+j].Normal, N);
  420.  
  421.         }
  422.     }
  423.  
  424.     //=======================================================
  425.     //                     Build Indices
  426.     //=======================================================
  427.  
  428.         std::vector<DWORD> indices(mNumFaces*3); // 3 indices per face
  429.  
  430.     // Iterate over each quad and compute indices.
  431.     int k = 0;
  432.     for(UINT i = 0; i < mInfo.NumRows-1; ++i)
  433.     {
  434.         for(UINT j = 0; j < mInfo.NumCols-1; ++j)
  435.         {
  436.             indices[k]   = i*mInfo.NumCols+j;
  437.             indices[k+1] = i*mInfo.NumCols+j+1;
  438.             indices[k+2] = (i+1)*mInfo.NumCols+j;
  439.  
  440.             indices[k+3] = (i+1)*mInfo.NumCols+j;
  441.             indices[k+4] = i*mInfo.NumCols+j+1;
  442.             indices[k+5] = (i+1)*mInfo.NumCols+j+1;
  443.  
  444.             k += 6; // next quad
  445.         }
  446.     }
  447.  
  448.  
  449.     int subGridRows = (mInfo.NumRows-1) / (SubMesh::NUM_ROWS-1);
  450.     int subGridCols = (mInfo.NumCols-1) / (SubMesh::NUM_COLS-1);
  451.  
  452.     for(int r = 0; r < subGridRows; ++r)
  453.     {
  454.         for(int c = 0; c < subGridCols; ++c)
  455.         {
  456.             // Rectangle that indicates (via matrix indices ij) the
  457.             // portion of grid vertices to use for this subgrid.
  458.             RECT R =
  459.             {
  460.                     c * (SubMesh::NUM_COLS - 1),
  461.                     r * (SubMesh::NUM_ROWS - 1),
  462.                 (c+1) * (SubMesh::NUM_COLS - 1),
  463.                 (r+1) * (SubMesh::NUM_ROWS - 1)
  464.             };
  465.  
  466.             BuildSubMesh(R, &vertices[0]);
  467.         }
  468.     }
  469.  
  470.    
  471.   /*  D3D11_BUFFER_DESC vbd;
  472.     vbd.Usage = D3D11_USAGE_IMMUTABLE;
  473.     vbd.ByteWidth = sizeof(Vertex::Basic32) * mNumVertices;
  474.     vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
  475.     vbd.CPUAccessFlags = 0;
  476.     vbd.MiscFlags = 0;
  477.  
  478.     D3D11_SUBRESOURCE_DATA vinitData;
  479.     vinitData.pSysMem = &vertices[0];
  480.  
  481.     HR(pDevice->CreateBuffer(&vbd, &vinitData, &mVB));
  482.  
  483.  
  484.  
  485.     D3D11_BUFFER_DESC ibd;
  486.     ibd.Usage = D3D11_USAGE_IMMUTABLE;
  487.     ibd.ByteWidth = sizeof(DWORD) * mNumFaces * 3;
  488.     ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
  489.     ibd.CPUAccessFlags = 0;
  490.     ibd.MiscFlags = 0;
  491.  
  492.     D3D11_SUBRESOURCE_DATA iinitData;
  493.     iinitData.pSysMem = &indices[0];
  494.  
  495.     HR(pDevice->CreateBuffer(&ibd, &iinitData, &mIB));
  496.     */
  497. }
  498.  
  499. void Terrain::BuildSubMesh(RECT &R, Vertex::Basic32* vertex)
  500. {
  501.     GeometryGenerator::MeshData data;
  502.     GeometryGenerator::MeshData subMesh;
  503.     GeometryGenerator gen;
  504.  
  505.     gen.CreateGrid(SubMesh::NUM_ROWS, SubMesh::NUM_COLS, SubMesh::NUM_ROWS / mInfo.CellSpacing, SubMesh::NUM_COLS / mInfo.CellSpacing, data);
  506.    
  507.     int k = 0;
  508.     for(int i = R.top; i <= R.bottom; ++i)
  509.     {
  510.         for(int j = R.left; j <= R.right; ++j)
  511.         {
  512.             subMesh.Vertices[k++].Position = vertex[i*mInfo.NumCols+j].Pos;
  513.             subMesh.Vertices[k++].Normal = vertex[i*mInfo.NumCols+j].Normal;
  514.             subMesh.Vertices[k++].TexC = vertex[i*mInfo.NumCols+j].Tex;
  515.         }
  516.     }
  517.  
  518.     XNA::AxisAlignedBox box;
  519.     XNA::ComputeBoundingAxisAlignedBoxFromPoints(&box, subMesh.Vertices.size(), &subMesh.Vertices[0].Position, sizeof(Vertex::Basic32));
  520.  
  521.     for(int i = 0; i < SubMesh::NUM_TRIS; ++i)
  522.     {
  523.         subMesh.Indices[i*3+0] = (WORD)data.Indices[i*3+0];
  524.         subMesh.Indices[i*3+1] = (WORD)data.Indices[i*3+1];
  525.         subMesh.Indices[i*3+2] = (WORD)data.Indices[i*3+2];
  526.  
  527.     }
  528.  
  529.     SubMesh m;
  530.  
  531.     m.box = box;
  532.     m.mesh = subMesh;
  533.  
  534.     ID3D11Buffer* vbuffer, *ibuffer;
  535.  
  536.     D3D11_BUFFER_DESC vbd;
  537.     vbd.Usage = D3D11_USAGE_IMMUTABLE;
  538.     vbd.ByteWidth = sizeof(Vertex::Basic32) * subMesh.Vertices.size();
  539.     vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
  540.     vbd.CPUAccessFlags = 0;
  541.     vbd.MiscFlags = 0;
  542.  
  543.     D3D11_SUBRESOURCE_DATA vinitData;
  544.     vinitData.pSysMem = &subMesh.Vertices[0];
  545.  
  546.     HR(pDevice->CreateBuffer(&vbd, &vinitData, &vbuffer));
  547.  
  548.  
  549.  
  550.     D3D11_BUFFER_DESC ibd;
  551.     ibd.Usage = D3D11_USAGE_IMMUTABLE;
  552.     ibd.ByteWidth = sizeof(DWORD) * mNumFaces * 3;
  553.     ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
  554.     ibd.CPUAccessFlags = 0;
  555.     ibd.MiscFlags = 0;
  556.  
  557.     D3D11_SUBRESOURCE_DATA iinitData;
  558.     iinitData.pSysMem = &subMesh.Indices[0];
  559.  
  560.     HR(pDevice->CreateBuffer(&ibd, &iinitData, &ibuffer));
  561.  
  562.     m.VertexBuffer = vbuffer;
  563.     m.IndexBuffer = ibuffer;
  564.  
  565.     mSubMesh.push_back(m);
  566. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement