////////////////////////////////////////////////////////////////////// // Use the tree ////////////////////////////////////////////////////////////////////// void plCullTree::Harvest(const plSpaceTree* space, hsTArray<int16_t>& outList) const { outList.SetCount(0); if (!space->IsEmpty()) IGetRoot()->IHarvest(space, outList); }
void plSceneNode::CollectForRender(plPipeline* pipe, hsTArray<plDrawVisList>& levList, plVisMgr* visMgr) { static hsTArray<int16_t> visList; visList.SetCount(0); pipe->HarvestVisible(GetSpaceTree(), visList); static hsTArray<int16_t> visSpans; visSpans.SetCount(0); int i; for( i = 0; i < visList.GetCount(); i++ ) { int idx = visList[i]; if( pipe->PreRender(fDrawPool[idx], visSpans, visMgr) ) { plDrawVisList* drawVis = levList.Push(); drawVis->fDrawable = fDrawPool[idx]; drawVis->fVisList.Swap(visSpans); } } }
void plSceneNode::Harvest(plVolumeIsect* isect, hsTArray<plDrawVisList>& levList) { static hsTArray<int16_t> visList; visList.SetCount(0); GetSpaceTree()->HarvestLeaves(isect, visList); static hsTArray<int16_t> visSpans; visSpans.SetCount(0); int i; for( i = 0; i < visList.GetCount(); i++ ) { int idx = visList[i]; fDrawPool[idx]->GetSpaceTree()->HarvestLeaves(isect, visSpans); if( visSpans.GetCount() ) { plDrawVisList* drawVis = levList.Push(); drawVis->fDrawable = fDrawPool[idx]; drawVis->fVisList.Swap(visSpans); } } }
void pl3DPipeline::IAttachSlaveToReceivers(int which, plDrawableSpans* drawable, const hsTArray<int16_t>& visList) { plShadowSlave* slave = fShadows[which]; // Whether the drawable is a character affects which lights/shadows affect it. bool isChar = drawable->GetNativeProperty(plDrawable::kPropCharacter); // If the shadow is part of a light group, it gets handled in ISetShadowFromGroup. // Unless the drawable is a character (something that moves around indeterminately, // like the avatar or a physical object), and the shadow affects all characters. if (slave->ObeysLightGroups() && !(slave->IncludesChars() && isChar)) return; // Do a space tree harvest looking for spans that are visible and whose bounds // intercect the shadow volume. plSpaceTree* space = drawable->GetSpaceTree(); static hsBitVector cache; cache.Clear(); space->EnableLeaves(visList, cache); static hsTArray<int16_t> hitList; hitList.SetCount(0); space->HarvestEnabledLeaves(slave->fIsect, cache, hitList); // For the visible spans that intercect the shadow volume, attach the shadow // to all appropriate for receiving this shadow map. for (size_t i = 0; i < hitList.GetCount(); i++) { const plSpan* span = drawable->GetSpan(hitList[i]); hsGMaterial* mat = drawable->GetMaterial(span->fMaterialIdx); // Check that the span isn't flagged as unshadowable, or has // a material that we can't shadow onto. if (!IReceivesShadows(span, mat)) continue; // Check for self shadowing. If the shadow doesn't want self shadowing, // and the span is part of the shadow caster, then skip. if (!IAcceptsShadow(span, slave)) continue; // Add it to this span's shadow list for this frame. span->AddShadowSlave(fShadows[which]->fIndex); } }
void plPipelineViewSettings::GetVisibleSpans(plDrawableSpans* drawable, hsTArray<int16_t>& visList, plVisMgr* visMgr) { static hsTArray<int16_t> tmpVis; tmpVis.SetCount(0); visList.SetCount(0); drawable->GetSpaceTree()->SetViewPos(GetViewPositionWorld()); drawable->GetSpaceTree()->Refresh(); if (fCullTreeDirty) RefreshCullTree(); const float viewDist = GetViewDirWorld().InnerProduct(GetViewPositionWorld()); const hsTArray<plSpan *> &spans = drawable->GetSpanArray(); plProfile_BeginTiming(Harvest); if (visMgr) { drawable->SetVisSet(visMgr); fCullTree.Harvest(drawable->GetSpaceTree(), tmpVis); drawable->SetVisSet(nullptr); } else { fCullTree.Harvest(drawable->GetSpaceTree(), tmpVis); } // This is a big waste of time, As a desparate "optimization" pass, the artists // insist on going through and marking objects to fade or pop out of rendering // past a certain distance. This breaks the batching and requires more CPU to // check the objects by distance. Since there is no pattern to the distance at // which objects will be told not to draw, there's no way to make this hierarchical, // which is what it would take to make it a performance win. So they succeed in // reducing the poly count, but generally the frame rate goes _down_ as well. // Unfortunately, this technique actually does work in a few key areas, so // I haven't been able to purge it. if (fPipeline->IsDebugFlagSet(plPipeDbg::kFlagSkipVisDist)) { int i; for (i = 0; i < tmpVis.GetCount(); i++) { if (spans[tmpVis[i]]->fSubType & fSubDrawableTypeMask) { visList.Append(tmpVis[i]); } } } else { int i; for (i = 0; i < tmpVis.GetCount(); i++) { if (spans[tmpVis[i]]->fSubType & fSubDrawableTypeMask) { // We'll check here for spans we can discard because they've completely distance faded out. // Note this is based on view direction distance (because the fade is), rather than the // preferrable distance to camera we sort by. float minDist, maxDist; if (drawable->GetSubVisDists(tmpVis[i], minDist, maxDist)) { const hsBounds3Ext& bnd = drawable->GetSpaceTree()->GetNode(tmpVis[i]).fWorldBounds; hsPoint2 depth; bnd.TestPlane(GetViewDirWorld(), depth); if ((0 < minDist + viewDist - depth.fY) ||(0 > maxDist + viewDist - depth.fX)) continue; } visList.Append(tmpVis[i]); } } } plProfile_EndTiming(Harvest); }
void pl3DPipeline::ICheckLighting(plDrawableSpans* drawable, hsTArray<int16_t>& visList, plVisMgr* visMgr) { if (fView.fRenderState & kRenderNoLights) return; if (!visList.GetCount()) return; plLightInfo* light; plProfile_BeginTiming(FindLights); // First add in the explicit lights (from LightGroups). // Refresh the lights as they are added (actually a lazy eval). plProfile_BeginTiming(FindPerm); for (size_t j = 0; j < visList.GetCount(); j++) { drawable->GetSpan(visList[j])->ClearLights(); if (IsDebugFlagSet(plPipeDbg::kFlagNoRuntimeLights)) continue; // Set the bits for the lights added from the permanent lists (during ClearLights()). const hsTArray<plLightInfo*>& permaLights = drawable->GetSpan(visList[j])->fPermaLights; for (size_t k = 0; k < permaLights.GetCount(); k++) { permaLights[k]->Refresh(); if (permaLights[k]->GetProperty(plLightInfo::kLPShadowLightGroup) && !permaLights[k]->IsIdle()) { // If it casts a shadow, attach the shadow now. ISetShadowFromGroup(drawable, drawable->GetSpan(visList[j]), permaLights[k]); } } const hsTArray<plLightInfo*>& permaProjs = drawable->GetSpan(visList[j])->fPermaProjs; for (size_t k = 0; k < permaProjs.GetCount(); k++) { permaProjs[k]->Refresh(); if (permaProjs[k]->GetProperty(plLightInfo::kLPShadowLightGroup) && !permaProjs[k]->IsIdle()) { // If it casts a shadow, attach the shadow now. ISetShadowFromGroup(drawable, drawable->GetSpan(visList[j]), permaProjs[k]); } } } plProfile_EndTiming(FindPerm); if (IsDebugFlagSet(plPipeDbg::kFlagNoRuntimeLights)) { plProfile_EndTiming(FindLights); return; } // Sort the incoming spans as either // A) moving - affected by all lights - moveList // B) specular - affected by specular lights - specList // C) visible - affected by moving lights - visList static hsTArray<int16_t> tmpList; static hsTArray<int16_t> moveList; static hsTArray<int16_t> specList; moveList.SetCount(0); specList.SetCount(0); plProfile_BeginTiming(FindSpan); for (size_t k = 0; k < visList.GetCount(); k++) { const plSpan* span = drawable->GetSpan(visList[k]); if (span->fProps & plSpan::kPropRunTimeLight) { moveList.Append(visList[k]); specList.Append(visList[k]); } else if (span->fProps & plSpan::kPropMatHasSpecular) { specList.Append(visList[k]); } } plProfile_EndTiming(FindSpan); // Make a list of lights that can potentially affect spans in this drawable // based on the drawables bounds and properties. // If the drawable has the PropCharacter property, it is affected by lights // in fCharLights, else only by the smaller list of fVisLights. plProfile_BeginTiming(FindActiveLights); static hsTArray<plLightInfo*> lightList; lightList.SetCount(0); if (drawable->GetNativeProperty(plDrawable::kPropCharacter)) { for (size_t i = 0; i < fCharLights.GetCount(); i++) { if (fCharLights[i]->AffectsBound(drawable->GetSpaceTree()->GetWorldBounds())) lightList.Append(fCharLights[i]); } } else { for (size_t i = 0; i < fVisLights.GetCount(); i++) { if (fVisLights[i]->AffectsBound(drawable->GetSpaceTree()->GetWorldBounds())) lightList.Append(fVisLights[i]); } } plProfile_EndTiming(FindActiveLights); // Loop over the lights and for each light, extract a list of the spans that light // affects. Append the light to each spans list with a scalar strength of how strongly // the light affects it. Since the strength is based on the object's center position, // it's not very accurate, but good enough for selecting which lights to use. plProfile_BeginTiming(ApplyActiveLights); for (size_t k = 0; k < lightList.GetCount(); k++) { light = lightList[k]; tmpList.SetCount(0); if (light->GetProperty(plLightInfo::kLPMovable)) { plProfile_BeginTiming(ApplyMoving); const hsTArray<int16_t>& litList = light->GetAffected(drawable->GetSpaceTree(), visList, tmpList, drawable->GetNativeProperty(plDrawable::kPropCharacter)); // PUT OVERRIDE FOR KILLING PROJECTORS HERE!!!! bool proj = nil != light->GetProjection(); if (fView.fRenderState & kRenderNoProjection) proj = false; for (size_t j = 0; j < litList.GetCount(); j++) { // Use the light IF light is enabled and // 1) light is movable // 2) span is movable, or // 3) Both the light and the span have specular const plSpan* span = drawable->GetSpan(litList[j]); bool currProj = proj; if (span->fProps & plSpan::kPropProjAsVtx) currProj = false; if (!(currProj && (span->fProps & plSpan::kPropSkipProjection))) { float strength, scale; light->GetStrengthAndScale(span->fWorldBounds, strength, scale); // We can't pitch a light because it's "strength" is zero, because the strength is based // on the center of the span and isn't conservative enough. We can pitch based on the // scale though, since a light scaled down to zero will have no effect no where. if (scale > 0) { plProfile_Inc(FindLightsFound); span->AddLight(light, strength, scale, currProj); } } } plProfile_EndTiming(ApplyMoving); } else if (light->GetProperty(plLightInfo::kLPHasSpecular)) { if (!specList.GetCount()) continue; plProfile_BeginTiming(ApplyToSpec); const hsTArray<int16_t>& litList = light->GetAffected(drawable->GetSpaceTree(), specList, tmpList, drawable->GetNativeProperty(plDrawable::kPropCharacter)); // PUT OVERRIDE FOR KILLING PROJECTORS HERE!!!! bool proj = nil != light->GetProjection(); if (fView.fRenderState & kRenderNoProjection) proj = false; for (size_t j = 0; j < litList.GetCount(); j++) { // Use the light IF light is enabled and // 1) light is movable // 2) span is movable, or // 3) Both the light and the span have specular const plSpan* span = drawable->GetSpan(litList[j]); bool currProj = proj; if (span->fProps & plSpan::kPropProjAsVtx) currProj = false; if (!(currProj && (span->fProps & plSpan::kPropSkipProjection))) { float strength, scale; light->GetStrengthAndScale(span->fWorldBounds, strength, scale); // We can't pitch a light because it's "strength" is zero, because the strength is based // on the center of the span and isn't conservative enough. We can pitch based on the // scale though, since a light scaled down to zero will have no effect no where. if (scale > 0) { plProfile_Inc(FindLightsFound); span->AddLight(light, strength, scale, currProj); } } } plProfile_EndTiming(ApplyToSpec); } else { if (!moveList.GetCount()) continue; plProfile_BeginTiming(ApplyToMoving); const hsTArray<int16_t>& litList = light->GetAffected(drawable->GetSpaceTree(), moveList, tmpList, drawable->GetNativeProperty(plDrawable::kPropCharacter)); // PUT OVERRIDE FOR KILLING PROJECTORS HERE!!!! bool proj = nil != light->GetProjection(); if (fView.fRenderState & kRenderNoProjection) proj = false; for (size_t j = 0; j < litList.GetCount(); j++) { // Use the light IF light is enabled and // 1) light is movable // 2) span is movable, or // 3) Both the light and the span have specular const plSpan* span = drawable->GetSpan(litList[j]); bool currProj = proj; if (span->fProps & plSpan::kPropProjAsVtx) currProj = false; if (!(currProj && (span->fProps & plSpan::kPropSkipProjection))) { float strength, scale; light->GetStrengthAndScale(span->fWorldBounds, strength, scale); // We can't pitch a light because it's "strength" is zero, because the strength is based // on the center of the span and isn't conservative enough. We can pitch based on the // scale though, since a light scaled down to zero will have no effect no where. if (scale > 0) { plProfile_Inc(FindLightsFound); span->AddLight(light, strength, scale, currProj); } } } plProfile_EndTiming(ApplyToMoving); } } plProfile_EndTiming(ApplyActiveLights); IAttachShadowsToReceivers(drawable, visList); plProfile_EndTiming(FindLights); }
plCullNode::plCullStatus plCullNode::ISplitPoly(const plCullPoly& poly, plCullPoly*& innerPoly, plCullPoly*& outerPoly) const { static hsTArray<float> depths; depths.SetCount(poly.fVerts.GetCount()); static hsBitVector onVerts; onVerts.Clear(); hsBool someInner = false; hsBool someOuter = false; hsBool someOn = false; int i; for( i = 0; i < poly.fVerts.GetCount(); i++ ) { depths[i] = fNorm.InnerProduct(poly.fVerts[i]) + fDist; if( depths[i] < -kTolerance ) someInner = true; else if( depths[i] > kTolerance ) someOuter = true; else { someOn = true; onVerts.SetBit(i); } } if( !(someInner || someOuter) ) { (innerPoly = ScratchPolys().Push())->Init(poly); (outerPoly = ScratchPolys().Push())->Init(poly); return kSplit; } else if( !someInner ) { IMarkClipped(poly, onVerts); return kClear; } else if( !someOuter ) { IMarkClipped(poly, onVerts); return kCulled; } // Okay, it's split, now break it into the two polys (innerPoly = ScratchPolys().Push())->Init(poly); (outerPoly = ScratchPolys().Push())->Init(poly); static plCullPoly scrPoly; static hsBitVector inVerts; static hsBitVector outVerts; IBreakPoly(poly, depths, inVerts, outVerts, onVerts, scrPoly); static hsTArray<int> inPolyIdx; inPolyIdx.SetCount(0); static hsTArray<int> outPolyIdx; outPolyIdx.SetCount(0); for( i = 0; i < scrPoly.fVerts.GetCount(); i++ ) { if( inVerts.IsBitSet(i) ) { inPolyIdx.Append(i); } else if( outVerts.IsBitSet(i) ) { outPolyIdx.Append(i); } else { inPolyIdx.Append(i); outPolyIdx.Append(i); } } ITakeHalfPoly(scrPoly, inPolyIdx, onVerts, *innerPoly); ITakeHalfPoly(scrPoly, outPolyIdx, onVerts, *outerPoly); return kSplit; }
plMsgWrap& ClearReceivers() { fReceivers.SetCount(0); return *this; }