- Backend update from GZDoom.

Bone model support in GLES and ZScript quaternions.
This commit is contained in:
Christoph Oelckers 2022-11-14 19:46:25 +01:00
parent c94d2fb3d0
commit 6e49f0bf8f
17 changed files with 250 additions and 61 deletions

View file

@ -125,9 +125,11 @@ xx(Fixed)
xx(Vector2)
xx(Vector3)
xx(Vector4)
xx(Quat)
xx(FVector2)
xx(FVector3)
xx(FVector4)
xx(FQuat)
xx(let)
xx(Min)

View file

@ -275,8 +275,10 @@ void GLBuffer::GPUWaitSync()
void GLVertexBuffer::SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs)
{
static int VFmtToGLFmt[] = { GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_UNSIGNED_BYTE, GL_FLOAT }; // TODO Fix last entry GL_INT_2_10_10_10_REV, normals for models will be broken
static uint8_t VFmtToSize[] = {4, 3, 2, 1, 4, 4};
static int VFmtToGLFmt[] = { GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_UNSIGNED_BYTE, GL_INT_2_10_10_10_REV, GL_UNSIGNED_BYTE }; // TODO Fix last entry GL_INT_2_10_10_10_REV, normals for models will be broken
static uint8_t VFmtToSize[] = {4, 3, 2, 1, 4, 4, 4};
static bool VFmtToNormalize[] = { false, false, false, false, true, true, false };
static bool VFmtToIntegerType[] = { false, false, false, false, false, false, true };
mStride = stride;
mNumBindingPoints = numBindingPoints;
@ -290,6 +292,8 @@ void GLVertexBuffer::SetFormat(int numBindingPoints, int numAttributes, size_t s
attrinf.size = VFmtToSize[attrs[i].format];
attrinf.offset = attrs[i].offset;
attrinf.bindingpoint = attrs[i].binding;
attrinf.normalize = VFmtToNormalize[attrs[i].format];
attrinf.integerType = VFmtToIntegerType[attrs[i].format];
}
}
}
@ -310,7 +314,13 @@ void GLVertexBuffer::Bind(int *offsets)
{
glEnableVertexAttribArray(i);
size_t ofs = offsets == nullptr ? attrinf.offset : attrinf.offset + mStride * offsets[attrinf.bindingpoint];
glVertexAttribPointer(i, attrinf.size, attrinf.format, attrinf.format != GL_FLOAT, (GLsizei)mStride, (void*)(intptr_t)ofs);
if (!attrinf.integerType)
glVertexAttribPointer(i, attrinf.size, attrinf.format, attrinf.normalize, (GLsizei)mStride, (void*)(intptr_t)ofs);
else
{
if (gles.gles3Features)
glVertexAttribIPointer(i, attrinf.size, attrinf.format, (GLsizei)mStride, (void*)(intptr_t)ofs);
}
}
i++;
}

View file

@ -49,6 +49,8 @@ class GLVertexBuffer : public IVertexBuffer, public GLBuffer
{
int bindingpoint;
int format;
bool normalize;
bool integerType;
int size;
int offset;
};

View file

