Skip to content

Commit

Permalink
OpenGL avatar clothing texture rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
dpogue committed Apr 1, 2023
1 parent 96f9821 commit e328950
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 21 deletions.
16 changes: 14 additions & 2 deletions Sources/Plasma/FeatureLib/pfGLPipeline/plGLDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,9 @@ bool plGLDevice::InitDevice()
glFrontFace(GL_CCW);
glCullFace(GL_BACK);

if (plGLVersion() >= 46)
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);

return true;
}

Expand Down Expand Up @@ -418,13 +421,22 @@ void plGLDevice::SetRenderTarget(plRenderTarget* target)
ref = static_cast<plGLRenderTargetRef*>(fPipeline->MakeRenderTargetRef(target));
}

if (ref == nullptr)
if (ref == nullptr) {
/// Set to main screen
glBindFramebuffer(GL_FRAMEBUFFER, 0);
else

if (plGLVersion() >= 46)
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
} else {
/// Set to this target
glBindFramebuffer(GL_FRAMEBUFFER, ref->fFrameBuffer);

// We need to flip the Y axis :(
if (plGLVersion() >= 46)
glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE);
// else... find a way to do this with the projection matrix?
}

SetViewport();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ void plGLMaterialShaderRef::ICompile()
char* log = new char[length];
glGetShaderInfoLog(vshader, length, &length, log);
hsStatusMessage(log);
delete[] log;
}
}
}
Expand All @@ -403,6 +404,7 @@ void plGLMaterialShaderRef::ICompile()
char* log = new char[length];
glGetShaderInfoLog(fFragShaderRef, length, &length, log);
hsStatusMessage(log);
delete[] log;
}
}
}
Expand Down
216 changes: 197 additions & 19 deletions Sources/Plasma/FeatureLib/pfGLPipeline/plGLPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1517,12 +1517,41 @@ void plGLPipeline::IDrawPlate(plPlate* plate)

struct plAVTexVert
{
float fPos[3];
float fPos[2];
float fUv[2];
};

static const char* AVATAR_VERTEX_SHADER_STRING = R"(#version 430
layout(location = 0) in vec2 aVtxPosition;
layout(location = 1) in vec2 aVtxUV;
out vec2 vVtxUV;
void main() {
vVtxUV = aVtxUV;
gl_Position = vec4(aVtxPosition, 0.0, 1.0);
})";

static const char* AVATAR_FRAGMENT_SHADER_STRING = R"(#version 430
precision mediump float;
layout(location = 0) uniform sampler2D uTex;
layout(location = 1) uniform vec4 uColor;
in highp vec2 vVtxUV;
out vec4 fragColor;
void main() {
fragColor = texture(uTex, vVtxUV.xy) * uColor;
})";

void plGLPipeline::IPreprocessAvatarTextures()
{
static GLuint sVertShader = 0;
static GLuint sFragShader = 0;
static GLuint sProgram = 0;

plProfile_Set(AvRTPoolUsed, fClothingOutfits.size());
plProfile_Set(AvRTPoolCount, fAvRTPool.size());
plProfile_Set(AvRTPoolRes, fAvRTWidth);
Expand All @@ -1534,51 +1563,200 @@ void plGLPipeline::IPreprocessAvatarTextures()
if (fClothingOutfits.empty())
return;

static float kIdentityMatrix[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
// Set up the shaders the first time to go through here
if (!sVertShader) {
GLuint vshader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vshader, 1, &AVATAR_VERTEX_SHADER_STRING, nullptr);
glCompileShader(vshader);
LOG_GL_ERROR_CHECK("Vertex Shader compile failed");

sVertShader = vshader;
}

if (!sFragShader) {
GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fshader, 1, &AVATAR_FRAGMENT_SHADER_STRING, nullptr);
glCompileShader(fshader);
LOG_GL_ERROR_CHECK("Vertex Shader compile failed");

//glUniformMatrix4fv(mRef->uMatrixProj, 1, GL_TRUE, kIdentityMatrix);
//glUniformMatrix4fv(mRef->uMatrixW2C, 1, GL_TRUE, kIdentityMatrix);
//glUniformMatrix4fv(mRef->uMatrixC2W, 1, GL_TRUE, kIdentityMatrix);
//glUniformMatrix4fv(mRef->uMatrixL2W, 1, GL_TRUE, kIdentityMatrix);
sFragShader = fshader;
}

if (!sProgram) {
GLuint program = glCreateProgram();
LOG_GL_ERROR_CHECK("Create Program failed");

if (plGLVersion() >= 43) {
const char* name = "AvatarClothing";
glObjectLabel(GL_PROGRAM, program, strlen(name), name);
}

glAttachShader(program, sVertShader);
LOG_GL_ERROR_CHECK("Attach Vertex Shader failed");

glAttachShader(program, sFragShader);
LOG_GL_ERROR_CHECK("Attach Fragment Shader failed");

glLinkProgram(program);
LOG_GL_ERROR_CHECK("Program Link failed");

GLint isLinked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &isLinked);
if (isLinked == GL_FALSE)
{
GLint maxLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);

// The maxLength includes the NULL character
char* log = new char[maxLength];
glGetProgramInfoLog(program, maxLength, &maxLength, log);

hsStatusMessage(log);
delete[] log;
}

sProgram = program;
}

for (size_t oIdx = 0; oIdx < fClothingOutfits.size(); oIdx++) {
plClothingOutfit* co = fClothingOutfits[oIdx];
if (co->fBase == nullptr || co->fBase->fBaseTexture == nullptr)
continue;

#if 0
plRenderTarget* rt = plRenderTarget::ConvertNoRef(co->fTargetLayer->GetTexture());
if (rt != nullptr && co->fDirtyItems.Empty())
// we've still got our valid RT from last frame and we have nothing to do.
continue;

if (rt == nullptr) {
rt = IGetNextAvRT();

plGLMaterialShaderRef* mRef = static_cast<plGLMaterialShaderRef*>(co->fMaterial->GetDeviceRef());
if (mRef)
mRef->SetDirty(true);

co->fTargetLayer->SetTexture(rt);
}
#endif

//PushRenderTarget(rt);
PushRenderTarget(rt);
glViewport(0, 0, rt->GetWidth(), rt->GetHeight());
glDepthRange(0.0, 1.0);

// HACK HACK HACK
co->fTargetLayer->SetTexture(co->fBase->fBaseTexture);
glUseProgram(sProgram);
LOG_GL_ERROR_CHECK("Use Program failed");
fDevice.fCurrentProgram = sProgram;

glUniform1i(0, 0);
glUniform4f(1, 1.f, 1.f, 1.f, 1.f);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);

// TODO: Actually render to the render target
float uOff = 0.5f / rt->GetWidth();
float vOff = 0.5f / rt->GetHeight();

//PopRenderTarget();
//co->fDirtyItems.Clear();
IDrawClothingQuad(-1.f, -1.f, 2.f, 2.f, uOff, vOff, co->fBase->fBaseTexture);
plClothingLayout *layout = plClothingMgr::GetClothingMgr()->GetLayout(co->fBase->fLayoutName);

for (plClothingItem *item : co->fItems) {
for (size_t j = 0; j < item->fElements.size(); j++) {
for (int k = 0; k < plClothingElement::kLayerMax; k++) {
if (item->fTextures[j][k] == nullptr)
continue;

plMipmap* itemBufferTex = item->fTextures[j][k];
hsColorRGBA tint = co->GetItemTint(item, k);
if (k >= plClothingElement::kLayerSkinBlend1 && k <= plClothingElement::kLayerSkinLast)
tint.a = co->fSkinBlends[k - plClothingElement::kLayerSkinBlend1];

if (k == plClothingElement::kLayerBase) {
glBlendFunc(GL_ONE, GL_ZERO);
} else {
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
}

glUniform4f(1, tint.r, tint.g, tint.b, tint.a);

float screenW = (float)item->fElements[j]->fWidth / layout->fOrigWidth * 2.f;
float screenH = (float)item->fElements[j]->fHeight / layout->fOrigWidth * 2.f;
float screenX = (float)item->fElements[j]->fXPos / layout->fOrigWidth * 2.f - 1.f;
float screenY = (1.f - (float)item->fElements[j]->fYPos / layout->fOrigWidth) * 2.f - 1.f - screenH;

IDrawClothingQuad(screenX, screenY, screenW, screenH, uOff, vOff, itemBufferTex);
}
}
}

PopRenderTarget();
co->fDirtyItems.Clear();
}

fView.fXformResetFlags = fView.kResetAll;

fClothingOutfits.swap(fPrevClothingOutfits);
}

void plGLPipeline::IDrawClothingQuad(float x, float y, float w, float h, float uOff, float vOff, plMipmap *tex)
{
const uint32_t kVSize = sizeof(plAVTexVert);

plGLTextureRef* ref = static_cast<plGLTextureRef*>(tex->GetDeviceRef());
if (!ref || ref->IsDirty())
{
CheckTextureRef(tex);
ref = (plGLTextureRef*)tex->GetDeviceRef();
}

glActiveTexture(GL_TEXTURE0);
LOG_GL_ERROR_CHECK("Active Texture failed")

glBindTexture(GL_TEXTURE_2D, ref->fRef);
LOG_GL_ERROR_CHECK("Bind Texture failed");

plAVTexVert ptr[4];
plAVTexVert vert;
vert.fPos[0] = x;
vert.fPos[1] = y;
vert.fUv[0] = uOff;
vert.fUv[1] = 1.f + vOff;

// P0
ptr[2] = vert;

// P1
ptr[0] = vert;
ptr[0].fPos[0] += w;
ptr[0].fUv[0] += 1.f;

// P2
ptr[1] = vert;
ptr[1].fPos[0] += w;
ptr[1].fUv[0] += 1.f;
ptr[1].fPos[1] += h;
ptr[1].fUv[1] -= 1.f;

// P3
ptr[3] = vert;
ptr[3].fPos[1] += h;
ptr[3].fUv[1] -= 1.f;

GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(ptr), ptr, GL_STATIC_DRAW);

glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, kVSize, (void*)(sizeof(float) * 0));
glEnableVertexAttribArray(0);

glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, kVSize, (void*)(sizeof(float) * 2));
glEnableVertexAttribArray(1);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);

LOG_GL_ERROR_CHECK("Render failed")

glDeleteBuffers(1, &vbo);
}


bool plGLPipeline::ISoftwareVertexBlend(plDrawableSpans* drawable, const std::vector<int16_t>& visList)
{
Expand Down
1 change: 1 addition & 0 deletions Sources/Plasma/FeatureLib/pfGLPipeline/plGLPipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ class plGLPipeline : public pl3DPipeline<plGLDevice>
void IScaleLight(plGLMaterialShaderRef* mRef, size_t i, float scale);
void IDrawPlate(plPlate* plate);
void IPreprocessAvatarTextures();
void IDrawClothingQuad(float x, float y, float w, float h, float uOff, float vOff, plMipmap *tex);

/**
* Emulate matrix palette operations in software.
Expand Down
1 change: 1 addition & 0 deletions Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ class plClothingBase : public hsKeyedObject
class plClothingOutfit : public plSynchedObject
{
friend class plDXPipeline;
friend class plGLPipeline;

public:
plArmatureMod *fAvatar;
Expand Down
34 changes: 34 additions & 0 deletions Sources/Plasma/PubUtilLib/plPipeline/pl3DPipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ class pl3DPipeline : public plPipeline

void CheckTextureRef(plLayerInterface* lay) override;

void CheckTextureRef(plBitmap* bmp);

void SetDefaultFogEnviron(plFogEnvironment* fog) override {
fView.SetDefaultFog(*fog);
}
Expand Down Expand Up @@ -1190,6 +1192,38 @@ void pl3DPipeline<DeviceType>::CheckTextureRef(plLayerInterface* layer)
}
}

template <class DeviceType>
void pl3DPipeline<DeviceType>::CheckTextureRef(plBitmap* bitmap)
{
typename DeviceType::TextureRef* tRef = static_cast<typename DeviceType::TextureRef*>(bitmap->GetDeviceRef());

if (!tRef) {
tRef = new typename DeviceType::TextureRef();
fDevice.SetupTextureRef(nullptr, bitmap, tRef);
}

if (!tRef->IsLinked())
tRef->Link(&fTextureRefList);

// Make sure it has all resources created.
fDevice.CheckTexture(tRef);

// If it's dirty, refill it.
if (tRef->IsDirty()) {
plMipmap* mip = plMipmap::ConvertNoRef(bitmap);
if (mip) {
fDevice.MakeTextureRef(tRef, nullptr, mip);
return;
}

plCubicEnvironmap* cubic = plCubicEnvironmap::ConvertNoRef(bitmap);
if (cubic) {
fDevice.MakeCubicTextureRef(tRef, nullptr, cubic);
return;
}
}
}


template <class DeviceType>
void pl3DPipeline<DeviceType>::RegisterLight(plLightInfo* liInfo)
Expand Down

0 comments on commit e328950

Please sign in to comment.