bool GFXGLShader::initShader( const Torque::Path &file, bool isVertex, const Vector<GFXShaderMacro> ¯os ) { PROFILE_SCOPE(GFXGLShader_CompileShader); GLuint activeShader = glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER); if(isVertex) mVertexShader = activeShader; else mPixelShader = activeShader; glAttachShader(mProgram, activeShader); // Ok it's not in the shader gen manager, so ask Torque for it FileStream stream; if ( !stream.open( file, Torque::FS::File::Read ) ) { AssertISV(false, avar("GFXGLShader::initShader - failed to open shader '%s'.", file.getFullPath().c_str())); if ( smLogErrors ) Con::errorf( "GFXGLShader::initShader - Failed to open shader file '%s'.", file.getFullPath().c_str() ); return false; } if ( !_loadShaderFromStream( activeShader, file, &stream, macros ) ) return false; GLint compile; glGetShaderiv(activeShader, GL_COMPILE_STATUS, &compile); // Dump the info log to the console U32 logLength = 0; glGetShaderiv(activeShader, GL_INFO_LOG_LENGTH, (GLint*)&logLength); GLint compileStatus = GL_TRUE; if ( logLength ) { FrameAllocatorMarker fam; char* log = (char*)fam.alloc(logLength); glGetShaderInfoLog(activeShader, logLength, NULL, log); // Always print errors glGetShaderiv( activeShader, GL_COMPILE_STATUS, &compileStatus ); if ( compileStatus == GL_FALSE ) { if ( smLogErrors ) { Con::errorf( "GFXGLShader::initShader - Error compiling shader!" ); Con::errorf( "Program %s: %s", file.getFullPath().c_str(), log ); } } else if ( smLogWarnings ) Con::warnf( "Program %s: %s", file.getFullPath().c_str(), log ); } return compileStatus != GL_FALSE; }
bool GFXGLTextureObject::copyToBmp(GBitmap * bmp) { if (!bmp) return false; // check format limitations // at the moment we only support RGBA for the source (other 4 byte formats should // be easy to add though) AssertFatal(mFormat == GFXFormatR8G8B8A8, "GFXGLTextureObject::copyToBmp - invalid format"); AssertFatal(bmp->getFormat() == GFXFormatR8G8B8A8 || bmp->getFormat() == GFXFormatR8G8B8, "GFXGLTextureObject::copyToBmp - invalid format"); if(mFormat != GFXFormatR8G8B8A8) return false; if(bmp->getFormat() != GFXFormatR8G8B8A8 && bmp->getFormat() != GFXFormatR8G8B8) return false; AssertFatal(bmp->getWidth() == getWidth(), "GFXGLTextureObject::copyToBmp - invalid size"); AssertFatal(bmp->getHeight() == getHeight(), "GFXGLTextureObject::copyToBmp - invalid size"); PROFILE_SCOPE(GFXGLTextureObject_copyToBmp); PRESERVE_TEXTURE(mBinding); glBindTexture(mBinding, mHandle); U8 dstBytesPerPixel = GFXFormat_getByteSize( bmp->getFormat() ); U8 srcBytesPerPixel = GFXFormat_getByteSize( mFormat ); if(dstBytesPerPixel == srcBytesPerPixel) { glGetTexImage(mBinding, 0, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], bmp->getWritableBits()); return true; } FrameAllocatorMarker mem; U32 srcPixelCount = mTextureSize.x * mTextureSize.y; U8 *dest = bmp->getWritableBits(); U8 *orig = (U8*)mem.alloc(srcPixelCount * srcBytesPerPixel); glGetTexImage(mBinding, 0, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], orig); for(int i = 0; i < srcPixelCount; ++i) { dest[0] = orig[0]; dest[1] = orig[1]; dest[2] = orig[2]; if(dstBytesPerPixel == 4) dest[3] = orig[3]; orig += srcBytesPerPixel; dest += dstBytesPerPixel; } return true; }
void DeferredPNGWriter::append( GBitmap* bitmap, U32 rows) { AssertFatal(mActive, "Cannot append to an inactive DeferredPNGWriter!"); U32 height = getMin( bitmap->getHeight(), rows); FrameAllocatorMarker marker; png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) ); for (U32 i=0; i<height; i++) row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i)); png_write_rows(mData->png_ptr, row_pointers, height); }
bool QuadTreeTracer::castRay(const Point3F &start, const Point3F &end, RayInfo *info) { PROFILE_START(QuadTreeTracer_castRay); // Do some precalculations we'll use for the rest of this routine. // Set up our intercept calculation methods. F32 invDeltaX; if(end.x == start.x) { calcInterceptX = calcInterceptNone; invDeltaX = 0; } else { invDeltaX = 1.f / (end.x - start.x); calcInterceptX = calcInterceptV; } F32 invDeltaY; if(end.y == start.y) { calcInterceptY = calcInterceptNone; invDeltaY = 0; } else { invDeltaY = 1.f / (end.y - start.y); calcInterceptY = calcInterceptV; } // Subdivide our space based on the size of the lowest level of the tree... const F32 invSize = 1.f / F32(BIT(mTreeDepth-1)); // Grab this off the frame allocator, we don't want to do a proper alloc // on every ray! FrameAllocatorMarker stackAlloc; RayStackNode *stack = (RayStackNode*)stackAlloc.alloc(sizeof(RayStackNode) * (mTreeDepth * 3 + 1)); U32 stackSize = 1; // Kick off the stack with the root node. stack[0].startT = 0; stack[0].endT = 1; stack[0].squarePos.set(0,0); stack[0].level = mTreeDepth - 1; //Con::printf("QuadTreeTracer::castRay(%x)", this); // Aright, now let's do some raycasting! while(stackSize--) { // Get the current node for easy access... RayStackNode *sn = stack + stackSize; const U32 level = sn->level; const F32 startT = sn->startT; const F32 endT = sn->endT; const Point2I squarePos = sn->squarePos; AssertFatal((startT >= 0.f) && (startT <= 1.f), "QuadTreeTracer::castRay - out of range startT on stack!"); AssertFatal((endT >= 0.f) && (endT <= 1.f), "QuadTreeTracer::castRay - out of range endT on stack!"); //Con::printf(" -- node(%d, %d @ %d), sT=%f, eT=%f", squarePos.x, squarePos.y, level, startT, endT); // Figure our start and end Z. const F32 startZ = startT * (end.z - start.z) + start.z; const F32 endZ = endT * (end.z - start.z) + start.z; // Ok, now let's see if we hit the lower bound const F32 squareMin = getSquareMin(level, squarePos); if(startZ < squareMin && endZ < squareMin) continue; //Nope, skip out. // Hmm, let's check the upper bound. const F32 squareMax = getSquareMax(level, squarePos); if(startZ > squareMax && endZ > squareMax) continue; //Nope, skip out. // We might be intersecting something... If we've hit // the tree depth let's deal with the leaf intersection. if(level == 0) { //Con::printf(" ++ check node(%d, %d @ %d), sT=%f, eT=%f", squarePos.x, squarePos.y, level, startT, endT); if(castLeafRay(squarePos, start, end, startT, endT, info)) { PROFILE_END(); return true; // We hit, tell 'em so! } continue; // Otherwise, keep looking. } else { // Ok, we have to push our children as we're an inner node. // First, figure out some widths... U32 subSqSize = BIT(level - 1); // Now, calculate intercepts so we know how to deal with this // situation... (intercept = position, int = t value for that pos) const F32 xIntercept = (squarePos.x + subSqSize) * invSize; F32 xInt = calcInterceptX(start.x, invDeltaX, xIntercept); const F32 yIntercept = (squarePos.y + subSqSize) * invSize; F32 yInt = calcInterceptY(start.y, invDeltaY, yIntercept); // Our starting position for this subray... const F32 startX = startT * (end.x - start.x) + start.x; const F32 startY = startT * (end.y - start.y) + start.y; // Deal with squares that might be "behind" the ray. if(xInt < startT) xInt = F32_MAX; if(yInt < startT) yInt = F32_MAX; // Do a little magic to calculate our next checks... const U32 x0 = (startX > xIntercept) * subSqSize; const U32 y0 = (startY > yIntercept) * subSqSize; const U32 x1 = subSqSize - x0; const U32 y1 = subSqSize - y0; const U32 nextLevel = level - 1; // Ok, now let's figure out what nodes, in what order, need to go // on the stack. We push things on in reverse order of processing. if(xInt > endT && yInt > endT) { stack[stackSize].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize].level = nextLevel; stackSize++; } else if(xInt < yInt) { F32 nextIntersect = endT; if(yInt <= endT) { stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); stack[stackSize].startT = yInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; nextIntersect = yInt; stackSize++; } // Do middle two, order doesn't matter. stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y0); stack[stackSize].startT = xInt; stack[stackSize].endT = nextIntersect; stack[stackSize].level = nextLevel; stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = xInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } else if(yInt < xInt) { F32 nextIntersect = endT; if(xInt <= endT) { stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); stack[stackSize].startT = xInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; nextIntersect = xInt; stackSize++; } stack[stackSize].squarePos.set(squarePos.x + x0, squarePos.y + y1); stack[stackSize].startT = yInt; stack[stackSize].endT = nextIntersect; stack[stackSize].level = nextLevel; stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = yInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } else { stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); stack[stackSize].startT = xInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = xInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } } } // Nothing found, so give up. PROFILE_END(); return false; }
bool GFXGLShader::_init() { // Don't initialize empty shaders. if ( mVertexFile.isEmpty() && mPixelFile.isEmpty() ) return false; clearShaders(); mProgram = glCreateProgram(); // Set the macros and add the global ones. Vector<GFXShaderMacro> macros; macros.merge( mMacros ); macros.merge( smGlobalMacros ); // Add the shader version to the macros. const U32 mjVer = (U32)mFloor( mPixVersion ); const U32 mnVer = (U32)( ( mPixVersion - F32( mjVer ) ) * 10.01f ); macros.increment(); macros.last().name = "TORQUE_SM"; macros.last().value = String::ToString( mjVer * 10 + mnVer ); // Default to true so we're "successful" if a vertex/pixel shader wasn't specified. bool compiledVertexShader = true; bool compiledPixelShader = true; // Compile the vertex and pixel shaders if specified. if(!mVertexFile.isEmpty()) compiledVertexShader = initShader(mVertexFile, true, macros); if(!mPixelFile.isEmpty()) compiledPixelShader = initShader(mPixelFile, false, macros); // If either shader was present and failed to compile, bail. if(!compiledVertexShader || !compiledPixelShader) return false; // Link it! glLinkProgram( mProgram ); GLint linkStatus; glGetProgramiv( mProgram, GL_LINK_STATUS, &linkStatus ); // Dump the info log to the console U32 logLength = 0; glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, (GLint*)&logLength); if ( logLength ) { FrameAllocatorMarker fam; char* log = (char*)fam.alloc( logLength ); glGetProgramInfoLog( mProgram, logLength, NULL, log ); if ( linkStatus == GL_FALSE ) { if ( smLogErrors ) { Con::errorf( "GFXGLShader::init - Error linking shader!" ); Con::errorf( "Program %s / %s: %s", mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), log); } } else if ( smLogWarnings ) { Con::warnf( "Program %s / %s: %s", mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), log); } } // If we failed to link, bail. if ( linkStatus == GL_FALSE ) return false; initConstantDescs(); initHandles(); // Notify Buffers we might have changed in size. // If this was our first init then we won't have any activeBuffers // to worry about unnecessarily calling. Vector<GFXShaderConstBuffer*>::iterator biter = mActiveBuffers.begin(); for ( ; biter != mActiveBuffers.end(); biter++ ) ((GFXGLShaderConstBuffer*)(*biter))->onShaderReload( this ); return true; }
bool GFXGLShader::_init() { PROFILE_SCOPE(GFXGLShader_Init); // Don't initialize empty shaders. if ( mVertexFile.isEmpty() && mPixelFile.isEmpty() ) return false; clearShaders(); mProgram = glCreateProgram(); // Set the macros and add the global ones. Vector<GFXShaderMacro> macros; macros.merge( mMacros ); macros.merge( smGlobalMacros ); // Add the shader version to the macros. const U32 mjVer = (U32)mFloor( mPixVersion ); const U32 mnVer = (U32)( ( mPixVersion - F32( mjVer ) ) * 10.01f ); macros.increment(); macros.last().name = "TORQUE_SM"; macros.last().value = String::ToString( mjVer * 10 + mnVer ); macros.increment(); macros.last().name = "TORQUE_VERTEX_SHADER"; macros.last().value = ""; // Default to true so we're "successful" if a vertex/pixel shader wasn't specified. bool compiledVertexShader = true; bool compiledPixelShader = true; // Compile the vertex and pixel shaders if specified. if(!mVertexFile.isEmpty()) compiledVertexShader = initShader(mVertexFile, true, macros); macros.last().name = "TORQUE_PIXEL_SHADER"; if(!mPixelFile.isEmpty()) compiledPixelShader = initShader(mPixelFile, false, macros); // If either shader was present and failed to compile, bail. if(!compiledVertexShader || !compiledPixelShader) return false; //bind vertex attributes glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_Position, "vPosition"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_Normal, "vNormal"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_Color, "vColor"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_Tangent, "vTangent"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TangentW, "vTangentW"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_Binormal, "vBinormal"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord0, "vTexCoord0"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord1, "vTexCoord1"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord2, "vTexCoord2"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord3, "vTexCoord3"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord4, "vTexCoord4"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord5, "vTexCoord5"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord6, "vTexCoord6"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord7, "vTexCoord7"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord8, "vTexCoord8"); glBindAttribLocation(mProgram, Torque::GL_VertexAttrib_TexCoord9, "vTexCoord9"); //bind fragment out color glBindFragDataLocation(mProgram, 0, "OUT_col"); glBindFragDataLocation(mProgram, 1, "OUT_col1"); glBindFragDataLocation(mProgram, 2, "OUT_col2"); glBindFragDataLocation(mProgram, 3, "OUT_col3"); // Link it! glLinkProgram( mProgram ); GLint linkStatus; glGetProgramiv( mProgram, GL_LINK_STATUS, &linkStatus ); // Dump the info log to the console U32 logLength = 0; glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, (GLint*)&logLength); if ( logLength ) { FrameAllocatorMarker fam; char* log = (char*)fam.alloc( logLength ); glGetProgramInfoLog( mProgram, logLength, NULL, log ); if ( linkStatus == GL_FALSE ) { if ( smLogErrors ) { Con::errorf( "GFXGLShader::init - Error linking shader!" ); Con::errorf( "Program %s / %s: %s", mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), log); } } else if ( smLogWarnings ) { Con::warnf( "Program %s / %s: %s", mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), log); } } // If we failed to link, bail. if ( linkStatus == GL_FALSE ) return false; initConstantDescs(); initHandles(); // Notify Buffers we might have changed in size. // If this was our first init then we won't have any activeBuffers // to worry about unnecessarily calling. Vector<GFXShaderConstBuffer*>::iterator biter = mActiveBuffers.begin(); for ( ; biter != mActiveBuffers.end(); biter++ ) ((GFXGLShaderConstBuffer*)(*biter))->onShaderReload( this ); return true; }
void AtlasOldMesher::writeCollision(Stream *s) { // First, do the binning. This is a bit gross but, hey, what can you do... const U32 gridSize = BIT(gAtlasColTreeDepth-1); const U32 gridCount = gridSize * gridSize; Vector<U16> bins[gridCount]; // Track the min/max for the bins. S16 binsMax[gridCount]; S16 binsMin[gridCount]; // Clear bins. for(S32 i=0; i<gridCount; i++) { binsMax[i] = S16_MIN; binsMin[i] = S16_MAX; } // Get the size of bins (we step in x/y, not in Z). Point3F binSize( mBounds.len_x() / F32(gridSize), mBounds.len_y() / F32(gridSize), mBounds.len_z()); for(S32 i=0; i<gridSize; i++) { for(S32 j=0; j<gridSize; j++) { // Figure the bounds for this bin... Box3F binBox; binBox.minExtents.x = binSize.x * i; binBox.minExtents.y = binSize.y * j; binBox.minExtents.z = mBounds.minExtents.z - 1.f; binBox.maxExtents.x = binSize.x * (i+1); binBox.maxExtents.y = binSize.y * (j+1); binBox.maxExtents.z = mBounds.maxExtents.z + 1.f; Vector<U16> &binList = bins[i * gridSize + j]; S16 &binMin = binsMin[i * gridSize + j]; S16 &binMax = binsMax[i * gridSize + j]; // Now, consider all the triangles in the mesh. Note: we assume a trilist. for(S32 v=0; v<mIndices.size(); v+=3) { // Get the verts. const Vert &a = mVerts[mIndices[v+0]]; const Vert &b = mVerts[mIndices[v+1]]; const Vert &c = mVerts[mIndices[v+2]]; // If it's a special, skip it, we don't want to collide with skirts. if(a.special || b.special || c.special) continue; // I can't stand skirts! // Reject anything degenerate... if(mIndices[v+0] == mIndices[v+1]) continue; if(mIndices[v+1] == mIndices[v+2]) continue; if(mIndices[v+2] == mIndices[v+0]) continue; // Otherwise, we're good, so consider it for the current bin. const Point3F aPos = getVertPos(a); const Point3F bPos = getVertPos(b); const Point3F cPos = getVertPos(c); if(triBoxOverlap(binBox, aPos, bPos, cPos)) { // Got a hit, add it to the list! binList.push_back(v); // Update the min/max info. This will be TOO BIG if we have a // very large triangle! An optimal implementation will do a clip, // then update the bin. This is probably ok for the moment. S16 hA = mHeight->sample(a.pos); S16 hB = mHeight->sample(b.pos); S16 hC = mHeight->sample(c.pos); if(hA > binMax) binMax = hA; if(hB > binMax) binMax = hB; if(hC > binMax) binMax = hC; if(hA < binMin) binMin = hA; if(hB < binMin) binMin = hB; if(hC < binMin) binMin = hC; } } // Limit the triangle count to 16bits. While primary meshes support more // than that, collision meshes don't. If we don't catch that here, we'll // see raycasting issues later in Atlas. AssertISV( binList.size() <= 65536, "AtlasOldMesher::writeCollision - too many triangles! (>65536) Try again with a deeper tree" ); // Ok, we're all set for this bin... AssertFatal(binMin <= binMax, "AtlasOldMesher::writeCollision - empty bin, crap!"); } } // Next, generate the quadtree. FrameAllocatorMarker qtPool; const U32 nodeCount = QuadTreeTracer::getNodeCount(gAtlasColTreeDepth); S16 *qtMin = (S16*)qtPool.alloc(sizeof(S16) * nodeCount); S16 *qtMax = (S16*)qtPool.alloc(sizeof(S16) * nodeCount); // We have to recursively generate this from the bins on up. First we copy // the bins from earlier, then we do our recursomatic thingummy. (It's // actually not recursive.) for(S32 i=0; i<gridSize; i++) { for(S32 j=0; j<gridSize; j++) { const U32 qtIdx = QuadTreeTracer::getNodeIndex(gAtlasColTreeDepth-1, Point2I(i,j)); qtMin[qtIdx] = binsMin[i * gridSize + j]; qtMax[qtIdx] = binsMax[i * gridSize + j]; AssertFatal(qtMin[qtIdx] <= qtMax[qtIdx], "AtlasOldMesher::writeCollision - bad child quadtree node min/max! (negative a)"); } } // Alright, now we go up the bins, generating from the four children of each, // till we hit the root. // For each empty level from bottom to top... for(S32 depth = gAtlasColTreeDepth - 2; depth >= 0; depth--) { // For each square... for(S32 i=0; i<BIT(depth); i++) for(S32 j=0; j<BIT(depth); j++) { const U32 curIdx = QuadTreeTracer::getNodeIndex(depth, Point2I(i,j)); // For each of this square's 4 children... for(S32 subI=0; subI<2; subI++) for(S32 subJ=0; subJ<2; subJ++) { const U32 subIdx = QuadTreeTracer::getNodeIndex(depth+1, Point2I(i*2+subI,j*2+subJ)); // As is the child. AssertFatal(qtMin[subIdx] <= qtMax[subIdx], "AtlasOldMesher::writeCollision - bad child quadtree node min/max! (a)"); // Update the min and max of the parent. if(qtMin[subIdx] < qtMin[curIdx]) qtMin[curIdx] = qtMin[subIdx]; if(qtMax[subIdx] > qtMax[curIdx]) qtMax[curIdx] = qtMax[subIdx]; // Make sure we actually contain the child. AssertFatal(qtMin[subIdx] >= qtMin[curIdx], "AtlasOldMesher::writeCollision - bad quadtree child min during coltree generation!"); AssertFatal(qtMax[subIdx] <= qtMax[curIdx], "AtlasOldMesher::writeCollision - bad quadtree child max during coltree generation!"); // And that the parent is still valid. AssertFatal(qtMin[curIdx] <= qtMax[curIdx], "AtlasOldMesher::writeCollision - bad parent quadtree node min/max!"); // As is the child. AssertFatal(qtMin[subIdx] <= qtMax[subIdx], "AtlasOldMesher::writeCollision - bad child quadtree node min/max! (b)"); } } } // Wasn't that fun? Now we have a ready-to-go quadtree. // Now write the quadtree, in proper order for(S32 i=0; i<nodeCount; i++) { AssertFatal(qtMin[i] <= qtMax[i], "AtlasOldMesher::writeCollision - invalid quadtree min/max."); s->write(qtMin[i]); s->write(qtMax[i]); } s->write(U32(0xb33fd34d)); // We have to generate... // ... the list of triangle offsets for each bin. (Done above!) // ... the triangle buffer which stores the offsets for each bin. ChunkTriangleBufferGenerator ctbg(gridSize); for(S32 i=0; i<gridSize; i++) for(S32 j=0; j<gridSize; j++) ctbg.insertBinList(Point2I(i,j), bins[i * gridSize + j]); // Finally, write the data out. ctbg.write(s); }
//-------------------------------------------------------------------------- static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter) { GFXFormat format = bitmap->getFormat(); // ONLY RGB bitmap writing supported at this time! AssertFatal( format == GFXFormatR8G8B8 || format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8 || format == GFXFormatA8 || format == GFXFormatR5G6B5, "_writePNG: ONLY RGB bitmap writing supported at this time."); if ( format != GFXFormatR8G8B8 && format != GFXFormatR8G8B8A8 && format != GFXFormatR8G8B8X8 && format != GFXFormatA8 && format != GFXFormatR5G6B5 ) return false; png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, pngFatalErrorFn, pngWarningFn, NULL, pngMallocFn, pngFreeFn); if (png_ptr == NULL) return (false); png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return false; } png_set_write_fn(png_ptr, &stream, pngWriteDataFn, pngFlushDataFn); // Set the compression level and image filters png_set_compression_window_bits(png_ptr, 15); png_set_compression_level(png_ptr, compressionLevel); png_set_filter(png_ptr, 0, filter); // Set the image information here. Width and height are up to 2^31, // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED U32 width = bitmap->getWidth(); U32 height = bitmap->getHeight(); if (format == GFXFormatR8G8B8) { png_set_IHDR(png_ptr, info_ptr, width, height, // the width & height 8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type, NULL, // no interlace NULL, // compression type NULL); // filter type } else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8) { png_set_IHDR(png_ptr, info_ptr, width, height, // the width & height 8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type, NULL, // no interlace NULL, // compression type NULL); // filter type } else if (format == GFXFormatA8) { png_set_IHDR(png_ptr, info_ptr, width, height, // the width & height 8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, NULL, // no interlace NULL, // compression type NULL); // filter type } else if (format == GFXFormatR5G6B5) { png_set_IHDR(png_ptr, info_ptr, width, height, // the width & height 16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, PNG_INTERLACE_NONE, // no interlace PNG_COMPRESSION_TYPE_DEFAULT, // compression type PNG_FILTER_TYPE_DEFAULT); // filter type png_color_8_struct sigBit = { 0 }; sigBit.gray = 16; png_set_sBIT(png_ptr, info_ptr, &sigBit ); png_set_swap( png_ptr ); } png_write_info(png_ptr, info_ptr); FrameAllocatorMarker marker; png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) ); for (U32 i=0; i<height; i++) row_pointers[i] = const_cast<png_bytep>(bitmap->getAddress(0, i)); png_write_image(png_ptr, row_pointers); // Write S3TC data if present... // Write FXT1 data if present... png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return true; }
bool GFXD3D9Shader::_compileShader( const Torque::Path &filePath, const String& target, const D3DXMACRO *defines, GenericConstBufferLayout* bufferLayoutF, GenericConstBufferLayout* bufferLayoutI, Vector<GFXShaderConstDesc> &samplerDescriptions ) { PROFILE_SCOPE( GFXD3D9Shader_CompileShader ); HRESULT res = D3DERR_INVALIDCALL; LPD3DXBUFFER code = NULL; LPD3DXBUFFER errorBuff = NULL; #ifdef TORQUE_DEBUG U32 flags = D3DXSHADER_DEBUG; #else U32 flags = 0; #endif #ifdef TORQUE_OS_XENON flags |= D3DXSHADER_PREFER_FLOW_CONTROL; #endif #ifdef D3DXSHADER_USE_LEGACY_D3DX9_31_DLL if( D3DX_SDK_VERSION >= 32 ) { // will need to use old compiler for 1_1 shaders - check for pixel // or vertex shader with appropriate version. if ((target.compare("vs1", 3) == 0) || (target.compare("vs_1", 4) == 0)) flags |= D3DXSHADER_USE_LEGACY_D3DX9_31_DLL; if ((target.compare("ps1", 3) == 0) || (target.compare("ps_1", 4) == 0)) flags |= D3DXSHADER_USE_LEGACY_D3DX9_31_DLL; } #endif #if !defined(TORQUE_OS_XENON) && (D3DX_SDK_VERSION <= 40) #error This version of the DirectX SDK is too old. Please install a newer version of the DirectX SDK: http://msdn.microsoft.com/en-us/directx/default.aspx #endif ID3DXConstantTable* table = NULL; static String sHLSLStr( "hlsl" ); static String sOBJStr( "obj" ); // Is it an HLSL shader? if ( filePath.getExtension().equal(sHLSLStr, String::NoCase) ) { FrameAllocatorMarker fam; char *buffer = NULL; // Set this so that the D3DXInclude::Open will have this // information for relative paths. smD3DXInclude->setPath( filePath.getRootAndPath() ); FileStream s; if ( !s.open( filePath, Torque::FS::File::Read ) ) { AssertISV(false, avar("GFXD3D9Shader::initShader - failed to open shader '%s'.", filePath.getFullPath().c_str())); if ( smLogErrors ) Con::errorf( "GFXD3D9Shader::_compileShader - Failed to open shader file '%s'.", filePath.getFullPath().c_str() ); return false; } // Convert the path which might have virtualized // mount paths to a real file system path. Torque::Path realPath; if ( !FS::GetFSPath( filePath, realPath ) ) realPath = filePath; // Add a #line pragma so that error and warning messages // returned by the HLSL compiler report the right file. String linePragma = String::ToString( "#line 1 \"%s\"\r\n", realPath.getFullPath().c_str() ); U32 linePragmaLen = linePragma.length(); U32 bufSize = s.getStreamSize(); buffer = (char *)fam.alloc( bufSize + linePragmaLen + 1 ); dStrncpy( buffer, linePragma.c_str(), linePragmaLen ); s.read( bufSize, buffer + linePragmaLen ); buffer[bufSize+linePragmaLen] = 0; res = GFXD3DX.D3DXCompileShader( buffer, bufSize + linePragmaLen, defines, smD3DXInclude, "main", target, flags, &code, &errorBuff, &table ); } // Is it a precompiled obj shader? else if ( filePath.getExtension().equal( sOBJStr, String::NoCase ) ) { FileStream s; if(!s.open(filePath, Torque::FS::File::Read)) { AssertISV(false, avar("GFXD3D9Shader::initShader - failed to open shader '%s'.", filePath.getFullPath().c_str())); if ( smLogErrors ) Con::errorf( "GFXD3D9Shader::_compileShader - Failed to open shader file '%s'.", filePath.getFullPath().c_str() ); return false; } res = GFXD3DX.D3DXCreateBuffer(s.getStreamSize(), &code); AssertISV(res == D3D_OK, "Unable to create buffer!"); s.read(s.getStreamSize(), code->GetBufferPointer()); if (res == D3D_OK) { DWORD* data = (DWORD*) code->GetBufferPointer(); res = GFXD3DX.D3DXGetShaderConstantTable(data, &table); } } else { if ( smLogErrors ) Con::errorf( "GFXD3D9Shader::_compileShader - Unsupported shader file type '%s'.", filePath.getFullPath().c_str() ); return false; } if ( res != D3D_OK && smLogErrors ) Con::errorf( "GFXD3D9Shader::_compileShader - Error compiling shader: %s: %s (%x)", DXGetErrorStringA(res), DXGetErrorDescriptionA(res), res ); if ( errorBuff ) { // remove \n at end of buffer U8 *buffPtr = (U8*) errorBuff->GetBufferPointer(); U32 len = dStrlen( (const char*) buffPtr ); buffPtr[len-1] = '\0'; if( res != D3D_OK ) { if ( smLogErrors ) Con::errorf( " %s", (const char*) errorBuff->GetBufferPointer() ); } else { if ( smLogWarnings ) Con::warnf( "%s", (const char*) errorBuff->GetBufferPointer() ); } } else if ( code == NULL && smLogErrors ) Con::errorf( "GFXD3D9Shader::_compileShader - no compiled code produced; possibly missing file '%s'.", filePath.getFullPath().c_str() ); // Create the proper shader if we have code if( code != NULL ) { #ifndef TORQUE_SHIPPING LPD3DXBUFFER disassem = NULL; D3DXDisassembleShader( (DWORD*)code->GetBufferPointer(), false, NULL, &disassem ); mDissasembly = (const char*)disassem->GetBufferPointer(); SAFE_RELEASE( disassem ); if ( gDisassembleAllShaders ) { String filename = filePath.getFullPath(); filename.replace( ".hlsl", "_dis.txt" ); FileStream *fstream = FileStream::createAndOpen( filename, Torque::FS::File::Write ); if ( fstream ) { fstream->write( mDissasembly ); fstream->close(); delete fstream; } } #endif if (target.compare("ps_", 3) == 0) res = mD3D9Device->CreatePixelShader( (DWORD*)code->GetBufferPointer(), &mPixShader ); else res = mD3D9Device->CreateVertexShader( (DWORD*)code->GetBufferPointer(), &mVertShader ); if (res == S_OK) _getShaderConstants(table, bufferLayoutF, bufferLayoutI, samplerDescriptions); #ifdef TORQUE_ENABLE_CSF_GENERATION // Ok, we've got a valid shader and constants, let's write them all out. if ( !_saveCompiledOutput(filePath, code, bufferLayoutF, bufferLayoutI) && smLogErrors ) Con::errorf( "GFXD3D9Shader::_compileShader - Unable to save shader compile output for: %s", filePath.getFullPath().c_str() ); #endif SAFE_RELEASE(table); if ( res != S_OK && smLogErrors ) Con::errorf( "GFXD3D9Shader::_compileShader - Unable to create shader for '%s'.", filePath.getFullPath().c_str() ); } bool result = code != NULL && res == S_OK; SAFE_RELEASE( code ); SAFE_RELEASE( errorBuff ); return result; }
void AtlasFile::syncThreads() { // Compare current time with last, don't update if it's less than 10ms or so. const U32 nextTime = Platform::getRealMilliseconds(); if((nextTime - mLastSyncTime) < 10) return; PROFILE_START(AtlasFile_syncThreads); //Con::printf("----------- sync ----------------"); mFile.sync(); // Do a sync callback for our TOCs PROFILE_START(AtlasFile_syncThreads_callback); F32 dt = F32(nextTime - mLastSyncTime) / 1000.f; for(S32 i=0; i<mTOCs.size(); i++) mTOCs[i]->syncCallback(dt); mLastSyncTime = nextTime; PROFILE_END(); // First, let's dequeue all our in-flight loads so we don't queue the same // thing twice. To make sure we don't get diddled, we also have to lock // the pending queue. // We'd better be sure we unlock these. Order is important to prevent // deadlocks. PROFILE_START(AtlasFile_syncThreads_notes); Vector<AtlasReadNote*> &pendingQ = mPendingLoadQueue.lockVector(); Vector<AtlasReadNote*> &inProcessQ = mLoadingNotes.lockVector(); // Filter out loads in process. for(S32 i=0; i<inProcessQ.size(); i++) { const AtlasReadNote *arn = inProcessQ[i]; arn->toc->cbOnChunkReadStarted(arn->stubIdx); } /*if(inProcessQ.size()) Con::printf(" %d are in-process", inProcessQ.size()); */ inProcessQ.clear(); // Update our load queue. Vector<AtlasReadNote*> *updateList = new Vector<AtlasReadNote*>[mTOCs.size()]; U32 remaining = 0; const U32 numToFetch = 5; for(S32 i=0; i<mTOCs.size(); i++) { updateList[i].reserve(numToFetch); mTOCs[i]->recalculateUpdates(updateList[i], numToFetch); remaining += updateList[i].size(); } /*if(remaining) Con::printf(" %d in queue", remaining);*/ // Round-robin add them to the master queue. { // Wipe pending ARNs... this is kinda gross. for(S32 i=0; i<pendingQ.size(); i++) { SAFE_DELETE(pendingQ[i]); } pendingQ.clear(); // And fill it up again, round robin. U32 originalRemaining = remaining; pendingQ.reserve(remaining); while(remaining) { for(S32 i=0; i<mTOCs.size(); i++) { if(updateList[i].size()) { pendingQ.push_back(updateList[i].first()); updateList[i].pop_front(); remaining--; } } } AssertFatal(pendingQ.size() == originalRemaining, "AtlasFile::syncThreads - unexpected pending queue size after population."); } // Remember what I said about locking order before? mLoadingNotes.unlockVector(); mPendingLoadQueue.unlockVector(); PROFILE_END(); // Clean up our temporary queues. delete[] updateList; PROFILE_START(AtlasFile_syncThreads_instatement); // Pass stuff to the TOCs for instatement. { FrameAllocatorMarker famAlloc; // Copy it out, then wipe the master, as instatement may take a while. Vector<AtlasReadNote*> &lockedProcessQ = mPendingProcessQueue.lockVector(); Vector<AtlasReadNote*> &lockedInProcessQ = mLoadingNotes.lockVector(); // Copy our pending data to a buffer so we can work on it. U32 pendingCount = lockedProcessQ.size(); AtlasReadNote **pendingData = (AtlasReadNote **)famAlloc.alloc(sizeof(AtlasReadNote*) * pendingCount); dMemcpy(pendingData, lockedProcessQ.address(), sizeof(AtlasReadNote*) * pendingCount); lockedProcessQ.clear(); mPendingProcessQueue.unlockVector(); // And do the instating, removing from the loadNoteQueue as needed. for(S32 i=0; i<pendingCount; i++) { PROFILE_START(AtlasFile_syncThreads_instatement_callback); pendingData[i]->toc->cbOnChunkReadComplete(pendingData[i]->stubIdx, pendingData[i]->chunk); PROFILE_END(); // Remove the ARN from the in process queue, so we won't puke // trying to mark it as pending later on. for(S32 j=0; j<lockedInProcessQ.size(); j++) { if(lockedInProcessQ[j] == pendingData[i]) { lockedInProcessQ.erase_fast(j); break; } } // Get rid of the ARN. delete pendingData[i]; } mLoadingNotes.unlockVector(); } PROFILE_END(); // Make sure we're loading. enqueueNextPendingLoad(false); PROFILE_END(); }