mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-11-14 08:30:58 +00:00
Corrected issues with detailmaps scaling, improved general model drawing performance and added support for using vertex arrays when drawing models (r_vertexarrays)
git-svn-id: https://svn.eduke32.com/eduke32@513 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
parent
19719cf7fb
commit
3f5f1b8fb2
7 changed files with 122 additions and 48 deletions
|
@ -498,6 +498,7 @@ void gltexapplyprops (void);
|
||||||
extern long r_depthpeeling, r_peelscount;
|
extern long r_depthpeeling, r_peelscount;
|
||||||
extern long r_detailmapping;
|
extern long r_detailmapping;
|
||||||
extern long r_glowmapping;
|
extern long r_glowmapping;
|
||||||
|
extern long r_vertexarrays;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void hicinit(void);
|
void hicinit(void);
|
||||||
|
|
|
@ -152,6 +152,7 @@ extern void (APIENTRY * bglDeleteProgramsARB)(GLsizei n, const GLuint *programs)
|
||||||
|
|
||||||
// Multitexturing
|
// Multitexturing
|
||||||
extern void (APIENTRY * bglActiveTextureARB)(GLenum texture);
|
extern void (APIENTRY * bglActiveTextureARB)(GLenum texture);
|
||||||
|
extern void (APIENTRY * bglClientActiveTextureARB)(GLenum texture);
|
||||||
extern void (APIENTRY * bglMultiTexCoord2dARB)(GLenum target, GLdouble s, GLdouble t );
|
extern void (APIENTRY * bglMultiTexCoord2dARB)(GLenum target, GLdouble s, GLdouble t );
|
||||||
extern void (APIENTRY * bglMultiTexCoord2fARB)(GLenum target, GLfloat s, GLfloat t );
|
extern void (APIENTRY * bglMultiTexCoord2fARB)(GLenum target, GLfloat s, GLfloat t );
|
||||||
|
|
||||||
|
|
|
@ -1193,6 +1193,7 @@ static int defsparser(scriptfile *script)
|
||||||
{
|
{
|
||||||
pal = DETAILPAL;
|
pal = DETAILPAL;
|
||||||
xscale = 1.0f / xscale;
|
xscale = 1.0f / xscale;
|
||||||
|
yscale = 1.0f / yscale;
|
||||||
}
|
}
|
||||||
else if (token == T_GLOW)
|
else if (token == T_GLOW)
|
||||||
pal = GLOWPAL;
|
pal = GLOWPAL;
|
||||||
|
|
|
@ -123,6 +123,7 @@ void (APIENTRY * bglDeleteProgramsARB)(GLsizei n, const GLuint *programs);
|
||||||
|
|
||||||
// Multitexturing
|
// Multitexturing
|
||||||
void (APIENTRY * bglActiveTextureARB)(GLenum texture);
|
void (APIENTRY * bglActiveTextureARB)(GLenum texture);
|
||||||
|
void (APIENTRY * bglClientActiveTextureARB)(GLenum texture);
|
||||||
void (APIENTRY * bglMultiTexCoord2dARB)(GLenum target, GLdouble s, GLdouble t );
|
void (APIENTRY * bglMultiTexCoord2dARB)(GLenum target, GLdouble s, GLdouble t );
|
||||||
void (APIENTRY * bglMultiTexCoord2fARB)(GLenum target, GLfloat s, GLfloat t );
|
void (APIENTRY * bglMultiTexCoord2fARB)(GLenum target, GLfloat s, GLfloat t );
|
||||||
|
|
||||||
|
@ -355,6 +356,7 @@ int loadglextensions(void)
|
||||||
|
|
||||||
// Multitexturing
|
// Multitexturing
|
||||||
bglActiveTextureARB = GETPROCEXTSOFT("glActiveTextureARB");
|
bglActiveTextureARB = GETPROCEXTSOFT("glActiveTextureARB");
|
||||||
|
bglClientActiveTextureARB = GETPROCEXTSOFT("glClientActiveTextureARB");
|
||||||
bglMultiTexCoord2dARB = GETPROCEXTSOFT("glMultiTexCoord2dARB");
|
bglMultiTexCoord2dARB = GETPROCEXTSOFT("glMultiTexCoord2dARB");
|
||||||
bglMultiTexCoord2fARB = GETPROCEXTSOFT("glMultiTexCoord2fARB");
|
bglMultiTexCoord2fARB = GETPROCEXTSOFT("glMultiTexCoord2fARB");
|
||||||
|
|
||||||
|
@ -489,6 +491,7 @@ int unloadgldriver(void)
|
||||||
|
|
||||||
// Multitexturing
|
// Multitexturing
|
||||||
bglActiveTextureARB = NULL;
|
bglActiveTextureARB = NULL;
|
||||||
|
bglClientActiveTextureARB = NULL;
|
||||||
bglMultiTexCoord2dARB = NULL;
|
bglMultiTexCoord2dARB = NULL;
|
||||||
bglMultiTexCoord2fARB = NULL;
|
bglMultiTexCoord2fARB = NULL;
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,9 @@ typedef struct
|
||||||
//MD3 specific
|
//MD3 specific
|
||||||
md3head_t head;
|
md3head_t head;
|
||||||
point3d *muladdframes;
|
point3d *muladdframes;
|
||||||
|
unsigned short *indexes;
|
||||||
|
unsigned short *vindexes;
|
||||||
|
float *maxdepths;
|
||||||
} md3model;
|
} md3model;
|
||||||
|
|
||||||
#define VOXBORDWIDTH 1 //use 0 to save memory, but has texture artifacts; 1 looks better...
|
#define VOXBORDWIDTH 1 //use 0 to save memory, but has texture artifacts; 1 looks better...
|
||||||
|
@ -1089,6 +1092,10 @@ m->basepath = (char *)malloc(i+1); if (!m->basepath) { free(m->uv); free(m->tris
|
||||||
m3->skinmap = sk;
|
m3->skinmap = sk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m3->indexes = malloc(sizeof(unsigned short) * s->numtris);
|
||||||
|
m3->vindexes = malloc(sizeof(unsigned short) * s->numtris * 3);
|
||||||
|
m3->maxdepths = malloc(sizeof(float) * s->numtris);
|
||||||
|
|
||||||
// die MD2 ! DIE !
|
// die MD2 ! DIE !
|
||||||
free(m->texid); free(m->skinfn); free(m->basepath); free(m->uv); free(m->tris); free(m->glcmds); free(m->frames); free(m);
|
free(m->texid); free(m->skinfn); free(m->basepath); free(m->uv); free(m->tris); free(m->glcmds); free(m->frames); free(m);
|
||||||
|
|
||||||
|
@ -1143,6 +1150,7 @@ static md3model *md3load (int fil)
|
||||||
{
|
{
|
||||||
char *buf, st[BMAX_PATH+2], bst[BMAX_PATH+2];
|
char *buf, st[BMAX_PATH+2], bst[BMAX_PATH+2];
|
||||||
long i, j, surfi, ofsurf, bsc, offs[4], leng[4];
|
long i, j, surfi, ofsurf, bsc, offs[4], leng[4];
|
||||||
|
long maxtrispersurf;
|
||||||
md3model *m;
|
md3model *m;
|
||||||
md3surf_t *s;
|
md3surf_t *s;
|
||||||
|
|
||||||
|
@ -1196,6 +1204,8 @@ if ((m->head.id != 0x33504449) && (m->head.vers != 15)) { free(m); return(0); }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
maxtrispersurf = 0;
|
||||||
|
|
||||||
for (surfi=0;surfi<m->head.numsurfs;surfi++)
|
for (surfi=0;surfi<m->head.numsurfs;surfi++)
|
||||||
{
|
{
|
||||||
s = &m->head.surfs[surfi];
|
s = &m->head.surfs[surfi];
|
||||||
|
@ -1256,6 +1266,7 @@ if ((m->head.id != 0x33504449) && (m->head.vers != 15)) { free(m); return(0); }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
maxmodelverts = max(maxmodelverts, s->numverts);
|
maxmodelverts = max(maxmodelverts, s->numverts);
|
||||||
|
maxtrispersurf = max(maxtrispersurf, s->numtris);
|
||||||
ofsurf += s->ofsend;
|
ofsurf += s->ofsend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1280,6 +1291,10 @@ if ((m->head.id != 0x33504449) && (m->head.vers != 15)) { free(m); return(0); }
|
||||||
if (!mdloadskin(&m->texid,&m->usesalpha,bst)) ;//bad!
|
if (!mdloadskin(&m->texid,&m->usesalpha,bst)) ;//bad!
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
m->indexes = malloc(sizeof(unsigned short) * maxtrispersurf);
|
||||||
|
m->vindexes = malloc(sizeof(unsigned short) * maxtrispersurf * 3);
|
||||||
|
m->maxdepths = malloc(sizeof(float) * maxtrispersurf);
|
||||||
|
|
||||||
return(m);
|
return(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1293,8 +1308,6 @@ static int md3draw (md3model *m, spritetype *tspr)
|
||||||
int texunits = GL_TEXTURE0_ARB;
|
int texunits = GL_TEXTURE0_ARB;
|
||||||
mdskinmap_t *sk;
|
mdskinmap_t *sk;
|
||||||
//PLAG : sorting stuff
|
//PLAG : sorting stuff
|
||||||
unsigned short *indexes;
|
|
||||||
float *maxdepths;
|
|
||||||
unsigned short tempus;
|
unsigned short tempus;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1581,9 +1594,6 @@ if (tspr->cstat&2) { if (!(tspr->cstat&512)) pc[3] = 0.66; else pc[3] = 0.33; }
|
||||||
//PLAG: delayed polygon-level sorted rendering
|
//PLAG: delayed polygon-level sorted rendering
|
||||||
if (m->usesalpha && !(tspr->cstat & 1024) && !r_depthpeeling)
|
if (m->usesalpha && !(tspr->cstat & 1024) && !r_depthpeeling)
|
||||||
{
|
{
|
||||||
indexes = malloc(sizeof(unsigned short) * s->numtris);
|
|
||||||
maxdepths = malloc(sizeof(float) * s->numtris);
|
|
||||||
|
|
||||||
// old sorting methods - dead code
|
// old sorting methods - dead code
|
||||||
/*for(i=s->numtris-1;i>=0;i--)
|
/*for(i=s->numtris-1;i>=0;i--)
|
||||||
{
|
{
|
||||||
|
@ -1652,8 +1662,8 @@ if (tspr->cstat&2) { if (!(tspr->cstat&512)) pc[3] = 0.66; else pc[3] = 0.33; }
|
||||||
if (f > g)
|
if (f > g)
|
||||||
f = g;
|
f = g;
|
||||||
|
|
||||||
maxdepths[i] = f;
|
m->maxdepths[i] = f;
|
||||||
indexes[i] = i;
|
m->indexes[i] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
//bubble sort - dead code
|
//bubble sort - dead code
|
||||||
|
@ -1677,13 +1687,32 @@ if (tspr->cstat&2) { if (!(tspr->cstat&512)) pc[3] = 0.66; else pc[3] = 0.33; }
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// dichotomic recursive sorting - about 100x less iterations than bubblesort
|
// dichotomic recursive sorting - about 100x less iterations than bubblesort
|
||||||
quicksort(indexes, maxdepths, 0, s->numtris - 1);
|
quicksort(m->indexes, m->maxdepths, 0, s->numtris - 1);
|
||||||
|
|
||||||
|
if (r_vertexarrays)
|
||||||
|
{
|
||||||
|
k = 0;
|
||||||
|
for (i=s->numtris-1;i>=0;i--)
|
||||||
|
for (j=0;j<3;j++)
|
||||||
|
m->vindexes[k++] = s->tris[m->indexes[i]].i[j];
|
||||||
|
|
||||||
|
bglVertexPointer(3, GL_FLOAT, 0, &(vertlist[0].x));
|
||||||
|
l = GL_TEXTURE0_ARB;
|
||||||
|
while (l <= texunits)
|
||||||
|
{
|
||||||
|
bglClientActiveTextureARB(l++);
|
||||||
|
bglEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
bglTexCoordPointer(2, GL_FLOAT, 0, &(s->uv[0].u));
|
||||||
|
}
|
||||||
|
bglDrawElements(GL_TRIANGLES, s->numtris * 3, GL_UNSIGNED_SHORT, m->vindexes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
bglBegin(GL_TRIANGLES);
|
bglBegin(GL_TRIANGLES);
|
||||||
for (i=s->numtris-1;i>=0;i--)
|
for (i=s->numtris-1;i>=0;i--)
|
||||||
for (j=0;j<3;j++)
|
for (j=0;j<3;j++)
|
||||||
{
|
{
|
||||||
k = s->tris[indexes[i]].i[j];
|
k = s->tris[m->indexes[i]].i[j];
|
||||||
if (texunits > GL_TEXTURE0_ARB)
|
if (texunits > GL_TEXTURE0_ARB)
|
||||||
{
|
{
|
||||||
l = GL_TEXTURE0_ARB;
|
l = GL_TEXTURE0_ARB;
|
||||||
|
@ -1695,9 +1724,26 @@ if (tspr->cstat&2) { if (!(tspr->cstat&512)) pc[3] = 0.66; else pc[3] = 0.33; }
|
||||||
bglVertex3fv((float *)&vertlist[k]);
|
bglVertex3fv((float *)&vertlist[k]);
|
||||||
}
|
}
|
||||||
bglEnd();
|
bglEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (r_vertexarrays)
|
||||||
|
{
|
||||||
|
k = 0;
|
||||||
|
for (i=s->numtris-1;i>=0;i--)
|
||||||
|
for (j=0;j<3;j++)
|
||||||
|
m->vindexes[k++] = s->tris[i].i[j];
|
||||||
|
|
||||||
free(indexes);
|
bglVertexPointer(3, GL_FLOAT, 0, &(vertlist[0].x));
|
||||||
free(maxdepths);
|
l = GL_TEXTURE0_ARB;
|
||||||
|
while (l <= texunits)
|
||||||
|
{
|
||||||
|
bglClientActiveTextureARB(l++);
|
||||||
|
bglEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
bglTexCoordPointer(2, GL_FLOAT, 0, &(s->uv[0].u));
|
||||||
|
}
|
||||||
|
bglDrawElements(GL_TRIANGLES, s->numtris * 3, GL_UNSIGNED_SHORT, m->vindexes);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1718,9 +1764,8 @@ if (tspr->cstat&2) { if (!(tspr->cstat&512)) pc[3] = 0.66; else pc[3] = 0.33; }
|
||||||
}
|
}
|
||||||
bglEnd();
|
bglEnd();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (texunits > GL_TEXTURE0_ARB)
|
|
||||||
{
|
|
||||||
while (texunits > GL_TEXTURE0_ARB)
|
while (texunits > GL_TEXTURE0_ARB)
|
||||||
{
|
{
|
||||||
bglMatrixMode(GL_TEXTURE);
|
bglMatrixMode(GL_TEXTURE);
|
||||||
|
@ -1728,8 +1773,12 @@ if (tspr->cstat&2) { if (!(tspr->cstat&512)) pc[3] = 0.66; else pc[3] = 0.33; }
|
||||||
bglMatrixMode(GL_MODELVIEW);
|
bglMatrixMode(GL_MODELVIEW);
|
||||||
bglTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1.0f);
|
bglTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1.0f);
|
||||||
bglDisable(GL_TEXTURE_2D);
|
bglDisable(GL_TEXTURE_2D);
|
||||||
bglActiveTextureARB(--texunits);
|
if (r_vertexarrays)
|
||||||
|
{
|
||||||
|
bglDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
bglClientActiveTextureARB(texunits - 1);
|
||||||
}
|
}
|
||||||
|
bglActiveTextureARB(--texunits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//------------
|
//------------
|
||||||
|
@ -1790,6 +1839,10 @@ static void md3free (md3model *m)
|
||||||
|
|
||||||
if (m->muladdframes) free(m->muladdframes);
|
if (m->muladdframes) free(m->muladdframes);
|
||||||
|
|
||||||
|
if (m->indexes) free(m->indexes);
|
||||||
|
if (m->vindexes) free(m->vindexes);
|
||||||
|
if (m->maxdepths) free(m->maxdepths);
|
||||||
|
|
||||||
free(m);
|
free(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,10 @@ long r_detailmapping = 1;
|
||||||
// Glow mapping cvar
|
// Glow mapping cvar
|
||||||
long r_glowmapping = 1;
|
long r_glowmapping = 1;
|
||||||
|
|
||||||
|
// Vertex Array model drawing cvar
|
||||||
|
long r_vertexarrays = 1;
|
||||||
|
|
||||||
|
|
||||||
static float fogresult, ofogresult, fogcol[4];
|
static float fogresult, ofogresult, fogcol[4];
|
||||||
|
|
||||||
static void fogcalc (signed char shade, char vis, char pal)
|
static void fogcalc (signed char shade, char vis, char pal)
|
||||||
|
@ -798,6 +802,9 @@ void polymost_glinit()
|
||||||
bglBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, peelprogram[1]);
|
bglBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, peelprogram[1]);
|
||||||
bglProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(peeledprogramstring), peeledprogramstring);
|
bglProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(peeledprogramstring), peeledprogramstring);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bglEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
bglEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resizeglcheck ()
|
void resizeglcheck ()
|
||||||
|
@ -5336,6 +5343,11 @@ static int osdcmd_polymostvars(const osdfuncparm_t *parm)
|
||||||
}
|
}
|
||||||
return OSDCMD_OK;
|
return OSDCMD_OK;
|
||||||
}
|
}
|
||||||
|
else if (!Bstrcasecmp(parm->name, "r_vertexarrays")) {
|
||||||
|
if (showval) { OSD_Printf("r_vertexarrays is %d\n", r_vertexarrays); }
|
||||||
|
else r_vertexarrays = (val != 0);
|
||||||
|
return OSDCMD_OK;
|
||||||
|
}
|
||||||
else if (!Bstrcasecmp(parm->name, "glpolygonmode")) {
|
else if (!Bstrcasecmp(parm->name, "glpolygonmode")) {
|
||||||
if (showval) { OSD_Printf("glpolygonmode is %d\n", glpolygonmode); }
|
if (showval) { OSD_Printf("glpolygonmode is %d\n", glpolygonmode); }
|
||||||
else glpolygonmode = val;
|
else glpolygonmode = val;
|
||||||
|
@ -5422,6 +5434,7 @@ void polymost_initosdfuncs(void)
|
||||||
OSD_RegisterFunction("r_curpeel","r_curpeel: allows to display one depth layer at a time (for development purposes)",osdcmd_polymostvars);
|
OSD_RegisterFunction("r_curpeel","r_curpeel: allows to display one depth layer at a time (for development purposes)",osdcmd_polymostvars);
|
||||||
OSD_RegisterFunction("r_detailmapping","r_detailmapping: enable/disable detail mapping",osdcmd_polymostvars);
|
OSD_RegisterFunction("r_detailmapping","r_detailmapping: enable/disable detail mapping",osdcmd_polymostvars);
|
||||||
OSD_RegisterFunction("r_glowmapping","r_glowmapping: enable/disable glow mapping",osdcmd_polymostvars);
|
OSD_RegisterFunction("r_glowmapping","r_glowmapping: enable/disable glow mapping",osdcmd_polymostvars);
|
||||||
|
OSD_RegisterFunction("r_vertexarrays","r_vertexarrays: enable/disable using vertex arrays when drawing models",osdcmd_polymostvars);
|
||||||
#endif
|
#endif
|
||||||
OSD_RegisterFunction("usemodels","usemodels: enable/disable model rendering in >8-bit mode",osdcmd_polymostvars);
|
OSD_RegisterFunction("usemodels","usemodels: enable/disable model rendering in >8-bit mode",osdcmd_polymostvars);
|
||||||
OSD_RegisterFunction("usehightile","usehightile: enable/disable hightile texture rendering in >8-bit mode",osdcmd_polymostvars);
|
OSD_RegisterFunction("usehightile","usehightile: enable/disable hightile texture rendering in >8-bit mode",osdcmd_polymostvars);
|
||||||
|
|
|
@ -681,6 +681,7 @@ int32 CONFIG_ReadSetup(void)
|
||||||
|
|
||||||
SCRIPT_GetNumber(scripthandle, "Screen Setup", "GLDetailMapping", &r_detailmapping);
|
SCRIPT_GetNumber(scripthandle, "Screen Setup", "GLDetailMapping", &r_detailmapping);
|
||||||
SCRIPT_GetNumber(scripthandle, "Screen Setup", "GLGlowMapping", &r_glowmapping);
|
SCRIPT_GetNumber(scripthandle, "Screen Setup", "GLGlowMapping", &r_glowmapping);
|
||||||
|
SCRIPT_GetNumber(scripthandle, "Screen Setup", "GLVertexArrays", &r_vertexarrays);
|
||||||
|
|
||||||
dummy = usemodels;
|
dummy = usemodels;
|
||||||
SCRIPT_GetNumber(scripthandle, "Screen Setup", "UseModels",&dummy);
|
SCRIPT_GetNumber(scripthandle, "Screen Setup", "UseModels",&dummy);
|
||||||
|
@ -838,6 +839,7 @@ void CONFIG_WriteSetup(void)
|
||||||
|
|
||||||
SCRIPT_PutNumber(scripthandle, "Screen Setup", "GLDetailMapping", r_detailmapping,false,false);
|
SCRIPT_PutNumber(scripthandle, "Screen Setup", "GLDetailMapping", r_detailmapping,false,false);
|
||||||
SCRIPT_PutNumber(scripthandle, "Screen Setup", "GLGlowMapping", r_glowmapping,false,false);
|
SCRIPT_PutNumber(scripthandle, "Screen Setup", "GLGlowMapping", r_glowmapping,false,false);
|
||||||
|
SCRIPT_PutNumber(scripthandle, "Screen Setup", "GLVertexArrays", r_vertexarrays,false,false);
|
||||||
#endif
|
#endif
|
||||||
#ifdef RENDERTYPEWIN
|
#ifdef RENDERTYPEWIN
|
||||||
SCRIPT_PutNumber(scripthandle, "Screen Setup", "MaxRefreshFreq",maxrefreshfreq,false,false);
|
SCRIPT_PutNumber(scripthandle, "Screen Setup", "MaxRefreshFreq",maxrefreshfreq,false,false);
|
||||||
|
|
Loading…
Reference in a new issue