/* * Copyright (C) 2012-2017 CypherCore * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using Framework.GameMath; using System; using System.Collections.Generic; using System.IO; namespace Game.Collision { public class StaticModelList { public static Dictionary models = new Dictionary(); } public class GameObjectModelOwnerBase { public virtual bool IsSpawned() { return false; } public virtual uint GetDisplayId() { return 0; } public virtual bool IsInPhase(List phases) { return false; } public virtual Vector3 GetPosition() { return Vector3.Zero; } public virtual float GetOrientation() { return 0.0f; } public virtual float GetScale() { return 1.0f; } public virtual void DebugVisualizeCorner(Vector3 corner) { } } public class GameObjectModel : IModel { bool initialize(GameObjectModelOwnerBase modelOwner) { var it = StaticModelList.models.LookupByKey(modelOwner.GetDisplayId()); if (it == null) return false; AxisAlignedBox mdl_box = new AxisAlignedBox(it.bound); // ignore models with no bounds if (mdl_box == AxisAlignedBox.Zero()) { Log.outError(LogFilter.Server, "GameObject model {0} has zero bounds, loading skipped", it.name); return false; } iModel = Global.VMapMgr.acquireModelInstance(it.name); if (iModel == null) return false; name = it.name; iPos = modelOwner.GetPosition(); iScale = modelOwner.GetScale(); iInvScale = 1.0f / iScale; Matrix3 iRotation = Matrix3.fromEulerAnglesZYX(modelOwner.GetOrientation(), 0, 0); iInvRot = iRotation.inverse(); // transform bounding box: mdl_box = new AxisAlignedBox(mdl_box.Lo * iScale, mdl_box.Hi * iScale); AxisAlignedBox rotated_bounds = new AxisAlignedBox(); for (int i = 0; i < 8; ++i) rotated_bounds.merge(iRotation * mdl_box.corner(i)); iBound = rotated_bounds + iPos; owner = modelOwner; return true; } public static GameObjectModel Create(GameObjectModelOwnerBase modelOwner) { GameObjectModel mdl = new GameObjectModel(); if (!mdl.initialize(modelOwner)) return null; return mdl; } public bool intersectRay(Ray ray, ref float maxDist, bool stopAtFirstHit, List phases) { if (!isCollisionEnabled() || !owner.IsSpawned()) return false; if (!owner.IsInPhase(phases)) return false; float time = ray.intersectionTime(iBound); if (time == float.PositiveInfinity) return false; // child bounds are defined in object space: Vector3 p = iInvRot * (ray.Origin - iPos) * iInvScale; Ray modRay = new Ray(p, iInvRot * ray.Direction); float distance = maxDist * iInvScale; bool hit = iModel.IntersectRay(modRay, ref distance, stopAtFirstHit); if (hit) { distance *= iScale; maxDist = distance; } return hit; } public bool UpdatePosition() { if (iModel == null) return false; var it = StaticModelList.models.LookupByKey(owner.GetDisplayId()); if (it == null) return false; AxisAlignedBox mdl_box = new AxisAlignedBox(it.bound); // ignore models with no bounds if (mdl_box == AxisAlignedBox.Zero()) { Log.outError(LogFilter.Server, "GameObject model {0} has zero bounds, loading skipped", it.name); return false; } iPos = owner.GetPosition(); Matrix3 iRotation = Matrix3.fromEulerAnglesZYX(owner.GetOrientation(), 0, 0); iInvRot = iRotation.inverse(); // transform bounding box: mdl_box = new AxisAlignedBox(mdl_box.Lo * iScale, mdl_box.Hi * iScale); AxisAlignedBox rotated_bounds = new AxisAlignedBox(); for (int i = 0; i < 8; ++i) rotated_bounds.merge(iRotation * mdl_box.corner(i)); iBound = rotated_bounds + iPos; return true; } public override Vector3 getPosition() { return iPos; } public override AxisAlignedBox getBounds() { return iBound; } public void enableCollision(bool enable) { _collisionEnabled = enable; } bool isCollisionEnabled() { return _collisionEnabled; } public static void LoadGameObjectModelList() { uint oldMSTime = Time.GetMSTime(); var filename = Global.WorldMgr.GetDataPath() + "/vmaps/GameObjectModels.dtree"; if (!File.Exists(filename)) { Log.outWarn(LogFilter.Server, "Unable to open '{0}' file.", filename); return; } try { using (BinaryReader reader = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) { uint name_length, displayId; string name; while (true) { if (reader.BaseStream.Position >= reader.BaseStream.Length) break; Vector3 v1, v2; displayId = reader.ReadUInt32(); name_length = reader.ReadUInt32(); name = reader.ReadString((int)name_length); v1 = reader.ReadStruct(); v2 = reader.ReadStruct(); StaticModelList.models.Add(displayId, new GameobjectModelData(name, new AxisAlignedBox(v1, v2))); } } } catch (EndOfStreamException ex) { Log.outException(ex); } Log.outInfo(LogFilter.ServerLoading, "Loaded {0} GameObject models in {1} ms", StaticModelList.models.Count, Time.GetMSTimeDiffToNow(oldMSTime)); } string name; bool _collisionEnabled; AxisAlignedBox iBound; Matrix3 iInvRot; Vector3 iPos; float iInvScale; float iScale; WorldModel iModel; GameObjectModelOwnerBase owner; } public class GameobjectModelData { public GameobjectModelData(string name_, AxisAlignedBox box) { bound = box; name = name_; } public AxisAlignedBox bound; public string name; } }