void Builder_MakeChunk(ChunkInfo* info) { Int32 x = info->CentreX - 8, y = info->CentreY - 8, z = info->CentreZ - 8; bool allAir = false, hasMesh; hasMesh = Builder_BuildChunk(x, y, z, &allAir); info->AllAir = allAir; if (!hasMesh) return; Int32 totalVerts = Builder_TotalVerticesCount(); if (totalVerts == 0) return; #if !CC_BUILD_GL11 /* add an extra element to fix crashing on some GPUs */ info->Vb = Gfx_CreateVb(Builder_Vertices, VERTEX_FORMAT_P3FT2FC4B, totalVerts + 1); #endif Int32 i, offset = 0, partsIndex = MapRenderer_Pack(x >> CHUNK_SHIFT, y >> CHUNK_SHIFT, z >> CHUNK_SHIFT); bool hasNormal = false, hasTranslucent = false; for (i = 0; i < MapRenderer_1DUsedCount; i++) { Int32 j = i + ATLAS1D_MAX_ATLASES; Int32 curIdx = partsIndex + i * MapRenderer_ChunksCount; Builder_SetPartInfo(&Builder_Parts[i], &offset, &MapRenderer_PartsNormal[curIdx], &hasNormal); Builder_SetPartInfo(&Builder_Parts[j], &offset, &MapRenderer_PartsTranslucent[curIdx], &hasTranslucent); } if (hasNormal) { info->NormalParts = &MapRenderer_PartsNormal[partsIndex]; } if (hasTranslucent) { info->TranslucentParts = &MapRenderer_PartsTranslucent[partsIndex]; } #if OCCLUSION if (info.NormalParts != null || info.TranslucentParts != null) info.occlusionFlags = (UInt8)ComputeOcclusion(); #endif }
//================================================================ //================================================================ bool __cdecl RunTask(IAgent* agent, DWORD sessionId, IGenericStream* inStream, IGenericStream* outStream) { EnterCriticalSection(&CS); DWORD dataKey = sessionId; //uncomment to allow each thread from pool to have separate global data //dataKey |= GetCurrentThreadId(); TSessionDataCache::iterator i = sessionDataCache.find(dataKey); if (i==sessionDataCache.end()) { sessionDataCache.insert(std::make_pair(dataKey,TSessionData())); //----------- read global data ------------- IGenericStream* globalDataStream; HRESULT rz = agent->GetData(sessionId, dataDesc, &globalDataStream); if (rz!=S_OK) { LeaveCriticalSection(&CS); return false; } i = sessionDataCache.find(dataKey); assert(i!=sessionDataCache.end()); TSessionData* sd = &i->second; sd->octree = new AtiOctree(globalDataStream); globalDataStream->Read(&sd->highNumTris,sizeof(sd->highNumTris)); sd->highTris = new NmRawTriangle[sd->highNumTris]; globalDataStream->Read(sd->highTris,sd->highNumTris*sizeof(NmRawTriangle)); globalDataStream->Read(&sd->gNumSamples,sizeof(sd->gNumSamples)); sd->gSamples = new NmSample[sd->gNumSamples]; globalDataStream->Read(sd->gSamples,sd->gNumSamples*sizeof(NmSample)); globalDataStream->Read(&sd->hTangentSpace,sizeof(sd->hTangentSpace)); if (sd->hTangentSpace!=NULL) { sd->hTangentSpace = new NmTangentMatrix[sd->highNumTris]; globalDataStream->Read(sd->hTangentSpace,sd->highNumTris*sizeof(NmTangentMatrix)); } globalDataStream->Read(&sd->bumpWidth,sizeof(sd->bumpWidth)); globalDataStream->Read(&sd->bumpHeight,sizeof(sd->bumpHeight)); globalDataStream->Read(&sd->bumpMap,sizeof(sd->bumpMap)); if (sd->bumpMap!=NULL) { sd->bumpMap = new float[sd->bumpWidth*sd->bumpHeight*3]; globalDataStream->Read(sd->bumpMap,sd->bumpWidth*sd->bumpHeight*3*sizeof(float)); } globalDataStream->Read(&sd->numRays,sizeof(sd->numRays)); if (sd->numRays!=0) { sd->rays = new NmRawPointD[sd->numRays]; globalDataStream->Read(sd->rays,sd->numRays*sizeof(NmRawPointD)); sd->rayWeights = new double[sd->numRays]; globalDataStream->Read(sd->rayWeights,sd->numRays*sizeof(double)); } { globalDataStream->AddRef(); DWORD d = globalDataStream->Release(); } globalDataStream->Release(); agent->FreeCachedData(sessionId, dataDesc); } TSessionData* sd = &i->second; LeaveCriticalSection(&CS); //------------------------------------------ float* img; float* img2; int minX; int pad; int maxX; int gWidth; int y; double x1,x2,x3,y1,y2,y3; double b0; bool gInTangentSpace; NmTangentMatrix tangentSpace_l; NmRawTriangle lTri; bool gOcclusion; bool gBentNormal; int numComponents; int gOcclIdx; bool gDisplacements; int gDispIdx; bool gEdgeCopy; int gNormalRules; double gMaxAngle; double gDistance; double gEpsilon; int gMaxCells = 0; AtiOctreeCell** gCell = NULL; DWORD intag; bool bFirstPass=true; int l; inStream->Read(&intag, sizeof(DWORD)); do { inStream->Read(&img, sizeof(float*)); inStream->Read(&img2, sizeof(float*)); if (bFirstPass==true) { outStream->Write(&img, sizeof(float*)); outStream->Write(&img2, sizeof(float*)); } inStream->Read(&minX, sizeof(minX)); inStream->Read(&pad, sizeof(pad)); inStream->Read(&maxX, sizeof(maxX)); inStream->Read(&gWidth, sizeof(gWidth)); inStream->Read(&y, sizeof(y)); inStream->Read(&x1, sizeof(x1)); inStream->Read(&x2, sizeof(x2)); inStream->Read(&x3, sizeof(x3)); inStream->Read(&y1, sizeof(y1)); inStream->Read(&y2, sizeof(y2)); inStream->Read(&y3, sizeof(y3)); inStream->Read(&b0, sizeof(b0)); inStream->Read(&gInTangentSpace, sizeof(gInTangentSpace)); inStream->Read(&tangentSpace_l,sizeof(NmTangentMatrix)); inStream->Read(&lTri, sizeof(NmRawTriangle)); /* if (bFirstPass) { octree = new AtiOctree(inStream); inStream->Read(&highNumTris,sizeof(highNumTris)); highTris = new NmRawTriangle[highNumTris]; inStream->Read(highTris,highNumTris*sizeof(NmRawTriangle)); inStream->Read(&gNumSamples,sizeof(gNumSamples)); gSamples = new NmSample[gNumSamples]; inStream->Read(gSamples,gNumSamples*sizeof(NmSample)); inStream->Read(&hTangentSpace,sizeof(hTangentSpace)); if (hTangentSpace!=NULL) { hTangentSpace = new NmTangentMatrix[highNumTris]; inStream->Read(hTangentSpace,highNumTris*sizeof(NmTangentMatrix)); } inStream->Read(&bumpWidth,sizeof(bumpWidth)); inStream->Read(&bumpHeight,sizeof(bumpHeight)); inStream->Read(&bumpMap,sizeof(bumpMap)); if (bumpMap!=NULL) { bumpMap = new float[bumpWidth*bumpHeight*3]; inStream->Read(bumpMap,bumpWidth*bumpHeight*3*sizeof(float)); } inStream->Read(&numRays,sizeof(numRays)); if (numRays!=0) { rays = new NmRawPointD[numRays]; inStream->Read(rays,numRays*sizeof(NmRawPointD)); rayWeights = new double[numRays]; inStream->Read(rayWeights,numRays*sizeof(double)); } } */ inStream->Read(&gOcclusion,sizeof(gOcclusion)); inStream->Read(&gBentNormal,sizeof(gBentNormal)); inStream->Read(&numComponents,sizeof(numComponents)); inStream->Read(&gOcclIdx,sizeof(gOcclIdx)); inStream->Read(&gDisplacements ,sizeof(gDisplacements)); inStream->Read(&gDispIdx ,sizeof(gDispIdx)); inStream->Read(&gEdgeCopy, sizeof(gEdgeCopy)); inStream->Read(&gNormalRules, sizeof(gNormalRules)); inStream->Read(&gMaxAngle, sizeof(gMaxAngle)); inStream->Read(&gDistance, sizeof(gDistance)); inStream->Read(&gEpsilon, sizeof(gEpsilon)); inStream->Read(&l, sizeof(l)); if (bFirstPass==true) { outStream->Write(&l, sizeof(l)); } bFirstPass=false; //--------------------------------------- // Loop over the Xs filling in each texel of the normal map for (int x = (minX - pad); x <= (maxX + pad); x++) { if (agent->TestConnection(sessionId)!=S_OK) { delete[] gCell; return false; } // Make sure this is a valid texture coordinate if ((x < 0) || (x >= gWidth)) { continue; } // Find Barycentric coordinates. double b1 = ((x2-x) * (y3-y) - (x3-x) * (y2-y)) / b0; double b2 = ((x3-x) * (y1-y) - (x1-x) * (y3-y)) / b0; double b3 = ((x1-x) * (y2-y) - (x2-x) * (y1-y)) / b0; // Interpolate tangent space. double ts[9]; if (gInTangentSpace == true) { for (int t = 0; t < 9; t++) { // ts[t] = (tangentSpace[l].m[0][t] * b1) + // (tangentSpace[l].m[1][t] * b2) + // (tangentSpace[l].m[2][t] * b3); ts[t] = (tangentSpace_l.m[0][t] * b1) + (tangentSpace_l.m[1][t] * b2) + (tangentSpace_l.m[2][t] * b3); } } // For each sample accumulate the normal double sNorm[3] = {0.0, 0.0, 0.0}; double sOcclusion = 0.0; double sDisplacement = 0.0; int sFound = 0; double aNorm[3] = {0.0, 0.0, 0.0}; for (int s = 0; s < sd->gNumSamples; s++) { // Compute new x & y double sX = (double)(x) + sd->gSamples[s].x; double sY = (double)(y) + sd->gSamples[s].y; // Find Barycentric coordinates. double b1 = ((x2-sX) * (y3-sY) - (x3-sX) * (y2-sY)) / b0; double b2 = ((x3-sX) * (y1-sY) - (x1-sX) * (y3-sY)) / b0; double b3 = ((x1-sX) * (y2-sY) - (x2-sX) * (y1-sY)) / b0; // Use Barycentric coordinates to deterimine if we are // are outside of the triangle. if ((b1 > 1.0) || (b1 < 0.0) || (b2 > 1.0) || (b2 < 0.0) || (b3 > 1.0) || (b3 < 0.0)) { continue; } // Compute position and normal NmRawPointD pos; NmRawPointD norm; BaryInterpolate (&lTri, b1, b2, b3, pos.v, norm.v); // First see if we even have an intersection. double newNorm[3]; double newPos[3]; double newDisplacement = 0.0f; if (FindBestIntersection (pos, norm, sd->octree, sd->highTris, sd->hTangentSpace, sd->bumpMap, sd->bumpHeight, sd->bumpWidth, newNorm, newPos, &newDisplacement, gNormalRules, gMaxAngle, gDistance, gEpsilon, gMaxCells, gCell)) { // Normalize the new normal sDisplacement += newDisplacement; Normalize (newNorm); // Do bent normal/occlusion if needed. if (gOcclusion || gBentNormal) { // First do the ray casting. NmRawPointD bentNormal; double occlusion; ComputeOcclusion ((NmRawPointD*)(newPos), (NmRawPointD*)(newNorm), sd->highNumTris, sd->highTris, sd->octree, sd->numRays, sd->rays, sd->rayWeights, &bentNormal, &occlusion, gMaxCells, gCell); // Add it to our sample sum. if (gBentNormal) { sNorm[0] += bentNormal.x; sNorm[1] += bentNormal.y; sNorm[2] += bentNormal.z; } else { sNorm[0] += newNorm[0]; sNorm[1] += newNorm[1]; sNorm[2] += newNorm[2]; } sOcclusion += occlusion; sFound++; } else { // Plain old normal map sNorm[0] += newNorm[0]; sNorm[1] += newNorm[1]; sNorm[2] += newNorm[2]; sFound++; } } // end if we found a normal } // end for samples (s); // If we found a normal put it in the image. if (sFound > 0) { // Convert to tangent space if needed if (gInTangentSpace == true) { ConvertToTangentSpace (ts, sNorm, sNorm); } // Add it to the image. int idx = y*gWidth*numComponents + x*numComponents; //======== write result ============== BYTE ostag; DWORD osindex; float osval; ostag = 3; osindex = idx; osval = (float)sNorm[0]; outStream->Write(&ostag,sizeof(ostag)); outStream->Write(&osindex,sizeof(osindex)); outStream->Write(&osval,sizeof(osval)); ostag = 3; osindex = idx+1; osval = (float)sNorm[1]; outStream->Write(&ostag,sizeof(ostag)); outStream->Write(&osindex,sizeof(osindex)); outStream->Write(&osval,sizeof(osval)); ostag = 3; osindex = idx+2; osval = (float)sNorm[2]; outStream->Write(&ostag,sizeof(ostag)); outStream->Write(&osindex,sizeof(osindex)); outStream->Write(&osval,sizeof(osval)); // img[idx + 0] += (float)sNorm[0]; // img[idx + 1] += (float)sNorm[1]; // img[idx + 2] += (float)sNorm[2]; if (gOcclusion) { ostag = 1; osindex = idx + gOcclIdx; osval = (float)(sOcclusion/sFound); outStream->Write(&ostag,sizeof(ostag)); outStream->Write(&osindex,sizeof(osindex)); outStream->Write(&osval,sizeof(osval)); // img[idx + gOcclIdx] = (float)(sOcclusion/sFound); } if (gDisplacements) { ostag = 1; osindex = idx + gDispIdx; osval = (float)(sDisplacement/sFound); outStream->Write(&ostag,sizeof(ostag)); outStream->Write(&osindex,sizeof(osindex)); outStream->Write(&osval,sizeof(osval)); // img[idx + gDispIdx] = (float)(sDisplacement/sFound); } //==================================== } // end if we found a normal else if (gEdgeCopy) { // Since we didn't find a normal, "snap" the center // sample's barycentric coordinates to the edge of the // triangle and find the normal there. Store this in // a temporary buffer that we'll use to dilate the image // in a way that doesn't produce texture seams. double sX = (double)x; double sY = (double)y; double b1 = ((x2-sX) * (y3-sY) - (x3-sX) * (y2-sY)) / b0; double b2 = ((x3-sX) * (y1-sY) - (x1-sX) * (y3-sY)) / b0; double b3 = ((x1-sX) * (y2-sY) - (x2-sX) * (y1-sY)) / b0; // "Snap" barycentric coordinates. if (b1 > 1.0) { double diff = b1 - 1.0; b1 = 1.0; b2 += (diff/2.0); b3 += (diff/2.0); } if (b1 < 0.0) { double diff = b1; b1 = 0.0; b2 += (diff/2.0); b3 += (diff/2.0); } if (b2 > 1.0) { double diff = b2 - 1.0; b1 = 1.0; b1 += (diff/2.0); b3 += (diff/2.0); } if (b2 < 0.0) { double diff = b2; b2 = 0.0; b1 += (diff/2.0); b3 += (diff/2.0); } if (b3 > 1.0) { double diff = b3 - 1.0; b3 = 1.0; b2 += (diff/2.0); b1 += (diff/2.0); } if (b3 < 0.0) { double diff = b3; b3 = 0.0; b2 += (diff/2.0); b1 += (diff/2.0); } // Compute position and normal NmRawPointD pos; NmRawPointD norm; BaryInterpolate (&lTri, b1, b2, b3, pos.v, norm.v); // First see if we even have an intersection. double newNorm[3]; double newPos[3]; double newOcclusion = 0.0; double newDisplacement = 0.0; if (FindBestIntersection (pos, norm, sd->octree, sd->highTris, sd->hTangentSpace, sd->bumpMap, sd->bumpHeight, sd->bumpWidth, newNorm, newPos, &newDisplacement, gNormalRules, gMaxAngle, gDistance, gEpsilon, gMaxCells, gCell)) { // Normalize the new normal Normalize (newNorm); // Do bent normal/occlusion if needed. if (gOcclusion || gBentNormal) { // First do the ray casting. NmRawPointD bentNormal; ComputeOcclusion ((NmRawPointD*)(newPos), (NmRawPointD*)(newNorm), sd->highNumTris, sd->highTris, sd->octree, sd->numRays, sd->rays, sd->rayWeights, &bentNormal, &newOcclusion, gMaxCells, gCell); // Replace normal with bent normal if needed if (gBentNormal) { newNorm[0] += bentNormal.x; newNorm[1] += bentNormal.y; newNorm[2] += bentNormal.z; } } // do ambient occlusion if needed // Convert to tangent space if needed if (gInTangentSpace == true) { ConvertToTangentSpace (ts, newNorm, newNorm); } // Add it to the image. int idx = y*gWidth*numComponents + x*numComponents; BYTE ostag; DWORD osindex; float osval; ostag = 4; osindex = idx; osval = (float)newNorm[0]; outStream->Write(&ostag,sizeof(ostag)); outStream->Write(&osindex,sizeof(osindex)); outStream->Write(&osval,sizeof(osval)); ostag = 4; osindex = idx+1; osval = (float)newNorm[1]; outStream->Write(&ostag,sizeof(ostag)); outStream->Write(&osindex,sizeof(osindex)); outStream->Write(&osval,sizeof(osval)); ostag = 4; osindex = idx+2; osval = (float)newNorm[2]; outStream->Write(&ostag,sizeof(ostag)); outStream->Write(&osindex,sizeof(osindex)); outStream->Write(&osval,sizeof(osval)); // img2[idx + 0] += (float)newNorm[0]; // img2[idx + 1] += (float)newNorm[1]; // img2[idx + 2] += (float)newNorm[2]; if (gOcclusion) { ostag = 2; osindex = idx + gOcclIdx; osval = (float)(newOcclusion); outStream->Write(&ostag,sizeof(ostag)); outStream->Write(&osindex,sizeof(osindex)); outStream->Write(&osval,sizeof(osval)); // img2[idx + gOcclIdx] = (float)(newOcclusion); } if (gDisplacements) { ostag = 2; osindex = idx + gDispIdx; osval = (float)(newDisplacement); outStream->Write(&ostag,sizeof(ostag)); outStream->Write(&osindex,sizeof(osindex)); outStream->Write(&osval,sizeof(osval)); // img2[idx + gDispIdx] = (float)(newDisplacement); } } // end if we found a normal // else don't add anything to either image. } // end else find a "snapped" normal // Spin! // ShowSpinner (prog); } // end for x inStream->Read(&intag, sizeof(DWORD)); } while (intag==1); delete[] gCell; gMaxCells = 0; gCell=NULL; BYTE ostag; ostag = 0; outStream->Write(&ostag,sizeof(ostag)); DWORD tricount; inStream->Read(&tricount, sizeof(tricount)); outStream->Write(&tricount, sizeof(tricount)); return true; }
static void Builder_Stretch(Int32 x1, Int32 y1, Int32 z1) { Int32 xMax = min(World_Width, x1 + CHUNK_SIZE); Int32 yMax = min(World_Height, y1 + CHUNK_SIZE); Int32 zMax = min(World_Length, z1 + CHUNK_SIZE); #if OCCLUSION Int32 flags = ComputeOcclusion(); #endif #if DEBUG_OCCLUSION FastColour col = new FastColour(60, 60, 60, 255); if (flags & 1) col.R = 255; // x if (flags & 4) col.G = 255; // y if (flags & 2) col.B = 255; // z map.Sunlight = map.Shadowlight = col; map.SunlightXSide = map.ShadowlightXSide = col; map.SunlightZSide = map.ShadowlightZSide = col; map.SunlightYBottom = map.ShadowlightYBottom = col; #endif Int32 x, y, z, xx, yy, zz; for (y = y1, yy = 0; y < yMax; y++, yy++) { for (z = z1, zz = 0; z < zMax; z++, zz++) { Int32 cIndex = (yy + 1) * EXTCHUNK_SIZE_2 + (zz + 1) * EXTCHUNK_SIZE + (-1 + 1); for (x = x1, xx = 0; x < xMax; x++, xx++) { cIndex++; BlockID b = Builder_Chunk[cIndex]; if (Block_Draw[b] == DRAW_GAS) continue; Int32 index = ((yy << 8) | (zz << 4) | xx) * FACE_COUNT; /* Sprites only use one face to indicate stretching count, so we can take a shortcut here. Note that sprites are not drawn with any of the DrawXFace, they are drawn using DrawSprite. */ if (Block_Draw[b] == DRAW_SPRITE) { index += FACE_YMAX; if (Builder_Counts[index]) { Builder_X = x; Builder_Y = y; Builder_Z = z; Builder_AddSpriteVertices(b); Builder_Counts[index] = 1; } continue; } Builder_X = x; Builder_Y = y; Builder_Z = z; Builder_FullBright = Block_FullBright[b]; UInt32 tileIdx = b * BLOCK_COUNT; /* All of these function calls are inlined as they can be called tens of millions to hundreds of millions of times. */ if (Builder_Counts[index] == 0 || (x == 0 && (y < Builder_SidesLevel || (b >= BLOCK_WATER && b <= BLOCK_STILL_LAVA && y < Builder_EdgeLevel))) || (x != 0 && (Block_Hidden[tileIdx + Builder_Chunk[cIndex - 1]] & (1 << FACE_XMIN)) != 0)) { Builder_Counts[index] = 0; } else { Int32 count = Builder_StretchZ(index, x, y, z, cIndex, b, FACE_XMIN); Builder_AddVertices(b, FACE_XMIN); Builder_Counts[index] = (UInt8)count; } index++; if (Builder_Counts[index] == 0 || (x == World_MaxX && (y < Builder_SidesLevel || (b >= BLOCK_WATER && b <= BLOCK_STILL_LAVA && y < Builder_EdgeLevel))) || (x != World_MaxX && (Block_Hidden[tileIdx + Builder_Chunk[cIndex + 1]] & (1 << FACE_XMAX)) != 0)) { Builder_Counts[index] = 0; } else { Int32 count = Builder_StretchZ(index, x, y, z, cIndex, b, FACE_XMAX); Builder_AddVertices(b, FACE_XMAX); Builder_Counts[index] = (UInt8)count; } index++; if (Builder_Counts[index] == 0 || (z == 0 && (y < Builder_SidesLevel || (b >= BLOCK_WATER && b <= BLOCK_STILL_LAVA && y < Builder_EdgeLevel))) || (z != 0 && (Block_Hidden[tileIdx + Builder_Chunk[cIndex - EXTCHUNK_SIZE]] & (1 << FACE_ZMIN)) != 0)) { Builder_Counts[index] = 0; } else { Int32 count = Builder_StretchX(index, Builder_X, Builder_Y, Builder_Z, cIndex, b, FACE_ZMIN); Builder_AddVertices(b, FACE_ZMIN); Builder_Counts[index] = (UInt8)count; } index++; if (Builder_Counts[index] == 0 || (z == World_MaxZ && (y < Builder_SidesLevel || (b >= BLOCK_WATER && b <= BLOCK_STILL_LAVA && y < Builder_EdgeLevel))) || (z != World_MaxZ && (Block_Hidden[tileIdx + Builder_Chunk[cIndex + EXTCHUNK_SIZE]] & (1 << FACE_ZMAX)) != 0)) { Builder_Counts[index] = 0; } else { Int32 count = Builder_StretchX(index, x, y, z, cIndex, b, FACE_ZMAX); Builder_AddVertices(b, FACE_ZMAX); Builder_Counts[index] = (UInt8)count; } index++; if (Builder_Counts[index] == 0 || y == 0 || (Block_Hidden[tileIdx + Builder_Chunk[cIndex - EXTCHUNK_SIZE_2]] & (1 << FACE_YMIN)) != 0) { Builder_Counts[index] = 0; } else { Int32 count = Builder_StretchX(index, x, y, z, cIndex, b, FACE_YMIN); Builder_AddVertices(b, FACE_YMIN); Builder_Counts[index] = (UInt8)count; } index++; if (Builder_Counts[index] == 0 || (Block_Hidden[tileIdx + Builder_Chunk[cIndex + EXTCHUNK_SIZE_2]] & (1 << FACE_YMAX)) != 0) { Builder_Counts[index] = 0; } else if (b < BLOCK_WATER || b > BLOCK_STILL_LAVA) { Int32 count = Builder_StretchX(index, x, y, z, cIndex, b, FACE_YMAX); Builder_AddVertices(b, FACE_YMAX); Builder_Counts[index] = (UInt8)count; } else { Int32 count = Builder_StretchXLiquid(index, x, y, z, cIndex, b); if (count > 0) Builder_AddVertices(b, FACE_YMAX); Builder_Counts[index] = (UInt8)count; } } } } }