public override idRenderModel InstantiateDynamicModel(idRenderEntity renderEntity, View view, idRenderModel cachedModel) { if (this.Disposed == true) { throw new ObjectDisposedException(this.GetType().Name); } idConsole.Warning("TODO: idRenderModel_MD5.InstantiateDynamicModel"); return(null); }
public override idRenderModel InstantiateDynamicModel(idRenderEntity renderEntity, View view, idRenderModel cachedModel) { if (cachedModel != null) { cachedModel.Dispose(); } idConsole.Error("InstantiateDynamicModel called on static model '{0}'", this.Name); return(null); }
/// <summary> /// Creates a static model based on the definition and view currently. /// </summary> /// <remarks> /// This will be regenerated for every view, even though /// some models, like character meshes, could be used for multiple (mirror) /// views in a frame, or may stay static for multiple frames (corpses) /// The renderer will delete the returned dynamic model the next view. /// </remarks> /// <param name="renderEntity"></param> /// <param name="view"></param> /// <param name="cachedModel"></param> /// <returns></returns> public abstract idRenderModel InstantiateDynamicModel(idRenderEntity renderEntity, View view, idRenderModel cachedModel);
public override idRenderModel InstantiateDynamicModel(idRenderEntity renderEntity, View view, idRenderModel cachedModel) { if(cachedModel != null) { cachedModel.Dispose(); } idConsole.Error("InstantiateDynamicModel called on static model '{0}'", this.Name); return null; }
public override idRenderModel InstantiateDynamicModel(idRenderEntity renderEntity, View view, idRenderModel cachedModel) { if(this.Disposed == true) { throw new ObjectDisposedException(this.GetType().Name); } idConsole.Warning("TODO: idRenderModel_MD5.InstantiateDynamicModel"); return null; }
private void AddWorldModelEntities() { // add the world model for each portal area // we can't just call AddEntityDef, because that would place the references // based on the bounding box, rather than explicitly into the correct area for(int i = 0; i < _portalAreaCount; i++) { idRenderEntity def = new idRenderEntity(); int index = _entityDefinitions.FindIndex(x => x == null); if(index == -1) { index = _entityDefinitions.Count; _entityDefinitions.Add(def); } else { _entityDefinitions[index] = def; } def.EntityIndex = index; def.World = this; def.Parameters.Model = idE.RenderModelManager.FindModel(string.Format("_area{0}", i)); if((def.Parameters.Model.IsDefault == true) || (def.Parameters.Model.IsStaticWorld == false)) { idConsole.Error("idRenderWorld::InitFromMap: bad area model lookup"); } idRenderModel model = def.Parameters.Model; for(int j = 0; j < model.SurfaceCount; j++) { RenderModelSurface surf = model.GetSurface(j); if(surf.Material.Name == "textures/smf/portal_sky") { def.NeedsPortalSky = true; } } def.ReferenceBounds = def.Parameters.Model.GetBounds(); def.Parameters.Axis = new Matrix( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0); def.ModelMatrix = idHelper.AxisToModelMatrix(def.Parameters.Axis, def.Parameters.Origin); // in case an explicit shader is used on the world, we don't // want it to have a 0 alpha or color def.Parameters.MaterialParameters[0] = def.Parameters.MaterialParameters[1] = def.Parameters.MaterialParameters[2] = def.Parameters.MaterialParameters[3] = 1; AddEntityRefToArea(def, _portalAreas[i]); } }
private void PreRender() { if(_needsRender == true) { _world.InitFromMap(null); idDict spawnArgs = new idDict(); spawnArgs.Set("classname", "light"); spawnArgs.Set("name", "light_1"); spawnArgs.Set("origin", _lightOrigin); spawnArgs.Set("_color", _lightColor); idConsole.Warning("TODO: lighting"); /*gameEdit->ParseSpawnArgsToRenderLight(&spawnArgs, &rLight); lightDef = world->AddLightDef(&rLight); if(!modelName[0]) { common->Warning("Window '%s' in gui '%s': no model set", GetName(), GetGui()->GetSourceFile()); }*/ spawnArgs.Clear(); spawnArgs.Set("classname", "func_static"); spawnArgs.Set("model", _modelName); spawnArgs.Set("origin", _modelOrigin); _worldEntity = idE.GameEdit.ParseSpawnArgsToRenderEntity(spawnArgs); if(_worldEntity.Model != null) { Vector3 v = _modelRotate.ToVector3(); _worldEntity.Axis = v.ToMatrix(); _worldEntity.MaterialParameters[0] = 1; _worldEntity.MaterialParameters[0] = 1; _worldEntity.MaterialParameters[0] = 1; _worldEntity.MaterialParameters[0] = 1; _renderEntity = _world.AddEntityDefinition(_worldEntity); } _needsRender.Set(false); } }
protected bool UpdateRenderEntity(idRenderEntity renderEntity, idRenderView renderView) { // TODO: cinematic /*if ( gameLocal.inCinematic && gameLocal.skipCinematic ) { return false; }*/ idAnimator animator = this.Animator; if(animator != null) { return animator.CreateFrame(idR.Game.Time, false); } return false; }
/// <summary> /// If the entityDef isn't already on the viewEntity list, create /// a viewEntity and add it to the list with an empty scissor rect. /// </summary> /// <remarks> /// This does not instantiate dynamic models for the entity yet. /// </remarks> /// <param name="def"></param> /// <returns></returns> private ViewEntity SetEntityDefViewEntity(idRenderEntity def) { if(def.ViewCount == idE.RenderSystem.ViewCount) { return def.ViewEntity; } def.ViewCount = idE.RenderSystem.ViewCount; // set the model and modelview matricies ViewEntity viewModel = new ViewEntity(); viewModel.EntityDef = def; // the scissorRect will be expanded as the model bounds is accepted into visible portal chains viewModel.ScissorRectangle.Clear(); // copy the model and weapon depth hack for back-end use viewModel.ModelDepthHack = def.Parameters.ModelDepthHack; viewModel.WeaponDepthHack = def.Parameters.WeaponDepthHack; viewModel.ModelMatrix = idHelper.AxisToModelMatrix(def.Parameters.Axis, def.Parameters.Origin); // we may not have a viewDef if we are just creating shadows at entity creation time if(idE.RenderSystem.ViewDefinition != null) { idHelper.ConvertMatrix(viewModel.ModelMatrix, idE.RenderSystem.ViewDefinition.WorldSpace.ModelViewMatrix, out viewModel.ModelViewMatrix); idE.RenderSystem.ViewDefinition.ViewEntities = new List<ViewEntity>(); idE.RenderSystem.ViewDefinition.ViewEntities.Add(viewModel); } def.ViewEntity = viewModel; return viewModel; }
/// <remarks> /// Used for both light volumes and model volumes. /// <para/> /// This does not clip the points by the planes, so some slop occurs. /// <para/> /// tr.viewCount should be bumped before calling, allowing it /// to prevent double checking areas. /// <para/> /// We might alternatively choose to do this with an area flow. /// </remarks> /// <param name="def"></param> /// <param name="?"></param> /// <param name="sphere"></param> /// <param name="pointCount"></param> /// <param name="points"></param> private void PushVolumeIntoTree_r(idRenderEntity def, /* idRenderLight */ object light, idSphere sphere, int pointCount, Vector3[] points, int nodeNumber) { if(nodeNumber < 0) { int areaNumber = -1 - nodeNumber; PortalArea area = _portalAreas[areaNumber]; if(area.ViewCount == idE.RenderSystem.ViewCount) { return; // already added a reference here } area.ViewCount = idE.RenderSystem.ViewCount; if(def != null) { AddEntityRefToArea(def, area); } if(light != null) { idConsole.Warning("TODO: AddLightRefToArea( light, area );"); } return; } AreaNode node = _areaNodes[nodeNumber]; // if we know that all possible children nodes only touch an area // we have already marked, we can early out if((idE.CvarSystem.GetBool("r_useNodeCommonChildren") == true) && (node.CommonChildrenArea != idRenderWorld.ChildrenHaveMultipleAreas)) { // note that we do NOT try to set a reference in this area // yet, because the test volume may yet wind up being in the // solid part, which would cause bounds slightly poked into // a wall to show up in the next room if(_portalAreas[node.CommonChildrenArea].ViewCount == idE.RenderSystem.ViewCount) { return; } } // if the bounding sphere is completely on one side, don't // bother checking the individual points float distance = node.Plane.Distance(sphere.Origin); if(distance >= sphere.Radius) { nodeNumber = node.Children[0]; if(nodeNumber != 0) // 0 = solid { PushVolumeIntoTree_r(def, light, sphere, pointCount, points, nodeNumber); } return; } if(distance <= -sphere.Radius) { nodeNumber = node.Children[1]; if(nodeNumber != 0) // 0 = solid { PushVolumeIntoTree_r(def, light, sphere, pointCount, points, nodeNumber); } return; } // exact check all the points against the node plane bool front = false; bool back = false; for(int i = 0; i < pointCount; i++) { float d = ((points[i] * node.Plane.Normal) + new Vector3(node.Plane.Normal.Z, node.Plane.Normal.Z, node.Plane.Normal.Z)).Length(); if(d >= 0.0f) { front = true; } else if(d <= 0.0f) { back = true; } if((back == true) && (front == true)) { break; } } if(front == true) { nodeNumber = node.Children[0]; if(nodeNumber != 0) // 0 = solid { PushVolumeIntoTree_r(def, light, sphere, pointCount, points, nodeNumber); } } if(back == true) { nodeNumber = node.Children[1]; if(nodeNumber != 0) // 0 = solid { PushVolumeIntoTree_r(def, light, sphere, pointCount, points, nodeNumber); } } }
private void PushVolumeIntoTree(idRenderEntity def, /* idRenderLight */ object light, int pointCount, Vector3[] points) { if(_areaNodes == null) { return; } // calculate a bounding sphere for the points Vector3 mid = Vector3.Zero; Vector3 dir; float radSquared = 0; float lr = 0; for(int i = 0; i < pointCount; i++) { mid += points[i]; } mid *= (1.0f / pointCount); for(int i = 0; i < pointCount; i++) { dir = points[i] - mid; lr = (dir * dir).Length(); if(lr > radSquared) { radSquared = lr; } } idSphere sphere = new idSphere(mid, idMath.Sqrt(radSquared)); PushVolumeIntoTree_r(def, light, sphere, pointCount, points, 0); }
private void FreeEntityDefDerivedData(idRenderEntity def, bool keepDecals, bool keepCachedDynamicModel) { // TODO: demo // demo playback needs to free the joints, while normal play // leaves them in the control of the game /*if ( session->readDemo ) { if ( def->parms.joints ) { Mem_Free16( def->parms.joints ); def->parms.joints = NULL; } if ( def->parms.callbackData ) { Mem_Free( def->parms.callbackData ); def->parms.callbackData = NULL; } for ( i = 0; i < MAX_RENDERENTITY_GUI; i++ ) { if ( def->parms.gui[ i ] ) { delete def->parms.gui[ i ]; def->parms.gui[ i ] = NULL; } } }*/ // TODO: interactions // free all the interactions /*while ( def->firstInteraction != NULL ) { def->firstInteraction->UnlinkAndFree(); }*/ // clear the dynamic model if present if(def.DynamicModel != null) { def.DynamicModel = null; } if(keepDecals == false) { idConsole.Warning("TODO: free decals"); /*R_FreeEntityDefDecals( def ); R_FreeEntityDefOverlay( def );*/ } if(keepCachedDynamicModel == false) { if(def.CachedDynamicModel != null) { def.CachedDynamicModel.Dispose(); def.CachedDynamicModel = null; } } // free the entityRefs from the areas AreaReference areaRef, next; for(areaRef = def.EntityReference; areaRef != null; areaRef = next) { next = areaRef.NextOwner; // unlink from the area areaRef.NextArea.PreviousArea = areaRef.PreviousArea; areaRef.PreviousArea.NextArea = areaRef.NextArea; } def.EntityReference = null; }
/// <summary> /// Creates all needed model references in portal areas, chaining them to both the area and the entityDef. /// </summary> /// <remarks> /// Bumps tr.viewCount.. /// </remarks> private void CreateEntityReferences(idRenderEntity def) { if(def.Parameters.Model == null) { def.Parameters.Model = idE.RenderModelManager.DefaultModel; } // if the entity hasn't been fully specified due to expensive animation calcs // for md5 and particles, use the provided conservative bounds. if(def.Parameters.Callback != null) { def.ReferenceBounds = def.Parameters.Bounds; } else { def.ReferenceBounds = def.Parameters.Model.GetBounds(def.Parameters); } // some models, like empty particles, may not need to be added at all if(def.ReferenceBounds.IsCleared == true) { return; } if((idE.CvarSystem.GetBool("r_showUpdates") == true) && (((def.ReferenceBounds.Max.X - def.ReferenceBounds.Min.X) > 1024) || ((def.ReferenceBounds.Max.Y - def.ReferenceBounds.Min.Y) > 1024))) { idConsole.WriteLine("big entityRef: {0}, {1}", def.ReferenceBounds.Max.X - def.ReferenceBounds.Min.X, def.ReferenceBounds.Max.Y - def.ReferenceBounds.Min.Y); } Vector3[] transformed = new Vector3[8]; Vector3 v; for(int i = 0; i < 8; i++) { v.X = ((i & 1) == 0) ? def.ReferenceBounds.Min.X : def.ReferenceBounds.Max.X; v.Y = (((i >> 1) & 1) == 0) ? def.ReferenceBounds.Min.Y : def.ReferenceBounds.Max.Y; v.Z = (((i >> 2) & 1) == 0) ? def.ReferenceBounds.Min.Z : def.ReferenceBounds.Max.Z; idHelper.LocalPointToGlobal(def.ModelMatrix, v, out transformed[i]); } // bump the view count so we can tell if an // area already has a reference idE.RenderSystem.ViewCount++; // push these points down the BSP tree into areas def.World.PushVolumeIntoTree(def, null, 8, transformed); }
/// <summary> /// Return true if the entity reference bounds do not intersect the current portal chain. /// </summary> /// <param name="entity"></param> /// <param name="portalStack"></param> /// <returns></returns> private bool CullEntityByPortals(idRenderEntity entity, PortalStack portalStack) { if(idE.CvarSystem.GetBool("r_useEntityCulling") == false) { return false; } // try to cull the entire thing using the reference bounds. // we do not yet do callbacks or dynamic model creation, // because we want to do all touching of the model after // we have determined all the lights that may effect it, // which optimizes cache usage return idHelper.CullLocalBox(entity.ReferenceBounds, entity.ModelMatrix, portalStack.PortalPlaneCount, portalStack.PortalPlanes); }
/// <summary> /// Present is called to allow entities to generate refEntities, lights, etc for the renderer. /// </summary> public virtual void Present() { if(this.Disposed == true) { throw new ObjectDisposedException(this.GetType().Name); } // TODO: /*if ( !gameLocal.isNewFrame ) { return; }*/ // don't present to the renderer if the entity hasn't changed /*if ( !( thinkFlags & TH_UPDATEVISUALS ) ) { return; } BecomeInactive( TH_UPDATEVISUALS );*/ // camera target for remote render views /*if ( cameraTarget && gameLocal.InPlayerPVS( this ) ) { renderEntity.remoteRenderView = cameraTarget->GetRenderView(); }*/ // if set to invisible, skip if((_renderEntity.Model == null) || (this.IsHidden == true)) { return; } // add to refresh list if(_renderModel == null) { _renderModel = idR.Game.CurrentRenderWorld.AddEntityDefinition(_renderEntity); } else { idR.Game.CurrentRenderWorld.UpdateEntityDefinition(_renderModel, _renderEntity); } }
/// <summary> /// /// </summary> /// <remarks> /// May not change the game state whatsoever! /// </remarks> /// <param name="renderEntity"></param> /// <param name="renderView"></param> /// <returns></returns> protected bool ModelCallback(idRenderEntity renderEntity, idRenderView renderView) { idEntity ent = idR.Game.Entities[renderEntity.EntityIndex]; if(ent == null) { idConsole.Error("idEntity::ModelCallback: callback with null game entity"); } return ent.UpdateRenderEntity(renderEntity, renderView); }
/// <summary> /// Does not write to the demo file, which will only be updated for visible entities. /// </summary> /// <param name="index"></param> /// <param name="renderComponent"></param> public void UpdateEntityDefinition(idRenderEntity renderEntity, RenderEntityComponent renderComponent) { UpdateEntityDefinition(_entityDefinitions[_entityDefinitions.IndexOf(renderEntity)], renderComponent); }
/// <summary> /// Does not write to the demo file, which will only be updated for visible entities. /// </summary> /// <param name="index"></param> /// <param name="renderComponent"></param> public void UpdateEntityDefinition(int index, RenderEntityComponent renderComponent) { if(idE.CvarSystem.GetBool("r_skipUpdates") == true) { return; } // TODO: tr.pc.c_entityUpdates++; if((renderComponent.Model == null) && (renderComponent.Callback == null)) { idConsole.Error("idRenderWorld::UpdateEntityDefinition: NULL model"); } // create new slots if needed if((index < 0) || (index > idE.LudicrousEntityIndex)) { idConsole.Error("idRenderWorld::UpdateEntityDefinitionL index = {0}", index); } while(index >= _entityDefinitions.Count) { _entityDefinitions.Add(null); } idRenderEntity def = _entityDefinitions[index]; if(def != null) { if(renderComponent.ForceUpdate == false) { idConsole.Warning("TODO: force update - quite important!"); /*// check for exact match (OPTIMIZE: check through pointers more) if ( !re->joints && !re->callbackData && !def->dynamicModel && !memcmp( re, &def->parms, sizeof( *re ) ) ) { return; } // if the only thing that changed was shaderparms, we can just leave things as they are // after updating parms // if we have a callback function and the bounds, origin, axis and model match, // then we can leave the references as they are if ( re->callback ) { bool axisMatch = ( re->axis == def->parms.axis ); bool originMatch = ( re->origin == def->parms.origin ); bool boundsMatch = ( re->bounds == def->referenceBounds ); bool modelMatch = ( re->hModel == def->parms.hModel ); if ( boundsMatch && originMatch && axisMatch && modelMatch ) { // only clear the dynamic model and interaction surfaces if they exist c_callbackUpdate++; R_ClearEntityDefDynamicModel( def ); def->parms = *re; return; } }*/ } // save any decals if the model is the same, allowing marks to move with entities if(def.Parameters.Model == renderComponent.Model) { FreeEntityDefDerivedData(def, true, true); } else { FreeEntityDefDerivedData(def, false, false); } } else { // creating a new one def = new idRenderEntity(); _entityDefinitions[index] = def; def.World = this; def.EntityIndex = index; } def.Parameters = renderComponent; def.ModelMatrix = idHelper.AxisToModelMatrix(def.Parameters.Axis, def.Parameters.Origin); def.LastModifiedFrameNumber = idE.RenderSystem.FrameCount; // TODO: demo /*if ( session->writeDemo && def->archived ) { WriteFreeEntity( entityHandle ); def->archived = false; }*/ // optionally immediately issue any callbacks if((idE.CvarSystem.GetBool("r_useEntityCallbacks") == false) && (def.Parameters.Callback != null)) { idConsole.Warning("TODO: R_IssueEntityDefCallback( def );"); } // based on the model bounds, add references in each area // that may contain the updated surface CreateEntityReferences(def); }
/// <summary> /// This is called by R_PushVolumeIntoTree and also directly /// for the world model references that are precalculated. /// </summary> /// <param name="def"></param> /// <param name="area"></param> private void AddEntityRefToArea(idRenderEntity def, PortalArea area) { if(def == null) { idConsole.Error("idRenderWorld::AddEntityRefToArea: null def"); } AreaReference areaRef = new AreaReference(); // TODO: counters tr.pc.c_entityReferences++; areaRef.Entity = def; // link to entityDef areaRef.NextOwner = def.EntityReference; def.EntityReference = areaRef; // link to end of area list areaRef.Area = area; areaRef.NextArea = area.EntityReference; areaRef.PreviousArea = area.EntityReference.PreviousArea; areaRef.NextArea.PreviousArea = areaRef; areaRef.PreviousArea.NextArea = areaRef; }
/// <summary> /// Issues a deferred entity callback if necessary. /// If the model isn't dynamic, it returns the original. /// Returns the cached dynamic model if present, otherwise creates /// it and any necessary overlays /// </summary> /// <param name="def"></param> /// <returns></returns> private idRenderModel EntityDefinitionDynamicModel(idRenderEntity def) { bool callbackUpdate; // allow deferred entities to construct themselves if(def.Parameters.Callback != null) { callbackUpdate = false; idConsole.Warning("TODO: R_IssueEntityDefCallback( def );"); } else { callbackUpdate = false; } idRenderModel model = def.Parameters.Model; if(model == null) { idConsole.Error("EntityDefinitionDynamicModel: null model"); } if(model.IsDynamic == DynamicModel.Static) { def.DynamicModel = null; def.DynamicModelFrameCount = 0; return model; } idConsole.Warning("TODO: dynamic model rendering!"); // continously animating models (particle systems, etc) will have their snapshot updated every single view /*if ( callbackUpdate || ( model->IsDynamicModel() == DM_CONTINUOUS && def->dynamicModelFrameCount != tr.frameCount ) ) { R_ClearEntityDefDynamicModel( def ); } // if we don't have a snapshot of the dynamic model, generate it now if ( !def->dynamicModel ) { // instantiate the snapshot of the dynamic model, possibly reusing memory from the cached snapshot def->cachedDynamicModel = model->InstantiateDynamicModel( &def->parms, tr.viewDef, def->cachedDynamicModel ); if ( def->cachedDynamicModel ) { // add any overlays to the snapshot of the dynamic model if ( def->overlay && !r_skipOverlays.GetBool() ) { def->overlay->AddOverlaySurfacesToModel( def->cachedDynamicModel ); } else { idRenderModelOverlay::RemoveOverlaySurfacesFromModel( def->cachedDynamicModel ); } if ( r_checkBounds.GetBool() ) { idBounds b = def->cachedDynamicModel->Bounds(); if ( b[0][0] < def->referenceBounds[0][0] - CHECK_BOUNDS_EPSILON || b[0][1] < def->referenceBounds[0][1] - CHECK_BOUNDS_EPSILON || b[0][2] < def->referenceBounds[0][2] - CHECK_BOUNDS_EPSILON || b[1][0] > def->referenceBounds[1][0] + CHECK_BOUNDS_EPSILON || b[1][1] > def->referenceBounds[1][1] + CHECK_BOUNDS_EPSILON || b[1][2] > def->referenceBounds[1][2] + CHECK_BOUNDS_EPSILON ) { common->Printf( "entity %i dynamic model exceeded reference bounds\n", def->index ); } } } def->dynamicModel = def->cachedDynamicModel; def->dynamicModelFrameCount = tr.frameCount; } // set model depth hack value if ( def->dynamicModel && model->DepthHack() != 0.0f && tr.viewDef ) { idPlane eye, clip; idVec3 ndc; R_TransformModelToClip( def->parms.origin, tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); R_TransformClipToDevice( clip, tr.viewDef, ndc ); def->parms.modelDepthHack = model->DepthHack() * ( 1.0f - ndc.z ); } */ // FIXME: if any of the surfaces have deforms, create a frame-temporary model with references to the // undeformed surfaces. This would allow deforms to be light interacting. return def.DynamicModel; }