void fillScanLine(Vertex vertex1, Vertex vertex2) { // Fills a scanline structure with information about the triangle on this y scanline. if (vertex1.y > vertex2.y) { fillScanLine(vertex2, vertex1); return; } // We need to go from low to high so switch if the other one is higher if (vertex2.y < 0 || vertex1.y >= AIMESH_TEXTURE_SIZE) return; // There's nothing on this line var deltaPos = new Vertex(); deltaPos.x = vertex2.x - vertex1.x; deltaPos.y = vertex2.y - vertex1.y; deltaPos.z = vertex2.z - vertex1.z; float tempWidth = AIMESH_TEXTURE_SIZE; float tempHeight = AIMESH_TEXTURE_SIZE; float t_DYResp = deltaPos.y == 0 ? 0 : 1.0f / deltaPos.y; int startY = (int)vertex1.y, endY = (int)vertex2.y; float x = vertex1.x; float z = vertex1.z; float deltaX = deltaPos.x * t_DYResp; float deltaZ = deltaPos.z * t_DYResp; float t_Inc = 1.0f - FRACPOS(vertex1.y); // subpixel correction startY++; x += deltaX * t_Inc; z += deltaZ * t_Inc; if (startY < 0) { x -= deltaX * startY; z -= deltaZ * startY; startY = 0; } // Small fix if (endY >= AIMESH_TEXTURE_SIZE) endY = AIMESH_TEXTURE_SIZE - 1; // For each scanline that this triangle uses for (int y = startY; y <= endY; y++) { if (x < scanlineLowest[y].x) // If the x is lower than our lowest x { scanlineLowest[y].x = (x); // Fill the scanline struct with our info scanlineLowest[y].y = (float)y; scanlineLowest[y].z = z; } if (x > scanlineHighest[y].x) // If the x is higher than our highest x { scanlineHighest[y].x = (x); // Fill the scanline struct with our info scanlineHighest[y].y = (float)y; scanlineHighest[y].z = z; } // Interpolate // Or going to the part of the triangle on the next scanline x += deltaX; z += deltaZ; } }
void drawTriangle(Triangle triangle, int width, int height) { // The heart of the rasterizer Vertex[] tempVertex = new Vertex[3]; tempVertex[0] = new Vertex(); tempVertex[0].x = triangle.Face.v1[0]; tempVertex[0].y = triangle.Face.v1[2]; tempVertex[0].z = triangle.Face.v1[1]; tempVertex[1] = new Vertex(); tempVertex[1].x = triangle.Face.v2[0]; tempVertex[1].y = triangle.Face.v2[2]; tempVertex[1].z = triangle.Face.v2[1]; tempVertex[2] = new Vertex(); tempVertex[2].x = triangle.Face.v3[0]; tempVertex[2].y = triangle.Face.v3[2]; tempVertex[2].z = triangle.Face.v3[1]; fillScanLine(tempVertex[0], tempVertex[1]); fillScanLine(tempVertex[1], tempVertex[2]); fillScanLine(tempVertex[2], tempVertex[0]); float tempWidth = width; float tempHeight = height; // target width and width int startY = (int)Math.Min(tempVertex[0].y, Math.Min(tempVertex[1].y, tempVertex[2].y)); int endY = (int)Math.Max(tempVertex[0].y, Math.Max(tempVertex[1].y, tempVertex[2].y)); // Get the scanline where we start drawing and where we stop. endY = Math.Min(endY, height - 1); startY = Math.Max(0, startY); for (int y = startY; y <= endY; y++) // for each scanline { if (scanlineLowest[y].x < scanlineHighest[y].x) // If we actually have something filled in this scanline { int yw = y * height; float z = scanlineLowest[y].z; float u = scanlineLowest[y].u; float v = scanlineLowest[y].v; // The start of the Z, U, and V coordinate. float deltaX = 1.0f / (scanlineHighest[y].x - scanlineLowest[y].x); // Interpolation over X (change in X between the two, then reverse it so it's usable as multiplication // in divisions float deltaZ = (scanlineHighest[y].z - scanlineLowest[y].z) * deltaX; float deltaU = (scanlineHighest[y].u - scanlineLowest[y].u) * deltaX; float deltaV = (scanlineHighest[y].v - scanlineLowest[y].v) * deltaX; // The interpolation in Z, U and V in respect to the interpolation of X // Sub-texel correction int x = (int)scanlineLowest[y].x; int tx = x + 1; int distInt = (int)(scanlineHighest[y].x) - (int)(scanlineLowest[y].x); if (distInt > 0.0f) { u += (((float)(tx)) - tx) * deltaU; v += (((float)(tx)) - tx) * deltaV; z += (((float)(tx)) - tx) * deltaZ; } if (!(scanlineHighest[y].x < 0 || x >= height)) for (int i = 0; x < (int)scanlineHighest[y].x; i++, x++) // for each piece of the scanline { if (x >= height) break; // If we're out of screen, break out this loop if (x < 0) { int inverseX = Math.Abs(x); z += deltaZ * inverseX; u += deltaU * inverseX; v += deltaV * inverseX; x = 0; } { // Get the point on the texture that we need to draw. It basically picks a pixel based on the uv. //a_Target->GetRenderTarget()->Plot(x, tempHeight - y, 255); heightMap[x + (y) * height] = z; xMap[x + (y) * height] = scanlineLowest[y].x + deltaX * i; yMap[x + (y) * height] = scanlineLowest[y].y; if (z < lowestZ) lowestZ = z; } z += deltaZ; u += deltaU; v += deltaV; // interpolate } } scanlineLowest[y].x = 1e10f; scanlineHighest[y].x = -1e10f; } }