@ -32,6 +32,7 @@
#include "gles_shader.h"
#include "gles_renderer.h"
#include "hw_lightbuffer.h"
#include "hw_bonebuffer.h"
#include "gles_renderbuffers.h"
#include "gles_hwtexture.h"
#include "gles_buffers.h"
@ -110,15 +111,13 @@ bool FGLRenderState::ApplyShader()
{
lightPtr = ((float*)screen->mLights->GetBuffer()->Memory());
lightPtr += ((int64_t)mLightIndex * 4);
//float array[64];
//memcpy(array, ptr, 4 * 64);
// Calculate how much light data there is to upload, this is stored in the first 4 floats
modLights = int(lightPtr[1]) / LIGHT_VEC4_NUM;
subLights = (int(lightPtr[2]) - int(lightPtr[1])) / LIGHT_VEC4_NUM;
addLights = (int(lightPtr[3]) - int(lightPtr[2])) / LIGHT_VEC4_NUM;
// Here we limit the number of lights, but dont' change the light data so priority has to be mod, sub then add
// Here we limit the number of lights, but don't change the light data so priority has to be mod, sub then add
if (modLights > (int)gles.maxlights)
modLights = gles.maxlights;
@ -224,12 +223,9 @@ bool FGLRenderState::ApplyShader()
activeShader->cur->muProjectionMatrix.Set(&mHwUniforms->mProjectionMatrix);
activeShader->cur->muViewMatrix.Set(&mHwUniforms->mViewMatrix);
activeShader->cur->muNormalViewMatrix.Set(&mHwUniforms->mNormalViewMatrix);
activeShader->cur->muCameraPos.Set(&mHwUniforms->mCameraPos.X);
activeShader->cur->muClipLine.Set(&mHwUniforms->mClipLine.X);
activeShader->cur->muGlobVis.Set(mHwUniforms->mGlobVis);
activeShader->cur->muPalLightLevels.Set(mHwUniforms->mPalLightLevels & 0xFF); // JUST pass the pal levels, clear the top bits
activeShader->cur->muViewHeight.Set(mHwUniforms->mViewHeight);
activeShader->cur->muClipHeight.Set(mHwUniforms->mClipHeight);
@ -250,6 +246,7 @@ bool FGLRenderState::ApplyShader()
activeShader->cur->muInterpolationFactor.Set(mStreamData.uInterpolationFactor);
activeShader->cur->muTimer.Set((double)(screen->FrameTime - firstFrame) * (double)mShaderTimer / 1000.);
activeShader->cur->muAlphaThreshold.Set(mAlphaThreshold);
activeShader->cur->muBoneIndexBase.Set(-1);
activeShader->cur->muClipSplit.Set(mClipSplit);
activeShader->cur->muSpecularMaterial.Set(mGlossiness, mSpecularLevel);
activeShader->cur->muAddColor.Set(mStreamData.uAddColor);
@ -349,6 +346,23 @@ bool FGLRenderState::ApplyShader()
activeShader->cur->muLightRange.Set(range);
}
if (gles.gles3Features)
{
// Upload bone data
// NOTE, this is pretty inefficient, it will be reloading the same data over and over in a single frame
// Need to add something to detect a start of new frame then only update the data when it's been changed
if ((mBoneIndexBase >= 0))
{
float* bonesPtr = ((float*)screen->mBones->GetBuffer()->Memory());
int number = screen->mBones->GetCurrentIndex();
glUniformMatrix4fv(activeShader->cur->bones_index, number, false, bonesPtr);
activeShader->cur->muBoneIndexBase.Set(mBoneIndexBase);
}
}
return true;
}
@ -369,18 +383,6 @@ void FGLRenderState::ApplyState()
if (mSplitEnabled != stSplitEnabled)
{
/*
if (mSplitEnabled)
{
glEnable(GL_CLIP_DISTANCE3);
glEnable(GL_CLIP_DISTANCE4);
}
else
{
glDisable(GL_CLIP_DISTANCE3);
glDisable(GL_CLIP_DISTANCE4);
}
*/
stSplitEnabled = mSplitEnabled;
}

View file

@ -66,6 +66,7 @@ class FGLRenderState final : public FRenderState
int lastTranslation = 0;
int maxBoundMaterial = -1;
size_t mLastMappedLightIndex = SIZE_MAX;
size_t mLastMappedBoneIndexBase = SIZE_MAX;
IVertexBuffer *mCurrentVertexBuffer;
int mCurrentVertexOffsets[2]; // one per binding point
@ -108,16 +109,7 @@ public:
void EnableDrawBuffers(int count, bool apply = false) override
{
/*
count = min(count, 3);
if (mNumDrawBuffers != count)
{
static GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(count, buffers);
mNumDrawBuffers = count;
}
if (apply) Apply();
*/
}
void ToggleState(int state, bool on);

View file

