//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "ts/tsShapeInstance.h" #include "dgl/dgl.h" #include "ts/tsLastDetail.h" #include "console/consoleTypes.h" #include "ts/tsDecal.h" #include "platform/profiler.h" #include "core/frameAllocator.h" TSShapeInstance::RenderData TSShapeInstance::smRenderData; MatrixF * TSShapeInstance::ObjectInstance::smTransforms = NULL; S32 TSShapeInstance::smMaxSnapshotScale = 2; bool TSShapeInstance::smNoRenderTranslucent = false; bool TSShapeInstance::smNoRenderNonTranslucent = false; F32 TSShapeInstance::smDetailAdjust = 1.0f; F32 TSShapeInstance::smScreenError = 5.0f; bool TSShapeInstance::smFogExemptionOn = false; S32 TSShapeInstance::smNumSkipRenderDetails = 0; bool TSShapeInstance::smSkipFirstFog = false; bool TSShapeInstance::smSkipFog = false; Vector TSShapeInstance::smNodeCurrentRotations(__FILE__, __LINE__); Vector TSShapeInstance::smNodeCurrentTranslations(__FILE__, __LINE__); Vector TSShapeInstance::smNodeCurrentUniformScales(__FILE__, __LINE__); Vector TSShapeInstance::smNodeCurrentAlignedScales(__FILE__, __LINE__); Vector TSShapeInstance::smNodeCurrentArbitraryScales(__FILE__, __LINE__); Vector TSShapeInstance::smRotationThreads(__FILE__, __LINE__); Vector TSShapeInstance::smTranslationThreads(__FILE__, __LINE__); Vector TSShapeInstance::smScaleThreads(__FILE__, __LINE__); namespace { void tsShapeTextureEventCB(const U32 eventCode, void *userData) { TSShape* pShape = reinterpret_cast(userData); if (eventCode == TextureManager::BeginZombification && pShape->mVertexBuffer != -1) { // ideally we would de-register the callback here, but that would screw up the loop if (dglDoesSupportVertexBuffer()) glFreeVertexBufferEXT(pShape->mVertexBuffer); else AssertFatal(false,"Vertex buffer should have already been freed!"); pShape->mVertexBuffer = -1; for (S32 i = 0; i < pShape->objects.size(); ++i) pShape->mPreviousMerge[i] = -1; } } } //------------------------------------------------------------------------------------- // constructors, destructors, initialization //------------------------------------------------------------------------------------- TSShapeInstance::TSShapeInstance(const Resource & shape, bool loadMaterials) { VECTOR_SET_ASSOCIATION(mMeshObjects); VECTOR_SET_ASSOCIATION(mDecalObjects); VECTOR_SET_ASSOCIATION(mIflMaterialInstances); VECTOR_SET_ASSOCIATION(mNodeTransforms); VECTOR_SET_ASSOCIATION(mNodeReferenceRotations); VECTOR_SET_ASSOCIATION(mNodeReferenceTranslations); VECTOR_SET_ASSOCIATION(mNodeReferenceUniformScales); VECTOR_SET_ASSOCIATION(mNodeReferenceScaleFactors); VECTOR_SET_ASSOCIATION(mNodeReferenceArbitraryScaleRots); VECTOR_SET_ASSOCIATION(mThreadList); VECTOR_SET_ASSOCIATION(mTransitionThreads); hShape = shape; mShape = hShape; buildInstanceData(mShape, loadMaterials); } TSShapeInstance::TSShapeInstance(TSShape * _shape, bool loadMaterials) { VECTOR_SET_ASSOCIATION(mMeshObjects); VECTOR_SET_ASSOCIATION(mDecalObjects); VECTOR_SET_ASSOCIATION(mIflMaterialInstances); VECTOR_SET_ASSOCIATION(mNodeTransforms); VECTOR_SET_ASSOCIATION(mNodeReferenceRotations); VECTOR_SET_ASSOCIATION(mNodeReferenceTranslations); VECTOR_SET_ASSOCIATION(mNodeReferenceUniformScales); VECTOR_SET_ASSOCIATION(mNodeReferenceScaleFactors); VECTOR_SET_ASSOCIATION(mNodeReferenceArbitraryScaleRots); VECTOR_SET_ASSOCIATION(mThreadList); VECTOR_SET_ASSOCIATION(mTransitionThreads); mShape = _shape; buildInstanceData(mShape, loadMaterials); } TSShapeInstance::~TSShapeInstance() { S32 i; for (i=0; imaterialList); } // set up node data S32 numNodes = mShape->nodes.size(); mNodeTransforms.setSize(numNodes); // add objects to trees S32 numObjects = mShape->objects.size(); mMeshObjects.setSize(numObjects); for (i=0; iobjects[i]; MeshObjectInstance * objInst = &mMeshObjects[i]; // call objInst constructor constructInPlace(objInst); // hook up the object to it's node objInst->nodeIndex = obj->nodeIndex; // set up list of meshes if (obj->numMeshes) objInst->meshList = &mShape->meshes[obj->startMeshIndex]; else objInst->meshList = NULL; objInst->object = obj; } // set up decal objects mDecalObjects.setSize(mShape->decals.size()); for (i=0; idecals.size(); i++) { const TSShape::Decal * decal = &mShape->decals[i]; DecalObjectInstance * decalInst = &mDecalObjects[i]; // call constructor constructInPlace(decalInst); decalInst->decalObject = decal; // hook up to node decalInst->targetObject = &mMeshObjects[decal->objectIndex]; decalInst->nodeIndex = decalInst->targetObject->nodeIndex; // set up list of decal meshes if (decal->numMeshes) { decalInst->decalList = (TSDecalMesh**)&mShape->meshes[decal->startMeshIndex]; for (S32 j=0; jnumMeshes; j++) if (decalInst->getDecalMesh(j)) { // point the decal mesh at it's target... // this is safe since meshes aren't shared between shapes TSDecalMesh * decalMesh = const_cast(decalInst->getDecalMesh(j)); decalMesh->targetMesh = decalInst->targetObject->getMesh(j); if (!decalMesh->targetMesh) { // detecting this a little late, but we don't need this decal since it isn't doing anything // should only happen on shapes exported before dtsexp 1.18 delete decalMesh; TSDecalMesh ** dm = const_cast(decalInst->decalList+j); *dm = NULL; } } } else decalInst->decalList = NULL; decalInst->frame = mShape->decalStates[i].frameIndex; } // construct ifl material objects if(loadMaterials) { for (i=0; iiflMaterials.size(); i++) { mIflMaterialInstances.increment(); mIflMaterialInstances.last().iflMaterial = &mShape->iflMaterials[i]; mIflMaterialInstances.last().frame = -1; } } // check to see which dl's have detail texturing mMaxDetailMapDL = -1; if(loadMaterials) { for (dl=0; dldetails.size(); dl++) { // check meshes on this detail level... S32 ss = mShape->details[dl].subShapeNum; S32 od = mShape->details[dl].objectDetailNum; if (ss<0) continue; // this is a billboard detail level S32 start = mShape->subShapeFirstObject[ss]; S32 end = mShape->subShapeNumObjects[ss] + start; for (i=start; iprimitives.size(); j++) { if (mesh->primitives[j].matIndex & TSDrawPrimitive::NoMaterial) continue; if (mMaterialList->getDetailMap(mesh->primitives[j].matIndex & TSDrawPrimitive::MaterialMask)) { mesh->setFlags(TSMesh::HasDetailTexture); if (dl>mMaxDetailMapDL) mMaxDetailMapDL = dl; } } } } } // check to see which dl's have lightmap texturing mMaxLightMapDL = -1; if(loadMaterials) { for (dl=0; dldetails.size(); dl++) { // check meshes on this detail level... S32 ss = mShape->details[dl].subShapeNum; S32 od = mShape->details[dl].objectDetailNum; if (ss<0) continue; // this is a billboard detail level S32 start = mShape->subShapeFirstObject[ss]; S32 end = mShape->subShapeNumObjects[ss] + start; for (i=start; iprimitives.size(); j++) { if (mesh->primitives[j].matIndex & TSDrawPrimitive::NoMaterial) continue; if (mMaterialList->getLightMap(mesh->primitives[j].matIndex & TSDrawPrimitive::MaterialMask)) { mesh->setFlags(TSMesh::HasLightTexture); if (dl>mMaxLightMapDL) mMaxLightMapDL = dl; } } } } } // set up subtree data S32 ss = mShape->subShapeFirstNode.size(); // we have this many subtrees mDirtyFlags = new U32[ss]; mGroundThread = NULL; mCurrentDetailLevel = 0; animateSubtrees(); // Construct billboards if not done already if(loadMaterials) ((TSShape *) mShape)->setupBillboardDetails(this); // Scan out the collision hulls... for (U32 i = 0; i < 16; i++) { char buff[128]; dSprintf(buff, sizeof(buff), "Collision-%d", i + 1); S32 colDetail = mShape->findDetail(buff); if (colDetail != -1) { S32 dl = colDetail; // get subshape and object detail const TSDetail * detail = &mShape->details[dl]; S32 ss = detail->subShapeNum; S32 od = detail->objectDetailNum; S32 start = mShape->subShapeFirstObject[ss]; S32 end = mShape->subShapeNumObjects[ss] + start; if (start= mesh->object->numMeshes) continue; // Yell at them if they named something! if (dStrnicmp(mShape->names[mesh->object->nameIndex], "Col", 3) != 0 && dStrnicmp(mShape->names[mesh->object->nameIndex], "LOSCol", 3) != 0) { Con::errorf("%s.dts - Collision mesh names should start with Col or LOSCol, encountered '%s' in detail level %d", mShape->mSourceResource->path,mShape->names[mesh->object->nameIndex], dl); continue; } } } } } } void TSShapeInstance::setMaterialList(TSMaterialList * ml) { // get rid of old list if (mOwnMaterialList) delete mMaterialList; mMaterialList = ml; mOwnMaterialList = false; if (mMaterialList && StringTable) // material lists need the string table to load... { // read ifl materials if necessary -- this is here rather than in shape because we can't open 2 files at once :( if (mShape->materialList == mMaterialList) ((TSShape*)mShape)->readIflMaterials(hShape.getFilePath()); mMaterialList->load(MeshTexture,hShape.getFilePath(),true); // check for reflectance map not in alpha of texture -- will require more work to emap for (U32 i=0; igetMaterialCount(); i++) { if (mMaterialList->getFlags(i) & (TSMaterialList::AuxiliaryMap|TSMaterialList::NeverEnvMap)) continue; if (!mMaterialList->reflectionInAlpha(i)) { mAlphaIsReflectanceMap = false; break; // found our exception } } } } void TSShapeInstance::cloneMaterialList() { if (mOwnMaterialList) return; mMaterialList = new TSMaterialList(mMaterialList); mOwnMaterialList = true; } static bool makeSkinPath(char* buffer, U32 bufferLength, const char* resourcePath, const char* oldSkin, const char* oldRoot, const char* newRoot) { bool replacedRoot = true; dsize_t oldRootLen = 0; char* rootStart = NULL; if (oldRoot == NULL) { // Not doing any replacing. replacedRoot = false; } else { // See if original name has the old root in it. oldRootLen = dStrlen(oldRoot); AssertFatal((oldRootLen + 1) < bufferLength, "makeSkinPath: Error, skin root name too long"); dStrcpy(buffer, oldRoot); dStrcat(buffer, "."); rootStart = dStrstr(oldSkin, buffer); if (rootStart == NULL) { replacedRoot = false; } } // Find out how long the total pathname will be. const dsize_t oldLen = dStrlen(oldSkin); dsize_t pathLen = 0; if (resourcePath != NULL) { pathLen = dStrlen(resourcePath); } if (replacedRoot) { const dsize_t newRootLen = dStrlen(newRoot); AssertFatal((pathLen + 1 + oldLen + newRootLen - oldRootLen) < bufferLength, "makeSkinPath: Error, pathname too long"); } else { AssertFatal((pathLen + 1 + oldLen) < bufferLength, "makeSkinPath: Error, pathname too long"); } // OK, now make the pathname. // Start with the resource path: if (resourcePath != NULL) { dStrcpy(buffer, resourcePath); dStrcat(buffer, "/"); } else { buffer[0] = '\0'; } if (replacedRoot) { // Then the pre-root part of the old name: dsize_t rootStartPos = rootStart - oldSkin; if (rootStartPos != 0) { dStrncat(buffer, oldSkin, rootStartPos); } // Then the new root: dStrcat(buffer, newRoot); dStrcat(buffer, "."); // Then the post-root part of the old name: dStrcat(buffer, oldSkin + rootStartPos + oldRootLen + 1); } else { // Then the old name: dStrcat(buffer, oldSkin); } return replacedRoot; } void TSShapeInstance::reSkin(StringHandle& newBaseHandle) { #define NAME_BUFFER_LENGTH 256 static char pathName[NAME_BUFFER_LENGTH]; const char* defaultBaseName = "base"; const char* newBaseName; if (newBaseHandle.isValidString()) { newBaseName = newBaseHandle.getString(); if (newBaseName == NULL) { return; } } else { newBaseName = defaultBaseName; } // Make our own copy of the materials list from the resource // if necessary. if (ownMaterialList() == false) { cloneMaterialList(); } const char* resourcePath = hShape.getFilePath(); // Cycle through the materials. TSMaterialList* pMatList = getMaterialList(); for (S32 j = 0; j < pMatList->mMaterialNames.size(); j++) { // Get the name of this material. const char* pName = pMatList->mMaterialNames[j]; // Bail if no name. if (pName == NULL) { continue; } // Make a texture file pathname with the new root if this name // has the old root in it; otherwise just make a path with the // original name. bool replacedRoot = makeSkinPath(pathName, NAME_BUFFER_LENGTH, resourcePath, pName, defaultBaseName, newBaseName); if (!replacedRoot) { // If this wasn't in the desired format, set the material's // texture handle (since that wasn't copied over in the // cloning) and continue. pMatList->mMaterials[j] = TextureHandle(pathName, MeshTexture, false); continue; } // OK, it is a skin texture. Get the handle. TextureHandle skinHandle = TextureHandle(pathName, MeshTexture, false); // Do a sanity check; if it fails, use the original skin instead. if (skinHandle.getGLName() != 0) { pMatList->mMaterials[j] = skinHandle; } else { makeSkinPath(pathName, NAME_BUFFER_LENGTH, resourcePath, pName, NULL, NULL); pMatList->mMaterials[j] = TextureHandle(pathName, MeshTexture, false); } } } //------------------------------------------------------------------------------------- // Render & detail selection //------------------------------------------------------------------------------------- void TSShapeInstance::render(const Point3F * objectScale) { if (mCurrentDetailLevel<0) return; PROFILE_START(TSShapeInstanceRender); dglSetRenderPrimType(3); // alphaIn: we start to alpha-in next detail level when intraDL > 1-alphaIn-alphaOut // (finishing when intraDL = 1-alphaOut) // alphaOut: start to alpha-out this detail level when intraDL > 1-alphaOut // NOTE: // intraDL is at 1 when if shape were any closer to us we'd be at dl-1, // intraDL is at 0 when if shape were any farther away we'd be at dl+1 F32 alphaOut = mShape->alphaOut[mCurrentDetailLevel]; F32 alphaIn = mShape->alphaIn[mCurrentDetailLevel]; F32 saveAA = mAlphaAlways ? mAlphaAlwaysValue : 1.0f; if (mCurrentIntraDetailLevel>alphaIn+alphaOut) render(mCurrentDetailLevel,mCurrentIntraDetailLevel,objectScale); else if (mCurrentIntraDetailLevel>alphaOut) { // draw this detail level w/ alpha=1 and next detail level w/ // alpha=1-(intraDl-alphaOut)/alphaIn // first draw next detail level if (mCurrentDetailLevel+1details.size() && mShape->details[mCurrentDetailLevel+1].size>0.0f) { setAlphaAlways(saveAA * (alphaIn+alphaOut-mCurrentIntraDetailLevel)/alphaIn); render(mCurrentDetailLevel+1,0.0f,objectScale); } setAlphaAlways(saveAA); render(mCurrentDetailLevel,mCurrentIntraDetailLevel,objectScale); } else { // draw next detail level w/ alpha=1 and this detail level w/ // alpha = 1-intraDL/alphaOut // first draw next detail level if (mCurrentDetailLevel+1details.size() && mShape->details[mCurrentDetailLevel+1].size>0.0f) render(mCurrentDetailLevel+1,0.0f,objectScale); setAlphaAlways(saveAA * mCurrentIntraDetailLevel / alphaOut); render(mCurrentDetailLevel,mCurrentIntraDetailLevel,objectScale); setAlphaAlways(saveAA); } dglSetRenderPrimType(0); PROFILE_END(); } bool TSShapeInstance::hasTranslucency() { if(!mShape->details.size()) return false; const TSDetail * detail = &mShape->details[0]; S32 ss = detail->subShapeNum; return mShape->subShapeFirstTranslucentObject[ss] != mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss]; } bool TSShapeInstance::hasSolid() { if(!mShape->details.size()) return false; const TSDetail * detail = &mShape->details[0]; S32 ss = detail->subShapeNum; return mShape->subShapeFirstTranslucentObject[ss] != mShape->subShapeFirstObject[ss]; } void TSShapeInstance::render(S32 dl, F32 intraDL, const Point3F * objectScale) { // if dl==-1, nothing to do if (dl==-1) return; AssertFatal(dl>=0 && dldetails.size(),"TSShapeInstance::render"); const TSDetail * detail = &mShape->details[dl]; S32 ss = detail->subShapeNum; // set up static data setStatics(dl,intraDL,objectScale); // if we're a billboard detail, draw it and exit PROFILE_START(TSShapeInstanceRenderBillboards); if (ss<0) { if (!smNoRenderTranslucent) mShape->billboardDetails[dl]->render(mAlphaAlways ? mAlphaAlwaysValue : 1.0f, smRenderData.fogOn); PROFILE_END(); return; } PROFILE_END(); S32 i; PROFILE_START(TSShapeInstanceMaterials); // set up animating ifl materials for (i=0; iiflMaterial; mMaterialList->remap(iflMaterial->materialSlot, iflMaterial->firstFrame + iflMaterialInstance->frame); } // decide how to use gl resources setupTexturing(dl,intraDL); // set up gl environment for drawing mesh materials TSMesh::initMaterials(); PROFILE_END(); S32 od = detail->objectDetailNum; bool supportBuffers = dglDoesSupportVertexBuffer(); if (!supportBuffers || !renderMeshesX(ss,od)) { // run through the meshes smRenderData.currentTransform = NULL; S32 start = smNoRenderNonTranslucent ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss]; S32 end = smNoRenderTranslucent ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss]; for (i=start; isubShapeFirstDecal[ss]; S32 end = mShape->subShapeNumDecals[ss] + start; if ( startmVertexBuffer) == -1) { // find out before we calc the needed buffer size if there are any free if (!glAvailableVertexBufferEXT()) { PROFILE_END(); return false; } GLsizei size = 0; start = mShape->subShapeFirstObject[0]; end = mShape->subShapeFirstObject[0] + mShape->subShapeNumObjects[0]; for (i = start; i < end; ++i) size += mMeshObjects[i].getSizeVB(size); mShape->mMorphable = false; for (i = start; i < end; ++i) if (mMeshObjects[i].hasMergeIndices()) { mShape->mMorphable = true; break; } vb = mShape->mVertexBuffer = glAllocateVertexBufferEXT(size,GL_V12MTNVFMT_EXT,true); if (vb == -1) { PROFILE_END(); return false; } if (mShape->mCallbackKey == -1) mShape->mCallbackKey = TextureManager::registerEventCallback(tsShapeTextureEventCB, mShape); // run through the meshes -- fill vertex buffer glLockVertexBufferEXT(vb,0); for (i = start; i < end; ++i) mMeshObjects[i].fillVB(vb,mMaterialList); glUnlockVertexBufferEXT(vb); } return true; } bool TSShapeInstance::renderMeshesX(S32 ss, S32 od) { // TODO: find out why this case doesn't work if (smRenderData.vertexAlpha.current < 1.0f) return false; PROFILE_START(TSShapeInstanceMeshes); S32 i,start,end,vb; if ((vb = mShape->mVertexBuffer) == -1) { // find out before we calc the needed buffer size if there are any free if (!glAvailableVertexBufferEXT()) { PROFILE_END(); return false; } GLsizei size = 0; start = mShape->subShapeFirstObject[0]; end = mShape->subShapeFirstObject[0] + mShape->subShapeNumObjects[0]; for (i = start; i < end; ++i) size += mMeshObjects[i].getSizeVB(size); mShape->mMorphable = false; for (i = start; i < end; ++i) if (mMeshObjects[i].hasMergeIndices()) { mShape->mMorphable = true; break; } vb = mShape->mVertexBuffer = glAllocateVertexBufferEXT(size,GL_V12MTNVFMT_EXT,true); if (vb == -1) { PROFILE_END(); return false; } if (mShape->mCallbackKey == -1) mShape->mCallbackKey = TextureManager::registerEventCallback(tsShapeTextureEventCB, mShape); // run through the meshes -- fill vertex buffer glLockVertexBufferEXT(vb,0); for (i = start; i < end; ++i) mMeshObjects[i].fillVB(vb,mMaterialList); glUnlockVertexBufferEXT(vb); } // run through the meshes start = smNoRenderNonTranslucent ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss]; end = smNoRenderTranslucent ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss]; if (mShape->mMorphable) { PROFILE_START(TSShapeInstanceMorphVB); glLockVertexBufferEXT(vb,0); for (i = start; i < end; ++i) mMeshObjects[i].morphVB(vb,mShape->mPreviousMerge[i],od,mMaterialList); glUnlockVertexBufferEXT(vb); PROFILE_END(); } smRenderData.currentTransform = NULL; PROFILE_START(TSShapeInstanceRenderVB); for (i = start; i < end; ++i) mMeshObjects[i].renderVB(vb,od,mMaterialList); PROFILE_END(); PROFILE_END(); return true; } bool TSShapeInstance::renderDecalsX(S32 ss, S32 od) { return false; ss,od; // I don't know why, but this doesn't quite work -- no time to fix #if 0 if (supportBuffers) { S32 i,start,end,vb; vb = mShape->mVertexBuffer; start = mShape->subShapeFirstDecal[ss]; end = mShape->subShapeNumDecals[ss] + start; if (smRenderData.renderDecals && starttargetMesh; TSDecalMesh *decal; if (!target0 || mDecalObjects[i].targetObject->visible <= 0.01f || !(decal = mDecalObjects[i].getDecalMesh(od)) || mDecalObjects[i].frame < 0 || !decal->targetMesh || decal->texgenS.empty() || decal->texgenT.empty()) continue; GLuint foffset = mDecalObjects[i].frame*target0->numMatFrames*target0->vertsPerFrame; glSetVertexBufferEXT(vb); glOffsetVertexBufferEXT(vb,target0->vbOffset+foffset); mDecalObjects[i].render(od,mMaterialList); } // if we have a matrix pushed, pop it now if (smRenderData.currentTransform) glPopMatrix(); // restore gl state TSDecalMesh::resetDecalMaterials(); } } else #endif } void TSShapeInstance::setStatics(S32 dl, F32 intraDL, const Point3F * objectScale) { ObjectInstance::smTransforms = mNodeTransforms.address(); smRenderData.objectScale = objectScale; smRenderData.detailLevel = dl; smRenderData.intraDetailLevel = intraDL; smRenderData.alwaysAlpha = mAlphaAlways; smRenderData.alwaysAlphaValue = getAlphaAlwaysValue(); smRenderData.balloonShape = mBalloonShape; smRenderData.balloonValue = getBalloonValue(); smRenderData.useOverride = mUseOverrideTexture; smRenderData.override = mOverrideTexture; S32 ss = mShape->details[dl].subShapeNum; S32 od = mShape->details[dl].objectDetailNum; TSMesh::smSaveVerts.setSize(mShape->mMergeBufferSize); TSMesh::smSaveTVerts.setSize(mShape->mMergeBufferSize); // If we have a billboard, skip the rest if (ss < 0) return; S32 start = smNoRenderNonTranslucent ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss]; S32 end = smNoRenderTranslucent ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss]; for (S32 i=start; isaveMergeVerts(); } } void TSShapeInstance::clearStatics() { ObjectInstance::smTransforms = NULL; smRenderData.override = NULL; S32 ss = mShape->details[smRenderData.detailLevel].subShapeNum; S32 od = mShape->details[smRenderData.detailLevel].objectDetailNum; S32 start = smNoRenderNonTranslucent ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss]; S32 end = smNoRenderTranslucent ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss]; for (S32 i=start; irestoreMergeVerts(); } } void TSShapeInstance::setupTexturing(S32 dl, F32 intraDL) { // first we'll decide what maps we want // then we'll decide how we can implement them (1-pass or 2-pass or not at all) // we need to set up these variables S32 & emapMethod = smRenderData.environmentMapMethod; S32 & dmapMethod = smRenderData.detailMapMethod; S32 & fogMethod = smRenderData.fogMethod; S32 & lmapMethod = smRenderData.lightMapMethod; S32 & dmapTE = smRenderData.detailMapTE; S32 & emapTE = smRenderData.environmentMapTE; S32 & baseTE = smRenderData.baseTE; S32 & fogTE = smRenderData.fogTE; S32 & lmapTE = smRenderData.lightMapTE; baseTE = 0; // initially assume base texture will go in first TE // ------------------------------------------------- // what do we want to do? bool wantEMap = ((mShape->mExportMerge && dl<=mShape->mSmallestVisibleDL/2) || (!mShape->mExportMerge && dl<=mMaxEnvironmentMapDL)) && mEnvironmentMapOn && (TextureObject*)mEnvironmentMap && mEnvironmentMapAlpha>0.01f; bool wantDMap = dl<=mMaxDetailMapDL; bool wantFog = smRenderData.fogOn && !smSkipFog; bool wantLMap = dl<=mMaxLightMapDL; smRenderData.detailMapAlpha = (dl0.5f) ? 1.0f : 2.0f * intraDL; smRenderData.environmentMapAlpha = mEnvironmentMapAlpha * ( (((mShape->mExportMerge && dl<=mShape->mSmallestVisibleDL/2) || (!mShape->mExportMerge && dl<=mMaxEnvironmentMapDL)) || intraDL>0.5f) ? 1.0f : 2.0f * intraDL ); smRenderData.environmentMapGLName = wantEMap ? mEnvironmentMap.getGLName() : 0; // ------------------------------------------------- // what can we do? if (!dglDoesSupportARBMultitexture()) { // we don't support multitexturing -- early out emapMethod = NO_ENVIRONMENT_MAP; dmapMethod = (wantDMap && mAllowTwoPassDetailMap) ? DETAIL_MAP_TWO_PASS : NO_DETAIL_MAP; fogMethod = wantFog ? FOG_TWO_PASS : NO_FOG; lmapMethod = wantLMap ? LIGHT_MAP_TWO_PASS : NO_LIGHT_MAP; return; } // how many texture environments (TE's) do we have? GLint numTE = 1, numUsedTE = 1; if (dglDoesSupportARBMultitexture()) glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&numTE); // what we do with the TE's will depend on whether the following extension is supported if (dglDoesSupportTextureEnvCombine()) { // environment map... if (wantEMap) { if (mAlphaIsReflectanceMap) { emapMethod = ENVIRONMENT_MAP_MULTI_1; emapTE = numUsedTE; numUsedTE++; } else if (numUsedTE+3<=numTE) { emapMethod = ENVIRONMENT_MAP_MULTI_3; emapTE = numUsedTE; numUsedTE += 3; } else if (mAllowTwoPassEnvironmentMap) emapMethod = ENVIRONMENT_MAP_TWO_PASS; else emapMethod = NO_ENVIRONMENT_MAP; } else emapMethod = NO_ENVIRONMENT_MAP; // detail map... if (wantDMap) { if (smRenderData.detailMapAlpha>0.99f && numTE>=numUsedTE+1) { dmapMethod = DETAIL_MAP_MULTI_1; dmapTE = numUsedTE; numUsedTE++; } else if (smRenderData.detailMapAlpha<=0.9f && numTE>=numUsedTE+2) { dmapMethod = DETAIL_MAP_MULTI_2; dmapTE = 0; // detail texture goes in first unit... baseTE++; // so we bump this back one... emapTE++; // this one gets bumped back 2... numUsedTE += 2; // end up using two additional units.. } else dmapMethod = mAllowTwoPassDetailMap ? DETAIL_MAP_TWO_PASS : NO_DETAIL_MAP; } else dmapMethod = NO_DETAIL_MAP; // fog... if (wantFog) { // DMMUNDO! if (numTE>=numUsedTE+1 && emapMethod!=ENVIRONMENT_MAP_TWO_PASS) { fogMethod = smRenderData.fogMapHandle ? FOG_MULTI_1_TEXGEN : FOG_MULTI_1; fogTE = numUsedTE; numUsedTE++; } else fogMethod = smRenderData.fogMapHandle ? FOG_TWO_PASS_TEXGEN : FOG_TWO_PASS; } else fogMethod = NO_FOG; // lightmaps... if (wantLMap) { if (numTE >= numUsedTE+1) { lmapMethod = LIGHT_MAP_MULTI; lmapTE = numUsedTE; numUsedTE++; } } else lmapMethod = LIGHT_MAP_TWO_PASS; } else { // we can't single pass environment map without texture combine extension... wantEMap = wantEMap && mAllowTwoPassEnvironmentMap; emapMethod = wantEMap ? ENVIRONMENT_MAP_TWO_PASS : NO_ENVIRONMENT_MAP; // ditto for detail map... wantDMap = wantDMap && mAllowTwoPassDetailMap; dmapMethod = wantDMap ? DETAIL_MAP_TWO_PASS : NO_DETAIL_MAP; fogMethod = wantFog ? FOG_TWO_PASS : NO_FOG; lmapMethod = wantLMap ? LIGHT_MAP_TWO_PASS : NO_LIGHT_MAP; } if (emapMethod == NO_ENVIRONMENT_MAP) smRenderData.environmentMapAlpha = 1.0f; } void TSShapeInstance::setupFog(F32 fogAmount, const ColorF & fogColor) { smRenderData.fogOn = (fogAmount > 1.0f / 64.0f); smRenderData.fogMapHandle = NULL; bool refresh = false; if (!smRenderData.fogBitmap) { smRenderData.fogBitmap = new GBitmap(8,8,false,GBitmap::RGBA); // clear the bitmap (defaults to 0xff) so if fogColor is 0,0,0 // we will have a valid bitmap dMemset(smRenderData.fogBitmap->getWritableBits(), 0, 256); } if (smRenderData.fogColor.x != fogColor.red || smRenderData.fogColor.y != fogColor.green || smRenderData.fogColor.z != fogColor.blue) { U8 *bits = smRenderData.fogBitmap->getWritableBits(); U8 red = U8(255*fogColor.red); U8 green = U8(255*fogColor.green); U8 blue = U8(255*fogColor.blue); for (U8 i = 0; i < 64; ++i) { *bits++ = red; *bits++ = green; *bits++ = blue; bits++; } refresh = true; } // the ATI Rage 128 needs a forthcoming driver to do do constant alpha blend if (smRenderData.fogTexture) { if (smRenderData.fogColor.w != fogAmount) { U8 *bits = smRenderData.fogBitmap->getWritableBits(); U8 fog = U8(255 * fogAmount); for (U8 i = 0; i < 64; ++i) { bits[3] = fog; bits += 4; } refresh = true; } } if (!smRenderData.fogHandle) smRenderData.fogHandle = new TextureHandle("fog_texture", smRenderData.fogBitmap); else if (refresh) smRenderData.fogHandle->refresh(); smRenderData.fogColor.set(fogColor.red,fogColor.green,fogColor.blue,fogAmount); } void TSShapeInstance::setupFog(F32 fogAmount, TextureHandle * fogMap, Point4F & s, Point4F & t) { smRenderData.fogColor.w = fogAmount; smRenderData.fogOn = true; smRenderData.fogMapHandle = fogMap; smRenderData.fogTexGenS = s; smRenderData.fogTexGenT = t; } bool TSShapeInstance::twoPassEnvironmentMap() { return (smRenderData.environmentMapMethod==ENVIRONMENT_MAP_TWO_PASS); } bool TSShapeInstance::twoPassDetailMap() { return (smRenderData.detailMapMethod==DETAIL_MAP_TWO_PASS); } bool TSShapeInstance::twoPassFog() { return (smRenderData.fogMethod==FOG_TWO_PASS || smRenderData.fogMethod==FOG_TWO_PASS_TEXGEN); } bool TSShapeInstance::twoPassLightMap() { return (smRenderData.lightMapMethod==LIGHT_MAP_TWO_PASS); } void TSShapeInstance::renderEnvironmentMap() { AssertFatal((void *)mEnvironmentMap!=NULL,"TSShapeInstance::renderEnvironmentMap (1)"); AssertFatal(mEnvironmentMapOn,"TSShapeInstance::renderEnvironmentMap (2)"); AssertFatal(dglDoesSupportARBMultitexture(),"TSShapeInstance::renderEnvironmentMap (3)"); AssertFatal(smRenderData.environmentMapMethod==ENVIRONMENT_MAP_TWO_PASS,"TSShapeInstance::renderEnvironmentMap (4)"); S32 dl = smRenderData.detailLevel; const TSDetail * detail = &mShape->details[dl]; S32 ss = detail->subShapeNum; S32 od = detail->objectDetailNum; S32 start = mShape->subShapeFirstObject[ss]; S32 end = mShape->subShapeNumObjects[ss] + start; if (start>=end) return; // set up gl environment for emap... TSMesh::initEnvironmentMapMaterials(); // run through objects and render smRenderData.currentTransform = NULL; for (S32 i=start; idetails[dl]; S32 ss = detail->subShapeNum; S32 od = detail->objectDetailNum; GLboolean wasLit = glIsEnabled(GL_LIGHTING); glDisable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnableClientState(GL_VERTEX_ARRAY); if (smRenderData.fogMethod==FOG_TWO_PASS_TEXGEN) { // set up fog map glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, TSShapeInstance::smRenderData.fogMapHandle->getGLName()); // set up texgen equations glTexGeni(GL_S,GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGeni(GL_T,GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glTexGenfv(GL_S,GL_OBJECT_PLANE,&TSShapeInstance::smRenderData.fogTexGenS.x); glTexGenfv(GL_T,GL_OBJECT_PLANE,&TSShapeInstance::smRenderData.fogTexGenT.x); } else { // just one fog color per shape... glColor4fv(smRenderData.fogColor); // texture should be disabled already... } smRenderData.currentTransform = NULL; S32 start = smNoRenderNonTranslucent ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss]; S32 end = smNoRenderTranslucent ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss]; for (S32 i=start; idetails[dl]; S32 ss = detail->subShapeNum; S32 od = detail->objectDetailNum; S32 start = mShape->subShapeFirstObject[ss]; S32 end = mShape->subShapeNumObjects[ss] + start; // set up gl environment for the detail map TSMesh::initDetailMapMaterials(); // run through objects and render detail maps smRenderData.currentTransform = NULL; for (S32 i=start; idetails[dl]; S32 ss = detail->subShapeNum; S32 od = detail->objectDetailNum; S32 start = mShape->subShapeFirstObject[ss]; S32 end = mShape->subShapeNumObjects[ss] + start; // set up gl environment for the detail map TSMesh::initLightMapMaterials(); // run through objects and render detail maps smRenderData.currentTransform = NULL; for (S32 i=start; i1.0f ? 1.0f : (intraDL<0.0f ? 0.0f : intraDL); // restrict chosen detail level by cutoff value S32 cutoff = getMin(smNumSkipRenderDetails,mShape->mSmallestVisibleDL); if (mCurrentDetailLevel>=0 && mCurrentDetailLevelmSmallestVisibleDL>=0 && mShape->details[0].maxError>=0) // use new scheme return selectCurrentDetailEx(ignoreScale); MatrixF toCam; Point3F p; dglGetModelview(&toCam); toCam.mulP(mShape->center,&p); F32 dist = mDot(p,p); F32 scale = 1.0f; if (!ignoreScale) { // any scale? Point3F x,y,z; toCam.getRow(0,&x); toCam.getRow(1,&y); toCam.getRow(2,&z); F32 scalex = mDot(x,x); F32 scaley = mDot(y,y); F32 scalez = mDot(z,z); scale = scalex; if (scaley > scale) scale = scaley; if (scalez > scale) scale = scalez; } dist /= scale; dist = mSqrt(dist); F32 pixelRadius = dglProjectRadius(dist,mShape->radius) * dglGetPixelScale() * smDetailAdjust; return selectCurrentDetail(pixelRadius); } S32 TSShapeInstance::selectCurrentDetailEx(bool ignoreScale) { MatrixF toCam; Point3F p; dglGetModelview(&toCam); toCam.mulP(mShape->center,&p); F32 dist = mDot(p,p); F32 scale = 1.0f; if (!ignoreScale) { // any scale? Point3F x,y,z; toCam.getRow(0,&x); toCam.getRow(1,&y); toCam.getRow(2,&z); F32 scalex = mDot(x,x); F32 scaley = mDot(y,y); F32 scalez = mDot(z,z); scale = scalex; if (scaley > scale) scale = scaley; if (scalez > scale) scale = scalez; } dist /= scale; dist = mSqrt(dist); // find tolerance F32 proj = dglProjectRadius(dist,1.0f) * dglGetPixelScale(); // pixel size of 1 meter at given distance if ( smFogExemptionOn ) return selectCurrentDetailEx(0.001f/proj); else return selectCurrentDetailEx(smScreenError/proj); } S32 TSShapeInstance::selectCurrentDetail(const Point3F &offset, F32 invScale) { F32 dist = mSqrt(mDot(offset,offset)); dist *= invScale; return selectCurrentDetail2(dist); } S32 TSShapeInstance::selectCurrentDetail2(F32 adjustedDist) { if (mShape->mSmallestVisibleDL>=0 && mShape->details[0].maxError>=0) // use new scheme return selectCurrentDetail2Ex(adjustedDist); F32 pixelRadius = dglProjectRadius(adjustedDist,mShape->radius) * dglGetPixelScale(); F32 adjustedPR = pixelRadius * smDetailAdjust; if(adjustedPR <= mShape->mSmallestVisibleSize) adjustedPR = mShape->mSmallestVisibleSize + 0.01f; return selectCurrentDetail(adjustedPR); } S32 TSShapeInstance::selectCurrentDetail2Ex(F32 adjustedDist) { // find tolerance F32 proj = dglProjectRadius(adjustedDist,1.0f) * dglGetPixelScale(); // pixel size of 1 meter at given distance if ( smFogExemptionOn ) return selectCurrentDetailEx(0.001f/proj); else return selectCurrentDetailEx(smScreenError/proj); } S32 TSShapeInstance::selectCurrentDetail(F32 size) { // check to see if not visible first... if (size<=mShape->mSmallestVisibleSize) { // don't render... mCurrentDetailLevel=-1; mCurrentIntraDetailLevel = 0.0f; return -1; } // same detail level as last time? // only search for detail level if the current one isn't the right one already if ( mCurrentDetailLevel<0 || (mCurrentDetailLevel==0 && size<=mShape->details[0].size) || (mCurrentDetailLevel>0 && (size<=mShape->details[mCurrentDetailLevel].size || size>mShape->details[mCurrentDetailLevel-1].size))) { // scan shape for highest detail size smaller than us... // shapes details are sorted from largest to smallest... // a detail of size <= 0 means it isn't a renderable detail level (utility detail) for (S32 i=0; idetails.size(); i++) { if (size>mShape->details[i].size) { mCurrentDetailLevel = i; break; } if (i+1>=mShape->details.size() || mShape->details[i+1].size<0) { // We've run out of details and haven't found anything? // Let's just grab this one. mCurrentDetailLevel = i; break; } } } F32 curSize = mShape->details[mCurrentDetailLevel].size; F32 nextSize = mCurrentDetailLevel==0 ? 2.0f * curSize : mShape->details[mCurrentDetailLevel-1].size; mCurrentIntraDetailLevel = nextSize-curSize>0.01f ? (size-curSize) / (nextSize-curSize) : 1.0f; mCurrentIntraDetailLevel = mCurrentIntraDetailLevel>1.0f ? 1.0f : (mCurrentIntraDetailLevel<0.0f ? 0.0f : mCurrentIntraDetailLevel); // now restrict chosen detail level by cutoff value S32 cutoff = getMin(smNumSkipRenderDetails,mShape->mSmallestVisibleDL); if (mCurrentDetailLevel>=0 && mCurrentDetailLevelmSmallestVisibleDL<0) prevErr=0.0f; else prevErr = 10.0f * mShape->details[mShape->mSmallestVisibleDL].averageError * 20.0f; if (mShape->mSmallestVisibleDL<0 || prevErrmSmallestVisibleDL; mCurrentIntraDetailLevel = 0.0f; return mCurrentDetailLevel; } // this function is a little odd // the reason is that the detail numbers correspond to // when we stop using a given detail level... // we search the details from most error to least error // until we fit under the tolerance (errorTOL) and then // we use the next highest detail (higher error) for (S32 i=mShape->mSmallestVisibleDL; i>=0; i--) { F32 err0 = 10.0f * mShape->details[i].averageError; if (err0 < errorTOL) { // ok, stop here // intraDL = 1 corresponds to fully this detail // intraDL = 0 corresponds to the next lower (higher number) detail mCurrentDetailLevel = i; mCurrentIntraDetailLevel = 1.0f - (errorTOL-err0)/(prevErr-err0); return mCurrentDetailLevel; } prevErr=err0; } // get here if we are drawing at DL==0 mCurrentDetailLevel = 1; mCurrentIntraDetailLevel = 1.0f; return mCurrentDetailLevel; } GBitmap * TSShapeInstance::snapshot(TSShape * shape, U32 width, U32 height, bool mip, MatrixF & cameraPos, S32 dl, F32 intraDL, bool hiQuality) { TSShapeInstance * shapeInstance = new TSShapeInstance(shape, true); shapeInstance->setCurrentDetail(dl,intraDL); shapeInstance->animate(); GBitmap * bmp = shapeInstance->snapshot(width,height,mip,cameraPos,hiQuality); delete shapeInstance; return bmp; } GBitmap * TSShapeInstance::snapshot(U32 width, U32 height, bool mip, MatrixF & cameraPos, S32 dl, F32 intraDL, bool hiQuality) { setCurrentDetail(dl,intraDL); animate(); return snapshot(width,height,mip,cameraPos,hiQuality); } GBitmap * TSShapeInstance::snapshot(U32 width, U32 height, bool mip, MatrixF & cameraMatrix,bool hiQuality) { U32 screenWidth = Platform::getWindowSize().x; U32 screenHeight = Platform::getWindowSize().y; U32 xcenter = screenWidth >> 1; U32 ycenter = screenHeight >> 1; if (screenWidth==0 || screenHeight==0) return NULL; // probably in exporter... AssertFatal(widthsmMaxSnapshotScale) scale = smMaxSnapshotScale; // height and width of intermediate bitmaps U32 bmpWidth = width*scale; U32 bmpHeight = height*scale; Point4F saveClearColor; glGetFloatv(GL_COLOR_CLEAR_VALUE,(F32*)&saveClearColor); // setup viewport and frustrum (do orthographic projection) dglSetViewport(RectI(xcenter-(bmpWidth>>1),ycenter-(bmpHeight>>1),bmpWidth,bmpHeight)); glMatrixMode(GL_PROJECTION); glLoadIdentity(); dglSetFrustum(-mShape->radius, mShape->radius, -mShape->radius, mShape->radius, 1, 20.0f * mShape->radius,true); // position camera... glMatrixMode(GL_MODELVIEW); Point3F y; cameraMatrix.getColumn(1,&y); y *= -10.0f * mShape->radius; y += mShape->center; cameraMatrix.setColumn(3,y); cameraMatrix.inverse(); dglLoadMatrix(&cameraMatrix); // set some initial gl states glDisable(GL_CULL_FACE); glDisable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); // take a snapshot of the shape with a black background... glClearColor(0,0,0,0); glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT); GBitmap * blackBmp = new GBitmap; blackBmp->allocateBitmap(bmpWidth,bmpHeight,false,GBitmap::RGB); render(mCurrentDetailLevel,mCurrentIntraDetailLevel); glReadPixels(xcenter-(bmpWidth>>1),ycenter-(bmpHeight>>1),bmpWidth,bmpHeight,GL_RGB,GL_UNSIGNED_BYTE,(void*)blackBmp->getBits(0)); // take a snapshot of the shape with a white background... glClearColor(1,1,1,1); glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT); GBitmap * whiteBmp = new GBitmap; whiteBmp->allocateBitmap(bmpWidth,bmpHeight,false,GBitmap::RGB); render(mCurrentDetailLevel,mCurrentIntraDetailLevel); glReadPixels(xcenter-(bmpWidth>>1),ycenter-(bmpHeight>>1),bmpWidth,bmpHeight,GL_RGB,GL_UNSIGNED_BYTE,(void*)whiteBmp->getBits(0)); glDisable(GL_DEPTH_TEST); glClearColor(saveClearColor.x,saveClearColor.y,saveClearColor.z,saveClearColor.w); // now separate the color and alpha channels GBitmap * bmp = new GBitmap; bmp->allocateBitmap(width,height,mip,GBitmap::RGBA); U8 * wbmp = (U8*)whiteBmp->getBits(0); U8 * bbmp = (U8*)blackBmp->getBits(0); U8 * dst = (U8*)bmp->getBits(0); U32 i,j; if (hiQuality) { for (i=0; i 0.01f ? 1.0f / alphaTally : 0.0f; U32 pos = 4*(i*width+j); dst[pos+0] = (U8)(rTally * invAlpha); dst[pos+1] = (U8)(gTally * invAlpha); dst[pos+2] = (U8)(bTally * invAlpha); dst[pos+3] = (U8)(((F32)alphaIntTally) / (F32) (3*alphaCount)); } } } else { // simpler, probably faster... for (i=0; iextrudeMipLevels(); return bmp; } void TSShapeInstance::renderShadow(S32 dl, const MatrixF & mat, S32 dim, U32 * bits) { // if dl==-1, nothing to do if (dl==-1) return; AssertFatal(dl>=0 && dldetails.size(),"TSShapeInstance::renderShadow"); S32 i; const TSDetail * detail = &mShape->details[dl]; S32 ss = detail->subShapeNum; S32 od = detail->objectDetailNum; // assert if we're a billboard detail AssertFatal(ss>=0,"TSShapeInstance::renderShadow: not with a billboard detail level"); // set up render data setStatics(dl); // run through the meshes smRenderData.currentTransform = NULL; S32 start = mShape->subShapeFirstObject[ss]; S32 end = start + mShape->subShapeNumObjects[ss]; for (i=start; igetMeshType() == TSMesh::StandardMeshType && mesh->vertsPerFrame > 0) { mesh->vbOffset = size; return mesh->numFrames*mesh->numMatFrames*mesh->vertsPerFrame; } else return 0; } bool TSShapeInstance::MeshObjectInstance::hasMergeIndices() { TSMesh *mesh = getMesh(0); return (mesh && mesh->getMeshType() == TSMesh::StandardMeshType && mesh->mergeIndices.size()); } void TSShapeInstance::MeshObjectInstance::fillVB(S32 vb, TSMaterialList *materials) { TSMesh *mesh = getMesh(0); if (!mesh || mesh->getMeshType() != TSMesh::StandardMeshType || mesh->vertsPerFrame <= 0) return; for (S32 f = 0; f < mesh->numFrames; ++f) for (S32 m = 0; m < mesh->numMatFrames; ++m) mesh->fillVB(vb,f,m,materials); } void TSShapeInstance::MeshObjectInstance::morphVB(S32 vb, S32 &previousMerge, S32 objectDetail, TSMaterialList *materials) { if (visible > 0.01f) { TSMesh *m0 = getMesh(0); TSMesh *mesh = getMesh(objectDetail); if (m0 && mesh) { // render TSSortedMesh's standard if (m0->getMeshType() != TSMesh::StandardMeshType) return; GLuint foffset = (frame*m0->numMatFrames + matFrame)*m0->vertsPerFrame; U32 morphSize = mesh->mergeIndices.size(); S32 merge = mesh->vertsPerFrame-morphSize; if (!morphSize) return; if (previousMerge != -1 && previousMerge < merge) { S32 tmp = merge; merge = previousMerge; previousMerge = tmp; morphSize = mesh->vertsPerFrame-merge; } else previousMerge = merge; glOffsetVertexBufferEXT(vb,m0->vbOffset + foffset + merge); mesh->morphVB(vb,morphSize,frame,matFrame,materials); } } } void TSShapeInstance::MeshObjectInstance::renderVB(S32 vb, S32 objectDetail, TSMaterialList *materials) { if (visible > 0.01f) { TSMesh *m0 = getMesh(0); TSMesh *mesh = getMesh(objectDetail); if (m0 && mesh) { // render TSSortedMesh's standard if (m0->getMeshType() != TSMesh::StandardMeshType) { render(objectDetail, materials); return; } if (mesh->vertsPerFrame <= 0) return; MatrixF *transform = getTransform(); if (transform != TSShapeInstance::smRenderData.currentTransform) { if (TSShapeInstance::smRenderData.currentTransform) glPopMatrix(); if (transform) { glPushMatrix(); dglMultMatrix(transform); } TSShapeInstance::smRenderData.currentTransform = transform; } GLuint foffset = (frame*m0->numMatFrames + matFrame)*m0->vertsPerFrame; glSetVertexBufferEXT(vb); glOffsetVertexBufferEXT(vb,m0->vbOffset + foffset); if (visible>0.99f) { if (TSShapeInstance::smRenderData.balloonShape) { glPushMatrix(); F32 &bv = TSShapeInstance::smRenderData.balloonValue; glScalef(bv,bv,bv); } mesh->renderVB(frame,matFrame,materials); if (TSShapeInstance::smRenderData.balloonShape) glPopMatrix(); } else { mesh->setFade(visible); mesh->renderVB(frame,matFrame,materials); mesh->clearFade(); } } } } void TSShapeInstance::MeshObjectInstance::render(S32 objectDetail, TSMaterialList * materials) { if (visible>0.01f) { TSMesh * mesh = getMesh(objectDetail); if (mesh) { MatrixF * transform = getTransform(); if (transform != TSShapeInstance::smRenderData.currentTransform) { if (TSShapeInstance::smRenderData.currentTransform) glPopMatrix(); if (transform) { glPushMatrix(); dglMultMatrix(transform); } TSShapeInstance::smRenderData.currentTransform = transform; } if (visible>0.99f) { if (TSShapeInstance::smRenderData.balloonShape) { glPushMatrix(); F32 & bv = TSShapeInstance::smRenderData.balloonValue; glScalef(bv,bv,bv); } mesh->render(frame,matFrame,materials); if (TSShapeInstance::smRenderData.balloonShape) glPopMatrix(); } else { mesh->setFade(visible); mesh->render(frame,matFrame,materials); mesh->clearFade(); } } } } void TSShapeInstance::DecalObjectInstance::render(S32 objectDetail, TSMaterialList * materials) { if (targetObject->visible>0.01f) { TSDecalMesh * decalMesh = getDecalMesh(objectDetail); if (decalMesh && frame>=0) { MatrixF * transform = getTransform(); if (transform != TSShapeInstance::smRenderData.currentTransform) { if (TSShapeInstance::smRenderData.currentTransform) glPopMatrix(); if (transform) { glPushMatrix(); dglMultMatrix(transform); } TSShapeInstance::smRenderData.currentTransform = transform; } if (TSShapeInstance::smRenderData.balloonShape) { glPushMatrix(); F32 & bv = TSShapeInstance::smRenderData.balloonValue; glScalef(bv,bv,bv); } decalMesh->render(targetObject->frame,frame,materials); if (TSShapeInstance::smRenderData.balloonShape) glPopMatrix(); } } } void TSShapeInstance::MeshObjectInstance::renderEnvironmentMap(S32 objectDetail, TSMaterialList * materials) { if (visible>0.01f) { TSMesh * mesh = getMesh(objectDetail); if (mesh) { MatrixF * transform = getTransform(); if (transform != TSShapeInstance::smRenderData.currentTransform) { if (TSShapeInstance::smRenderData.currentTransform) glPopMatrix(); if (transform) { glPushMatrix(); dglMultMatrix(transform); } TSShapeInstance::smRenderData.currentTransform = transform; } if (TSShapeInstance::smRenderData.balloonShape) { glPushMatrix(); F32 & bv = TSShapeInstance::smRenderData.balloonValue; glScalef(bv,bv,bv); } mesh->renderEnvironmentMap(frame,matFrame,materials); if (TSShapeInstance::smRenderData.balloonShape) glPopMatrix(); } } } void TSShapeInstance::MeshObjectInstance::renderDetailMap(S32 objectDetail, TSMaterialList * materials) { if (visible>0.01f) { TSMesh * mesh = getMesh(objectDetail); if (mesh && mesh->getFlags(TSMesh::HasDetailTexture)) { MatrixF * transform = getTransform(); if (transform != TSShapeInstance::smRenderData.currentTransform) { if (TSShapeInstance::smRenderData.currentTransform) glPopMatrix(); if (transform) { glPushMatrix(); dglMultMatrix(transform); } TSShapeInstance::smRenderData.currentTransform = transform; } if (TSShapeInstance::smRenderData.balloonShape) { glPushMatrix(); F32 & bv = TSShapeInstance::smRenderData.balloonValue; glScalef(bv,bv,bv); } mesh->renderDetailMap(frame,matFrame,materials); if (TSShapeInstance::smRenderData.balloonShape) glPopMatrix(); } } } void TSShapeInstance::MeshObjectInstance::renderShadow(S32 objectDetail, const MatrixF & mat, S32 dim, U32 * bits, TSMaterialList * materialList) { if (visible>0.01f) { TSMesh * mesh = getMesh(objectDetail); if (mesh) { MatrixF mat2; MatrixF * transform = getTransform(); if (transform) mat2.mul(mat,*transform); else mat2=mat; mesh->renderShadow(frame,mat2,dim,bits,materialList); } } } void TSShapeInstance::MeshObjectInstance::renderFog(S32 objectDetail, TSMaterialList* materials) { if (visible>0.01f) { TSMesh * mesh = getMesh(objectDetail); if (mesh) { MatrixF * transform = getTransform(); if (transform != TSShapeInstance::smRenderData.currentTransform) { if (TSShapeInstance::smRenderData.currentTransform) glPopMatrix(); if (transform) { glPushMatrix(); dglMultMatrix(transform); } TSShapeInstance::smRenderData.currentTransform = transform; } if (TSShapeInstance::smRenderData.balloonShape) { glPushMatrix(); F32 & bv = TSShapeInstance::smRenderData.balloonValue; glScalef(bv,bv,bv); } mesh->renderFog(frame, materials); if (TSShapeInstance::smRenderData.balloonShape) glPopMatrix(); } } } void TSShapeInstance::MeshObjectInstance::renderLightMap(S32 objectDetail, TSMaterialList * materials) { if (visible>0.01f) { TSMesh * mesh = getMesh(objectDetail); if (mesh && mesh->getFlags(TSMesh::HasLightTexture)) { MatrixF * transform = getTransform(); if (transform != TSShapeInstance::smRenderData.currentTransform) { if (TSShapeInstance::smRenderData.currentTransform) glPopMatrix(); if (transform) { glPushMatrix(); dglMultMatrix(transform); } TSShapeInstance::smRenderData.currentTransform = transform; } if (TSShapeInstance::smRenderData.balloonShape) { glPushMatrix(); F32 & bv = TSShapeInstance::smRenderData.balloonValue; glScalef(bv,bv,bv); } mesh->renderLightMap(frame,matFrame,materials); if (TSShapeInstance::smRenderData.balloonShape) glPopMatrix(); } } } void TSShapeInstance::incDebrisRefCount() { ++debrisRefCount; } void TSShapeInstance::decDebrisRefCount() { if( debrisRefCount == 0 ) return; --debrisRefCount; } U32 TSShapeInstance::getDebrisRefCount() { return debrisRefCount; } U32 TSShapeInstance::getNumDetails() { if( mShape ) { return mShape->details.size(); } return 0; }