mirror of
https://github.com/Halofreak1990/XFXFramework
synced 2024-12-26 13:49:34 +01:00
Added Curve and related classes
Updated comments Updated List class
This commit is contained in:
parent
940e173334
commit
c1151a1a67
@ -41,7 +41,6 @@ namespace XFX
|
||||
|
||||
static Vector3 ComputeIntersection(Plane plane, Ray ray);
|
||||
static Ray ComputeIntersectionLine(Plane p1, Plane p2);
|
||||
void SetMatrix(Matrix value);
|
||||
void SupportMapping(Vector3 v, out Vector3& result);
|
||||
|
||||
public:
|
||||
|
46
include/Curve.h
Normal file
46
include/Curve.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*****************************************************************************
|
||||
* Curve.h *
|
||||
* *
|
||||
* XFX::Curve class declaration file *
|
||||
* Copyright (c) XFX Team. All Rights Reserved *
|
||||
*****************************************************************************/
|
||||
#ifndef _XFX_CURVE_
|
||||
#define _XFX_CURVE_
|
||||
|
||||
#include <System/Type.h>
|
||||
#include "CurveKeyCollection.h"
|
||||
|
||||
using namespace System;
|
||||
|
||||
namespace XFX
|
||||
{
|
||||
/**
|
||||
* Stores an arbitrary collection of 2D CurveKey points, and provides methods for evaluating features of the curve they define.
|
||||
*/
|
||||
class Curve
|
||||
{
|
||||
private:
|
||||
CurveKeyCollection keys;
|
||||
|
||||
float GetCurvePosition(float position);
|
||||
int GetNumberOfCycle(float position);
|
||||
|
||||
public:
|
||||
bool IsConstant() const;
|
||||
CurveKeyCollection getKeys() const;
|
||||
CurveLoopType_t PostLoop;
|
||||
CurveLoopType_t PreLoop;
|
||||
|
||||
Curve();
|
||||
|
||||
Curve Clone() const;
|
||||
void ComputeTangent(int keyIndex, CurveTangent_t tangentInType, CurveTangent_t tangentOutType);
|
||||
inline void ComputeTangent(int keyIndex, CurveTangent_t tangentType) { ComputeTangent(keyIndex, tangentType, tangentType); }
|
||||
void ComputeTangents(CurveTangent_t tangentInType, CurveTangent_t tangentOutType);
|
||||
inline void ComputeTangents(CurveTangent_t tangentType) { ComputeTangents(tangentType, tangentType); }
|
||||
float Evaluate(float position);
|
||||
static const Type& GetType();
|
||||
};
|
||||
}
|
||||
|
||||
#endif //_XFX_CURVE_
|
48
include/CurveKey.h
Normal file
48
include/CurveKey.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*****************************************************************************
|
||||
* CurveKey.h *
|
||||
* *
|
||||
* XFX::CurveKey class declaration file *
|
||||
* Copyright (c) XFX Team. All Rights Reserved *
|
||||
*****************************************************************************/
|
||||
#ifndef _XFX_CURVEKEY_
|
||||
#define _XFX_CURVEKEY_
|
||||
|
||||
#include <System/Interfaces.h>
|
||||
#include "Enums.h"
|
||||
|
||||
using namespace System;
|
||||
|
||||
namespace XFX
|
||||
{
|
||||
/**
|
||||
* Represents a point in a multi-point curve.
|
||||
*/
|
||||
class CurveKey : public IComparable<CurveKey>, public IEquatable<CurveKey>, public Object
|
||||
{
|
||||
private:
|
||||
float position;
|
||||
|
||||
public:
|
||||
CurveContinuity_t Continuity;
|
||||
float getPosition() const;
|
||||
float TangentIn;
|
||||
float TangentOut;
|
||||
float Value;
|
||||
|
||||
CurveKey(float position, float value);
|
||||
CurveKey(float position, float value, float tangentIn, float tangentOut);
|
||||
CurveKey(float position, float value, float tangentIn, float tangentOut, CurveContinuity_t continuity);
|
||||
|
||||
CurveKey Clone();
|
||||
int CompareTo(const CurveKey other) const;
|
||||
bool Equals(Object const * const obj) const;
|
||||
bool Equals(const CurveKey other) const;
|
||||
int GetHashCode() const;
|
||||
static const Type& GetType();
|
||||
|
||||
bool operator!=(const CurveKey& right) const;
|
||||
bool operator==(const CurveKey& right) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //_XFX_CURVEKEY_
|
48
include/CurveKeyCollection.h
Normal file
48
include/CurveKeyCollection.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*****************************************************************************
|
||||
* CurveKeyCollection.h *
|
||||
* *
|
||||
* XFX::CurveKeyCollection class declaration file *
|
||||
* Copyright (c) XFX Team. All Rights Reserved *
|
||||
*****************************************************************************/
|
||||
#ifndef _XFX_CURVEKEYCOLLECTION_
|
||||
#define _XFX_CURVEKEYCOLLECTION_
|
||||
|
||||
#include "CurveKey.h"
|
||||
#include <System/Collections/Generic/List.h>
|
||||
|
||||
using namespace System;
|
||||
using namespace System::Collections::Generic;
|
||||
|
||||
namespace XFX
|
||||
{
|
||||
/**
|
||||
* Contains the CurveKeys making up a Curve.
|
||||
*/
|
||||
class CurveKeyCollection : public ICollection<CurveKey>
|
||||
{
|
||||
private:
|
||||
bool isReadOnly;
|
||||
List<CurveKey> innerList;
|
||||
|
||||
public:
|
||||
int Count() const;
|
||||
bool IsReadOnly() const;
|
||||
|
||||
CurveKeyCollection();
|
||||
|
||||
void Add(const CurveKey& item);
|
||||
void Clear();
|
||||
CurveKeyCollection Clone() const;
|
||||
bool Contains(const CurveKey& item) const;
|
||||
void CopyTo(CurveKey array[], int arrayIndex) const;
|
||||
static const Type& GetType();
|
||||
int IndexOf(const CurveKey& item) const;
|
||||
bool Remove(const CurveKey& item);
|
||||
void RemoveAt(int index);
|
||||
|
||||
CurveKey& operator [](int index) const;
|
||||
void set(int index, const CurveKey& value); // function to set, because custom action is needed
|
||||
};
|
||||
}
|
||||
|
||||
#endif //_XFX_CURVEKEYCOLLECTION_
|
@ -22,10 +22,25 @@ namespace XFX
|
||||
class Video : public Object
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Gets the duration of the Video.
|
||||
*/
|
||||
TimeSpan getDuration() const;
|
||||
/**
|
||||
* Gets the frame rate of this video.
|
||||
*/
|
||||
float getFramesPerSecond() const;
|
||||
/**
|
||||
* Gets the height of this video, in pixels.
|
||||
*/
|
||||
int getHeight() const;
|
||||
/**
|
||||
* Gets the VideoSoundtrackType for this video.
|
||||
*/
|
||||
VideoSoundtrackType_t getVideoSoundtrackType() const;
|
||||
/**
|
||||
* Gets the width of this video, in pixels.
|
||||
*/
|
||||
int getWidth() const;
|
||||
|
||||
static const Type& GetType();
|
||||
|
@ -15,8 +15,10 @@ namespace XFX
|
||||
|
||||
namespace Net
|
||||
{
|
||||
// Provides common functionality for efficiently reading incoming network packets.
|
||||
class PacketReader : public BinaryReader, public Object
|
||||
/**
|
||||
* Provides common functionality for efficiently reading incoming network packets.
|
||||
*/
|
||||
class PacketReader : public BinaryReader
|
||||
{
|
||||
public:
|
||||
int Length();
|
||||
|
@ -43,26 +43,23 @@ namespace System
|
||||
Array(const Array<T> &obj)
|
||||
: _array(new T[obj.Length]), _version(obj._version), Length(obj.Length)
|
||||
{
|
||||
for (int i = 0; i < Length; i++)
|
||||
{
|
||||
_array[i] = obj._array[i];
|
||||
}
|
||||
memcpy(_array, obj._array, sizeof(T) * Length);
|
||||
}
|
||||
|
||||
~Array() { delete[] _array; }
|
||||
|
||||
template <typename U>
|
||||
static void Clear(U array[], int startIndex, int count)
|
||||
inline static void Clear(U array[], int startIndex, int count)
|
||||
{
|
||||
memset(&array[startIndex], 0, sizeof(U) * count);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
inline void Clear()
|
||||
{
|
||||
Clear(_array, 0, Length);
|
||||
}
|
||||
|
||||
bool Contains(const T item) const
|
||||
inline bool Contains(const T item) const
|
||||
{
|
||||
return (IndexOf(item) != -1);
|
||||
}
|
||||
@ -77,7 +74,7 @@ namespace System
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator<T>* GetEnumerator() const
|
||||
inline IEnumerator<T>* GetEnumerator() const
|
||||
{
|
||||
return new ArrayEnumerator(this);
|
||||
}
|
||||
@ -87,12 +84,15 @@ namespace System
|
||||
for (int i = 0; i < Length; i++)
|
||||
{
|
||||
if (i == item)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Reverse()
|
||||
inline void Reverse()
|
||||
{
|
||||
Reverse(0, Length);
|
||||
}
|
||||
@ -103,6 +103,7 @@ namespace System
|
||||
|
||||
int num = startIndex;
|
||||
int num2 = (startIndex + count) - 1;
|
||||
|
||||
while (num < num2)
|
||||
{
|
||||
swap(_array[num], _array[num2]);
|
||||
@ -113,7 +114,7 @@ namespace System
|
||||
_version++;
|
||||
}
|
||||
|
||||
T& operator[](const int index)
|
||||
inline T& operator[](const int index)
|
||||
{
|
||||
return _array[index];
|
||||
}
|
||||
@ -141,7 +142,7 @@ namespace System
|
||||
{
|
||||
sassert(_version == _array->_version, "");
|
||||
|
||||
return (_position < _array->Length);
|
||||
return _position++ < _array->Length;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
@ -184,12 +185,12 @@ namespace System
|
||||
|
||||
~Array() { delete _array; }
|
||||
|
||||
void Clear()
|
||||
inline void Clear()
|
||||
{
|
||||
memset(_array, 0, sizeof(T *) * Length);
|
||||
}
|
||||
|
||||
bool Contains(const T* item) const
|
||||
inline bool Contains(const T* item) const
|
||||
{
|
||||
return (IndexOf(item) != -1);
|
||||
}
|
||||
@ -204,7 +205,7 @@ namespace System
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator<T *>* GetEnumerator() const
|
||||
inline IEnumerator<T *>* GetEnumerator() const
|
||||
{
|
||||
return new ArrayEnumerator(this);
|
||||
}
|
||||
@ -214,12 +215,15 @@ namespace System
|
||||
for(int i = 0; i < Length; i++)
|
||||
{
|
||||
if (_array[i] == item)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Reverse()
|
||||
inline void Reverse()
|
||||
{
|
||||
Reverse(0, Length);
|
||||
}
|
||||
@ -240,7 +244,7 @@ namespace System
|
||||
_version++;
|
||||
}
|
||||
|
||||
T& operator[](const int index)
|
||||
inline T* operator[](const int index)
|
||||
{
|
||||
return _array[index];
|
||||
}
|
||||
@ -268,7 +272,7 @@ namespace System
|
||||
{
|
||||
sassert(_version == _array->_version, "");
|
||||
|
||||
return _position < _array->Length;
|
||||
return _position++ < _array->Length;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
|
@ -27,7 +27,7 @@ namespace System
|
||||
// Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.
|
||||
// NOTE: types used with the List<T> class must provide at least an == operator.
|
||||
template <typename T>
|
||||
class List : public IList<T>, public Object
|
||||
class List : public IList<T>, public IEnumerable<T>, public Object
|
||||
{
|
||||
private:
|
||||
static const int defaultCapacity = 4;
|
||||
@ -60,6 +60,35 @@ namespace System
|
||||
*y = temp;
|
||||
}
|
||||
|
||||
class Enumerator : public IEnumerator<T>
|
||||
{
|
||||
private:
|
||||
int index = -1;
|
||||
const int version;
|
||||
List<T> const * const parent;
|
||||
|
||||
public:
|
||||
T Current() const { return (*parent)[index]; }
|
||||
|
||||
Enumerator(List<T> const * const parent)
|
||||
: parent(parent), version(parent->_version)
|
||||
{
|
||||
}
|
||||
|
||||
bool MoveNext()
|
||||
{
|
||||
sassert(version == parent->_version, "");
|
||||
|
||||
return index++ < parent->_size;
|
||||
}
|
||||
void Reset()
|
||||
{
|
||||
sassert(version == parent->_version, "");
|
||||
|
||||
index = -1;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
// Gets the number of elements actually contained in the List<>.
|
||||
int Count() const
|
||||
@ -147,6 +176,18 @@ namespace System
|
||||
_version++;
|
||||
}
|
||||
|
||||
void AddRange(IEnumerable<T> const * const collection)
|
||||
{
|
||||
sassert(collection != NULL, "");
|
||||
|
||||
IEnumerator<T>* enumerator = collection->GetEnumerator();
|
||||
|
||||
while (enumerator->MoveNext())
|
||||
{
|
||||
Add(enumerator->Current();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements from the list
|
||||
*/
|
||||
@ -182,6 +223,11 @@ namespace System
|
||||
memcpy(&array[arrayIndex], _items, _size * sizeof(T));
|
||||
}
|
||||
|
||||
Enumerator* GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
static const Type& GetType()
|
||||
{
|
||||
static Type ListTypeInfo("List", "System::Collections::Generic::List", TypeCode::Object, true);
|
||||
|
@ -35,6 +35,7 @@ public:
|
||||
ptr = rhs.ptr;
|
||||
rhs.ptr = NULL;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
@ -110,6 +111,7 @@ public:
|
||||
{
|
||||
rhs.ptr->GetRef();
|
||||
}
|
||||
|
||||
UnBind();
|
||||
ptr = rhs.ptr;
|
||||
return *this;
|
||||
|
@ -77,7 +77,8 @@ namespace XFX
|
||||
cornerArray[5] = Vector3();
|
||||
cornerArray[6] = Vector3();
|
||||
cornerArray[7] = Vector3();
|
||||
SetMatrix(value);
|
||||
|
||||
setMatrix(value);
|
||||
}
|
||||
|
||||
BoundingFrustum::BoundingFrustum(const BoundingFrustum &obj)
|
||||
@ -96,7 +97,8 @@ namespace XFX
|
||||
cornerArray[5] = obj.cornerArray[5];
|
||||
cornerArray[6] = obj.cornerArray[6];
|
||||
cornerArray[7] = obj.cornerArray[7];
|
||||
SetMatrix(obj.matrix);
|
||||
|
||||
setMatrix(obj.matrix);
|
||||
}
|
||||
|
||||
Plane BoundingFrustum::Bottom()
|
||||
@ -121,7 +123,51 @@ namespace XFX
|
||||
|
||||
void BoundingFrustum::setMatrix(Matrix value)
|
||||
{
|
||||
SetMatrix(value);
|
||||
matrix = value;
|
||||
planes[2].Normal.X = -value.M14 - value.M11;
|
||||
planes[2].Normal.Y = -value.M24 - value.M21;
|
||||
planes[2].Normal.Z = -value.M34 - value.M31;
|
||||
planes[2].D = -value.M44 - value.M41;
|
||||
planes[3].Normal.X = -value.M14 + value.M11;
|
||||
planes[3].Normal.Y = -value.M24 + value.M21;
|
||||
planes[3].Normal.Z = -value.M34 + value.M31;
|
||||
planes[3].D = -value.M44 + value.M41;
|
||||
planes[4].Normal.X = -value.M14 + value.M12;
|
||||
planes[4].Normal.Y = -value.M24 + value.M22;
|
||||
planes[4].Normal.Z = -value.M34 + value.M32;
|
||||
planes[4].D = -value.M44 + value.M42;
|
||||
planes[5].Normal.X = -value.M14 - value.M12;
|
||||
planes[5].Normal.Y = -value.M24 - value.M22;
|
||||
planes[5].Normal.Z = -value.M34 - value.M32;
|
||||
planes[5].D = -value.M44 - value.M42;
|
||||
planes[0].Normal.X = -value.M13;
|
||||
planes[0].Normal.Y = -value.M23;
|
||||
planes[0].Normal.Z = -value.M33;
|
||||
planes[0].D = -value.M43;
|
||||
planes[1].Normal.X = -value.M14 + value.M13;
|
||||
planes[1].Normal.Y = -value.M24 + value.M23;
|
||||
planes[1].Normal.Z = -value.M34 + value.M33;
|
||||
planes[1].D = -value.M44 + value.M43;
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
float num2 = planes[i].Normal.Length();
|
||||
planes[i].Normal = (planes[i].Normal / num2);
|
||||
planes[i].D /= num2;
|
||||
}
|
||||
|
||||
Ray ray = ComputeIntersectionLine(planes[0], planes[2]);
|
||||
cornerArray[0] = ComputeIntersection(planes[4], ray);
|
||||
cornerArray[3] = ComputeIntersection(planes[5], ray);
|
||||
ray = ComputeIntersectionLine(planes[3], planes[0]);
|
||||
cornerArray[1] = ComputeIntersection(planes[4], ray);
|
||||
cornerArray[2] = ComputeIntersection(planes[5], ray);
|
||||
ray = ComputeIntersectionLine(planes[2], planes[1]);
|
||||
cornerArray[4] = ComputeIntersection(planes[4], ray);
|
||||
cornerArray[7] = ComputeIntersection(planes[5], ray);
|
||||
ray = ComputeIntersectionLine(planes[1], planes[3]);
|
||||
cornerArray[5] = ComputeIntersection(planes[4], ray);
|
||||
cornerArray[6] = ComputeIntersection(planes[5], ray);
|
||||
}
|
||||
|
||||
Plane BoundingFrustum::Near()
|
||||
@ -185,6 +231,7 @@ namespace XFX
|
||||
if (Intersects(frustrum))
|
||||
{
|
||||
disjoint = ContainmentType::Contains;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (Contains(frustrum.cornerArray[i]) == ContainmentType::Disjoint)
|
||||
@ -207,10 +254,12 @@ namespace XFX
|
||||
{
|
||||
float num5 = ((planes[i].Normal.X * center.X) + (planes[i].Normal.Y * center.Y)) + (planes[i].Normal.Z * center.Z);
|
||||
float num3 = num5 + planes[i].D;
|
||||
|
||||
if (num3 > radius)
|
||||
{
|
||||
return ContainmentType::Disjoint;
|
||||
}
|
||||
|
||||
if (num3 < -radius)
|
||||
{
|
||||
num2++;
|
||||
@ -506,55 +555,6 @@ namespace XFX
|
||||
}
|
||||
}
|
||||
|
||||
void BoundingFrustum::SetMatrix(Matrix value)
|
||||
{
|
||||
matrix = value;
|
||||
planes[2].Normal.X = -value.M14 - value.M11;
|
||||
planes[2].Normal.Y = -value.M24 - value.M21;
|
||||
planes[2].Normal.Z = -value.M34 - value.M31;
|
||||
planes[2].D = -value.M44 - value.M41;
|
||||
planes[3].Normal.X = -value.M14 + value.M11;
|
||||
planes[3].Normal.Y = -value.M24 + value.M21;
|
||||
planes[3].Normal.Z = -value.M34 + value.M31;
|
||||
planes[3].D = -value.M44 + value.M41;
|
||||
planes[4].Normal.X = -value.M14 + value.M12;
|
||||
planes[4].Normal.Y = -value.M24 + value.M22;
|
||||
planes[4].Normal.Z = -value.M34 + value.M32;
|
||||
planes[4].D = -value.M44 + value.M42;
|
||||
planes[5].Normal.X = -value.M14 - value.M12;
|
||||
planes[5].Normal.Y = -value.M24 - value.M22;
|
||||
planes[5].Normal.Z = -value.M34 - value.M32;
|
||||
planes[5].D = -value.M44 - value.M42;
|
||||
planes[0].Normal.X = -value.M13;
|
||||
planes[0].Normal.Y = -value.M23;
|
||||
planes[0].Normal.Z = -value.M33;
|
||||
planes[0].D = -value.M43;
|
||||
planes[1].Normal.X = -value.M14 + value.M13;
|
||||
planes[1].Normal.Y = -value.M24 + value.M23;
|
||||
planes[1].Normal.Z = -value.M34 + value.M33;
|
||||
planes[1].D = -value.M44 + value.M43;
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
float num2 = planes[i].Normal.Length();
|
||||
planes[i].Normal = (planes[i].Normal / num2);
|
||||
planes[i].D /= num2;
|
||||
}
|
||||
|
||||
Ray ray = ComputeIntersectionLine(planes[0], planes[2]);
|
||||
cornerArray[0] = ComputeIntersection(planes[4], ray);
|
||||
cornerArray[3] = ComputeIntersection(planes[5], ray);
|
||||
ray = ComputeIntersectionLine(planes[3], planes[0]);
|
||||
cornerArray[1] = ComputeIntersection(planes[4], ray);
|
||||
cornerArray[2] = ComputeIntersection(planes[5], ray);
|
||||
ray = ComputeIntersectionLine(planes[2], planes[1]);
|
||||
cornerArray[4] = ComputeIntersection(planes[4], ray);
|
||||
cornerArray[7] = ComputeIntersection(planes[5], ray);
|
||||
ray = ComputeIntersectionLine(planes[1], planes[3]);
|
||||
cornerArray[5] = ComputeIntersection(planes[4], ray);
|
||||
cornerArray[6] = ComputeIntersection(planes[5], ray);
|
||||
}
|
||||
|
||||
bool BoundingFrustum::operator !=(const BoundingFrustum& right) const
|
||||
{
|
||||
return !Equals(right);
|
||||
|
287
src/libXFX/Curve.cpp
Normal file
287
src/libXFX/Curve.cpp
Normal file
@ -0,0 +1,287 @@
|
||||
// Copyright (C) XFX Team
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the copyright holder nor the names of any
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Curve.h>
|
||||
#include <System/Math.h>
|
||||
#include <System/Single.h>
|
||||
|
||||
namespace XFX
|
||||
{
|
||||
const Type CurveTypeInfo("Curve", "XFX::Curve", TypeCode::Object);
|
||||
|
||||
bool Curve::IsConstant() const
|
||||
{
|
||||
return keys.Count() <= 1;
|
||||
}
|
||||
|
||||
CurveKeyCollection Curve::getKeys() const
|
||||
{
|
||||
return keys;
|
||||
}
|
||||
|
||||
Curve::Curve()
|
||||
{
|
||||
}
|
||||
|
||||
Curve Curve::Clone() const
|
||||
{
|
||||
Curve curve = Curve();
|
||||
|
||||
curve.keys = keys.Clone();
|
||||
curve.PreLoop = PreLoop;
|
||||
curve.PostLoop = PostLoop;
|
||||
|
||||
return curve;
|
||||
}
|
||||
|
||||
void Curve::ComputeTangent(int keyIndex, CurveTangent_t tangentInType, CurveTangent_t tangentOutType)
|
||||
{
|
||||
// See http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.curvetangent.aspx
|
||||
|
||||
CurveKey key = keys[keyIndex];
|
||||
|
||||
float p0, p, p1;
|
||||
p0 = p = p1 = key.getPosition();
|
||||
|
||||
float v0, v, v1;
|
||||
v0 = v = v1 = key.Value;
|
||||
|
||||
if ( keyIndex > 0 )
|
||||
{
|
||||
p0 = keys[keyIndex - 1].getPosition();
|
||||
v0 = keys[keyIndex - 1].Value;
|
||||
}
|
||||
|
||||
if (keyIndex < keys.Count() - 1)
|
||||
{
|
||||
p1 = keys[keyIndex + 1].getPosition();
|
||||
v1 = keys[keyIndex + 1].Value;
|
||||
}
|
||||
|
||||
switch (tangentInType)
|
||||
{
|
||||
case CurveTangent::Flat:
|
||||
key.TangentIn = 0;
|
||||
break;
|
||||
case CurveTangent::Linear:
|
||||
key.TangentIn = v - v0;
|
||||
break;
|
||||
case CurveTangent::Smooth:
|
||||
float pn = p1 - p0;
|
||||
|
||||
if (Math::Abs(pn) < Single::Epsilon)
|
||||
{
|
||||
key.TangentIn = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
key.TangentIn = (v1 - v0) * ((p - p0) / pn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (tangentOutType)
|
||||
{
|
||||
case CurveTangent::Flat:
|
||||
key.TangentOut = 0;
|
||||
break;
|
||||
case CurveTangent::Linear:
|
||||
key.TangentOut = v1 - v;
|
||||
break;
|
||||
case CurveTangent::Smooth:
|
||||
float pn = p1 - p0;
|
||||
|
||||
if (Math::Abs(pn) < Single::Epsilon)
|
||||
{
|
||||
key.TangentOut = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
key.TangentOut = (v1 - v0) * ((p1 - p) / pn);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Curve::ComputeTangents(CurveTangent_t tangentInType, CurveTangent_t tangentOutType)
|
||||
{
|
||||
for (int i = 0; i < keys.Count(); i++)
|
||||
{
|
||||
ComputeTangent(i, tangentInType, tangentOutType);
|
||||
}
|
||||
}
|
||||
|
||||
float Curve::Evaluate(float position)
|
||||
{
|
||||
CurveKey first = keys[0];
|
||||
CurveKey last = keys[keys.Count() - 1];
|
||||
|
||||
if (position < first.getPosition())
|
||||
{
|
||||
switch (PreLoop)
|
||||
{
|
||||
case CurveLoopType::Constant:
|
||||
//constant
|
||||
return first.Value;
|
||||
|
||||
case CurveLoopType::Linear:
|
||||
// linear y = a*x +b with a tangent of last point
|
||||
return first.Value - first.TangentIn * (first.getPosition() - position);
|
||||
|
||||
case CurveLoopType::Cycle:
|
||||
//start -> end / start -> end
|
||||
int cycle = GetNumberOfCycle(position);
|
||||
float virtualPos = position - (cycle * (last.getPosition() - first.getPosition()));
|
||||
return GetCurvePosition(virtualPos);
|
||||
|
||||
case CurveLoopType::CycleOffset:
|
||||
//make the curve continue (with no step) so must up the curve each cycle of delta(value)
|
||||
cycle = GetNumberOfCycle(position);
|
||||
virtualPos = position - (cycle * (last.getPosition() - first.getPosition()));
|
||||
return (GetCurvePosition(virtualPos) + cycle * (last.Value - first.Value));
|
||||
|
||||
case CurveLoopType::Oscillate:
|
||||
//go back on curve from end and target start
|
||||
// start-> end / end -> start
|
||||
cycle = GetNumberOfCycle(position);
|
||||
|
||||
if (0 == cycle % 2f)//if pair
|
||||
{
|
||||
virtualPos = position - (cycle * (last.getPosition() - first.getPosition()));
|
||||
}
|
||||
else
|
||||
{
|
||||
virtualPos = last.getPosition() - position + first.getPosition() + (cycle * (last.getPosition() - first.getPosition()));
|
||||
}
|
||||
|
||||
return GetCurvePosition(virtualPos);
|
||||
}
|
||||
}
|
||||
else if (position > last.getPosition())
|
||||
{
|
||||
int cycle;
|
||||
|
||||
switch (PostLoop)
|
||||
{
|
||||
case CurveLoopType::Constant:
|
||||
//constant
|
||||
return last.Value;
|
||||
|
||||
case CurveLoopType::Linear:
|
||||
// linear y = a*x +b with a tangent of last point
|
||||
return last.Value + first.TangentOut * (position - last.getPosition());
|
||||
|
||||
case CurveLoopType::Cycle:
|
||||
//start -> end / start -> end
|
||||
cycle = GetNumberOfCycle(position);
|
||||
float virtualPos = position - (cycle * (last.getPosition() - first.getPosition()));
|
||||
return GetCurvePosition(virtualPos);
|
||||
|
||||
case CurveLoopType::CycleOffset:
|
||||
//make the curve continue (with no step) so must up the curve each cycle of delta(value)
|
||||
cycle = GetNumberOfCycle(position);
|
||||
virtualPos = position - (cycle * (last.getPosition() - first.getPosition()));
|
||||
return (GetCurvePosition(virtualPos) + cycle * (last.Value - first.Value));
|
||||
|
||||
case CurveLoopType::Oscillate:
|
||||
//go back on curve from end and target start
|
||||
// start-> end / end -> start
|
||||
cycle = GetNumberOfCycle(position);
|
||||
virtualPos = position - (cycle * (last.getPosition() - first.getPosition()));
|
||||
|
||||
if (0 == cycle % 2) // if pair
|
||||
{
|
||||
virtualPos = position - (cycle * (last.getPosition() - first.getPosition()));
|
||||
}
|
||||
else
|
||||
{
|
||||
virtualPos = last.getPosition() - position + first.getPosition() + (cycle * (last.getPosition() - first.getPosition()));
|
||||
}
|
||||
|
||||
return GetCurvePosition(virtualPos);
|
||||
}
|
||||
}
|
||||
|
||||
//in curve
|
||||
return GetCurvePosition(position);
|
||||
}
|
||||
|
||||
float Curve::GetCurvePosition(float position)
|
||||
{
|
||||
// only for position in curve
|
||||
CurveKey prev = keys[0];
|
||||
|
||||
for (int i = 1; i < keys.Count(); i++)
|
||||
{
|
||||
CurveKey next = keys[i];
|
||||
|
||||
if (next.getPosition() >= position)
|
||||
{
|
||||
if (prev.Continuity == CurveContinuity::Step)
|
||||
{
|
||||
if (position >= 1.0f)
|
||||
{
|
||||
return next.Value;
|
||||
}
|
||||
|
||||
return prev.Value;
|
||||
}
|
||||
|
||||
float t = (position - prev.getPosition()) / (next.getPosition() - prev.getPosition());//to have t in [0,1]
|
||||
float ts = t * t;
|
||||
float tss = ts * t;
|
||||
//After a lot of search on internet I have found all about spline function
|
||||
// and bezier (phi'sss ancien) but finally use hermite curve
|
||||
//http://en.wikipedia.org/wiki/Cubic_Hermite_spline
|
||||
//P(t) = (2*t^3 - 3t^2 + 1)*P0 + (t^3 - 2t^2 + t)m0 + (-2t^3 + 3t^2)P1 + (t^3-t^2)m1
|
||||
//with P0.value = prev.value , m0 = prev.tangentOut, P1= next.value, m1 = next.TangentIn
|
||||
return (2 * tss - 3 * ts + 1.0f) * prev.Value + (tss - 2 * ts + t) * prev.TangentOut + (3 * ts - 2 * tss) * next.Value + (tss - ts) * next.TangentIn;
|
||||
}
|
||||
|
||||
prev = next;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
int Curve::GetNumberOfCycle(float position)
|
||||
{
|
||||
float cycle = (position - keys[0].getPosition()) / (keys[keys.Count() - 1].getPosition() - keys[0].getPosition());
|
||||
|
||||
if (cycle < 0.0f)
|
||||
{
|
||||
cycle--;
|
||||
}
|
||||
|
||||
return (int)cycle;
|
||||
}
|
||||
|
||||
const Type& Curve::GetType()
|
||||
{
|
||||
return CurveTypeInfo;
|
||||
}
|
||||
}
|
111
src/libXFX/CurveKey.cpp
Normal file
111
src/libXFX/CurveKey.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright (C) XFX Team
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the copyright holder nor the names of any
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <CurveKey.h>
|
||||
#include <System/Type.h>
|
||||
|
||||
namespace XFX
|
||||
{
|
||||
const Type CurveKeyTypeInfo("CurveKey", "XFX::CurveKey", TypeCode::Object);
|
||||
|
||||
float CurveKey::getPosition() const
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
CurveKey::CurveKey(float position, float value)
|
||||
: position(position), Continuity(CurveContinuity::Smooth), Value(value)
|
||||
{
|
||||
}
|
||||
|
||||
CurveKey::CurveKey(float position, float value, float tangentIn, float tangentOut)
|
||||
: position(position), Continuity(CurveContinuity::Smooth), TangentIn(tangentIn), TangentOut(tangentOut), Value(value)
|
||||
{
|
||||
}
|
||||
|
||||
CurveKey::CurveKey(float position, float value, float tangentIn, float tangentOut, CurveContinuity_t continuity)
|
||||
: position(position), Continuity(continuity), TangentIn(tangentIn), TangentOut(tangentOut), Value(value)
|
||||
{
|
||||
}
|
||||
|
||||
CurveKey CurveKey::Clone()
|
||||
{
|
||||
return CurveKey(position, Value, TangentIn, TangentOut, Continuity);
|
||||
}
|
||||
|
||||
int CurveKey::CompareTo(const CurveKey other) const
|
||||
{
|
||||
if (position > other.position)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (position < other.position)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CurveKey::Equals(Object const * const obj) const
|
||||
{
|
||||
return is(obj, this) ? *this == *(CurveKey *)obj : false;
|
||||
}
|
||||
|
||||
bool CurveKey::Equals(const CurveKey other) const
|
||||
{
|
||||
return *this == other;
|
||||
}
|
||||
|
||||
int CurveKey::GetHashCode() const
|
||||
{
|
||||
return (int)position ^ (int)Value ^ (int)TangentIn ^ (int)TangentOut ^ (int)Continuity;
|
||||
}
|
||||
|
||||
const Type& CurveKey::GetType()
|
||||
{
|
||||
return CurveKeyTypeInfo;
|
||||
}
|
||||
|
||||
bool CurveKey::operator!=(const CurveKey& right) const
|
||||
{
|
||||
return ((position != right.position) ||
|
||||
(Value != right.Value) ||
|
||||
(TangentIn != right.TangentIn) ||
|
||||
(TangentOut != right.TangentOut) ||
|
||||
(Continuity != right.Continuity));
|
||||
}
|
||||
|
||||
bool CurveKey::operator==(const CurveKey& right) const
|
||||
{
|
||||
return ((position == right.position) &&
|
||||
(Value == right.Value) &&
|
||||
(TangentIn == right.TangentIn) &&
|
||||
(TangentOut == right.TangentOut) &&
|
||||
(Continuity == right.Continuity));
|
||||
}
|
||||
}
|
138
src/libXFX/CurveKeyCollection.cpp
Normal file
138
src/libXFX/CurveKeyCollection.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright (C) XFX Team
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the copyright holder nor the names of any
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <CurveKeyCollection.h>
|
||||
#include <System/Type.h>
|
||||
|
||||
namespace XFX
|
||||
{
|
||||
const Type CurveKeyCollectionTypeInfo("CurveKeyCollection", "XFX::CurveKeyCollection", TypeCode::Object);
|
||||
|
||||
int CurveKeyCollection::Count() const
|
||||
{
|
||||
return innerList.Count();
|
||||
}
|
||||
|
||||
bool CurveKeyCollection::IsReadOnly() const
|
||||
{
|
||||
return isReadOnly;
|
||||
}
|
||||
|
||||
CurveKeyCollection::CurveKeyCollection()
|
||||
{
|
||||
}
|
||||
|
||||
void CurveKeyCollection::Add(const CurveKey& item)
|
||||
{
|
||||
if (innerList.Count() == 0)
|
||||
{
|
||||
innerList.Add(item);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < innerList.Count(); i++)
|
||||
{
|
||||
if (item.getPosition() < innerList[i].getPosition())
|
||||
{
|
||||
innerList.Insert(i, item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
innerList.Add(item);
|
||||
}
|
||||
|
||||
void CurveKeyCollection::Clear()
|
||||
{
|
||||
innerList.Clear();
|
||||
}
|
||||
|
||||
CurveKeyCollection CurveKeyCollection::Clone() const
|
||||
{
|
||||
CurveKeyCollection collection = CurveKeyCollection();
|
||||
|
||||
collection.innerList.AddRange(&innerList);
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
bool CurveKeyCollection::Contains(const CurveKey& item) const
|
||||
{
|
||||
return innerList.Contains(item);
|
||||
}
|
||||
|
||||
void CurveKeyCollection::CopyTo(CurveKey array[], const int arrayIndex) const
|
||||
{
|
||||
innerList.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
const Type& CurveKeyCollection::GetType()
|
||||
{
|
||||
return CurveKeyCollectionTypeInfo;
|
||||
}
|
||||
|
||||
int CurveKeyCollection::IndexOf(const CurveKey& item) const
|
||||
{
|
||||
return innerList.IndexOf(item);
|
||||
}
|
||||
|
||||
bool CurveKeyCollection::Remove(const CurveKey& item)
|
||||
{
|
||||
int index = IndexOf(item);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CurveKeyCollection::RemoveAt(const int index)
|
||||
{
|
||||
innerList.RemoveAt(index);
|
||||
}
|
||||
|
||||
CurveKey& CurveKeyCollection::operator[](int index) const
|
||||
{
|
||||
return innerList[index];
|
||||
}
|
||||
|
||||
void CurveKeyCollection::set(int index, const CurveKey& value)
|
||||
{
|
||||
if (innerList[index].getPosition() == value.getPosition())
|
||||
{
|
||||
innerList[index] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
innerList.RemoveAt(index);
|
||||
innerList.Add(value);
|
||||
}
|
||||
}
|
||||
}
|
@ -79,6 +79,9 @@
|
||||
<ClCompile Include="BoundingBox.cpp" />
|
||||
<ClCompile Include="BoundingFrustum.cpp" />
|
||||
<ClCompile Include="BoundingSphere.cpp" />
|
||||
<ClCompile Include="Curve.cpp" />
|
||||
<ClCompile Include="CurveKey.cpp" />
|
||||
<ClCompile Include="CurveKeyCollection.cpp" />
|
||||
<ClCompile Include="DirectionalLight.cpp" />
|
||||
<ClCompile Include="DynamicSoundEffectInstance.cpp" />
|
||||
<ClCompile Include="Effect.cpp" />
|
||||
@ -143,6 +146,9 @@
|
||||
<ClInclude Include="..\..\include\BoundingBox.h" />
|
||||
<ClInclude Include="..\..\include\BoundingFrustum.h" />
|
||||
<ClInclude Include="..\..\include\BoundingSphere.h" />
|
||||
<ClInclude Include="..\..\include\Curve.h" />
|
||||
<ClInclude Include="..\..\include\CurveKey.h" />
|
||||
<ClInclude Include="..\..\include\CurveKeyCollection.h" />
|
||||
<ClInclude Include="..\..\include\Enums.h" />
|
||||
<ClInclude Include="..\..\include\Graphics\AlphaTestEffect.h" />
|
||||
<ClInclude Include="..\..\include\Graphics\BlendState.h" />
|
||||
|
@ -248,6 +248,15 @@
|
||||
<ClCompile Include="DirectionalLight.cpp">
|
||||
<Filter>Source Files\Graphics</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Curve.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CurveKey.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CurveKeyCollection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\include\BoundingBox.h">
|
||||
@ -583,6 +592,15 @@
|
||||
<ClInclude Include="StorageDeviceAsyncResult.h">
|
||||
<Filter>Source Files\Storage</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\Curve.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\CurveKey.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\CurveKeyCollection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="makefile" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user