@ -267,6 +267,9 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
// light buffers
uniform vec4 lights[MAXIMUM_LIGHT_VECTORS];
// bone matrix buffers
uniform mat4 bones[MAXIMUM_LIGHT_VECTORS];
uniform mat4 ProjectionMatrix;
uniform mat4 ViewMatrix;
uniform mat4 NormalViewMatrix;
@ -321,6 +324,9 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
// dynamic lights
uniform ivec4 uLightRange;
// bone animation
uniform int uBoneIndexBase;
// Blinn glossiness and specular level
uniform vec2 uSpecularMaterial;
@ -391,10 +397,11 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
FString vp_comb;
assert(screen->mLights != NULL);
assert(screen->mBones != NULL);
unsigned int lightbuffersize = screen->mLights->GetBlockSize();
vp_comb.Format("#version 100\n#define NUM_UBO_LIGHTS %d\n#define NO_CLIPDISTANCE_SUPPORT\n", lightbuffersize);
vp_comb.Format("#version %s\n\n#define NO_CLIPDISTANCE_SUPPORT\n", gles.shaderVersionString);
FString fp_comb = vp_comb;
vp_comb << defines << i_data.GetChars();
@ -529,6 +536,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
glBindAttribLocation(shaderData->hShader, VATTR_VERTEX2, "aVertex2");
glBindAttribLocation(shaderData->hShader, VATTR_NORMAL, "aNormal");
glBindAttribLocation(shaderData->hShader, VATTR_NORMAL2, "aNormal2");
glBindAttribLocation(shaderData->hShader, VATTR_BONEWEIGHT, "aBoneWeight");
glBindAttribLocation(shaderData->hShader, VATTR_BONESELECTOR, "aBoneSelector");
glLinkProgram(shaderData->hShader);
@ -569,10 +578,6 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
shaderData->muViewMatrix.Init(shaderData->hShader, "ViewMatrix");
shaderData->muNormalViewMatrix.Init(shaderData->hShader, "NormalViewMatrix");
//shaderData->ProjectionMatrix_index = glGetUniformLocation(shaderData->hShader, "ProjectionMatrix");
//shaderData->ViewMatrix_index = glGetUniformLocation(shaderData->hShader, "ViewMatrix");
//shaderData->NormalViewMatrix_index = glGetUniformLocation(shaderData->hShader, "NormalViewMatrix");
shaderData->muCameraPos.Init(shaderData->hShader, "uCameraPos");
shaderData->muClipLine.Init(shaderData->hShader, "uClipLine");
@ -591,6 +596,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
shaderData->muLightParms.Init(shaderData->hShader, "uLightAttr");
shaderData->muClipSplit.Init(shaderData->hShader, "uClipSplit");
shaderData->muLightRange.Init(shaderData->hShader, "uLightRange");
shaderData->muBoneIndexBase.Init(shaderData->hShader, "uBoneIndexBase");
shaderData->muFogColor.Init(shaderData->hShader, "uFogColor");
shaderData->muDynLightColor.Init(shaderData->hShader, "uDynLightColor");
shaderData->muObjectColor.Init(shaderData->hShader, "uObjectColor");
@ -619,6 +625,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
shaderData->muFixedColormapRange.Init(shaderData->hShader, "uFixedColormapRange");
shaderData->lights_index = glGetUniformLocation(shaderData->hShader, "lights");
shaderData->bones_index = glGetUniformLocation(shaderData->hShader, "bones");
shaderData->modelmatrix_index = glGetUniformLocation(shaderData->hShader, "ModelMatrix");
shaderData->texturematrix_index = glGetUniformLocation(shaderData->hShader, "TextureMatrix");
shaderData->normalmodelmatrix_index = glGetUniformLocation(shaderData->hShader, "NormalModelMatrix");

View file

@ -326,6 +326,7 @@ public: class ShaderVariantData
FBufferedUniform1i muTextureMode;
FBufferedUniform4f muLightParms;
FBufferedUniform2f muClipSplit;
FBufferedUniform1i muBoneIndexBase;
FBufferedUniform4i muLightRange;
FBufferedUniformPE muFogColor;
FBufferedUniform4f muDynLightColor;
@ -356,6 +357,7 @@ public: class ShaderVariantData
int lights_index = 0;
int bones_index = 0;
int modelmatrix_index = 0;
int normalmodelmatrix_index = 0;
int texturematrix_index = 0;

View file

@ -236,7 +236,7 @@ FString FShaderProgram::PatchShader(ShaderType type, const FString &code, const
{
FString patchedCode;
patchedCode.AppendFormat("#version %d\n", 100); // Set to GLES2
patchedCode.AppendFormat("#version %s\n", gles.shaderVersionString);
patchedCode += GetGLSLPrecision();

View file

@ -16,6 +16,7 @@ void setGlVersion(double glv);
PFNGLMAPBUFFERRANGEEXTPROC glMapBufferRange = NULL;
PFNGLUNMAPBUFFEROESPROC glUnmapBuffer = NULL;
PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer = NULL;
#ifdef __ANDROID__
#include <dlfcn.h>
@ -134,7 +135,7 @@ namespace OpenGLESRenderer
glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC)LoadGLES2Proc("glMapBufferRange");
glUnmapBuffer = (PFNGLUNMAPBUFFEROESPROC)LoadGLES2Proc("glUnmapBuffer");
glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)LoadGLES2Proc("glVertexAttribIPointer");
#else
static bool first = true;
@ -181,11 +182,16 @@ namespace OpenGLESRenderer
Printf("GL_MAX_TEXTURE_SIZE: %d\n", gles.max_texturesize);
#if USE_GLES2
gles.gles3Features = false; // Enales IQM bones
gles.shaderVersionString = "100";
gles.depthStencilAvailable = CheckExtension("GL_OES_packed_depth_stencil");
gles.npotAvailable = CheckExtension("GL_OES_texture_npot");
gles.depthClampAvailable = CheckExtension("GL_EXT_depth_clamp");
gles.anistropicFilterAvailable = CheckExtension("GL_EXT_texture_filter_anisotropic");
#else
gles.gles3Features = true;
gles.shaderVersionString = "330";
gles.depthStencilAvailable = true;
gles.npotAvailable = true;
gles.useMappedBuffers = true;

View file

@ -36,6 +36,9 @@ GLAPI PFNGLMAPBUFFERRANGEEXTPROC glMapBufferRange;
typedef GLboolean(APIENTRYP PFNGLUNMAPBUFFEROESPROC)(GLenum target);
GLAPI PFNGLUNMAPBUFFEROESPROC glUnmapBuffer;
typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer);
GLAPI PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer;
#define GL_DEPTH24_STENCIL8 0x88F0
#define GL_MAP_PERSISTENT_BIT 0x0040
#define GL_MAP_READ_BIT 0x0001
@ -45,7 +48,7 @@ GLAPI PFNGLUNMAPBUFFEROESPROC glUnmapBuffer;
#define GL_BGRA 0x80E1
#define GL_DEPTH_CLAMP 0x864F
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#define GL_INT_2_10_10_10_REV 0x8D9F
#else
#include "gl_load/gl_load.h"
#endif
@ -74,6 +77,8 @@ namespace OpenGLESRenderer
bool forceGLSLv100;
bool depthClampAvailable;
bool anistropicFilterAvailable;
bool gles3Features;
const char* shaderVersionString;
int max_texturesize;
char* vendorstring;
char* modelstring;

View file

@ -35,6 +35,9 @@ public:
bool GetBufferType() const { return mBufferType; }
int GetBinding(unsigned int index, size_t* pOffset, size_t* pSize);
// Only for GLES to determin how much data is in the buffer
int GetCurrentIndex() { return mIndex; };
// OpenGL needs the buffer to mess around with the binding.
IDataBuffer* GetBuffer() const
{

View file

@ -809,6 +809,34 @@ ExpEmit FxVectorValue::Emit(VMFunctionBuilder *build)
return out;
}
//==========================================================================
//
//
//
//==========================================================================
FxQuaternionValue::FxQuaternionValue(FxExpression* x, FxExpression* y, FxExpression* z, FxExpression* w, const FScriptPosition& sc) : FxVectorValue(x, y, z, w, sc)
{
}
FxExpression* FxQuaternionValue::Resolve(FCompileContext& ctx)
{
auto base = FxVectorValue::Resolve(ctx);
if (base)
{
if (base->ValueType->GetRegCount() != 4)
{
ScriptPosition.Message(MSG_ERROR, "Quat expression requires 4 arguments, got %d instead", base->ValueType->GetRegCount());
delete base;
return nullptr;
}
base->ValueType = TypeQuaternion;
}
return base;
}
//==========================================================================
//
//
@ -1727,7 +1755,7 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
delete this;
return x;
}
else if ((basex->IsVector2() && IsVector2()) || (basex->IsVector3() && IsVector3()) || (basex->IsVector4() && IsVector4()))
else if ((basex->IsVector2() && IsVector2()) || (basex->IsVector3() && IsVector3()) || (basex->IsVector4() && IsVector4()) || (basex->IsQuaternion() && IsQuaternion()))
{
auto x = basex;
basex = nullptr;
@ -1799,7 +1827,7 @@ FxExpression *FxPlusSign::Resolve(FCompileContext& ctx)
CHECKRESOLVED();
SAFE_RESOLVE(Operand, ctx);
if (Operand->IsNumeric() || Operand->IsVector())
if (Operand->IsNumeric() || Operand->IsVector() || Operand->IsQuaternion())
{
FxExpression *e = Operand;
Operand = nullptr;
@ -1853,7 +1881,7 @@ FxExpression *FxMinusSign::Resolve(FCompileContext& ctx)
CHECKRESOLVED();
SAFE_RESOLVE(Operand, ctx);
if (Operand->IsNumeric() || Operand->IsVector())
if (Operand->IsNumeric() || Operand->IsVector() || Operand->IsQuaternion())
{
if (Operand->isConstant())
{
@ -2441,7 +2469,7 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx)
delete this;
return nullptr;
}
if (!Base->IsVector() && Base->ValueType->isStruct())
if (!Base->IsVector() && !Base->IsQuaternion() && Base->ValueType->isStruct())
{
ScriptPosition.Message(MSG_ERROR, "Struct assignment not implemented yet");
delete this;
@ -2839,6 +2867,10 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx)
{
ValueType = TypeTextureID;
}
else if (left->IsQuaternion() && right->IsQuaternion())
{
ValueType = left->ValueType;
}
else if (left->IsVector() && right->IsVector())
{
// a vector2 can be added to or subtracted from a vector 3 but it needs to be the right operand.
@ -2944,6 +2976,13 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build)
}
return to;
}
else if (IsQuaternion())
{
assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT);
assert(op1.RegCount == 4 && op2.RegCount == 4);
build->Emit(OP_ADDV4_RR, to.RegNum, op1.RegNum, op2.RegNum);
return to;
}
else if (ValueType->GetRegType() == REGT_FLOAT)
{
assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT);
@ -2972,6 +3011,13 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build)
build->Emit(right->IsVector4() ? OP_SUBV4_RR : right->IsVector3() ? OP_SUBV3_RR : OP_SUBV2_RR, to.RegNum, op1.RegNum, op2.RegNum);
return to;
}
else if (IsQuaternion())
{
assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT);
assert(op1.RegCount == 4 && op2.RegCount == 4);
build->Emit(OP_SUBV4_RR, to.RegNum, op1.RegNum, op2.RegNum);
return to;
}
else if (ValueType->GetRegType() == REGT_FLOAT)
{
assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT);
@ -3037,7 +3083,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
return nullptr;
}
if (left->IsVector() || right->IsVector())
if (left->IsVector() || right->IsVector() || left->IsQuaternion() || right->IsQuaternion())
{
switch (Operator)
{
@ -3047,7 +3093,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
[[fallthrough]];
case '*':
if (left->IsVector() && right->IsNumeric())
if ((left->IsVector() || left->IsQuaternion()) && right->IsNumeric())
{
if (right->IsInteger())
{
@ -3062,7 +3108,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
ValueType = left->ValueType;
break;
}
else if (right->IsVector() && left->IsNumeric())
else if ((right->IsVector() || right->IsQuaternion()) && left->IsNumeric())
{
if (left->IsInteger())
{
@ -3162,7 +3208,7 @@ ExpEmit FxMulDiv::Emit(VMFunctionBuilder *build)
ExpEmit op1 = left->Emit(build);
ExpEmit op2 = right->Emit(build);
if (IsVector())
if (IsVector() || IsQuaternion())
{
assert(Operator != '%');
if (right->IsVector())
@ -3641,7 +3687,7 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx)
}
// identical types are always comparable, if they can be placed in a register, so we can save most checks if this is the case.
if (left->ValueType != right->ValueType && !(left->IsVector2() && right->IsVector2()) && !(left->IsVector3() && right->IsVector3()) && !(left->IsVector4() && right->IsVector4()))
if (left->ValueType != right->ValueType && !(left->IsVector2() && right->IsVector2()) && !(left->IsVector3() && right->IsVector3()) && !(left->IsVector4() && right->IsVector4()) && !(left->IsQuaternion() && right->IsQuaternion()))
{
FxExpression *x;
if (left->IsNumeric() && right->ValueType == TypeString && (x = StringConstToChar(right)))
@ -7139,7 +7185,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx)
classx = nullptr;
return x;
}
else if (classx->ExprType == EFX_LocalVariable && classx->IsVector()) // vectors are a special case because they are held in registers
else if (classx->ExprType == EFX_LocalVariable && (classx->IsVector() || classx->IsQuaternion())) // vectors are a special case because they are held in registers
{
// since this is a vector, all potential things that may get here are single float or an xy-vector.
auto locvar = static_cast<FxLocalVariable *>(classx);
@ -8058,6 +8104,25 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
}
break;
case NAME_FQuat:
case NAME_Quat:
if (CheckArgSize(MethodName, ArgList, 1, 4, ScriptPosition))
{
// Reuse vector expression
func = new FxQuaternionValue(
ArgList[0],
ArgList.Size() >= 2 ? ArgList[1] : nullptr,
ArgList.Size() >= 3 ? ArgList[2] : nullptr,
ArgList.Size() >= 4 ? ArgList[3] : nullptr,
ScriptPosition
);
ArgList.Clear();
delete this;
auto vector = func->Resolve(ctx);
return vector;
}
break;
default:
ScriptPosition.Message(MSG_ERROR, "Call to unknown function '%s'", MethodName.GetChars());
@ -8273,7 +8338,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
else if (Self->IsVector())
{
// handle builtins: Vectors got 5.
if (MethodName == NAME_Length || MethodName == NAME_LengthSquared || MethodName == NAME_Sum || MethodName == NAME_Unit || MethodName == NAME_Angle)
if (MethodName == NAME_Length || MethodName == NAME_LengthSquared || MethodName == NAME_Unit || MethodName == NAME_Angle)
{
if (ArgList.Size() > 0)
{
@ -8301,6 +8366,24 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
return x->Resolve(ctx);
}
}
else if (Self->IsQuaternion())
{
// Reuse vector built-ins for quaternion
if (MethodName == NAME_Length || MethodName == NAME_LengthSquared || MethodName == NAME_Unit)
{
if (ArgList.Size() > 0)
{
ScriptPosition.Message(MSG_ERROR, "Too many parameters in call to %s", MethodName.GetChars());
delete this;
return nullptr;
}
auto x = new FxVectorBuiltin(Self, MethodName);
Self = nullptr;
delete this;
return x->Resolve(ctx);
}
}
else if (Self->ValueType == TypeString)
{
@ -9275,13 +9358,14 @@ FxVectorBuiltin::~FxVectorBuiltin()
FxExpression *FxVectorBuiltin::Resolve(FCompileContext &ctx)
{
SAFE_RESOLVE(Self, ctx);
assert(Self->IsVector()); // should never be created for anything else.
assert(Self->IsVector() || Self->IsQuaternion()); // should never be created for anything else.
switch (Function.GetIndex())
{
case NAME_Angle:
assert(Self->IsVector());
case NAME_Length:
case NAME_LengthSquared:
case NAME_Sum:
case NAME_Angle:
ValueType = TypeFloat64;
break;
@ -10691,7 +10775,7 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
else if (retCount == 1)
{
// If we already know the real return type we need at least try to cast the value to its proper type (unless in an anonymous function.)
if (hasProto && protoRetCount > 0 && ctx.Function->SymbolName != NAME_None)
if (hasProto && protoRetCount > 0 && ctx.Function->SymbolName != NAME_None && Args[0]->ValueType != ctx.ReturnProto->ReturnTypes[0])
{
Args[0] = new FxTypeCast(Args[0], ctx.ReturnProto->ReturnTypes[0], false, false);
Args[0] = Args[0]->Resolve(ctx);
@ -10701,11 +10785,15 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
}
else
{
assert(ctx.ReturnProto != nullptr);
for (unsigned i = 0; i < retCount; i++)
{
Args[i] = new FxTypeCast(Args[i], ctx.ReturnProto->ReturnTypes[i], false, false);
Args[i] = Args[i]->Resolve(ctx);
if (Args[i] == nullptr) fail = true;
if (Args[i]->ValueType != ctx.ReturnProto->ReturnTypes[i])
{
Args[i] = new FxTypeCast(Args[i], ctx.ReturnProto->ReturnTypes[i], false, false);
Args[i] = Args[i]->Resolve(ctx);
if (Args[i] == nullptr) fail = true;
}
}
if (fail)
{
@ -11083,6 +11171,7 @@ FxLocalVariableDeclaration::FxLocalVariableDeclaration(PType *type, FName name,
if (type == TypeFVector2) type = TypeVector2;
else if (type == TypeFVector3) type = TypeVector3;
else if (type == TypeFVector4) type = TypeVector4;
else if (type == TypeFQuaternion) type = TypeQuaternion;
ValueType = type;
VarFlags = varflags;

View file

@ -343,6 +343,7 @@ public:
bool IsVector2() const { return ValueType == TypeVector2 || ValueType == TypeFVector2; };
bool IsVector3() const { return ValueType == TypeVector3 || ValueType == TypeFVector3; };
bool IsVector4() const { return ValueType == TypeVector4 || ValueType == TypeFVector4; };
bool IsQuaternion() const { return ValueType == TypeQuaternion || ValueType == TypeFQuaternion; };
bool IsBoolCompat() const { return ValueType->isScalar(); }
bool IsObject() const { return ValueType->isObjectPointer(); }
bool IsArray() const { return ValueType->isArray() || (ValueType->isPointer() && ValueType->toPointer()->PointedType->isArray()); }
@ -577,6 +578,20 @@ public:
};
//==========================================================================
//
//
//
//==========================================================================
class FxQuaternionValue : public FxVectorValue
{
public:
FxQuaternionValue(FxExpression* x, FxExpression* y, FxExpression* z, FxExpression* w, const FScriptPosition& sc);
FxExpression* Resolve(FCompileContext&);
};
//==========================================================================
//
//

View file

@ -62,9 +62,11 @@ PStateLabel *TypeStateLabel;
PStruct *TypeVector2;
PStruct *TypeVector3;
PStruct* TypeVector4;
PStruct* TypeQuaternion;
PStruct* TypeFVector2;
PStruct* TypeFVector3;
PStruct* TypeFVector4;
PStruct* TypeFQuaternion;
PStruct *TypeColorStruct;
PStruct *TypeStringStruct;
PPointer *TypeNullPtr;
@ -410,6 +412,40 @@ void PType::StaticInit()
TypeFVector4->RegCount = 4;
TypeFVector4->isOrdered = true;
TypeQuaternion = new PStruct(NAME_Quat, nullptr);
TypeQuaternion->AddField(NAME_X, TypeFloat64);
TypeQuaternion->AddField(NAME_Y, TypeFloat64);
TypeQuaternion->AddField(NAME_Z, TypeFloat64);
TypeQuaternion->AddField(NAME_W, TypeFloat64);
// allow vector access.
TypeQuaternion->Symbols.AddSymbol(Create<PField>(NAME_XYZ, TypeVector3, VARF_Transient, 0));
TypeQuaternion->Symbols.AddSymbol(Create<PField>(NAME_XY, TypeVector2, VARF_Transient, 0));
TypeTable.AddType(TypeQuaternion, NAME_Struct);
TypeQuaternion->loadOp = OP_LV4;
TypeQuaternion->storeOp = OP_SV4;
TypeQuaternion->moveOp = OP_MOVEV4;
TypeQuaternion->RegType = REGT_FLOAT;
TypeQuaternion->RegCount = 4;
TypeQuaternion->isOrdered = true;
TypeFQuaternion = new PStruct(NAME_FQuat, nullptr);
TypeFQuaternion->AddField(NAME_X, TypeFloat32);
TypeFQuaternion->AddField(NAME_Y, TypeFloat32);
TypeFQuaternion->AddField(NAME_Z, TypeFloat32);
TypeFQuaternion->AddField(NAME_W, TypeFloat32);
// allow accessing xyz as a vector3
TypeFQuaternion->Symbols.AddSymbol(Create<PField>(NAME_XYZ, TypeFVector3, VARF_Transient, 0));
TypeFQuaternion->Symbols.AddSymbol(Create<PField>(NAME_XY, TypeFVector2, VARF_Transient, 0));
TypeTable.AddType(TypeFQuaternion, NAME_Struct);
TypeFQuaternion->loadOp = OP_LFV4;
TypeFQuaternion->storeOp = OP_SFV4;
TypeFQuaternion->moveOp = OP_MOVEV4;
TypeFQuaternion->RegType = REGT_FLOAT;
TypeFQuaternion->RegCount = 4;
TypeFQuaternion->isOrdered = true;
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_sByte, TypeSInt8));
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Byte, TypeUInt8));
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Short, TypeSInt16));
@ -429,9 +465,11 @@ void PType::StaticInit()
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Vector2, TypeVector2));
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Vector3, TypeVector3));
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Vector4, TypeVector4));
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Quat, TypeQuaternion));
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_FVector2, TypeFVector2));
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_FVector3, TypeFVector3));
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_FVector4, TypeFVector4));
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_FQuat, TypeFQuaternion));
}

