📄 load3ds.cpp
字号:
/////
///// 读取一个块的ID 和长度(字节)
/////
///////////////////////////////// READ CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadChunk(CLoad3DS::tChunk *pChunk)
{
// This reads the chunk ID which is 2 bytes.
// The chunk ID is like OBJECT or MATERIAL. It tells what data is
// able to be read in within the chunks section.
pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);
// Then, we read the length of the chunk which is 4 bytes.
// This is how we know how much to read in, or read past.
pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);
}
///////////////////////////////// GET STRING \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// 读取字符串
/////
///////////////////////////////// GET STRING \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
int CLoad3DS::GetString(char *pBuffer)
{
int index = 0;
// Read 1 byte of data which is the first letter of the string
fread(pBuffer, 1, 1, m_FilePointer);
// Loop until we get NULL
while (*(pBuffer + index++) != 0) {
// Read in a character at a time until we hit NULL.
fread(pBuffer + index, 1, 1, m_FilePointer);
}
// Return the string length, which is how many bytes we read in (including the NULL)
return strlen(pBuffer) + 1;
}
///////////////////////////////// READ COLOR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// 读取颜色数据,改造了,添加了一个标志
/////
///////////////////////////////// READ COLOR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadColorChunk(CLoad3DS::tMaterialInfo *pMaterial, CLoad3DS::tChunk *pChunk,USHORT typeFlag)
{
tChunk tempChunk = {0};
BYTE btmp[3];
// Read the material color chunk info
ReadChunk(&tempChunk);
switch(typeFlag)
{
case MAT_AMBIENT:
tempChunk.bytesRead += fread(btmp, 1, tempChunk.length - tempChunk.bytesRead, m_FilePointer);
Bytes2Floats(btmp,pMaterial->ambient,3,1.0f/256.0f);
pMaterial->ambient[3]=1.0f;
break;
case MAT_SPECULAR:
tempChunk.bytesRead += fread(btmp, 1, tempChunk.length - tempChunk.bytesRead, m_FilePointer);
Bytes2Floats(btmp,pMaterial->specular,3,1.0f/256.0f);
pMaterial->specular[3]=1.0f;
break;
case MAT_EMISSIVE:
tempChunk.bytesRead += fread(btmp, 1, tempChunk.length - tempChunk.bytesRead, m_FilePointer);
//-----------------(为了好看,全为0,0,0;如果是自发光物体,可以去掉memset)
memset(btmp,0,3);
//-----------------
Bytes2Floats(btmp,pMaterial->emissive,3,1.0f/256.0f);
pMaterial->emissive[3]=1.0f;
break;
case MATDIFFUSE:
default:
// Read in the R G B color (3 bytes - 0 through 255)
tempChunk.bytesRead += fread(pMaterial->color, 1, tempChunk.length - tempChunk.bytesRead, m_FilePointer);
Bytes2Floats(pMaterial->color,pMaterial->diffuse,3,1.0f/256.0f);
pMaterial->diffuse[3]=1.0f;
break;
}
// Add the bytes read to our chunk
pChunk->bytesRead += tempChunk.bytesRead;
}
///////////////////////////////// READ VERTEX INDECES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// 读取端点索引(构成三角面的点序号)
/////
///////////////////////////////// READ VERTEX INDECES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadVertexIndices(CLoad3DS::t3DObject *pObject, CLoad3DS::tChunk *pPreviousChunk)
{
unsigned short index = 0; // This is used to read in the current face index
// In order to read in the vertex indices for the object, we need to first
// read in the number of them, then read them in. Remember,
// we only want 3 of the 4 values read in for each face. The fourth is
// a visibility flag for 3D Studio Max that doesn't mean anything to us.
// Read in the number of faces that are in this object (int)
pPreviousChunk->bytesRead += fread(&pObject->numOfFaces, 1, 2, m_FilePointer);
// Alloc enough memory for the faces and initialize the structure
pObject->pFaces = new tFace [pObject->numOfFaces];
memset(pObject->pFaces, 0, sizeof(tFace) * pObject->numOfFaces);
// Go through all of the faces in this object
for(int i = 0; i < pObject->numOfFaces; i++)
{
// Next, we read in the A then B then C index for the face, but ignore the 4th value.
// The fourth value is a visibility flag for 3D Studio Max, we don't care about this.
for(int j = 0; j < 4; j++)
{
// Read the first vertice index for the current face
pPreviousChunk->bytesRead += fread(&index, 1, sizeof(index), m_FilePointer);
if(j < 3)
{
// Store the index in our face structure.
pObject->pFaces[i].vertIndex[j] = index;
}
}
}
}
///////////////////////////////// READ UV COORDINATES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// 读取纹理坐标
/////
///////////////////////////////// READ UV COORDINATES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadUVCoordinates(t3DObject *pObject, tChunk *pPreviousChunk)
{
// In order to read in the UV indices for the object, we need to first
// read in the amount there are, then read them in.
// Read in the number of UV coordinates there are (int)
pPreviousChunk->bytesRead += fread(&pObject->numTexVertex, 1, 2, m_FilePointer);
// Allocate memory to hold the UV coordinates
pObject->pTexVerts = new CVector2 [pObject->numTexVertex];
// Read in the texture coodinates (an array 2 float)
pPreviousChunk->bytesRead += fread(pObject->pTexVerts, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
}
///////////////////////////////// READ VERTICES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// 读取端点
/////
///////////////////////////////// READ VERTICES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadVertices(CLoad3DS::t3DObject *pObject, CLoad3DS::tChunk *pPreviousChunk)
{
// Like most chunks, before we read in the actual vertices, we need
// to find out how many there are to read in. Once we have that number
// we then fread() them into our vertice array.
// Read in the number of vertices (int)
pPreviousChunk->bytesRead += fread(&(pObject->numOfVerts), 1, 2, m_FilePointer);
// Allocate the memory for the verts and initialize the structure
pObject->pVerts = new CVector3 [pObject->numOfVerts];
memset(pObject->pVerts, 0, sizeof(CVector3) * pObject->numOfVerts);
// Read in the array of vertices (an array of 3 floats)
pPreviousChunk->bytesRead += fread(pObject->pVerts, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
// Now we should have all of the vertices read in. Because 3D Studio Max
// Models with the Z-Axis pointing up (strange and ugly I know!), we need
// to flip the y values with the z values in our vertices. That way it
// will be normal, with Y pointing up. If you prefer to work with Z pointing
// up, then just delete this next loop. Also, because we swap the Y and Z
// we need to negate the Z to make it come out correctly.
// Go through all of the vertices that we just read and swap the Y and Z values
for(int i = 0; i < pObject->numOfVerts; i++)
{
// Store off the Y value
float fTempY = pObject->pVerts[i].y;
// Set the Y value to the Z value
pObject->pVerts[i].y = pObject->pVerts[i].z;
// Set the Z value to the Y value,
// but negative Z because 3D Studio max does the opposite.
pObject->pVerts[i].z = -fTempY;
}
}
///////////////////////////////// READ OBJECT MATERIAL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// 读取材料名称,设置材料的ID (改动比较大)
/////
///////////////////////////////// READ OBJECT MATERIAL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadObjectMaterial(CLoad3DS::t3DModel *pModel,CLoad3DS::t3DObject *pObject, CLoad3DS::tChunk *pPreviousChunk,vector<tMatREF*> *pvmatids)
{
char strMaterial[255] = {0}; // This is used to hold the objects material name
bool bmaterror=true;
tMatREF *pMatref;
pMatref=new tMatREF;
// *What is a material?* - A material is either the color or the texture map of the object.
// It can also hold other information like the brightness, shine, etc... Stuff we don't
// really care about. We just want the color, or the texture map file name really.
// Here we read the material name that is assigned to the current object.
// strMaterial should now have a string of the material name, like "Material #2" etc..
pPreviousChunk->bytesRead += GetString(strMaterial);
// Now that we have a material name, we need to go through all of the materials
// and check the name against each material. When we find a material in our material
// list that matches this name we just read in, then we assign the materialID
// of the object to that material index. You will notice that we passed in the
// model to this function. This is because we need the number of textures.
// Yes though, we could have just passed in the model and not the object too.
// Go through all of the textures
for(int i = 0; i < pModel->numOfMaterials; i++)
{
// If the material we just read in matches the current texture name
if(strcmp(strMaterial, pModel->vctMaterials[i].strName) == 0)
{
// Set the material ID to the current index 'i' and stop checking
pObject->materialID = i;
pMatref->nMaterialID=i;
// Now that we found the material, check if it's a texture map.
// If the strFile has a string length of 1 and over it's a texture
if(strlen(pModel->vctMaterials[i].strFile) > 0) {
// Set the object's flag to say it has a texture map to bind.
pObject->bHasTexture = true;
pMatref->bHasTexture=true;
}
bmaterror=false;
break;
}
else
{
// Set the ID to -1 to show there is no material for this object
pObject->materialID = -1;
pMatref->nMaterialID=-1;
bmaterror=true;
}
}
//--------------------------------------------------------------
//下面语句,读入了该材料相关的面索引号
pPreviousChunk->bytesRead += fread(gBuffer, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
if(!bmaterror)
{
pMatref->nFaceNum=gBuffer[0]&0x0000FFFF;
pMatref->pFaceIndexs=new USHORT[pMatref->nFaceNum];
memcpy(pMatref->pFaceIndexs,2+(BYTE*)gBuffer,pMatref->nFaceNum*sizeof(USHORT));
//保存材料号
pvmatids->push_back(pMatref);
}
//--------------------------------------------------------------
}
///////////////////////////////// COMPUTER NORMALS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// 计算各端点法线
/////
///////////////////////////////// COMPUTER NORMALS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ComputeNormals(CLoad3DS::t3DModel *pModel)
{
CVector3 vVector1, vVector2, vNormal, vPoly[3];
// If there are no objects, we can skip this part
if(pModel->numOfObjects <= 0)
return;
// What are vertex normals? And how are they different from other normals?
// Well, if you find the normal to a triangle, you are finding a "Face Normal".
// If you give OpenGL a face normal for lighting, it will make your object look
// really flat and not very round. If we find the normal for each vertex, it makes
// the smooth lighting look. This also covers up blocky looking objects and they appear
// to have more polygons than they do. Basically, what you do is first
// calculate the face normals, then you take the average of all the normals around each
// vertex. It's just averaging. That way you get a better approximation for that vertex.
// Go through each of the objects to calculate their normals
for(int index = 0; index < pModel->numOfObjects; index++)
{
// Get the current object
t3DObject *pObject = &(pModel->vctObjects[index]);
// Here we allocate all the memory we need to calculate the normals
CVector3 *pNormals = new CVector3 [pObject->numOfFaces];
CVector3 *pTempNormals = new CVector3 [pObject->numOfFaces];
pObject->pNormals = new CVector3 [pObject->numOfVerts];
// Go though all of the faces of this object
for(int i=0; i < pObject->numOfFaces; i++)
{
// To cut down LARGE code, we extract the 3 points of this face
vPoly[0] = pObject->pVerts[pObject->pFaces[i].vertIndex[0]];
vPoly[1] = pObject->pVerts[pObject->pFaces[i].vertIndex[1]];
vPoly[2] = pObject->pVerts[pObject->pFaces[i].vertIndex[2]];
// Now let's calculate the face normals (Get 2 vectors and find the cross product of those 2)
vVector1 = Vector(vPoly[0], vPoly[2]); // Get the vector of the polygon (we just need 2 sides for the normal)
vVector2 = Vector(vPoly[2], vPoly[1]); // Get a second vector of the polygon
vNormal = Cross(vVector1, vVector2); // Return the cross product of the 2 vectors (normalize vector, but not a unit vector)
pTempNormals[i] = vNormal; // Save the un-normalized normal for the vertex normals
vNormal = Normalize(vNormal); // Normalize the cross product to give us the polygons normal
pNormals[i] = vNormal; // Assign the normal to the list of normals
}
//////////////// Now Get The Vertex Normals /////////////////
CVector3 vSum (0.0, 0.0, 0.0);
CVector3 vZero = vSum;
int shared=0;
for (i = 0; i < pObject->numOfVerts; i++) // Go through all of the vertices
{
for (int j = 0; j < pObject->numOfFaces; j++) // Go through all of the triangles
{ // Check if the vertex is shared by another face
if (pObject->pFaces[j].vertIndex[0] == i ||
pObject->pFaces[j].vertIndex[1] == i ||
pObject->pFaces[j].vertIndex[2] == i)
{
vSum =vSum+pTempNormals[j];// AddVector(vSum, pTempNormals[j]);// Add the un-normalized normal of the shared face
shared++; // Increase the number of shared triangles
}
}
// Get the normal by dividing the sum by the shared. We negate the shared so it has the normals pointing out.
pObject->pNormals[i] = vSum/float(-shared);// .DivideVectorByScaler(vSum, float(-shared));
// Normalize the normal for the final vertex normal
pObject->pNormals[i] = Normalize(pObject->pNormals[i]);
vSum = vZero; // Reset the sum
shared = 0; // Reset the shared
}
// Free our memory and start over on the next object
delete [] pTempNormals;
delete [] pNormals;
}
}
//字节数组到浮点转化
void CLoad3DS::Bytes2Floats(BYTE *pbs, float *pfs, int num, float fsk)
{
if(num==0||num>100)return;
for(int i=0;i<num;i++)
{
pfs[i]=float(pbs[i])*fsk;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -