1 #region Copyright & License Information 2 /* 3 * Copyright 2007-2020 The OpenRA Developers (see AUTHORS) 4 * This file is part of OpenRA, which is free software. It is made 5 * available to you under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, either version 3 of 7 * the License, or (at your option) any later version. For more 8 * information, see COPYING. 9 */ 10 #endregion 11 12 using System; 13 using System.Collections.Generic; 14 using System.Linq; 15 using OpenRA.Graphics; 16 using OpenRA.Mods.Common.Graphics; 17 using OpenRA.Primitives; 18 using OpenRA.Traits; 19 20 namespace OpenRA.Mods.Common.Traits.Render 21 { 22 public interface IRenderActorPreviewVoxelsInfo : ITraitInfo 23 { RenderPreviewVoxels( ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p)24 IEnumerable<ModelAnimation> RenderPreviewVoxels( 25 ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func<WRot> orientation, int facings, PaletteReference p); 26 } 27 28 public class RenderVoxelsInfo : ITraitInfo, IRenderActorPreviewInfo, Requires<BodyOrientationInfo> 29 { 30 [Desc("Defaults to the actor name.")] 31 public readonly string Image = null; 32 33 [Desc("Custom palette name")] 34 [PaletteReference] 35 public readonly string Palette = null; 36 37 [PaletteReference] 38 [Desc("Custom PlayerColorPalette: BaseName")] 39 public readonly string PlayerPalette = "player"; 40 41 [PaletteReference] 42 public readonly string NormalsPalette = "normals"; 43 44 [PaletteReference] 45 public readonly string ShadowPalette = "shadow"; 46 47 [Desc("Change the image size.")] 48 public readonly float Scale = 12; 49 50 public readonly WAngle LightPitch = WAngle.FromDegrees(50); 51 public readonly WAngle LightYaw = WAngle.FromDegrees(240); 52 public readonly float[] LightAmbientColor = { 0.6f, 0.6f, 0.6f }; 53 public readonly float[] LightDiffuseColor = { 0.4f, 0.4f, 0.4f }; 54 Create(ActorInitializer init)55 public virtual object Create(ActorInitializer init) { return new RenderVoxels(init.Self, this); } 56 RenderPreview(ActorPreviewInitializer init)57 public virtual IEnumerable<IActorPreview> RenderPreview(ActorPreviewInitializer init) 58 { 59 var body = init.Actor.TraitInfo<BodyOrientationInfo>(); 60 var faction = init.Get<FactionInit, string>(); 61 var ownerName = init.Get<OwnerInit>().PlayerName; 62 var sequenceProvider = init.World.Map.Rules.Sequences; 63 var image = Image ?? init.Actor.Name; 64 var facings = body.QuantizedFacings == -1 ? 65 init.Actor.TraitInfo<IQuantizeBodyOrientationInfo>().QuantizedBodyFacings(init.Actor, sequenceProvider, faction) : 66 body.QuantizedFacings; 67 var palette = init.WorldRenderer.Palette(Palette ?? PlayerPalette + ownerName); 68 69 var components = init.Actor.TraitInfos<IRenderActorPreviewVoxelsInfo>() 70 .SelectMany(rvpi => rvpi.RenderPreviewVoxels(init, this, image, init.GetOrientation(), facings, palette)) 71 .ToArray(); 72 73 yield return new ModelPreview(components, WVec.Zero, 0, Scale, LightPitch, 74 LightYaw, LightAmbientColor, LightDiffuseColor, body.CameraPitch, 75 palette, init.WorldRenderer.Palette(NormalsPalette), init.WorldRenderer.Palette(ShadowPalette)); 76 } 77 } 78 79 public class RenderVoxels : IRender, ITick, INotifyOwnerChanged 80 { 81 class AnimationWrapper 82 { 83 readonly ModelAnimation model; 84 bool cachedVisible; 85 WVec cachedOffset; 86 AnimationWrapper(ModelAnimation model)87 public AnimationWrapper(ModelAnimation model) 88 { 89 this.model = model; 90 } 91 Tick()92 public bool Tick() 93 { 94 // Return to the caller whether the renderable position or size has changed 95 var visible = model.IsVisible; 96 var offset = model.OffsetFunc != null ? model.OffsetFunc() : WVec.Zero; 97 98 var updated = visible != cachedVisible || offset != cachedOffset; 99 cachedVisible = visible; 100 cachedOffset = offset; 101 102 return updated; 103 } 104 } 105 106 public readonly RenderVoxelsInfo Info; 107 108 readonly List<ModelAnimation> components = new List<ModelAnimation>(); 109 readonly Dictionary<ModelAnimation, AnimationWrapper> wrappers = new Dictionary<ModelAnimation, AnimationWrapper>(); 110 111 readonly Actor self; 112 readonly BodyOrientation body; 113 readonly WRot camera; 114 readonly WRot lightSource; 115 RenderVoxels(Actor self, RenderVoxelsInfo info)116 public RenderVoxels(Actor self, RenderVoxelsInfo info) 117 { 118 this.self = self; 119 Info = info; 120 body = self.Trait<BodyOrientation>(); 121 camera = new WRot(WAngle.Zero, body.CameraPitch - new WAngle(256), new WAngle(256)); 122 lightSource = new WRot(WAngle.Zero, new WAngle(256) - info.LightPitch, info.LightYaw); 123 } 124 125 bool initializePalettes = true; OnOwnerChanged(Actor self, Player oldOwner, Player newOwner)126 public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { initializePalettes = true; } 127 ITick.Tick(Actor self)128 void ITick.Tick(Actor self) 129 { 130 var updated = false; 131 foreach (var w in wrappers.Values) 132 updated |= w.Tick(); 133 134 if (updated) 135 self.World.ScreenMap.AddOrUpdate(self); 136 } 137 138 protected PaletteReference colorPalette, normalsPalette, shadowPalette; IRender.Render(Actor self, WorldRenderer wr)139 IEnumerable<IRenderable> IRender.Render(Actor self, WorldRenderer wr) 140 { 141 if (initializePalettes) 142 { 143 var paletteName = Info.Palette ?? Info.PlayerPalette + self.Owner.InternalName; 144 colorPalette = wr.Palette(paletteName); 145 normalsPalette = wr.Palette(Info.NormalsPalette); 146 shadowPalette = wr.Palette(Info.ShadowPalette); 147 initializePalettes = false; 148 } 149 150 return new IRenderable[] 151 { 152 new ModelRenderable( 153 components, self.CenterPosition, 0, camera, Info.Scale, 154 lightSource, Info.LightAmbientColor, Info.LightDiffuseColor, 155 colorPalette, normalsPalette, shadowPalette) 156 }; 157 } 158 IRender.ScreenBounds(Actor self, WorldRenderer wr)159 IEnumerable<Rectangle> IRender.ScreenBounds(Actor self, WorldRenderer wr) 160 { 161 var pos = self.CenterPosition; 162 foreach (var c in components) 163 if (c.IsVisible) 164 yield return c.ScreenBounds(pos, wr, Info.Scale); 165 } 166 167 public string Image { get { return Info.Image ?? self.Info.Name; } } Add(ModelAnimation m)168 public void Add(ModelAnimation m) 169 { 170 components.Add(m); 171 wrappers.Add(m, new AnimationWrapper(m)); 172 } 173 Remove(ModelAnimation m)174 public void Remove(ModelAnimation m) 175 { 176 components.Remove(m); 177 wrappers.Remove(m); 178 } 179 } 180 } 181