View file

@ -619,6 +619,8 @@ extern PStruct* TypeVector4;
extern PStruct* TypeFVector2;
extern PStruct* TypeFVector3;
extern PStruct* TypeFVector4;
extern PStruct* TypeQuaternion;
extern PStruct* TypeFQuaternion;
extern PStruct *TypeColorStruct;
extern PStruct *TypeStringStruct;
extern PStatePointer *TypeState;

View file

@ -2154,7 +2154,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
do
{
auto type = DetermineType(c->Type(), f, f->Name, t, false, false);
if (type->isContainer() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4)
if (type->isContainer() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion)
{
// structs and classes only get passed by pointer.
type = NewPointer(type);
@ -2176,6 +2176,10 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
{
type = TypeVector4;
}
else if (type == TypeFQuaternion)
{
type = TypeQuaternion;
}
// TBD: disallow certain types? For now, let everything pass that isn't an array.
rets.Push(type);
t = static_cast<decltype(t)>(t->SiblingNext);
@ -2353,7 +2357,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
{
auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
int flags = 0;
if ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4) || type->isDynArray())
if ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion) || type->isDynArray())
{
// Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly.
type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/);
@ -2370,12 +2374,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
{
elementcount = 3;
}
else if (type == TypeVector4 || type == TypeFVector4)
else if (type == TypeVector4 || type == TypeFVector4 || type == TypeQuaternion || type == TypeFQuaternion)
{
elementcount = 4;
}
}
if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4)
if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion)
{
// If it's TypeError, then an error was already given
if (type != TypeError)
@ -2437,6 +2441,14 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
vmval[2] = static_cast<FxConstant*>(vx->xyzw[2])->GetValue().GetFloat();
vmval[3] = static_cast<FxConstant*>(vx->xyzw[3])->GetValue().GetFloat();
}
else if ((type == TypeQuaternion || type == TypeFQuaternion) && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue*>(x)->isConstVector(4))
{
auto vx = static_cast<FxVectorValue*>(x);
vmval[0] = static_cast<FxConstant*>(vx->xyzw[0])->GetValue().GetFloat();
vmval[1] = static_cast<FxConstant*>(vx->xyzw[1])->GetValue().GetFloat();
vmval[2] = static_cast<FxConstant*>(vx->xyzw[2])->GetValue().GetFloat();
vmval[3] = static_cast<FxConstant*>(vx->xyzw[3])->GetValue().GetFloat();
}
else if (!x->isConstant())
{
Error(p, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars());

View file

@ -7,6 +7,8 @@ extern PString *TypeString;
extern PStruct *TypeVector2;
extern PStruct *TypeVector3;
extern PStruct* TypeVector4;
extern PStruct* TypeQuaternion;
extern PStruct* TypeFQuaternion;
static void OutputJitLog(const asmjit::StringLogger &logger);
@ -316,7 +318,7 @@ void JitCompiler::SetupSimpleFrame()
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
}
else if (type == TypeVector4 || type == TypeFVector4)
else if (type == TypeVector4 || type == TypeFVector4 || type == TypeQuaternion || type == TypeFQuaternion)
{
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));