diff options
Diffstat (limited to 'src/threed')
189 files changed, 59574 insertions, 0 deletions
diff --git a/src/threed/api/api.pri b/src/threed/api/api.pri new file mode 100644 index 000000000..0499f0dc7 --- /dev/null +++ b/src/threed/api/api.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +VPATH += $$PWD +HEADERS += qopenglfunctions.h +SOURCES += qopenglfunctions.cpp diff --git a/src/threed/api/gl-funcs.txt b/src/threed/api/gl-funcs.txt new file mode 100644 index 000000000..6bef6ce4e --- /dev/null +++ b/src/threed/api/gl-funcs.txt @@ -0,0 +1,858 @@ +void glActiveTexture(GLenum texture); + inline es1, es2 + alt_name glActiveTextureARB + +void glAttachShader(GLuint program, GLuint shader); + shader_only yes + inline es2 + alt_name glAttachObjectARB + +void glBindAttribLocation(GLuint program, GLuint index, const char* name); + shader_only yes + inline es2 + alt_name glBindAttribLocationARB + +void glBindBuffer(GLenum target, GLuint buffer); + inline es1, es2 + +void glBindFramebuffer(GLenum target, GLuint framebuffer); + inline es2 + +void glBindRenderbuffer(GLenum target, GLuint renderbuffer); + inline es2 + +void glBindTexture(GLenum target, GLuint texture); + inline all + +void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); + inline es2 + +void glBlendEquation( GLenum mode ); + inline es2 + +void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha); + inline es2 + +void glBlendFunc(GLenum sfactor, GLenum dfactor); + inline all + +void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + inline es2 + +void glBufferData(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage); + inline es1, es2 + +void glBufferSubData(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data); + inline es1, es2 + +GLenum glCheckFramebufferStatus(GLenum target); + inline es2 + +void glClear(GLbitfield mask); + inline all + +void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); + inline all + +void glClearDepth(GLclampf depth); + inline all_diff + es_name glClearDepthf + +void glClearStencil(GLint s); + inline all + +void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); + inline all + +void glCompileShader(GLuint shader); + shader_only yes + inline es2 + alt_name glCompileShader + +void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data); + inline es1, es2 + +void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data); + inline es1, es2 + +void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + inline all + +void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + inline all + +GLuint glCreateProgram(void); + shader_only yes + inline es2 + alt_name glCreateProgramObjectARB + +GLuint glCreateShader(GLenum type); + shader_only yes + inline es2 + alt_name glCreateShaderObjectARB + +void glCullFace(GLenum mode); + inline all + +void glDeleteBuffers(GLsizei n, const GLuint* buffers); + inline es1, es2 + +void glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers); + inline es2 + +void glDeleteProgram(GLuint program); + shader_only yes + inline es2 + alt_name glDeleteObjectARB + +void glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers); + inline es2 + +void glDeleteShader(GLuint shader); + shader_only yes + inline es2 + alt_name glDeleteObjectARB + +void glDeleteTextures(GLsizei n, const GLuint* textures); + inline all + +void glDepthFunc(GLenum func); + inline all + +void glDepthMask(GLboolean flag); + inline all + +void glDepthRange(GLclampf zNear, GLclampf zFar); + inline all_diff + es_name glDepthRangef + +void glDetachShader(GLuint program, GLuint shader); + shader_only yes + inline es2 + alt_name glDetachObjectARB + +void glDisable(GLenum cap); + inline all + +void glDisableVertexAttribArray(GLuint index); + shader_only yes + inline es2 + alt_name glDisableVertexAttribArrayARB + +void glDrawArrays(GLenum mode, GLint first, GLsizei count); + inline all + +void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices); + inline all + +void glEnable(GLenum cap); + inline all + +void glEnableVertexAttribArray(GLuint index); + shader_only yes + inline es2 + alt_name glEnableVertexAttribArrayARB + +void glFinish(void); + inline all + +void glFlush(void); + inline all + +void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + inline es2 + +void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + inline es2 + +void glFrontFace(GLenum mode); + inline all + +void glGenBuffers(GLsizei n, GLuint* buffers); + inline es1, es2 + +void glGenerateMipmap(GLenum target); + inline es2 + +void glGenFramebuffers(GLsizei n, GLuint* framebuffers); + inline es2 + +void glGenRenderbuffers(GLsizei n, GLuint* renderbuffers); + inline es2 + +void glGenTextures(GLsizei n, GLuint* textures); + inline all + +void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + shader_only yes + inline es2 + alt_name glGetActiveAttribARB + +void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + shader_only yes + inline es2 + alt_name glGetActiveUniformARB + +void glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); + shader_only yes + inline es2 + alt_name glGetAttachedObjectsARB + +int glGetAttribLocation(GLuint program, const char* name); + shader_only yes + inline es2 + alt_name glGetAttribLocationARB + +void glGetBooleanv(GLenum pname, GLboolean* params); + inline all + +void glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params); + inline es2 + +GLenum glGetError(void); + inline all + +void glGetFloatv(GLenum pname, GLfloat* params); + inline all + +void glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params); + inline es2 + +void glGetIntegerv(GLenum pname, GLint* params); + inline all + +void glGetProgramiv(GLuint program, GLenum pname, GLint* params); + shader_only yes + inline es2 + alt_name glGetObjectParameterivARB + +void glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog); + shader_only yes + inline es2 + alt_name glGetInfoLogARB + +void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params); + inline es2 + +void glGetShaderiv(GLuint shader, GLenum pname, GLint* params); + shader_only yes + inline es2 + alt_name glGetObjectParameterivARB + +void glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog); + shader_only yes + inline es2 + alt_name glGetInfoLogARB + +void glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); + shader_only yes + inline es2 + special_handling yes + +void glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source); + shader_only yes + inline es2 + alt_name glGetShaderSourceARB + +const GLubyte* glGetString(GLenum name); + inline all + +void glGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params); + inline all + +void glGetTexParameteriv(GLenum target, GLenum pname, GLint* params); + inline all + +void glGetUniformfv(GLuint program, GLint location, GLfloat* params); + shader_only yes + inline es2 + alt_name glGetUniformfvARB + +void glGetUniformiv(GLuint program, GLint location, GLint* params); + shader_only yes + inline es2 + alt_name glGetUniformivARB + +int glGetUniformLocation(GLuint program, const char* name); + shader_only yes + inline es2 + alt_name glGetUniformLocationARB + +void glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params); + shader_only yes + inline es2 + alt_name glGetVertexAttribfvARB + +void glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params); + shader_only yes + inline es2 + alt_name glGetVertexAttribivARB + +void glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer); + shader_only yes + inline es2 + alt_name glGetVertexAttribPointervARB + +void glHint(GLenum target, GLenum mode); + inline all + +GLboolean glIsBuffer(GLuint buffer); + inline es1, es2 + +GLboolean glIsEnabled(GLenum cap); + inline all + +GLboolean glIsFramebuffer(GLuint framebuffer); + inline es2 + +GLboolean glIsProgram(GLuint program); + shader_only yes + inline es2 + alt_name glIsProgramARB + special_handling yes + +GLboolean glIsRenderbuffer(GLuint renderbuffer); + inline es2 + +GLboolean glIsShader(GLuint shader); + shader_only yes + inline es2 + alt_name glIsShaderARB + special_handling yes + +GLboolean glIsTexture(GLuint texture); + inline all + +void glLineWidth(GLfloat width); + inline all + +void glLinkProgram(GLuint program); + shader_only yes + inline es2 + alt_name glLinkProgramARB + +void glPixelStorei(GLenum pname, GLint param); + inline all + +void glPolygonOffset(GLfloat factor, GLfloat units); + inline all + +void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels); + inline all + +void glReleaseShaderCompiler(void); + shader_only yes + inline es2 + alt_name glReleaseShaderCompilerARB + special_handling yes + +void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + inline es2 + +void glSampleCoverage(GLclampf value, GLboolean invert); + inline es1, es2 + +void glScissor(GLint x, GLint y, GLsizei width, GLsizei height); + inline all + +void glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length); + shader_only yes + inline es2 + alt_name glShaderBinaryARB + +void glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length); + shader_only yes + inline es2 + alt_name glShaderSourceARB + +void glStencilFunc(GLenum func, GLint ref, GLuint mask); + inline all + +void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask); + inline es2 + +void glStencilMask(GLuint mask); + inline all + +void glStencilMaskSeparate(GLenum face, GLuint mask); + inline es2 + +void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass); + inline all + +void glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass); + inline es2 + +void glTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels); + inline all + +void glTexParameterf(GLenum target, GLenum pname, GLfloat param); + inline all + +void glTexParameterfv(GLenum target, GLenum pname, const GLfloat* params); + inline all + +void glTexParameteri(GLenum target, GLenum pname, GLint param); + inline all + +void glTexParameteriv(GLenum target, GLenum pname, const GLint* params); + inline all + +void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels); + inline all + +void glUniform1f(GLint location, GLfloat x); + shader_only yes + inline es2 + alt_name glUniform1fARB + +void glUniform1fv(GLint location, GLsizei count, const GLfloat* v); + shader_only yes + inline es2 + alt_name glUniform1fvARB + +void glUniform1i(GLint location, GLint x); + shader_only yes + inline es2 + alt_name glUniform1iARB + +void glUniform1iv(GLint location, GLsizei count, const GLint* v); + shader_only yes + inline es2 + alt_name glUniform1ivARB + +void glUniform2f(GLint location, GLfloat x, GLfloat y); + shader_only yes + inline es2 + alt_name glUniform2fARB + +void glUniform2fv(GLint location, GLsizei count, const GLfloat* v); + shader_only yes + inline es2 + alt_name glUniform2fvARB + +void glUniform2i(GLint location, GLint x, GLint y); + shader_only yes + inline es2 + alt_name glUniform2iARB + +void glUniform2iv(GLint location, GLsizei count, const GLint* v); + shader_only yes + inline es2 + alt_name glUniform2ivARB + +void glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z); + shader_only yes + inline es2 + alt_name glUniform3fARB + +void glUniform3fv(GLint location, GLsizei count, const GLfloat* v); + shader_only yes + inline es2 + alt_name glUniform3fvARB + +void glUniform3i(GLint location, GLint x, GLint y, GLint z); + shader_only yes + inline es2 + alt_name glUniform3iARB + +void glUniform3iv(GLint location, GLsizei count, const GLint* v); + shader_only yes + inline es2 + alt_name glUniform3ivARB + +void glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + shader_only yes + inline es2 + alt_name glUniform4fARB + +void glUniform4fv(GLint location, GLsizei count, const GLfloat* v); + shader_only yes + inline es2 + alt_name glUniform4fvARB + +void glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w); + shader_only yes + inline es2 + alt_name glUniform4iARB + +void glUniform4iv(GLint location, GLsizei count, const GLint* v); + shader_only yes + inline es2 + alt_name glUniform4ivARB + +void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + shader_only yes + inline es2 + alt_name glUniformMatrix2fvARB + +void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + shader_only yes + inline es2 + alt_name glUniformMatrix3fvARB + +void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + shader_only yes + inline es2 + alt_name glUniformMatrix4fvARB + +void glUseProgram(GLuint program); + shader_only yes + inline es2 + alt_name glUseProgramObjectARB + +void glValidateProgram(GLuint program); + shader_only yes + inline es2 + alt_name glValidateProgramARB + +void glVertexAttrib1f(GLuint indx, GLfloat x); + shader_only yes + inline es2 + alt_name glVertexAttrib1fARB + +void glVertexAttrib1fv(GLuint indx, const GLfloat* values); + shader_only yes + inline es2 + alt_name glVertexAttrib1fvARB + +void glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y); + shader_only yes + inline es2 + alt_name glVertexAttrib2fARB + +void glVertexAttrib2fv(GLuint indx, const GLfloat* values); + shader_only yes + inline es2 + alt_name glVertexAttrib2fvARB + +void glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z); + shader_only yes + inline es2 + alt_name glVertexAttrib3fARB + +void glVertexAttrib3fv(GLuint indx, const GLfloat* values); + shader_only yes + inline es2 + alt_name glVertexAttrib3fvARB + +void glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + shader_only yes + inline es2 + alt_name glVertexAttrib4fARB + +void glVertexAttrib4fv(GLuint indx, const GLfloat* values); + shader_only yes + inline es2 + alt_name glVertexAttrib4fvARB + +void glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr); + shader_only yes + inline es2 + alt_name glVertexAttribPointerARB + +void glViewport(GLint x, GLint y, GLsizei width, GLsizei height); + inline all + +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALPHA 0x1906 +#define GL_ALPHA_BITS 0x0D55 +#define GL_ALWAYS 0x0207 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_BACK 0x0405 +#define GL_BLEND 0x0BE2 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_EQUATION 0x8009 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLUE_BITS 0x0D54 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_BYTE 0x1400 +#define GL_CCW 0x0901 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_CW 0x0900 +#define GL_DECR 0x1E03 +#define GL_DECR_WRAP 0x8508 +#define GL_DELETE_STATUS 0x8B80 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_DEPTH_BITS 0x0D56 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DITHER 0x0BD0 +#define GL_DONT_CARE 0x1100 +#define GL_DST_ALPHA 0x0304 +#define GL_DST_COLOR 0x0306 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_EQUAL 0x0202 +#define GL_EXTENSIONS 0x1F03 +#define GL_FALSE 0 +#define GL_FASTEST 0x1101 +#define GL_FIXED 0x140C +#define GL_FLOAT 0x1406 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_FRONT 0x0404 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_FRONT_FACE 0x0B46 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_GEQUAL 0x0206 +#define GL_GREATER 0x0204 +#define GL_GREEN_BITS 0x0D53 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_HIGH_INT 0x8DF5 +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_INCR 0x1E02 +#define GL_INCR_WRAP 0x8507 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_INT 0x1404 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_INVALID_OPERATION 0x0502 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVERT 0x150A +#define GL_KEEP 0x1E00 +#define GL_LEQUAL 0x0203 +#define GL_LESS 0x0201 +#define GL_LINEAR 0x2601 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINE_LOOP 0x0002 +#define GL_LINES 0x0001 +#define GL_LINE_STRIP 0x0003 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINK_STATUS 0x8B82 +#define GL_LOW_FLOAT 0x8DF0 +#define GL_LOW_INT 0x8DF3 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_NEAREST 0x2600 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEVER 0x0200 +#define GL_NICEST 0x1102 +#define GL_NO_ERROR 0 +#define GL_NONE 0 +#define GL_NOTEQUAL 0x0205 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_ONE 1 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_POINTS 0x0000 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_RED_BITS 0x0D52 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERER 0x1F01 +#define GL_REPEAT 0x2901 +#define GL_REPLACE 0x1E01 +#define GL_RGB 0x1907 +#define GL_RGB565 0x8D62 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA 0x1908 +#define GL_RGBA4 0x8056 +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLES 0x80A9 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_SHADER_TYPE 0x8B4F +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_SHORT 0x1402 +#define GL_SRC_ALPHA 0x0302 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_SRC_COLOR 0x0300 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_STENCIL_BITS 0x0D57 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_INDEX 0x1901 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STREAM_DRAW 0x88E0 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE 0x1702 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE31 0x84DF +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRUE 1 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_VENDOR 0x1F00 +#define GL_VERSION 0x1F02 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_VIEWPORT 0x0BA2 +#define GL_ZERO 0 diff --git a/src/threed/api/gl-gen-funcs.pl b/src/threed/api/gl-gen-funcs.pl new file mode 100755 index 000000000..b42551e2d --- /dev/null +++ b/src/threed/api/gl-gen-funcs.pl @@ -0,0 +1,483 @@ +#!/usr/bin/perl +############################################################################# +## +## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +## All rights reserved. +## Contact: Nokia Corporation (qt-info@nokia.com) +## +## This file is part of the Qt3D module of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## No Commercial Usage +## This file contains pre-release code and may not be distributed. +## You may use this file in accordance with the terms and conditions +## contained in the Technology Preview License Agreement accompanying +## this package. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 2.1 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 2.1 requirements +## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +## +## In addition, as a special exception, Nokia gives you certain additional +## rights. These rights are described in the Nokia Qt LGPL Exception +## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +## +## If you have questions regarding the use of this file, please contact +## Nokia at qt-info@nokia.com. +## +## +## +## +## +## +## +## +## $QT_END_LICENSE$ +## +############################################################################# + +# +# USAGE: +# +# perl gl-gen-funcs.pl < gl-functs.txt > output.txt +# +# This will automatically generate the qopenglfunctions.h/.cpp files for Qt3D +# based on the platform and available functions/capabilities. +# +# The file output.txt will contain the text for the header and implementation +# files - it is left to the user to manually cut and paste this content into +# their own qopenglfunctions.h and qopenglfunctions.cpp files. +# +# Note: this script is intended for perl version 5.10.1 or better, and has not +# been tested on previous versions of perl. +# + +use strict; +use warnings; + +use Data::Dumper; + +# Read the function definitions from the input. +my @functions = (); +my @macros = (); +my %func_info = (); +while (<>) +{ + my ($returnType, $name, $argstr) = + m/^(\w+|const GLubyte\*)\s+(\w+)\s*\(([^)]*)\)/; + + if (!$returnType) { + my ($macroname, $macrovalue) = m/^#define\s+(\w+)\s+(.*)$/; + if ($macroname) { + my %macro_info = (); + $macro_info{'name'} = $macroname; + $macro_info{'value'} = $macrovalue; + push @macros, { %macro_info }; + next; + } + my ($tag, $value) = m/^\s+(\w+)\s+(.*)$/; + next unless $tag; + $func_info{$tag} = $value; + $func_info{'funcname'} = $value if ($tag eq "es_name"); + next; + } + + if ($func_info{'name'}) { + push @functions, { %func_info }; + %func_info = (); + } + + $argstr =~ s/^\s+//; + $argstr =~ s/\s+$//; + my @args = split /,\s*/,$argstr; + + my @argNames = (); + if ($argstr ne 'void') { + foreach (@args) { + my ($argType, $argName) = m/^(\w+|\w+.*\*)\s+(\w+)$/; + push @argNames, $argName; + } + } else { + $argstr = ""; + } + my $argnamestr = join(', ', @argNames); + + $func_info{'name'} = $name; + $func_info{'funcname'} = $name; + $name =~ s/^gl//; + $func_info{'varname'} = lcfirst($name); + $func_info{'returnType'} = $returnType; + $func_info{'argstr'} = $argstr; + $func_info{'argnamestr'} = $argnamestr; +} +if ($func_info{'name'}) { + push @functions, { %func_info }; +} + +# Generate the declarations for qopenglfunctions.h. +print "// qopenglfunctions.h\n\n"; +print "#ifdef Q_WS_WIN\n"; +print "# define QT3D_GLF_APIENTRY APIENTRY\n"; +print "#endif\n"; +print "\n"; +print "#ifndef Q_WS_MAC\n"; +print "# ifndef QT3D_GLF_APIENTRYP\n"; +print "# ifdef QT3D_GLF_APIENTRY\n"; +print "# define QT3D_GLF_APIENTRYP QT3D_GLF_APIENTRY *\n"; +print "# else\n"; +print "# define QT3D_GLF_APIENTRY\n"; +print "# define QT3D_GLF_APIENTRYP *\n"; +print "# endif\n"; +print "# endif\n"; +print "#else\n"; +print "# define QT3D_GLF_APIENTRY\n"; +print "# define QT3D_GLF_APIENTRYP *\n"; +print "#endif\n"; +print "\n"; +print "struct QOpenGLFunctionsPrivate;\n"; +print "\n"; + +print "// Undefine any macros from GLEW, qglextensions_p.h, etc that\n"; +print "// may interfere with the definition of QOpenGLFunctions.\n"; +foreach ( @functions ) { + my $inline = $_->{'inline'}; + next if ($inline && $inline eq 'all'); + my $name = $_->{'funcname'}; + print "#undef $name\n"; +} +print "\n"; + +# Output the prototypes into the QOpenGLFunctions class. +print "class Q_QT3D_EXPORT QOpenGLFunctions\n"; +print "{\n"; +print "public:\n"; +print " QOpenGLFunctions();\n"; +print " explicit QOpenGLFunctions(const QGLContext *context);\n"; +print " ~QOpenGLFunctions() {}\n"; +print "\n"; +print " enum OpenGLFeature\n"; +print " {\n"; +print " Multitexture = 0x0001,\n"; +print " Shaders = 0x0002,\n"; +print " Buffers = 0x0004,\n"; +print " Framebuffers = 0x0008,\n"; +print " BlendColor = 0x0010,\n"; +print " BlendEquation = 0x0020,\n"; +print " BlendEquationSeparate = 0x0040,\n"; +print " BlendFuncSeparate = 0x0080,\n"; +print " BlendSubtract = 0x0100,\n"; +print " CompressedTextures = 0x0200,\n"; +print " Multisample = 0x0400,\n"; +print " StencilSeparate = 0x0800,\n"; +print " NPOTTextures = 0x1000\n"; +print " };\n"; +print " Q_DECLARE_FLAGS(OpenGLFeatures, OpenGLFeature)\n"; +print "\n"; +print " QOpenGLFunctions::OpenGLFeatures openGLFeatures() const;\n"; +print " bool hasOpenGLFeature(QOpenGLFunctions::OpenGLFeature feature) const;\n"; +print "\n"; +print " void initializeGLFunctions(const QGLContext *context = 0);\n"; +print "\n"; +my $last_shader_only = 0; +foreach ( @functions ) { + my $inline = $_->{'inline'}; + next if ($inline && $inline eq 'all'); + my $shader_only = ($_->{'shader_only'} && $_->{'shader_only'} eq 'yes'); + my $name = $_->{'funcname'}; + #print "#ifndef QT_OPENGL_ES_1\n" if ($shader_only && !$last_shader_only); + #print "#endif\n" if (!$shader_only && $last_shader_only); + print " $_->{'returnType'} $name($_->{'argstr'});\n"; + $last_shader_only = $shader_only; +} +#print "#endif\n" if $last_shader_only; + +print "\n"; +print "private:\n"; +print " QOpenGLFunctionsPrivate *d_ptr;\n"; +print " static bool isInitialized(const QOpenGLFunctionsPrivate *d) { return d != 0; }\n"; +print "};\n"; +print "\n"; +print "Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLFunctions::OpenGLFeatures)\n"; +print "\n"; +print "struct QOpenGLFunctionsPrivate\n"; +print "{\n"; +print " QOpenGLFunctionsPrivate(const QGLContext *context = 0);\n"; +print "\n"; +print "#ifndef QT_OPENGL_ES_2"; +print "\n"; + +# Output the function pointers into the QOpenGLFunctionsPrivate class. +$last_shader_only = 0; +foreach ( @functions ) { + my $shader_only = ($_->{'shader_only'} && $_->{'shader_only'} eq 'yes'); + my $inline = $_->{'inline'}; + next if ($inline && $inline eq 'all'); + next if $inline && $inline eq 'all_diff'; + my $name = $_->{'varname'}; + #print "#ifndef QT_OPENGL_ES_1\n" if ($shader_only && !$last_shader_only); + #print "#endif\n" if (!$shader_only && $last_shader_only); + print " $_->{'returnType'} (QT3D_GLF_APIENTRYP $name)($_->{'argstr'});\n"; + $last_shader_only = $shader_only; +} +#print "#endif\n" if $last_shader_only; + +print "#endif\n"; +print "};\n"; +print "\n"; + +my %platform_defines = (); +$platform_defines{'es1'} = "defined(QT_OPENGL_ES_1)"; +$platform_defines{'es2'} = "defined(QT_OPENGL_ES_2)"; +$platform_defines{'es'} = "defined(QT_OPENGL_ES)"; +$platform_defines{'desktop'} = "!defined(QT_OPENGL_ES)"; + +# Output the inline functions that call either the raw GL function +# or resolve via the function pointer. +$last_shader_only = 0; +foreach ( @functions ) { + my $shader_only = ($_->{'shader_only'} && $_->{'shader_only'} eq 'yes'); + my $funcname = $_->{'funcname'}; + my $varname = $_->{'varname'}; + my $is_void = ($_->{'returnType'} eq 'void'); + my $inline = $_->{'inline'}; + next if ($inline && $inline eq 'all'); + #print "#ifndef QT_OPENGL_ES_1\n\n" if ($shader_only && !$last_shader_only); + #print "#endif\n\n" if (!$shader_only && $last_shader_only); + print "inline $_->{'returnType'} QOpenGLFunctions::$funcname($_->{'argstr'})\n"; + print "{\n"; + if ($_->{'es_name'}) { + # Functions like glClearDepth() that are inline, but named differently. + print "#ifndef QT_OPENGL_ES\n"; + print " ::$_->{'name'}($_->{'argnamestr'});\n"; + print "#else\n"; + print " ::$_->{'es_name'}($_->{'argnamestr'});\n"; + print "#endif\n"; + } elsif ($inline && $inline eq 'all') { + # Inlined on all platforms. + if ($is_void) { + print (" "); + } else { + print (" return "); + } + print "::$_->{'name'}($_->{'argnamestr'});\n"; + } elsif ($inline) { + # Inlined only on certain platforms. + my @platforms = split /,\s*/,$inline; + my @defines = (); + foreach (@platforms) { + push @defines, $platform_defines{$_}; + } + print "#if "; + print join(' || ', @defines); + print "\n"; + if ($is_void) { + print (" "); + } else { + print (" return "); + } + print "::$_->{'name'}($_->{'argnamestr'});\n"; + print "#else\n"; + print " Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr));\n"; + if ($is_void) { + print (" "); + } else { + print (" return "); + } + print "d_ptr->$varname($_->{'argnamestr'});\n"; + print "#endif\n"; + } else { + # Resolve on all platforms. + print " Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr));\n"; + if ($is_void) { + print (" "); + } else { + print (" return "); + } + print "d_ptr->$varname($_->{'argnamestr'});\n"; + } + print "}\n\n"; + $last_shader_only = $shader_only; +} +#print "#endif\n" if $last_shader_only; + +# Output the macro definitions. +foreach ( @macros ) { + my $name = $_->{'name'}; + my $value = $_->{'value'}; + print "#ifndef $name\n"; + print "#define $name $value\n"; + print "#endif\n"; +} +print "\n"; + +print "// qopenglfunctions.cpp\n\n"; + +# Generate qdoc documentation for all of the functions. +foreach ( @functions ) { + my $inline = $_->{'inline'}; + next if $inline && $inline eq 'all'; + + my $shader_only = ($_->{'shader_only'} && $_->{'shader_only'} eq 'yes'); + + my $name = $_->{'funcname'}; + + my $docargs = $_->{'argnamestr'}; + if (length($docargs) > 0) { + $docargs =~ s/,/, \\a/g; + $docargs =~ s/^/\\a /; + } + + my $khronos_name = $_->{'es_name'}; + if (!$khronos_name) { + $khronos_name = $_->{'name'}; + } + + print "/*!\n"; + print " \\fn $_->{'returnType'} QOpenGLFunctions::$name($_->{'argstr'})\n"; + print "\n"; + if ($khronos_name eq $_->{'name'}) { + print " Convenience function that calls $khronos_name($docargs).\n"; + } else { + print " Convenience function that calls $_->{'name'}($docargs) on\n"; + print " desktop OpenGL systems and $khronos_name($docargs) on\n"; + print " embedded OpenGL/ES systems.\n"; + } + print "\n"; + print " For more information, see the OpenGL/ES 2.0 documentation for\n"; + print " \\l{http://www.khronos.org/opengles/sdk/docs/man/$khronos_name.xml}{$khronos_name()}.\n"; + if ($shader_only) { + print "\n"; + print " This convenience function will do nothing on OpenGL/ES 1.x systems.\n"; + } + print "*/\n\n"; +} + +# Generate the resolver functions. +print "#ifndef QT_OPENGL_ES_2\n\n"; +$last_shader_only = 0; +foreach ( @functions ) { + my $inline = $_->{'inline'}; + next if $inline && $inline eq 'all'; + next if $inline && $inline eq 'all_diff'; + my $shader_only = ($_->{'shader_only'} && $_->{'shader_only'} eq 'yes'); + my $name = $_->{'varname'}; + my $resolver_name = $_->{'name'}; + $resolver_name =~ s/^gl/qglfResolve/; + my $special_name = $_->{'name'}; + $special_name =~ s/^gl/qglfSpecial/; + my @platforms = split /,\s*/,$inline; + $shader_only = 1 if @platforms ~~ 'es1'; + my $is_void = ($_->{'returnType'} eq 'void'); + my $special_handling = ($_->{'special_handling'} && $_->{'special_handling'} eq 'yes'); + #print "#ifndef QT_OPENGL_ES_1\n\n" if ($shader_only && !$last_shader_only); + #print "#endif\n\n" if (!$shader_only && $last_shader_only); + + if ($special_handling) { + # Output special fallback implementations for certain functions. + if ($name eq "getShaderPrecisionFormat") { + print "static $_->{'returnType'} QT3D_GLF_APIENTRY $special_name($_->{'argstr'})\n"; + print "{\n"; + print " Q_UNUSED(shadertype);\n"; + print " Q_UNUSED(precisiontype);\n"; + print " range[0] = range[1] = precision[0] = 0;\n"; + print "}\n\n"; + } elsif ($name eq "isProgram" || $name eq "isShader") { + print "static $_->{'returnType'} QT3D_GLF_APIENTRY $special_name($_->{'argstr'})\n"; + print "{\n"; + print " return $_->{'argnamestr'} != 0;\n"; + print "}\n\n"; + } elsif ($name eq "releaseShaderCompiler") { + print "static $_->{'returnType'} QT3D_GLF_APIENTRY $special_name($_->{'argstr'})\n"; + print "{\n"; + print "}\n\n"; + } + } + + print "static $_->{'returnType'} QT3D_GLF_APIENTRY $resolver_name($_->{'argstr'})\n"; + print "{\n"; + my $type_name = "type_$_->{'name'}"; + print " typedef $_->{'returnType'} (QT3D_GLF_APIENTRYP $type_name)($_->{'argstr'});\n\n"; + print " const QGLContext *context = QGLContext::currentContext();\n"; + print " QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context);\n"; + print "\n"; + print " funcs->$name = ($type_name)\n"; + print " context->getProcAddress(QLatin1String(\"$_->{'name'}\"));\n"; + my @alt_names = (); + if ($_->{'alt_name'}) { + push @alt_names, $_->{'alt_name'}; + } else { + push @alt_names, "$_->{'name'}OES"; + push @alt_names, "$_->{'name'}EXT"; + push @alt_names, "$_->{'name'}ARB"; + } + foreach (@alt_names) { + print "#ifdef QT_OPENGL_ES\n" if /OES/; + print " if (!funcs->$name) {\n"; + print " funcs->$name = ($type_name)\n"; + print " context->getProcAddress(QLatin1String(\"$_\"));\n"; + print " }\n"; + print "#endif\n" if /OES/; + } + if ($special_handling) { + print "\n"; + print " if (!funcs->$name)\n"; + print " funcs->$name = $special_name;\n\n"; + if ($is_void) { + print (" "); + } else { + print (" return "); + } + print "funcs->$name($_->{'argnamestr'});\n"; + } else { + print "\n"; + print " if (funcs->$name)\n"; + if ($is_void) { + print (" "); + } else { + print (" return "); + } + print "funcs->$name($_->{'argnamestr'});\n"; + if ($is_void) { + print " else\n"; + print " funcs->$name = $resolver_name;\n"; + } else { + print " funcs->$name = $resolver_name;\n"; + print " return $_->{'returnType'}(0);\n"; + } + } + print "}\n\n"; + + $last_shader_only = $shader_only; +} +#print "#endif\n" if $last_shader_only; +print "#endif // !QT_OPENGL_ES_2\n\n"; + +# Generate the initialization code for QOpenGLFunctionsPrivate. +print "QOpenGLFunctionsPrivate::QOpenGLFunctionsPrivate(const QGLContext *)\n"; +print "{\n"; +print "#ifndef QT_OPENGL_ES_2\n"; +$last_shader_only = 0; +foreach ( @functions ) { + my $inline = $_->{'inline'}; + next if $inline && $inline eq 'all'; + next if $inline && $inline eq 'all_diff'; + my $shader_only = ($_->{'shader_only'} && $_->{'shader_only'} eq 'yes'); + my $name = $_->{'varname'}; + my $resolver_name = $_->{'name'}; + $resolver_name =~ s/^gl/qglfResolve/; + my @platforms = split /,\s*/,$inline; + $shader_only = 1 if @platforms ~~ 'es1'; + #print "#ifndef QT_OPENGL_ES_1\n" if ($shader_only && !$last_shader_only); + #print "#endif\n" if (!$shader_only && $last_shader_only); + print " $name = $resolver_name;\n"; + $last_shader_only = $shader_only; +} +#print "#endif\n" if $last_shader_only; +print "#endif // !QT_OPENGL_ES_2\n"; +print "}\n\n"; + +#print Dumper(\@functions); diff --git a/src/threed/api/qopenglfunctions.cpp b/src/threed/api/qopenglfunctions.cpp new file mode 100644 index 000000000..fd5e1f072 --- /dev/null +++ b/src/threed/api/qopenglfunctions.cpp @@ -0,0 +1,3690 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qopenglfunctions.h" +#include "qglext_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLFunctions + \brief The QOpenGLFunctions class provides cross-platform access to the OpenGL/ES 2.0 API. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::enablers + + OpenGL/ES 2.0 defines a subset of the OpenGL specification that is + common across many desktop and embedded OpenGL implementations. + However, it can be difficult to use the functions from that subset + because they need to be resolved manually on desktop systems. + + QOpenGLFunctions provides a guaranteed API that is available on all + OpenGL systems and takes care of function resolution on systems + that need it. The recommended way to use QOpenGLFunctions is by + direct inheritance: + + \code + class MyGLWidget : public QGLWidget, protected QOpenGLFunctions + { + Q_OBJECT + public: + MyGLWidget(QWidget *parent = 0) : QGLWidget(parent) {} + + protected: + void initializeGL(); + void paintGL(); + }; + + void MyGLWidget::initializeGL() + { + initializeGLFunctions(); + } + \endcode + + The \c{paintGL()} function can then use any of the OpenGL/ES 2.0 + functions without explicit resolution, such as glActiveTexture() + in the following example: + + \code + void MyGLWidget::paintGL() + { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, textureId); + ... + } + \endcode + + QOpenGLFunctions can also be used directly for ad-hoc invocation + of OpenGL/ES 2.0 functions on all platforms: + + \code + QOpenGLFunctions glFuncs(QGLContext::currentContext()); + glFuncs.glActiveTexture(GL_TEXTURE1); + \endcode + + QOpenGLFunctions provides wrappers for all OpenGL/ES 2.0 functions, + except those like \c{glDrawArrays()}, \c{glViewport()}, and + \c{glBindTexture()} that don't have portability issues. + + Including the header for QOpenGLFunctions will also define all of + the OpenGL/ES 2.0 macro constants that are not already defined by + the system's OpenGL headers, such as \c{GL_TEXTURE1} above. + + The hasOpenGLFeature() and openGLFeatures() functions can be used + to determine if the OpenGL implementation has a major OpenGL/ES 2.0 + feature. For example, the following checks if non power of two + textures are available: + + \code + QOpenGLFunctions funcs(QGLContext::currentContext()); + bool npot = funcs.hasOpenGLFeature(QOpenGLFunctions::NPOTTextures); + \endcode +*/ + +/*! + \enum QOpenGLFunctions::OpenGLFeature + This enum defines OpenGL/ES 2.0 features that may be optional + on other platforms. + + \value Multitexture glActiveTexture() function is available. + \value Shaders Shader functions are available. + \value Buffers Vertex and index buffer functions are available. + \value Framebuffers Framebuffer object functions are available. + \value BlendColor glBlendColor() is available. + \value BlendEquation glBlendEquation() is available. + \value BlendEquationSeparate glBlendEquationSeparate() is available. + \value BlendFuncSeparate glBlendFuncSeparate() is available. + \value BlendSubtract Blend subtract mode is available. + \value CompressedTextures Compressed texture functions are available. + \value Multisample glSampleCoverage() function is available. + \value StencilSeparate Separate stencil functions are available. + \value NPOTTextures Non power of two textures are available. +*/ + +// Hidden private fields for additional extension data. +// Hidden private fields for additional extension data. +struct QOpenGLFunctionsPrivateEx : public QOpenGLFunctionsPrivate +{ + QOpenGLFunctionsPrivateEx(const QGLContext *context = 0) + : QOpenGLFunctionsPrivate(context) + , m_features(-1) {} + + int m_features; +}; + +Q_GLOBAL_STATIC(QGLResource<QOpenGLFunctionsPrivateEx>, qt_gl_functions_resource) + +static QOpenGLFunctionsPrivateEx *qt_gl_functions(const QGLContext *context = 0) +{ + if (!context) + context = QGLContext::currentContext(); + Q_ASSERT(context); + return qt_gl_functions_resource()->value(context); +} + +/*! + Constructs a default function resolver. The resolver cannot + be used until initializeGLFunctions() is called to specify + the context. + + \sa initializeGLFunctions() +*/ +QOpenGLFunctions::QOpenGLFunctions() + : d_ptr(0) +{ +} + +/*! + Constructs a function resolver for \a context. If \a context + is null, then the resolver will be created for the current QGLContext. + + An object constructed in this way can only be used with \a context + and other contexts that share with it. Use initializeGLFunctions() + to change the object's context association. + + \sa initializeGLFunctions() +*/ +QOpenGLFunctions::QOpenGLFunctions(const QGLContext *context) + : d_ptr(qt_gl_functions(context)) +{ +} + +/*! + \fn QOpenGLFunctions::~QOpenGLFunctions() + + Destroys this function resolver. +*/ + +static int qt_gl_resolve_features() +{ +#if defined(QT_OPENGL_ES_2) + return QOpenGLFunctions::Multitexture | + QOpenGLFunctions::Shaders | + QOpenGLFunctions::Buffers | + QOpenGLFunctions::Framebuffers | + QOpenGLFunctions::BlendColor | + QOpenGLFunctions::BlendEquation | + QOpenGLFunctions::BlendEquationSeparate | + QOpenGLFunctions::BlendFuncSeparate | + QOpenGLFunctions::BlendSubtract | + QOpenGLFunctions::CompressedTextures | + QOpenGLFunctions::Multisample | + QOpenGLFunctions::StencilSeparate | + QOpenGLFunctions::NPOTTextures; +#elif defined(QT_OPENGL_ES) + int features = QOpenGLFunctions::Multitexture | + QOpenGLFunctions::Buffers | + QOpenGLFunctions::CompressedTextures | + QOpenGLFunctions::Multisample; + QGLExtensionChecker extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS))); + if (extensions.match("GL_OES_framebuffer_object")) + features |= QOpenGLFunctions::Framebuffers; + if (extensions.match("GL_OES_blend_equation_separate")) + features |= QOpenGLFunctions::BlendEquationSeparate; + if (extensions.match("GL_OES_blend_func_separate")) + features |= QOpenGLFunctions::BlendFuncSeparate; + if (extensions.match("GL_OES_blend_subtract")) + features |= QOpenGLFunctions::BlendSubtract; + if (extensions.match("GL_OES_texture_npot")) + features |= QOpenGLFunctions::NPOTTextures; + return features; +#else + int features = 0; + QGLFormat::OpenGLVersionFlags versions = QGLFormat::openGLVersionFlags(); + QGLExtensionChecker extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS))); + + // Recognize features by extension name. + if (extensions.match("GL_ARB_multitexture")) + features |= QOpenGLFunctions::Multitexture; + if (extensions.match("GL_ARB_shader_objects")) + features |= QOpenGLFunctions::Shaders; + if (extensions.match("GL_EXT_framebuffer_object") || + extensions.match("GL_ARB_framebuffer_object")) + features |= QOpenGLFunctions::Framebuffers; + if (extensions.match("GL_EXT_blend_color")) + features |= QOpenGLFunctions::BlendColor; + if (extensions.match("GL_EXT_blend_equation_separate")) + features |= QOpenGLFunctions::BlendEquationSeparate; + if (extensions.match("GL_EXT_blend_func_separate")) + features |= QOpenGLFunctions::BlendFuncSeparate; + if (extensions.match("GL_EXT_blend_subtract")) + features |= QOpenGLFunctions::BlendSubtract; + if (extensions.match("GL_ARB_texture_compression")) + features |= QOpenGLFunctions::CompressedTextures; + if (extensions.match("GL_ARB_multisample")) + features |= QOpenGLFunctions::Multisample; + if (extensions.match("GL_ARB_texture_non_power_of_two")) + features |= QOpenGLFunctions::NPOTTextures; + + // Recognize features by minimum OpenGL version. + if (versions & QGLFormat::OpenGL_Version_1_2) { + features |= QOpenGLFunctions::BlendColor | + QOpenGLFunctions::BlendEquation; + } + if (versions & QGLFormat::OpenGL_Version_1_3) { + features |= QOpenGLFunctions::Multitexture | + QOpenGLFunctions::CompressedTextures | + QOpenGLFunctions::Multisample; + } + if (versions & QGLFormat::OpenGL_Version_1_4) + features |= QOpenGLFunctions::BlendFuncSeparate; + if (versions & QGLFormat::OpenGL_Version_1_5) + features |= QOpenGLFunctions::Buffers; + if (versions & QGLFormat::OpenGL_Version_2_0) { + features |= QOpenGLFunctions::Shaders | + QOpenGLFunctions::StencilSeparate | + QOpenGLFunctions::BlendEquationSeparate | + QOpenGLFunctions::NPOTTextures; + } + return features; +#endif +} + +/*! + Returns the set of features that are present on this system's + OpenGL implementation. + + It is assumed that the QGLContext associated with this function + resolver is current. + + \sa hasOpenGLFeature() +*/ +QOpenGLFunctions::OpenGLFeatures QOpenGLFunctions::openGLFeatures() const +{ + QOpenGLFunctionsPrivateEx *d = static_cast<QOpenGLFunctionsPrivateEx *>(d_ptr); + if (!d) + return 0; + if (d->m_features == -1) + d->m_features = qt_gl_resolve_features(); + return QOpenGLFunctions::OpenGLFeatures(d->m_features); +} + +/*! + Returns true if \a feature is present on this system's OpenGL + implementation; false otherwise. + + It is assumed that the QGLContext associated with this function + resolver is current. + + \sa openGLFeatures() +*/ +bool QOpenGLFunctions::hasOpenGLFeature(QOpenGLFunctions::OpenGLFeature feature) const +{ + QOpenGLFunctionsPrivateEx *d = static_cast<QOpenGLFunctionsPrivateEx *>(d_ptr); + if (!d) + return false; + if (d->m_features == -1) + d->m_features = qt_gl_resolve_features(); + return (d->m_features & int(feature)) != 0; +} + +/*! + Initializes GL function resolution for \a context. If \a context + is null, then the current QGLContext will be used. + + After calling this function, the QOpenGLFunctions object can only be + used with \a context and other contexts that share with it. + Call initializeGLFunctions() again to change the object's context + association. +*/ +void QOpenGLFunctions::initializeGLFunctions(const QGLContext *context) +{ + d_ptr = qt_gl_functions(context); +} + + +/*! + \fn void QOpenGLFunctions::glActiveTexture(GLenum texture) + + Convenience function that calls glActiveTexture(\a texture). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glActiveTexture.xml}{glActiveTexture()}. +*/ + +/*! + \fn void QOpenGLFunctions::glAttachShader(GLuint program, GLuint shader) + + Convenience function that calls glAttachShader(\a program, \a shader). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glAttachShader.xml}{glAttachShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glBindAttribLocation(GLuint program, GLuint index, const char* name) + + Convenience function that calls glBindAttribLocation(\a program, \a index, \a name). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBindAttribLocation.xml}{glBindAttribLocation()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glBindBuffer(GLenum target, GLuint buffer) + + Convenience function that calls glBindBuffer(\a target, \a buffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBindBuffer.xml}{glBindBuffer()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBindFramebuffer(GLenum target, GLuint framebuffer) + + Convenience function that calls glBindFramebuffer(\a target, \a framebuffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBindFramebuffer.xml}{glBindFramebuffer()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBindRenderbuffer(GLenum target, GLuint renderbuffer) + + Convenience function that calls glBindRenderbuffer(\a target, \a renderbuffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBindRenderbuffer.xml}{glBindRenderbuffer()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) + + Convenience function that calls glBlendColor(\a red, \a green, \a blue, \a alpha). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendColor.xml}{glBlendColor()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBlendEquation(GLenum mode) + + Convenience function that calls glBlendEquation(\a mode). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendEquation.xml}{glBlendEquation()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) + + Convenience function that calls glBlendEquationSeparate(\a modeRGB, \a modeAlpha). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendEquationSeparate.xml}{glBlendEquationSeparate()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) + + Convenience function that calls glBlendFuncSeparate(\a srcRGB, \a dstRGB, \a srcAlpha, \a dstAlpha). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBlendFuncSeparate.xml}{glBlendFuncSeparate()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBufferData(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage) + + Convenience function that calls glBufferData(\a target, \a size, \a data, \a usage). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBufferData.xml}{glBufferData()}. +*/ + +/*! + \fn void QOpenGLFunctions::glBufferSubData(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data) + + Convenience function that calls glBufferSubData(\a target, \a offset, \a size, \a data). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glBufferSubData.xml}{glBufferSubData()}. +*/ + +/*! + \fn GLenum QOpenGLFunctions::glCheckFramebufferStatus(GLenum target) + + Convenience function that calls glCheckFramebufferStatus(\a target). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCheckFramebufferStatus.xml}{glCheckFramebufferStatus()}. +*/ + +/*! + \fn void QOpenGLFunctions::glClearDepthf(GLclampf depth) + + Convenience function that calls glClearDepth(\a depth) on + desktop OpenGL systems and glClearDepthf(\a depth) on + embedded OpenGL/ES systems. + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glClearDepthf.xml}{glClearDepthf()}. +*/ + +/*! + \fn void QOpenGLFunctions::glCompileShader(GLuint shader) + + Convenience function that calls glCompileShader(\a shader). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCompileShader.xml}{glCompileShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) + + Convenience function that calls glCompressedTexImage2D(\a target, \a level, \a internalformat, \a width, \a height, \a border, \a imageSize, \a data). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCompressedTexImage2D.xml}{glCompressedTexImage2D()}. +*/ + +/*! + \fn void QOpenGLFunctions::glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) + + Convenience function that calls glCompressedTexSubImage2D(\a target, \a level, \a xoffset, \a yoffset, \a width, \a height, \a format, \a imageSize, \a data). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCompressedTexSubImage2D.xml}{glCompressedTexSubImage2D()}. +*/ + +/*! + \fn GLuint QOpenGLFunctions::glCreateProgram() + + Convenience function that calls glCreateProgram(). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCreateProgram.xml}{glCreateProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn GLuint QOpenGLFunctions::glCreateShader(GLenum type) + + Convenience function that calls glCreateShader(\a type). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glCreateShader.xml}{glCreateShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glDeleteBuffers(GLsizei n, const GLuint* buffers) + + Convenience function that calls glDeleteBuffers(\a n, \a buffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteBuffers.xml}{glDeleteBuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) + + Convenience function that calls glDeleteFramebuffers(\a n, \a framebuffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteFramebuffers.xml}{glDeleteFramebuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glDeleteProgram(GLuint program) + + Convenience function that calls glDeleteProgram(\a program). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteProgram.xml}{glDeleteProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) + + Convenience function that calls glDeleteRenderbuffers(\a n, \a renderbuffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteRenderbuffers.xml}{glDeleteRenderbuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glDeleteShader(GLuint shader) + + Convenience function that calls glDeleteShader(\a shader). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDeleteShader.xml}{glDeleteShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glDepthRangef(GLclampf zNear, GLclampf zFar) + + Convenience function that calls glDepthRange(\a zNear, \a zFar) on + desktop OpenGL systems and glDepthRangef(\a zNear, \a zFar) on + embedded OpenGL/ES systems. + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDepthRangef.xml}{glDepthRangef()}. +*/ + +/*! + \fn void QOpenGLFunctions::glDetachShader(GLuint program, GLuint shader) + + Convenience function that calls glDetachShader(\a program, \a shader). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDetachShader.xml}{glDetachShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glDisableVertexAttribArray(GLuint index) + + Convenience function that calls glDisableVertexAttribArray(\a index). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glDisableVertexAttribArray.xml}{glDisableVertexAttribArray()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glEnableVertexAttribArray(GLuint index) + + Convenience function that calls glEnableVertexAttribArray(\a index). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glEnableVertexAttribArray.xml}{glEnableVertexAttribArray()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) + + Convenience function that calls glFramebufferRenderbuffer(\a target, \a attachment, \a renderbuffertarget, \a renderbuffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glFramebufferRenderbuffer.xml}{glFramebufferRenderbuffer()}. +*/ + +/*! + \fn void QOpenGLFunctions::glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) + + Convenience function that calls glFramebufferTexture2D(\a target, \a attachment, \a textarget, \a texture, \a level). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glFramebufferTexture2D.xml}{glFramebufferTexture2D()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGenBuffers(GLsizei n, GLuint* buffers) + + Convenience function that calls glGenBuffers(\a n, \a buffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGenBuffers.xml}{glGenBuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGenerateMipmap(GLenum target) + + Convenience function that calls glGenerateMipmap(\a target). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGenerateMipmap.xml}{glGenerateMipmap()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGenFramebuffers(GLsizei n, GLuint* framebuffers) + + Convenience function that calls glGenFramebuffers(\a n, \a framebuffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGenFramebuffers.xml}{glGenFramebuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGenRenderbuffers(GLsizei n, GLuint* renderbuffers) + + Convenience function that calls glGenRenderbuffers(\a n, \a renderbuffers). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGenRenderbuffers.xml}{glGenRenderbuffers()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) + + Convenience function that calls glGetActiveAttrib(\a program, \a index, \a bufsize, \a length, \a size, \a type, \a name). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetActiveAttrib.xml}{glGetActiveAttrib()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) + + Convenience function that calls glGetActiveUniform(\a program, \a index, \a bufsize, \a length, \a size, \a type, \a name). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetActiveUniform.xml}{glGetActiveUniform()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) + + Convenience function that calls glGetAttachedShaders(\a program, \a maxcount, \a count, \a shaders). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetAttachedShaders.xml}{glGetAttachedShaders()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn int QOpenGLFunctions::glGetAttribLocation(GLuint program, const char* name) + + Convenience function that calls glGetAttribLocation(\a program, \a name). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetAttribLocation.xml}{glGetAttribLocation()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) + + Convenience function that calls glGetBufferParameteriv(\a target, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetBufferParameteriv.xml}{glGetBufferParameteriv()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) + + Convenience function that calls glGetFramebufferAttachmentParameteriv(\a target, \a attachment, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetFramebufferAttachmentParameteriv.xml}{glGetFramebufferAttachmentParameteriv()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGetProgramiv(GLuint program, GLenum pname, GLint* params) + + Convenience function that calls glGetProgramiv(\a program, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetProgramiv.xml}{glGetProgramiv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) + + Convenience function that calls glGetProgramInfoLog(\a program, \a bufsize, \a length, \a infolog). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetProgramInfoLog.xml}{glGetProgramInfoLog()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) + + Convenience function that calls glGetRenderbufferParameteriv(\a target, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetRenderbufferParameteriv.xml}{glGetRenderbufferParameteriv()}. +*/ + +/*! + \fn void QOpenGLFunctions::glGetShaderiv(GLuint shader, GLenum pname, GLint* params) + + Convenience function that calls glGetShaderiv(\a shader, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderiv.xml}{glGetShaderiv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) + + Convenience function that calls glGetShaderInfoLog(\a shader, \a bufsize, \a length, \a infolog). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderInfoLog.xml}{glGetShaderInfoLog()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) + + Convenience function that calls glGetShaderPrecisionFormat(\a shadertype, \a precisiontype, \a range, \a precision). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderPrecisionFormat.xml}{glGetShaderPrecisionFormat()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) + + Convenience function that calls glGetShaderSource(\a shader, \a bufsize, \a length, \a source). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetShaderSource.xml}{glGetShaderSource()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetUniformfv(GLuint program, GLint location, GLfloat* params) + + Convenience function that calls glGetUniformfv(\a program, \a location, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetUniformfv.xml}{glGetUniformfv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetUniformiv(GLuint program, GLint location, GLint* params) + + Convenience function that calls glGetUniformiv(\a program, \a location, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetUniformiv.xml}{glGetUniformiv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn int QOpenGLFunctions::glGetUniformLocation(GLuint program, const char* name) + + Convenience function that calls glGetUniformLocation(\a program, \a name). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetUniformLocation.xml}{glGetUniformLocation()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) + + Convenience function that calls glGetVertexAttribfv(\a index, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetVertexAttribfv.xml}{glGetVertexAttribfv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params) + + Convenience function that calls glGetVertexAttribiv(\a index, \a pname, \a params). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetVertexAttribiv.xml}{glGetVertexAttribiv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer) + + Convenience function that calls glGetVertexAttribPointerv(\a index, \a pname, \a pointer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glGetVertexAttribPointerv.xml}{glGetVertexAttribPointerv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn GLboolean QOpenGLFunctions::glIsBuffer(GLuint buffer) + + Convenience function that calls glIsBuffer(\a buffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glIsBuffer.xml}{glIsBuffer()}. +*/ + +/*! + \fn GLboolean QOpenGLFunctions::glIsFramebuffer(GLuint framebuffer) + + Convenience function that calls glIsFramebuffer(\a framebuffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glIsFramebuffer.xml}{glIsFramebuffer()}. +*/ + +/*! + \fn GLboolean QOpenGLFunctions::glIsProgram(GLuint program) + + Convenience function that calls glIsProgram(\a program). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glIsProgram.xml}{glIsProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn GLboolean QOpenGLFunctions::glIsRenderbuffer(GLuint renderbuffer) + + Convenience function that calls glIsRenderbuffer(\a renderbuffer). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glIsRenderbuffer.xml}{glIsRenderbuffer()}. +*/ + +/*! + \fn GLboolean QOpenGLFunctions::glIsShader(GLuint shader) + + Convenience function that calls glIsShader(\a shader). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glIsShader.xml}{glIsShader()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glLinkProgram(GLuint program) + + Convenience function that calls glLinkProgram(\a program). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glLinkProgram.xml}{glLinkProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glReleaseShaderCompiler() + + Convenience function that calls glReleaseShaderCompiler(). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glReleaseShaderCompiler.xml}{glReleaseShaderCompiler()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) + + Convenience function that calls glRenderbufferStorage(\a target, \a internalformat, \a width, \a height). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glRenderbufferStorage.xml}{glRenderbufferStorage()}. +*/ + +/*! + \fn void QOpenGLFunctions::glSampleCoverage(GLclampf value, GLboolean invert) + + Convenience function that calls glSampleCoverage(\a value, \a invert). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glSampleCoverage.xml}{glSampleCoverage()}. +*/ + +/*! + \fn void QOpenGLFunctions::glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length) + + Convenience function that calls glShaderBinary(\a n, \a shaders, \a binaryformat, \a binary, \a length). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glShaderBinary.xml}{glShaderBinary()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length) + + Convenience function that calls glShaderSource(\a shader, \a count, \a string, \a length). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glShaderSource.xml}{glShaderSource()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) + + Convenience function that calls glStencilFuncSeparate(\a face, \a func, \a ref, \a mask). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glStencilFuncSeparate.xml}{glStencilFuncSeparate()}. +*/ + +/*! + \fn void QOpenGLFunctions::glStencilMaskSeparate(GLenum face, GLuint mask) + + Convenience function that calls glStencilMaskSeparate(\a face, \a mask). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glStencilMaskSeparate.xml}{glStencilMaskSeparate()}. +*/ + +/*! + \fn void QOpenGLFunctions::glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) + + Convenience function that calls glStencilOpSeparate(\a face, \a fail, \a zfail, \a zpass). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glStencilOpSeparate.xml}{glStencilOpSeparate()}. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform1f(GLint location, GLfloat x) + + Convenience function that calls glUniform1f(\a location, \a x). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1f.xml}{glUniform1f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform1fv(GLint location, GLsizei count, const GLfloat* v) + + Convenience function that calls glUniform1fv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1fv.xml}{glUniform1fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform1i(GLint location, GLint x) + + Convenience function that calls glUniform1i(\a location, \a x). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1i.xml}{glUniform1i()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform1iv(GLint location, GLsizei count, const GLint* v) + + Convenience function that calls glUniform1iv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform1iv.xml}{glUniform1iv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform2f(GLint location, GLfloat x, GLfloat y) + + Convenience function that calls glUniform2f(\a location, \a x, \a y). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2f.xml}{glUniform2f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform2fv(GLint location, GLsizei count, const GLfloat* v) + + Convenience function that calls glUniform2fv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2fv.xml}{glUniform2fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform2i(GLint location, GLint x, GLint y) + + Convenience function that calls glUniform2i(\a location, \a x, \a y). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2i.xml}{glUniform2i()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform2iv(GLint location, GLsizei count, const GLint* v) + + Convenience function that calls glUniform2iv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform2iv.xml}{glUniform2iv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) + + Convenience function that calls glUniform3f(\a location, \a x, \a y, \a z). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3f.xml}{glUniform3f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform3fv(GLint location, GLsizei count, const GLfloat* v) + + Convenience function that calls glUniform3fv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3fv.xml}{glUniform3fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform3i(GLint location, GLint x, GLint y, GLint z) + + Convenience function that calls glUniform3i(\a location, \a x, \a y, \a z). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3i.xml}{glUniform3i()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform3iv(GLint location, GLsizei count, const GLint* v) + + Convenience function that calls glUniform3iv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform3iv.xml}{glUniform3iv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) + + Convenience function that calls glUniform4f(\a location, \a x, \a y, \a z, \a w). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4f.xml}{glUniform4f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform4fv(GLint location, GLsizei count, const GLfloat* v) + + Convenience function that calls glUniform4fv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4fv.xml}{glUniform4fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) + + Convenience function that calls glUniform4i(\a location, \a x, \a y, \a z, \a w). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4i.xml}{glUniform4i()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniform4iv(GLint location, GLsizei count, const GLint* v) + + Convenience function that calls glUniform4iv(\a location, \a count, \a v). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniform4iv.xml}{glUniform4iv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) + + Convenience function that calls glUniformMatrix2fv(\a location, \a count, \a transpose, \a value). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniformMatrix2fv.xml}{glUniformMatrix2fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) + + Convenience function that calls glUniformMatrix3fv(\a location, \a count, \a transpose, \a value). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniformMatrix3fv.xml}{glUniformMatrix3fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) + + Convenience function that calls glUniformMatrix4fv(\a location, \a count, \a transpose, \a value). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUniformMatrix4fv.xml}{glUniformMatrix4fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glUseProgram(GLuint program) + + Convenience function that calls glUseProgram(\a program). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glUseProgram.xml}{glUseProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glValidateProgram(GLuint program) + + Convenience function that calls glValidateProgram(\a program). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glValidateProgram.xml}{glValidateProgram()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib1f(GLuint indx, GLfloat x) + + Convenience function that calls glVertexAttrib1f(\a indx, \a x). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib1f.xml}{glVertexAttrib1f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib1fv(GLuint indx, const GLfloat* values) + + Convenience function that calls glVertexAttrib1fv(\a indx, \a values). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib1fv.xml}{glVertexAttrib1fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) + + Convenience function that calls glVertexAttrib2f(\a indx, \a x, \a y). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib2f.xml}{glVertexAttrib2f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib2fv(GLuint indx, const GLfloat* values) + + Convenience function that calls glVertexAttrib2fv(\a indx, \a values). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib2fv.xml}{glVertexAttrib2fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) + + Convenience function that calls glVertexAttrib3f(\a indx, \a x, \a y, \a z). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib3f.xml}{glVertexAttrib3f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib3fv(GLuint indx, const GLfloat* values) + + Convenience function that calls glVertexAttrib3fv(\a indx, \a values). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib3fv.xml}{glVertexAttrib3fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) + + Convenience function that calls glVertexAttrib4f(\a indx, \a x, \a y, \a z, \a w). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib4f.xml}{glVertexAttrib4f()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttrib4fv(GLuint indx, const GLfloat* values) + + Convenience function that calls glVertexAttrib4fv(\a indx, \a values). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttrib4fv.xml}{glVertexAttrib4fv()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +/*! + \fn void QOpenGLFunctions::glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) + + Convenience function that calls glVertexAttribPointer(\a indx, \a size, \a type, \a normalized, \a stride, \a ptr). + + For more information, see the OpenGL/ES 2.0 documentation for + \l{http://www.khronos.org/opengles/sdk/docs/man/glVertexAttribPointer.xml}{glVertexAttribPointer()}. + + This convenience function will do nothing on OpenGL/ES 1.x systems. +*/ + +#ifndef QT_OPENGL_ES_2 + +static void QT3D_GLF_APIENTRY qglfResolveActiveTexture(GLenum texture) +{ + typedef void (QT3D_GLF_APIENTRYP type_glActiveTexture)(GLenum texture); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->activeTexture = (type_glActiveTexture) + context->getProcAddress(QLatin1String("glActiveTexture")); + if (!funcs->activeTexture) { + funcs->activeTexture = (type_glActiveTexture) + context->getProcAddress(QLatin1String("glActiveTextureARB")); + } + + if (funcs->activeTexture) + funcs->activeTexture(texture); + else + funcs->activeTexture = qglfResolveActiveTexture; +} + +static void QT3D_GLF_APIENTRY qglfResolveAttachShader(GLuint program, GLuint shader) +{ + typedef void (QT3D_GLF_APIENTRYP type_glAttachShader)(GLuint program, GLuint shader); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->attachShader = (type_glAttachShader) + context->getProcAddress(QLatin1String("glAttachShader")); + if (!funcs->attachShader) { + funcs->attachShader = (type_glAttachShader) + context->getProcAddress(QLatin1String("glAttachObjectARB")); + } + + if (funcs->attachShader) + funcs->attachShader(program, shader); + else + funcs->attachShader = qglfResolveAttachShader; +} + +static void QT3D_GLF_APIENTRY qglfResolveBindAttribLocation(GLuint program, GLuint index, const char* name) +{ + typedef void (QT3D_GLF_APIENTRYP type_glBindAttribLocation)(GLuint program, GLuint index, const char* name); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->bindAttribLocation = (type_glBindAttribLocation) + context->getProcAddress(QLatin1String("glBindAttribLocation")); + if (!funcs->bindAttribLocation) { + funcs->bindAttribLocation = (type_glBindAttribLocation) + context->getProcAddress(QLatin1String("glBindAttribLocationARB")); + } + + if (funcs->bindAttribLocation) + funcs->bindAttribLocation(program, index, name); + else + funcs->bindAttribLocation = qglfResolveBindAttribLocation; +} + +static void QT3D_GLF_APIENTRY qglfResolveBindBuffer(GLenum target, GLuint buffer) +{ + typedef void (QT3D_GLF_APIENTRYP type_glBindBuffer)(GLenum target, GLuint buffer); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->bindBuffer = (type_glBindBuffer) + context->getProcAddress(QLatin1String("glBindBuffer")); +#ifdef QT_OPENGL_ES + if (!funcs->bindBuffer) { + funcs->bindBuffer = (type_glBindBuffer) + context->getProcAddress(QLatin1String("glBindBufferOES")); + } +#endif + if (!funcs->bindBuffer) { + funcs->bindBuffer = (type_glBindBuffer) + context->getProcAddress(QLatin1String("glBindBufferEXT")); + } + if (!funcs->bindBuffer) { + funcs->bindBuffer = (type_glBindBuffer) + context->getProcAddress(QLatin1String("glBindBufferARB")); + } + + if (funcs->bindBuffer) + funcs->bindBuffer(target, buffer); + else + funcs->bindBuffer = qglfResolveBindBuffer; +} + +static void QT3D_GLF_APIENTRY qglfResolveBindFramebuffer(GLenum target, GLuint framebuffer) +{ + typedef void (QT3D_GLF_APIENTRYP type_glBindFramebuffer)(GLenum target, GLuint framebuffer); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->bindFramebuffer = (type_glBindFramebuffer) + context->getProcAddress(QLatin1String("glBindFramebuffer")); +#ifdef QT_OPENGL_ES + if (!funcs->bindFramebuffer) { + funcs->bindFramebuffer = (type_glBindFramebuffer) + context->getProcAddress(QLatin1String("glBindFramebufferOES")); + } +#endif + if (!funcs->bindFramebuffer) { + funcs->bindFramebuffer = (type_glBindFramebuffer) + context->getProcAddress(QLatin1String("glBindFramebufferEXT")); + } + if (!funcs->bindFramebuffer) { + funcs->bindFramebuffer = (type_glBindFramebuffer) + context->getProcAddress(QLatin1String("glBindFramebufferARB")); + } + + if (funcs->bindFramebuffer) + funcs->bindFramebuffer(target, framebuffer); + else + funcs->bindFramebuffer = qglfResolveBindFramebuffer; +} + +static void QT3D_GLF_APIENTRY qglfResolveBindRenderbuffer(GLenum target, GLuint renderbuffer) +{ + typedef void (QT3D_GLF_APIENTRYP type_glBindRenderbuffer)(GLenum target, GLuint renderbuffer); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->bindRenderbuffer = (type_glBindRenderbuffer) + context->getProcAddress(QLatin1String("glBindRenderbuffer")); +#ifdef QT_OPENGL_ES + if (!funcs->bindRenderbuffer) { + funcs->bindRenderbuffer = (type_glBindRenderbuffer) + context->getProcAddress(QLatin1String("glBindRenderbufferOES")); + } +#endif + if (!funcs->bindRenderbuffer) { + funcs->bindRenderbuffer = (type_glBindRenderbuffer) + context->getProcAddress(QLatin1String("glBindRenderbufferEXT")); + } + if (!funcs->bindRenderbuffer) { + funcs->bindRenderbuffer = (type_glBindRenderbuffer) + context->getProcAddress(QLatin1String("glBindRenderbufferARB")); + } + + if (funcs->bindRenderbuffer) + funcs->bindRenderbuffer(target, renderbuffer); + else + funcs->bindRenderbuffer = qglfResolveBindRenderbuffer; +} + +static void QT3D_GLF_APIENTRY qglfResolveBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) +{ + typedef void (QT3D_GLF_APIENTRYP type_glBlendColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->blendColor = (type_glBlendColor) + context->getProcAddress(QLatin1String("glBlendColor")); +#ifdef QT_OPENGL_ES + if (!funcs->blendColor) { + funcs->blendColor = (type_glBlendColor) + context->getProcAddress(QLatin1String("glBlendColorOES")); + } +#endif + if (!funcs->blendColor) { + funcs->blendColor = (type_glBlendColor) + context->getProcAddress(QLatin1String("glBlendColorEXT")); + } + if (!funcs->blendColor) { + funcs->blendColor = (type_glBlendColor) + context->getProcAddress(QLatin1String("glBlendColorARB")); + } + + if (funcs->blendColor) + funcs->blendColor(red, green, blue, alpha); + else + funcs->blendColor = qglfResolveBlendColor; +} + +static void QT3D_GLF_APIENTRY qglfResolveBlendEquation(GLenum mode) +{ + typedef void (QT3D_GLF_APIENTRYP type_glBlendEquation)(GLenum mode); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->blendEquation = (type_glBlendEquation) + context->getProcAddress(QLatin1String("glBlendEquation")); +#ifdef QT_OPENGL_ES + if (!funcs->blendEquation) { + funcs->blendEquation = (type_glBlendEquation) + context->getProcAddress(QLatin1String("glBlendEquationOES")); + } +#endif + if (!funcs->blendEquation) { + funcs->blendEquation = (type_glBlendEquation) + context->getProcAddress(QLatin1String("glBlendEquationEXT")); + } + if (!funcs->blendEquation) { + funcs->blendEquation = (type_glBlendEquation) + context->getProcAddress(QLatin1String("glBlendEquationARB")); + } + + if (funcs->blendEquation) + funcs->blendEquation(mode); + else + funcs->blendEquation = qglfResolveBlendEquation; +} + +static void QT3D_GLF_APIENTRY qglfResolveBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) +{ + typedef void (QT3D_GLF_APIENTRYP type_glBlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->blendEquationSeparate = (type_glBlendEquationSeparate) + context->getProcAddress(QLatin1String("glBlendEquationSeparate")); +#ifdef QT_OPENGL_ES + if (!funcs->blendEquationSeparate) { + funcs->blendEquationSeparate = (type_glBlendEquationSeparate) + context->getProcAddress(QLatin1String("glBlendEquationSeparateOES")); + } +#endif + if (!funcs->blendEquationSeparate) { + funcs->blendEquationSeparate = (type_glBlendEquationSeparate) + context->getProcAddress(QLatin1String("glBlendEquationSeparateEXT")); + } + if (!funcs->blendEquationSeparate) { + funcs->blendEquationSeparate = (type_glBlendEquationSeparate) + context->getProcAddress(QLatin1String("glBlendEquationSeparateARB")); + } + + if (funcs->blendEquationSeparate) + funcs->blendEquationSeparate(modeRGB, modeAlpha); + else + funcs->blendEquationSeparate = qglfResolveBlendEquationSeparate; +} + +static void QT3D_GLF_APIENTRY qglfResolveBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +{ + typedef void (QT3D_GLF_APIENTRYP type_glBlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->blendFuncSeparate = (type_glBlendFuncSeparate) + context->getProcAddress(QLatin1String("glBlendFuncSeparate")); +#ifdef QT_OPENGL_ES + if (!funcs->blendFuncSeparate) { + funcs->blendFuncSeparate = (type_glBlendFuncSeparate) + context->getProcAddress(QLatin1String("glBlendFuncSeparateOES")); + } +#endif + if (!funcs->blendFuncSeparate) { + funcs->blendFuncSeparate = (type_glBlendFuncSeparate) + context->getProcAddress(QLatin1String("glBlendFuncSeparateEXT")); + } + if (!funcs->blendFuncSeparate) { + funcs->blendFuncSeparate = (type_glBlendFuncSeparate) + context->getProcAddress(QLatin1String("glBlendFuncSeparateARB")); + } + + if (funcs->blendFuncSeparate) + funcs->blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + else + funcs->blendFuncSeparate = qglfResolveBlendFuncSeparate; +} + +static void QT3D_GLF_APIENTRY qglfResolveBufferData(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage) +{ + typedef void (QT3D_GLF_APIENTRYP type_glBufferData)(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->bufferData = (type_glBufferData) + context->getProcAddress(QLatin1String("glBufferData")); +#ifdef QT_OPENGL_ES + if (!funcs->bufferData) { + funcs->bufferData = (type_glBufferData) + context->getProcAddress(QLatin1String("glBufferDataOES")); + } +#endif + if (!funcs->bufferData) { + funcs->bufferData = (type_glBufferData) + context->getProcAddress(QLatin1String("glBufferDataEXT")); + } + if (!funcs->bufferData) { + funcs->bufferData = (type_glBufferData) + context->getProcAddress(QLatin1String("glBufferDataARB")); + } + + if (funcs->bufferData) + funcs->bufferData(target, size, data, usage); + else + funcs->bufferData = qglfResolveBufferData; +} + +static void QT3D_GLF_APIENTRY qglfResolveBufferSubData(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data) +{ + typedef void (QT3D_GLF_APIENTRYP type_glBufferSubData)(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->bufferSubData = (type_glBufferSubData) + context->getProcAddress(QLatin1String("glBufferSubData")); +#ifdef QT_OPENGL_ES + if (!funcs->bufferSubData) { + funcs->bufferSubData = (type_glBufferSubData) + context->getProcAddress(QLatin1String("glBufferSubDataOES")); + } +#endif + if (!funcs->bufferSubData) { + funcs->bufferSubData = (type_glBufferSubData) + context->getProcAddress(QLatin1String("glBufferSubDataEXT")); + } + if (!funcs->bufferSubData) { + funcs->bufferSubData = (type_glBufferSubData) + context->getProcAddress(QLatin1String("glBufferSubDataARB")); + } + + if (funcs->bufferSubData) + funcs->bufferSubData(target, offset, size, data); + else + funcs->bufferSubData = qglfResolveBufferSubData; +} + +static GLenum QT3D_GLF_APIENTRY qglfResolveCheckFramebufferStatus(GLenum target) +{ + typedef GLenum (QT3D_GLF_APIENTRYP type_glCheckFramebufferStatus)(GLenum target); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->checkFramebufferStatus = (type_glCheckFramebufferStatus) + context->getProcAddress(QLatin1String("glCheckFramebufferStatus")); +#ifdef QT_OPENGL_ES + if (!funcs->checkFramebufferStatus) { + funcs->checkFramebufferStatus = (type_glCheckFramebufferStatus) + context->getProcAddress(QLatin1String("glCheckFramebufferStatusOES")); + } +#endif + if (!funcs->checkFramebufferStatus) { + funcs->checkFramebufferStatus = (type_glCheckFramebufferStatus) + context->getProcAddress(QLatin1String("glCheckFramebufferStatusEXT")); + } + if (!funcs->checkFramebufferStatus) { + funcs->checkFramebufferStatus = (type_glCheckFramebufferStatus) + context->getProcAddress(QLatin1String("glCheckFramebufferStatusARB")); + } + + if (funcs->checkFramebufferStatus) + return funcs->checkFramebufferStatus(target); + funcs->checkFramebufferStatus = qglfResolveCheckFramebufferStatus; + return GLenum(0); +} + +static void QT3D_GLF_APIENTRY qglfResolveCompileShader(GLuint shader) +{ + typedef void (QT3D_GLF_APIENTRYP type_glCompileShader)(GLuint shader); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->compileShader = (type_glCompileShader) + context->getProcAddress(QLatin1String("glCompileShader")); + if (!funcs->compileShader) { + funcs->compileShader = (type_glCompileShader) + context->getProcAddress(QLatin1String("glCompileShader")); + } + + if (funcs->compileShader) + funcs->compileShader(shader); + else + funcs->compileShader = qglfResolveCompileShader; +} + +static void QT3D_GLF_APIENTRY qglfResolveCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) +{ + typedef void (QT3D_GLF_APIENTRYP type_glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->compressedTexImage2D = (type_glCompressedTexImage2D) + context->getProcAddress(QLatin1String("glCompressedTexImage2D")); +#ifdef QT_OPENGL_ES + if (!funcs->compressedTexImage2D) { + funcs->compressedTexImage2D = (type_glCompressedTexImage2D) + context->getProcAddress(QLatin1String("glCompressedTexImage2DOES")); + } +#endif + if (!funcs->compressedTexImage2D) { + funcs->compressedTexImage2D = (type_glCompressedTexImage2D) + context->getProcAddress(QLatin1String("glCompressedTexImage2DEXT")); + } + if (!funcs->compressedTexImage2D) { + funcs->compressedTexImage2D = (type_glCompressedTexImage2D) + context->getProcAddress(QLatin1String("glCompressedTexImage2DARB")); + } + + if (funcs->compressedTexImage2D) + funcs->compressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); + else + funcs->compressedTexImage2D = qglfResolveCompressedTexImage2D; +} + +static void QT3D_GLF_APIENTRY qglfResolveCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) +{ + typedef void (QT3D_GLF_APIENTRYP type_glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->compressedTexSubImage2D = (type_glCompressedTexSubImage2D) + context->getProcAddress(QLatin1String("glCompressedTexSubImage2D")); +#ifdef QT_OPENGL_ES + if (!funcs->compressedTexSubImage2D) { + funcs->compressedTexSubImage2D = (type_glCompressedTexSubImage2D) + context->getProcAddress(QLatin1String("glCompressedTexSubImage2DOES")); + } +#endif + if (!funcs->compressedTexSubImage2D) { + funcs->compressedTexSubImage2D = (type_glCompressedTexSubImage2D) + context->getProcAddress(QLatin1String("glCompressedTexSubImage2DEXT")); + } + if (!funcs->compressedTexSubImage2D) { + funcs->compressedTexSubImage2D = (type_glCompressedTexSubImage2D) + context->getProcAddress(QLatin1String("glCompressedTexSubImage2DARB")); + } + + if (funcs->compressedTexSubImage2D) + funcs->compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); + else + funcs->compressedTexSubImage2D = qglfResolveCompressedTexSubImage2D; +} + +static GLuint QT3D_GLF_APIENTRY qglfResolveCreateProgram() +{ + typedef GLuint (QT3D_GLF_APIENTRYP type_glCreateProgram)(); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->createProgram = (type_glCreateProgram) + context->getProcAddress(QLatin1String("glCreateProgram")); + if (!funcs->createProgram) { + funcs->createProgram = (type_glCreateProgram) + context->getProcAddress(QLatin1String("glCreateProgramObjectARB")); + } + + if (funcs->createProgram) + return funcs->createProgram(); + funcs->createProgram = qglfResolveCreateProgram; + return GLuint(0); +} + +static GLuint QT3D_GLF_APIENTRY qglfResolveCreateShader(GLenum type) +{ + typedef GLuint (QT3D_GLF_APIENTRYP type_glCreateShader)(GLenum type); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->createShader = (type_glCreateShader) + context->getProcAddress(QLatin1String("glCreateShader")); + if (!funcs->createShader) { + funcs->createShader = (type_glCreateShader) + context->getProcAddress(QLatin1String("glCreateShaderObjectARB")); + } + + if (funcs->createShader) + return funcs->createShader(type); + funcs->createShader = qglfResolveCreateShader; + return GLuint(0); +} + +static void QT3D_GLF_APIENTRY qglfResolveDeleteBuffers(GLsizei n, const GLuint* buffers) +{ + typedef void (QT3D_GLF_APIENTRYP type_glDeleteBuffers)(GLsizei n, const GLuint* buffers); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->deleteBuffers = (type_glDeleteBuffers) + context->getProcAddress(QLatin1String("glDeleteBuffers")); +#ifdef QT_OPENGL_ES + if (!funcs->deleteBuffers) { + funcs->deleteBuffers = (type_glDeleteBuffers) + context->getProcAddress(QLatin1String("glDeleteBuffersOES")); + } +#endif + if (!funcs->deleteBuffers) { + funcs->deleteBuffers = (type_glDeleteBuffers) + context->getProcAddress(QLatin1String("glDeleteBuffersEXT")); + } + if (!funcs->deleteBuffers) { + funcs->deleteBuffers = (type_glDeleteBuffers) + context->getProcAddress(QLatin1String("glDeleteBuffersARB")); + } + + if (funcs->deleteBuffers) + funcs->deleteBuffers(n, buffers); + else + funcs->deleteBuffers = qglfResolveDeleteBuffers; +} + +static void QT3D_GLF_APIENTRY qglfResolveDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) +{ + typedef void (QT3D_GLF_APIENTRYP type_glDeleteFramebuffers)(GLsizei n, const GLuint* framebuffers); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->deleteFramebuffers = (type_glDeleteFramebuffers) + context->getProcAddress(QLatin1String("glDeleteFramebuffers")); +#ifdef QT_OPENGL_ES + if (!funcs->deleteFramebuffers) { + funcs->deleteFramebuffers = (type_glDeleteFramebuffers) + context->getProcAddress(QLatin1String("glDeleteFramebuffersOES")); + } +#endif + if (!funcs->deleteFramebuffers) { + funcs->deleteFramebuffers = (type_glDeleteFramebuffers) + context->getProcAddress(QLatin1String("glDeleteFramebuffersEXT")); + } + if (!funcs->deleteFramebuffers) { + funcs->deleteFramebuffers = (type_glDeleteFramebuffers) + context->getProcAddress(QLatin1String("glDeleteFramebuffersARB")); + } + + if (funcs->deleteFramebuffers) + funcs->deleteFramebuffers(n, framebuffers); + else + funcs->deleteFramebuffers = qglfResolveDeleteFramebuffers; +} + +static void QT3D_GLF_APIENTRY qglfResolveDeleteProgram(GLuint program) +{ + typedef void (QT3D_GLF_APIENTRYP type_glDeleteProgram)(GLuint program); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->deleteProgram = (type_glDeleteProgram) + context->getProcAddress(QLatin1String("glDeleteProgram")); + if (!funcs->deleteProgram) { + funcs->deleteProgram = (type_glDeleteProgram) + context->getProcAddress(QLatin1String("glDeleteObjectARB")); + } + + if (funcs->deleteProgram) + funcs->deleteProgram(program); + else + funcs->deleteProgram = qglfResolveDeleteProgram; +} + +static void QT3D_GLF_APIENTRY qglfResolveDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) +{ + typedef void (QT3D_GLF_APIENTRYP type_glDeleteRenderbuffers)(GLsizei n, const GLuint* renderbuffers); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->deleteRenderbuffers = (type_glDeleteRenderbuffers) + context->getProcAddress(QLatin1String("glDeleteRenderbuffers")); +#ifdef QT_OPENGL_ES + if (!funcs->deleteRenderbuffers) { + funcs->deleteRenderbuffers = (type_glDeleteRenderbuffers) + context->getProcAddress(QLatin1String("glDeleteRenderbuffersOES")); + } +#endif + if (!funcs->deleteRenderbuffers) { + funcs->deleteRenderbuffers = (type_glDeleteRenderbuffers) + context->getProcAddress(QLatin1String("glDeleteRenderbuffersEXT")); + } + if (!funcs->deleteRenderbuffers) { + funcs->deleteRenderbuffers = (type_glDeleteRenderbuffers) + context->getProcAddress(QLatin1String("glDeleteRenderbuffersARB")); + } + + if (funcs->deleteRenderbuffers) + funcs->deleteRenderbuffers(n, renderbuffers); + else + funcs->deleteRenderbuffers = qglfResolveDeleteRenderbuffers; +} + +static void QT3D_GLF_APIENTRY qglfResolveDeleteShader(GLuint shader) +{ + typedef void (QT3D_GLF_APIENTRYP type_glDeleteShader)(GLuint shader); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->deleteShader = (type_glDeleteShader) + context->getProcAddress(QLatin1String("glDeleteShader")); + if (!funcs->deleteShader) { + funcs->deleteShader = (type_glDeleteShader) + context->getProcAddress(QLatin1String("glDeleteObjectARB")); + } + + if (funcs->deleteShader) + funcs->deleteShader(shader); + else + funcs->deleteShader = qglfResolveDeleteShader; +} + +static void QT3D_GLF_APIENTRY qglfResolveDetachShader(GLuint program, GLuint shader) +{ + typedef void (QT3D_GLF_APIENTRYP type_glDetachShader)(GLuint program, GLuint shader); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->detachShader = (type_glDetachShader) + context->getProcAddress(QLatin1String("glDetachShader")); + if (!funcs->detachShader) { + funcs->detachShader = (type_glDetachShader) + context->getProcAddress(QLatin1String("glDetachObjectARB")); + } + + if (funcs->detachShader) + funcs->detachShader(program, shader); + else + funcs->detachShader = qglfResolveDetachShader; +} + +static void QT3D_GLF_APIENTRY qglfResolveDisableVertexAttribArray(GLuint index) +{ + typedef void (QT3D_GLF_APIENTRYP type_glDisableVertexAttribArray)(GLuint index); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->disableVertexAttribArray = (type_glDisableVertexAttribArray) + context->getProcAddress(QLatin1String("glDisableVertexAttribArray")); + if (!funcs->disableVertexAttribArray) { + funcs->disableVertexAttribArray = (type_glDisableVertexAttribArray) + context->getProcAddress(QLatin1String("glDisableVertexAttribArrayARB")); + } + + if (funcs->disableVertexAttribArray) + funcs->disableVertexAttribArray(index); + else + funcs->disableVertexAttribArray = qglfResolveDisableVertexAttribArray; +} + +static void QT3D_GLF_APIENTRY qglfResolveEnableVertexAttribArray(GLuint index) +{ + typedef void (QT3D_GLF_APIENTRYP type_glEnableVertexAttribArray)(GLuint index); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->enableVertexAttribArray = (type_glEnableVertexAttribArray) + context->getProcAddress(QLatin1String("glEnableVertexAttribArray")); + if (!funcs->enableVertexAttribArray) { + funcs->enableVertexAttribArray = (type_glEnableVertexAttribArray) + context->getProcAddress(QLatin1String("glEnableVertexAttribArrayARB")); + } + + if (funcs->enableVertexAttribArray) + funcs->enableVertexAttribArray(index); + else + funcs->enableVertexAttribArray = qglfResolveEnableVertexAttribArray; +} + +static void QT3D_GLF_APIENTRY qglfResolveFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +{ + typedef void (QT3D_GLF_APIENTRYP type_glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->framebufferRenderbuffer = (type_glFramebufferRenderbuffer) + context->getProcAddress(QLatin1String("glFramebufferRenderbuffer")); +#ifdef QT_OPENGL_ES + if (!funcs->framebufferRenderbuffer) { + funcs->framebufferRenderbuffer = (type_glFramebufferRenderbuffer) + context->getProcAddress(QLatin1String("glFramebufferRenderbufferOES")); + } +#endif + if (!funcs->framebufferRenderbuffer) { + funcs->framebufferRenderbuffer = (type_glFramebufferRenderbuffer) + context->getProcAddress(QLatin1String("glFramebufferRenderbufferEXT")); + } + if (!funcs->framebufferRenderbuffer) { + funcs->framebufferRenderbuffer = (type_glFramebufferRenderbuffer) + context->getProcAddress(QLatin1String("glFramebufferRenderbufferARB")); + } + + if (funcs->framebufferRenderbuffer) + funcs->framebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); + else + funcs->framebufferRenderbuffer = qglfResolveFramebufferRenderbuffer; +} + +static void QT3D_GLF_APIENTRY qglfResolveFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ + typedef void (QT3D_GLF_APIENTRYP type_glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->framebufferTexture2D = (type_glFramebufferTexture2D) + context->getProcAddress(QLatin1String("glFramebufferTexture2D")); +#ifdef QT_OPENGL_ES + if (!funcs->framebufferTexture2D) { + funcs->framebufferTexture2D = (type_glFramebufferTexture2D) + context->getProcAddress(QLatin1String("glFramebufferTexture2DOES")); + } +#endif + if (!funcs->framebufferTexture2D) { + funcs->framebufferTexture2D = (type_glFramebufferTexture2D) + context->getProcAddress(QLatin1String("glFramebufferTexture2DEXT")); + } + if (!funcs->framebufferTexture2D) { + funcs->framebufferTexture2D = (type_glFramebufferTexture2D) + context->getProcAddress(QLatin1String("glFramebufferTexture2DARB")); + } + + if (funcs->framebufferTexture2D) + funcs->framebufferTexture2D(target, attachment, textarget, texture, level); + else + funcs->framebufferTexture2D = qglfResolveFramebufferTexture2D; +} + +static void QT3D_GLF_APIENTRY qglfResolveGenBuffers(GLsizei n, GLuint* buffers) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGenBuffers)(GLsizei n, GLuint* buffers); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->genBuffers = (type_glGenBuffers) + context->getProcAddress(QLatin1String("glGenBuffers")); +#ifdef QT_OPENGL_ES + if (!funcs->genBuffers) { + funcs->genBuffers = (type_glGenBuffers) + context->getProcAddress(QLatin1String("glGenBuffersOES")); + } +#endif + if (!funcs->genBuffers) { + funcs->genBuffers = (type_glGenBuffers) + context->getProcAddress(QLatin1String("glGenBuffersEXT")); + } + if (!funcs->genBuffers) { + funcs->genBuffers = (type_glGenBuffers) + context->getProcAddress(QLatin1String("glGenBuffersARB")); + } + + if (funcs->genBuffers) + funcs->genBuffers(n, buffers); + else + funcs->genBuffers = qglfResolveGenBuffers; +} + +static void QT3D_GLF_APIENTRY qglfResolveGenerateMipmap(GLenum target) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGenerateMipmap)(GLenum target); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->generateMipmap = (type_glGenerateMipmap) + context->getProcAddress(QLatin1String("glGenerateMipmap")); +#ifdef QT_OPENGL_ES + if (!funcs->generateMipmap) { + funcs->generateMipmap = (type_glGenerateMipmap) + context->getProcAddress(QLatin1String("glGenerateMipmapOES")); + } +#endif + if (!funcs->generateMipmap) { + funcs->generateMipmap = (type_glGenerateMipmap) + context->getProcAddress(QLatin1String("glGenerateMipmapEXT")); + } + if (!funcs->generateMipmap) { + funcs->generateMipmap = (type_glGenerateMipmap) + context->getProcAddress(QLatin1String("glGenerateMipmapARB")); + } + + if (funcs->generateMipmap) + funcs->generateMipmap(target); + else + funcs->generateMipmap = qglfResolveGenerateMipmap; +} + +static void QT3D_GLF_APIENTRY qglfResolveGenFramebuffers(GLsizei n, GLuint* framebuffers) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGenFramebuffers)(GLsizei n, GLuint* framebuffers); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->genFramebuffers = (type_glGenFramebuffers) + context->getProcAddress(QLatin1String("glGenFramebuffers")); +#ifdef QT_OPENGL_ES + if (!funcs->genFramebuffers) { + funcs->genFramebuffers = (type_glGenFramebuffers) + context->getProcAddress(QLatin1String("glGenFramebuffersOES")); + } +#endif + if (!funcs->genFramebuffers) { + funcs->genFramebuffers = (type_glGenFramebuffers) + context->getProcAddress(QLatin1String("glGenFramebuffersEXT")); + } + if (!funcs->genFramebuffers) { + funcs->genFramebuffers = (type_glGenFramebuffers) + context->getProcAddress(QLatin1String("glGenFramebuffersARB")); + } + + if (funcs->genFramebuffers) + funcs->genFramebuffers(n, framebuffers); + else + funcs->genFramebuffers = qglfResolveGenFramebuffers; +} + +static void QT3D_GLF_APIENTRY qglfResolveGenRenderbuffers(GLsizei n, GLuint* renderbuffers) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGenRenderbuffers)(GLsizei n, GLuint* renderbuffers); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->genRenderbuffers = (type_glGenRenderbuffers) + context->getProcAddress(QLatin1String("glGenRenderbuffers")); +#ifdef QT_OPENGL_ES + if (!funcs->genRenderbuffers) { + funcs->genRenderbuffers = (type_glGenRenderbuffers) + context->getProcAddress(QLatin1String("glGenRenderbuffersOES")); + } +#endif + if (!funcs->genRenderbuffers) { + funcs->genRenderbuffers = (type_glGenRenderbuffers) + context->getProcAddress(QLatin1String("glGenRenderbuffersEXT")); + } + if (!funcs->genRenderbuffers) { + funcs->genRenderbuffers = (type_glGenRenderbuffers) + context->getProcAddress(QLatin1String("glGenRenderbuffersARB")); + } + + if (funcs->genRenderbuffers) + funcs->genRenderbuffers(n, renderbuffers); + else + funcs->genRenderbuffers = qglfResolveGenRenderbuffers; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getActiveAttrib = (type_glGetActiveAttrib) + context->getProcAddress(QLatin1String("glGetActiveAttrib")); + if (!funcs->getActiveAttrib) { + funcs->getActiveAttrib = (type_glGetActiveAttrib) + context->getProcAddress(QLatin1String("glGetActiveAttribARB")); + } + + if (funcs->getActiveAttrib) + funcs->getActiveAttrib(program, index, bufsize, length, size, type, name); + else + funcs->getActiveAttrib = qglfResolveGetActiveAttrib; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getActiveUniform = (type_glGetActiveUniform) + context->getProcAddress(QLatin1String("glGetActiveUniform")); + if (!funcs->getActiveUniform) { + funcs->getActiveUniform = (type_glGetActiveUniform) + context->getProcAddress(QLatin1String("glGetActiveUniformARB")); + } + + if (funcs->getActiveUniform) + funcs->getActiveUniform(program, index, bufsize, length, size, type, name); + else + funcs->getActiveUniform = qglfResolveGetActiveUniform; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetAttachedShaders)(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getAttachedShaders = (type_glGetAttachedShaders) + context->getProcAddress(QLatin1String("glGetAttachedShaders")); + if (!funcs->getAttachedShaders) { + funcs->getAttachedShaders = (type_glGetAttachedShaders) + context->getProcAddress(QLatin1String("glGetAttachedObjectsARB")); + } + + if (funcs->getAttachedShaders) + funcs->getAttachedShaders(program, maxcount, count, shaders); + else + funcs->getAttachedShaders = qglfResolveGetAttachedShaders; +} + +static int QT3D_GLF_APIENTRY qglfResolveGetAttribLocation(GLuint program, const char* name) +{ + typedef int (QT3D_GLF_APIENTRYP type_glGetAttribLocation)(GLuint program, const char* name); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getAttribLocation = (type_glGetAttribLocation) + context->getProcAddress(QLatin1String("glGetAttribLocation")); + if (!funcs->getAttribLocation) { + funcs->getAttribLocation = (type_glGetAttribLocation) + context->getProcAddress(QLatin1String("glGetAttribLocationARB")); + } + + if (funcs->getAttribLocation) + return funcs->getAttribLocation(program, name); + funcs->getAttribLocation = qglfResolveGetAttribLocation; + return int(0); +} + +static void QT3D_GLF_APIENTRY qglfResolveGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetBufferParameteriv)(GLenum target, GLenum pname, GLint* params); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getBufferParameteriv = (type_glGetBufferParameteriv) + context->getProcAddress(QLatin1String("glGetBufferParameteriv")); +#ifdef QT_OPENGL_ES + if (!funcs->getBufferParameteriv) { + funcs->getBufferParameteriv = (type_glGetBufferParameteriv) + context->getProcAddress(QLatin1String("glGetBufferParameterivOES")); + } +#endif + if (!funcs->getBufferParameteriv) { + funcs->getBufferParameteriv = (type_glGetBufferParameteriv) + context->getProcAddress(QLatin1String("glGetBufferParameterivEXT")); + } + if (!funcs->getBufferParameteriv) { + funcs->getBufferParameteriv = (type_glGetBufferParameteriv) + context->getProcAddress(QLatin1String("glGetBufferParameterivARB")); + } + + if (funcs->getBufferParameteriv) + funcs->getBufferParameteriv(target, pname, params); + else + funcs->getBufferParameteriv = qglfResolveGetBufferParameteriv; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint* params); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getFramebufferAttachmentParameteriv = (type_glGetFramebufferAttachmentParameteriv) + context->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameteriv")); +#ifdef QT_OPENGL_ES + if (!funcs->getFramebufferAttachmentParameteriv) { + funcs->getFramebufferAttachmentParameteriv = (type_glGetFramebufferAttachmentParameteriv) + context->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameterivOES")); + } +#endif + if (!funcs->getFramebufferAttachmentParameteriv) { + funcs->getFramebufferAttachmentParameteriv = (type_glGetFramebufferAttachmentParameteriv) + context->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameterivEXT")); + } + if (!funcs->getFramebufferAttachmentParameteriv) { + funcs->getFramebufferAttachmentParameteriv = (type_glGetFramebufferAttachmentParameteriv) + context->getProcAddress(QLatin1String("glGetFramebufferAttachmentParameterivARB")); + } + + if (funcs->getFramebufferAttachmentParameteriv) + funcs->getFramebufferAttachmentParameteriv(target, attachment, pname, params); + else + funcs->getFramebufferAttachmentParameteriv = qglfResolveGetFramebufferAttachmentParameteriv; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetProgramiv(GLuint program, GLenum pname, GLint* params) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetProgramiv)(GLuint program, GLenum pname, GLint* params); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getProgramiv = (type_glGetProgramiv) + context->getProcAddress(QLatin1String("glGetProgramiv")); + if (!funcs->getProgramiv) { + funcs->getProgramiv = (type_glGetProgramiv) + context->getProcAddress(QLatin1String("glGetObjectParameterivARB")); + } + + if (funcs->getProgramiv) + funcs->getProgramiv(program, pname, params); + else + funcs->getProgramiv = qglfResolveGetProgramiv; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetProgramInfoLog)(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getProgramInfoLog = (type_glGetProgramInfoLog) + context->getProcAddress(QLatin1String("glGetProgramInfoLog")); + if (!funcs->getProgramInfoLog) { + funcs->getProgramInfoLog = (type_glGetProgramInfoLog) + context->getProcAddress(QLatin1String("glGetInfoLogARB")); + } + + if (funcs->getProgramInfoLog) + funcs->getProgramInfoLog(program, bufsize, length, infolog); + else + funcs->getProgramInfoLog = qglfResolveGetProgramInfoLog; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint* params); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getRenderbufferParameteriv = (type_glGetRenderbufferParameteriv) + context->getProcAddress(QLatin1String("glGetRenderbufferParameteriv")); +#ifdef QT_OPENGL_ES + if (!funcs->getRenderbufferParameteriv) { + funcs->getRenderbufferParameteriv = (type_glGetRenderbufferParameteriv) + context->getProcAddress(QLatin1String("glGetRenderbufferParameterivOES")); + } +#endif + if (!funcs->getRenderbufferParameteriv) { + funcs->getRenderbufferParameteriv = (type_glGetRenderbufferParameteriv) + context->getProcAddress(QLatin1String("glGetRenderbufferParameterivEXT")); + } + if (!funcs->getRenderbufferParameteriv) { + funcs->getRenderbufferParameteriv = (type_glGetRenderbufferParameteriv) + context->getProcAddress(QLatin1String("glGetRenderbufferParameterivARB")); + } + + if (funcs->getRenderbufferParameteriv) + funcs->getRenderbufferParameteriv(target, pname, params); + else + funcs->getRenderbufferParameteriv = qglfResolveGetRenderbufferParameteriv; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetShaderiv(GLuint shader, GLenum pname, GLint* params) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetShaderiv)(GLuint shader, GLenum pname, GLint* params); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getShaderiv = (type_glGetShaderiv) + context->getProcAddress(QLatin1String("glGetShaderiv")); + if (!funcs->getShaderiv) { + funcs->getShaderiv = (type_glGetShaderiv) + context->getProcAddress(QLatin1String("glGetObjectParameterivARB")); + } + + if (funcs->getShaderiv) + funcs->getShaderiv(shader, pname, params); + else + funcs->getShaderiv = qglfResolveGetShaderiv; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetShaderInfoLog)(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getShaderInfoLog = (type_glGetShaderInfoLog) + context->getProcAddress(QLatin1String("glGetShaderInfoLog")); + if (!funcs->getShaderInfoLog) { + funcs->getShaderInfoLog = (type_glGetShaderInfoLog) + context->getProcAddress(QLatin1String("glGetInfoLogARB")); + } + + if (funcs->getShaderInfoLog) + funcs->getShaderInfoLog(shader, bufsize, length, infolog); + else + funcs->getShaderInfoLog = qglfResolveGetShaderInfoLog; +} + +static void QT3D_GLF_APIENTRY qglfSpecialGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) +{ + Q_UNUSED(shadertype); + Q_UNUSED(precisiontype); + range[0] = range[1] = precision[0] = 0; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getShaderPrecisionFormat = (type_glGetShaderPrecisionFormat) + context->getProcAddress(QLatin1String("glGetShaderPrecisionFormat")); +#ifdef QT_OPENGL_ES + if (!funcs->getShaderPrecisionFormat) { + funcs->getShaderPrecisionFormat = (type_glGetShaderPrecisionFormat) + context->getProcAddress(QLatin1String("glGetShaderPrecisionFormatOES")); + } +#endif + if (!funcs->getShaderPrecisionFormat) { + funcs->getShaderPrecisionFormat = (type_glGetShaderPrecisionFormat) + context->getProcAddress(QLatin1String("glGetShaderPrecisionFormatEXT")); + } + if (!funcs->getShaderPrecisionFormat) { + funcs->getShaderPrecisionFormat = (type_glGetShaderPrecisionFormat) + context->getProcAddress(QLatin1String("glGetShaderPrecisionFormatARB")); + } + + if (!funcs->getShaderPrecisionFormat) + funcs->getShaderPrecisionFormat = qglfSpecialGetShaderPrecisionFormat; + + funcs->getShaderPrecisionFormat(shadertype, precisiontype, range, precision); +} + +static void QT3D_GLF_APIENTRY qglfResolveGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, char* source); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getShaderSource = (type_glGetShaderSource) + context->getProcAddress(QLatin1String("glGetShaderSource")); + if (!funcs->getShaderSource) { + funcs->getShaderSource = (type_glGetShaderSource) + context->getProcAddress(QLatin1String("glGetShaderSourceARB")); + } + + if (funcs->getShaderSource) + funcs->getShaderSource(shader, bufsize, length, source); + else + funcs->getShaderSource = qglfResolveGetShaderSource; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetUniformfv(GLuint program, GLint location, GLfloat* params) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetUniformfv)(GLuint program, GLint location, GLfloat* params); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getUniformfv = (type_glGetUniformfv) + context->getProcAddress(QLatin1String("glGetUniformfv")); + if (!funcs->getUniformfv) { + funcs->getUniformfv = (type_glGetUniformfv) + context->getProcAddress(QLatin1String("glGetUniformfvARB")); + } + + if (funcs->getUniformfv) + funcs->getUniformfv(program, location, params); + else + funcs->getUniformfv = qglfResolveGetUniformfv; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetUniformiv(GLuint program, GLint location, GLint* params) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetUniformiv)(GLuint program, GLint location, GLint* params); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getUniformiv = (type_glGetUniformiv) + context->getProcAddress(QLatin1String("glGetUniformiv")); + if (!funcs->getUniformiv) { + funcs->getUniformiv = (type_glGetUniformiv) + context->getProcAddress(QLatin1String("glGetUniformivARB")); + } + + if (funcs->getUniformiv) + funcs->getUniformiv(program, location, params); + else + funcs->getUniformiv = qglfResolveGetUniformiv; +} + +static int QT3D_GLF_APIENTRY qglfResolveGetUniformLocation(GLuint program, const char* name) +{ + typedef int (QT3D_GLF_APIENTRYP type_glGetUniformLocation)(GLuint program, const char* name); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getUniformLocation = (type_glGetUniformLocation) + context->getProcAddress(QLatin1String("glGetUniformLocation")); + if (!funcs->getUniformLocation) { + funcs->getUniformLocation = (type_glGetUniformLocation) + context->getProcAddress(QLatin1String("glGetUniformLocationARB")); + } + + if (funcs->getUniformLocation) + return funcs->getUniformLocation(program, name); + funcs->getUniformLocation = qglfResolveGetUniformLocation; + return int(0); +} + +static void QT3D_GLF_APIENTRY qglfResolveGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getVertexAttribfv = (type_glGetVertexAttribfv) + context->getProcAddress(QLatin1String("glGetVertexAttribfv")); + if (!funcs->getVertexAttribfv) { + funcs->getVertexAttribfv = (type_glGetVertexAttribfv) + context->getProcAddress(QLatin1String("glGetVertexAttribfvARB")); + } + + if (funcs->getVertexAttribfv) + funcs->getVertexAttribfv(index, pname, params); + else + funcs->getVertexAttribfv = qglfResolveGetVertexAttribfv; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetVertexAttribiv(GLuint index, GLenum pname, GLint* params) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetVertexAttribiv)(GLuint index, GLenum pname, GLint* params); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getVertexAttribiv = (type_glGetVertexAttribiv) + context->getProcAddress(QLatin1String("glGetVertexAttribiv")); + if (!funcs->getVertexAttribiv) { + funcs->getVertexAttribiv = (type_glGetVertexAttribiv) + context->getProcAddress(QLatin1String("glGetVertexAttribivARB")); + } + + if (funcs->getVertexAttribiv) + funcs->getVertexAttribiv(index, pname, params); + else + funcs->getVertexAttribiv = qglfResolveGetVertexAttribiv; +} + +static void QT3D_GLF_APIENTRY qglfResolveGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer) +{ + typedef void (QT3D_GLF_APIENTRYP type_glGetVertexAttribPointerv)(GLuint index, GLenum pname, void** pointer); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->getVertexAttribPointerv = (type_glGetVertexAttribPointerv) + context->getProcAddress(QLatin1String("glGetVertexAttribPointerv")); + if (!funcs->getVertexAttribPointerv) { + funcs->getVertexAttribPointerv = (type_glGetVertexAttribPointerv) + context->getProcAddress(QLatin1String("glGetVertexAttribPointervARB")); + } + + if (funcs->getVertexAttribPointerv) + funcs->getVertexAttribPointerv(index, pname, pointer); + else + funcs->getVertexAttribPointerv = qglfResolveGetVertexAttribPointerv; +} + +static GLboolean QT3D_GLF_APIENTRY qglfResolveIsBuffer(GLuint buffer) +{ + typedef GLboolean (QT3D_GLF_APIENTRYP type_glIsBuffer)(GLuint buffer); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->isBuffer = (type_glIsBuffer) + context->getProcAddress(QLatin1String("glIsBuffer")); +#ifdef QT_OPENGL_ES + if (!funcs->isBuffer) { + funcs->isBuffer = (type_glIsBuffer) + context->getProcAddress(QLatin1String("glIsBufferOES")); + } +#endif + if (!funcs->isBuffer) { + funcs->isBuffer = (type_glIsBuffer) + context->getProcAddress(QLatin1String("glIsBufferEXT")); + } + if (!funcs->isBuffer) { + funcs->isBuffer = (type_glIsBuffer) + context->getProcAddress(QLatin1String("glIsBufferARB")); + } + + if (funcs->isBuffer) + return funcs->isBuffer(buffer); + funcs->isBuffer = qglfResolveIsBuffer; + return GLboolean(0); +} + +static GLboolean QT3D_GLF_APIENTRY qglfResolveIsFramebuffer(GLuint framebuffer) +{ + typedef GLboolean (QT3D_GLF_APIENTRYP type_glIsFramebuffer)(GLuint framebuffer); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->isFramebuffer = (type_glIsFramebuffer) + context->getProcAddress(QLatin1String("glIsFramebuffer")); +#ifdef QT_OPENGL_ES + if (!funcs->isFramebuffer) { + funcs->isFramebuffer = (type_glIsFramebuffer) + context->getProcAddress(QLatin1String("glIsFramebufferOES")); + } +#endif + if (!funcs->isFramebuffer) { + funcs->isFramebuffer = (type_glIsFramebuffer) + context->getProcAddress(QLatin1String("glIsFramebufferEXT")); + } + if (!funcs->isFramebuffer) { + funcs->isFramebuffer = (type_glIsFramebuffer) + context->getProcAddress(QLatin1String("glIsFramebufferARB")); + } + + if (funcs->isFramebuffer) + return funcs->isFramebuffer(framebuffer); + funcs->isFramebuffer = qglfResolveIsFramebuffer; + return GLboolean(0); +} + +static GLboolean QT3D_GLF_APIENTRY qglfSpecialIsProgram(GLuint program) +{ + return program != 0; +} + +static GLboolean QT3D_GLF_APIENTRY qglfResolveIsProgram(GLuint program) +{ + typedef GLboolean (QT3D_GLF_APIENTRYP type_glIsProgram)(GLuint program); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->isProgram = (type_glIsProgram) + context->getProcAddress(QLatin1String("glIsProgram")); + if (!funcs->isProgram) { + funcs->isProgram = (type_glIsProgram) + context->getProcAddress(QLatin1String("glIsProgramARB")); + } + + if (!funcs->isProgram) + funcs->isProgram = qglfSpecialIsProgram; + + return funcs->isProgram(program); +} + +static GLboolean QT3D_GLF_APIENTRY qglfResolveIsRenderbuffer(GLuint renderbuffer) +{ + typedef GLboolean (QT3D_GLF_APIENTRYP type_glIsRenderbuffer)(GLuint renderbuffer); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->isRenderbuffer = (type_glIsRenderbuffer) + context->getProcAddress(QLatin1String("glIsRenderbuffer")); +#ifdef QT_OPENGL_ES + if (!funcs->isRenderbuffer) { + funcs->isRenderbuffer = (type_glIsRenderbuffer) + context->getProcAddress(QLatin1String("glIsRenderbufferOES")); + } +#endif + if (!funcs->isRenderbuffer) { + funcs->isRenderbuffer = (type_glIsRenderbuffer) + context->getProcAddress(QLatin1String("glIsRenderbufferEXT")); + } + if (!funcs->isRenderbuffer) { + funcs->isRenderbuffer = (type_glIsRenderbuffer) + context->getProcAddress(QLatin1String("glIsRenderbufferARB")); + } + + if (funcs->isRenderbuffer) + return funcs->isRenderbuffer(renderbuffer); + funcs->isRenderbuffer = qglfResolveIsRenderbuffer; + return GLboolean(0); +} + +static GLboolean QT3D_GLF_APIENTRY qglfSpecialIsShader(GLuint shader) +{ + return shader != 0; +} + +static GLboolean QT3D_GLF_APIENTRY qglfResolveIsShader(GLuint shader) +{ + typedef GLboolean (QT3D_GLF_APIENTRYP type_glIsShader)(GLuint shader); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->isShader = (type_glIsShader) + context->getProcAddress(QLatin1String("glIsShader")); + if (!funcs->isShader) { + funcs->isShader = (type_glIsShader) + context->getProcAddress(QLatin1String("glIsShaderARB")); + } + + if (!funcs->isShader) + funcs->isShader = qglfSpecialIsShader; + + return funcs->isShader(shader); +} + +static void QT3D_GLF_APIENTRY qglfResolveLinkProgram(GLuint program) +{ + typedef void (QT3D_GLF_APIENTRYP type_glLinkProgram)(GLuint program); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->linkProgram = (type_glLinkProgram) + context->getProcAddress(QLatin1String("glLinkProgram")); + if (!funcs->linkProgram) { + funcs->linkProgram = (type_glLinkProgram) + context->getProcAddress(QLatin1String("glLinkProgramARB")); + } + + if (funcs->linkProgram) + funcs->linkProgram(program); + else + funcs->linkProgram = qglfResolveLinkProgram; +} + +static void QT3D_GLF_APIENTRY qglfSpecialReleaseShaderCompiler() +{ +} + +static void QT3D_GLF_APIENTRY qglfResolveReleaseShaderCompiler() +{ + typedef void (QT3D_GLF_APIENTRYP type_glReleaseShaderCompiler)(); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->releaseShaderCompiler = (type_glReleaseShaderCompiler) + context->getProcAddress(QLatin1String("glReleaseShaderCompiler")); + if (!funcs->releaseShaderCompiler) { + funcs->releaseShaderCompiler = (type_glReleaseShaderCompiler) + context->getProcAddress(QLatin1String("glReleaseShaderCompilerARB")); + } + + if (!funcs->releaseShaderCompiler) + funcs->releaseShaderCompiler = qglfSpecialReleaseShaderCompiler; + + funcs->releaseShaderCompiler(); +} + +static void QT3D_GLF_APIENTRY qglfResolveRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +{ + typedef void (QT3D_GLF_APIENTRYP type_glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->renderbufferStorage = (type_glRenderbufferStorage) + context->getProcAddress(QLatin1String("glRenderbufferStorage")); +#ifdef QT_OPENGL_ES + if (!funcs->renderbufferStorage) { + funcs->renderbufferStorage = (type_glRenderbufferStorage) + context->getProcAddress(QLatin1String("glRenderbufferStorageOES")); + } +#endif + if (!funcs->renderbufferStorage) { + funcs->renderbufferStorage = (type_glRenderbufferStorage) + context->getProcAddress(QLatin1String("glRenderbufferStorageEXT")); + } + if (!funcs->renderbufferStorage) { + funcs->renderbufferStorage = (type_glRenderbufferStorage) + context->getProcAddress(QLatin1String("glRenderbufferStorageARB")); + } + + if (funcs->renderbufferStorage) + funcs->renderbufferStorage(target, internalformat, width, height); + else + funcs->renderbufferStorage = qglfResolveRenderbufferStorage; +} + +static void QT3D_GLF_APIENTRY qglfResolveSampleCoverage(GLclampf value, GLboolean invert) +{ + typedef void (QT3D_GLF_APIENTRYP type_glSampleCoverage)(GLclampf value, GLboolean invert); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->sampleCoverage = (type_glSampleCoverage) + context->getProcAddress(QLatin1String("glSampleCoverage")); +#ifdef QT_OPENGL_ES + if (!funcs->sampleCoverage) { + funcs->sampleCoverage = (type_glSampleCoverage) + context->getProcAddress(QLatin1String("glSampleCoverageOES")); + } +#endif + if (!funcs->sampleCoverage) { + funcs->sampleCoverage = (type_glSampleCoverage) + context->getProcAddress(QLatin1String("glSampleCoverageEXT")); + } + if (!funcs->sampleCoverage) { + funcs->sampleCoverage = (type_glSampleCoverage) + context->getProcAddress(QLatin1String("glSampleCoverageARB")); + } + + if (funcs->sampleCoverage) + funcs->sampleCoverage(value, invert); + else + funcs->sampleCoverage = qglfResolveSampleCoverage; +} + +static void QT3D_GLF_APIENTRY qglfResolveShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length) +{ + typedef void (QT3D_GLF_APIENTRYP type_glShaderBinary)(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->shaderBinary = (type_glShaderBinary) + context->getProcAddress(QLatin1String("glShaderBinary")); + if (!funcs->shaderBinary) { + funcs->shaderBinary = (type_glShaderBinary) + context->getProcAddress(QLatin1String("glShaderBinaryARB")); + } + + if (funcs->shaderBinary) + funcs->shaderBinary(n, shaders, binaryformat, binary, length); + else + funcs->shaderBinary = qglfResolveShaderBinary; +} + +static void QT3D_GLF_APIENTRY qglfResolveShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length) +{ + typedef void (QT3D_GLF_APIENTRYP type_glShaderSource)(GLuint shader, GLsizei count, const char** string, const GLint* length); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->shaderSource = (type_glShaderSource) + context->getProcAddress(QLatin1String("glShaderSource")); + if (!funcs->shaderSource) { + funcs->shaderSource = (type_glShaderSource) + context->getProcAddress(QLatin1String("glShaderSourceARB")); + } + + if (funcs->shaderSource) + funcs->shaderSource(shader, count, string, length); + else + funcs->shaderSource = qglfResolveShaderSource; +} + +static void QT3D_GLF_APIENTRY qglfResolveStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) +{ + typedef void (QT3D_GLF_APIENTRYP type_glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->stencilFuncSeparate = (type_glStencilFuncSeparate) + context->getProcAddress(QLatin1String("glStencilFuncSeparate")); +#ifdef QT_OPENGL_ES + if (!funcs->stencilFuncSeparate) { + funcs->stencilFuncSeparate = (type_glStencilFuncSeparate) + context->getProcAddress(QLatin1String("glStencilFuncSeparateOES")); + } +#endif + if (!funcs->stencilFuncSeparate) { + funcs->stencilFuncSeparate = (type_glStencilFuncSeparate) + context->getProcAddress(QLatin1String("glStencilFuncSeparateEXT")); + } + if (!funcs->stencilFuncSeparate) { + funcs->stencilFuncSeparate = (type_glStencilFuncSeparate) + context->getProcAddress(QLatin1String("glStencilFuncSeparateARB")); + } + + if (funcs->stencilFuncSeparate) + funcs->stencilFuncSeparate(face, func, ref, mask); + else + funcs->stencilFuncSeparate = qglfResolveStencilFuncSeparate; +} + +static void QT3D_GLF_APIENTRY qglfResolveStencilMaskSeparate(GLenum face, GLuint mask) +{ + typedef void (QT3D_GLF_APIENTRYP type_glStencilMaskSeparate)(GLenum face, GLuint mask); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->stencilMaskSeparate = (type_glStencilMaskSeparate) + context->getProcAddress(QLatin1String("glStencilMaskSeparate")); +#ifdef QT_OPENGL_ES + if (!funcs->stencilMaskSeparate) { + funcs->stencilMaskSeparate = (type_glStencilMaskSeparate) + context->getProcAddress(QLatin1String("glStencilMaskSeparateOES")); + } +#endif + if (!funcs->stencilMaskSeparate) { + funcs->stencilMaskSeparate = (type_glStencilMaskSeparate) + context->getProcAddress(QLatin1String("glStencilMaskSeparateEXT")); + } + if (!funcs->stencilMaskSeparate) { + funcs->stencilMaskSeparate = (type_glStencilMaskSeparate) + context->getProcAddress(QLatin1String("glStencilMaskSeparateARB")); + } + + if (funcs->stencilMaskSeparate) + funcs->stencilMaskSeparate(face, mask); + else + funcs->stencilMaskSeparate = qglfResolveStencilMaskSeparate; +} + +static void QT3D_GLF_APIENTRY qglfResolveStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) +{ + typedef void (QT3D_GLF_APIENTRYP type_glStencilOpSeparate)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->stencilOpSeparate = (type_glStencilOpSeparate) + context->getProcAddress(QLatin1String("glStencilOpSeparate")); +#ifdef QT_OPENGL_ES + if (!funcs->stencilOpSeparate) { + funcs->stencilOpSeparate = (type_glStencilOpSeparate) + context->getProcAddress(QLatin1String("glStencilOpSeparateOES")); + } +#endif + if (!funcs->stencilOpSeparate) { + funcs->stencilOpSeparate = (type_glStencilOpSeparate) + context->getProcAddress(QLatin1String("glStencilOpSeparateEXT")); + } + if (!funcs->stencilOpSeparate) { + funcs->stencilOpSeparate = (type_glStencilOpSeparate) + context->getProcAddress(QLatin1String("glStencilOpSeparateARB")); + } + + if (funcs->stencilOpSeparate) + funcs->stencilOpSeparate(face, fail, zfail, zpass); + else + funcs->stencilOpSeparate = qglfResolveStencilOpSeparate; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform1f(GLint location, GLfloat x) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform1f)(GLint location, GLfloat x); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform1f = (type_glUniform1f) + context->getProcAddress(QLatin1String("glUniform1f")); + if (!funcs->uniform1f) { + funcs->uniform1f = (type_glUniform1f) + context->getProcAddress(QLatin1String("glUniform1fARB")); + } + + if (funcs->uniform1f) + funcs->uniform1f(location, x); + else + funcs->uniform1f = qglfResolveUniform1f; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform1fv(GLint location, GLsizei count, const GLfloat* v) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform1fv)(GLint location, GLsizei count, const GLfloat* v); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform1fv = (type_glUniform1fv) + context->getProcAddress(QLatin1String("glUniform1fv")); + if (!funcs->uniform1fv) { + funcs->uniform1fv = (type_glUniform1fv) + context->getProcAddress(QLatin1String("glUniform1fvARB")); + } + + if (funcs->uniform1fv) + funcs->uniform1fv(location, count, v); + else + funcs->uniform1fv = qglfResolveUniform1fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform1i(GLint location, GLint x) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform1i)(GLint location, GLint x); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform1i = (type_glUniform1i) + context->getProcAddress(QLatin1String("glUniform1i")); + if (!funcs->uniform1i) { + funcs->uniform1i = (type_glUniform1i) + context->getProcAddress(QLatin1String("glUniform1iARB")); + } + + if (funcs->uniform1i) + funcs->uniform1i(location, x); + else + funcs->uniform1i = qglfResolveUniform1i; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform1iv(GLint location, GLsizei count, const GLint* v) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform1iv)(GLint location, GLsizei count, const GLint* v); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform1iv = (type_glUniform1iv) + context->getProcAddress(QLatin1String("glUniform1iv")); + if (!funcs->uniform1iv) { + funcs->uniform1iv = (type_glUniform1iv) + context->getProcAddress(QLatin1String("glUniform1ivARB")); + } + + if (funcs->uniform1iv) + funcs->uniform1iv(location, count, v); + else + funcs->uniform1iv = qglfResolveUniform1iv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform2f(GLint location, GLfloat x, GLfloat y) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform2f)(GLint location, GLfloat x, GLfloat y); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform2f = (type_glUniform2f) + context->getProcAddress(QLatin1String("glUniform2f")); + if (!funcs->uniform2f) { + funcs->uniform2f = (type_glUniform2f) + context->getProcAddress(QLatin1String("glUniform2fARB")); + } + + if (funcs->uniform2f) + funcs->uniform2f(location, x, y); + else + funcs->uniform2f = qglfResolveUniform2f; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform2fv(GLint location, GLsizei count, const GLfloat* v) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform2fv)(GLint location, GLsizei count, const GLfloat* v); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform2fv = (type_glUniform2fv) + context->getProcAddress(QLatin1String("glUniform2fv")); + if (!funcs->uniform2fv) { + funcs->uniform2fv = (type_glUniform2fv) + context->getProcAddress(QLatin1String("glUniform2fvARB")); + } + + if (funcs->uniform2fv) + funcs->uniform2fv(location, count, v); + else + funcs->uniform2fv = qglfResolveUniform2fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform2i(GLint location, GLint x, GLint y) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform2i)(GLint location, GLint x, GLint y); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform2i = (type_glUniform2i) + context->getProcAddress(QLatin1String("glUniform2i")); + if (!funcs->uniform2i) { + funcs->uniform2i = (type_glUniform2i) + context->getProcAddress(QLatin1String("glUniform2iARB")); + } + + if (funcs->uniform2i) + funcs->uniform2i(location, x, y); + else + funcs->uniform2i = qglfResolveUniform2i; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform2iv(GLint location, GLsizei count, const GLint* v) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform2iv)(GLint location, GLsizei count, const GLint* v); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform2iv = (type_glUniform2iv) + context->getProcAddress(QLatin1String("glUniform2iv")); + if (!funcs->uniform2iv) { + funcs->uniform2iv = (type_glUniform2iv) + context->getProcAddress(QLatin1String("glUniform2ivARB")); + } + + if (funcs->uniform2iv) + funcs->uniform2iv(location, count, v); + else + funcs->uniform2iv = qglfResolveUniform2iv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform3f)(GLint location, GLfloat x, GLfloat y, GLfloat z); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform3f = (type_glUniform3f) + context->getProcAddress(QLatin1String("glUniform3f")); + if (!funcs->uniform3f) { + funcs->uniform3f = (type_glUniform3f) + context->getProcAddress(QLatin1String("glUniform3fARB")); + } + + if (funcs->uniform3f) + funcs->uniform3f(location, x, y, z); + else + funcs->uniform3f = qglfResolveUniform3f; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform3fv(GLint location, GLsizei count, const GLfloat* v) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform3fv)(GLint location, GLsizei count, const GLfloat* v); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform3fv = (type_glUniform3fv) + context->getProcAddress(QLatin1String("glUniform3fv")); + if (!funcs->uniform3fv) { + funcs->uniform3fv = (type_glUniform3fv) + context->getProcAddress(QLatin1String("glUniform3fvARB")); + } + + if (funcs->uniform3fv) + funcs->uniform3fv(location, count, v); + else + funcs->uniform3fv = qglfResolveUniform3fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform3i(GLint location, GLint x, GLint y, GLint z) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform3i)(GLint location, GLint x, GLint y, GLint z); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform3i = (type_glUniform3i) + context->getProcAddress(QLatin1String("glUniform3i")); + if (!funcs->uniform3i) { + funcs->uniform3i = (type_glUniform3i) + context->getProcAddress(QLatin1String("glUniform3iARB")); + } + + if (funcs->uniform3i) + funcs->uniform3i(location, x, y, z); + else + funcs->uniform3i = qglfResolveUniform3i; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform3iv(GLint location, GLsizei count, const GLint* v) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform3iv)(GLint location, GLsizei count, const GLint* v); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform3iv = (type_glUniform3iv) + context->getProcAddress(QLatin1String("glUniform3iv")); + if (!funcs->uniform3iv) { + funcs->uniform3iv = (type_glUniform3iv) + context->getProcAddress(QLatin1String("glUniform3ivARB")); + } + + if (funcs->uniform3iv) + funcs->uniform3iv(location, count, v); + else + funcs->uniform3iv = qglfResolveUniform3iv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform4f)(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform4f = (type_glUniform4f) + context->getProcAddress(QLatin1String("glUniform4f")); + if (!funcs->uniform4f) { + funcs->uniform4f = (type_glUniform4f) + context->getProcAddress(QLatin1String("glUniform4fARB")); + } + + if (funcs->uniform4f) + funcs->uniform4f(location, x, y, z, w); + else + funcs->uniform4f = qglfResolveUniform4f; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform4fv(GLint location, GLsizei count, const GLfloat* v) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform4fv)(GLint location, GLsizei count, const GLfloat* v); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform4fv = (type_glUniform4fv) + context->getProcAddress(QLatin1String("glUniform4fv")); + if (!funcs->uniform4fv) { + funcs->uniform4fv = (type_glUniform4fv) + context->getProcAddress(QLatin1String("glUniform4fvARB")); + } + + if (funcs->uniform4fv) + funcs->uniform4fv(location, count, v); + else + funcs->uniform4fv = qglfResolveUniform4fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform4i)(GLint location, GLint x, GLint y, GLint z, GLint w); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform4i = (type_glUniform4i) + context->getProcAddress(QLatin1String("glUniform4i")); + if (!funcs->uniform4i) { + funcs->uniform4i = (type_glUniform4i) + context->getProcAddress(QLatin1String("glUniform4iARB")); + } + + if (funcs->uniform4i) + funcs->uniform4i(location, x, y, z, w); + else + funcs->uniform4i = qglfResolveUniform4i; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniform4iv(GLint location, GLsizei count, const GLint* v) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniform4iv)(GLint location, GLsizei count, const GLint* v); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniform4iv = (type_glUniform4iv) + context->getProcAddress(QLatin1String("glUniform4iv")); + if (!funcs->uniform4iv) { + funcs->uniform4iv = (type_glUniform4iv) + context->getProcAddress(QLatin1String("glUniform4ivARB")); + } + + if (funcs->uniform4iv) + funcs->uniform4iv(location, count, v); + else + funcs->uniform4iv = qglfResolveUniform4iv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniformMatrix2fv = (type_glUniformMatrix2fv) + context->getProcAddress(QLatin1String("glUniformMatrix2fv")); + if (!funcs->uniformMatrix2fv) { + funcs->uniformMatrix2fv = (type_glUniformMatrix2fv) + context->getProcAddress(QLatin1String("glUniformMatrix2fvARB")); + } + + if (funcs->uniformMatrix2fv) + funcs->uniformMatrix2fv(location, count, transpose, value); + else + funcs->uniformMatrix2fv = qglfResolveUniformMatrix2fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniformMatrix3fv = (type_glUniformMatrix3fv) + context->getProcAddress(QLatin1String("glUniformMatrix3fv")); + if (!funcs->uniformMatrix3fv) { + funcs->uniformMatrix3fv = (type_glUniformMatrix3fv) + context->getProcAddress(QLatin1String("glUniformMatrix3fvARB")); + } + + if (funcs->uniformMatrix3fv) + funcs->uniformMatrix3fv(location, count, transpose, value); + else + funcs->uniformMatrix3fv = qglfResolveUniformMatrix3fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->uniformMatrix4fv = (type_glUniformMatrix4fv) + context->getProcAddress(QLatin1String("glUniformMatrix4fv")); + if (!funcs->uniformMatrix4fv) { + funcs->uniformMatrix4fv = (type_glUniformMatrix4fv) + context->getProcAddress(QLatin1String("glUniformMatrix4fvARB")); + } + + if (funcs->uniformMatrix4fv) + funcs->uniformMatrix4fv(location, count, transpose, value); + else + funcs->uniformMatrix4fv = qglfResolveUniformMatrix4fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveUseProgram(GLuint program) +{ + typedef void (QT3D_GLF_APIENTRYP type_glUseProgram)(GLuint program); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->useProgram = (type_glUseProgram) + context->getProcAddress(QLatin1String("glUseProgram")); + if (!funcs->useProgram) { + funcs->useProgram = (type_glUseProgram) + context->getProcAddress(QLatin1String("glUseProgramObjectARB")); + } + + if (funcs->useProgram) + funcs->useProgram(program); + else + funcs->useProgram = qglfResolveUseProgram; +} + +static void QT3D_GLF_APIENTRY qglfResolveValidateProgram(GLuint program) +{ + typedef void (QT3D_GLF_APIENTRYP type_glValidateProgram)(GLuint program); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->validateProgram = (type_glValidateProgram) + context->getProcAddress(QLatin1String("glValidateProgram")); + if (!funcs->validateProgram) { + funcs->validateProgram = (type_glValidateProgram) + context->getProcAddress(QLatin1String("glValidateProgramARB")); + } + + if (funcs->validateProgram) + funcs->validateProgram(program); + else + funcs->validateProgram = qglfResolveValidateProgram; +} + +static void QT3D_GLF_APIENTRY qglfResolveVertexAttrib1f(GLuint indx, GLfloat x) +{ + typedef void (QT3D_GLF_APIENTRYP type_glVertexAttrib1f)(GLuint indx, GLfloat x); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->vertexAttrib1f = (type_glVertexAttrib1f) + context->getProcAddress(QLatin1String("glVertexAttrib1f")); + if (!funcs->vertexAttrib1f) { + funcs->vertexAttrib1f = (type_glVertexAttrib1f) + context->getProcAddress(QLatin1String("glVertexAttrib1fARB")); + } + + if (funcs->vertexAttrib1f) + funcs->vertexAttrib1f(indx, x); + else + funcs->vertexAttrib1f = qglfResolveVertexAttrib1f; +} + +static void QT3D_GLF_APIENTRY qglfResolveVertexAttrib1fv(GLuint indx, const GLfloat* values) +{ + typedef void (QT3D_GLF_APIENTRYP type_glVertexAttrib1fv)(GLuint indx, const GLfloat* values); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->vertexAttrib1fv = (type_glVertexAttrib1fv) + context->getProcAddress(QLatin1String("glVertexAttrib1fv")); + if (!funcs->vertexAttrib1fv) { + funcs->vertexAttrib1fv = (type_glVertexAttrib1fv) + context->getProcAddress(QLatin1String("glVertexAttrib1fvARB")); + } + + if (funcs->vertexAttrib1fv) + funcs->vertexAttrib1fv(indx, values); + else + funcs->vertexAttrib1fv = qglfResolveVertexAttrib1fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) +{ + typedef void (QT3D_GLF_APIENTRYP type_glVertexAttrib2f)(GLuint indx, GLfloat x, GLfloat y); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->vertexAttrib2f = (type_glVertexAttrib2f) + context->getProcAddress(QLatin1String("glVertexAttrib2f")); + if (!funcs->vertexAttrib2f) { + funcs->vertexAttrib2f = (type_glVertexAttrib2f) + context->getProcAddress(QLatin1String("glVertexAttrib2fARB")); + } + + if (funcs->vertexAttrib2f) + funcs->vertexAttrib2f(indx, x, y); + else + funcs->vertexAttrib2f = qglfResolveVertexAttrib2f; +} + +static void QT3D_GLF_APIENTRY qglfResolveVertexAttrib2fv(GLuint indx, const GLfloat* values) +{ + typedef void (QT3D_GLF_APIENTRYP type_glVertexAttrib2fv)(GLuint indx, const GLfloat* values); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->vertexAttrib2fv = (type_glVertexAttrib2fv) + context->getProcAddress(QLatin1String("glVertexAttrib2fv")); + if (!funcs->vertexAttrib2fv) { + funcs->vertexAttrib2fv = (type_glVertexAttrib2fv) + context->getProcAddress(QLatin1String("glVertexAttrib2fvARB")); + } + + if (funcs->vertexAttrib2fv) + funcs->vertexAttrib2fv(indx, values); + else + funcs->vertexAttrib2fv = qglfResolveVertexAttrib2fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) +{ + typedef void (QT3D_GLF_APIENTRYP type_glVertexAttrib3f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->vertexAttrib3f = (type_glVertexAttrib3f) + context->getProcAddress(QLatin1String("glVertexAttrib3f")); + if (!funcs->vertexAttrib3f) { + funcs->vertexAttrib3f = (type_glVertexAttrib3f) + context->getProcAddress(QLatin1String("glVertexAttrib3fARB")); + } + + if (funcs->vertexAttrib3f) + funcs->vertexAttrib3f(indx, x, y, z); + else + funcs->vertexAttrib3f = qglfResolveVertexAttrib3f; +} + +static void QT3D_GLF_APIENTRY qglfResolveVertexAttrib3fv(GLuint indx, const GLfloat* values) +{ + typedef void (QT3D_GLF_APIENTRYP type_glVertexAttrib3fv)(GLuint indx, const GLfloat* values); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->vertexAttrib3fv = (type_glVertexAttrib3fv) + context->getProcAddress(QLatin1String("glVertexAttrib3fv")); + if (!funcs->vertexAttrib3fv) { + funcs->vertexAttrib3fv = (type_glVertexAttrib3fv) + context->getProcAddress(QLatin1String("glVertexAttrib3fvARB")); + } + + if (funcs->vertexAttrib3fv) + funcs->vertexAttrib3fv(indx, values); + else + funcs->vertexAttrib3fv = qglfResolveVertexAttrib3fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + typedef void (QT3D_GLF_APIENTRYP type_glVertexAttrib4f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->vertexAttrib4f = (type_glVertexAttrib4f) + context->getProcAddress(QLatin1String("glVertexAttrib4f")); + if (!funcs->vertexAttrib4f) { + funcs->vertexAttrib4f = (type_glVertexAttrib4f) + context->getProcAddress(QLatin1String("glVertexAttrib4fARB")); + } + + if (funcs->vertexAttrib4f) + funcs->vertexAttrib4f(indx, x, y, z, w); + else + funcs->vertexAttrib4f = qglfResolveVertexAttrib4f; +} + +static void QT3D_GLF_APIENTRY qglfResolveVertexAttrib4fv(GLuint indx, const GLfloat* values) +{ + typedef void (QT3D_GLF_APIENTRYP type_glVertexAttrib4fv)(GLuint indx, const GLfloat* values); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->vertexAttrib4fv = (type_glVertexAttrib4fv) + context->getProcAddress(QLatin1String("glVertexAttrib4fv")); + if (!funcs->vertexAttrib4fv) { + funcs->vertexAttrib4fv = (type_glVertexAttrib4fv) + context->getProcAddress(QLatin1String("glVertexAttrib4fvARB")); + } + + if (funcs->vertexAttrib4fv) + funcs->vertexAttrib4fv(indx, values); + else + funcs->vertexAttrib4fv = qglfResolveVertexAttrib4fv; +} + +static void QT3D_GLF_APIENTRY qglfResolveVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) +{ + typedef void (QT3D_GLF_APIENTRYP type_glVertexAttribPointer)(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr); + + const QGLContext *context = QGLContext::currentContext(); + QOpenGLFunctionsPrivate *funcs = qt_gl_functions(context); + + funcs->vertexAttribPointer = (type_glVertexAttribPointer) + context->getProcAddress(QLatin1String("glVertexAttribPointer")); + if (!funcs->vertexAttribPointer) { + funcs->vertexAttribPointer = (type_glVertexAttribPointer) + context->getProcAddress(QLatin1String("glVertexAttribPointerARB")); + } + + if (funcs->vertexAttribPointer) + funcs->vertexAttribPointer(indx, size, type, normalized, stride, ptr); + else + funcs->vertexAttribPointer = qglfResolveVertexAttribPointer; +} + +#endif // !QT_OPENGL_ES_2 + +QOpenGLFunctionsPrivate::QOpenGLFunctionsPrivate(const QGLContext *) +{ +#ifndef QT_OPENGL_ES_2 + activeTexture = qglfResolveActiveTexture; + attachShader = qglfResolveAttachShader; + bindAttribLocation = qglfResolveBindAttribLocation; + bindBuffer = qglfResolveBindBuffer; + bindFramebuffer = qglfResolveBindFramebuffer; + bindRenderbuffer = qglfResolveBindRenderbuffer; + blendColor = qglfResolveBlendColor; + blendEquation = qglfResolveBlendEquation; + blendEquationSeparate = qglfResolveBlendEquationSeparate; + blendFuncSeparate = qglfResolveBlendFuncSeparate; + bufferData = qglfResolveBufferData; + bufferSubData = qglfResolveBufferSubData; + checkFramebufferStatus = qglfResolveCheckFramebufferStatus; + compileShader = qglfResolveCompileShader; + compressedTexImage2D = qglfResolveCompressedTexImage2D; + compressedTexSubImage2D = qglfResolveCompressedTexSubImage2D; + createProgram = qglfResolveCreateProgram; + createShader = qglfResolveCreateShader; + deleteBuffers = qglfResolveDeleteBuffers; + deleteFramebuffers = qglfResolveDeleteFramebuffers; + deleteProgram = qglfResolveDeleteProgram; + deleteRenderbuffers = qglfResolveDeleteRenderbuffers; + deleteShader = qglfResolveDeleteShader; + detachShader = qglfResolveDetachShader; + disableVertexAttribArray = qglfResolveDisableVertexAttribArray; + enableVertexAttribArray = qglfResolveEnableVertexAttribArray; + framebufferRenderbuffer = qglfResolveFramebufferRenderbuffer; + framebufferTexture2D = qglfResolveFramebufferTexture2D; + genBuffers = qglfResolveGenBuffers; + generateMipmap = qglfResolveGenerateMipmap; + genFramebuffers = qglfResolveGenFramebuffers; + genRenderbuffers = qglfResolveGenRenderbuffers; + getActiveAttrib = qglfResolveGetActiveAttrib; + getActiveUniform = qglfResolveGetActiveUniform; + getAttachedShaders = qglfResolveGetAttachedShaders; + getAttribLocation = qglfResolveGetAttribLocation; + getBufferParameteriv = qglfResolveGetBufferParameteriv; + getFramebufferAttachmentParameteriv = qglfResolveGetFramebufferAttachmentParameteriv; + getProgramiv = qglfResolveGetProgramiv; + getProgramInfoLog = qglfResolveGetProgramInfoLog; + getRenderbufferParameteriv = qglfResolveGetRenderbufferParameteriv; + getShaderiv = qglfResolveGetShaderiv; + getShaderInfoLog = qglfResolveGetShaderInfoLog; + getShaderPrecisionFormat = qglfResolveGetShaderPrecisionFormat; + getShaderSource = qglfResolveGetShaderSource; + getUniformfv = qglfResolveGetUniformfv; + getUniformiv = qglfResolveGetUniformiv; + getUniformLocation = qglfResolveGetUniformLocation; + getVertexAttribfv = qglfResolveGetVertexAttribfv; + getVertexAttribiv = qglfResolveGetVertexAttribiv; + getVertexAttribPointerv = qglfResolveGetVertexAttribPointerv; + isBuffer = qglfResolveIsBuffer; + isFramebuffer = qglfResolveIsFramebuffer; + isProgram = qglfResolveIsProgram; + isRenderbuffer = qglfResolveIsRenderbuffer; + isShader = qglfResolveIsShader; + linkProgram = qglfResolveLinkProgram; + releaseShaderCompiler = qglfResolveReleaseShaderCompiler; + renderbufferStorage = qglfResolveRenderbufferStorage; + sampleCoverage = qglfResolveSampleCoverage; + shaderBinary = qglfResolveShaderBinary; + shaderSource = qglfResolveShaderSource; + stencilFuncSeparate = qglfResolveStencilFuncSeparate; + stencilMaskSeparate = qglfResolveStencilMaskSeparate; + stencilOpSeparate = qglfResolveStencilOpSeparate; + uniform1f = qglfResolveUniform1f; + uniform1fv = qglfResolveUniform1fv; + uniform1i = qglfResolveUniform1i; + uniform1iv = qglfResolveUniform1iv; + uniform2f = qglfResolveUniform2f; + uniform2fv = qglfResolveUniform2fv; + uniform2i = qglfResolveUniform2i; + uniform2iv = qglfResolveUniform2iv; + uniform3f = qglfResolveUniform3f; + uniform3fv = qglfResolveUniform3fv; + uniform3i = qglfResolveUniform3i; + uniform3iv = qglfResolveUniform3iv; + uniform4f = qglfResolveUniform4f; + uniform4fv = qglfResolveUniform4fv; + uniform4i = qglfResolveUniform4i; + uniform4iv = qglfResolveUniform4iv; + uniformMatrix2fv = qglfResolveUniformMatrix2fv; + uniformMatrix3fv = qglfResolveUniformMatrix3fv; + uniformMatrix4fv = qglfResolveUniformMatrix4fv; + useProgram = qglfResolveUseProgram; + validateProgram = qglfResolveValidateProgram; + vertexAttrib1f = qglfResolveVertexAttrib1f; + vertexAttrib1fv = qglfResolveVertexAttrib1fv; + vertexAttrib2f = qglfResolveVertexAttrib2f; + vertexAttrib2fv = qglfResolveVertexAttrib2fv; + vertexAttrib3f = qglfResolveVertexAttrib3f; + vertexAttrib3fv = qglfResolveVertexAttrib3fv; + vertexAttrib4f = qglfResolveVertexAttrib4f; + vertexAttrib4fv = qglfResolveVertexAttrib4fv; + vertexAttribPointer = qglfResolveVertexAttribPointer; +#endif // !QT_OPENGL_ES_2 +} + diff --git a/src/threed/api/qopenglfunctions.h b/src/threed/api/qopenglfunctions.h new file mode 100644 index 000000000..0081e907c --- /dev/null +++ b/src/threed/api/qopenglfunctions.h @@ -0,0 +1,2292 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOPENGLFUNCTIONS_H +#define QOPENGLFUNCTIONS_H + +#include <QtOpenGL/qgl.h> +#include "qt3dglobal.h" +#include <QDebug> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +// Types that aren't defined in all system's gl.h files. +typedef ptrdiff_t qgl_GLintptr; +typedef ptrdiff_t qgl_GLsizeiptr; + +#ifdef Q_WS_WIN +# define QT3D_GLF_APIENTRY APIENTRY +#endif + +#ifndef Q_WS_MAC +# ifndef QT3D_GLF_APIENTRYP +# ifdef QT3D_GLF_APIENTRY +# define QT3D_GLF_APIENTRYP QT3D_GLF_APIENTRY * +# else +# define QT3D_GLF_APIENTRY +# define QT3D_GLF_APIENTRYP * +# endif +# endif +#else +# define QT3D_GLF_APIENTRY +# define QT3D_GLF_APIENTRYP * +#endif + +struct QOpenGLFunctionsPrivate; + +// Undefine any macros from GLEW, qglextensions_p.h, etc that +// may interfere with the definition of QOpenGLFunctions. +#undef glActiveTexture +#undef glAttachShader +#undef glBindAttribLocation +#undef glBindBuffer +#undef glBindFramebuffer +#undef glBindRenderbuffer +#undef glBlendColor +#undef glBlendEquation +#undef glBlendEquationSeparate +#undef glBlendFuncSeparate +#undef glBufferData +#undef glBufferSubData +#undef glCheckFramebufferStatus +#undef glClearDepthf +#undef glCompileShader +#undef glCompressedTexImage2D +#undef glCompressedTexSubImage2D +#undef glCreateProgram +#undef glCreateShader +#undef glDeleteBuffers +#undef glDeleteFramebuffers +#undef glDeleteProgram +#undef glDeleteRenderbuffers +#undef glDeleteShader +#undef glDepthRangef +#undef glDetachShader +#undef glDisableVertexAttribArray +#undef glEnableVertexAttribArray +#undef glFramebufferRenderbuffer +#undef glFramebufferTexture2D +#undef glGenBuffers +#undef glGenerateMipmap +#undef glGenFramebuffers +#undef glGenRenderbuffers +#undef glGetActiveAttrib +#undef glGetActiveUniform +#undef glGetAttachedShaders +#undef glGetAttribLocation +#undef glGetBufferParameteriv +#undef glGetFramebufferAttachmentParameteriv +#undef glGetProgramiv +#undef glGetProgramInfoLog +#undef glGetRenderbufferParameteriv +#undef glGetShaderiv +#undef glGetShaderInfoLog +#undef glGetShaderPrecisionFormat +#undef glGetShaderSource +#undef glGetUniformfv +#undef glGetUniformiv +#undef glGetUniformLocation +#undef glGetVertexAttribfv +#undef glGetVertexAttribiv +#undef glGetVertexAttribPointerv +#undef glIsBuffer +#undef glIsFramebuffer +#undef glIsProgram +#undef glIsRenderbuffer +#undef glIsShader +#undef glLinkProgram +#undef glReleaseShaderCompiler +#undef glRenderbufferStorage +#undef glSampleCoverage +#undef glShaderBinary +#undef glShaderSource +#undef glStencilFuncSeparate +#undef glStencilMaskSeparate +#undef glStencilOpSeparate +#undef glUniform1f +#undef glUniform1fv +#undef glUniform1i +#undef glUniform1iv +#undef glUniform2f +#undef glUniform2fv +#undef glUniform2i +#undef glUniform2iv +#undef glUniform3f +#undef glUniform3fv +#undef glUniform3i +#undef glUniform3iv +#undef glUniform4f +#undef glUniform4fv +#undef glUniform4i +#undef glUniform4iv +#undef glUniformMatrix2fv +#undef glUniformMatrix3fv +#undef glUniformMatrix4fv +#undef glUseProgram +#undef glValidateProgram +#undef glVertexAttrib1f +#undef glVertexAttrib1fv +#undef glVertexAttrib2f +#undef glVertexAttrib2fv +#undef glVertexAttrib3f +#undef glVertexAttrib3fv +#undef glVertexAttrib4f +#undef glVertexAttrib4fv +#undef glVertexAttribPointer + +class Q_QT3D_EXPORT QOpenGLFunctions +{ +public: + QOpenGLFunctions(); + explicit QOpenGLFunctions(const QGLContext *context); + ~QOpenGLFunctions() {} + + enum OpenGLFeature + { + Multitexture = 0x0001, + Shaders = 0x0002, + Buffers = 0x0004, + Framebuffers = 0x0008, + BlendColor = 0x0010, + BlendEquation = 0x0020, + BlendEquationSeparate = 0x0040, + BlendFuncSeparate = 0x0080, + BlendSubtract = 0x0100, + CompressedTextures = 0x0200, + Multisample = 0x0400, + StencilSeparate = 0x0800, + NPOTTextures = 0x1000 + }; + Q_DECLARE_FLAGS(OpenGLFeatures, OpenGLFeature) + + QOpenGLFunctions::OpenGLFeatures openGLFeatures() const; + bool hasOpenGLFeature(QOpenGLFunctions::OpenGLFeature feature) const; + + void initializeGLFunctions(const QGLContext *context = 0); + + void glActiveTexture(GLenum texture); + void glAttachShader(GLuint program, GLuint shader); + void glBindAttribLocation(GLuint program, GLuint index, const char* name); + void glBindBuffer(GLenum target, GLuint buffer); + void glBindFramebuffer(GLenum target, GLuint framebuffer); + void glBindRenderbuffer(GLenum target, GLuint renderbuffer); + void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); + void glBlendEquation(GLenum mode); + void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha); + void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + void glBufferData(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage); + void glBufferSubData(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data); + GLenum glCheckFramebufferStatus(GLenum target); + void glClearDepthf(GLclampf depth); + void glCompileShader(GLuint shader); + void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data); + void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data); + GLuint glCreateProgram(); + GLuint glCreateShader(GLenum type); + void glDeleteBuffers(GLsizei n, const GLuint* buffers); + void glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers); + void glDeleteProgram(GLuint program); + void glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers); + void glDeleteShader(GLuint shader); + void glDepthRangef(GLclampf zNear, GLclampf zFar); + void glDetachShader(GLuint program, GLuint shader); + void glDisableVertexAttribArray(GLuint index); + void glEnableVertexAttribArray(GLuint index); + void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + void glGenBuffers(GLsizei n, GLuint* buffers); + void glGenerateMipmap(GLenum target); + void glGenFramebuffers(GLsizei n, GLuint* framebuffers); + void glGenRenderbuffers(GLsizei n, GLuint* renderbuffers); + void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + void glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); + int glGetAttribLocation(GLuint program, const char* name); + void glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params); + void glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params); + void glGetProgramiv(GLuint program, GLenum pname, GLint* params); + void glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog); + void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params); + void glGetShaderiv(GLuint shader, GLenum pname, GLint* params); + void glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog); + void glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); + void glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source); + void glGetUniformfv(GLuint program, GLint location, GLfloat* params); + void glGetUniformiv(GLuint program, GLint location, GLint* params); + int glGetUniformLocation(GLuint program, const char* name); + void glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params); + void glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params); + void glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer); + GLboolean glIsBuffer(GLuint buffer); + GLboolean glIsFramebuffer(GLuint framebuffer); + GLboolean glIsProgram(GLuint program); + GLboolean glIsRenderbuffer(GLuint renderbuffer); + GLboolean glIsShader(GLuint shader); + void glLinkProgram(GLuint program); + void glReleaseShaderCompiler(); + void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + void glSampleCoverage(GLclampf value, GLboolean invert); + void glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length); + void glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length); + void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask); + void glStencilMaskSeparate(GLenum face, GLuint mask); + void glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass); + void glUniform1f(GLint location, GLfloat x); + void glUniform1fv(GLint location, GLsizei count, const GLfloat* v); + void glUniform1i(GLint location, GLint x); + void glUniform1iv(GLint location, GLsizei count, const GLint* v); + void glUniform2f(GLint location, GLfloat x, GLfloat y); + void glUniform2fv(GLint location, GLsizei count, const GLfloat* v); + void glUniform2i(GLint location, GLint x, GLint y); + void glUniform2iv(GLint location, GLsizei count, const GLint* v); + void glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z); + void glUniform3fv(GLint location, GLsizei count, const GLfloat* v); + void glUniform3i(GLint location, GLint x, GLint y, GLint z); + void glUniform3iv(GLint location, GLsizei count, const GLint* v); + void glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void glUniform4fv(GLint location, GLsizei count, const GLfloat* v); + void glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w); + void glUniform4iv(GLint location, GLsizei count, const GLint* v); + void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void glUseProgram(GLuint program); + void glValidateProgram(GLuint program); + void glVertexAttrib1f(GLuint indx, GLfloat x); + void glVertexAttrib1fv(GLuint indx, const GLfloat* values); + void glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y); + void glVertexAttrib2fv(GLuint indx, const GLfloat* values); + void glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z); + void glVertexAttrib3fv(GLuint indx, const GLfloat* values); + void glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void glVertexAttrib4fv(GLuint indx, const GLfloat* values); + void glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr); + +private: + QOpenGLFunctionsPrivate *d_ptr; + static bool isInitialized(const QOpenGLFunctionsPrivate *d) { return d != 0; } +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QOpenGLFunctions::OpenGLFeatures) + +struct QOpenGLFunctionsPrivate +{ + QOpenGLFunctionsPrivate(const QGLContext *context = 0); + +#ifndef QT_OPENGL_ES_2 + void (QT3D_GLF_APIENTRYP activeTexture)(GLenum texture); + void (QT3D_GLF_APIENTRYP attachShader)(GLuint program, GLuint shader); + void (QT3D_GLF_APIENTRYP bindAttribLocation)(GLuint program, GLuint index, const char* name); + void (QT3D_GLF_APIENTRYP bindBuffer)(GLenum target, GLuint buffer); + void (QT3D_GLF_APIENTRYP bindFramebuffer)(GLenum target, GLuint framebuffer); + void (QT3D_GLF_APIENTRYP bindRenderbuffer)(GLenum target, GLuint renderbuffer); + void (QT3D_GLF_APIENTRYP blendColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); + void (QT3D_GLF_APIENTRYP blendEquation)(GLenum mode); + void (QT3D_GLF_APIENTRYP blendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha); + void (QT3D_GLF_APIENTRYP blendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + void (QT3D_GLF_APIENTRYP bufferData)(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage); + void (QT3D_GLF_APIENTRYP bufferSubData)(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data); + GLenum (QT3D_GLF_APIENTRYP checkFramebufferStatus)(GLenum target); + void (QT3D_GLF_APIENTRYP compileShader)(GLuint shader); + void (QT3D_GLF_APIENTRYP compressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data); + void (QT3D_GLF_APIENTRYP compressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data); + GLuint (QT3D_GLF_APIENTRYP createProgram)(); + GLuint (QT3D_GLF_APIENTRYP createShader)(GLenum type); + void (QT3D_GLF_APIENTRYP deleteBuffers)(GLsizei n, const GLuint* buffers); + void (QT3D_GLF_APIENTRYP deleteFramebuffers)(GLsizei n, const GLuint* framebuffers); + void (QT3D_GLF_APIENTRYP deleteProgram)(GLuint program); + void (QT3D_GLF_APIENTRYP deleteRenderbuffers)(GLsizei n, const GLuint* renderbuffers); + void (QT3D_GLF_APIENTRYP deleteShader)(GLuint shader); + void (QT3D_GLF_APIENTRYP detachShader)(GLuint program, GLuint shader); + void (QT3D_GLF_APIENTRYP disableVertexAttribArray)(GLuint index); + void (QT3D_GLF_APIENTRYP enableVertexAttribArray)(GLuint index); + void (QT3D_GLF_APIENTRYP framebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + void (QT3D_GLF_APIENTRYP framebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + void (QT3D_GLF_APIENTRYP genBuffers)(GLsizei n, GLuint* buffers); + void (QT3D_GLF_APIENTRYP generateMipmap)(GLenum target); + void (QT3D_GLF_APIENTRYP genFramebuffers)(GLsizei n, GLuint* framebuffers); + void (QT3D_GLF_APIENTRYP genRenderbuffers)(GLsizei n, GLuint* renderbuffers); + void (QT3D_GLF_APIENTRYP getActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + void (QT3D_GLF_APIENTRYP getActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name); + void (QT3D_GLF_APIENTRYP getAttachedShaders)(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders); + int (QT3D_GLF_APIENTRYP getAttribLocation)(GLuint program, const char* name); + void (QT3D_GLF_APIENTRYP getBufferParameteriv)(GLenum target, GLenum pname, GLint* params); + void (QT3D_GLF_APIENTRYP getFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint* params); + void (QT3D_GLF_APIENTRYP getProgramiv)(GLuint program, GLenum pname, GLint* params); + void (QT3D_GLF_APIENTRYP getProgramInfoLog)(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog); + void (QT3D_GLF_APIENTRYP getRenderbufferParameteriv)(GLenum target, GLenum pname, GLint* params); + void (QT3D_GLF_APIENTRYP getShaderiv)(GLuint shader, GLenum pname, GLint* params); + void (QT3D_GLF_APIENTRYP getShaderInfoLog)(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog); + void (QT3D_GLF_APIENTRYP getShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); + void (QT3D_GLF_APIENTRYP getShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, char* source); + void (QT3D_GLF_APIENTRYP getUniformfv)(GLuint program, GLint location, GLfloat* params); + void (QT3D_GLF_APIENTRYP getUniformiv)(GLuint program, GLint location, GLint* params); + int (QT3D_GLF_APIENTRYP getUniformLocation)(GLuint program, const char* name); + void (QT3D_GLF_APIENTRYP getVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params); + void (QT3D_GLF_APIENTRYP getVertexAttribiv)(GLuint index, GLenum pname, GLint* params); + void (QT3D_GLF_APIENTRYP getVertexAttribPointerv)(GLuint index, GLenum pname, void** pointer); + GLboolean (QT3D_GLF_APIENTRYP isBuffer)(GLuint buffer); + GLboolean (QT3D_GLF_APIENTRYP isFramebuffer)(GLuint framebuffer); + GLboolean (QT3D_GLF_APIENTRYP isProgram)(GLuint program); + GLboolean (QT3D_GLF_APIENTRYP isRenderbuffer)(GLuint renderbuffer); + GLboolean (QT3D_GLF_APIENTRYP isShader)(GLuint shader); + void (QT3D_GLF_APIENTRYP linkProgram)(GLuint program); + void (QT3D_GLF_APIENTRYP releaseShaderCompiler)(); + void (QT3D_GLF_APIENTRYP renderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + void (QT3D_GLF_APIENTRYP sampleCoverage)(GLclampf value, GLboolean invert); + void (QT3D_GLF_APIENTRYP shaderBinary)(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length); + void (QT3D_GLF_APIENTRYP shaderSource)(GLuint shader, GLsizei count, const char** string, const GLint* length); + void (QT3D_GLF_APIENTRYP stencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask); + void (QT3D_GLF_APIENTRYP stencilMaskSeparate)(GLenum face, GLuint mask); + void (QT3D_GLF_APIENTRYP stencilOpSeparate)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass); + void (QT3D_GLF_APIENTRYP uniform1f)(GLint location, GLfloat x); + void (QT3D_GLF_APIENTRYP uniform1fv)(GLint location, GLsizei count, const GLfloat* v); + void (QT3D_GLF_APIENTRYP uniform1i)(GLint location, GLint x); + void (QT3D_GLF_APIENTRYP uniform1iv)(GLint location, GLsizei count, const GLint* v); + void (QT3D_GLF_APIENTRYP uniform2f)(GLint location, GLfloat x, GLfloat y); + void (QT3D_GLF_APIENTRYP uniform2fv)(GLint location, GLsizei count, const GLfloat* v); + void (QT3D_GLF_APIENTRYP uniform2i)(GLint location, GLint x, GLint y); + void (QT3D_GLF_APIENTRYP uniform2iv)(GLint location, GLsizei count, const GLint* v); + void (QT3D_GLF_APIENTRYP uniform3f)(GLint location, GLfloat x, GLfloat y, GLfloat z); + void (QT3D_GLF_APIENTRYP uniform3fv)(GLint location, GLsizei count, const GLfloat* v); + void (QT3D_GLF_APIENTRYP uniform3i)(GLint location, GLint x, GLint y, GLint z); + void (QT3D_GLF_APIENTRYP uniform3iv)(GLint location, GLsizei count, const GLint* v); + void (QT3D_GLF_APIENTRYP uniform4f)(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void (QT3D_GLF_APIENTRYP uniform4fv)(GLint location, GLsizei count, const GLfloat* v); + void (QT3D_GLF_APIENTRYP uniform4i)(GLint location, GLint x, GLint y, GLint z, GLint w); + void (QT3D_GLF_APIENTRYP uniform4iv)(GLint location, GLsizei count, const GLint* v); + void (QT3D_GLF_APIENTRYP uniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void (QT3D_GLF_APIENTRYP uniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void (QT3D_GLF_APIENTRYP uniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + void (QT3D_GLF_APIENTRYP useProgram)(GLuint program); + void (QT3D_GLF_APIENTRYP validateProgram)(GLuint program); + void (QT3D_GLF_APIENTRYP vertexAttrib1f)(GLuint indx, GLfloat x); + void (QT3D_GLF_APIENTRYP vertexAttrib1fv)(GLuint indx, const GLfloat* values); + void (QT3D_GLF_APIENTRYP vertexAttrib2f)(GLuint indx, GLfloat x, GLfloat y); + void (QT3D_GLF_APIENTRYP vertexAttrib2fv)(GLuint indx, const GLfloat* values); + void (QT3D_GLF_APIENTRYP vertexAttrib3f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z); + void (QT3D_GLF_APIENTRYP vertexAttrib3fv)(GLuint indx, const GLfloat* values); + void (QT3D_GLF_APIENTRYP vertexAttrib4f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void (QT3D_GLF_APIENTRYP vertexAttrib4fv)(GLuint indx, const GLfloat* values); + void (QT3D_GLF_APIENTRYP vertexAttribPointer)(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr); +#endif +}; + +inline void QOpenGLFunctions::glActiveTexture(GLenum texture) +{ +#if defined(QT_OPENGL_ES_2) + ::glActiveTexture(texture); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->activeTexture(texture); +#endif +} + +inline void QOpenGLFunctions::glAttachShader(GLuint program, GLuint shader) +{ +#if defined(QT_OPENGL_ES_2) + ::glAttachShader(program, shader); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->attachShader(program, shader); +#endif +} + +inline void QOpenGLFunctions::glBindAttribLocation(GLuint program, GLuint index, const char* name) +{ +#if defined(QT_OPENGL_ES_2) + ::glBindAttribLocation(program, index, name); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->bindAttribLocation(program, index, name); +#endif +} + +inline void QOpenGLFunctions::glBindBuffer(GLenum target, GLuint buffer) +{ +#if defined(QT_OPENGL_ES_2) + ::glBindBuffer(target, buffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->bindBuffer(target, buffer); +#endif +} + +inline void QOpenGLFunctions::glBindFramebuffer(GLenum target, GLuint framebuffer) +{ +#if defined(QT_OPENGL_ES_2) + ::glBindFramebuffer(target, framebuffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->bindFramebuffer(target, framebuffer); +#endif +} + +inline void QOpenGLFunctions::glBindRenderbuffer(GLenum target, GLuint renderbuffer) +{ +#if defined(QT_OPENGL_ES_2) + ::glBindRenderbuffer(target, renderbuffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->bindRenderbuffer(target, renderbuffer); +#endif +} + +inline void QOpenGLFunctions::glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) +{ +#if defined(QT_OPENGL_ES_2) + ::glBlendColor(red, green, blue, alpha); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->blendColor(red, green, blue, alpha); +#endif +} + +inline void QOpenGLFunctions::glBlendEquation(GLenum mode) +{ +#if defined(QT_OPENGL_ES_2) + ::glBlendEquation(mode); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->blendEquation(mode); +#endif +} + +inline void QOpenGLFunctions::glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) +{ +#if defined(QT_OPENGL_ES_2) + ::glBlendEquationSeparate(modeRGB, modeAlpha); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->blendEquationSeparate(modeRGB, modeAlpha); +#endif +} + +inline void QOpenGLFunctions::glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +{ +#if defined(QT_OPENGL_ES_2) + ::glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); +#endif +} + +inline void QOpenGLFunctions::glBufferData(GLenum target, qgl_GLsizeiptr size, const void* data, GLenum usage) +{ +#if defined(QT_OPENGL_ES_2) + ::glBufferData(target, size, data, usage); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->bufferData(target, size, data, usage); +#endif +} + +inline void QOpenGLFunctions::glBufferSubData(GLenum target, qgl_GLintptr offset, qgl_GLsizeiptr size, const void* data) +{ +#if defined(QT_OPENGL_ES_2) + ::glBufferSubData(target, offset, size, data); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->bufferSubData(target, offset, size, data); +#endif +} + +inline GLenum QOpenGLFunctions::glCheckFramebufferStatus(GLenum target) +{ +#if defined(QT_OPENGL_ES_2) + return ::glCheckFramebufferStatus(target); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + return d_ptr->checkFramebufferStatus(target); +#endif +} + +inline void QOpenGLFunctions::glClearDepthf(GLclampf depth) +{ +#ifndef QT_OPENGL_ES + ::glClearDepth(depth); +#else + ::glClearDepthf(depth); +#endif +} + +inline void QOpenGLFunctions::glCompileShader(GLuint shader) +{ +#if defined(QT_OPENGL_ES_2) + ::glCompileShader(shader); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->compileShader(shader); +#endif +} + +inline void QOpenGLFunctions::glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) +{ +#if defined(QT_OPENGL_ES_2) + ::glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->compressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); +#endif +} + +inline void QOpenGLFunctions::glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) +{ +#if defined(QT_OPENGL_ES_2) + ::glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); +#endif +} + +inline GLuint QOpenGLFunctions::glCreateProgram() +{ +#if defined(QT_OPENGL_ES_2) + return ::glCreateProgram(); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + return d_ptr->createProgram(); +#endif +} + +inline GLuint QOpenGLFunctions::glCreateShader(GLenum type) +{ +#if defined(QT_OPENGL_ES_2) + return ::glCreateShader(type); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + return d_ptr->createShader(type); +#endif +} + +inline void QOpenGLFunctions::glDeleteBuffers(GLsizei n, const GLuint* buffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glDeleteBuffers(n, buffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->deleteBuffers(n, buffers); +#endif +} + +inline void QOpenGLFunctions::glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glDeleteFramebuffers(n, framebuffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->deleteFramebuffers(n, framebuffers); +#endif +} + +inline void QOpenGLFunctions::glDeleteProgram(GLuint program) +{ +#if defined(QT_OPENGL_ES_2) + ::glDeleteProgram(program); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->deleteProgram(program); +#endif +} + +inline void QOpenGLFunctions::glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glDeleteRenderbuffers(n, renderbuffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->deleteRenderbuffers(n, renderbuffers); +#endif +} + +inline void QOpenGLFunctions::glDeleteShader(GLuint shader) +{ +#if defined(QT_OPENGL_ES_2) + ::glDeleteShader(shader); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->deleteShader(shader); +#endif +} + +inline void QOpenGLFunctions::glDepthRangef(GLclampf zNear, GLclampf zFar) +{ +#ifndef QT_OPENGL_ES + ::glDepthRange(zNear, zFar); +#else + ::glDepthRangef(zNear, zFar); +#endif +} + +inline void QOpenGLFunctions::glDetachShader(GLuint program, GLuint shader) +{ +#if defined(QT_OPENGL_ES_2) + ::glDetachShader(program, shader); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->detachShader(program, shader); +#endif +} + +inline void QOpenGLFunctions::glDisableVertexAttribArray(GLuint index) +{ +#if defined(QT_OPENGL_ES_2) + ::glDisableVertexAttribArray(index); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->disableVertexAttribArray(index); +#endif +} + +inline void QOpenGLFunctions::glEnableVertexAttribArray(GLuint index) +{ +#if defined(QT_OPENGL_ES_2) + ::glEnableVertexAttribArray(index); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->enableVertexAttribArray(index); +#endif +} + +inline void QOpenGLFunctions::glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +{ +#if defined(QT_OPENGL_ES_2) + ::glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->framebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); +#endif +} + +inline void QOpenGLFunctions::glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ +#if defined(QT_OPENGL_ES_2) + ::glFramebufferTexture2D(target, attachment, textarget, texture, level); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->framebufferTexture2D(target, attachment, textarget, texture, level); +#endif +} + +inline void QOpenGLFunctions::glGenBuffers(GLsizei n, GLuint* buffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glGenBuffers(n, buffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->genBuffers(n, buffers); +#endif +} + +inline void QOpenGLFunctions::glGenerateMipmap(GLenum target) +{ +#if defined(QT_OPENGL_ES_2) + ::glGenerateMipmap(target); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->generateMipmap(target); +#endif +} + +inline void QOpenGLFunctions::glGenFramebuffers(GLsizei n, GLuint* framebuffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glGenFramebuffers(n, framebuffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->genFramebuffers(n, framebuffers); +#endif +} + +inline void QOpenGLFunctions::glGenRenderbuffers(GLsizei n, GLuint* renderbuffers) +{ +#if defined(QT_OPENGL_ES_2) + ::glGenRenderbuffers(n, renderbuffers); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->genRenderbuffers(n, renderbuffers); +#endif +} + +inline void QOpenGLFunctions::glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetActiveAttrib(program, index, bufsize, length, size, type, name); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getActiveAttrib(program, index, bufsize, length, size, type, name); +#endif +} + +inline void QOpenGLFunctions::glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetActiveUniform(program, index, bufsize, length, size, type, name); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getActiveUniform(program, index, bufsize, length, size, type, name); +#endif +} + +inline void QOpenGLFunctions::glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetAttachedShaders(program, maxcount, count, shaders); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getAttachedShaders(program, maxcount, count, shaders); +#endif +} + +inline int QOpenGLFunctions::glGetAttribLocation(GLuint program, const char* name) +{ +#if defined(QT_OPENGL_ES_2) + return ::glGetAttribLocation(program, name); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + return d_ptr->getAttribLocation(program, name); +#endif +} + +inline void QOpenGLFunctions::glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetBufferParameteriv(target, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getBufferParameteriv(target, pname, params); +#endif +} + +inline void QOpenGLFunctions::glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetFramebufferAttachmentParameteriv(target, attachment, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getFramebufferAttachmentParameteriv(target, attachment, pname, params); +#endif +} + +inline void QOpenGLFunctions::glGetProgramiv(GLuint program, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetProgramiv(program, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getProgramiv(program, pname, params); +#endif +} + +inline void QOpenGLFunctions::glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetProgramInfoLog(program, bufsize, length, infolog); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getProgramInfoLog(program, bufsize, length, infolog); +#endif +} + +inline void QOpenGLFunctions::glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetRenderbufferParameteriv(target, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getRenderbufferParameteriv(target, pname, params); +#endif +} + +inline void QOpenGLFunctions::glGetShaderiv(GLuint shader, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetShaderiv(shader, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getShaderiv(shader, pname, params); +#endif +} + +inline void QOpenGLFunctions::glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetShaderInfoLog(shader, bufsize, length, infolog); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getShaderInfoLog(shader, bufsize, length, infolog); +#endif +} + +inline void QOpenGLFunctions::glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getShaderPrecisionFormat(shadertype, precisiontype, range, precision); +#endif +} + +inline void QOpenGLFunctions::glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetShaderSource(shader, bufsize, length, source); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getShaderSource(shader, bufsize, length, source); +#endif +} + +inline void QOpenGLFunctions::glGetUniformfv(GLuint program, GLint location, GLfloat* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetUniformfv(program, location, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getUniformfv(program, location, params); +#endif +} + +inline void QOpenGLFunctions::glGetUniformiv(GLuint program, GLint location, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetUniformiv(program, location, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getUniformiv(program, location, params); +#endif +} + +inline int QOpenGLFunctions::glGetUniformLocation(GLuint program, const char* name) +{ +#if defined(QT_OPENGL_ES_2) + return ::glGetUniformLocation(program, name); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + return d_ptr->getUniformLocation(program, name); +#endif +} + +inline void QOpenGLFunctions::glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetVertexAttribfv(index, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getVertexAttribfv(index, pname, params); +#endif +} + +inline void QOpenGLFunctions::glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetVertexAttribiv(index, pname, params); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getVertexAttribiv(index, pname, params); +#endif +} + +inline void QOpenGLFunctions::glGetVertexAttribPointerv(GLuint index, GLenum pname, void** pointer) +{ +#if defined(QT_OPENGL_ES_2) + ::glGetVertexAttribPointerv(index, pname, pointer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->getVertexAttribPointerv(index, pname, pointer); +#endif +} + +inline GLboolean QOpenGLFunctions::glIsBuffer(GLuint buffer) +{ +#if defined(QT_OPENGL_ES_2) + return ::glIsBuffer(buffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + return d_ptr->isBuffer(buffer); +#endif +} + +inline GLboolean QOpenGLFunctions::glIsFramebuffer(GLuint framebuffer) +{ +#if defined(QT_OPENGL_ES_2) + return ::glIsFramebuffer(framebuffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + return d_ptr->isFramebuffer(framebuffer); +#endif +} + +inline GLboolean QOpenGLFunctions::glIsProgram(GLuint program) +{ +#if defined(QT_OPENGL_ES_2) + return ::glIsProgram(program); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + return d_ptr->isProgram(program); +#endif +} + +inline GLboolean QOpenGLFunctions::glIsRenderbuffer(GLuint renderbuffer) +{ +#if defined(QT_OPENGL_ES_2) + return ::glIsRenderbuffer(renderbuffer); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + return d_ptr->isRenderbuffer(renderbuffer); +#endif +} + +inline GLboolean QOpenGLFunctions::glIsShader(GLuint shader) +{ +#if defined(QT_OPENGL_ES_2) + return ::glIsShader(shader); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + return d_ptr->isShader(shader); +#endif +} + +inline void QOpenGLFunctions::glLinkProgram(GLuint program) +{ +#if defined(QT_OPENGL_ES_2) + ::glLinkProgram(program); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->linkProgram(program); +#endif +} + +inline void QOpenGLFunctions::glReleaseShaderCompiler() +{ +#if defined(QT_OPENGL_ES_2) + ::glReleaseShaderCompiler(); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->releaseShaderCompiler(); +#endif +} + +inline void QOpenGLFunctions::glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +{ +#if defined(QT_OPENGL_ES_2) + ::glRenderbufferStorage(target, internalformat, width, height); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->renderbufferStorage(target, internalformat, width, height); +#endif +} + +inline void QOpenGLFunctions::glSampleCoverage(GLclampf value, GLboolean invert) +{ +#if defined(QT_OPENGL_ES_2) + ::glSampleCoverage(value, invert); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->sampleCoverage(value, invert); +#endif +} + +inline void QOpenGLFunctions::glShaderBinary(GLint n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLint length) +{ +#if defined(QT_OPENGL_ES_2) + ::glShaderBinary(n, shaders, binaryformat, binary, length); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->shaderBinary(n, shaders, binaryformat, binary, length); +#endif +} + +inline void QOpenGLFunctions::glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length) +{ +#if defined(QT_OPENGL_ES_2) + ::glShaderSource(shader, count, string, length); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->shaderSource(shader, count, string, length); +#endif +} + +inline void QOpenGLFunctions::glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) +{ +#if defined(QT_OPENGL_ES_2) + ::glStencilFuncSeparate(face, func, ref, mask); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->stencilFuncSeparate(face, func, ref, mask); +#endif +} + +inline void QOpenGLFunctions::glStencilMaskSeparate(GLenum face, GLuint mask) +{ +#if defined(QT_OPENGL_ES_2) + ::glStencilMaskSeparate(face, mask); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->stencilMaskSeparate(face, mask); +#endif +} + +inline void QOpenGLFunctions::glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) +{ +#if defined(QT_OPENGL_ES_2) + ::glStencilOpSeparate(face, fail, zfail, zpass); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->stencilOpSeparate(face, fail, zfail, zpass); +#endif +} + +inline void QOpenGLFunctions::glUniform1f(GLint location, GLfloat x) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform1f(location, x); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform1f(location, x); +#endif +} + +inline void QOpenGLFunctions::glUniform1fv(GLint location, GLsizei count, const GLfloat* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform1fv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform1fv(location, count, v); +#endif +} + +inline void QOpenGLFunctions::glUniform1i(GLint location, GLint x) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform1i(location, x); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform1i(location, x); +#endif +} + +inline void QOpenGLFunctions::glUniform1iv(GLint location, GLsizei count, const GLint* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform1iv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform1iv(location, count, v); +#endif +} + +inline void QOpenGLFunctions::glUniform2f(GLint location, GLfloat x, GLfloat y) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform2f(location, x, y); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform2f(location, x, y); +#endif +} + +inline void QOpenGLFunctions::glUniform2fv(GLint location, GLsizei count, const GLfloat* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform2fv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform2fv(location, count, v); +#endif +} + +inline void QOpenGLFunctions::glUniform2i(GLint location, GLint x, GLint y) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform2i(location, x, y); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform2i(location, x, y); +#endif +} + +inline void QOpenGLFunctions::glUniform2iv(GLint location, GLsizei count, const GLint* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform2iv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform2iv(location, count, v); +#endif +} + +inline void QOpenGLFunctions::glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform3f(location, x, y, z); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform3f(location, x, y, z); +#endif +} + +inline void QOpenGLFunctions::glUniform3fv(GLint location, GLsizei count, const GLfloat* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform3fv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform3fv(location, count, v); +#endif +} + +inline void QOpenGLFunctions::glUniform3i(GLint location, GLint x, GLint y, GLint z) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform3i(location, x, y, z); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform3i(location, x, y, z); +#endif +} + +inline void QOpenGLFunctions::glUniform3iv(GLint location, GLsizei count, const GLint* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform3iv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform3iv(location, count, v); +#endif +} + +inline void QOpenGLFunctions::glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform4f(location, x, y, z, w); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform4f(location, x, y, z, w); +#endif +} + +inline void QOpenGLFunctions::glUniform4fv(GLint location, GLsizei count, const GLfloat* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform4fv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform4fv(location, count, v); +#endif +} + +inline void QOpenGLFunctions::glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform4i(location, x, y, z, w); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform4i(location, x, y, z, w); +#endif +} + +inline void QOpenGLFunctions::glUniform4iv(GLint location, GLsizei count, const GLint* v) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniform4iv(location, count, v); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniform4iv(location, count, v); +#endif +} + +inline void QOpenGLFunctions::glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniformMatrix2fv(location, count, transpose, value); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniformMatrix2fv(location, count, transpose, value); +#endif +} + +inline void QOpenGLFunctions::glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniformMatrix3fv(location, count, transpose, value); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniformMatrix3fv(location, count, transpose, value); +#endif +} + +inline void QOpenGLFunctions::glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) +{ +#if defined(QT_OPENGL_ES_2) + ::glUniformMatrix4fv(location, count, transpose, value); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->uniformMatrix4fv(location, count, transpose, value); +#endif +} + +inline void QOpenGLFunctions::glUseProgram(GLuint program) +{ +#if defined(QT_OPENGL_ES_2) + ::glUseProgram(program); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->useProgram(program); +#endif +} + +inline void QOpenGLFunctions::glValidateProgram(GLuint program) +{ +#if defined(QT_OPENGL_ES_2) + ::glValidateProgram(program); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->validateProgram(program); +#endif +} + +inline void QOpenGLFunctions::glVertexAttrib1f(GLuint indx, GLfloat x) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib1f(indx, x); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->vertexAttrib1f(indx, x); +#endif +} + +inline void QOpenGLFunctions::glVertexAttrib1fv(GLuint indx, const GLfloat* values) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib1fv(indx, values); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->vertexAttrib1fv(indx, values); +#endif +} + +inline void QOpenGLFunctions::glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib2f(indx, x, y); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->vertexAttrib2f(indx, x, y); +#endif +} + +inline void QOpenGLFunctions::glVertexAttrib2fv(GLuint indx, const GLfloat* values) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib2fv(indx, values); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->vertexAttrib2fv(indx, values); +#endif +} + +inline void QOpenGLFunctions::glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib3f(indx, x, y, z); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->vertexAttrib3f(indx, x, y, z); +#endif +} + +inline void QOpenGLFunctions::glVertexAttrib3fv(GLuint indx, const GLfloat* values) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib3fv(indx, values); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->vertexAttrib3fv(indx, values); +#endif +} + +inline void QOpenGLFunctions::glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib4f(indx, x, y, z, w); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->vertexAttrib4f(indx, x, y, z, w); +#endif +} + +inline void QOpenGLFunctions::glVertexAttrib4fv(GLuint indx, const GLfloat* values) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttrib4fv(indx, values); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->vertexAttrib4fv(indx, values); +#endif +} + +inline void QOpenGLFunctions::glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) +{ +#if defined(QT_OPENGL_ES_2) + ::glVertexAttribPointer(indx, size, type, normalized, stride, ptr); +#else + Q_ASSERT(QOpenGLFunctions::isInitialized(d_ptr)); + d_ptr->vertexAttribPointer(indx, size, type, normalized, stride, ptr); +#endif +} + +#ifndef GL_ACTIVE_ATTRIBUTE_MAX_LENGTH +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#endif +#ifndef GL_ACTIVE_ATTRIBUTES +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#endif +#ifndef GL_ACTIVE_TEXTURE +#define GL_ACTIVE_TEXTURE 0x84E0 +#endif +#ifndef GL_ACTIVE_UNIFORM_MAX_LENGTH +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#endif +#ifndef GL_ACTIVE_UNIFORMS +#define GL_ACTIVE_UNIFORMS 0x8B86 +#endif +#ifndef GL_ALIASED_LINE_WIDTH_RANGE +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#endif +#ifndef GL_ALIASED_POINT_SIZE_RANGE +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#endif +#ifndef GL_ALPHA +#define GL_ALPHA 0x1906 +#endif +#ifndef GL_ALPHA_BITS +#define GL_ALPHA_BITS 0x0D55 +#endif +#ifndef GL_ALWAYS +#define GL_ALWAYS 0x0207 +#endif +#ifndef GL_ARRAY_BUFFER +#define GL_ARRAY_BUFFER 0x8892 +#endif +#ifndef GL_ARRAY_BUFFER_BINDING +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#endif +#ifndef GL_ATTACHED_SHADERS +#define GL_ATTACHED_SHADERS 0x8B85 +#endif +#ifndef GL_BACK +#define GL_BACK 0x0405 +#endif +#ifndef GL_BLEND +#define GL_BLEND 0x0BE2 +#endif +#ifndef GL_BLEND_COLOR +#define GL_BLEND_COLOR 0x8005 +#endif +#ifndef GL_BLEND_DST_ALPHA +#define GL_BLEND_DST_ALPHA 0x80CA +#endif +#ifndef GL_BLEND_DST_RGB +#define GL_BLEND_DST_RGB 0x80C8 +#endif +#ifndef GL_BLEND_EQUATION +#define GL_BLEND_EQUATION 0x8009 +#endif +#ifndef GL_BLEND_EQUATION_ALPHA +#define GL_BLEND_EQUATION_ALPHA 0x883D +#endif +#ifndef GL_BLEND_EQUATION_RGB +#define GL_BLEND_EQUATION_RGB 0x8009 +#endif +#ifndef GL_BLEND_SRC_ALPHA +#define GL_BLEND_SRC_ALPHA 0x80CB +#endif +#ifndef GL_BLEND_SRC_RGB +#define GL_BLEND_SRC_RGB 0x80C9 +#endif +#ifndef GL_BLUE_BITS +#define GL_BLUE_BITS 0x0D54 +#endif +#ifndef GL_BOOL +#define GL_BOOL 0x8B56 +#endif +#ifndef GL_BOOL_VEC2 +#define GL_BOOL_VEC2 0x8B57 +#endif +#ifndef GL_BOOL_VEC3 +#define GL_BOOL_VEC3 0x8B58 +#endif +#ifndef GL_BOOL_VEC4 +#define GL_BOOL_VEC4 0x8B59 +#endif +#ifndef GL_BUFFER_SIZE +#define GL_BUFFER_SIZE 0x8764 +#endif +#ifndef GL_BUFFER_USAGE +#define GL_BUFFER_USAGE 0x8765 +#endif +#ifndef GL_BYTE +#define GL_BYTE 0x1400 +#endif +#ifndef GL_CCW +#define GL_CCW 0x0901 +#endif +#ifndef GL_CLAMP_TO_EDGE +#define GL_CLAMP_TO_EDGE 0x812F +#endif +#ifndef GL_COLOR_ATTACHMENT0 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#endif +#ifndef GL_COLOR_BUFFER_BIT +#define GL_COLOR_BUFFER_BIT 0x00004000 +#endif +#ifndef GL_COLOR_CLEAR_VALUE +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#endif +#ifndef GL_COLOR_WRITEMASK +#define GL_COLOR_WRITEMASK 0x0C23 +#endif +#ifndef GL_COMPILE_STATUS +#define GL_COMPILE_STATUS 0x8B81 +#endif +#ifndef GL_COMPRESSED_TEXTURE_FORMATS +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#endif +#ifndef GL_CONSTANT_ALPHA +#define GL_CONSTANT_ALPHA 0x8003 +#endif +#ifndef GL_CONSTANT_COLOR +#define GL_CONSTANT_COLOR 0x8001 +#endif +#ifndef GL_CULL_FACE +#define GL_CULL_FACE 0x0B44 +#endif +#ifndef GL_CULL_FACE_MODE +#define GL_CULL_FACE_MODE 0x0B45 +#endif +#ifndef GL_CURRENT_PROGRAM +#define GL_CURRENT_PROGRAM 0x8B8D +#endif +#ifndef GL_CURRENT_VERTEX_ATTRIB +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#endif +#ifndef GL_CW +#define GL_CW 0x0900 +#endif +#ifndef GL_DECR +#define GL_DECR 0x1E03 +#endif +#ifndef GL_DECR_WRAP +#define GL_DECR_WRAP 0x8508 +#endif +#ifndef GL_DELETE_STATUS +#define GL_DELETE_STATUS 0x8B80 +#endif +#ifndef GL_DEPTH_ATTACHMENT +#define GL_DEPTH_ATTACHMENT 0x8D00 +#endif +#ifndef GL_DEPTH_BITS +#define GL_DEPTH_BITS 0x0D56 +#endif +#ifndef GL_DEPTH_BUFFER_BIT +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#endif +#ifndef GL_DEPTH_CLEAR_VALUE +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#endif +#ifndef GL_DEPTH_COMPONENT +#define GL_DEPTH_COMPONENT 0x1902 +#endif +#ifndef GL_DEPTH_COMPONENT16 +#define GL_DEPTH_COMPONENT16 0x81A5 +#endif +#ifndef GL_DEPTH_FUNC +#define GL_DEPTH_FUNC 0x0B74 +#endif +#ifndef GL_DEPTH_RANGE +#define GL_DEPTH_RANGE 0x0B70 +#endif +#ifndef GL_DEPTH_TEST +#define GL_DEPTH_TEST 0x0B71 +#endif +#ifndef GL_DEPTH_WRITEMASK +#define GL_DEPTH_WRITEMASK 0x0B72 +#endif +#ifndef GL_DITHER +#define GL_DITHER 0x0BD0 +#endif +#ifndef GL_DONT_CARE +#define GL_DONT_CARE 0x1100 +#endif +#ifndef GL_DST_ALPHA +#define GL_DST_ALPHA 0x0304 +#endif +#ifndef GL_DST_COLOR +#define GL_DST_COLOR 0x0306 +#endif +#ifndef GL_DYNAMIC_DRAW +#define GL_DYNAMIC_DRAW 0x88E8 +#endif +#ifndef GL_ELEMENT_ARRAY_BUFFER +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#endif +#ifndef GL_ELEMENT_ARRAY_BUFFER_BINDING +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#endif +#ifndef GL_EQUAL +#define GL_EQUAL 0x0202 +#endif +#ifndef GL_EXTENSIONS +#define GL_EXTENSIONS 0x1F03 +#endif +#ifndef GL_FALSE +#define GL_FALSE 0 +#endif +#ifndef GL_FASTEST +#define GL_FASTEST 0x1101 +#endif +#ifndef GL_FIXED +#define GL_FIXED 0x140C +#endif +#ifndef GL_FLOAT +#define GL_FLOAT 0x1406 +#endif +#ifndef GL_FLOAT_MAT2 +#define GL_FLOAT_MAT2 0x8B5A +#endif +#ifndef GL_FLOAT_MAT3 +#define GL_FLOAT_MAT3 0x8B5B +#endif +#ifndef GL_FLOAT_MAT4 +#define GL_FLOAT_MAT4 0x8B5C +#endif +#ifndef GL_FLOAT_VEC2 +#define GL_FLOAT_VEC2 0x8B50 +#endif +#ifndef GL_FLOAT_VEC3 +#define GL_FLOAT_VEC3 0x8B51 +#endif +#ifndef GL_FLOAT_VEC4 +#define GL_FLOAT_VEC4 0x8B52 +#endif +#ifndef GL_FRAGMENT_SHADER +#define GL_FRAGMENT_SHADER 0x8B30 +#endif +#ifndef GL_FRAMEBUFFER +#define GL_FRAMEBUFFER 0x8D40 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#endif +#ifndef GL_FRAMEBUFFER_BINDING +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#endif +#ifndef GL_FRAMEBUFFER_COMPLETE +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 +#endif +#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#endif +#ifndef GL_FRAMEBUFFER_UNSUPPORTED +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#endif +#ifndef GL_FRONT +#define GL_FRONT 0x0404 +#endif +#ifndef GL_FRONT_AND_BACK +#define GL_FRONT_AND_BACK 0x0408 +#endif +#ifndef GL_FRONT_FACE +#define GL_FRONT_FACE 0x0B46 +#endif +#ifndef GL_FUNC_ADD +#define GL_FUNC_ADD 0x8006 +#endif +#ifndef GL_FUNC_REVERSE_SUBTRACT +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#endif +#ifndef GL_FUNC_SUBTRACT +#define GL_FUNC_SUBTRACT 0x800A +#endif +#ifndef GL_GENERATE_MIPMAP_HINT +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#endif +#ifndef GL_GEQUAL +#define GL_GEQUAL 0x0206 +#endif +#ifndef GL_GREATER +#define GL_GREATER 0x0204 +#endif +#ifndef GL_GREEN_BITS +#define GL_GREEN_BITS 0x0D53 +#endif +#ifndef GL_HIGH_FLOAT +#define GL_HIGH_FLOAT 0x8DF2 +#endif +#ifndef GL_HIGH_INT +#define GL_HIGH_INT 0x8DF5 +#endif +#ifndef GL_IMPLEMENTATION_COLOR_READ_FORMAT +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#endif +#ifndef GL_IMPLEMENTATION_COLOR_READ_TYPE +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#endif +#ifndef GL_INCR +#define GL_INCR 0x1E02 +#endif +#ifndef GL_INCR_WRAP +#define GL_INCR_WRAP 0x8507 +#endif +#ifndef GL_INFO_LOG_LENGTH +#define GL_INFO_LOG_LENGTH 0x8B84 +#endif +#ifndef GL_INT +#define GL_INT 0x1404 +#endif +#ifndef GL_INT_VEC2 +#define GL_INT_VEC2 0x8B53 +#endif +#ifndef GL_INT_VEC3 +#define GL_INT_VEC3 0x8B54 +#endif +#ifndef GL_INT_VEC4 +#define GL_INT_VEC4 0x8B55 +#endif +#ifndef GL_INVALID_ENUM +#define GL_INVALID_ENUM 0x0500 +#endif +#ifndef GL_INVALID_FRAMEBUFFER_OPERATION +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#endif +#ifndef GL_INVALID_OPERATION +#define GL_INVALID_OPERATION 0x0502 +#endif +#ifndef GL_INVALID_VALUE +#define GL_INVALID_VALUE 0x0501 +#endif +#ifndef GL_INVERT +#define GL_INVERT 0x150A +#endif +#ifndef GL_KEEP +#define GL_KEEP 0x1E00 +#endif +#ifndef GL_LEQUAL +#define GL_LEQUAL 0x0203 +#endif +#ifndef GL_LESS +#define GL_LESS 0x0201 +#endif +#ifndef GL_LINEAR +#define GL_LINEAR 0x2601 +#endif +#ifndef GL_LINEAR_MIPMAP_LINEAR +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#endif +#ifndef GL_LINEAR_MIPMAP_NEAREST +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#endif +#ifndef GL_LINE_LOOP +#define GL_LINE_LOOP 0x0002 +#endif +#ifndef GL_LINES +#define GL_LINES 0x0001 +#endif +#ifndef GL_LINE_STRIP +#define GL_LINE_STRIP 0x0003 +#endif +#ifndef GL_LINE_WIDTH +#define GL_LINE_WIDTH 0x0B21 +#endif +#ifndef GL_LINK_STATUS +#define GL_LINK_STATUS 0x8B82 +#endif +#ifndef GL_LOW_FLOAT +#define GL_LOW_FLOAT 0x8DF0 +#endif +#ifndef GL_LOW_INT +#define GL_LOW_INT 0x8DF3 +#endif +#ifndef GL_LUMINANCE +#define GL_LUMINANCE 0x1909 +#endif +#ifndef GL_LUMINANCE_ALPHA +#define GL_LUMINANCE_ALPHA 0x190A +#endif +#ifndef GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#endif +#ifndef GL_MAX_CUBE_MAP_TEXTURE_SIZE +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#endif +#ifndef GL_MAX_FRAGMENT_UNIFORM_VECTORS +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#endif +#ifndef GL_MAX_RENDERBUFFER_SIZE +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#endif +#ifndef GL_MAX_TEXTURE_IMAGE_UNITS +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#endif +#ifndef GL_MAX_TEXTURE_SIZE +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#endif +#ifndef GL_MAX_VARYING_VECTORS +#define GL_MAX_VARYING_VECTORS 0x8DFC +#endif +#ifndef GL_MAX_VERTEX_ATTRIBS +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#endif +#ifndef GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#endif +#ifndef GL_MAX_VERTEX_UNIFORM_VECTORS +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#endif +#ifndef GL_MAX_VIEWPORT_DIMS +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#endif +#ifndef GL_MEDIUM_FLOAT +#define GL_MEDIUM_FLOAT 0x8DF1 +#endif +#ifndef GL_MEDIUM_INT +#define GL_MEDIUM_INT 0x8DF4 +#endif +#ifndef GL_MIRRORED_REPEAT +#define GL_MIRRORED_REPEAT 0x8370 +#endif +#ifndef GL_NEAREST +#define GL_NEAREST 0x2600 +#endif +#ifndef GL_NEAREST_MIPMAP_LINEAR +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#endif +#ifndef GL_NEAREST_MIPMAP_NEAREST +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#endif +#ifndef GL_NEVER +#define GL_NEVER 0x0200 +#endif +#ifndef GL_NICEST +#define GL_NICEST 0x1102 +#endif +#ifndef GL_NO_ERROR +#define GL_NO_ERROR 0 +#endif +#ifndef GL_NONE +#define GL_NONE 0 +#endif +#ifndef GL_NOTEQUAL +#define GL_NOTEQUAL 0x0205 +#endif +#ifndef GL_NUM_COMPRESSED_TEXTURE_FORMATS +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#endif +#ifndef GL_NUM_SHADER_BINARY_FORMATS +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#endif +#ifndef GL_ONE +#define GL_ONE 1 +#endif +#ifndef GL_ONE_MINUS_CONSTANT_ALPHA +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#endif +#ifndef GL_ONE_MINUS_CONSTANT_COLOR +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#endif +#ifndef GL_ONE_MINUS_DST_ALPHA +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#endif +#ifndef GL_ONE_MINUS_DST_COLOR +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#endif +#ifndef GL_ONE_MINUS_SRC_ALPHA +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#endif +#ifndef GL_ONE_MINUS_SRC_COLOR +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#endif +#ifndef GL_OUT_OF_MEMORY +#define GL_OUT_OF_MEMORY 0x0505 +#endif +#ifndef GL_PACK_ALIGNMENT +#define GL_PACK_ALIGNMENT 0x0D05 +#endif +#ifndef GL_POINTS +#define GL_POINTS 0x0000 +#endif +#ifndef GL_POLYGON_OFFSET_FACTOR +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#endif +#ifndef GL_POLYGON_OFFSET_FILL +#define GL_POLYGON_OFFSET_FILL 0x8037 +#endif +#ifndef GL_POLYGON_OFFSET_UNITS +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#endif +#ifndef GL_RED_BITS +#define GL_RED_BITS 0x0D52 +#endif +#ifndef GL_RENDERBUFFER +#define GL_RENDERBUFFER 0x8D41 +#endif +#ifndef GL_RENDERBUFFER_ALPHA_SIZE +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#endif +#ifndef GL_RENDERBUFFER_BINDING +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#endif +#ifndef GL_RENDERBUFFER_BLUE_SIZE +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#endif +#ifndef GL_RENDERBUFFER_DEPTH_SIZE +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#endif +#ifndef GL_RENDERBUFFER_GREEN_SIZE +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#endif +#ifndef GL_RENDERBUFFER_HEIGHT +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#endif +#ifndef GL_RENDERBUFFER_INTERNAL_FORMAT +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#endif +#ifndef GL_RENDERBUFFER_RED_SIZE +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#endif +#ifndef GL_RENDERBUFFER_STENCIL_SIZE +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#endif +#ifndef GL_RENDERBUFFER_WIDTH +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#endif +#ifndef GL_RENDERER +#define GL_RENDERER 0x1F01 +#endif +#ifndef GL_REPEAT +#define GL_REPEAT 0x2901 +#endif +#ifndef GL_REPLACE +#define GL_REPLACE 0x1E01 +#endif +#ifndef GL_RGB +#define GL_RGB 0x1907 +#endif +#ifndef GL_RGB565 +#define GL_RGB565 0x8D62 +#endif +#ifndef GL_RGB5_A1 +#define GL_RGB5_A1 0x8057 +#endif +#ifndef GL_RGBA +#define GL_RGBA 0x1908 +#endif +#ifndef GL_RGBA4 +#define GL_RGBA4 0x8056 +#endif +#ifndef GL_SAMPLE_ALPHA_TO_COVERAGE +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#endif +#ifndef GL_SAMPLE_BUFFERS +#define GL_SAMPLE_BUFFERS 0x80A8 +#endif +#ifndef GL_SAMPLE_COVERAGE +#define GL_SAMPLE_COVERAGE 0x80A0 +#endif +#ifndef GL_SAMPLE_COVERAGE_INVERT +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#endif +#ifndef GL_SAMPLE_COVERAGE_VALUE +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#endif +#ifndef GL_SAMPLER_2D +#define GL_SAMPLER_2D 0x8B5E +#endif +#ifndef GL_SAMPLER_CUBE +#define GL_SAMPLER_CUBE 0x8B60 +#endif +#ifndef GL_SAMPLES +#define GL_SAMPLES 0x80A9 +#endif +#ifndef GL_SCISSOR_BOX +#define GL_SCISSOR_BOX 0x0C10 +#endif +#ifndef GL_SCISSOR_TEST +#define GL_SCISSOR_TEST 0x0C11 +#endif +#ifndef GL_SHADER_BINARY_FORMATS +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#endif +#ifndef GL_SHADER_COMPILER +#define GL_SHADER_COMPILER 0x8DFA +#endif +#ifndef GL_SHADER_SOURCE_LENGTH +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#endif +#ifndef GL_SHADER_TYPE +#define GL_SHADER_TYPE 0x8B4F +#endif +#ifndef GL_SHADING_LANGUAGE_VERSION +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#endif +#ifndef GL_SHORT +#define GL_SHORT 0x1402 +#endif +#ifndef GL_SRC_ALPHA +#define GL_SRC_ALPHA 0x0302 +#endif +#ifndef GL_SRC_ALPHA_SATURATE +#define GL_SRC_ALPHA_SATURATE 0x0308 +#endif +#ifndef GL_SRC_COLOR +#define GL_SRC_COLOR 0x0300 +#endif +#ifndef GL_STATIC_DRAW +#define GL_STATIC_DRAW 0x88E4 +#endif +#ifndef GL_STENCIL_ATTACHMENT +#define GL_STENCIL_ATTACHMENT 0x8D20 +#endif +#ifndef GL_STENCIL_BACK_FAIL +#define GL_STENCIL_BACK_FAIL 0x8801 +#endif +#ifndef GL_STENCIL_BACK_FUNC +#define GL_STENCIL_BACK_FUNC 0x8800 +#endif +#ifndef GL_STENCIL_BACK_PASS_DEPTH_FAIL +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#endif +#ifndef GL_STENCIL_BACK_PASS_DEPTH_PASS +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#endif +#ifndef GL_STENCIL_BACK_REF +#define GL_STENCIL_BACK_REF 0x8CA3 +#endif +#ifndef GL_STENCIL_BACK_VALUE_MASK +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#endif +#ifndef GL_STENCIL_BACK_WRITEMASK +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#endif +#ifndef GL_STENCIL_BITS +#define GL_STENCIL_BITS 0x0D57 +#endif +#ifndef GL_STENCIL_BUFFER_BIT +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#endif +#ifndef GL_STENCIL_CLEAR_VALUE +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#endif +#ifndef GL_STENCIL_FAIL +#define GL_STENCIL_FAIL 0x0B94 +#endif +#ifndef GL_STENCIL_FUNC +#define GL_STENCIL_FUNC 0x0B92 +#endif +#ifndef GL_STENCIL_INDEX +#define GL_STENCIL_INDEX 0x1901 +#endif +#ifndef GL_STENCIL_INDEX8 +#define GL_STENCIL_INDEX8 0x8D48 +#endif +#ifndef GL_STENCIL_PASS_DEPTH_FAIL +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#endif +#ifndef GL_STENCIL_PASS_DEPTH_PASS +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#endif +#ifndef GL_STENCIL_REF +#define GL_STENCIL_REF 0x0B97 +#endif +#ifndef GL_STENCIL_TEST +#define GL_STENCIL_TEST 0x0B90 +#endif +#ifndef GL_STENCIL_VALUE_MASK +#define GL_STENCIL_VALUE_MASK 0x0B93 +#endif +#ifndef GL_STENCIL_WRITEMASK +#define GL_STENCIL_WRITEMASK 0x0B98 +#endif +#ifndef GL_STREAM_DRAW +#define GL_STREAM_DRAW 0x88E0 +#endif +#ifndef GL_SUBPIXEL_BITS +#define GL_SUBPIXEL_BITS 0x0D50 +#endif +#ifndef GL_TEXTURE0 +#define GL_TEXTURE0 0x84C0 +#endif +#ifndef GL_TEXTURE +#define GL_TEXTURE 0x1702 +#endif +#ifndef GL_TEXTURE10 +#define GL_TEXTURE10 0x84CA +#endif +#ifndef GL_TEXTURE1 +#define GL_TEXTURE1 0x84C1 +#endif +#ifndef GL_TEXTURE11 +#define GL_TEXTURE11 0x84CB +#endif +#ifndef GL_TEXTURE12 +#define GL_TEXTURE12 0x84CC +#endif +#ifndef GL_TEXTURE13 +#define GL_TEXTURE13 0x84CD +#endif +#ifndef GL_TEXTURE14 +#define GL_TEXTURE14 0x84CE +#endif +#ifndef GL_TEXTURE15 +#define GL_TEXTURE15 0x84CF +#endif +#ifndef GL_TEXTURE16 +#define GL_TEXTURE16 0x84D0 +#endif +#ifndef GL_TEXTURE17 +#define GL_TEXTURE17 0x84D1 +#endif +#ifndef GL_TEXTURE18 +#define GL_TEXTURE18 0x84D2 +#endif +#ifndef GL_TEXTURE19 +#define GL_TEXTURE19 0x84D3 +#endif +#ifndef GL_TEXTURE20 +#define GL_TEXTURE20 0x84D4 +#endif +#ifndef GL_TEXTURE2 +#define GL_TEXTURE2 0x84C2 +#endif +#ifndef GL_TEXTURE21 +#define GL_TEXTURE21 0x84D5 +#endif +#ifndef GL_TEXTURE22 +#define GL_TEXTURE22 0x84D6 +#endif +#ifndef GL_TEXTURE23 +#define GL_TEXTURE23 0x84D7 +#endif +#ifndef GL_TEXTURE24 +#define GL_TEXTURE24 0x84D8 +#endif +#ifndef GL_TEXTURE25 +#define GL_TEXTURE25 0x84D9 +#endif +#ifndef GL_TEXTURE26 +#define GL_TEXTURE26 0x84DA +#endif +#ifndef GL_TEXTURE27 +#define GL_TEXTURE27 0x84DB +#endif +#ifndef GL_TEXTURE28 +#define GL_TEXTURE28 0x84DC +#endif +#ifndef GL_TEXTURE29 +#define GL_TEXTURE29 0x84DD +#endif +#ifndef GL_TEXTURE_2D +#define GL_TEXTURE_2D 0x0DE1 +#endif +#ifndef GL_TEXTURE30 +#define GL_TEXTURE30 0x84DE +#endif +#ifndef GL_TEXTURE3 +#define GL_TEXTURE3 0x84C3 +#endif +#ifndef GL_TEXTURE31 +#define GL_TEXTURE31 0x84DF +#endif +#ifndef GL_TEXTURE4 +#define GL_TEXTURE4 0x84C4 +#endif +#ifndef GL_TEXTURE5 +#define GL_TEXTURE5 0x84C5 +#endif +#ifndef GL_TEXTURE6 +#define GL_TEXTURE6 0x84C6 +#endif +#ifndef GL_TEXTURE7 +#define GL_TEXTURE7 0x84C7 +#endif +#ifndef GL_TEXTURE8 +#define GL_TEXTURE8 0x84C8 +#endif +#ifndef GL_TEXTURE9 +#define GL_TEXTURE9 0x84C9 +#endif +#ifndef GL_TEXTURE_BINDING_2D +#define GL_TEXTURE_BINDING_2D 0x8069 +#endif +#ifndef GL_TEXTURE_BINDING_CUBE_MAP +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#endif +#ifndef GL_TEXTURE_CUBE_MAP +#define GL_TEXTURE_CUBE_MAP 0x8513 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_X +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_Y +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#endif +#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_X +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_Y +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_Z +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#endif +#ifndef GL_TEXTURE_MAG_FILTER +#define GL_TEXTURE_MAG_FILTER 0x2800 +#endif +#ifndef GL_TEXTURE_MIN_FILTER +#define GL_TEXTURE_MIN_FILTER 0x2801 +#endif +#ifndef GL_TEXTURE_WRAP_S +#define GL_TEXTURE_WRAP_S 0x2802 +#endif +#ifndef GL_TEXTURE_WRAP_T +#define GL_TEXTURE_WRAP_T 0x2803 +#endif +#ifndef GL_TRIANGLE_FAN +#define GL_TRIANGLE_FAN 0x0006 +#endif +#ifndef GL_TRIANGLES +#define GL_TRIANGLES 0x0004 +#endif +#ifndef GL_TRIANGLE_STRIP +#define GL_TRIANGLE_STRIP 0x0005 +#endif +#ifndef GL_TRUE +#define GL_TRUE 1 +#endif +#ifndef GL_UNPACK_ALIGNMENT +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#endif +#ifndef GL_UNSIGNED_BYTE +#define GL_UNSIGNED_BYTE 0x1401 +#endif +#ifndef GL_UNSIGNED_INT +#define GL_UNSIGNED_INT 0x1405 +#endif +#ifndef GL_UNSIGNED_SHORT +#define GL_UNSIGNED_SHORT 0x1403 +#endif +#ifndef GL_UNSIGNED_SHORT_4_4_4_4 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#endif +#ifndef GL_UNSIGNED_SHORT_5_5_5_1 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#endif +#ifndef GL_UNSIGNED_SHORT_5_6_5 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#endif +#ifndef GL_VALIDATE_STATUS +#define GL_VALIDATE_STATUS 0x8B83 +#endif +#ifndef GL_VENDOR +#define GL_VENDOR 0x1F00 +#endif +#ifndef GL_VERSION +#define GL_VERSION 0x1F02 +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_ENABLED +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_NORMALIZED +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_POINTER +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_SIZE +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_STRIDE +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#endif +#ifndef GL_VERTEX_ATTRIB_ARRAY_TYPE +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#endif +#ifndef GL_VERTEX_SHADER +#define GL_VERTEX_SHADER 0x8B31 +#endif +#ifndef GL_VIEWPORT +#define GL_VIEWPORT 0x0BA2 +#endif +#ifndef GL_ZERO +#define GL_ZERO 0 +#endif + +#endif diff --git a/src/threed/arrays/arrays.pri b/src/threed/arrays/arrays.pri new file mode 100644 index 000000000..f9eb72d94 --- /dev/null +++ b/src/threed/arrays/arrays.pri @@ -0,0 +1,28 @@ +INCLUDEPATH += $$PWD +VPATH += $$PWD +HEADERS += \ + qglattributedescription.h \ + qglattributeset.h \ + qglattributevalue.h \ + qglindexbuffer.h \ + qglvertexbundle.h \ + qarray.h \ + qcolor4ub.h \ + qcustomdataarray.h \ + qvector2darray.h \ + qvector3darray.h \ + qvector4darray.h +SOURCES += \ + qglattributedescription.cpp \ + qglattributeset.cpp \ + qglattributevalue.cpp \ + qglindexbuffer.cpp \ + qglvertexbundle.cpp \ + qarray.cpp \ + qcolor4ub.cpp \ + qcustomdataarray.cpp \ + qvector2darray.cpp \ + qvector3darray.cpp \ + qvector4darray.cpp +PRIVATE_HEADERS += \ + qglvertexbundle_p.h diff --git a/src/threed/arrays/qarray.cpp b/src/threed/arrays/qarray.cpp new file mode 100644 index 000000000..52bb1eb2a --- /dev/null +++ b/src/threed/arrays/qarray.cpp @@ -0,0 +1,1022 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qarray.h" +#include <limits.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QArray + \brief The QArray class is a template class that provides a dynamic array of simple types. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays + + QArray is similar to QVector except that it has much less overhead + when constructing large arrays by appending individual elements + one by one. + + QArray instances have a preallocated data area for quickly + building small arrays on the stack without malloc overhead. + Once the array grows beyond the preallocated size, it is copied + to the heap. The size of the preallocated area, which defaults to 8, + can be specified with the second template parameter: + + \code + QArray<QVector3D, 32> array; + \endcode + + QArray uses implicit sharing and copy-on-write semantics to support + passing large arrays around an application with little overhead. + + QArray is heavily optimized for copy-on-write and the case of + constructing an array by calling append(). It has a slight + performance penalty for random access using the non-const + version of operator[](). +*/ + +/*! + \fn QArray::QArray() + + Constructs an empty array. + + \sa reserve() +*/ + +/*! + \fn QArray::QArray(int size, const T &value) + + Constructs an array of \a size elements, all initialized + to \a value. + + \sa fill() +*/ + +/*! + \fn QArray::QArray(int size) + + Constructs an array of \a size elements, all initialized + to their default-constructed values. +*/ + +/*! + \fn QArray::QArray(const T *values, int size) + + Constructs an array of \a size elements, initialized + from \a values. +*/ + +/*! + \fn QArray::QArray(const QArray<T, PreallocSize> &other) + + Constructs a copy of \a other. + + \sa operator=() +*/ + +/*! + \fn QArray::~QArray() + + Destroys the array. +*/ + +/*! + \fn QArray<T, PreallocSize> &QArray::operator=(const QArray<T, PreallocSize> &other) + + Assigns \a other to this array and returns a reference + to this array. +*/ + +/*! + \fn int QArray::size() const + + Returns the number of elements in this array. + + \sa resize(), capacity(), isEmpty() +*/ + +/*! + \fn int QArray::count() const + \overload + + Same as size(), provided for convenience. +*/ + +/*! + \fn int QArray::capacity() const + + Returns the number of elements that can be stored in this + array before reallocation. + + \sa reserve(), size() +*/ + +/*! + \fn bool QArray::isEmpty() const + + Returns true if this array is empty; false otherwise. + + \sa size(), clear() +*/ + +/*! + \fn bool QArray::isDetached() const + \internal + + Returns true if this array has definitely been detached from all + other shared copies of the data; false otherwise. + + It is possible for this function to return false if the + array was previously shared but no longer is. It is thus + an indication that a detach() will probably be required. + + This function can be used to determine if functions that + write to this array such as append(), replace(), + and data(), will need to make a copy. + + Raw data arrays that are created with fromRawData() are + never detached. + + \sa detach() +*/ + +/*! + \fn void QArray::detach() + \internal + + Detaches this array from all other shared copies of the data. + + \sa isDetached() +*/ + +/*! + \fn void QArray::clear() + + Clears all elements from this array and sets the size to zero. + + This function will deallocate any memory that is used on the heap + to store the array's elements. To reuse the same memory + as before, call resize() with an argument of zero. + + \sa resize(), isEmpty() +*/ + +/*! + \fn const T &QArray::at(int index) const + + Returns the item at position \a index in the array. + + \a index must be a valid index position in the array (i.e., 0 <= \a + index < size()). + + \sa operator[](), constData(), value() +*/ + +/*! + \fn T &QArray::operator[](int index) + + Returns the item at position \a index as a modifiable reference. + + \a index must be a valid index position in the vector (i.e., 0 <= \a index + < size()). + + Note that using non-const operators can cause QArray + to do a deep copy. + + \sa at(), value() +*/ + +/*! + \fn const T &QArray::operator[](int index) const + + \overload + + Same as at(\a index). +*/ + +/*! + \fn T QArray::value(int index) const + + Returns the value at position \a index in the vector. + + If the \a index is out of bounds, the function returns + a default-constructed value. If you are certain that + \a index is within bounds, you can use at() instead, + which is slightly faster. + + \sa at(), operator[]() +*/ + +/*! + \fn T QArray::value(int index, const T &defaultValue) const + \overload + + If the \a index is out of bounds, the function returns + \a defaultValue. +*/ + +/*! + \fn T *QArray::extend(int size) + + Extends this array by \a size elements and returns a pointer + to the storage, which is not initialized. The pointer is only + valid until the array is reallocated or destroyed. + + The append() or resize() functions are recommended if T is a + complex type, with extend() only used for simple types. + Because the storage is not initialized, the caller should use + the in-place new operator to set elements: + + \code + QArray<QRegExp> array; + QRegExp *space = array.extend(1); + new (space) QRegExp(QLatin1String("exp")); + \endcode + + \sa append(), resize() +*/ + +/*! + \fn void QArray::append(const T &value) + + Appends \a value to this array. + + \sa prepend(), insert() +*/ + +/*! + \fn void QArray::append(const T &value1, const T &value2) + + \overload + + Appends \a value1 and \a value2 to this array. +*/ + +/*! + \fn void QArray::append(const T &value1, const T &value2, const T &value3) + + \overload + + Appends \a value1, \a value2, and \a value3 to this array. +*/ + +/*! + \fn void QArray::append(const T &value1, const T &value2, const T &value3, const T &value4) + + \overload + + Appends \a value1, \a value2, \a value3, and \a value4 to this array. +*/ + +/*! + \fn void QArray::append(const T *values, int count) + + Appends the \a count elements of \a values to this array. +*/ + +/*! + \fn void QArray::append(const QArray<T, PreallocSize> &other) + + Appends the elements of \a other to this array. +*/ + +/*! + \fn void QArray::prepend(const T &value) + + Prepends \a value to this array. + + \sa append(), insert() +*/ + +/*! + \fn void QArray::insert(int index, const T &value) + + Inserts \a value at position \a index in this array. + If \a index is 0, then \a value is prepended to the array. + If \a index is size(), then \a value is appended to the array. + + \sa append(), prepend() +*/ + +/*! + \fn void QArray::insert(int index, int count, const T &value) + \overload + + Inserts \a count copies of \a value at position \a index + in this array. +*/ + +/*! + \fn QArray::iterator QArray::insert(iterator before, int count, const T &value) + + Inserts \a count copies of \a value in front of the item + pointed to by the iterator \a before. Returns an iterator + pointing at the first of the inserted items. +*/ + +/*! + \fn QArray::iterator QArray::insert(iterator before, const T &value) + \overload + + Inserts \a value in front of the item pointed to by the + iterator \a before. Returns an iterator pointing at the + inserted item. +*/ + +/*! + \fn void QArray::replace(int index, const T &value) + + Replaces the element at \a index with \a value. + + \sa operator[](), remove() +*/ + +/*! + \fn void QArray::replace(int index, const T *values, int count) + \overload + + Replaces the \a count elements of this array with the + contents of \a values, starting at \a index. + + If (\a index + \a count) is larger than the current size of this + array, the array will be extended to that size. + + \sa append() +*/ + +/*! + \fn void QArray::remove(int index) + + \overload + + Removes the element at position \a index in this array. +*/ + +/*! + \fn void QArray::remove(int index, int count) + + Removes the \a count elements starting at position \a index + in this array. If \a index or \a count is out of range, + the set of removed elements will be truncated to those that + are in range. +*/ + +/*! + \fn QArray::iterator QArray::erase(iterator begin, iterator end) + \overload + + Removes all the items from \a begin up to (but not including) \a + end. Returns an iterator to the same item that \a end referred to + before the call. +*/ + +/*! + \fn QArray::iterator QArray::erase(iterator pos) + + Removes the item pointed to by the iterator \a pos from the + vector, and returns an iterator to the next item in the vector + (which may be end()). + + \sa insert(), remove() +*/ + +/*! + \fn void QArray::removeFirst() + + Removes the first element from this array. Does nothing if + the array is empty. + + \sa remove(), removeLast() +*/ + +/*! + \fn void QArray::removeLast() + + Removes the last element from this array. Does nothing if + the array is empty. + + \sa remove(), removeFirst() +*/ + +/*! + \fn int QArray::indexOf(const T &value, int from) const + + Returns the index position of the first occurrence of + \a value in the array, searching forward from index + position \a from. Returns -1 if no item matched. + + If \a from is negative, then it indicates an index position + relative to the end of the array, -1 being the last index + position. + + This function requires the value type T to have an implementation + of \c operator==(). + + \sa lastIndexOf(), contains() +*/ + +/*! + \fn int QArray::lastIndexOf(const T &value, int from) const + + Returns the index position of the last occurrence of + \a value in the array, searching backward from index + position \a from. Returns -1 if no item matched. + + If \a from is negative, then it indicates an index position + relative to the end of the array, -1 being the last index + position. The default for \a from is -1. + + This function requires the value type T to have an implementation + of \c operator==(). + + \sa indexOf(), contains() +*/ + +/*! + \fn bool QArray::contains(const T &value) const + + Returns true if the array contains an occurrence of \a value; + false otherwise. + + This function requires the value type T to have an implementation + of \c operator==(). + + \sa indexOf(), count() +*/ + +/*! + \fn int QArray::count(const T &value) const + + Returns the number of occurrences of \a value in the array. + + This function requires the value type T to have an implementation + of \c operator==(). + + \sa contains(), indexOf() +*/ + +/*! + \fn void QArray::resize(int size) + + Sets the size of the array to \a size. If \a size is greater + than the current size, elements are added to the end and are + initialized to a default-constructed value. If \a size is less + than the current size, elements are removed from the end. + + \sa size(), reserve(), squeeze() +*/ + +/*! + \fn void QArray::reserve(int size) + + Increases the capacity of this array to reserve space for + at least \a size elements. If the capacity is already larger + than \a size, this function does nothing; in particular, it does + not remove elements from the array like resize() does. + + This function can be useful when you know how roughly many elements + will be appended ahead of time. Reserving the space once can avoid + unnecessary realloc operations later. + + \sa capacity(), resize(), squeeze() +*/ + +/*! + \fn void QArray::squeeze() + + Releases any memory not required to store the array's elements + by reducing its capacity() to size(). + + This function is intended for reclaiming memory in an + array that is being used over and over with different contents. + As elements are added to an array, it will be constantly + expanded in size. This function can realloc the array + to a smaller size to reclaim unused memory. + + \sa reserve(), capacity() +*/ + +/*! + \fn QArray<T, PreallocSize> &QArray::fill(const T &value, int size) + + Assigns \a value to all items in the array. If \a size is + different from -1 (the default), the array is resized to + \a size beforehand. Returns a reference to the array. + + \sa resize() +*/ + +/*! + \fn void QArray::reverse() + + Reverses the order of this array in place. + + \sa reversed() +*/ + +/*! + \fn QArray<T, PreallocSize> QArray::reversed() const + + Returns a copy of this array with elements in the reverse order. + + \sa reverse() +*/ + +/*! + \fn QArray<T, PreallocSize> QArray::mid(int index, int length) const + + Returns an array containing the \a length elements of + this array, starting at \a index. If \a length is less + than zero, or extends further than the end of the array, then all + elements extending from \a index to the end of the array will be + included in the return value. + + \sa left(), right() +*/ + +/*! + \fn QArray<T, PreallocSize> QArray::left(int length) const; + + Returns an array containing the first \a length + elements of this array. If \a length is less than zero, + or greater than size(), then all elements in this array will + be included in the return value. + + \sa mid(), right() +*/ + +/*! + \fn QArray<T, PreallocSize> QArray::right(int length) const; + + Returns an array containing the last \a length + elements of this array. If \a length is less than zero, + or greater than size(), then all elements in this array + will be included in the return value. + + \sa mid(), left() +*/ + +/*! + \fn T *QArray::data() + + Returns a pointer to the data stored in the array. The pointer + can be used to access and modify the items in the array. + + The pointer remains valid as long as the array isn't + reallocated. + + This function is mostly useful to pass an array to a function + that accepts a plain C++ array. It may make a deep copy of the + array's elements if the array is implicitly shared. + + \sa constData(), operator[]() +*/ + +/*! + \fn const T *QArray::data() const + + \overload +*/ + +/*! + \fn const T *QArray::constData() const + + Returns a const pointer to the data stored in the array. + The pointer can be used to access the items in the array. + The pointer remains valid as long as the array isn't + reallocated. + + This function is mostly useful to pass an array to a function + that accepts a plain C++ array. + + \sa data(), operator[]() +*/ + +/*! + \fn QArray<T, PreallocSize> QArray::fromRawData(const T *data, int size) + + Returns an array consisting of the \a size elements from \a data. + + This function takes a reference to \a data, but does not copy + the elements until the array is modified. The memory at \a data + must remain valid until the returned array is destroyed + or modified. + + Use append() instead of fromRawData() to force a copy to be made + of the elements at \a data when the array is created: + + \code + // Makes a copy of the data immediately. + QArray<float> array; + array.append(data, size); + + // Does not make a copy of the data until the array is modified. + QArray<float> array; + array = QArray<float>::fromRawData(data, size); + \endcode + + \sa fromWritableRawData(), append() +*/ + +/*! + \fn QArray<T, PreallocSize> QArray::fromWritableRawData(T *data, int size) + + Returns an array consisting of the \a size elements from \a data. + + This function takes a reference to \a data, but does not copy + the elements until the array is reallocated to a larger size. + The memory at \a data must remain valid until the returned + array is destroyed or reallocated. + + The elements of \a data will be modified in-place. This differs + from fromRawData() which will make a copy of the elements + of \a data when the array is modified. + + If the returned array is resized to less than \a size, + then a copy will not be made, and append() can be used to + append new items up to \a size. Further calls to append() + after \a size will force the array to be reallocated. + + If the returned array is resized to more than \a size, + then a copy of the data will be made and further modifications + will not affect the elements at \a data. + + \sa fromRawData() +*/ + +/*! + \fn bool QArray::operator==(const QArray<T, PreallocSize> &other) const + + Returns true if \a other is equal to this array; otherwise + returns false. + + Two arrays are considered equal if they contain the same values + in the same order. + + This function requires the value type to have an implementation + of \c operator==(). + + \sa operator!=() +*/ + +/*! + \fn bool QArray::operator!=(const QArray<T, PreallocSize> &other) const + + Returns true if \a other is not equal to this array; otherwise + returns false. + + Two arrays are considered equal if they contain the same values + in the same order. + + This function requires the value type to have an implementation + of \c operator==(). + + \sa operator==() +*/ + +/*! + \fn QArray<T, PreallocSize> &QArray::operator+=(const T &value) + + \overload + + Appends \a value to this array and returns a reference to + this array. + + \sa operator<<(), append() +*/ + +/*! + \fn QArray<T, PreallocSize> &QArray::operator+=(const QArray<T, PreallocSize> &other) + + Appends the elements of the \a other array to this array + and returns a reference to this array. + + \sa operator<<(), append() +*/ + +/*! + \fn QArray<T, PreallocSize> &QArray::operator<<(const T &value) + + \overload + + Appends \a value to this array and returns a reference to + this array. + + \sa operator+=(), append() +*/ + +/*! + \fn QArray<T, PreallocSize> &QArray::operator<<(const QArray<T, PreallocSize> &other) + + Appends the elements of the \a other array to this array + and returns a reference to this array. + + \sa operator+=(), append() +*/ + +/*! + \typedef QArray::iterator + + The QArray::iterator typedef provides an STL-style non-const + iterator for QArray. The iterator is simply a typedef + for "T *" (pointer to T). + + \sa QArray::begin(), QArray::const_iterator +*/ + +/*! + \typedef QArray::const_iterator + + The QArray::iterator typedef provides an STL-style const + iterator for QArray. The iterator is simply a typedef + for "const T *" (pointer to const T). + + \sa QArray::constBegin(), QArray::iterator +*/ + +/*! + \typedef QArray::Iterator + + Qt-style synonym for QArray::iterator. +*/ + +/*! + \typedef QArray::ConstIterator + + Qt-style synonym for QArray::const_iterator. +*/ + +/*! + \typedef QArray::const_pointer + + Typedef for const T *. Provided for STL compatibility. +*/ + +/*! + \typedef QArray::const_reference + + Typedef for T &. Provided for STL compatibility. +*/ + +/*! + \typedef QArray::difference_type + + Typedef for ptrdiff_t. Provided for STL compatibility. +*/ + +/*! + \typedef QArray::pointer + + Typedef for T *. Provided for STL compatibility. +*/ + +/*! + \typedef QArray::reference + + Typedef for T &. Provided for STL compatibility. +*/ + +/*! + \typedef QArray::size_type + + Typedef for int. Provided for STL compatibility. +*/ + +/*! + \typedef QArray::value_type + + Typedef for T. Provided for STL compatibility. +*/ + +/*! + \fn QArray::iterator QArray::begin() + + Returns an STL-style iterator pointing to the first item + in the array. + + \sa end(), constBegin(), QArray::iterator +*/ + +/*! + \fn QArray::const_iterator QArray::begin() const + \overload +*/ + +/*! + \fn QArray::const_iterator QArray::constBegin() const + + Returns a const STL-style iterator pointing to the first item + in the array. + + \sa constEnd(), begin(), QArray::const_iterator +*/ + +/*! + \fn QArray::iterator QArray::end() + + Returns an STL-style iterator pointing to the imaginary item + after the last item in the array. + + \sa begin(), constEnd(), QArray::iterator +*/ + +/*! + \fn QArray::const_iterator QArray::end() const + \overload +*/ + +/*! + \fn QArray::const_iterator QArray::constEnd() const + + Returns a const STL-style iterator pointing to the imaginary item + after the last item in the array. + + \sa constBegin(), end(), QArray::const_iterator +*/ + +/*! + \fn T &QArray::first() + + Returns a reference to the first item in the array. This + function assumes that the array isn't empty. + + \sa last(), isEmpty() +*/ + +/*! + \fn const T &QArray::first() const + \overload +*/ + +/*! + \fn T &QArray::last() + + Returns a reference to the last item in the array. This function + assumes that the array isn't empty. + + \sa first(), isEmpty() +*/ + +/*! + \fn const T &QArray::last() const + \overload +*/ + +/*! + \fn bool QArray::startsWith(const T &value) const + + Returns true if this array is not empty and its first + item is equal to \a value; otherwise returns false. + + \sa isEmpty(), first() +*/ + +/*! + \fn bool QArray::endsWith(const T &value) const + + Returns true if this array is not empty and its last + item is equal to \a value; otherwise returns false. + + \sa isEmpty(), last() +*/ + +/*! + \fn void QArray::push_back(const T &value) + + This function is provided for STL compatibility. It is equivalent + to append(\a value). +*/ + +/*! + \fn void QArray::push_front(const T &value) + + This function is provided for STL compatibility. It is equivalent + to prepend(\a value). +*/ + +/*! + \fn void QArray::pop_front() + + This function is provided for STL compatibility. It is equivalent + to removeFirst(). +*/ + +/*! + \fn void QArray::pop_back() + + This function is provided for STL compatibility. It is equivalent + to removeLast(). +*/ + +/*! + \fn QArray::reference QArray::front() + + This function is provided for STL compatibility. It is equivalent + to first(). +*/ + +/*! + \fn QArray::const_reference QArray::front() const + \overload +*/ + +/*! + \fn QArray::reference QArray::back() + + This function is provided for STL compatibility. It is equivalent + to last(). +*/ + +/*! + \fn QArray::const_reference QArray::back() const + \overload +*/ + +/*! + \fn bool QArray::empty() const + + This function is provided for STL compatibility. It is equivalent + to isEmpty(), returning true if the array is empty; otherwise + returns false. +*/ + +#ifndef QT_NO_DATASTREAM + +/*! + \fn QDataStream& operator<<(QDataStream& stream, const QArray<T, PreallocSize>& array) + \relates QArray + + Writes \a array to the given \a stream and returns a reference + to the \a stream. +*/ + +/*! + \fn QDataStream& operator>>(QDataStream& stream, QArray<T, PreallocSize>& array) + \relates QArray + + Reads \a array from the given \a stream and returns a reference + to the \a stream. +*/ + +#endif + +int qArrayAllocMore(int alloc, int extra, int sizeOfT) +{ + if (alloc == 0 && extra == 0) + return 0; + const int page = 1 << 12; + int nalloc; + alloc += extra; + alloc *= sizeOfT; + // don't do anything if the loop will overflow signed int. + if (alloc >= INT_MAX/2) + return INT_MAX / sizeOfT; + nalloc = (alloc < page) ? 64 : page; + while (nalloc < alloc) { + if (nalloc <= 0) + return INT_MAX / sizeOfT; + nalloc *= 2; + } + return nalloc / sizeOfT; +} + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qarray.h b/src/threed/arrays/qarray.h new file mode 100644 index 000000000..c00ec20e5 --- /dev/null +++ b/src/threed/arrays/qarray.h @@ -0,0 +1,1209 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAY_H +#define QARRAY_H + +#include <QtCore/qglobal.h> +#include <QtCore/qatomic.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qdebug.h> +#include <string.h> +#include <new> + +#include "qt3dglobal.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +#if defined(Q_DECL_ALIGN) && defined(Q_ALIGNOF) + +#if defined(Q_CC_GNU) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) + typedef char __attribute__((__may_alias__)) QArrayAlignedChar; +#else + typedef char QArrayAlignedChar; +#endif + +template <typename T, int PreallocSize, size_t AlignT> +struct QArrayAlignedPrealloc; +template <typename T, int PreallocSize> +struct QArrayAlignedPrealloc<T, PreallocSize, 1> +{ + QArrayAlignedChar Q_DECL_ALIGN(1) data[sizeof(T) * PreallocSize]; +}; +template <typename T, int PreallocSize> +struct QArrayAlignedPrealloc<T, PreallocSize, 2> +{ + QArrayAlignedChar Q_DECL_ALIGN(2) data[sizeof(T) * PreallocSize]; +}; +template <typename T, int PreallocSize> +struct QArrayAlignedPrealloc<T, PreallocSize, 4> +{ + QArrayAlignedChar Q_DECL_ALIGN(4) data[sizeof(T) * PreallocSize]; +}; +template <typename T, int PreallocSize> +struct QArrayAlignedPrealloc<T, PreallocSize, 8> +{ + QArrayAlignedChar Q_DECL_ALIGN(8) data[sizeof(T) * PreallocSize]; +}; +template <typename T, int PreallocSize> +struct QArrayAlignedPrealloc<T, PreallocSize, 16> +{ + QArrayAlignedChar Q_DECL_ALIGN(16) data[sizeof(T) * PreallocSize]; +}; +template <typename T, int PreallocSize> +struct QArrayAlignedPrealloc<T, PreallocSize, 32> +{ + QArrayAlignedChar Q_DECL_ALIGN(32) data[sizeof(T) * PreallocSize]; +}; +template <typename T, int PreallocSize> +struct QArrayAlignedPrealloc<T, PreallocSize, 64> +{ + QArrayAlignedChar Q_DECL_ALIGN(64) data[sizeof(T) * PreallocSize]; +}; +template <typename T, int PreallocSize> +struct QArrayAlignedPrealloc<T, PreallocSize, 128> +{ + QArrayAlignedChar Q_DECL_ALIGN(128) data[sizeof(T) * PreallocSize]; +}; + +#else + +template <typename T, int PreallocSize, size_t AlignT> +union QArrayAlignedPrealloc +{ + char data[sizeof(T) * PreallocSize]; + qint64 q_for_alignment_1; + double q_for_alignment_2; +}; + +#endif + +template <typename T, int PreallocSize> +class QArrayData +{ +public: +#if defined(Q_ALIGNOF) + QArrayAlignedPrealloc<T, PreallocSize, Q_ALIGNOF(T)> m_prealloc; +#else + QArrayAlignedPrealloc<T, PreallocSize, sizeof(T)> m_prealloc; +#endif + + inline T *prealloc() + { + return reinterpret_cast<T *>(m_prealloc.data); + } + + inline bool isPrealloc(const T *start) const + { + return start == reinterpret_cast<const T *>(m_prealloc.data); + } +}; + +template <typename T> +class QArrayData<T, 0> +{ +public: + + inline T *prealloc() { return 0; } + + inline bool isPrealloc(const T *start) const + { + Q_UNUSED(start); + return false; + } +}; + +template <typename T, int PreallocSize = 8> +class QArray : private QArrayData<T, PreallocSize> +{ +public: + QArray(); + explicit QArray(int size); + QArray(int size, const T &value); + QArray(const T *values, int size); + QArray(const QArray<T, PreallocSize> &other); + ~QArray(); + + typedef T *iterator; + typedef const T *const_iterator; + + QArray<T, PreallocSize> &operator= + (const QArray<T, PreallocSize> &other); + + int size() const; + int count() const; + int capacity() const; + + bool isEmpty() const; + + bool isDetached() const; + void detach(); + + void clear(); + + const T &at(int index) const; + const T &operator[](int index) const; + T &operator[](int index); + + T value(int index) const; + T value(int index, const T &defaultValue) const; + + T *extend(int size); + + void append(const T &value); + void append(const T &value1, const T &value2); + void append(const T &value1, const T &value2, const T &value3); + void append(const T &value1, const T &value2, const T &value3, const T &value4); + void append(const T *values, int count); + void append(const QArray<T, PreallocSize> &other); + + void prepend(const T &value); + + void insert(int index, const T &value); + void insert(int index, int count, const T &value); + iterator insert(iterator before, int count, const T &value); + iterator insert(iterator before, const T &value); + + void replace(int index, const T &value); + void replace(int index, const T *values, int count); + + void remove(int index); + void remove(int index, int count); + void removeFirst() { remove(0); } + void removeLast() { remove(size() - 1); } + + iterator erase(iterator begin, iterator end); + iterator erase(iterator pos); + + int indexOf(const T &value, int from = 0) const; + int lastIndexOf(const T &value, int from = -1) const; + bool contains(const T &value) const; + int count(const T &value) const; + + void resize(int size); + void reserve(int size); + void squeeze(); + + QArray<T, PreallocSize> &fill(const T &value, int size = -1); + + void reverse(); + QArray<T, PreallocSize> reversed() const; + + QArray<T, PreallocSize> mid(int index, int length = -1) const; + QArray<T, PreallocSize> left(int length) const; + QArray<T, PreallocSize> right(int length) const; + + T *data(); + const T *data() const; + const T *constData() const; + + static QArray<T, PreallocSize> fromRawData(const T *data, int size); + static QArray<T, PreallocSize> fromWritableRawData(T *data, int size); + + bool operator==(const QArray<T, PreallocSize> &other) const; + bool operator!=(const QArray<T, PreallocSize> &other) const; + + QArray<T, PreallocSize> &operator+=(const T &value); + QArray<T, PreallocSize> &operator+=(const QArray<T, PreallocSize> &other); + QArray<T, PreallocSize> &operator<<(const T &value); + QArray<T, PreallocSize> &operator<<(const QArray<T, PreallocSize> &other); + + typedef iterator Iterator; + typedef const_iterator ConstIterator; + typedef T value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type &reference; + typedef const value_type &const_reference; + typedef ptrdiff_t difference_type; + typedef int size_type; + + inline iterator begin() { return data(); } + inline const_iterator begin() const { return constData(); } + inline const_iterator constBegin() const { return constData(); } + inline iterator end() { return data() + size(); } + inline const_iterator end() const { return constData() + size(); } + inline const_iterator constEnd() const { return constData() + size(); } + + inline T &first() { Q_ASSERT(!isEmpty()); return *begin(); } + inline const T &first() const { Q_ASSERT(!isEmpty()); return *begin(); } + inline T &last() { Q_ASSERT(!isEmpty()); return *(end()-1); } + inline const T &last() const { Q_ASSERT(!isEmpty()); return *(end()-1); } + inline bool startsWith(const T &t) const { return !isEmpty() && first() == t; } + inline bool endsWith(const T &t) const { return !isEmpty() && last() == t; } + + inline void push_back(const T &value) { append(value); } + inline void push_front(const T &value) { prepend(value); } + inline void pop_back() { Q_ASSERT(!isEmpty()); removeLast(); } + inline void pop_front() { Q_ASSERT(!isEmpty()); removeFirst(); } + inline bool empty() const { return isEmpty(); } + inline reference front() { return first(); } + inline const_reference front() const { return first(); } + inline reference back() { return last(); } + inline const_reference back() const { return last(); } + +private: + struct Data + { + QBasicAtomicInt ref; + int capacity; + T array[1]; + }; + + // Invariants: + // 1. If the data is not shared, then the usual condition is + // for m_limit >= m_end. + // 2. If the data is shared, then m_limit == m_start. + // This triggers the range check in append() to call grow(), + // which will copy-on-write. It also triggers the detach + // check in data() and operator[] to cause a copy-on-write. + // 3. If the data is not shared, but previously was, then + // m_limit == m_start. This will trigger grow() or + // detach(), which may then notice that it doesn't have to + // copy-on-write. In that case, m_limit is set back + // to m_start + m_data->capacity. + // 4. If m_data is null, then m_start is either the same as + // m_prealloc, or it points at raw data (const or non-const). + // 5. If the array contains const raw data, then m_limit will + // be set to m_start to force copy-on-write. + T *m_start; + T *m_end; + mutable T *m_limit; + Data *m_data; + + inline void initPrealloc() + { + m_end = m_start = QArrayData<T, PreallocSize>::prealloc(); + m_limit = m_start + PreallocSize; + } + + QArray(const T *data, int size, bool isWritable); + + void free(T *data, int count); + void release(); + void copyReplace(T *dst, const T *src, int count); + Data *copyData(const T *src, int size, int capacity); + void reallocate(int capacity); + void detach_helper(); + void assign(const QArray<T, PreallocSize> &other); + void grow(int needed); + void setSize(int size); +}; + +int Q_QT3D_EXPORT qArrayAllocMore(int alloc, int extra, int sizeOfT); + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::free(T *data, int count) +{ + while (count-- > 0) { + data->~T(); + ++data; + } +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::release() +{ + if (m_data) { + if (!m_data->ref.deref()) { + if (QTypeInfo<T>::isComplex) + free(m_start, m_end - m_start); + qFree(m_data); + } + } else if (this->isPrealloc(m_start)) { + if (QTypeInfo<T>::isComplex) + free(m_start, m_end - m_start); + } +} + +// Copy values to initialized memory, replacing previous values. +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::copyReplace(T *dst, const T *src, int count) +{ + if (!QTypeInfo<T>::isStatic) { + ::memmove(dst, src, count * sizeof(T)); + } else { + while (count-- > 0) + *dst++ = *src++; + } +} + +// Make a copy of m_data, while remaining exception-safe. +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE Q_TYPENAME QArray<T, PreallocSize>::Data *QArray<T, PreallocSize>::copyData(const T *src, int size, int capacity) +{ + Data *data = reinterpret_cast<Data *> + (qMalloc(sizeof(Data) + sizeof(T) * (capacity - 1))); + Q_CHECK_PTR(data); + data->ref = 1; + data->capacity = capacity; + T *dst = data->array; + int copied = 0; + QT_TRY { + while (copied < size) { + new (dst) T(*src++); + ++dst; + ++copied; + } + } QT_CATCH(...) { + while (copied-- > 0) + (--dst)->~T(); + qFree(data); + QT_RETHROW; + } + return data; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::reallocate(int capacity) +{ + int size = m_end - m_start; + if (!QTypeInfo<T>::isStatic) { + Data *data = reinterpret_cast<Data *> + (qRealloc(m_data, sizeof(Data) + sizeof(T) * (capacity - 1))); + Q_CHECK_PTR(data); + data->capacity = capacity; + m_data = data; + } else { + Data *data = copyData(m_data->array, size, capacity); + free(m_data->array, size); + qFree(m_data); + m_data = data; + } + m_start = m_data->array; + m_end = m_start + size; + m_limit = m_start + capacity; +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE void QArray<T, PreallocSize>::detach_helper() +{ + // If the reference count is 1, then the array may have been + // copied and then the copy released. So just reset the limit. + if (m_data && m_data->ref == 1) { + m_limit = m_start + m_data->capacity; + return; + } + + // Allocate a new block on the heap and copy the data across. + int size = m_end - m_start; + int capacity = qArrayAllocMore(size, 0, sizeof(T)); + m_data = copyData(m_start, size, capacity); + + // Update the start/end/append pointers for faster updates. + m_start = m_data->array; + m_end = m_start + size; + m_limit = m_start + capacity; +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE void QArray<T, PreallocSize>::assign(const QArray<T, PreallocSize> &other) +{ + if (other.m_data) { + m_start = other.m_start; + m_end = other.m_end; + m_data = other.m_data; + m_data->ref.ref(); + + // We set the append limit of both objects to m_start, which forces + // the next append() or data() in either object to copy-on-write. + other.m_limit = m_limit = m_start; + } else if (other.isPrealloc(other.m_start)) { + // Make a deep copy of preallocated data. + initPrealloc(); + m_data = 0; + append(other.constData(), other.size()); + } else { + // Shallow copy of raw data. + m_start = other.m_start; + m_end = other.m_end; + m_limit = other.m_limit; + m_data = 0; + } +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE void QArray<T, PreallocSize>::grow(int needed) +{ + int size = m_end - m_start; + int capacity = qArrayAllocMore(size, needed, sizeof(T)); + if (!m_data || m_data->ref != 1) { + // Copy preallocated, raw, or shared data and expand the capacity. + Data *data = copyData(m_start, size, capacity); + if (this->isPrealloc(m_start)) + free(m_start, size); + if (m_data) + m_data->ref.deref(); + m_data = data; + m_start = data->array; + m_end = m_start + size; + m_limit = m_start + capacity; + } else if ((size + needed) > m_data->capacity) { + // Reallocate to create more capacity. + reallocate(capacity); + } else { + // We have enough capacity - just fix the append limit. + // This can happen when an array is copied and then the + // copy is removed. + m_limit = m_start + m_data->capacity; + } +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE void QArray<T, PreallocSize>::setSize(int size) +{ + if (size <= PreallocSize) { + initPrealloc(); + m_data = 0; + } else { + int capacity = qArrayAllocMore(size, 0, sizeof(T)); + Data *data = reinterpret_cast<Data *> + (qMalloc(sizeof(Data) + sizeof(T) * (capacity - 1))); + Q_CHECK_PTR(data); + m_data = data; + m_data->ref = 1; + m_data->capacity = capacity; + m_start = m_data->array; + m_end = m_start; + m_limit = m_start + capacity; + } +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize>::QArray() +{ + initPrealloc(); + m_data = 0; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize>::QArray(int size) +{ + setSize(size); + while (size-- > 0) + new (m_end++) T(); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize>::QArray(int size, const T &value) +{ + setSize(size); + while (size-- > 0) + new (m_end++) T(value); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize>::QArray(const T *values, int size) +{ + setSize(size); + while (size-- > 0) + new (m_end++) T(*values++); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize>::QArray(const QArray<T, PreallocSize> &other) +{ + assign(other); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize>::QArray(const T *data, int size, bool isWritable) +{ + // Constructing a raw data array. + m_start = const_cast<T *>(data); + m_end = m_start + size; + if (isWritable) + m_limit = m_end; + else + m_limit = m_start; + m_data = 0; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize>::~QArray() +{ + release(); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize> &QArray<T, PreallocSize>::operator=(const QArray<T, PreallocSize> &other) +{ + if (this == &other) + return *this; + if (other.m_data && m_data == other.m_data) + return *this; + release(); + assign(other); + return *this; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE int QArray<T, PreallocSize>::size() const +{ + return m_end - m_start; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE int QArray<T, PreallocSize>::count() const +{ + return m_end - m_start; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE int QArray<T, PreallocSize>::capacity() const +{ + if (m_data) + return m_data->capacity; + else if (this->isPrealloc(m_start)) + return PreallocSize; + else + return m_end - m_start; // raw data, m_limit == m_start +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE bool QArray<T, PreallocSize>::isEmpty() const +{ + return m_start == m_end; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE bool QArray<T, PreallocSize>::isDetached() const +{ + // If m_limit is the same as m_start, then the array + // is either shared or contains raw data. + return m_limit != m_start; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::detach() +{ + if (m_limit == m_start) + detach_helper(); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::clear() +{ + release(); + initPrealloc(); + m_data = 0; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE const T &QArray<T, PreallocSize>::operator[](int index) const +{ + Q_ASSERT_X(index >= 0 && index < size(), + "QArray<T>::at", "index out of range"); + return m_start[index]; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE const T &QArray<T, PreallocSize>::at(int index) const +{ + Q_ASSERT_X(index >= 0 && index < size(), + "QArray<T>::operator[]", "index out of range"); + return m_start[index]; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE T &QArray<T, PreallocSize>::operator[](int index) +{ + Q_ASSERT_X(index >= 0 && index < size(), + "QArray<T>::operator[]", "index out of range"); + return data()[index]; +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE T QArray<T, PreallocSize>::value(int index) const +{ + if (index >= 0 && index < size()) + return m_start[index]; + else + return T(); +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE T QArray<T, PreallocSize>::value(int index, const T &defaultValue) const +{ + if (index >= 0 && index < size()) + return m_start[index]; + else + return defaultValue; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE T *QArray<T, PreallocSize>::extend(int size) +{ + Q_ASSERT(size > 0); + if ((m_end + size) >= m_limit) + grow(size); + T *end = m_end; + m_end += size; // Note: new elements are not initialized. + return end; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::append(const T &value) +{ + if (m_end >= m_limit) + grow(1); + new (m_end) T(value); + ++m_end; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::append(const T &value1, const T &value2) +{ + if ((m_end + 1) >= m_limit) + grow(2); + new (m_end) T(value1); + ++m_end; // Increment one at a time in case an exception is thrown. + new (m_end) T(value2); + ++m_end; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::append(const T &value1, const T &value2, const T &value3) +{ + if ((m_end + 2) >= m_limit) + grow(3); + new (m_end) T(value1); + ++m_end; + new (m_end) T(value2); + ++m_end; + new (m_end) T(value3); + ++m_end; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::append(const T &value1, const T &value2, const T &value3, const T &value4) +{ + if ((m_end + 3) >= m_limit) + grow(4); + new (m_end) T(value1); + ++m_end; + new (m_end) T(value2); + ++m_end; + new (m_end) T(value3); + ++m_end; + new (m_end) T(value4); + ++m_end; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::append(const T *values, int count) +{ + if (count <= 0) + return; + if (!m_start || (m_end + count) > m_limit) + grow(count); + while (count-- > 0) { + new (m_end) T(*values++); + ++m_end; + } +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE void QArray<T, PreallocSize>::append(const QArray<T, PreallocSize> &other) +{ + if (isEmpty()) { + *this = other; + } else { + if (&other == this || (m_data && other.m_data == m_data)) + grow(size()); // Appending to ourselves: make some room. + append(other.constData(), other.size()); + } +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::prepend(const T &value) +{ + insert(begin(), 1, value); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::insert(int index, const T &value) +{ + Q_ASSERT_X(index >= 0 && index <= size(), + "QArray<T>::insert", "index out of range"); + insert(begin() + index, 1, value); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::insert(int index, int count, const T &value) +{ + Q_ASSERT_X(index >= 0 && index <= size(), + "QArray<T>::insert", "index out of range"); + insert(begin() + index, count, value); +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE Q_TYPENAME QArray<T, PreallocSize>::iterator QArray<T, PreallocSize>::insert(iterator before, int count, const T &value) +{ + // Check the parameters. + int size = this->size(); + int offset = int(before - m_start); + Q_ASSERT_X(offset >= 0 && offset <= size, + "QArray<T>::insert", "iterator offset is out of range"); + Q_ASSERT(count >= 0); + if (count <= 0) + return m_start + offset; + + // Reserve extra space and then copy-on-write. + reserve(size + count); + detach(); + + // Move items up to make room, and replace at the insert point. + if (QTypeInfo<T>::isStatic) { + int newcount = count; + while (newcount > 0) { + new (m_end++) T(); + --newcount; + } + int posn = size; + while (posn > offset) { + --posn; + m_start[posn + count] = m_start[posn]; + } + while (count > 0) { + --count; + m_start[offset + count] = value; + } + } else { + ::memmove(m_start + offset + count, m_start + offset, + (size - offset) * sizeof(T)); + m_end += count; + while (count > 0) { + --count; + new (m_start + offset + count) T(value); + } + } + + // Return the new iterator at the insert position. + return m_start + offset; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE Q_TYPENAME QArray<T, PreallocSize>::iterator QArray<T, PreallocSize>::insert(iterator before, const T &value) +{ + return insert(before, 1, value); +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE void QArray<T, PreallocSize>::replace(int index, const T &value) +{ + Q_ASSERT_X(index >= 0 && index < size(), + "QArray<T>::replace", "index out of range"); + data()[index] = value; +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE void QArray<T, PreallocSize>::replace(int index, const T *values, int count) +{ + if (index < 0 || count <= 0) + return; + int replaceSize = index + count; + if (replaceSize > size()) + resize(replaceSize); + copyReplace(data() + index, values, count); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::remove(int index) +{ + remove(index, 1); +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE void QArray<T, PreallocSize>::remove(int index, int count) +{ + // Truncate the range to be removed. + int currentSize = size(); + if (index < 0) { + count += index; + index = 0; + } + if (count > 0 && (index + count) > currentSize) + count = currentSize - index; + if (count <= 0) + return; + + // Perform the removal. + if (index == 0 && count >= currentSize) { + clear(); + return; + } + T *start = data(); + copyReplace(start + index, start + index + count, + (currentSize - (index + count))); + resize(currentSize - count); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE Q_TYPENAME QArray<T, PreallocSize>::iterator QArray<T, PreallocSize>::erase(iterator begin, iterator end) +{ + int index = begin - m_start; + remove(index, end - begin); + return m_start + index; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE Q_TYPENAME QArray<T, PreallocSize>::iterator QArray<T, PreallocSize>::erase(iterator pos) +{ + int index = pos - m_start; + remove(index, 1); + return m_start + index; +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE int QArray<T, PreallocSize>::indexOf(const T &value, int from) const +{ + if (from < 0) + from = qMax(from + size(), 0); + const T *ptr = m_start + from; + while (ptr < m_end) { + if (*ptr == value) + return ptr - m_start; + ++ptr; + } + return -1; +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE int QArray<T, PreallocSize>::lastIndexOf(const T &value, int from) const +{ + int size = count(); + if (from < 0) + from += size; + else if (from >= size) + from = size - 1; + if (from >= 0) { + const T *ptr = m_start + from; + while (ptr >= m_start) { + if (*ptr == value) + return ptr - m_start; + --ptr; + } + } + return -1; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE bool QArray<T, PreallocSize>::contains(const T &value) const +{ + const T *ptr = m_start; + while (ptr < m_end) { + if (*ptr == value) + return true; + ++ptr; + } + return false; +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE int QArray<T, PreallocSize>::count(const T &value) const +{ + const T *ptr = m_start; + int count = 0; + while (ptr < m_end) { + if (*ptr == value) + ++count; + ++ptr; + } + return count; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::resize(int size) +{ + if (size < 0) + return; + int currentSize = count(); + if (size < currentSize) { + T *start = data(); // Force copy on write if necessary. + if (QTypeInfo<T>::isComplex) + free(start + size, currentSize - size); + m_end = start + size; + } else if (size > currentSize) { + grow(size - currentSize); + while (currentSize++ < size) { + new (m_end) T(); + ++m_end; + } + } +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE void QArray<T, PreallocSize>::reserve(int size) +{ + int cap = capacity(); + if (size > cap) + grow(size - this->size()); +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE QArray<T, PreallocSize> &QArray<T, PreallocSize>::fill(const T &value, int size) +{ + if (size >= 0) + resize(size); + T *ptr = m_start; + while (ptr < m_end) + *ptr++ = value; + return *this; +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE void QArray<T, PreallocSize>::squeeze() +{ + int size = count(); + if (size <= 0) { + clear(); + } else if (size < capacity() && m_data) { + reallocate(size); + } +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE void QArray<T, PreallocSize>::reverse() +{ + if (count() > 0) { + T *src = m_start; + T *dst = m_end - 1; + while (src < dst) + qSwap(*(dst--), *(src++)); + } +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE QArray<T, PreallocSize> QArray<T, PreallocSize>::reversed() const +{ + QArray<T, PreallocSize> result; + int count = size(); + if (count > 0) { + result.extend(count); + const T *src = m_start; + T *dst = result.m_end - 1; + if (!QTypeInfo<T>::isComplex) { + while (src != m_end) + *(dst--) = *(src++); + } else { + while (src != m_end) + new (dst--) T(*src++); + } + } + return result; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize> QArray<T, PreallocSize>::mid(int index, int length) const +{ + int count = size(); + Q_ASSERT(index >= 0 && index <= count); + if (length < 0 || (index + length) > count) + length = count - index; + if (index == 0 && length == count) + return *this; + QArray<T, PreallocSize> result; + result.append(constData() + index, length); + return result; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize> QArray<T, PreallocSize>::left(int length) const +{ + return mid(0, length); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize> QArray<T, PreallocSize>::right(int length) const +{ + int size = count(); + if (length < 0 || length >= size) + length = size; + return mid(size - length, length); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE T *QArray<T, PreallocSize>::data() +{ + detach(); + return m_start; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE const T *QArray<T, PreallocSize>::data() const +{ + return m_start; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE const T *QArray<T, PreallocSize>::constData() const +{ + return m_start; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize> QArray<T, PreallocSize>::fromRawData(const T *data, int size) +{ + Q_ASSERT(size >= 0); + return QArray<T, PreallocSize>(data, size, false); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize> QArray<T, PreallocSize>::fromWritableRawData(T *data, int size) +{ + Q_ASSERT(size >= 0); + return QArray<T, PreallocSize>(data, size, true); +} + +template <typename T, int PreallocSize> +Q_OUTOFLINE_TEMPLATE bool QArray<T, PreallocSize>::operator== + (const QArray<T, PreallocSize> &other) const +{ + if (this == &other) + return true; + const T *thisData = constData(); + const T *otherData = other.constData(); + if (thisData == otherData) + return true; + int thisCount = count(); + int otherCount = other.count(); + if (thisCount == 0 && otherCount == 0) + return true; + if (thisCount != otherCount) + return false; + for (int index = 0; index < thisCount; ++index, ++thisData, ++otherData) + if (*thisData != *otherData) + return false; + return true; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE bool QArray<T, PreallocSize>::operator!= + (const QArray<T, PreallocSize> &other) const +{ + return !(*this == other); +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize> &QArray<T, PreallocSize>::operator+=(const T &value) +{ + append(value); + return *this; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize> &QArray<T, PreallocSize>::operator+=(const QArray<T, PreallocSize> &other) +{ + append(other); + return *this; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize> &QArray<T, PreallocSize>::operator<<(const T &value) +{ + append(value); + return *this; +} + +template <typename T, int PreallocSize> +Q_INLINE_TEMPLATE QArray<T, PreallocSize> &QArray<T, PreallocSize>::operator<<(const QArray<T, PreallocSize> &other) +{ + append(other); + return *this; +} + +#ifndef QT_NO_DATASTREAM + +template <typename T, int PreallocSize> +QDataStream& operator<<(QDataStream& stream, const QArray<T, PreallocSize>& array) +{ + int size = array.size(); + stream << quint32(size); + for (int index = 0; index < size; ++index) + stream << array.at(index); + return stream; +} + +template <typename T, int PreallocSize> +QDataStream& operator>>(QDataStream& stream, QArray<T, PreallocSize>& array) +{ + array.clear(); + quint32 size; + stream >> size; + array.reserve(size); + for (int index = 0; index < int(size); ++index) { + T t; + stream >> t; + array.append(t); + if (stream.atEnd()) + break; + } + return stream; +} + +#endif + +#ifndef QT_NO_DEBUG_STREAM + +template <typename T, int PreallocSize> +QDebug operator<<(QDebug dbg, const QArray<T, PreallocSize>& array) +{ + dbg.nospace() << "QArray(\n"; + int size = array.size(); + for (int index = 0; index < size; ++index) { + dbg << " " << index << ": " << array.at(index) << "\n"; + } + dbg << ")\n"; + return dbg.space(); +} + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qcolor4ub.cpp b/src/threed/arrays/qcolor4ub.cpp new file mode 100644 index 000000000..9a26e6ed4 --- /dev/null +++ b/src/threed/arrays/qcolor4ub.cpp @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcolor4ub.h" +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QColor4ub + \brief The QColor4ub class represents a color by four unsigned byte components. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays + + OpenGL applications commonly use four unsigned byte values to compactly + represent a color value. QColor4ub provides a convenience + class for manipulating such compact color values. + + An alternative is to represent a color value as four floating-point + values between 0.0 and 1.0. The QVector4D class can be used for + that purpose if required. +*/ + +/*! + \fn QColor4ub::QColor4ub() + + Constructs a four-byte default color value of (0, 0, 0, 255). +*/ + +/*! + \fn QColor4ub::QColor4ub(int red, int green, int blue, int alpha) + + Constructs a four-byte color value with the components \a red, + \a green, \a blue, and \a alpha. +*/ + +/*! + \fn QColor4ub::QColor4ub(const QColor& color) + + Constructs a four-byte color value from \a color. +*/ + +/*! + \fn QColor4ub::QColor4ub(Qt::GlobalColor color) + + Constructs a four-byte color value from \a color. +*/ + +/*! + \fn QColor4ub::QColor4ub(QRgb rgba) + + Constructs a four-byte color value from the red, green, blue, and + alpha components of \a rgba. +*/ + +/*! + \fn QColor4ub& QColor4ub::operator=(const QColor& color) + + Copies the red, green, blue, and alpha components of \a color + into this object. +*/ + +/*! + \fn QColor4ub& QColor4ub::operator=(Qt::GlobalColor color) + + Copies the red, green, blue, and alpha components of the + specified global \a color name into this object. +*/ + +/*! + \fn int QColor4ub::red() const + + Returns the red component of this color, between 0 and 255. + + \sa green(), blue(), alpha(), setRed(), redF() +*/ + +/*! + \fn int QColor4ub::green() const + + Returns the green component of this color, between 0 and 255. + + \sa red(), blue(), alpha(), setGreen(), greenF() +*/ + +/*! + \fn int QColor4ub::blue() const + + Returns the blue component of this color, between 0 and 255. + + \sa red(), green(), alpha(), setBlue(), blueF() +*/ + +/*! + \fn int QColor4ub::alpha() const + + Returns the alpha component of this color, between 0 and 255. + + \sa red(), green(), blue(), setAlpha(), alphaF() +*/ + +/*! + \fn void QColor4ub::setRed(int value) + + Sets the red component of this color to \a value, between 0 and 255. + + \sa setGreen(), setBlue(), setAlpha(), red(), setRedF() +*/ + +/*! + \fn void QColor4ub::setGreen(int value) + + Sets the green component of this color to \a value, between 0 and 255. + + \sa setRed(), setBlue(), setAlpha(), green(), setGreenF() +*/ + +/*! + \fn void QColor4ub::setBlue(int value) + + Sets the blue component of this color to \a value, between 0 and 255. + + \sa setRed(), setGreen(), setAlpha(), blue(), setBlueF() +*/ + +/*! + \fn void QColor4ub::setAlpha(int value) + + Sets the alpha component of this color to \a value, between 0 and 255. + + \sa setRed(), setGreen(), setBlue(), alpha(), setAlphaF() +*/ + +/*! + \fn qreal QColor4ub::redF() const { return m_red / 255.0f; } + + Returns the red component of this color as a floating-point + value between 0 and 1. + + \sa greenF(), blueF(), alphaF(), setRedF(), red() +*/ + +/*! + \fn qreal QColor4ub::greenF() const { return m_green / 255.0f; } + + Returns the green component of this color as a floating-point + value between 0 and 1. + + \sa redF(), blueF(), alphaF(), setGreenF(), green() +*/ + +/*! + \fn qreal QColor4ub::blueF() const { return m_blue / 255.0f; } + + Returns the blue component of this color as a floating-point + value between 0 and 1. + + \sa redF(), greenF(), alphaF(), setBlueF(), blue() +*/ + +/*! + \fn qreal QColor4ub::alphaF() const { return m_alpha / 255.0f; } + + Returns the alpha component of this color as a floating-point + value between 0 and 1. + + \sa redF(), greenF(), blueF(), setAlphaF(), alpha() +*/ + +/*! + \fn void QColor4ub::setRedF(qreal value) + + Sets the red component of this color to a floating-point \a value, + between 0 and 1. + + \sa setGreenF(), setBlueF(), setAlphaF(), redF(), setRed() +*/ + +/*! + \fn void QColor4ub::setGreenF(qreal value) + + Sets the green component of this color to a floating-point \a value, + between 0 and 1. + + \sa setRedF(), setBlueF(), setAlphaF(), greenF(), setGreen() +*/ + +/*! + \fn void QColor4ub::setBlueF(qreal value) + + Sets the blue component of this color to a floating-point \a value, + between 0 and 1. + + \sa setRedF(), setGreenF(), setAlphaF(), blueF(), setBlue() +*/ + +/*! + \fn void QColor4ub::setAlphaF(qreal value) + + Sets the alpha component of this color to a floating-point \a value, + between 0 and 1. + + \sa setRedF(), setGreenF(), setBlueF(), alphaF(), setAlpha() +*/ + +/*! + \fn void QColor4ub::setRgb(int red, int green, int blue, int alpha) + + Sets the components of this color to \a red, \a green, \a blue, + and \a alpha. Each component is between 0 and 255. + + \sa setRgbF(), fromRgb() +*/ + +/*! + \fn void QColor4ub::setRgbF(qreal red, qreal green, qreal blue, qreal alpha) + + Sets the components of this color to \a red, \a green, \a blue, + and \a alpha. Each component is a floating-point value between 0 and 1. + + \sa setRgb(), fromRgbF() +*/ + +/*! + \fn QColor4ub QColor4ub::fromRgb(int red, int green, int blue, int alpha) + + Returns a QColor4ub with the components \a red, \a green, \a blue, + and \a alpha. Each component is between 0 and 255. + + \sa fromRgbF(), setRgb() +*/ + +/*! + \fn QColor4ub QColor4ub::fromRgbF(qreal red, qreal green, qreal blue, qreal alpha) + + Returns a QColor4ub with the components \a red, \a green, \a blue, + and \a alpha. Each component is a floating-point value between 0 and 1. + + \sa fromRgb(), setRgbF() +*/ + +/*! + \fn QColor4ub QColor4ub::fromRaw(const uchar *data) + + Returns a QColor4ub with components from the first four elements + in \a data. The \a data parameter must contain at least four + elements and not be null. +*/ + +/*! + \fn QColor QColor4ub::toColor() const + + Returns this color as a QColor. +*/ + +/*! + \fn bool QColor4ub::operator==(const QColor4ub& other) const + + Returns true if this color is the same as \a other; false otherwise. +*/ + +/*! + \fn bool QColor4ub::operator!=(const QColor4ub& other) const + + Returns true if this color is not the same as \a other; false otherwise. +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QColor4ub &color) +{ + dbg.nospace() << "QColor4ub(" + << color.redF() << ", " << color.greenF() << ", " + << color.blueF() << ", " << color.alphaF() << ')'; + return dbg.space(); +} + +#endif + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qcolor4ub.h b/src/threed/arrays/qcolor4ub.h new file mode 100644 index 000000000..3ec27ba20 --- /dev/null +++ b/src/threed/arrays/qcolor4ub.h @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOLOR4UB_H +#define QCOLOR4UB_H + +#include "qt3dglobal.h" +#include <QtGui/qcolor.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QColor4ub +{ +public: + QColor4ub(); + QColor4ub(int red, int green, int blue, int alpha = 255); + QColor4ub(const QColor& color); + QColor4ub(Qt::GlobalColor color); + QColor4ub(QRgb rgba); + + QColor4ub& operator=(const QColor& color); + QColor4ub& operator=(Qt::GlobalColor color); + + int red() const { return m_red; } + int green() const { return m_green; } + int blue() const { return m_blue; } + int alpha() const { return m_alpha; } + + void setRed(int value) { m_red = uchar(value); } + void setGreen(int value) { m_green = uchar(value); } + void setBlue(int value) { m_blue = uchar(value); } + void setAlpha(int value) { m_alpha = uchar(value); } + + qreal redF() const { return m_red / 255.0f; } + qreal greenF() const { return m_green / 255.0f; } + qreal blueF() const { return m_blue / 255.0f; } + qreal alphaF() const { return m_alpha / 255.0f; } + + void setRedF(qreal value) { m_red = uchar(qRound(value * 255.0f)); } + void setGreenF(qreal value) { m_green = uchar(qRound(value * 255.0f)); } + void setBlueF(qreal value) { m_blue = uchar(qRound(value * 255.0f)); } + void setAlphaF(qreal value) { m_alpha = uchar(qRound(value * 255.0f)); } + + void setRgb(int red, int green, int blue, int alpha = 255); + void setRgbF(qreal red, qreal green, qreal blue, qreal alpha = 1.0f); + + static QColor4ub fromRgb(int red, int green, int blue, int alpha = 255); + static QColor4ub fromRgbF + (qreal red, qreal green, qreal blue, qreal alpha = 1.0f); + static QColor4ub fromRaw(const uchar *data); + + QColor toColor() const; + + bool operator==(const QColor4ub& other) const; + bool operator!=(const QColor4ub& other) const; + +private: + QColor4ub(const uchar *data); + + uchar m_red; + uchar m_green; + uchar m_blue; + uchar m_alpha; +}; + +inline QColor4ub::QColor4ub() : m_red(0), m_green(0), m_blue(0), m_alpha(255) {} + +inline QColor4ub::QColor4ub(int red, int green, int blue, int alpha) + : m_red(uchar(red)), m_green(uchar(green)), + m_blue(uchar(blue)), m_alpha(uchar(alpha)) {} + +inline QColor4ub::QColor4ub(const QColor& color) + : m_red(uchar(color.red())), m_green(uchar(color.green())), + m_blue(uchar(color.blue())), m_alpha(uchar(color.alpha())) {} + +inline QColor4ub::QColor4ub(Qt::GlobalColor color) +{ + QColor c(color); + m_red = uchar(c.red()); + m_green = uchar(c.green()); + m_blue = uchar(c.blue()); + m_alpha = uchar(c.alpha()); +} + +inline QColor4ub::QColor4ub(QRgb rgba) + : m_red(uchar(qRed(rgba))), m_green(uchar(qGreen(rgba))), + m_blue(uchar(qBlue(rgba))), m_alpha(uchar(qAlpha(rgba))) {} + +inline QColor4ub::QColor4ub(const uchar *data) + : m_red(data[0]), m_green(data[1]), m_blue(data[2]), m_alpha(data[3]) {} + +inline QColor4ub& QColor4ub::operator=(const QColor& color) +{ + m_red = uchar(color.red()); + m_green = uchar(color.green()); + m_blue = uchar(color.blue()); + m_alpha = uchar(color.alpha()); + return *this; +} + +inline QColor4ub& QColor4ub::operator=(Qt::GlobalColor color) +{ + QColor c(color); + m_red = uchar(c.red()); + m_green = uchar(c.green()); + m_blue = uchar(c.blue()); + m_alpha = uchar(c.alpha()); + return *this; +} + +inline void QColor4ub::setRgb(int red, int green, int blue, int alpha) +{ + m_red = uchar(red); + m_green = uchar(green); + m_blue = uchar(blue); + m_alpha = uchar(alpha); +} + +inline void QColor4ub::setRgbF(qreal red, qreal green, qreal blue, qreal alpha) +{ + m_red = uchar(qRound(red * 255.0f)); + m_green = uchar(qRound(green * 255.0f)); + m_blue = uchar(qRound(blue * 255.0f)); + m_alpha = uchar(qRound(alpha * 255.0f)); +} + +inline QColor4ub QColor4ub::fromRgb(int red, int green, int blue, int alpha) +{ + return QColor4ub(red, green, blue, alpha); +} + +inline QColor4ub QColor4ub::fromRgbF + (qreal red, qreal green, qreal blue, qreal alpha) +{ + return QColor4ub(qRound(red * 255.0f), qRound(green * 255.0f), + qRound(blue * 255.0f), qRound(alpha * 255.0f)); +} + +inline QColor4ub QColor4ub::fromRaw(const uchar *data) +{ + return QColor4ub(data); +} + +inline QColor QColor4ub::toColor() const +{ + return QColor(m_red, m_green, m_blue, m_alpha); +} + +inline bool QColor4ub::operator==(const QColor4ub& other) const +{ + return m_red == other.m_red && m_green == other.m_green && + m_blue == other.m_blue && m_alpha == other.m_alpha; +} + +inline bool QColor4ub::operator!=(const QColor4ub& other) const +{ + return m_red != other.m_red || m_green != other.m_green || + m_blue != other.m_blue || m_alpha != other.m_alpha; +} + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QColor4ub &color); +#endif + +Q_DECLARE_TYPEINFO(QColor4ub, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QColor4ub) + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qcustomdataarray.cpp b/src/threed/arrays/qcustomdataarray.cpp new file mode 100644 index 000000000..f34793af7 --- /dev/null +++ b/src/threed/arrays/qcustomdataarray.cpp @@ -0,0 +1,909 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcustomdataarray.h" +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QCustomDataArray + \brief The QCustomDataArray class is a polymorphic array of data values suitable for use in 3D applications. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays + + QArray is an efficient storage mechanism for vertex attributes. + However, there are some situations where the element type of a custom + vertex attribute is not known until runtime. QCustomDataArray is + intended for use in those situations. It has a small performance + penalty compared to QArray to achieve polymorphism. + + The elements that may be stored in a QCustomDataArray are limited + to a few types: float, QVector2D, QVector3D, QVector4D, and + QColor4ub. This provides a reasonable range of efficient use + cases without overloading the API. QArray can be used on + any type, but is restricted to types that are known at compile time. + + Like QArray, QCustomDataArray uses implicit sharing and + copy-on-write semantics to support passing large arrays around + an application with little overhead. + + \sa QArray +*/ + +/*! + \enum QCustomDataArray::ElementType + This enum defines the element type within a QCustomDataArray. + + \value Float The elements are of type float. + \value Vector2D The elements are of type QVector2D. + \value Vector3D The elements are of type QVector3D. + \value Vector4D The elements are of type QVector4D. + \value Color The elements are of type QColor4ub, which consists of + four unsigned bytes. To represent colors as four floating-point + values, use Vector4D as the element type. +*/ + +/*! + \fn QCustomDataArray::QCustomDataArray() + + Constructs an empty custom data array. The elementType() will + initially be QCustomDataArray::Float, which can be changed with a + call to setElementType() before the elements are appended. + + \sa setElementType(), append() +*/ + +/*! + Constructs an empty custom data array with elements represented + by the specified \a type. + + \sa setElementType() +*/ +QCustomDataArray::QCustomDataArray(QCustomDataArray::ElementType type) +{ + setElementType(type); +} + +/*! + Constructs an empty custom data array with elements represented + by the specified \a type. The array is initially resized to \a size; + filling all elements with zeroes. + + \sa setElementType(), resize() +*/ +QCustomDataArray::QCustomDataArray(QCustomDataArray::ElementType type, int size) +{ + setElementType(type); + resize(size); +} + +/*! + \fn QCustomDataArray::QCustomDataArray(const QCustomDataArray& other) + + Constructs a copy of \a other. +*/ + +/*! + Constructs a copy of the floating-point QArray \a other. + + The elementType() will be set to QCustomDataArray::Float. + + \sa toFloatArray() +*/ +QCustomDataArray::QCustomDataArray(const QArray<float>& other) + : m_array(other), + m_elementType(QCustomDataArray::Float), + m_elementComponents(1) +{ +} + +/*! + Constructs a copy of the 2D vector QArray \a other. + + The elementType() will be set to QCustomDataArray::Vector2D. + + This constructor needs to make a complete copy of the data + in \a other so it may be expensive performance-wise. + + \sa toVector2DArray() +*/ +QCustomDataArray::QCustomDataArray(const QArray<QVector2D>& other) + : m_elementType(QCustomDataArray::Vector2D), + m_elementComponents(2) +{ + int size = other.size(); + if (size > 0) { + const QVector2D *src = other.constData(); + float *dst = m_array.extend(size * 2); + qMemCopy(dst, src, size * sizeof(QVector2D)); + } +} + +/*! + Constructs a copy of the 3D vector QArray \a other. + + The elementType() will be set to QCustomDataArray::Vector3D. + + This constructor needs to make a complete copy of the data + in \a other so it may be expensive performance-wise. + + \sa toVector3DArray() +*/ +QCustomDataArray::QCustomDataArray(const QArray<QVector3D>& other) + : m_elementType(QCustomDataArray::Vector3D), + m_elementComponents(3) +{ + int size = other.size(); + if (size > 0) { + const QVector3D *src = other.constData(); + float *dst = m_array.extend(size * 3); + qMemCopy(dst, src, size * sizeof(QVector3D)); + } +} + +/*! + Constructs a copy of the 4D vector QArray \a other. + + The elementType() will be set to QCustomDataArray::Vector4D. + + This constructor needs to make a complete copy of the data + in \a other so it may be expensive performance-wise. + + \sa toVector3DArray() +*/ +QCustomDataArray::QCustomDataArray(const QArray<QVector4D>& other) + : m_elementType(QCustomDataArray::Vector4D), + m_elementComponents(4) +{ + int size = other.size(); + if (size > 0) { + const QVector4D *src = other.constData(); + float *dst = m_array.extend(size * 4); + qMemCopy(dst, src, size * sizeof(QVector4D)); + } +} + +/*! + Constructs a copy of the color QArray \a other. + + The elementType() will be set to QCustomDataArray::Color. + + This constructor needs to make a complete copy of the data + in \a other so it may be expensive performance-wise. + + \sa toColorArray() +*/ +QCustomDataArray::QCustomDataArray(const QArray<QColor4ub>& other) + : m_elementType(QCustomDataArray::Color), + m_elementComponents(1) +{ + int size = other.size(); + qMemCopy(m_array.extend(size), other.constData(), sizeof(QColor4ub) * size); +} + +/*! + \fn QCustomDataArray::~QCustomDataArray() + + Destroys this custom data array. +*/ + +/*! + \fn QCustomDataArray& QCustomDataArray::operator=(const QCustomDataArray& other) + + Assigns \a other to this custom data array and returns a reference + to this custom data array. + + The previous elementType() for this custom data array will be + replaced with the type from \a other. The element data is assigned + directly without conversion. +*/ + +/*! + \fn QCustomDataArray::ElementType QCustomDataArray::elementType() const + + Returns the representation type of elements in this custom data array. + + \sa setElementType() +*/ + +/*! + Sets the representation \a type of elements in this custom data array. + The array must be empty to change the element type. + + \sa elementType(), append() +*/ +void QCustomDataArray::setElementType(QCustomDataArray::ElementType type) +{ + Q_ASSERT(m_array.isEmpty()); + m_elementType = type; + switch (type) { + case QCustomDataArray::Float: + m_elementComponents = 1; + break; + case QCustomDataArray::Vector2D: + m_elementComponents = 2; + break; + case QCustomDataArray::Vector3D: + m_elementComponents = 3; + break; + case QCustomDataArray::Vector4D: + m_elementComponents = 4; + break; + case QCustomDataArray::Color: + m_elementComponents = 1; // 4 bytes packed into a float. + break; + default: + Q_ASSERT_X(false, "QCustomDataArray::setElementType", + "unknown element type"); + m_elementComponents = 1; + break; + } +} + +/*! + \fn int QCustomDataArray::size() const + + Returns the number of elements in this custom data array. + + \sa resize(), capacity(), isEmpty() +*/ + +/*! + \fn int QCustomDataArray::count() const + + Same as size(); provided for convenience. +*/ + +/*! + \fn int QCustomDataArray::capacity() const + + Returns the number of elements that can be stored in this + custom data array before reallocation. + + \sa reserve(), size() +*/ + +/*! + \fn bool QCustomDataArray::isEmpty() const + + Returns true if this data array is empty; false otherwise. + + \sa size(), clear() +*/ + +/*! + \fn int QCustomDataArray::elementSize() const + + Returns the size of individual elements in this custom data + array, in bytes. For example, the element size of an array + containing QVector3D values will be 3 * sizeof(float), + normally 12. + + \sa setElementType() +*/ + +/*! + \fn void QCustomDataArray::clear() + + Clears all elements from this custom data array and sets the size to zero. + + This function will deallocate any memory that is used on the heap + to store the custom data array's elements. To reuse the same memory + as before, call resize() with an argument of zero. + + \sa resize(), isEmpty() +*/ + +/*! + \fn void QCustomDataArray::resize(int size) + + Sets the size of the custom data array to \a size. If \a size is greater + than the current size, elements are added to the end; the new elements + are initialized with all-zeroes. If \a size is less than the current + size, elements are removed from the end. + + \sa size(), reserve(), squeeze() +*/ + +/*! + \fn void QCustomDataArray::reserve(int size) + + Increases the capacity of this custom data array to reserve space for + at least \a size elements. If the capacity is already larger + than \a size, this function does nothing; in particular, it does + not remove elements from the array like resize() does. + + This function can be useful when you know how roughly many elements + will be appended ahead of time. Reserving the space once can avoid + unnecessary realloc operations later. + + \sa capacity(), resize(), squeeze() +*/ + +/*! + \fn void QCustomDataArray::squeeze() + + Releases any memory not required to store the custom data array's + elements by reducing its capacity() to size(). + + This function is intended for reclaiming memory in a custom data + array that is being used over and over with different contents. + As elements are added to a custom data array, it will be constantly + expanded in size. This function can realloc the custom data array + to a smaller size to reclaim unused memory. + + \sa reserve(), capacity() +*/ + +/*! + Returns the value of the element at \a index in this custom + data array as a QVariant. + + Color elements are returned as a QVariant containing a + QColor4ub, not a QColor. + + \sa setAt(), append(), floatAt(), vector2DAt(), vector3DAt() + \sa vector4DAt(), colorAt() +*/ +QVariant QCustomDataArray::at(int index) const +{ + Q_ASSERT(index >= 0 && index < size()); + + const float *data; + switch (m_elementType) { + + case QCustomDataArray::Float: + return QVariant(m_array.at(index)); + + case QCustomDataArray::Vector2D: + data = m_array.constData() + index * 2; + return qVariantFromValue(QVector2D(data[0], data[1])); + + case QCustomDataArray::Vector3D: + data = m_array.constData() + index * 3; + return qVariantFromValue(QVector3D(data[0], data[1], data[2])); + + case QCustomDataArray::Vector4D: + data = m_array.constData() + index * 4; + return qVariantFromValue + (QVector4D(data[0], data[1], data[2], data[3])); + + case QCustomDataArray::Color: + data = m_array.constData() + index; + return qVariantFromValue + (QColor4ub::fromRaw(reinterpret_cast<const uchar *>(data))); + + default: break; + } + return QVariant(); +} + +/*! + Sets the element at \a index in this custom data array to \a value. + + The type of \a value must be consistent with elementType(). + The two exceptions to this are that a Float value can be + specified by either a float or double QVariant, and a Color + value can be specified as either a QColor4ub or QColor QVariant. + + \sa at(), elementType() +*/ +void QCustomDataArray::setAt(int index, const QVariant& value) +{ + Q_ASSERT(index >= 0 && index < size()); + + switch (value.type()) { + + case (QVariant::Type)QMetaType::Float: + Q_ASSERT(m_elementType == QCustomDataArray::Float); + m_array[index] = value.toFloat(); + break; + + case QVariant::Double: + // Convert Double into Float. + Q_ASSERT(m_elementType == QCustomDataArray::Float); + m_array[index] = float(value.toDouble()); + break; + + case QVariant::Vector2D: + Q_ASSERT(m_elementType == QCustomDataArray::Vector2D); + *(reinterpret_cast<QVector2D *>(m_array.data() + index * 2)) + = qVariantValue<QVector2D>(value); + break; + + case QVariant::Vector3D: + Q_ASSERT(m_elementType == QCustomDataArray::Vector3D); + *(reinterpret_cast<QVector3D *>(m_array.data() + index * 3)) + = qVariantValue<QVector3D>(value); + break; + + case QVariant::Vector4D: + Q_ASSERT(m_elementType == QCustomDataArray::Vector4D); + *(reinterpret_cast<QVector4D *>(m_array.data() + index * 4)) + = qVariantValue<QVector4D>(value); + break; + + case QVariant::Color: + // Convert QColor into QColor4ub. + Q_ASSERT(m_elementType == QCustomDataArray::Color); + *(reinterpret_cast<QColor4ub *>(m_array.data() + index)) + = QColor4ub(qVariantValue<QColor>(value)); + break; + + case QVariant::UserType: + if (value.userType() == qMetaTypeId<QColor4ub>()) { + Q_ASSERT(m_elementType == QCustomDataArray::Color); + *(reinterpret_cast<QColor4ub *>(m_array.data() + index)) + = qVariantValue<QColor4ub>(value); + break; + } + // Fall through. + + default: + Q_ASSERT_X(false, "QCustomDataArray::setAt", + "QVariant type not supported for elements"); + break; + } +} + +/*! + \fn void QCustomDataArray::setAt(int index, qreal x) + \overload + + Sets the floating-point element at \a index in this custom data + array to \a x. The elementType() must be QCustomDataArray::Float. + + \sa at(), elementType(), floatAt() +*/ + +/*! + \fn void QCustomDataArray::setAt(int index, qreal x, qreal y) + \overload + + Sets the 2D vector element at \a index in this custom + data array to (\a x, \a y). The elementType() must be + QCustomDataArray::Vector2D. + + \sa at(), elementType(), vector2DAt() +*/ + +/*! + \fn void QCustomDataArray::setAt(int index, qreal x, qreal y, qreal z) + \overload + + Sets the 3D vector element at \a index in this custom + data array to (\a x, \a y, \a z). The elementType() must be + QCustomDataArray::Vector3D. + + \sa at(), elementType(), vector3DAt() +*/ + +/*! + \fn void QCustomDataArray::setAt(int index, qreal x, qreal y, qreal z, qreal w) + \overload + + Sets the 4D vector element at \a index in this custom + data array to (\a x, \a y, \a z, \a w). The elementType() must be + QCustomDataArray::Vector4D. + + \sa at(), elementType(), vector4DAt() +*/ + +/*! + \fn void QCustomDataArray::setAt(int index, const QVector2D& value) + \overload + + Sets the 2D vector element at \a index in this custom + data array to \a value. The elementType() must be + QCustomDataArray::Vector2D. + + \sa at(), elementType(), vector2DAt() +*/ + +/*! + \fn void QCustomDataArray::setAt(int index, const QVector3D& value) + \overload + + Sets the 3D vector element at \a index in this custom + data array to \a value. The elementType() must be + QCustomDataArray::Vector3D. + + \sa at(), elementType(), vector3DAt() +*/ + +/*! + \fn void QCustomDataArray::setAt(int index, const QVector4D& value) + \overload + + Sets the 4D vector element at \a index in this custom + data array to \a value. The elementType() must be + QCustomDataArray::Vector4D. + + \sa at(), elementType(), vector4DAt() +*/ + +/*! + \fn void QCustomDataArray::setAt(int index, const QColor4ub& value) + \overload + + Sets the color element at \a index in this custom data array to \a value. + The elementType() must be QCustomDataArray::Color. + + \sa at(), elementType(), colorAt() +*/ + +/*! + \fn void QCustomDataArray::setAt(int index, Qt::GlobalColor value) + \overload + + Sets the color element at \a index in this custom data array to \a value. + The elementType() must be QCustomDataArray::Color. + + \sa at(), elementType(), colorAt() +*/ + +/*! + \fn qreal QCustomDataArray::floatAt(int index) const + + Returns the floating-point element at \a index in this custom data array. + The elementType() must be QCustomDataArray::Float. + + \sa at(), setAt(), elementType() +*/ + +/*! + \fn QVector2D QCustomDataArray::vector2DAt(int index) const + + Returns the 2D vector element at \a index in this custom data array. + The elementType() must be QCustomDataArray::Vector2D. + + \sa at(), setAt(), elementType() +*/ + +/*! + \fn QVector3D QCustomDataArray::vector3DAt(int index) const + + Returns the 3D vector element at \a index in this custom data array. + The elementType() must be QCustomDataArray::Vector3D. + + \sa at(), setAt(), elementType() +*/ + +/*! + \fn QVector4D QCustomDataArray::vector4DAt(int index) const + + Returns the 4D vector element at \a index in this custom data array. + The elementType() must be QCustomDataArray::Vector4D. + + \sa at(), setAt(), elementType() +*/ + +/*! + \fn QColor4ub QCustomDataArray::colorAt(int index) const + + Returns the color element at \a index in this custom data array. + The elementType() must be QCustomDataArray::Color. + + \sa at(), setAt(), elementType() +*/ + +/*! + \fn void QCustomDataArray::append(qreal x) + \overload + + Appends the floating-point value \a x to this custom data array. + The elementType() must be QCustomDataArray::Float. + + \sa setAt(), floatAt() +*/ + +/*! + \fn void QCustomDataArray::append(qreal x, qreal y) + \overload + + Appends the 2D vector value (\a x, \a y) to this custom data array. + The elementType() must be QCustomDataArray::Vector2D. + + \sa setAt(), vector2DAt() +*/ + +/*! + \fn void QCustomDataArray::append(qreal x, qreal y, qreal z) + \overload + + Appends the 3D vector value (\a x, \a y, \a z) to this custom + data array. The elementType() must be QCustomDataArray::Vector3D. + + \sa setAt(), vector3DAt() +*/ + +/*! + \fn void QCustomDataArray::append(qreal x, qreal y, qreal z, qreal w) + \overload + + Appends the 4D vector value (\a x, \a y, \a z, \a w) to this custom + data array. The elementType() must be QCustomDataArray::Vector4D. + + \sa setAt(), vector4DAt() +*/ + +/*! + \fn void QCustomDataArray::append(const QVector2D& value) + \overload + + Appends the 2D vector \a value to this custom data array. + The elementType() must be QCustomDataArray::Vector2D. + + \sa setAt(), vector2DAt() +*/ + +/*! + \fn void QCustomDataArray::append(const QVector3D& value) + \overload + + Appends the 3D vector \a value to this custom data array. + The elementType() must be QCustomDataArray::Vector3D. + + \sa setAt(), vector3DAt() +*/ + +/*! + \fn void QCustomDataArray::append(const QVector4D& value) + \overload + + Appends the 4D vector \a value to this custom data array. + The elementType() must be QCustomDataArray::Vector4D. + + \sa setAt(), vector4DAt() +*/ + +/*! + \fn void QCustomDataArray::append(const QColor4ub& value) + \overload + + Appends the color \a value to this custom data array. + The elementType() must be QCustomDataArray::Color. + + \sa setAt(), colorAt() +*/ + +/*! + \fn void QCustomDataArray::append(Qt::GlobalColor value) + \overload + + Appends the color \a value to this custom data array. + The elementType() must be QCustomDataArray::Color. + + \sa setAt(), colorAt() +*/ + +/*! + \fn void QCustomDataArray::append(const QCustomDataArray &array) + \overload + + Appends the values in \a array to this custom data array. This + custom data array must have the same element type as \a array, + unless this custom data array is empty - in which case the + element type and data of \a array will be assigned to this. +*/ + +/*! + Appends \a value to this custom data array. + + The type of \a value must be consistent with elementType(). + The two exceptions to this are that a Float value can be + specified by either a float or double QVariant, and a Color + value can be specified as either a QColor4ub or QColor QVariant. + + \sa at(), setAt(), elementType() +*/ +void QCustomDataArray::append(const QVariant& value) +{ + switch (value.type()) { + + case (QVariant::Type)QMetaType::Float: + Q_ASSERT(m_elementType == QCustomDataArray::Float); + m_array.append(value.toFloat()); + break; + + case QVariant::Double: + // Convert Double into Float. + Q_ASSERT(m_elementType == QCustomDataArray::Float); + m_array.append(float(value.toDouble())); + break; + + case QVariant::Vector2D: + append(qVariantValue<QVector2D>(value)); + break; + + case QVariant::Vector3D: + append(qVariantValue<QVector3D>(value)); + break; + + case QVariant::Vector4D: + append(qVariantValue<QVector4D>(value)); + break; + + case QVariant::Color: + // Convert QColor into QColor4ub. + append(QColor4ub(qVariantValue<QColor>(value))); + break; + + case QVariant::UserType: + if (value.userType() == qMetaTypeId<QColor4ub>()) { + append(qVariantValue<QColor4ub>(value)); + break; + } + // Fall through. + + default: + Q_ASSERT_X(false, "QCustomDataArray::append", + "QVariant type not supported for elements"); + break; + } +} + +/*! + Returns the contents of this custom data array as a QArray + of float values. + + The elementType() must be QCustomDataArray::Float. +*/ +QArray<float> QCustomDataArray::toFloatArray() const +{ + Q_ASSERT(m_elementType == QCustomDataArray::Float); + return m_array; +} + +/*! + Returns the contents of this custom data array as a QArray + of QVector2D values. + + The elementType() must be QCustomDataArray::Vector2D. + + This function needs to make a complete copy of the data + in this array so it may be expensive performance-wise. +*/ +QArray<QVector2D> QCustomDataArray::toVector2DArray() const +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector2D); + int size = m_array.size() / 2; + QArray<QVector2D> result; + if (size > 0) { + QVector2D *dst = result.extend(size); + const float *src = m_array.constData(); + qMemCopy(dst, src, size * sizeof(QVector2D)); + } + return result; +} + +/*! + Returns the contents of this custom data array as a QArray + of QVector3D values. + + The elementType() must be QCustomDataArray::Vector3D. + + This function needs to make a complete copy of the data + in this array so it may be expensive performance-wise. +*/ +QArray<QVector3D> QCustomDataArray::toVector3DArray() const +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector3D); + int size = m_array.size() / 3; + QArray<QVector3D> result; + if (size > 0) { + QVector3D *dst = result.extend(size); + const float *src = m_array.constData(); + qMemCopy(dst, src, size * sizeof(QVector3D)); + } + return result; +} + +/*! + Returns the contents of this custom data array as a QArray + of QVector4D values. + + The elementType() must be QCustomDataArray::Vector4D. + + This function needs to make a complete copy of the data + in this array so it may be expensive performance-wise. +*/ +QArray<QVector4D> QCustomDataArray::toVector4DArray() const +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector4D); + int size = m_array.size() / 4; + QArray<QVector4D> result; + if (size > 0) { + QVector4D *dst = result.extend(size); + const float *src = m_array.constData(); + qMemCopy(dst, src, size * sizeof(QVector4D)); + } + return result; +} + +/*! + Returns the contents of this custom data array as a QArray + of QColor4ub values. + + The elementType() must be QCustomDataArray::Color. + + This function needs to make a complete copy of the data + in this array so it may be expensive performance-wise. +*/ +QArray<QColor4ub> QCustomDataArray::toColorArray() const +{ + Q_ASSERT(m_elementType == QCustomDataArray::Color); + int size = m_array.size(); + QArray<QColor4ub> result; + result.reserve(size); + const QColor4ub *data = + reinterpret_cast<const QColor4ub *>(m_array.constData()); + for (int index = 0; index < size; ++index) + result.append(*data++); + return result; +} + +/*! + \fn const void *QCustomDataArray::data() const + + Returns a const pointer to the data stored in the custom data array. + The pointer can be used to access the items in the custom data array. + The pointer remains valid as long as the custom data array isn't + reallocated. + + This function is mostly useful to pass a custom data array to a function + that accepts a plain C++ array. +*/ + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QCustomDataArray &array) +{ + dbg << "QCustomDataArray" << &array << " -- count:" << array.count(); + for (int i = 0; i < array.count(); ++i) + dbg << array.at(i); + return dbg; +} +#endif + + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qcustomdataarray.h b/src/threed/arrays/qcustomdataarray.h new file mode 100644 index 000000000..c4064a982 --- /dev/null +++ b/src/threed/arrays/qcustomdataarray.h @@ -0,0 +1,421 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCUSTOMDATAARRAY_H +#define QCUSTOMDATAARRAY_H + +#include "qarray.h" +#include "qcolor4ub.h" +#include <QtCore/qvariant.h> +#include <QtGui/qvector2d.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLVertexBundleCustomAttribute; +class QGeometryData; + +class Q_QT3D_EXPORT QCustomDataArray +{ +public: + enum ElementType + { + Float, + Vector2D, + Vector3D, + Vector4D, + Color + }; + + QCustomDataArray(); + explicit QCustomDataArray(QCustomDataArray::ElementType type); + QCustomDataArray(QCustomDataArray::ElementType type, int size); + QCustomDataArray(const QCustomDataArray& other); + QCustomDataArray(const QArray<float>& other); + QCustomDataArray(const QArray<QVector2D>& other); + QCustomDataArray(const QArray<QVector3D>& other); + QCustomDataArray(const QArray<QVector4D>& other); + QCustomDataArray(const QArray<QColor4ub>& other); + ~QCustomDataArray(); + + QCustomDataArray& operator=(const QCustomDataArray& other); + + QCustomDataArray::ElementType elementType() const; + void setElementType(QCustomDataArray::ElementType type); + + int size() const; + int count() const; + + int capacity() const; + bool isEmpty() const; + + int elementSize() const; + + void clear(); + void reserve(int size); + void resize(int size); + void squeeze(); + + QVariant at(int index) const; + void setAt(int index, const QVariant& value); + + void setAt(int index, qreal x); + void setAt(int index, qreal x, qreal y); + void setAt(int index, qreal x, qreal y, qreal z); + void setAt(int index, qreal x, qreal y, qreal z, qreal w); + void setAt(int index, const QVector2D& value); + void setAt(int index, const QVector3D& value); + void setAt(int index, const QVector4D& value); + void setAt(int index, const QColor4ub& value); + void setAt(int index, Qt::GlobalColor value); + + qreal floatAt(int index) const; + QVector2D vector2DAt(int index) const; + QVector3D vector3DAt(int index) const; + QVector4D vector4DAt(int index) const; + QColor4ub colorAt(int index) const; + + void append(qreal x); + void append(qreal x, qreal y); + void append(qreal x, qreal y, qreal z); + void append(qreal x, qreal y, qreal z, qreal w); + void append(const QVector2D& value); + void append(const QVector3D& value); + void append(const QVector4D& value); + void append(const QColor4ub& value); + void append(const QVariant& value); + void append(Qt::GlobalColor value); + void append(const QCustomDataArray &array); + + QArray<float> toFloatArray() const; + QArray<QVector2D> toVector2DArray() const; + QArray<QVector3D> toVector3DArray() const; + QArray<QVector4D> toVector4DArray() const; + QArray<QColor4ub> toColorArray() const; + + const void *data() const; + +private: + QArray<float> m_array; + QCustomDataArray::ElementType m_elementType; + int m_elementComponents; + + friend class QGLVertexBundleCustomAttribute; + friend class QGeometryData; +}; + +inline QCustomDataArray::QCustomDataArray() + : m_elementType(QCustomDataArray::Float), + m_elementComponents(1) +{ +} + +inline QCustomDataArray::QCustomDataArray(const QCustomDataArray& other) + : m_array(other.m_array), + m_elementType(other.m_elementType), + m_elementComponents(other.m_elementComponents) +{ +} + +inline QCustomDataArray::~QCustomDataArray() {} + +inline QCustomDataArray& QCustomDataArray::operator=(const QCustomDataArray& other) +{ + if (this != &other) { + m_array = other.m_array; + m_elementType = other.m_elementType; + m_elementComponents = other.m_elementComponents; + } + return *this; +} + +inline QCustomDataArray::ElementType QCustomDataArray::elementType() const +{ + return m_elementType; +} + +inline int QCustomDataArray::size() const +{ + return m_array.size() / m_elementComponents; +} + +inline int QCustomDataArray::count() const +{ + return m_array.size() / m_elementComponents; +} + +inline int QCustomDataArray::capacity() const +{ + return m_array.capacity() / m_elementComponents; +} + +inline bool QCustomDataArray::isEmpty() const +{ + return m_array.isEmpty(); +} + +inline int QCustomDataArray::elementSize() const +{ + return m_elementComponents * sizeof(float); +} + +inline void QCustomDataArray::clear() +{ + m_array.clear(); +} + +inline void QCustomDataArray::reserve(int size) +{ + m_array.reserve(size * m_elementComponents); +} + +inline void QCustomDataArray::resize(int size) +{ + m_array.resize(size * m_elementComponents); +} + +inline void QCustomDataArray::squeeze() +{ + m_array.squeeze(); +} + +inline void QCustomDataArray::setAt(int index, qreal x) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Float); + Q_ASSERT(index >= 0 && index < size()); + m_array[index] = float(x); +} + +inline void QCustomDataArray::setAt(int index, qreal x, qreal y) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector2D); + Q_ASSERT(index >= 0 && index < size()); + float *data = m_array.data() + index * 2; + data[0] = float(x); + data[1] = float(y); +} + +inline void QCustomDataArray::setAt(int index, qreal x, qreal y, qreal z) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector3D); + Q_ASSERT(index >= 0 && index < size()); + float *data = m_array.data() + index * 3; + data[0] = float(x); + data[1] = float(y); + data[2] = float(z); +} + +inline void QCustomDataArray::setAt(int index, qreal x, qreal y, qreal z, qreal w) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector4D); + Q_ASSERT(index >= 0 && index < size()); + float *data = m_array.data() + index * 4; + data[0] = float(x); + data[1] = float(y); + data[2] = float(z); + data[3] = float(w); +} + +inline void QCustomDataArray::setAt(int index, const QVector2D& value) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector2D); + Q_ASSERT(index >= 0 && index < size()); + float *data = m_array.data() + index * 2; + data[0] = float(value.x()); + data[1] = float(value.y()); +} + +inline void QCustomDataArray::setAt(int index, const QVector3D& value) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector3D); + Q_ASSERT(index >= 0 && index < size()); + float *data = m_array.data() + index * 3; + data[0] = float(value.x()); + data[1] = float(value.y()); + data[2] = float(value.z()); +} + +inline void QCustomDataArray::setAt(int index, const QVector4D& value) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector4D); + Q_ASSERT(index >= 0 && index < size()); + float *data = m_array.data() + index * 4; + data[0] = float(value.x()); + data[1] = float(value.y()); + data[2] = float(value.z()); + data[3] = float(value.w()); +} + +inline void QCustomDataArray::setAt(int index, const QColor4ub& value) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Color); + Q_ASSERT(index >= 0 && index < size()); + *(reinterpret_cast<QColor4ub *>(m_array.data() + index)) = value; +} + +inline void QCustomDataArray::setAt(int index, Qt::GlobalColor value) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Color); + Q_ASSERT(index >= 0 && index < size()); + *(reinterpret_cast<QColor4ub *>(m_array.data() + index)) = QColor4ub(value); +} + +inline qreal QCustomDataArray::floatAt(int index) const +{ + Q_ASSERT(m_elementType == QCustomDataArray::Float); + Q_ASSERT(index >= 0 && index < size()); + return m_array.at(index); +} + +inline QVector2D QCustomDataArray::vector2DAt(int index) const +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector2D); + Q_ASSERT(index >= 0 && index < size()); + const float *data = m_array.constData() + index * 2; + return QVector2D(data[0], data[1]); +} + +inline QVector3D QCustomDataArray::vector3DAt(int index) const +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector3D); + Q_ASSERT(index >= 0 && index < size()); + const float *data = m_array.constData() + index * 3; + return QVector3D(data[0], data[1], data[2]); +} + +inline QVector4D QCustomDataArray::vector4DAt(int index) const +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector4D); + Q_ASSERT(index >= 0 && index < size()); + const float *data = m_array.constData() + index * 4; + return QVector4D(data[0], data[1], data[2], data[3]); +} + +inline QColor4ub QCustomDataArray::colorAt(int index) const +{ + Q_ASSERT(m_elementType == QCustomDataArray::Color); + Q_ASSERT(index >= 0 && index < size()); + return *(reinterpret_cast<const QColor4ub *>(m_array.constData() + index)); +} + +inline void QCustomDataArray::append(qreal x) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Float); + m_array.append(float(x)); +} + +inline void QCustomDataArray::append(qreal x, qreal y) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector2D); + m_array.append(float(x), float(y)); +} + +inline void QCustomDataArray::append(qreal x, qreal y, qreal z) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector3D); + m_array.append(float(x), float(y), float(z)); +} + +inline void QCustomDataArray::append(qreal x, qreal y, qreal z, qreal w) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector4D); + m_array.append(float(x), float(y), float(z), float(w)); +} + +inline void QCustomDataArray::append(const QVector2D& value) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector2D); + m_array.append(float(value.x()), float(value.y())); +} + +inline void QCustomDataArray::append(const QVector3D& value) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector3D); + m_array.append(float(value.x()), float(value.y()), float(value.z())); +} + +inline void QCustomDataArray::append(const QVector4D& value) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Vector4D); + m_array.append(float(value.x()), float(value.y()), + float(value.z()), float(value.w())); +} + +inline void QCustomDataArray::append(const QColor4ub& value) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Color); + *(reinterpret_cast<QColor4ub *>(m_array.extend(1))) = value; +} + +inline void QCustomDataArray::append(Qt::GlobalColor value) +{ + Q_ASSERT(m_elementType == QCustomDataArray::Color); + *(reinterpret_cast<QColor4ub *>(m_array.extend(1))) = QColor4ub(value); +} + +inline void QCustomDataArray::append(const QCustomDataArray &array) +{ + Q_ASSERT(isEmpty() || (array.elementType() == elementType())); + if (isEmpty()) + *this = array; + else + m_array.append(array.m_array); +} + +inline const void *QCustomDataArray::data() const +{ + return m_array.constData(); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QCustomDataArray &array); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qglattributedescription.cpp b/src/threed/arrays/qglattributedescription.cpp new file mode 100644 index 000000000..572c04f87 --- /dev/null +++ b/src/threed/arrays/qglattributedescription.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglattributedescription.h" +#include "qopenglfunctions.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLAttributeDescription + \brief The QGLAttributeDescription class encapsulates information about an OpenGL attribute value's layout and type. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays + + OpenGL has many functions that take a pointer to vertex attribute + values: \c{glVertexPointer()}, \c{glNormalPointer()}, + \c{glVertexAttribPointer()}, etc. These functions typically + take four arguments: tuple size (1, 2, 3, or 4), component type + (e.g. GL_FLOAT), stride, and data pointer (\c{glNormalPointer()} + does not use tuple size, assuming that it is 3). When used with + vertex buffers, the data pointer may be an offset into the vertex + buffer instead. + + QGLAttributeDescription encapsulates the vertex attribute() kind + (QGL::Position, QGL::Normal, etc) with the type(), tupleSize(), + and stride() information of an attribute. The companion + QGLAttributeValue class adds the data pointer. + + \sa QGLAttributeValue +*/ + +/*! + \fn QGLAttributeDescription::QGLAttributeDescription() + + Constructs a null attribute description with default parameters of + tupleSize() and stride() set to zero, type() set to GL_FLOAT, + and attribute() set to QGL::Position. + + \sa isNull() +*/ + +/*! + \fn QGLAttributeDescription::QGLAttributeDescription(QGL::VertexAttribute attribute, int tupleSize, GLenum type, int stride) + + Constructs an attribute description with the fields \a attribute, + \a tupleSize, \a type, and \a stride. +*/ + +/*! + \fn bool QGLAttributeDescription::isNull() const + + Returns true if tupleSize() is zero, which indicates an unset + attribute description; false otherwise. +*/ + +/*! + \fn QGL::VertexAttribute QGLAttributeDescription::attribute() const + + Returns the vertex attribute that this description applies to. + The default value is QGL::Position. + + \sa setAttribute(), type() +*/ + +/*! + \fn void QGLAttributeDescription::setAttribute(QGL::VertexAttribute attribute) + + Sets the vertex \a attribute that this description applies to. + + \sa attribute() +*/ + +/*! + \fn GLenum QGLAttributeDescription::type() const + + Returns the component type for this attribute description. The default + value is GL_FLOAT. + + \sa setType(), sizeOfType(), attribute() +*/ + +/*! + \fn void QGLAttributeDescription::setType(GLenum type) + + Sets the component \a type for this attribute description. + + \sa type(), sizeOfType() +*/ + +/*! + Returns the size in bytes of type(). + + \sa type(), tupleSize() +*/ +int QGLAttributeDescription::sizeOfType() const +{ + switch (m_type) { + case GL_BYTE: return int(sizeof(GLbyte)); + case GL_UNSIGNED_BYTE: return int(sizeof(GLubyte)); + case GL_SHORT: return int(sizeof(GLshort)); + case GL_UNSIGNED_SHORT: return int(sizeof(GLushort)); + case GL_INT: return int(sizeof(GLint)); + case GL_UNSIGNED_INT: return int(sizeof(GLuint)); + case GL_FLOAT: return int(sizeof(GLfloat)); +#if defined(GL_DOUBLE) && !defined(QT_OPENGL_ES) + case GL_DOUBLE: return int(sizeof(GLdouble)); +#endif + default: return 0; + } +} + +/*! + \fn int QGLAttributeDescription::tupleSize() const + + Returns the tuple size of this attribute in components. For example, + a return value of 3 indicates a vector of 3-dimensional values. + If tupleSize() is zero, then this attribute description is null. + + \sa setTupleSize(), isNull(), sizeOfType() +*/ + +/*! + \fn void QGLAttributeDescription::setTupleSize(int tupleSize) + + Sets the tuple size of this attribute in components to \a tupleSize. + + \sa tupleSize() +*/ + +/*! + \fn int QGLAttributeDescription::stride() const + + Returns the stride in bytes from one vertex element to the + next for this attribute description. The default value of 0 indicates + that the elements are tightly packed within the data array. + + \sa setStride() +*/ + +/*! + \fn void QGLAttributeDescription::setStride(int stride) + + Sets the \a stride in bytes from one vertex element to the next + for this attribute description. + + \sa stride() +*/ + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qglattributedescription.h b/src/threed/arrays/qglattributedescription.h new file mode 100644 index 000000000..533ff68d1 --- /dev/null +++ b/src/threed/arrays/qglattributedescription.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLATTRIBUTEDESCRIPTION_H +#define QGLATTRIBUTEDESCRIPTION_H + +#include <QtOpenGL/qgl.h> +#include "qt3dglobal.h" +#include "qglnamespace.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QGLAttributeDescription +{ +public: + QGLAttributeDescription(); + QGLAttributeDescription(QGL::VertexAttribute attribute, + int tupleSize, GLenum type, int stride); + + bool isNull() const; + + QGL::VertexAttribute attribute() const; + void setAttribute(QGL::VertexAttribute attribute); + + GLenum type() const; + void setType(GLenum type); + + int sizeOfType() const; + + int tupleSize() const; + void setTupleSize(int tupleSize); + + int stride() const; + void setStride(int stride); + +private: + QGL::VertexAttribute m_attribute; + GLenum m_type; + int m_tupleSize; + int m_stride; +}; + +inline QGLAttributeDescription::QGLAttributeDescription() + : m_attribute(QGL::Position), m_type(GL_FLOAT), + m_tupleSize(0), m_stride(0) +{ +} + +inline QGLAttributeDescription::QGLAttributeDescription + (QGL::VertexAttribute attribute, int tupleSize, GLenum type, int stride) + : m_attribute(attribute), m_type(type), + m_tupleSize(tupleSize), m_stride(stride) +{ + Q_ASSERT(tupleSize >= 1 && tupleSize <= 4); +} + +inline bool QGLAttributeDescription::isNull() const +{ + return m_tupleSize == 0; +} + +inline QGL::VertexAttribute QGLAttributeDescription::attribute() const +{ + return m_attribute; +} + +inline void QGLAttributeDescription::setAttribute(QGL::VertexAttribute attribute) +{ + m_attribute = attribute; +} + +inline GLenum QGLAttributeDescription::type() const +{ + return m_type; +} + +inline void QGLAttributeDescription::setType(GLenum type) +{ + m_type = type; +} + +inline int QGLAttributeDescription::tupleSize() const +{ + return m_tupleSize; +} + +inline void QGLAttributeDescription::setTupleSize(int tupleSize) +{ + Q_ASSERT(tupleSize >= 1 && tupleSize <= 4); + m_tupleSize = tupleSize; +} + +inline int QGLAttributeDescription::stride() const +{ + return m_stride; +} + +inline void QGLAttributeDescription::setStride(int stride) +{ + m_stride = stride; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qglattributeset.cpp b/src/threed/arrays/qglattributeset.cpp new file mode 100644 index 000000000..6f6ae0e84 --- /dev/null +++ b/src/threed/arrays/qglattributeset.cpp @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglattributeset.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLAttributeSet + \brief The QGLAttributeSet class provides a set of QGL::VertexAttribute indexes. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays + + QGLAttributeSet is intended for checking if a specific vertex + attribute is present in a piece of geometry, or has been set on a + QGLPainter during rendering operations. The members of the set + are instances of QGL::VertexAttribute, with the restriction that + the index must be between 0 and 31. + + The most common use for this class is to determine if specific + attributes have been supplied on a QGLPainter so as to adjust the + current drawing effect accordingly. The following example will + use a lit texture effect if texture co-ordinates were provided + in the vertex bundle, or a simple lit material effect if + texture co-ordinates were not provided: + + \code + painter.clearAttributes(); + painter.setVertexBundle(bundle); + if (painter.attributes().contains(QGL::TextureCoord0)) + painter.setStandardEffect(QGL::LitModulateTexture2D); + else + painter.setStandardEffect(QGL::LitMaterial); + \endcode + + It is important to clear the attributes before setting the vertex + bundle, so that attributes from a previous bundle will not leak + through. Multiple vertex bundles may be supplied if they contain + different parts of the same logical piece of geometry. + + \sa QGLVertexBundle::attributes(), QGLPainter::attributes() +*/ + +/*! + \fn QGLAttributeSet::QGLAttributeSet() + + Constructs an empty attribute set. + + \sa isEmpty() +*/ + +/*! + \fn bool QGLAttributeSet::isEmpty() const + + Returns true if this attribute set is empty; false otherwise. +*/ + +/*! + \fn void QGLAttributeSet::clear() + + Clears this attribute set to empty. +*/ + +/*! + \fn bool QGLAttributeSet::contains(QGL::VertexAttribute attr) const + + Returns true if this attribute set contains \a attr; false otherwise. + + \sa insert(), remove() +*/ + +/*! + \fn void QGLAttributeSet::insert(QGL::VertexAttribute attr) + + Inserts \a attr into this attribute set. Note: \a attr must be + within the range 0 to 31. Attribute indexes outside this range + are ignored and not added to the set. + + \sa remove(), contains() +*/ + +/*! + \fn void QGLAttributeSet::remove(QGL::VertexAttribute attr) + + Removes \a attr from this attribute set. + + \sa insert(), contains() +*/ + +/*! + Returns the members of this attribute set as a list. + + \sa fromList() +*/ +QList<QGL::VertexAttribute> QGLAttributeSet::toList() const +{ + QList<QGL::VertexAttribute> list; + quint32 attrs = m_attrs; + int index = 0; + while (attrs != 0) { + if ((attrs & 1) != 0) + list.append(QGL::VertexAttribute(index)); + ++index; + attrs >>= 1; + } + return list; +} + +/*! + Returns a new attribute set that is initialized with the members + of \a list. + + \sa toList(), insert() +*/ +QGLAttributeSet QGLAttributeSet::fromList(const QList<QGL::VertexAttribute> &list) +{ + QGLAttributeSet set; + for (int index = 0; index < list.size(); ++index) + set.insert(list.at(index)); + return set; +} + +/*! + \fn void QGLAttributeSet::unite(const QGLAttributeSet &other) + + Unites the contents of \a other with this attribute set + and modifies this set accordingly. + + \sa intersect(), subtract(), insert() +*/ + +/*! + \fn void QGLAttributeSet::intersect(const QGLAttributeSet &other) + + Intersects the contents of \a other with this attribute set + and modifies this set accordingly. + + \sa unite(), subtract() +*/ + +/*! + \fn void QGLAttributeSet::subtract(const QGLAttributeSet &other) + + Subtracts the contents of \a other from this attribute set + and modifies this set accordingly. + + \sa unite(), intersect(), remove() +*/ + +/*! + \fn bool QGLAttributeSet::operator==(const QGLAttributeSet &other) const + + Returns true if this attribute set has the same elements as \a other; + false otherwise. + + \sa operator!=() +*/ + +/*! + \fn bool QGLAttributeSet::operator!=(const QGLAttributeSet &other) const + + Returns true if this attribute set does not have the same elements as + \a other; false otherwise. + + \sa operator==() +*/ + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qglattributeset.h b/src/threed/arrays/qglattributeset.h new file mode 100644 index 000000000..39aed5bae --- /dev/null +++ b/src/threed/arrays/qglattributeset.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLATTRIBUTESET_H +#define QGLATTRIBUTESET_H + +#include "qt3dglobal.h" +#include "qglnamespace.h" +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QGLAttributeSet +{ +public: + QGLAttributeSet() : m_attrs(0) {} + + bool isEmpty() const { return !m_attrs; } + void clear() { m_attrs = 0; } + + bool contains(QGL::VertexAttribute attr) const; + void insert(QGL::VertexAttribute attr); + void remove(QGL::VertexAttribute attr); + + QList<QGL::VertexAttribute> toList() const; + static QGLAttributeSet fromList(const QList<QGL::VertexAttribute> &list); + + void unite(const QGLAttributeSet &other); + void intersect(const QGLAttributeSet &other); + void subtract(const QGLAttributeSet &other); + + bool operator==(const QGLAttributeSet &other) const; + bool operator!=(const QGLAttributeSet &other) const; + +private: + quint32 m_attrs; +}; + +inline bool QGLAttributeSet::contains(QGL::VertexAttribute attr) const +{ + quint32 flag = quint32(attr); + return flag < 32 ? ((m_attrs & (((quint32)1) << flag)) != 0) : false; +} + +inline void QGLAttributeSet::insert(QGL::VertexAttribute attr) +{ + quint32 flag = quint32(attr); + if (flag < 32) + m_attrs |= (((quint32)1) << flag); +} + +inline void QGLAttributeSet::remove(QGL::VertexAttribute attr) +{ + quint32 flag = quint32(attr); + if (flag < 32) + m_attrs &= ~(((quint32)1) << flag); +} + +inline void QGLAttributeSet::unite(const QGLAttributeSet &other) +{ + m_attrs |= other.m_attrs; +} + +inline void QGLAttributeSet::intersect(const QGLAttributeSet &other) +{ + m_attrs &= other.m_attrs; +} + +inline void QGLAttributeSet::subtract(const QGLAttributeSet &other) +{ + m_attrs &= ~(other.m_attrs); +} + +inline bool QGLAttributeSet::operator==(const QGLAttributeSet &other) const +{ + return m_attrs == other.m_attrs; +} + +inline bool QGLAttributeSet::operator!=(const QGLAttributeSet &other) const +{ + return m_attrs != other.m_attrs; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qglattributevalue.cpp b/src/threed/arrays/qglattributevalue.cpp new file mode 100644 index 000000000..104aa39ff --- /dev/null +++ b/src/threed/arrays/qglattributevalue.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglattributevalue.h" +#include "qopenglfunctions.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLAttributeValue + \brief The QGLAttributeValue class encapsulates information about an OpenGL attribute value. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays + + OpenGL has many functions that take a pointer to vertex attribute + values: \c{glVertexPointer()}, \c{glNormalPointer()}, + \c{glVertexAttribPointer()}, etc. These functions typically + take four arguments: tuple size (1, 2, 3, or 4), component type + (e.g. GL_FLOAT), stride, and data pointer (\c{glNormalPointer()} + does not use tuple size, assuming that it is 3). When used with + vertex buffers, the data pointer may be an offset into the vertex + buffer instead. + + QGLAttributeValue encapsulates these four values so that they can + be easily manipulated as a set during OpenGL painting operations. + Constructors are provided for converting QArray and + QCustomDataArray objects into an attribute value. + + Because the data() value is a raw pointer to arbitrary memory, + care should be taken that the memory remains valid until the + QGLAttributeValue is no longer required. + + \sa QGLAttributeDescription, QArray, QCustomDataArray +*/ + +/*! + \fn QGLAttributeValue::QGLAttributeValue() + + Constructs a null attribute value with default parameters of + tupleSize(), and stride() set to zero, type() set to GL_FLOAT, + and data() set to null. + + \sa isNull() +*/ + +/*! + \fn QGLAttributeValue::QGLAttributeValue(const QArray<float>& array) + + Constructs an attribute value that refers to the contents of \a array, + setting tupleSize() to 1, type() to GL_FLOAT, and stride() to zero. + + The \a array must not be destroyed or modified until the attribute + value is no longer required. +*/ + +/*! + \fn QGLAttributeValue::QGLAttributeValue(const QArray<QVector2D>& array) + + Constructs an attribute value that refers to the contents of \a array, + setting tupleSize() to 2, type() to GL_FLOAT, and stride() to zero. + + The \a array must not be destroyed or modified until the attribute + value is no longer required. +*/ + +/*! + \fn QGLAttributeValue::QGLAttributeValue(const QArray<QVector3D>& array) + + Constructs an attribute value that refers to the contents of \a array, + setting tupleSize() to 3, type() to GL_FLOAT, and stride() to zero. + + The \a array must not be destroyed or modified until the attribute + value is no longer required. +*/ + +/*! + \fn QGLAttributeValue::QGLAttributeValue(const QArray<QVector4D>& array) + + Constructs an attribute value that refers to the contents of \a array, + setting tupleSize() to 4, type() to GL_FLOAT, and stride() to zero. + + The \a array must not be destroyed or modified until the attribute + value is no longer required. +*/ + +/*! + \fn QGLAttributeValue::QGLAttributeValue(const QArray<QColor4ub>& array) + + Constructs an attribute value that refers to the contents of \a array, + setting tupleSize() to 4, type() to GL_UNSIGNED_BYTE, and stride() to zero. + + The \a array must not be destroyed or modified until the attribute + value is no longer required. +*/ + +/*! + Constructs an attribute value that refers to the contents of \a array. + The tupleSize() and type() of the attribute value will be set according + to the QCustomDataArray::elementType() of \a array. + + The \a array must not be destroyed or modified until the attribute + value is no longer required. +*/ +QGLAttributeValue::QGLAttributeValue(const QCustomDataArray& array) + : m_tupleSize(0), m_type(GL_FLOAT), m_stride(0) + , m_data(array.data()), m_count(array.count()) +{ + switch (array.elementType()) { + case QCustomDataArray::Float: + m_tupleSize = 1; + break; + case QCustomDataArray::Vector2D: + m_tupleSize = 2; + break; + case QCustomDataArray::Vector3D: + m_tupleSize = 3; + break; + case QCustomDataArray::Vector4D: + m_tupleSize = 4; + break; + case QCustomDataArray::Color: + m_tupleSize = 4; + m_type = GL_UNSIGNED_BYTE; + break; + } +} + +/*! + \fn QGLAttributeValue::QGLAttributeValue(int tupleSize, GLenum type, int stride, const void *data, int count) + + Constructs an attribute value with the fields \a tupleSize, \a type, + \a stride, \a data, and \a count. +*/ + +/*! + \fn QGLAttributeValue::QGLAttributeValue(int tupleSize, GLenum type, int stride, int offset, int count) + + Constructs an attribute value with the fields \a tupleSize, \a type, + \a stride, \a offset, and \a count. +*/ + +/*! + \fn QGLAttributeValue::QGLAttributeValue(const QGLAttributeDescription& description, const void *data, int count) + + Constructs an attribute value with the supplied \a description, + \a data, and \a count. +*/ + +/*! + \fn QGLAttributeValue::QGLAttributeValue(const QGLAttributeDescription& description, int offset, int count) + + Constructs an attribute value with the supplied \a description, + \a offset, and \a count. +*/ + +/*! + \fn bool QGLAttributeValue::isNull() const + + Returns true if tupleSize() is zero, which indicates an unset + attribute value; false otherwise. + + Note: it is possible for data() to be null, but isNull() returns true. + This can happen when data() is actually a zero offset into a + vertex buffer. +*/ + +/*! + \fn QGLAttributeDescription QGLAttributeValue::description(QGL::VertexAttribute attribute) const + + Returns the description of this value, tagged with \a attribute. + + \sa type() +*/ + +/*! + \fn GLenum QGLAttributeValue::type() const + + Returns the component type for this attribute value. The default + value is GL_FLOAT. + + \sa sizeOfType(), description() +*/ + +/*! + Returns the size in bytes of type(). + + \sa type(), tupleSize() +*/ +int QGLAttributeValue::sizeOfType() const +{ + switch (m_type) { + case GL_BYTE: return int(sizeof(GLbyte)); + case GL_UNSIGNED_BYTE: return int(sizeof(GLubyte)); + case GL_SHORT: return int(sizeof(GLshort)); + case GL_UNSIGNED_SHORT: return int(sizeof(GLushort)); + case GL_INT: return int(sizeof(GLint)); + case GL_UNSIGNED_INT: return int(sizeof(GLuint)); + case GL_FLOAT: return int(sizeof(GLfloat)); +#if defined(GL_DOUBLE) && !defined(QT_OPENGL_ES) + case GL_DOUBLE: return int(sizeof(GLdouble)); +#endif + default: return 0; + } +} + +/*! + \fn int QGLAttributeValue::tupleSize() const + + Returns the tuple size of this attribute in components. For example, + a return value of 3 indicates a vector of 3-dimensional values. + If tupleSize() is zero, then this attribute value is null. + + \sa isNull(), sizeOfType() +*/ + +/*! + \fn int QGLAttributeValue::stride() const + + Returns the stride in bytes from one vertex element to the + next for this attribute value. The default value of 0 indicates + that the elements are tightly packed within the data() array. +*/ + +/*! + \fn const void *QGLAttributeValue::data() const + + Returns the data pointer for the elements in this attribute value. +*/ + +/*! + \fn int QGLAttributeValue::count() const + + Returns the count of vertex elements in this attribute value; + zero if the count is unknown. +*/ + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qglattributevalue.h b/src/threed/arrays/qglattributevalue.h new file mode 100644 index 000000000..cf4c9319b --- /dev/null +++ b/src/threed/arrays/qglattributevalue.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLATTRIBUTEVALUE_H +#define QGLATTRIBUTEVALUE_H + +#include "qglattributedescription.h" +#include "qcustomdataarray.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLVertexBundle; + +class Q_QT3D_EXPORT QGLAttributeValue +{ +public: + QGLAttributeValue(); + QGLAttributeValue(const QArray<float>& array); + QGLAttributeValue(const QArray<QVector2D>& array); + QGLAttributeValue(const QArray<QVector3D>& array); + QGLAttributeValue(const QArray<QVector4D>& array); + QGLAttributeValue(const QArray<QColor4ub>& array); + QGLAttributeValue(const QCustomDataArray& array); + QGLAttributeValue(int tupleSize, GLenum type, int stride, const void *data, int count = 0); + QGLAttributeValue(int tupleSize, GLenum type, int stride, int offset, int count = 0); + QGLAttributeValue(const QGLAttributeDescription& description, const void *data, int count = 0); + QGLAttributeValue(const QGLAttributeDescription& description, int offset, int count = 0); + + bool isNull() const; + + QGLAttributeDescription description(QGL::VertexAttribute attribute) const; + GLenum type() const; + int sizeOfType() const; + int tupleSize() const; + int stride() const; + const void *data() const; + int count() const; + +private: + int m_tupleSize; + GLenum m_type; + int m_stride; + const void *m_data; + int m_count; + + void setStride(int stride) { m_stride = stride; } + void setOffset(int offset) + { m_data = reinterpret_cast<const void *>(offset); } + + friend class QGLVertexBundle; +}; + +inline QGLAttributeValue::QGLAttributeValue() + : m_tupleSize(0), m_type(GL_FLOAT), m_stride(0), m_data(0), m_count(0) +{ +} + +inline QGLAttributeValue::QGLAttributeValue(const QArray<float>& array) + : m_tupleSize(1), m_type(GL_FLOAT), m_stride(0) + , m_data(array.constData()), m_count(array.count()) +{ +} + +inline QGLAttributeValue::QGLAttributeValue(const QArray<QVector2D>& array) + : m_tupleSize(2), m_type(GL_FLOAT), m_stride(0) + , m_data(array.constData()), m_count(array.count()) +{ +} + +inline QGLAttributeValue::QGLAttributeValue(const QArray<QVector3D>& array) + : m_tupleSize(3), m_type(GL_FLOAT), m_stride(0) + , m_data(array.constData()), m_count(array.count()) +{ +} + +inline QGLAttributeValue::QGLAttributeValue(const QArray<QVector4D>& array) + : m_tupleSize(4), m_type(GL_FLOAT), m_stride(0) + , m_data(array.constData()), m_count(array.count()) +{ +} + +inline QGLAttributeValue::QGLAttributeValue(const QArray<QColor4ub>& array) + : m_tupleSize(4), m_type(GL_UNSIGNED_BYTE), m_stride(0) + , m_data(array.constData()), m_count(array.count()) +{ +} + +inline QGLAttributeValue::QGLAttributeValue + (int tupleSize, GLenum type, int stride, const void *data, int count) + : m_tupleSize(tupleSize), m_type(type), m_stride(stride) + , m_data(data), m_count(count) +{ + Q_ASSERT(tupleSize >= 1 && tupleSize <= 4); +} + +inline QGLAttributeValue::QGLAttributeValue + (int tupleSize, GLenum type, int stride, int offset, int count) + : m_tupleSize(tupleSize), m_type(type), m_stride(stride) + , m_data(reinterpret_cast<const void *>(offset)), m_count(count) +{ + Q_ASSERT(tupleSize >= 1 && tupleSize <= 4); +} + +inline QGLAttributeValue::QGLAttributeValue + (const QGLAttributeDescription& description, const void *data, int count) + : m_tupleSize(description.tupleSize()), m_type(description.type()) + , m_stride(description.stride()), m_data(data), m_count(count) +{ +} + +inline QGLAttributeValue::QGLAttributeValue + (const QGLAttributeDescription& description, int offset, int count) + : m_tupleSize(description.tupleSize()), m_type(description.type()) + , m_stride(description.stride()) + , m_data(reinterpret_cast<const void *>(offset)) + , m_count(count) +{ +} + +inline bool QGLAttributeValue::isNull() const +{ + return m_tupleSize == 0; +} + +inline QGLAttributeDescription QGLAttributeValue::description(QGL::VertexAttribute attribute) const +{ + if (!isNull()) { + return QGLAttributeDescription(attribute, m_tupleSize, m_type, m_stride); + } else { + QGLAttributeDescription desc; + desc.setAttribute(attribute); + return desc; + } +} + +inline GLenum QGLAttributeValue::type() const +{ + return m_type; +} + +inline int QGLAttributeValue::tupleSize() const +{ + return m_tupleSize; +} + +inline int QGLAttributeValue::stride() const +{ + return m_stride; +} + +inline const void *QGLAttributeValue::data() const +{ + return m_data; +} + +inline int QGLAttributeValue::count() const +{ + return m_count; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qglindexbuffer.cpp b/src/threed/arrays/qglindexbuffer.cpp new file mode 100644 index 000000000..b50b19257 --- /dev/null +++ b/src/threed/arrays/qglindexbuffer.cpp @@ -0,0 +1,777 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglindexbuffer.h" +#include "qglpainter.h" +#include "qglpainter_p.h" +#include "qglext_p.h" +#include <QtOpenGL/qgl.h> +#include <QtCore/qatomic.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLIndexBuffer + \brief The QGLIndexBuffer class manages uploading of index arrays into a GL server. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays +*/ + +#ifdef QT_OPENGL_ES + +static bool qt_has_uint_buffers() +{ + static bool done = false; + static bool answer = false; + if (!done) { + QGLExtensionChecker extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS))); + answer = extensions.match("GL_OES_element_index_uint"); + done = true; + } + return answer; +} + +#endif + +class QGLIndexBufferPrivate +{ +public: + QGLIndexBufferPrivate() + : indexCount(0) + , elementType(GL_UNSIGNED_SHORT) + , buffer(QGLBuffer::IndexBuffer) +#ifdef QT_OPENGL_ES + , hasIntBuffers(qt_has_uint_buffers()) +#else + , hasIntBuffers(true) +#endif + { + ref = 1; + } + + QBasicAtomicInt ref; + int indexCount; + QArray<ushort> indexesShort; + QArray<uint> indexesInt; + GLenum elementType; + QGLBuffer buffer; + bool hasIntBuffers; + + void append(const QGLIndexBufferPrivate *other, uint offset, int start); + uint headIndex(int posn) const; + uint tailIndex(int posn) const; +}; + +/*! + Creates a new index buffer. +*/ +QGLIndexBuffer::QGLIndexBuffer() + : d_ptr(new QGLIndexBufferPrivate) +{ +} + +/*! + Creates a copy of \a other. Note that this just copies a reference + to the index buffer. Any modifications to the copy will also + affect the original object. +*/ +QGLIndexBuffer::QGLIndexBuffer(const QGLIndexBuffer& other) + : d_ptr(other.d_ptr) +{ + d_ptr->ref.ref(); +} + +/*! + Destroys this index buffer if this object is the last reference to it. +*/ +QGLIndexBuffer::~QGLIndexBuffer() +{ + if (!d_ptr->ref.deref()) + delete d_ptr; +} + +/*! + Assigns \a other to this object. Note that this just assigns a + reference to the \a other index buffer. Any modifications to this + object will also affect \a other. +*/ +QGLIndexBuffer& QGLIndexBuffer::operator=(const QGLIndexBuffer& other) +{ + if (d_ptr != other.d_ptr) { + if (!d_ptr->ref.deref()) + delete d_ptr; + d_ptr = other.d_ptr; + d_ptr->ref.ref(); + } + return *this; +} + +/*! + Returns the indexes in this buffer as an array of ushort values. + + Returns an empty array if type() is not \c GL_UNSIGNED_SHORT or the + buffer has already been uploaded. +*/ +QArray<ushort> QGLIndexBuffer::indexesUShort() const +{ + Q_D(const QGLIndexBuffer); + return d->indexesShort; +} + +/*! + Returns the indexes in this buffer as an array of uint values. + + Returns an empty array if type() is not \c GL_UNSIGNED_INT or the + buffer has already been uploaded. +*/ +QArray<uint> QGLIndexBuffer::indexesUInt() const +{ + Q_D(const QGLIndexBuffer); + return d->indexesInt; +} + +/*! + Returns the usage pattern for this index buffer. + The default value is QGLBuffer::StaticDraw. + + \sa setUsagePattern() +*/ +QGLBuffer::UsagePattern QGLIndexBuffer::usagePattern() const +{ + Q_D(const QGLIndexBuffer); + return d->buffer.usagePattern(); +} + +/*! + Sets the usage pattern for this index buffer to \a value. + This function must be called before upload() for the \a value + to take effect. + + \sa usagePattern(), upload() +*/ +void QGLIndexBuffer::setUsagePattern(QGLBuffer::UsagePattern value) +{ + Q_D(QGLIndexBuffer); + d->buffer.setUsagePattern(value); +} + +static QArray<ushort> qt_qarray_uint_to_ushort(const QArray<uint> &array) +{ + QArray<ushort> result; + const uint *values = array.constData(); + int size = array.size(); + bool largeValue = false; + result.reserve(size); + while (size-- > 0) { + uint value = *values++; + if (ushort(value) != value) + largeValue = true; + result.append(ushort(value)); + } + if (largeValue) + qWarning("QGLIndexBuffer::setIndexes: large 32-bit value provided to a 16-bit only buffer"); + return result; +} + +/*! + Sets the index \a values in this index buffer, replacing the + entire current contents. + + If the index buffer has been uploaded to the GL server, then this + function must be called with a current GL context that is compatible + with the uploaded buffer. + + \sa replaceIndexes() +*/ +void QGLIndexBuffer::setIndexes(const QArray<ushort>& values) +{ + Q_D(QGLIndexBuffer); + if (d->buffer.isCreated()) { + d->buffer.bind(); + d->buffer.allocate(values.constData(), values.size() * sizeof(ushort)); + d->buffer.release(); + // The element type may have changed from int to ushort. + d->elementType = GL_UNSIGNED_SHORT; + } else { + d->indexesShort = values; + d->elementType = GL_UNSIGNED_SHORT; + d->indexesInt = QArray<uint>(); + } + d->indexCount = values.size(); +} + +/*! + Sets the index \a values in this index buffer, replacing the + entire current contents. + + If the index buffer has been uploaded to the GL server, then this + function must be called with a current GL context that is compatible + with the uploaded buffer. + + OpenGL/ES systems usually do not support 32-bit index values unless + they have a special extension for that purpose. On systems without + 32-bit index values, this function will need to convert all values + to 16-bit which may incur a performance penalty and lose information. + + \sa replaceIndexes() +*/ +void QGLIndexBuffer::setIndexes(const QArray<uint>& values) +{ + Q_D(QGLIndexBuffer); + if (d->buffer.isCreated()) { + if (d->hasIntBuffers) { + d->buffer.bind(); + d->buffer.allocate(values.constData(), values.size() * sizeof(int)); + d->buffer.release(); + // The element type may have changed from ushort to int. + d->elementType = GL_UNSIGNED_INT; + } else { + QArray<ushort> svalues = qt_qarray_uint_to_ushort(values); + d->buffer.bind(); + d->buffer.allocate(svalues.constData(), svalues.size() * sizeof(ushort)); + d->buffer.release(); + } + } else if (d->hasIntBuffers) { + d->indexesInt = values; + d->elementType = GL_UNSIGNED_INT; + d->indexesShort = QArray<ushort>(); + } else { + d->indexesShort = qt_qarray_uint_to_ushort(values); + d->elementType = GL_UNSIGNED_SHORT; + d->indexesInt = QArray<uint>(); + } + d->indexCount = values.size(); +} + +/*! + Replaces the elements of this index buffer, starting at \a index, + with the contents of \a values. All other elements keep their + current values. + + If the index buffer has been uploaded to the GL server, then this + function must be called with a current GL context that is compatible + with the uploaded buffer. + + The index buffer must have been originally created with the + ushort element type. + + \sa setIndexes() +*/ +void QGLIndexBuffer::replaceIndexes(int index, const QArray<ushort>& values) +{ + Q_D(QGLIndexBuffer); + Q_ASSERT_X(d->elementType == GL_UNSIGNED_SHORT, + "QGLIndexBuffer::replaceIndexes()", + "buffer created with int element type, replacing with ushort"); + if (d->elementType != GL_UNSIGNED_SHORT) + return; + if (d->buffer.isCreated()) { + d->buffer.bind(); + d->buffer.write(index * sizeof(ushort), + values.constData(), values.size() * sizeof(ushort)); + d->buffer.release(); + } else { + d->indexesShort.replace(index, values.constData(), values.size()); + d->indexCount = d->indexesShort.size(); + } +} + +/*! + Replaces the elements of this index buffer, starting at \a index, + with the contents of \a values. All other elements keep their + current values. + + If the index buffer has been uploaded to the GL server, then this + function must be called with a current GL context that is compatible + with the uploaded buffer. + + The index buffer must have been originally created with the + int element type. + + OpenGL/ES systems usually do not support 32-bit index values unless + they have a special extension for that purpose. On systems without + 32-bit index values, this function will need to convert all values + to 16-bit which may incur a performance penalty and lose information. + + \sa setIndexes() +*/ +void QGLIndexBuffer::replaceIndexes(int index, const QArray<uint>& values) +{ + Q_D(QGLIndexBuffer); + Q_ASSERT_X(d->elementType == GL_UNSIGNED_INT || !d->hasIntBuffers, + "QGLIndexBuffer::replaceIndexes()", + "buffer created with ushort element type, replacing with int"); + if (d->elementType != GL_UNSIGNED_INT && d->hasIntBuffers) + return; + if (d->buffer.isCreated()) { + if (d->hasIntBuffers) { + d->buffer.bind(); + d->buffer.write(index * sizeof(int), + values.constData(), values.size() * sizeof(int)); + d->buffer.release(); + } else { + QArray<ushort> svalues = qt_qarray_uint_to_ushort(values); + d->buffer.bind(); + d->buffer.write(index * sizeof(ushort), + svalues.constData(), + svalues.size() * sizeof(ushort)); + d->buffer.release(); + } + } else if (d->elementType == GL_UNSIGNED_INT) { + d->indexesInt.replace(index, values.constData(), values.size()); + d->indexCount = d->indexesInt.size(); + } else { + QArray<ushort> svalues = qt_qarray_uint_to_ushort(values); + d->indexesShort.replace(index, svalues.constData(), svalues.size()); + d->indexCount = d->indexesShort.size(); + } +} + +/*! + Returns the element type for this index buffer, \c{GL_UNSIGNED_SHORT} + or \c{GL_UNSIGNED_INT}. +*/ +GLenum QGLIndexBuffer::elementType() const +{ + Q_D(const QGLIndexBuffer); + return d->elementType; +} + +/*! + Returns the number of indexes in this index buffer. +*/ +int QGLIndexBuffer::indexCount() const +{ + Q_D(const QGLIndexBuffer); + return d->indexCount; +} + +/*! + \fn bool QGLIndexBuffer::isEmpty() const + + Returns true if indexCount() is zero; false otherwise. +*/ + +/*! + Uploads the index data specified by a previous setIndexes() + call into the GL server as an index buffer object. + + Returns true if the data could be uploaded; false if index buffer + objects are not supported or there is insufficient memory to complete + the request. Returns true if the data was already uploaded. + + Once the index data has been uploaded, the client-side copies of + the data arrays will be released. If the index data could not be + uploaded, then it is retained client-side. This way, regardless of + whether the data could be uploaded or not, QGLPainter::draw() can + be used to support drawing of primitives using this object. + + \sa isUploaded(), setIndexes(), QGLPainter::draw() +*/ +bool QGLIndexBuffer::upload() +{ + Q_D(QGLIndexBuffer); + if (d->buffer.isCreated()) + return true; + if (!d->buffer.create()) + return false; + d->buffer.bind(); + if (d->elementType == GL_UNSIGNED_SHORT) { + d->buffer.allocate(d->indexesShort.constData(), + d->indexesShort.size() * sizeof(ushort)); + d->indexesShort = QArray<ushort>(); + } else { + d->buffer.allocate(d->indexesInt.constData(), + d->indexesInt.size() * sizeof(int)); + d->indexesInt = QArray<uint>(); + } + d->buffer.release(); + return true; +} + +/*! + Returns true if the index data specified by previous a setIndexes() + call has been uploaded into the GL server; false otherwise. + + \sa upload(), setIndexes() +*/ +bool QGLIndexBuffer::isUploaded() const +{ + Q_D(const QGLIndexBuffer); + return d->buffer.isCreated(); +} + +/*! + Returns the QGLBuffer in use by this index buffer object, + so that its properties or contents can be modified directly. + + \sa isUploaded() +*/ +QGLBuffer QGLIndexBuffer::buffer() const +{ + Q_D(const QGLIndexBuffer); + return d->buffer; +} + +/*! + Binds this index buffer to the current GL context. Returns false if + binding was not possible, usually because upload() has not been called. + + The buffer must be bound to the same QGLContext current when upload() + was called, or to another QGLContext that is sharing with it. + Otherwise, false will be returned from this function. + + \sa release(), upload() +*/ +bool QGLIndexBuffer::bind() +{ + Q_D(QGLIndexBuffer); + return d->buffer.bind(); +} + +/*! + Releases this index buffer from the current GL context. + + This function must be called with the same QGLContext current + as when bind() was called on the index buffer. + + \sa bind() +*/ +void QGLIndexBuffer::release() +{ + Q_D(QGLIndexBuffer); + d->buffer.release(); +} + +void QGLIndexBufferPrivate::append + (const QGLIndexBufferPrivate *other, uint offset, int start) +{ + if (elementType == GL_UNSIGNED_SHORT && + other->elementType == GL_UNSIGNED_SHORT) { + // Both buffers are ushort. + const ushort *data = other->indexesShort.constData() + start; + int count = other->indexesShort.count() - start; + indexesShort.reserve(indexesShort.count() + count); + indexCount += count; + while (count-- > 0) + indexesShort.append(ushort(*data++ + offset)); + } else if (elementType == GL_UNSIGNED_SHORT) { + // Only first buffer is ushort: convert it to int first. + const ushort *indexes = indexesShort.constData(); + int count = indexesShort.count(); + indexesInt.reserve(count + other->indexesInt.count()); + while (count-- > 0) + indexesInt.append(*indexes++); + indexesShort = QArray<ushort>(); + elementType = GL_UNSIGNED_INT; + const uint *data = other->indexesInt.constData() + start; + count = other->indexesInt.count() - start; + indexCount += count; + while (count-- > 0) + indexesInt.append(*data++ + offset); + } else if (other->elementType == GL_UNSIGNED_SHORT) { + // Only second buffer is ushort. + const ushort *data = other->indexesShort.constData() + start; + int count = other->indexesShort.count() - start; + indexesInt.reserve(indexesInt.count() + count); + indexCount += count; + while (count-- > 0) + indexesInt.append(*data++ + offset); + } else { + // Neither buffer is ushort. + const uint *data = other->indexesInt.constData() + start; + int count = other->indexesInt.count() - start; + indexesInt.reserve(indexesInt.count() + count); + indexCount += count; + while (count-- > 0) + indexesInt.append(*data++ + offset); + } +} + +uint QGLIndexBufferPrivate::headIndex(int posn) const +{ + if (indexCount <= posn) + return uint(-1); + if (elementType == GL_UNSIGNED_SHORT) + return indexesShort[posn]; + else + return indexesInt[posn]; +} + +uint QGLIndexBufferPrivate::tailIndex(int posn) const +{ + if (indexCount <= posn) + return uint(-1); + if (elementType == GL_UNSIGNED_SHORT) + return indexesShort[indexCount - posn - 1]; + else + return indexesInt[indexCount - posn - 1]; +} + +/*! + Appends the contents of \a buffer to this index buffer and adds + \a offset to all of the entries in \a buffer. + + This function is typically used to combine multiple geometry meshes + into a single mesh that can be bound as a single buffer. + + The request is ignored if this index buffer or \a buffer have already + been uploaded, or \a buffer is this index buffer. + + \sa isUploaded(), setIndexes() +*/ +void QGLIndexBuffer::append(const QGLIndexBuffer &buffer, uint offset) +{ + Q_D(QGLIndexBuffer); + const QGLIndexBufferPrivate *dbuf = buffer.d_ptr; + + // Bail out if the buffers are uploaded or identical. + if (d->buffer.isCreated() || dbuf->buffer.isCreated()) + return; + if (d == dbuf) + return; + + // Append the two index arrays. + d->append(dbuf, offset, 0); +} + +/*! + Appends the contents of \a buffer to this index buffer and adds + \a offset to all of the entries in \a buffer. + + The two buffers will be merged at the join point according to + \a combineMode. For example, if \a combineMode is QGL::TriangleStrip, + then the result will be a single triangle strip. Indexes are + dropped from the front of \a buffer as necessary to correctly + merge the buffers. + + This function is typically used to combine multiple geometry meshes + into a single mesh that can be bound as a single buffer. + + The request is ignored if this index buffer or \a buffer have already + been uploaded, or \a buffer is this index buffer. + + \sa isUploaded(), setIndexes() +*/ +void QGLIndexBuffer::append + (const QGLIndexBuffer &buffer, uint offset, QGL::DrawingMode combineMode) +{ + Q_D(QGLIndexBuffer); + const QGLIndexBufferPrivate *dbuf = buffer.d_ptr; + + // Bail out if the buffers are uploaded or identical. + if (d->buffer.isCreated() || dbuf->buffer.isCreated()) + return; + if (d == dbuf) + return; + + // Determine how to combine the buffers. + switch (int(combineMode)) { + case QGL::Points: + case QGL::Lines: + case QGL::Triangles: + case QGL::LinesAdjacency: + case QGL::TrianglesAdjacency: + case 0x0007: // GL_QUADS + // These can be done by just appending the raw data with no changes. + d->append(dbuf, offset, 0); + break; + + case QGL::LineLoop: + case QGL::LineStrip: + case 0x0009: // GL_POLYGON + // Join the last index of the first buffer to the first + // index of the second buffer to continue the loop or strip. + if (d->tailIndex(0) == (dbuf->headIndex(0) + offset)) + d->append(dbuf, offset, 1); + else + d->append(dbuf, offset, 0); + break; + + case QGL::TriangleStrip: + // Join the last two indexes of the first buffer to the first + // two indexes of the second buffer to continue the strip. + // It is possible that the first two indexes of the second + // buffer may be reversed for strip continuation depending + // upon whether the first strip is odd or even in length. + if (d->tailIndex(1) == (dbuf->headIndex(0) + offset) && + d->tailIndex(0) == (dbuf->headIndex(1) + offset)) + d->append(dbuf, offset, 2); + else if (d->tailIndex(1) == (dbuf->headIndex(1) + offset) && + d->tailIndex(0) == (dbuf->headIndex(0) + offset)) + d->append(dbuf, offset, 2); + else + d->append(dbuf, offset, 0); + break; + + case 0x0008: // GL_QUAD_STRIP + // Join the last two indexes of the first buffer to the first + // two indexes of the second buffer to continue the strip. + if (d->tailIndex(1) == (dbuf->headIndex(0) + offset) && + d->tailIndex(0) == (dbuf->headIndex(1) + offset)) + d->append(dbuf, offset, 2); + else + d->append(dbuf, offset, 0); + break; + + case QGL::TriangleFan: + // The first index of both buffers should be the same, and the + // last index of the first buffer should be the same as the second + // index of the second buffer. + if (d->headIndex(0) == (dbuf->headIndex(0) + offset) && + d->tailIndex(0) == (dbuf->headIndex(1) + offset)) + d->append(dbuf, offset, 2); + else + d->append(dbuf, offset, 0); + break; + + case QGL::LineStripAdjacency: + // Join the last three indexes of the first buffer to the first + // three indexes of the second buffer to continue the strip. + if (d->tailIndex(2) == (dbuf->headIndex(0) + offset) && + d->tailIndex(1) == (dbuf->headIndex(1) + offset) && + d->tailIndex(0) == (dbuf->headIndex(2) + offset)) + d->append(dbuf, offset, 3); + else + d->append(dbuf, offset, 0); + break; + + case QGL::TriangleStripAdjacency: + // Fourth last and second last of first buffer need to be the + // same as the first and third of the second buffer. + if (d->tailIndex(3) == (dbuf->headIndex(0) + offset) && + d->tailIndex(1) == (dbuf->headIndex(2) + offset)) + d->append(dbuf, offset, 4); + else + d->append(dbuf, offset, 0); + break; + + default: + qWarning("QGLIndexBuffer::append: unknown drawing mode 0x%04x", + int(combineMode)); + break; + } +} + +/*! + \overload + + Draws primitives using vertices from the arrays specified by + setVertexAttribute(). The type of primitive to draw is + specified by \a mode. + + This operation will consume all of the elements of \a indexes, + which are used to index into the enabled arrays. + + If \a indexes has not been uploaded to the GL server as an index + buffer, then this function will draw using a client-side array. + + \sa update(), QGLIndexBuffer::upload() +*/ +void QGLPainter::draw(QGL::DrawingMode mode, const QGLIndexBuffer& indexes) +{ + QGLIndexBufferPrivate *d = const_cast<QGLIndexBufferPrivate *>(indexes.d_func()); + update(); + GLuint id = d->buffer.bufferId(); + if (id != d_ptr->boundIndexBuffer) { + if (id) + d->buffer.bind(); + else + QGLBuffer::release(QGLBuffer::IndexBuffer); + d_ptr->boundIndexBuffer = id; + } + if (id) { + glDrawElements(GLenum(mode), d->indexCount, d->elementType, 0); + } else if (d->elementType == GL_UNSIGNED_SHORT) { + glDrawElements(GLenum(mode), d->indexCount, GL_UNSIGNED_SHORT, + d->indexesShort.constData()); + } else { + glDrawElements(GLenum(mode), d->indexCount, GL_UNSIGNED_INT, + d->indexesInt.constData()); + } +} + +/*! + \overload + + Draws primitives using vertices from the arrays specified by + setVertexAttribute(). The type of primitive to draw is + specified by \a mode. + + This operation will consume \a count elements of \a indexes, + starting at \a offset, which are used to index into the enabled arrays. + + If \a indexes has not been uploaded to the GL server as an index + buffer, then this function will draw using a client-side array. + + \sa update(), QGLIndexBuffer::upload() +*/ +void QGLPainter::draw(QGL::DrawingMode mode, const QGLIndexBuffer& indexes, int offset, int count) +{ + QGLIndexBufferPrivate *d = const_cast<QGLIndexBufferPrivate *>(indexes.d_func()); + update(); + GLuint id = d->buffer.bufferId(); + if (id != d_ptr->boundIndexBuffer) { + if (id) + d->buffer.bind(); + else + QGLBuffer::release(QGLBuffer::IndexBuffer); + d_ptr->boundIndexBuffer = id; + } + if (id) { + if (d->elementType == GL_UNSIGNED_SHORT) { + glDrawElements(GLenum(mode), count, GL_UNSIGNED_SHORT, + reinterpret_cast<const void *>(offset * sizeof(ushort))); + } else { + glDrawElements(GLenum(mode), count, GL_UNSIGNED_INT, + reinterpret_cast<const void *>(offset * sizeof(int))); + } + } else if (d->elementType == GL_UNSIGNED_SHORT) { + glDrawElements(GLenum(mode), count, GL_UNSIGNED_SHORT, + d->indexesShort.constData() + offset); + } else { + glDrawElements(GLenum(mode), count, GL_UNSIGNED_INT, + d->indexesInt.constData() + offset); + } +} + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qglindexbuffer.h b/src/threed/arrays/qglindexbuffer.h new file mode 100644 index 000000000..c3e92e4f8 --- /dev/null +++ b/src/threed/arrays/qglindexbuffer.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLINDEXBUFFER_H +#define QGLINDEXBUFFER_H + +#include <QtOpenGL/qgl.h> +#include <QtOpenGL/qglbuffer.h> +#include "qglnamespace.h" +#include "qarray.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLIndexBufferPrivate; +class QGLPainter; + +class Q_QT3D_EXPORT QGLIndexBuffer +{ +public: + QGLIndexBuffer(); + QGLIndexBuffer(const QGLIndexBuffer& other); + ~QGLIndexBuffer(); + + QGLIndexBuffer& operator=(const QGLIndexBuffer& other); + + QGLBuffer::UsagePattern usagePattern() const; + void setUsagePattern(QGLBuffer::UsagePattern value); + + QArray<ushort> indexesUShort() const; + QArray<uint> indexesUInt() const; + + void setIndexes(const QArray<ushort>& values); + void setIndexes(const QArray<uint>& values); + + void replaceIndexes(int index, const QArray<ushort>& values); + void replaceIndexes(int index, const QArray<uint>& values); + + GLenum elementType() const; + + int indexCount() const; + bool isEmpty() const { return indexCount() == 0; } + + bool upload(); + bool isUploaded() const; + + QGLBuffer buffer() const; + + bool bind(); + void release(); + + void append(const QGLIndexBuffer &buffer, uint offset); + void append(const QGLIndexBuffer &buffer, uint offset, QGL::DrawingMode combineMode); + +private: + QGLIndexBufferPrivate *d_ptr; + + Q_DECLARE_PRIVATE(QGLIndexBuffer) + + friend class QGLPainter; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qglvertexbundle.cpp b/src/threed/arrays/qglvertexbundle.cpp new file mode 100644 index 000000000..000c93c84 --- /dev/null +++ b/src/threed/arrays/qglvertexbundle.cpp @@ -0,0 +1,495 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglvertexbundle.h" +#include "qglvertexbundle_p.h" +#include "qglabstracteffect.h" +#include <QtCore/qlist.h> +#include <QtCore/qatomic.h> +#include <QtOpenGL/qglshaderprogram.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLVertexBundle + \brief The QGLVertexBundle class bundles vertex attribute arrays for efficient uploading into a GL server. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays + + QGLVertexBundle provides an implementation of a static vertex + buffer, where the vertex attributes are supplied once at construction + time and then never modified until the bundle is destroyed. + When the vertex attributes are sent ot the GL server by upload(), + they may be repacked for greater drawing efficiency. + + For general-purpose vertex buffers that can be allocated and modified + in-place, use QGLBuffer instead. +*/ + +/*! + Constructs a new vertex bundle. +*/ +QGLVertexBundle::QGLVertexBundle() + : d_ptr(new QGLVertexBundlePrivate()) +{ +} + +/*! + Creates a copy of \a other. Note that this just copies a reference + to the vertex bundle. Any modifications to the copy will also + affect the original object. +*/ +QGLVertexBundle::QGLVertexBundle(const QGLVertexBundle& other) + : d_ptr(other.d_ptr) +{ + d_ptr->ref.ref(); +} + +/*! + Destroys this vertex bundle if this object is the last reference to it. +*/ +QGLVertexBundle::~QGLVertexBundle() +{ + if (!d_ptr->ref.deref()) + delete d_ptr; +} + +/*! + Assigns \a other to this object. Note that this just assigns a + reference to the \a other vertex bundle. Any modifications to this + object will also affect \a other. +*/ +QGLVertexBundle& QGLVertexBundle::operator=(const QGLVertexBundle& other) +{ + if (d_ptr != other.d_ptr) { + if (!d_ptr->ref.deref()) + delete d_ptr; + d_ptr = other.d_ptr; + d_ptr->ref.ref(); + } + return *this; +} + +/*! + Adds the floating-point array \a value to this vertex bundle as the + data for \a attribute. + + \sa upload() +*/ +void QGLVertexBundle::addAttribute + (QGL::VertexAttribute attribute, const QArray<float>& value) +{ + Q_D(QGLVertexBundle); + if (!d->buffer.isCreated()) { + d->attributeSet.insert(attribute); + d->attributes += + new QGLVertexBundleFloatAttribute(attribute, value); + d->vertexCount = qMax(d->vertexCount, value.count()); + } +} + +/*! + Adds the 2D vector array \a value to this vertex bundle as the + data for \a attribute. + + \sa upload() +*/ +void QGLVertexBundle::addAttribute + (QGL::VertexAttribute attribute, const QArray<QVector2D>& value) +{ + Q_D(QGLVertexBundle); + if (!d->buffer.isCreated()) { + d->attributeSet.insert(attribute); + d->attributes += + new QGLVertexBundleVector2DAttribute(attribute, value); + d->vertexCount = qMax(d->vertexCount, value.count()); + } +} + +/*! + Adds the 3D vector array \a value to this vertex bundle as the + data for \a attribute. + + \sa upload() +*/ +void QGLVertexBundle::addAttribute + (QGL::VertexAttribute attribute, const QArray<QVector3D>& value) +{ + Q_D(QGLVertexBundle); + if (!d->buffer.isCreated()) { + d->attributeSet.insert(attribute); + d->attributes += + new QGLVertexBundleVector3DAttribute(attribute, value); + d->vertexCount = qMax(d->vertexCount, value.count()); + } +} + +/*! + Adds the 4D vector array \a value to this vertex bundle as the + data for \a attribute. + + \sa upload() +*/ +void QGLVertexBundle::addAttribute + (QGL::VertexAttribute attribute, const QArray<QVector4D>& value) +{ + Q_D(QGLVertexBundle); + if (!d->buffer.isCreated()) { + d->attributeSet.insert(attribute); + d->attributes += + new QGLVertexBundleVector4DAttribute(attribute, value); + d->vertexCount = qMax(d->vertexCount, value.count()); + } +} + +/*! + Adds the color array \a value to this vertex bundle as the + data for \a attribute. + + \sa upload() +*/ +void QGLVertexBundle::addAttribute + (QGL::VertexAttribute attribute, const QArray<QColor4ub>& value) +{ + Q_D(QGLVertexBundle); + if (!d->buffer.isCreated()) { + d->attributeSet.insert(attribute); + d->attributes += + new QGLVertexBundleColorAttribute(attribute, value); + d->vertexCount = qMax(d->vertexCount, value.count()); + } +} + +/*! + Adds the custom data array \a value to this vertex bundle as the + data for \a attribute. + + \sa upload() +*/ +void QGLVertexBundle::addAttribute + (QGL::VertexAttribute attribute, const QCustomDataArray& value) +{ + Q_D(QGLVertexBundle); + if (!d->buffer.isCreated()) { + d->attributeSet.insert(attribute); + d->attributes += + new QGLVertexBundleCustomAttribute(attribute, value); + d->vertexCount = qMax(d->vertexCount, value.count()); + } +} + +// Interleave a source array into a destination array. +static void vertexBufferInterleave + (float *dst, int dstStride, const float *src, int srcStride, int count) +{ + switch (srcStride) { + case 1: + while (count-- > 0) { + dst[0] = src[0]; + ++src; + dst += dstStride; + } + break; + case 2: + while (count-- > 0) { + dst[0] = src[0]; + dst[1] = src[1]; + src += 2; + dst += dstStride; + } + break; + case 3: + while (count-- > 0) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + src += 3; + dst += dstStride; + } + break; + case 4: + while (count-- > 0) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + src += 4; + dst += dstStride; + } + break; + default: + while (count-- > 0) { + for (int component = 0; component < srcStride; ++component) + dst[component] = src[component]; + src += srcStride; + dst += dstStride; + } + break; + } +} + +/*! + Returns the set of attributes that are present in this vertex bundle. +*/ +QGLAttributeSet QGLVertexBundle::attributes() const +{ + Q_D(const QGLVertexBundle); + return d->attributeSet; +} + +/*! + Returns the raw attribute value associated with \a attribute in + this vertex bundle; null if \a attribute does not exist in the + vertex bundle. + + If isUploaded() is true, then the returned value will contain a + buffer offset to the attribute. If isUploaded() is false, + then the returned value will contain a client-side data pointer + to the attribute. + + \sa addAttribute() +*/ +QGLAttributeValue QGLVertexBundle::attributeValue(QGL::VertexAttribute attribute) const +{ + Q_D(const QGLVertexBundle); + QGLVertexBundleAttribute *attr = 0; + int attrIndex; + for (attrIndex = 0; attrIndex < d->attributes.size(); ++attrIndex) { + attr = d->attributes[attrIndex]; + if (attr->attribute == attribute) + return attr->value; + } + return QGLAttributeValue(); +} + +/*! + Returns the number of vertices that were defined by previous + called to addAttribute(). + + \sa addAttribute() +*/ +int QGLVertexBundle::vertexCount() const +{ + Q_D(const QGLVertexBundle); + return d->vertexCount; +} + +/*! + \fn bool QGLVertexBundle::isEmpty() const + + Returns true if vertexCount() is zero; false otherwise. +*/ + +/*! + Uploads the vertex data specified by previous addAttribute() + calls into the GL server as a vertex buffer object. + + Returns true if the data could be uploaded; false if vertex buffer + objects are not supported or there is insufficient memory to complete + the request. Returns true if the data was already uploaded. + + Once the vertex data has been uploaded, the client-side copies of + the data arrays will be released. If the vertex data could not be + uploaded, then it is retained client-side. This way, regardless of + whether the data could be uploaded or not, QGLPainter::setVertexBundle() + can be used to support drawing of primitives using this object. + + \sa isUploaded(), addAttribute(), QGLPainter::setVertexBundle() +*/ +bool QGLVertexBundle::upload() +{ + Q_D(QGLVertexBundle); + QGLVertexBundleAttribute *attr; + + // Nothing to do if already uploaded or there are no attributes. + if (d->buffer.isCreated()) + return true; + if (d->attributes.isEmpty()) + return false; + + // Create the VBO in the GL server and bind it. + if (!d->buffer.create()) + return false; + d->buffer.bind(); + + // If there is only one attribute, then realloc and write in one step. + if (d->attributes.size() == 1) { + attr = d->attributes[0]; + d->buffer.allocate(attr->value.data(), + attr->count() * attr->elementSize()); + attr->value.setOffset(0); + attr->clear(); + d->buffer.release(); + return true; + } + + // Calculate the total size of the VBO that we will need, + // the maximum number of interleaved vertices, and the + // interleaved stride. + int size = 0; + int stride = 0; + int maxCount = 0; + for (int index = 0; index < d->attributes.size(); ++index) { + attr = d->attributes[index]; + int count = attr->count(); + if (count > maxCount) + maxCount = count; + int elemSize = attr->elementSize(); + size += count * elemSize; + stride += elemSize; + } + int bufferSize = size; + d->buffer.allocate(bufferSize); + stride /= sizeof(float); + + // Determine how to upload the data, using a map if possible. + // Interleave the data into the final buffer. We do it in + // sections so as to keep locality problems to a minimum. + void *mapped = d->buffer.map(QGLBuffer::WriteOnly); + int offset = 0; + QArray<float> temp; + float *dst; + if (mapped) + dst = reinterpret_cast<float *>(mapped); + else + dst = temp.extend(1024); + int sectionSize = 1024 / stride; + for (int vertex = 0; vertex < maxCount; vertex += sectionSize) { + int attrPosn = 0; + for (int index = 0; index < d->attributes.size(); ++index) { + attr = d->attributes[index]; + int count = attr->count() - vertex; + if (count <= 0) + continue; + count = qMin(count, sectionSize); + int components = attr->elementSize() / sizeof(float); + vertexBufferInterleave + (dst + attrPosn, stride, + reinterpret_cast<const float *>(attr->value.data()) + + vertex * components, + components, count); + attrPosn += attr->elementSize() / sizeof(float); + } + size = sectionSize * stride; + if (mapped) { + dst += size; + } else { + size *= sizeof(float); + if ((offset + size) > bufferSize) // buffer overflow check + size = bufferSize-offset; + d->buffer.write(offset, dst, size); + offset += size; + } + } + offset = 0; + for (int index = 0; index < d->attributes.size(); ++index) { + attr = d->attributes[index]; + attr->value.setOffset(offset); + attr->value.setStride(stride * sizeof(float)); + offset += attr->elementSize(); + attr->clear(); + } + if (mapped) + d->buffer.unmap(); + + // Buffer is uploaded and ready to go. + d->buffer.release(); + return true; +} + +/*! + Returns true if the vertex data specified by previous addAttribute() + calls has been uploaded into the GL server; false otherwise. + + \sa upload(), addAttribute() +*/ +bool QGLVertexBundle::isUploaded() const +{ + Q_D(const QGLVertexBundle); + return d->buffer.isCreated(); +} + +/*! + Returns the QGLBuffer in use by this vertex bundle object, + so that its properties or contents can be modified directly. + + \sa isUploaded() +*/ +QGLBuffer QGLVertexBundle::buffer() const +{ + Q_D(const QGLVertexBundle); + return d->buffer; +} + +/*! + Binds the vertex buffer associated with this bundle to the current GL + context. Returns false if binding was not possible, usually because + upload() has not been called. + + The buffer must be bound to the same QGLContext current when upload() + was called, or to another QGLContext that is sharing with it. + Otherwise, false will be returned from this function. + + \sa release(), upload() +*/ +bool QGLVertexBundle::bind() +{ + Q_D(QGLVertexBundle); + return d->buffer.bind(); +} + +/*! + Releases the vertex buffer associated with this bundle from the + current GL context. + + This function must be called with the same QGLContext current + as when bind() was called on the vertex buffer. + + \sa bind() +*/ +void QGLVertexBundle::release() +{ + Q_D(QGLVertexBundle); + d->buffer.release(); +} + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qglvertexbundle.h b/src/threed/arrays/qglvertexbundle.h new file mode 100644 index 000000000..8776782e8 --- /dev/null +++ b/src/threed/arrays/qglvertexbundle.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLVERTEXBUNDLE_H +#define QGLVERTEXBUNDLE_H + +#include <QtOpenGL/qglbuffer.h> +#include "qcustomdataarray.h" +#include "qglattributevalue.h" +#include "qglattributeset.h" +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLVertexBundlePrivate; +class QGLPainter; +class QGLAbstractEffect; +class QGLShaderProgram; + +class Q_QT3D_EXPORT QGLVertexBundle +{ +public: + QGLVertexBundle(); + QGLVertexBundle(const QGLVertexBundle& other); + ~QGLVertexBundle(); + + QGLVertexBundle& operator=(const QGLVertexBundle& other); + + void addAttribute(QGL::VertexAttribute attribute, + const QArray<float>& value); + void addAttribute(QGL::VertexAttribute attribute, + const QArray<QVector2D>& value); + void addAttribute(QGL::VertexAttribute attribute, + const QArray<QVector3D>& value); + void addAttribute(QGL::VertexAttribute attribute, + const QArray<QVector4D>& value); + void addAttribute(QGL::VertexAttribute attribute, + const QArray<QColor4ub>& value); + void addAttribute(QGL::VertexAttribute attribute, + const QCustomDataArray& value); + + QGLAttributeSet attributes() const; + + QGLAttributeValue attributeValue(QGL::VertexAttribute attribute) const; + + int vertexCount() const; + bool isEmpty() const { return vertexCount() == 0; } + + bool upload(); + bool isUploaded() const; + + QGLBuffer buffer() const; + + bool bind(); + void release(); + +private: + QGLVertexBundlePrivate *d_ptr; + + Q_DECLARE_PRIVATE(QGLVertexBundle) + + friend class QGLPainter; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qglvertexbundle_p.h b/src/threed/arrays/qglvertexbundle_p.h new file mode 100644 index 000000000..ae2866be5 --- /dev/null +++ b/src/threed/arrays/qglvertexbundle_p.h @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLVERTEXBUNDLE_P_H +#define QGLVERTEXBUNDLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglvertexbundle.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGLVertexBundleAttribute +{ +public: + QGLVertexBundleAttribute(QGL::VertexAttribute attr) : attribute(attr) {} + virtual ~QGLVertexBundleAttribute() {} + + virtual void clear() = 0; + virtual QGLAttributeValue uploadValue() = 0; + virtual int count() = 0; + virtual int elementSize() = 0; + + QGL::VertexAttribute attribute; + QGLAttributeValue value; +}; + +class QGLVertexBundleFloatAttribute : public QGLVertexBundleAttribute +{ +public: + QGLVertexBundleFloatAttribute + (QGL::VertexAttribute attr, const QArray<float>& array) + : QGLVertexBundleAttribute(attr), floatArray(array) + { + value = QGLAttributeValue(floatArray); + } + + void clear() { floatArray.clear(); } + QGLAttributeValue uploadValue() + { return QGLAttributeValue(floatArray); } + int count() { return floatArray.count(); } + int elementSize() { return sizeof(float); } + + QArray<float> floatArray; +}; + +class QGLVertexBundleVector2DAttribute : public QGLVertexBundleAttribute +{ +public: + QGLVertexBundleVector2DAttribute + (QGL::VertexAttribute attr, const QArray<QVector2D>& array) + : QGLVertexBundleAttribute(attr), vector2DArray(array) + { + value = QGLAttributeValue(vector2DArray); + } + + void clear() { vector2DArray.clear(); } + QGLAttributeValue uploadValue() + { return QGLAttributeValue(vector2DArray); } + int count() { return vector2DArray.count(); } + int elementSize() { return sizeof(QVector2D); } + + QArray<QVector2D> vector2DArray; +}; + +class QGLVertexBundleVector3DAttribute : public QGLVertexBundleAttribute +{ +public: + QGLVertexBundleVector3DAttribute + (QGL::VertexAttribute attr, const QArray<QVector3D>& array) + : QGLVertexBundleAttribute(attr), vector3DArray(array) + { + value = QGLAttributeValue(vector3DArray); + } + + void clear() { vector3DArray.clear(); } + QGLAttributeValue uploadValue() + { return QGLAttributeValue(vector3DArray); } + int count() { return vector3DArray.count(); } + int elementSize() { return sizeof(QVector3D); } + + QArray<QVector3D> vector3DArray; +}; + +class QGLVertexBundleVector4DAttribute : public QGLVertexBundleAttribute +{ +public: + QGLVertexBundleVector4DAttribute + (QGL::VertexAttribute attr, const QArray<QVector4D>& array) + : QGLVertexBundleAttribute(attr), vector4DArray(array) + { + value = QGLAttributeValue(vector4DArray); + } + + void clear() { vector4DArray.clear(); } + QGLAttributeValue uploadValue() + { return QGLAttributeValue(vector4DArray); } + int count() { return vector4DArray.count(); } + int elementSize() { return sizeof(QVector4D); } + + QArray<QVector4D> vector4DArray; +}; + +class QGLVertexBundleColorAttribute : public QGLVertexBundleAttribute +{ +public: + QGLVertexBundleColorAttribute + (QGL::VertexAttribute attr, const QArray<QColor4ub>& array) + : QGLVertexBundleAttribute(attr), colorArray(array) + { + value = QGLAttributeValue(colorArray); + } + + void clear() { colorArray.clear(); } + QGLAttributeValue uploadValue() + { return QGLAttributeValue(colorArray); } + int count() { return colorArray.count(); } + int elementSize() { return sizeof(QColor4ub); } + + QArray<QColor4ub> colorArray; +}; + +class QGLVertexBundleCustomAttribute : public QGLVertexBundleAttribute +{ +public: + QGLVertexBundleCustomAttribute + (QGL::VertexAttribute attr, const QCustomDataArray& array) + : QGLVertexBundleAttribute(attr), customArray(array) + { + value = QGLAttributeValue(customArray); + } + + void clear() { customArray.clear(); } + QGLAttributeValue uploadValue() + { return QGLAttributeValue(customArray); } + int count() { return customArray.count(); } + int elementSize() { return customArray.elementSize(); } + + QCustomDataArray customArray; +}; + +class QGLVertexBundlePrivate +{ +public: + QGLVertexBundlePrivate() + : buffer(QGLBuffer::VertexBuffer), + vertexCount(0) + { + ref = 1; + } + ~QGLVertexBundlePrivate() + { + qDeleteAll(attributes); + } + + QBasicAtomicInt ref; + QGLBuffer buffer; + QList<QGLVertexBundleAttribute *> attributes; + int vertexCount; + QGLAttributeSet attributeSet; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qvector2darray.cpp b/src/threed/arrays/qvector2darray.cpp new file mode 100644 index 000000000..368417f4d --- /dev/null +++ b/src/threed/arrays/qvector2darray.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvector2darray.h" +#include <QtGui/qmatrix4x4.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QVector2DArray + \brief The QVector2DArray class is a convenience for wrapping a QArray of QVector2D values. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays + + QVector2DArray is used to build an array of 2D vector values + based on floating-point x and y arguments: + + \code + QVector2DArray array; + array.append(1.0f, 2.0f); + array.append(-1.0f, 2.0f); + array.append(1.0f, -2.0f); + \endcode + + This is more convenient and readable than the equivalent with + QArray: + + \code + QArray<QVector2D> array; + array.append(QVector2D(1.0f, 2.0f)); + array.append(QVector2D(-1.0f, 2.0f)); + array.append(QVector2D(1.0f, -2.0f)); + \endcode + + QVector2DArray also has convenience functions for transforming + the contents of the array with translate(), translated(), + transform(), and transformed(). + + \sa QArray, QVector3DArray, QVector4DArray +*/ + +/*! + \fn QVector2DArray::QVector2DArray() + + Constructs an empty array of QVector2D values. +*/ + +/*! + \fn QVector2DArray::QVector2DArray(int size, const QVector2D& value) + + Constructs an array of QVector2D values with an initial \a size. + All elements in the array are initialized to \a value. +*/ + +/*! + \fn QVector2DArray::QVector2DArray(const QArray<QVector2D>& other) + + Constructs a copy of \a other. +*/ + +/*! + \fn void QVector2DArray::append(qreal x, qreal y) + \overload + + Appends (\a x, \a y) to this array of QVector2D values. +*/ + +/*! + \fn void QVector2DArray::append(const QPointF& point) + \overload + + Appends \a point to this array of QVector2D values. +*/ + +/*! + \fn void QVector2DArray::append(const QPoint& point); + \overload + + Appends \a point to this array of QVector2D values. +*/ + +/*! + Multiplies the elements in this array of QVector2D values by + the \a scale. + + \sa scaled() +*/ +void QVector2DArray::scale(qreal scale) +{ + if (isDetached()) { + // Modify the array in-place. + int size = count(); + QVector2D *dst = data(); + for (int index = 0; index < size; ++index) + *dst++ *= scale; + } else { + // Create a new array, translate the values, and assign. + QArray<QVector2D> result; + int size = count(); + const QVector2D *src = constData(); + QVector2D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ * scale; + *this = result; + } +} + +/*! + Returns a copy of this array of QVector2D values, multiplied + by the \a scale. + + \sa scale() +*/ +QVector2DArray QVector2DArray::scaled(qreal scale) const +{ + const qreal identity = 1.0; + if (qFuzzyCompare(scale, identity)) + return *this; + QArray<QVector2D> result; + int size = count(); + const QVector2D *src = constData(); + QVector2D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ * scale; + return result; +} + +/*! + Translates the elements in this array of QVector2D values + by the components of \a value. + + \sa translated() +*/ +void QVector2DArray::translate(const QVector2D& value) +{ + if (isDetached()) { + // Modify the array in-place. + int size = count(); + QVector2D *dst = data(); + for (int index = 0; index < size; ++index) + *dst++ += value; + } else { + // Create a new array, translate the values, and assign. + QArray<QVector2D> result; + int size = count(); + const QVector2D *src = constData(); + QVector2D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ + value; + *this = result; + } +} + +/*! + \fn void QVector2DArray::translate(qreal x, qreal y) + \overload + + Translates the elements in this array of QVector2D values + by (\a x, \a y). + + \sa translated() +*/ + +/*! + Returns a copy of this array of QVector2D values, translated + by the components of \a value. + + \sa translate() +*/ +QArray<QVector2D> QVector2DArray::translated(const QVector2D& value) const +{ + QArray<QVector2D> result; + int size = count(); + QVector2D *dst = result.extend(size); + const QVector2D *src = constData(); + for (int index = 0; index < size; ++index) + *dst++ = *src++ + value; + return result; +} + +/*! + \fn QArray<QVector2D> QVector2DArray::translated(qreal x, qreal y) const + \overload + + Returns a copy of this array of QVector2D values, translated + by (\a x, \a y). + + \sa translate() +*/ + +/*! + Transforms the elements in this array of QVector2D values + by \a matrix. + + \sa transformed() +*/ +void QVector2DArray::transform(const QMatrix4x4& matrix) +{ + if (isDetached()) { + // Modify the array in-place. + int size = count(); + QVector2D *dst = data(); + for (int index = 0; index < size; ++index) { + *dst = (matrix * QVector3D(*dst, 0.0f)).toVector2D(); + ++dst; + } + } else { + // Create a new array, transform the values, and assign. + QArray<QVector2D> result; + int size = count(); + const QVector2D *src = constData(); + QVector2D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = (matrix * QVector3D(*src++, 0.0f)).toVector2D(); + *this = result; + } +} + +/*! + Returns a copy of this array of QVector2D values, + transformed by \a matrix. + + \sa transform() +*/ +QArray<QVector2D> QVector2DArray::transformed(const QMatrix4x4& matrix) const +{ + QArray<QVector2D> result; + int size = count(); + const QVector2D *src = constData(); + QVector2D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = (matrix * QVector3D(*src++, 0.0f)).toVector2D(); + return result; +} + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qvector2darray.h b/src/threed/arrays/qvector2darray.h new file mode 100644 index 000000000..f72eef63b --- /dev/null +++ b/src/threed/arrays/qvector2darray.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVECTOR2DARRAY_H +#define QVECTOR2DARRAY_H + +#include "qarray.h" +#include <QtGui/qvector2d.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QMatrix4x4; + +class Q_QT3D_EXPORT QVector2DArray : public QArray<QVector2D> +{ +public: + QVector2DArray(); + QVector2DArray(int size, const QVector2D& value = QVector2D()); + QVector2DArray(const QArray<QVector2D>& other); + + void append(qreal x, qreal y); + void append(const QPointF& point); + void append(const QPoint& point); + + void scale(qreal scale); + QVector2DArray scaled(qreal scale) const; + + void translate(const QVector2D& value); + void translate(qreal x, qreal y); + + QArray<QVector2D> translated(const QVector2D& value) const; + QArray<QVector2D> translated(qreal x, qreal y) const; + + void transform(const QMatrix4x4& matrix); + QArray<QVector2D> transformed(const QMatrix4x4& matrix) const; + +#if !defined(Q_NO_USING_KEYWORD) || defined(Q_QDOC) + using QArray<QVector2D>::append; +#else + inline void append(const QVector2D& value) + { QArray<QVector2D>::append(value); } + inline void append(const QVector2D& value1, const QVector2D& value2) + { QArray<QVector2D>::append(value1, value2); } + inline void append(const QVector2D& value1, const QVector2D& value2, const QVector2D& value3) + { QArray<QVector2D>::append(value1, value2, value3); } + inline void append(const QVector2D& value1, const QVector2D& value2, const QVector2D& value3, const QVector2D& value4) + { QArray<QVector2D>::append(value1, value2, value3, value4); } + inline void append(const QVector2D *values, int count) + { QArray<QVector2D>::append(values, count); } + inline void append(const QArray<QVector2D>& other) + { QArray<QVector2D>::append(other); } +#endif +}; + +inline QVector2DArray::QVector2DArray() {} + +inline QVector2DArray::QVector2DArray(int size, const QVector2D& value) + : QArray<QVector2D>(size, value) {} + +inline QVector2DArray::QVector2DArray(const QArray<QVector2D>& other) + : QArray<QVector2D>(other) {} + +inline void QVector2DArray::append(qreal x, qreal y) + { QArray<QVector2D>::append(QVector2D(x, y)); } + +inline void QVector2DArray::append(const QPointF& point) + { QArray<QVector2D>::append(QVector2D(point)); } + +inline void QVector2DArray::append(const QPoint& point) + { QArray<QVector2D>::append(QVector2D(point)); } + +inline void QVector2DArray::translate(qreal x, qreal y) + { translate(QVector2D(x, y)); } + +inline QArray<QVector2D> QVector2DArray::translated(qreal x, qreal y) const + { return translated(QVector2D(x, y)); } + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qvector3darray.cpp b/src/threed/arrays/qvector3darray.cpp new file mode 100644 index 000000000..460ccf6c0 --- /dev/null +++ b/src/threed/arrays/qvector3darray.cpp @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvector3darray.h" +#include <QtGui/qmatrix4x4.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QVector3DArray + \brief The QVector3DArray class is a convenience for wrapping a QArray of QVector3D values. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays + + QVector3DArray is used to build an array of 3D vector values + based on floating-point x, y, and z arguments: + + \code + QVector3DArray array; + array.append(1.0f, 2.0f, 3.0f); + array.append(-1.0f, 2.0f, 3.0f); + array.append(1.0f, -2.0f, 3.0f); + \endcode + + This is more convenient and readable than the equivalent with + QArray: + + \code + QArray<QVector3D> array; + array.append(QVector3D(1.0f, 2.0f, 3.0f)); + array.append(QVector3D(-1.0f, 2.0f, 3.0f)); + array.append(QVector3D(1.0f, -2.0f, 3.0f)); + \endcode + + QVector3DArray also has convenience functions for transforming + the contents of the array with translate(), translated(), + transform(), and transformed(). + + \sa QArray, QVector2DArray, QVector4DArray +*/ + +/*! + \fn QVector3DArray::QVector3DArray() + + Constructs an empty array of QVector3D values. +*/ + +/*! + \fn QVector3DArray::QVector3DArray(int size, const QVector3D& value) + + Constructs an array of QVector3D values with an initial \a size. + All elements in the array are initialized to \a value. +*/ + +/*! + \fn QVector3DArray::QVector3DArray(const QArray<QVector3D>& other) + + Constructs a copy of \a other. +*/ + +/*! + \fn void QVector3DArray::append(qreal x, qreal y, qreal z) + + Appends (\a x, \a y, \a z) to this array of QVector3D values. +*/ + +/*! + Multiplies the elements in this array of QVector3D values by + the \a scale. + + \sa scaled() +*/ +void QVector3DArray::scale(qreal scale) +{ + if (isDetached()) { + // Modify the array in-place. + int size = count(); + QVector3D *dst = data(); + for (int index = 0; index < size; ++index) + *dst++ *= scale; + } else { + // Create a new array, translate the values, and assign. + QArray<QVector3D> result; + int size = count(); + const QVector3D *src = constData(); + QVector3D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ * scale; + *this = result; + } +} + +/*! + Returns a copy of this array of QVector3D values, multiplied + by the \a scale. + + \sa scale() +*/ +QVector3DArray QVector3DArray::scaled(qreal scale) const +{ + QArray<QVector3D> result; + int size = count(); + const QVector3D *src = constData(); + QVector3D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ * scale; + return result; +} + +/*! + Translates the elements in this array of QVector3D values + by the components of \a value. + + \sa translated() +*/ +void QVector3DArray::translate(const QVector3D& value) +{ + if (isDetached()) { + // Modify the array in-place. + int size = count(); + QVector3D *dst = data(); + for (int index = 0; index < size; ++index) + *dst++ += value; + } else { + // Create a new array, translate the values, and assign. + QArray<QVector3D> result; + int size = count(); + const QVector3D *src = constData(); + QVector3D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ + value; + *this = result; + } +} + +/*! + \fn void QVector3DArray::translate(qreal x, qreal y, qreal z) + \overload + + Translates the elements in this array of QVector3D values + by (\a x, \a y, \a z). + + \sa translated() +*/ + +/*! + Returns a copy of this array of QVector3D values, translated + by the components of \a value. + + \sa translate() +*/ +QArray<QVector3D> QVector3DArray::translated(const QVector3D& value) const +{ + QArray<QVector3D> result; + int size = count(); + const QVector3D *src = constData(); + QVector3D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ + value; + return result; +} + +/*! + \fn QArray<QVector3D> QVector3DArray::translated(qreal x, qreal y, qreal z) const + \overload + + Returns a copy of this array of QVector3D values, translated + by (\a x, \a y, \a z). + + \sa translate() +*/ + +/*! + Transforms the elements in this array of QVector3D values + by \a matrix. + + \sa transformed() +*/ +void QVector3DArray::transform(const QMatrix4x4& matrix) +{ + if (isDetached()) { + // Modify the array in-place. + int size = count(); + QVector3D *dst = data(); + for (int index = 0; index < size; ++index) { + *dst = matrix * *dst; + ++dst; + } + } else { + // Create a new array, transform the values, and assign. + QArray<QVector3D> result; + int size = count(); + const QVector3D *src = constData(); + QVector3D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = matrix * *src++; + *this = result; + } +} + +/*! + Returns a copy of this array of QVector3D values, transformed + by \a matrix. + + \sa transform() +*/ +QArray<QVector3D> QVector3DArray::transformed(const QMatrix4x4& matrix) const +{ + QArray<QVector3D> result; + int size = count(); + const QVector3D *src = constData(); + QVector3D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = matrix * *src++; + return result; +} + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qvector3darray.h b/src/threed/arrays/qvector3darray.h new file mode 100644 index 000000000..f04742e5f --- /dev/null +++ b/src/threed/arrays/qvector3darray.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVECTOR3DARRAY_H +#define QVECTOR3DARRAY_H + +#include "qarray.h" +#include <QtGui/qvector3d.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QMatrix4x4; + +class Q_QT3D_EXPORT QVector3DArray : public QArray<QVector3D> +{ +public: + QVector3DArray(); + QVector3DArray(int size, const QVector3D& value = QVector3D()); + QVector3DArray(const QArray<QVector3D>& other); + + void append(qreal x, qreal y, qreal z); + + void scale(qreal scale); + QVector3DArray scaled(qreal scale) const; + + void translate(const QVector3D& value); + void translate(qreal x, qreal y, qreal z); + + QArray<QVector3D> translated(const QVector3D& value) const; + QArray<QVector3D> translated(qreal x, qreal y, qreal z) const; + + void transform(const QMatrix4x4& matrix); + QArray<QVector3D> transformed(const QMatrix4x4& matrix) const; + +#if !defined(Q_NO_USING_KEYWORD) || defined(Q_QDOC) + using QArray<QVector3D>::append; +#else + inline void append(const QVector3D& value) + { QArray<QVector3D>::append(value); } + inline void append(const QVector3D& value1, const QVector3D& value2) + { QArray<QVector3D>::append(value1, value2); } + inline void append(const QVector3D& value1, const QVector3D& value2, const QVector3D& value3) + { QArray<QVector3D>::append(value1, value2, value3); } + inline void append(const QVector3D& value1, const QVector3D& value2, const QVector3D& value3, const QVector3D& value4) + { QArray<QVector3D>::append(value1, value2, value3, value4); } + inline void append(const QVector3D *values, int count) + { QArray<QVector3D>::append(values, count); } + inline void append(const QArray<QVector3D>& other) + { QArray<QVector3D>::append(other); } +#endif +}; + +inline QVector3DArray::QVector3DArray() {} + +inline QVector3DArray::QVector3DArray(int size, const QVector3D& value) + : QArray<QVector3D>(size, value) {} + +inline QVector3DArray::QVector3DArray(const QArray<QVector3D>& other) + : QArray<QVector3D>(other) {} + +inline void QVector3DArray::append(qreal x, qreal y, qreal z) + { QArray<QVector3D>::append(QVector3D(x, y, z)); } + +inline void QVector3DArray::translate(qreal x, qreal y, qreal z) + { translate(QVector3D(x, y, z)); } + +inline QArray<QVector3D> QVector3DArray::translated(qreal x, qreal y, qreal z) const + { return translated(QVector3D(x, y, z)); } + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/arrays/qvector4darray.cpp b/src/threed/arrays/qvector4darray.cpp new file mode 100644 index 000000000..0c7056695 --- /dev/null +++ b/src/threed/arrays/qvector4darray.cpp @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvector4darray.h" +#include <QtGui/qmatrix4x4.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QVector4DArray + \brief The QVector4DArray class is a convenience for wrapping a QArray of QVector4D values. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::arrays + + QVector4DArray is used to build an array of 4D vector values + based on floating-point x, y, and z arguments: + + \code + QVector4DArray array; + array.append(1.0f, 2.0f, 3.0f, -4.0f); + array.append(-1.0f, 2.0f, 3.0f, -4.0f); + array.append(1.0f, -2.0f, 3.0f, -4.0f); + \endcode + + This is more convenient and readable than the equivalent with + QArray: + + \code + QArray<QVector4D> array; + array.append(QVector4D(1.0f, 2.0f, 3.0f, -4.0f)); + array.append(QVector4D(-1.0f, 2.0f, 3.0f, -4.0f)); + array.append(QVector4D(1.0f, -2.0f, 3.0f, -4.0f)); + \endcode + + QVector4DArray also has convenience functions for transforming + the contents of the array with translate(), translated(), + transform(), and transformed(). + + \sa QArray, QVector2DArray, QVector3DArray +*/ + +/*! + \fn QVector4DArray::QVector4DArray() + + Constructs an empty array of QVector4D values. +*/ + +/*! + \fn QVector4DArray::QVector4DArray(int size, const QVector4D& value) + + Constructs an array of QVector4D values with an initial \a size. + All elements in the array are initialized to \a value. +*/ + +/*! + \fn QVector4DArray::QVector4DArray(const QArray<QVector4D>& other) + + Constructs a copy of \a other. +*/ + +/*! + \fn void QVector4DArray::append(qreal x, qreal y, qreal z, qreal w) + + Appends (\a x, \a y, \a z, \a w) to this array of QVector4D values. +*/ + +/*! + Multiplies the elements in this array of QVector4D values by + the \a scale. + + \sa scaled() +*/ +void QVector4DArray::scale(qreal scale) +{ + if (isDetached()) { + // Modify the array in-place. + int size = count(); + QVector4D *dst = data(); + for (int index = 0; index < size; ++index) + *dst++ *= scale; + } else { + // Create a new array, translate the values, and assign. + QArray<QVector4D> result; + int size = count(); + const QVector4D *src = constData(); + QVector4D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ * scale; + *this = result; + } +} + +/*! + Returns a copy of this array of QVector4D values, multiplied + by the \a scale. + + \sa scale() +*/ +QVector4DArray QVector4DArray::scaled(qreal scale) const +{ + QArray<QVector4D> result; + int size = count(); + const QVector4D *src = constData(); + QVector4D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ * scale; + return result; +} + +/*! + Translates the elements in this array of QVector4D values + by the components of \a value. + + \sa translated() +*/ +void QVector4DArray::translate(const QVector4D& value) +{ + if (isDetached()) { + // Modify the array in-place. + int size = count(); + QVector4D *dst = data(); + for (int index = 0; index < size; ++index) + *dst++ += value; + } else { + // Create a new array, translate the values, and assign. + QArray<QVector4D> result; + int size = count(); + const QVector4D *src = constData(); + QVector4D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ + value; + *this = result; + } +} + +/*! + \fn void QVector4DArray::translate(qreal x, qreal y, qreal z, qreal w); + \overload + + Translates the elements in this array of QVector4D values + by (\a x, \a y, \a z, \a w). + + \sa translated() +*/ + +/*! + Returns a copy of this array of QVector4D values, translated + by the components of \a value. + + \sa translate() +*/ +QArray<QVector4D> QVector4DArray::translated(const QVector4D& value) const +{ + QArray<QVector4D> result; + int size = count(); + const QVector4D *src = constData(); + QVector4D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = *src++ + value; + return result; +} + +/*! + \fn QArray<QVector4D> QVector4DArray::translated(qreal x, qreal y, qreal z, qreal w) const + \overload + + Returns a copy of this array of QVector4D values, translated + by (\a x, \a y, \a z, \a w). + + \sa translate() +*/ + +/*! + Transforms the elements in this array of QVector4D values + by \a matrix. + + \sa transformed() +*/ +void QVector4DArray::transform(const QMatrix4x4& matrix) +{ + if (isDetached()) { + // Modify the array in-place. + int size = count(); + QVector4D *dst = data(); + for (int index = 0; index < size; ++index) { + *dst = matrix * *dst; + ++dst; + } + } else { + // Create a new array, transform the values, and assign. + QArray<QVector4D> result; + int size = count(); + const QVector4D *src = constData(); + QVector4D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = matrix * *src++; + *this = result; + } +} + +/*! + Returns a copy of this array of QVector3D values, transformed + by \a matrix. + + \sa transform() +*/ +QArray<QVector4D> QVector4DArray::transformed(const QMatrix4x4& matrix) const +{ + QArray<QVector4D> result; + int size = count(); + const QVector4D *src = constData(); + QVector4D *dst = result.extend(size); + for (int index = 0; index < size; ++index) + *dst++ = matrix * *src++; + return result; +} + +QT_END_NAMESPACE diff --git a/src/threed/arrays/qvector4darray.h b/src/threed/arrays/qvector4darray.h new file mode 100644 index 000000000..c9770a023 --- /dev/null +++ b/src/threed/arrays/qvector4darray.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVECTOR4DARRAY_H +#define QVECTOR4DARRAY_H + +#include "qarray.h" +#include <QtGui/qvector4d.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QMatrix4x4; + +class Q_QT3D_EXPORT QVector4DArray : public QArray<QVector4D> +{ +public: + QVector4DArray(); + QVector4DArray(int size, const QVector4D& value = QVector4D()); + QVector4DArray(const QArray<QVector4D>& other); + + void append(qreal x, qreal y, qreal z, qreal w); + + void scale(qreal scale); + QVector4DArray scaled(qreal scale) const; + + void translate(const QVector4D& value); + void translate(qreal x, qreal y, qreal z, qreal w); + + QArray<QVector4D> translated(const QVector4D& value) const; + QArray<QVector4D> translated + (qreal x, qreal y, qreal z, qreal w) const; + + void transform(const QMatrix4x4& matrix); + QArray<QVector4D> transformed(const QMatrix4x4& matrix) const; + +#if !defined(Q_NO_USING_KEYWORD) || defined(Q_QDOC) + using QArray<QVector4D>::append; +#else + inline void append(const QVector4D& value) + { QArray<QVector4D>::append(value); } + inline void append(const QVector4D& value1, const QVector4D& value2) + { QArray<QVector4D>::append(value1, value2); } + inline void append(const QVector4D& value1, const QVector4D& value2, const QVector4D& value3) + { QArray<QVector4D>::append(value1, value2, value3); } + inline void append(const QVector4D& value1, const QVector4D& value2, const QVector4D& value3, const QVector4D& value4) + { QArray<QVector4D>::append(value1, value2, value3, value4); } + inline void append(const QVector4D *values, int count) + { QArray<QVector4D>::append(values, count); } + inline void append(const QArray<QVector4D>& other) + { QArray<QVector4D>::append(other); } +#endif +}; + +inline QVector4DArray::QVector4DArray() {} + +inline QVector4DArray::QVector4DArray(int size, const QVector4D& value) + : QArray<QVector4D>(size, value) {} + +inline QVector4DArray::QVector4DArray(const QArray<QVector4D>& other) + : QArray<QVector4D>(other) {} + +inline void QVector4DArray::append(qreal x, qreal y, qreal z, qreal w) + { QArray<QVector4D>::append(QVector4D(x, y, z, w)); } + +inline void QVector4DArray::translate(qreal x, qreal y, qreal z, qreal w) + { translate(QVector4D(x, y, z, w)); } + +inline QArray<QVector4D> QVector4DArray::translated + (qreal x, qreal y, qreal z, qreal w) const + { return translated(QVector4D(x, y, z, w)); } + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/effects/effects.pri b/src/threed/effects/effects.pri new file mode 100644 index 000000000..19b0cae37 --- /dev/null +++ b/src/threed/effects/effects.pri @@ -0,0 +1,26 @@ + +INCLUDEPATH += $$PWD +VPATH += $$PWD + +HEADERS += \ + qglshaderprogrameffect.h \ + qglcolladafxeffectfactory.h \ + qglcolladafxeffect.h \ + qglcolladafxeffectloader.h + +SOURCES += \ + qglflatcoloreffect.cpp \ + qglflattextureeffect.cpp \ + qgllitmaterialeffect.cpp \ + qgllittextureeffect.cpp \ + qglshaderprogrameffect.cpp \ + qglcolladafxeffect.cpp \ + qglcolladafxeffectfactory.cpp \ + qglcolladafxeffectloader.cpp + +PRIVATE_HEADERS += \ + qglflatcoloreffect_p.h \ + qglflattextureeffect_p.h \ + qgllitmaterialeffect_p.h \ + qgllittextureeffect_p.h \ + qglcolladafxeffect_p.h diff --git a/src/threed/effects/qglcolladafxeffect.cpp b/src/threed/effects/qglcolladafxeffect.cpp new file mode 100644 index 000000000..35940d3e8 --- /dev/null +++ b/src/threed/effects/qglcolladafxeffect.cpp @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QString> +#include <QXmlStreamReader> +#include <QFile> +#include <QDebug> +#include <QImage> +#include "qgl.h" +#include "qgltexture2d.h" +#include "qglcolladafxeffect.h" +#include "qglcolladafxeffect_p.h" + +QGLColladaFxEffect::QGLColladaFxEffect() : QGLShaderProgramEffect() + , d( new QGLColladaFxEffectPrivate ) +{ +} + +QGLColladaFxEffect::QGLColladaFxEffect(const QGLColladaFxEffect&) : QGLShaderProgramEffect() +{ + Q_ASSERT(false); +}; + + +QGLColladaFxEffect::~QGLColladaFxEffect() +{ + delete d; +} + + + +QGLColladaFxEffectPrivate::QGLColladaFxEffectPrivate() : id() + , sid() + , name() + , emissiveTexture(0) + , ambientTexture(0) + , diffuseTexture(0) + , specularTexture(0) + , lighting(QGLColladaFxEffect::NoLighting) + , material(0) +{ + resetGlueSnippets(); +} + + + +QGLColladaFxEffectPrivate::~QGLColladaFxEffectPrivate() +{ + delete emissiveTexture; + emissiveTexture = 0; + delete ambientTexture; + ambientTexture = 0; + delete diffuseTexture; + diffuseTexture = 0; + delete specularTexture; + specularTexture = 0; + delete material; + material = 0; + +} + + + +inline void QGLColladaFxEffectPrivate::updateMaterialChannelSnippets(QString channelName, QGLTexture2D* texture, int* textureUnit, QColor fallbackColor) +{ + QString qVariableName = QLatin1String("q") + channelName; + if (texture != 0) + { + QString sourceVariableName = QLatin1String("texture") + channelName; + QString texVariableName = QString(QLatin1String("texCoord%1")).arg(*textureUnit); + // Take care of texture coordinates + QString varyingSnippet = QString(QLatin1String("varying vec4 %1;")).arg(texVariableName); + vertexShaderDeclarationSnippets.append(varyingSnippet); + vertexShaderVariableNames.append(texVariableName); + fragmentShaderDeclarationSnippets.append(varyingSnippet); + fragmentShaderVariableNames.append(texVariableName); + + vertexShaderCodeSnippets.append(QString(QLatin1String("%1 = texCoords; // TODO: dynamically add tex attributes\n")).arg(texVariableName)); + vertexShaderVariableNames.append(texVariableName); + + // Declare the color variable in the fragment shader + fragmentShaderDeclarationSnippets.append(QString(QLatin1String("lowp vec4 %1;")).arg(qVariableName)); + fragmentShaderVariableNames.append(qVariableName); + fragmentShaderDeclarationSnippets.append(QString(QLatin1String("uniform sampler2D %1;")).arg(sourceVariableName)); + fragmentShaderVariableNames.append(sourceVariableName); + + // Assign a colour to the variable out of the appropriate sampler + fragmentShaderCodeSnippets.append(QLatin1String(" mediump vec4 ") + qVariableName + QLatin1String(" = texture2D(") + sourceVariableName + QLatin1String(", ") + texVariableName + QLatin1String(".st);")); + fragmentShaderVariableNames.append(qVariableName); + // mediump? lowp? + + *textureUnit++; + } else { + fragmentShaderDeclarationSnippets.append(QString (QLatin1String("const vec4 %1 = vec4(%2, %3, %4, %5);")).arg( qVariableName).arg(fallbackColor.redF(), 0, 'f', 6).arg(fallbackColor.greenF(), 0, 'f', 6).arg(fallbackColor.blueF(), 0, 'f', 6).arg(fallbackColor.alphaF(), 0, 'f', 6 )); + fragmentShaderVariableNames.append(qVariableName); + } +} + + + +inline void QGLColladaFxEffectPrivate::setTextureUniform(QGLShaderProgram *program, QGLPainter* painter, QString channelName, QGLTexture2D* texture, int* textureUnit, QColor fallbackColor) +{ + QString qVariableName = QLatin1String("q") + channelName; + + if (texture != 0) + { + QString sourceVariableName = QLatin1String("texture") + channelName; + QString texVariableName = QString(QLatin1String("texCoord%1")).arg(*textureUnit); + painter->glActiveTexture(GL_TEXTURE0 + *textureUnit); + texture->bind(); + program->setUniformValue(sourceVariableName.toAscii().data(), *textureUnit); + } + else + { + // It's just a const value, so set it that way in the fragment shader. + program->setUniformValue(qVariableName.toAscii().data(), fallbackColor); + } +} + + + +void QGLColladaFxEffect::update(QGLPainter *painter, QGLPainter::Updates updates) +{ + QGLShaderProgramEffect::update(painter, updates); + + if (updates && QGLPainter::UpdateMaterials) + { + + if (program() == 0) + { + qWarning() << "no program in QGLColladaFxEffect::update()"; + return; + } + + // Start from texture unit 1 so as not to stomp a texture set on the + // painter. + int textureUnit = 1; + d->setTextureUniform( + program(), painter, QLatin1String("Emissive"), d->emissiveTexture, + &textureUnit, + material() ? material()->emittedLight() : QColor()); + + d->setTextureUniform( + program(), painter, QLatin1String("Ambient"), d->ambientTexture, &textureUnit, + material() ? material()->ambientColor() : QColor()); + + d->setTextureUniform( + program(), painter, QLatin1String("Diffuse"), d->diffuseTexture, &textureUnit, + material() ? material()->diffuseColor() : QColor()); + + d->setTextureUniform( + program(), painter, QLatin1String("Specular"), d->specularTexture, + &textureUnit, + material() ? material()->specularColor() : QColor()); + } +} + + + +void QGLColladaFxEffect::setId(QString id) +{ + d->id = id; +} + + + +QString QGLColladaFxEffect::id() +{ + return d->id; +} + + + +void QGLColladaFxEffect::setSid(QString sid) +{ + d->sid = sid; +} + + + +void QGLColladaFxEffectPrivate::addMaterialChannelsToShaderSnippets(const QGLMaterial *material) +{ + int textureUnit = 1; + + updateMaterialChannelSnippets(QLatin1String("Emissive"), emissiveTexture, &textureUnit, material->emittedLight()); + updateMaterialChannelSnippets(QLatin1String("Ambient"), ambientTexture, &textureUnit, material->ambientColor()); + updateMaterialChannelSnippets(QLatin1String("Diffuse"), diffuseTexture, &textureUnit, material->diffuseColor()); + updateMaterialChannelSnippets(QLatin1String("Specular"), specularTexture, &textureUnit, material->specularColor()); +} + + + +void QGLColladaFxEffect::addBlinnPhongLighting() +{ + d->addMaterialChannelsToShaderSnippets(material()); + + // Fragment shader declarations: + d->fragmentShaderDeclarationSnippets.append(QLatin1String("uniform mediump sampler2D texture0;")); + d->fragmentShaderVariableNames.append(QLatin1String("texture0")); + d->fragmentShaderDeclarationSnippets.append(QLatin1String("varying highp vec4 qt_TexCoord0;")); + d->fragmentShaderVariableNames.append(QLatin1String("qt_TexCoord0")); + + // Fragment Shader code + d->fragmentShaderCodeSnippets.append(QLatin1String( + " vec4 specularComponent = vec4( 0.0, 0.0, 0.0, 0.0 );\n"\ + " if (intensity > 0.0)\n"\ + " {\n"\ + " float specularIntensity = max( dot(perPixelNormal, qHalfVector), 0.0 );\n"\ + " if (specularIntensity > 0.0)\n"\ + " specularComponent = qSpecular * pow(specularIntensity, shininess);\n"\ + " }\n")); + d->fragmentShaderVariableNames.append(QLatin1String("lighting")); + + + // Replace the "end glue" to set colour from lighting + d->fragmentShaderEndGlueSnippet = QLatin1String( + " vec4 texture0Color = texture2D(texture0, qt_TexCoord0.st);\n"\ + " vec4 diffuseColor = qDiffuse;\n"\ + " vec4 lightingColor = qAmbient + diffuseColor * intensity + specularComponent;\n"\ + " vec4 texturedColor = vec4(lightingColor.xyz * (1.0 - texture0Color.a)\n"\ + "+ (texture0Color.xyz + specularComponent.rgb) * texture0Color.a, lightingColor.a);\n"\ + " gl_FragColor = texturedColor;\n"\ + "}"); + generateShaders(); +} + + + +void QGLColladaFxEffect::generateShaders() +{ + if (vertexShader().isEmpty()) + { + QString shader = + d->vertexShaderDeclarationSnippets.join(QLatin1String("\n")) + + QLatin1String("\n") + d->vertexShaderMainGlueSnippet + + d->vertexShaderCodeSnippets.join(QLatin1String("\n")) + + QLatin1String("\n") + d->vertexShaderEndGlueSnippet; + setVertexShader(shader.toLatin1()); + } + + if (fragmentShader().isEmpty()) + { + QString shader = + d->fragmentShaderDeclarationSnippets.join(QLatin1String("\n")) + + QLatin1String("\n") + d->fragmentShaderMainGlueSnippet + + d->fragmentShaderCodeSnippets.join(QLatin1String("\n")) + + QLatin1String("\n") + d->fragmentShaderEndGlueSnippet; + setFragmentShader(shader.toLatin1()); + } +} + + + +void QGLColladaFxEffectPrivate::resetGlueSnippets() +{ + vertexShaderMainGlueSnippet = QLatin1String( + "attribute highp vec4 vertex;\n"\ + "attribute highp vec4 normal;\n"\ + "attribute highp vec4 texCoords;\n"\ + "uniform highp mat4 matrix;\n"\ + "uniform highp mat3 qt_NormalMatrix;\n"\ + "varying mediump vec3 qNormal;\n"\ + "varying mediump vec3 qLightDirection;\n"\ + "varying mediump vec3 qHalfVector;\n"\ + "uniform mediump vec3 pli; // Position of the light\n"\ + "varying highp vec4 qt_TexCoord0; // TEMP\n" /* Got to get rid of this*/\ + "\n"\ + "void qLightVertex(vec4 vertex, vec3 normal)\n"\ + "{\n"\ + " vec3 toEye;\n"\ + " qLightDirection = normalize(pli);\n"\ + " toEye = vec3(0, 0, 1); // assume viewer at infinity\n"\ + " qHalfVector = normalize(qLightDirection + toEye);\n"\ + "}\n"\ + "\n"\ + "void main(void)\n"\ + "{\n"\ + " qNormal = normalize(qt_NormalMatrix * vec3(normal));\n"\ + " qLightVertex(vertex, qNormal);\n"\ + " qt_TexCoord0 = texCoords;\n"\ + ); + + vertexShaderEndGlueSnippet = QLatin1String ( + " gl_Position = matrix * vertex;\n"\ + "}\n"); + + + fragmentShaderEndGlueSnippet = QLatin1String( + " gl_FragColor = color;\n"\ + "}\n" + ); + + fragmentShaderMainGlueSnippet = QLatin1String( + "varying mediump vec3 qNormal;\n"\ + "varying mediump vec3 qLightDirection;\n"\ + "varying mediump vec3 qHalfVector;\n"\ + "uniform float shininess;\n"\ + "uniform vec4 color;\n"\ + "vec3 perPixelNormal;" + "\n"\ + "void main()\n"\ + "{\n"\ + " perPixelNormal = normalize(qNormal);\n"\ + " float intensity = max(dot(perPixelNormal, qLightDirection), 0.0);\n" + ); +} + + + +QString QGLColladaFxEffect::sid() +{ + return d->sid; +} + + + +QGLTexture2D* QGLColladaFxEffect::diffuseTexture() +{ + return d->diffuseTexture; +} + + +void QGLColladaFxEffect::setLighting(int lighting) +{ + d->lighting = lighting; +} + +int QGLColladaFxEffect::lighting() +{ + return d->lighting; +} + +/*! + Sets this effect to use \a newMaterial. If \a newMaterial is 0, sets this + effect to have no material, and instead use whatever material is set + on the QGLPainter. + + \sa QGLPainter, material() +*/ +void QGLColladaFxEffect::setMaterial(QGLMaterial* newMaterial) +{ + d->material = newMaterial; +} + +/*! + Returns a pointer to the material of this effect. If the effect has no material, + this function returns 0; +*/ +QGLMaterial* QGLColladaFxEffect::material() +{ + return d->material; +} + +/*! + Returns true if the effect is currently active (applied to a QGLPainter) + and false if it is not. + */ +bool QGLColladaFxEffect::isActive() +{ + return d->currentlyActive; +} + +void QGLColladaFxEffect::setActive(QGLPainter *painter, bool flag) +{ + d->currentlyActive = flag && !vertexShader().isEmpty() && + !fragmentShader().isEmpty(); + QGLShaderProgramEffect::setActive(painter, d->currentlyActive); +} diff --git a/src/threed/effects/qglcolladafxeffect.h b/src/threed/effects/qglcolladafxeffect.h new file mode 100644 index 000000000..fc224a87f --- /dev/null +++ b/src/threed/effects/qglcolladafxeffect.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLCOLLADAEFFECT_H +#define QGLCOLLADAEFFECT_H + +#include <QStack> +#include <QList> +#include <QGLShaderProgram> +#include "qglshaderprogrameffect.h" + +class QXmlStreamReader; +class QGLColladaParam; +class QGLColladaFxEffectPrivate; + +class Q_QT3D_EXPORT QGLColladaFxEffect : public QGLShaderProgramEffect +{ + friend class QGLColladaFxEffectFactory; + +public: + enum Lighting + { + NoLighting, + BlinnLighting, + PhongLighting, + ConstantLighting, + LambertLighting, + CustomLighting + }; + + QGLColladaFxEffect(); + ~QGLColladaFxEffect(); + void update(QGLPainter *painter, QGLPainter::Updates updates); + void generateShaders(); + void addBlinnPhongLighting(); + + void setId(QString); + void setSid(QString); + QString id(); + QString sid(); + + void setLighting(int lighting); + int lighting(); + void setMaterial(QGLMaterial* newMaterial); + QGLMaterial* material(); + + QGLTexture2D* diffuseTexture(); + + bool isActive(); + void setActive(QGLPainter *painter, bool flag); +private: + QGLColladaFxEffect(const QGLColladaFxEffect&); + QGLColladaFxEffectPrivate* d; +}; + +#endif // QGLCOLLADAEFFECT_H diff --git a/src/threed/effects/qglcolladafxeffect_p.h b/src/threed/effects/qglcolladafxeffect_p.h new file mode 100644 index 000000000..ac47898d5 --- /dev/null +++ b/src/threed/effects/qglcolladafxeffect_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLCOLLADAFXEFFECT_P_H +#define QGLCOLLADAFXEFFECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QString> +#include <QMap> +#include <QColor> +#include "qgltexture2d.h" +#include "qglcolladafxeffect.h" + +class QGLPainter; +class QGLShaderProgram; +class QGLMaterial; + +class QGLColladaFxEffectPrivate +{ + friend class QGLColladaFxEffectFactory; +public: + QGLColladaFxEffectPrivate(); + ~QGLColladaFxEffectPrivate(); + + void addMaterialChannelsToShaderSnippets(const QGLMaterial *material); + void resetGlueSnippets(); + void setTextureUniform(QGLShaderProgram *program, QGLPainter* painter, QString channelName, QGLTexture2D* texture, int* textureUnit, QColor fallbackColor); + void updateMaterialChannelSnippets(QString channelName, QGLTexture2D* texture, int* textureUnit, QColor fallbackColor); + + QString id; + QString sid; + QString name; + + // The spec allows for 3D textures as well, but for now only 2D is + // supported + QGLTexture2D* emissiveTexture; + QGLTexture2D* ambientTexture; + QGLTexture2D* diffuseTexture; + QGLTexture2D* specularTexture; + int lighting; + QGLMaterial* material; + + QStringList vertexShaderCodeSnippets; + QStringList vertexShaderDeclarationSnippets; + QStringList vertexShaderVariableNames; + + QStringList fragmentShaderCodeSnippets; + QStringList fragmentShaderDeclarationSnippets; + QStringList fragmentShaderVariableNames; + + QString vertexShaderEndGlueSnippet; + QString vertexShaderMainGlueSnippet; + QString fragmentShaderEndGlueSnippet; + QString fragmentShaderMainGlueSnippet; + + bool currentlyActive; +}; + +#endif // QGLCOLLADAFXEFFECT_P_H diff --git a/src/threed/effects/qglcolladafxeffectfactory.cpp b/src/threed/effects/qglcolladafxeffectfactory.cpp new file mode 100644 index 000000000..3728b00b5 --- /dev/null +++ b/src/threed/effects/qglcolladafxeffectfactory.cpp @@ -0,0 +1,1688 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QVector> +#include <QFile> +#include <QFileInfo> +#include <QTime> +#include <QDir> +#include "qgltexture2d.h" +#include "qgl.h" + +#include "qglcolladafxeffect.h" +#include "qglcolladafxeffectfactory.h" +//#include "qglcolladafxeffectfactory_p.h" +#include "qglcolladafxeffect_p.h" + +// The QGLColladaFxFactory class creates a list of QGLColladaFx objects from +// COLLADA FX information according to the 1.4.1 March 2008 Release from +// http://www.khronos.org/collada/ + +//#define DEBUG_QGL_COLLADA_PARSING +//#define DEBUG_MATERIALS + +#define INDENT_SIZE 4 + +static int QGLColladaIndentLevel = 0; + +// This is a simple RAII helper class to conveniently indent across +// various functions when building up the collada elements. +// Instantiate an indent to increment the indent level with that scope. +// add an indent object to the beginning of a string to indent it +// the correct amount. +class Indent +{ +public: + static int currentIndent() { return QGLColladaIndentLevel; }; + Indent() + { + ++QGLColladaIndentLevel; + }; + ~Indent() + { + --QGLColladaIndentLevel; + } +}; + +QString operator +(Indent&, QString string) +{ + return QString(QGLColladaIndentLevel * INDENT_SIZE, QLatin1Char(' ')) + string; +} + +QString operator +(char c, Indent&) +{ + return QLatin1Char(c) + QString(QGLColladaIndentLevel * INDENT_SIZE, QLatin1Char(' ')); +} + +// xml convenience function - find the first end tag with the given tagname +// Note that this is not as smart as xml.skipCurrentElement(), and does +// not handle the case where (grand)children node share tagName +// With this caveat, can be called from within any (grand)child node. +static inline void findEndTag( QXmlStreamReader& xml, QString tagName ) +{ + while ( !xml.atEnd() && !( xml.tokenType() == xml.EndElement && xml.name() == tagName)) + { + xml.readNext(); + } +} + + + +/*! + \internal + Convenience function to find parameters in \a resultState that have an id, + sid, or name that is equal to \a stringToMatch + Note that this using this is not in line with the Collada specification, + and that non-unique sids will always return the last parameter parsed. + */ +static inline QVariant findParameterVariant(ResultState* resultState, QString stringToMatch) +{ + QVariant result = resultState->paramSids.value(stringToMatch); + + if ( result.isNull() ) + result = resultState->paramIds.value(stringToMatch); + + if ( result.isNull() ) + result = resultState->paramNames.value(stringToMatch); + + return result; +} + +/*! + Parse a collada 1.4 or 1.5 .dae file \a fileName, find the effects in the + library_effects element, and convert these into a list of QGLColladaFxEffect objects + suitable for use with Qt3D. Specific effects can be identified out of + this list using their sid. + + This is the only function intended to be called from outside this class, e.g.: + + QList<QGLColladaFxEffect*> colladaEffects = QGLColladaFxEffectFactory::loadEffectsFromFile("myColladaFile.dae"); + + \sa QGLColladaFxEffect +*/ +QList<QGLColladaFxEffect*> QGLColladaFxEffectFactory::loadEffectsFromFile( const QString& fileName ) +{ + QFile file( fileName ); + if ( !file.open( QIODevice::ReadOnly )) + { + qWarning() << "Warning: QGLColladaFxEffect failed to open file" << fileName; + return QList<QGLColladaFxEffect*>(); + } + + + QXmlStreamReader xml; + xml.setDevice( &file ); + if (xml.tokenType() == QXmlStreamReader::Invalid) + file.readLine(); + xml.setDevice( &file ); + + QFileInfo fileInfo(file); + + return loadEffectsFromXml( xml, fileInfo.dir() ); +} + + + +QString QGLColladaFxEffectFactory::exportEffect(QGLColladaFxEffect *effect, QString effectId, QString techniqueSid) +{ + QStringList result; + + result += QLatin1String("<?xml version=\"1.0\"?>"); + + result += QLatin1String("<COLLADA "\ + "xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.5.0\" >"); + + { + Indent indent; + result += indent + QLatin1String("<asset>"); + { + Indent indent; + QDateTime time = QDateTime::currentDateTime(); + result += indent + QLatin1String("<created>") + time.toString(Qt::ISODate) + QLatin1String("</created>"); + result += indent + QLatin1String("<modified>") + time.toString(Qt::ISODate) + QLatin1String("</modified>"); + } + result += indent + QLatin1String("</asset>"); + + result += indent + QLatin1String("<library_effects>"); + { + Indent indent; + result += indent + QLatin1String("<effect id=\"") + effectId + QLatin1String("\">"); + result += glslProfileFromEffect(effect, techniqueSid); + result += indent + QLatin1String("</effect>"); + } + result += indent + QLatin1String("</library_effects>"); + result += QLatin1String("</COLLADA>"); + return result.join(QLatin1String("\n")); + } +} + + +/*! + \internal + parse the top level \a xml from a .dae file and process the library_effects elements therein. +*/ +QList<QGLColladaFxEffect*> QGLColladaFxEffectFactory::loadEffectsFromXml( QXmlStreamReader& xml, QDir homeDirectory ) +{ + ResultState resultState; + resultState.sourceDir = homeDirectory; + QList<QGLColladaFxEffect*> result; + + while ( !xml.atEnd() ) { + xml.readNextStartElement(); + if ( xml.name() == "library_effects" ) { + result += processLibraryEffectsElement( xml , &resultState ); + } else if (xml.name() == "library_images") + { + processLibraryImagesElement( xml, &resultState ); + } + } + + // Try and resolve outstanding textures + QList<QGLTexture2D*> unresolvedTexturePointers = resultState.unresolvedTexture2Ds.keys(); + for (int i = 0; i < unresolvedTexturePointers.count(); i++) + { + QGLTexture2D* texture = unresolvedTexturePointers[i]; + QString parameterName = resultState.unresolvedTexture2Ds.value(texture); + resolveTexture2DImage(texture, &resultState, parameterName); + } + return result; +} + + + +QGLColladaParam::~QGLColladaParam() +{ + +} + + + +/*! + \internal +*/ +int QGLColladaParam::type() +{ + return mType; +} + + + +/*! + \internal +*/ +QString QGLColladaParam::sid() +{ + return mSid; +} + + + +/*! + \internal +*/ +QVector<float> QGLColladaParam::value() { + return mValue; +} + + + +/*! + \internal +*/ +QGLColladaParam::QGLColladaParam(QString sid, int type) : mSid(sid) + , mType(type) +{ +} + + + +/*! + \internal +*/ +QString QGLColladaParam::id() +{ + return mId; +} + + + +/*! + \internal +*/ +QString QGLColladaParam::typeString(int type) +{ + const char* typeStringArray[] = { + "UnkownType", + "Sampler2DType", + "Texture2DType", + "SurfaceType", + "ImageType"}; + + if (type >= UserDefinedType) + return QLatin1String("UserDefinedType"); + else if ( type < 0 || type > ImageType) + return QLatin1String("Unrecognized Type"); + else + return QLatin1String(typeStringArray[type]); +} + + + +/*! + \internal +*/ +QGLColladaTextureParam::QGLColladaTextureParam(QString sid, QGLTexture2D* texture) : QGLColladaParam(sid, Texture2DType) +{ + mTexture = texture; +} + + + +/*! + \internal +*/ +QGLTexture2D* QGLColladaTextureParam::texture() +{ + Q_ASSERT( mType == Sampler2DType || mType == Texture2DType); + return mTexture; +} + + + +/*! + \internal +*/ +QString QGLColladaTextureParam::samplerSid() +{ + return sampler2DSid; +} + + + +/*! + \internal +*/ +QGLColladaFxEffectFactory::QGLColladaFxEffectFactory() +{ +} + + + +/*! + \internal +*/ +QGLColladaSampler2DParam::QGLColladaSampler2DParam(QString sid, QGLTexture2D* texture) + : QGLColladaParam(sid, QGLColladaParam::Sampler2DType) + , mTexture(texture) +{ +} + + + +/*! + \internal +*/ +QGLColladaSampler2DParam::QGLColladaSampler2DParam(QString sid, QString sourceSid) + : QGLColladaParam(sid, QGLColladaParam::Sampler2DType) + , mTexture(0) + , mSourceSid(sourceSid) +{ +} + + + +/*! + \internal +*/ +QString QGLColladaSampler2DParam::sourceSid() +{ + return mSourceSid; +} + + + +/*! + \internal +*/ +QGLColladaSurfaceParam::QGLColladaSurfaceParam(QString sid) : QGLColladaParam(sid, QGLColladaParam::SurfaceType) + , mInitFrom() + , mFormat() + , mFormatHint() + , mSize() + , mSizeVector( 3, 0 ) + , mViewportRatio( 1.0, 1.0 ) + , mMipLevels( 0 ) + , mMipMapGenerate( false ) + , mExtra() + , mGenerator() +{ +} + + + +/*! + \internal +*/ +QGLColladaImageParam::QGLColladaImageParam(QString sid, QImage image) + : QGLColladaParam(sid, QGLColladaParam::ImageType) + , mImage(image) +{ +} + + + +/*! + \internal +*/ +QImage QGLColladaImageParam::image() +{ + return mImage; +} + + + +/*! + \internal +*/ +QString QGLColladaImageParam::name() +{ + return mName; +} + + + +/*! + \internal +*/ +QGLColladaSurfaceParam* QGLColladaFxEffectFactory::processSurfaceElement( QXmlStreamReader& xml , ResultState* resultState, QString passedInSid) +{ + Q_UNUSED(resultState); + QXmlStreamAttributes attributes = xml.attributes(); + QString surfaceSid = attributes.value(QLatin1String("sid")).toString(); + // Surfaces are the only children of a newparam, but don't have their own + // sids. For simplicity, use the parent's sid. + + if (surfaceSid.isEmpty() && !passedInSid.isEmpty()) + surfaceSid = passedInSid; + QGLColladaSurfaceParam* result = new QGLColladaSurfaceParam( surfaceSid ); + xml.readNextStartElement(); + if ( xml.name().toString().left(4) == QLatin1String("init") ) + { + if (xml.name().toString() != QLatin1String("init_from")) + qWarning() << "Warning: only ""init_from"" supported in surface element ( line:" << xml.lineNumber() << ")"; + QString init_from = xml.readElementText(); + result->mInitFrom = init_from; + resultState->paramSids[surfaceSid] = init_from; + } + return result; +} + + + +/*! + \internal + Processes a list of floating point numbers. If the list contains only 1 + element, a QVariant<float> is returned. If the list containst 2, 3 or 4 + elements, they are converted into a QVariant containing a QVector2D, + QVector3D, or QVector4D respectively. + If the list containst more elements than that, they are returned as a + QArray<float>. + +*/ +QVariant QGLColladaFxEffectFactory::processFloatList( QXmlStreamReader& xml ) +{ + QArray<float> floats; + QString elementString = xml.readElementText(); + QStringList list = elementString.split( QRegExp( QLatin1String("\\s+") ), QString::SkipEmptyParts ); + bool ok; + float f; + foreach ( QString string, list ) + { + f = string.toFloat( &ok ); + if ( ok ) + floats.append(string.toFloat()); + else + { + qWarning() << "Warning: malformed float ( line" << xml.lineNumber() << ")"; + } + } + + switch(floats.count()) + { + case 0: + return QVariant(); + // no break necessary + case 1: + return QVariant(floats[0]); + // no break necessary + case 2: + return QVariant(QVector2D(floats[0], floats[1])); + // no break necessary + case 3: + return QVariant(QVector3D(floats[0], floats[1], floats[2])); + // no break necessary + case 4: + return QVariant(QVector4D(floats[0], floats[1], floats[2], floats[3])); + // no break necessary + default: + { + QVariant result; + result.setValue(floats); + return result; + } + } + // Function should always return out of switch statement +} + + + +/*! + \internal +*/ +void QGLColladaFxEffectFactory::processSampler2DElement( QXmlStreamReader& xml, ResultState* resultState, QString passedInSid ) +{ + Q_UNUSED(resultState); + QXmlStreamAttributes attributes = xml.attributes(); + QString sid = attributes.value(QLatin1String("sid")).toString(); + if (sid.isEmpty() && !passedInSid.isEmpty()) + { + sid = passedInSid; + } + + xml.readNextStartElement(); + if ( xml.name() == QLatin1String("source")) + { + // Collada 1.4 Spec + QString sourceSurfaceSid = xml.readElementText().trimmed(); + resultState->paramSids[sid] = sourceSurfaceSid; + } + + if ( xml.name() == QLatin1String("instance_image") ) + { + // Collada 1.5 Spec + qWarning() << "collada 1.5 sampler elements not supported ( line:" << xml.lineNumber() << ")"; + } + // exit cleanly, just in case. + findEndTag( xml, QLatin1String("sampler2D")); + return; +} + + + +/*! + \internal + Parses and consumes a color collada element from \a xml. +*/ +QColor QGLColladaFxEffectFactory::processColorElement( QXmlStreamReader& xml ) +{ + QVariant floatList = processFloatList( xml ); + + QColor result( 0, 0, 0, 255 ); + if (floatList.type() == QVariant::Vector3D) + { + QVector3D vector3D = floatList.value<QVector3D>(); + if ( !vector3D.isNull()) + { + result.setRgbF( vector3D.x() + , vector3D.y() + , vector3D.z() + , 1.0 ); + return result; + } + } + else if (floatList.type() == QVariant::Vector4D) + { + QVector4D vector4D = floatList.value<QVector4D>(); + if (!vector4D.isNull()) + { + result.setRgbF( vector4D.x() + , vector4D.y() + , vector4D.z() + , vector4D.w() ); + return result; + } + } + qWarning() << "Warning: Malformed color element ( line" << xml.lineNumber() << ")"; + return result; +} + + + +/*! + \internal + Parses and consumes an fx_common_color_or_texture_type collada element from \a xml. +*/ +QVariant QGLColladaFxEffectFactory::processColorOrTextureElement( QXmlStreamReader& xml ) +{ + if ( xml.name() == QLatin1String("color")) + { + return processColorElement( xml ); + } else if ( xml.name() == QLatin1String("texture") ) + { + qWarning() << "Warning: texture element not supported ( line" << xml.lineNumber()<<")"; + } else + { + qWarning() << "Color or Texture expected ( line" << xml.lineNumber() << ")"; + } + xml.skipCurrentElement(); + return( QColor( 0, 0, 0,255 )); +} + + + +/*! + \internal + Parses an fx_common_float_or_param_type collada element from \a xml. + Always consumes the element. +*/ +float QGLColladaFxEffectFactory::processParamOrFloatElement( QXmlStreamReader& xml ) +{ + if ( xml.name() == QLatin1String("param") ) + { + qWarning() << "Warning: params not supported ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + return 0.0; + } else + { + return xml.readElementText().toFloat(); + } +} + + + +/*! + Parses and consumes a library_images collada element pointed to by \a xml, + and pushes any images found onto the \a resultState for use in resolving + elements later. +*/ +void QGLColladaFxEffectFactory::processLibraryImagesElement( QXmlStreamReader& xml, ResultState* resultState ) +{ + xml.readNextStartElement(); + + if ( xml.name() == QLatin1String("asset") ) + { + qWarning() << "Warning: effect asset handling not supported in library_images element ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + while ( xml.name() == QLatin1String("image") && xml.tokenType() == QXmlStreamReader::StartElement ) + { + processImageElement( xml , resultState ); + xml.skipCurrentElement(); + xml.readNextStartElement(); + } +} + + +/*! + \internal +*/ +QList<QGLColladaFxEffect*> QGLColladaFxEffectFactory::processLibraryEffectsElement( QXmlStreamReader& xml, ResultState* resultState ) +{ + QList<QGLColladaFxEffect*> result; + // A collada library_effects is + // 0 or 1 <asset> + // 1 or more <effect>, + // 0 or more <extra>; + xml.readNextStartElement(); + + if ( xml.name() == QLatin1String("asset") ) + { + qWarning() << "Warning: effect asset handling not supported in effects library ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + while ( xml.name() == QLatin1String("effect") && xml.tokenType() == QXmlStreamReader::StartElement ) + { + result += processEffectElement( xml , resultState ); + xml.readNextStartElement(); + } + + while ( xml.name() == QLatin1String("extra") ) + { + qWarning() << "Warning: extra element not handled in effects library ( line" << xml.lineNumber() << ")"; + xml.readNextStartElement(); + } + + // be sure to exit cleanly + findEndTag(xml, QLatin1String("library_effects")); + return result; +} + + + +/*! + \internal + Parses and consumes an effect element from \a xml. +*/ +QList<QGLColladaFxEffect*> QGLColladaFxEffectFactory::processEffectElement( QXmlStreamReader& xml, ResultState* resultState ) +{ + // An effect element is: + // 0 or 1 <annotate> + // 0 or more newparam + // 1 or more profile ( either <profile_BRIDGE>, <profile_CG>, <profile_GLES>, <profile_GLES2>, <profile_GLSL>, or <profile_COMMON> + // 0 or more <extra> + + QList<QGLColladaFxEffect*> result; + xml.readNextStartElement(); + + if ( xml.name() == QLatin1String("annotate") ) + { + qWarning() << "effect annotation not supported ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + while ( xml.name() == QLatin1String("newparam") && xml.tokenType() == QXmlStreamReader::StartElement ) + { + processNewparamElement( xml , resultState ); + xml.readNextStartElement(); + } + + // find any of the profile_* elements defined in the spec + QRegExp profileRegExp( QLatin1String("profile_(BRIDGE|CG|GLES2?|GLSL|COMMON)") ); + while ( profileRegExp.indexIn( xml.name().toString() ) == 0 && xml.tokenType() == QXmlStreamReader::StartElement ) + { + result += processProfileElement( xml, resultState ); + xml.readNextStartElement(); + } + + findEndTag(xml, QLatin1String("effect")); + return result; +} + + + +/*! + \internal +*/ +QList<QGLColladaFxEffect*> QGLColladaFxEffectFactory::processProfileElement( QXmlStreamReader& xml, ResultState* resultState ) +{ + // A profile_GLES2 element is: + // 0 or 1 <asset> + // 0 or more <code> or <include> + // 0 or more <newparam> + // 1 or more <technique> + + // A profile_GLSL element is: + // 0 or 1 <asset> + // 0 or more <code> + // 0 or more <include> + // 0 or more <newparam> + // 1 or more <technique> + // 0 or more <extra> + + // A profile_COMMON element is + // an optional id element + // 0 or 1 <asset> + // 0 or more <newparam> + // 1 <technique> + // 0 or more <extra> + + // Note: techniques need to be handled differently for different profiles + + QString rootNodeString = xml.name().toString(); + QList<QGLColladaFxEffect*> result; + + xml.readNextStartElement(); + if ( xml.name() == QLatin1String("asset") ) + { + qWarning() << "Warning: asset element not supported in " << rootNodeString << "elements ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + if (rootNodeString == QLatin1String("profile_GLSL")) + { + while ( xml.name() == QLatin1String("code") ) + { + QString codeSid = xml.attributes().value(QLatin1String("sid")).toString(); + QString codeText = xml.readElementText(); + resultState->paramSids[codeSid] = codeText; + + findEndTag(xml, QLatin1String("code")); + xml.readNextStartElement(); + } + + while ( xml.name() == QLatin1String("include") ) + { + QString includeSid = xml.attributes().value(QLatin1String("sid")).toString(); + QString includeUrl = xml.attributes().value(QLatin1String("url")).toString(); + + // create an include param? + qWarning() << "Warning: include element not supported in " << rootNodeString << "elements ( line" << xml.lineNumber() << ")"; + + findEndTag(xml, QLatin1String("include")); + xml.readNextStartElement(); + } + + } + + while ( xml.tokenType() == QXmlStreamReader::StartElement && + ( xml.name() == QLatin1String("newparam") || xml.name() == QLatin1String("image") )) + { + if ( xml.name() == QLatin1String("newparam") ) + processNewparamElement( xml , resultState ); + else if ( xml.name() == QLatin1String("image") ) + processImageElement( xml , resultState ); + + xml.readNextStartElement(); + } + + + while ( xml.name() == QLatin1String("technique") ) + { + result.append( + processTechniqueElement( xml, resultState, rootNodeString )); + xml.readNextStartElement(); + // only 1 technique in profile_COMMON + if ( rootNodeString == QLatin1String("profile_COMMON")) + { + break; + } + }; + + while ( xml.name() == QLatin1String("extra") ) + { + qWarning() << "extra elements currently not supported in " << rootNodeString << "elements ( line" << xml.lineNumber() << ")"; + findEndTag( xml, QLatin1String("extra") ); + xml.readNextStartElement(); + }; + + findEndTag( xml, rootNodeString ); + return result; +} + + +QGLColladaParam* QGLColladaFxEffectFactory::processPassElement( QXmlStreamReader& xml, ResultState* resultState, QGLColladaFxEffect* effect ) +{ + QGLColladaParam* result = 0; + // a profile_GLSL pass is: + // 0 or 1 <annotate> + // 0 or 1 <states> + // 0 or 1 <program> (CG, GLES2 or GLSL only) + // 0 or 1 <evaluate> + // 0 or 1 <extra> + + xml.readNextStartElement(); + + if ( xml.name() == QLatin1String("annotate") ) + { + qWarning() << "Warning: annotate element not supported ( line" << xml.lineNumber() << ")"; + findEndTag( xml, QLatin1String("annotate") ); + xml.readNextStartElement(); + } + + if ( xml.name() == QLatin1String("states") ) + { + qWarning() << "Warning: states element not supported ( line" << xml.lineNumber() << ")"; + findEndTag( xml, QLatin1String("states") ); + xml.readNextStartElement(); + } + + // 0 or 1 <program> (CG, GLES2 or GLSL only) + if ( xml.name() == QLatin1String("program") ) + { + processProgramElement( xml, resultState, effect ); + findEndTag( xml, QLatin1String("program") ); + xml.readNextStartElement(); + } + + // 0 or 1 <evaluate> + if ( xml.name() == QLatin1String("evaluate") ) + { + qWarning() << "Warning: evaluate element not supported ( line" << xml.lineNumber() << ")"; + findEndTag( xml, QLatin1String("evaluate") ); + xml.readNextStartElement(); + } + + // 0 or more <extra> + while ( xml.name() == QLatin1String("extra") ) + { + qWarning() << "Warning: extra element not supported ( line" << xml.lineNumber() << ")"; + findEndTag( xml, QLatin1String("extra") ); + xml.readNextStartElement(); + } + + findEndTag( xml, QLatin1String("pass")); + return result; +} + +QGLColladaFxEffect* QGLColladaFxEffectFactory::processTechniqueElement( QXmlStreamReader& xml, ResultState* resultState, QString &profileName ) +{ + // A 1.4 technique is: + // 0 or 1 <asset> + // 0 or more <newparam> or <image> (in any order) + // 0 or more of <constant>, <lambert>, <phong>, <blinn> + // 0 or more <extra> + + // A 1.5 profile_COMMON technique is: + // 0 or 1 <asset> + // exactly 1 of <blinn>, <constant>, <lambert>, or <phong> + // 0 or more <extra> + + // a profile_GLSL technique is: + // 0 or 1 <asset> + // 0 or more <annotate> + // 1 or more <pass> + // 0 or more <extra> + + QGLColladaFxEffect* effect = new QGLColladaFxEffect(); + + QXmlStreamAttributes attributes = xml.attributes(); + effect->setSid( attributes.value( QLatin1String("sid") ).toString() ); + QStringRef id = attributes.value( QLatin1String("id") ); + Q_UNUSED( id ); + + xml.readNextStartElement(); + + if ( xml.name() == QLatin1String("asset") ) + { + qWarning() << "Warning: asset element not supported ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + while ( xml.name() == QLatin1String("annotate") && xml.tokenType() == QXmlStreamReader::StartElement ) + { + qWarning() << "Warning: annotate element not supported ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + // Collada specifies exactly one of blinn, lambert, constant or phong + // If the effect is malformed, default QGLMaterial will be used. + QGLMaterial* material = new QGLMaterial; + + if ( profileName == QLatin1String("profile_COMMON") && + (xml.name() == QLatin1String("blinn") || xml.name() == QLatin1String("phong") || + xml.name() == QLatin1String("constant") || xml.name() == QLatin1String("lambert")) ) + { + if ( xml.name() == QLatin1String("blinn") ) + { + effect->setLighting( QGLColladaFxEffect::BlinnLighting ); + } else if ( xml.name() == QLatin1String("phong") ) { + effect->setLighting( QGLColladaFxEffect::PhongLighting ); + } else if ( xml.name() == QLatin1String("constant") ) { + effect->setLighting( QGLColladaFxEffect::ConstantLighting ); + } else if ( xml.name() == QLatin1String("lambert") ) { + effect->setLighting( QGLColladaFxEffect::LambertLighting ); + } + + // TODO: get appropriate shader fragments for the specified lighting models + if ( xml.readNextStartElement() ) + { + // a blinn element is 0 or 1 of each of: + // <emission> + // <diffuse> + // <specular> + // <shininess> + // <reflectivity> + // <transparent> + // <transparency> + // <index_of_refraction> + + if ( xml.name() == QLatin1String("emission") ) + { + if ( xml.readNextStartElement() ) + { + // handle color or texture element: + if (xml.name() == QLatin1String("color")) + { + material->setEmittedLight( processColorElement( xml )); +#ifdef DEBUG_MATERIALS + qDebug() << "set emitted light to " << material->emittedLight(); +#endif + } + else if ( xml.name() == QLatin1String("texture")) + { + effect->d->emissiveTexture = processTextureElement( xml, resultState ); +#ifdef DEBUG_MATERIALS + qDebug() << "set emissive texture to " << effect->d->emissiveTexture; +#endif + } else if ( xml.name() == QLatin1String("param")) + { + qWarning() << "params not supported in lighting elements ( line" << xml.lineNumber() << ")"; + } + } + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + if ( xml.name() == QLatin1String("ambient") ) + { + if ( xml.readNextStartElement() ) + { + // handle color or texture element: + if (xml.name() == QLatin1String("color")) + { + material->setAmbientColor( processColorElement( xml )); +#ifdef DEBUG_MATERIALS + qDebug() << "set ambient color to " << material->ambientColor(); +#endif + } + else if ( xml.name() == QLatin1String("texture")) + { + effect->d->ambientTexture = processTextureElement( xml, resultState ); +#ifdef DEBUG_MATERIALS + qDebug() << "set ambient texture to " << effect->d->ambientTexture; +#endif + } else if ( xml.name() == QLatin1String("param")) + { + qWarning() << "params not supported in lighting elements ( line" << xml.lineNumber() << ")"; + } + } + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + if ( xml.name() == QLatin1String("diffuse") ) + { + if ( xml.readNextStartElement() ) + { + // handle color or texture element: + if (xml.name() == QLatin1String("color")) + { + material->setDiffuseColor( processColorElement( xml )); +#ifdef DEBUG_MATERIALS + qDebug() << "set diffuse color to " << material->diffuseColor(); +#endif + } + else if ( xml.name() == QLatin1String("texture")) + { + effect->d->diffuseTexture = processTextureElement( xml, resultState ); +#ifdef DEBUG_MATERIALS + qDebug() << "set diffuse texture to " << effect->d->diffuseTexture; +#endif + } else if ( xml.name() == QLatin1String("param")) + { + qWarning() << "params not supported in lighting elements ( line" << xml.lineNumber() << ")"; + } + } + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + if ( xml.name() == QLatin1String("specular") ) + { + if ( xml.readNextStartElement() ) + { + // handle color or texture element: + if (xml.name() == QLatin1String("color")) + { + material->setSpecularColor( processColorElement( xml )); +#ifdef DEBUG_MATERIALS + qDebug() << "set specular color to " << material->specularColor(); +#endif + } + else if ( xml.name() == QLatin1String("texture")) + { + effect->d->specularTexture = processTextureElement( xml, resultState ); +#ifdef DEBUG_MATERIALS + qDebug() << "set specular texture to " << effect->d->specularTexture; +#endif + } else if ( xml.name() == QLatin1String("param")) + { + qWarning() << "params not supported in lighting elements ( line" << xml.lineNumber() << ")"; + } + } + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + if ( xml.name() == QLatin1String("shininess") ) + { + if ( xml.readNextStartElement() ) + { + float shininess = processParamOrFloatElement( xml ); + if ( 0.0 < shininess && shininess < 1.0 ) + { + qWarning() << "Warning: Blinn-Torrance-Sparrow specular lighting not suported ( line" << xml.lineNumber() + << "), converting to Blinn-Phong specular model"; + material->setShininess( int( shininess * 128.0 )); + } + else + material->setShininess( int( shininess ) ); +#ifdef DEBUG_MATERIALS + qDebug() << "set shininess to " << material->shininess(); +#endif + } + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + if ( xml.name() == QLatin1String("reflective") ) + { + qWarning() << "Warning reflective not supported ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + if ( xml.name() == QLatin1String("reflectivity") ) + { + qWarning() << "Warning: reflectivity not supported ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + if ( xml.name() == QLatin1String("transparent") ) + { + if ( xml.readNextStartElement() ) + { + if (xml.name() == QLatin1String("texture")) + { + QGLTexture2D* transparentTexture = processTextureElement( xml , resultState ); + Q_UNUSED(transparentTexture); + qWarning() << "Warning: transparent not supported ( line" << xml.lineNumber() << ")"; +#ifdef DEBUG_MATERIALS + qDebug() << "unused transparent texture" << transparentTexture; +#endif + } + else if (xml.name() == QLatin1String("color")) + { + QColor transparent = processColorElement( xml ); + Q_UNUSED( transparent ); + qWarning() << "Warning: transparent not supported ( line" << xml.lineNumber() << ")"; +#ifdef DEBUG_MATERIALS + qDebug() << "unused transparent color of " << transparent; +#endif + } + } + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + if ( xml.name() == QLatin1String("transparency") ) + { + if ( xml.readNextStartElement() ) + { + float transparency = processParamOrFloatElement( xml ); + if ( transparency < 1.0 ) + { + qWarning() << "Warning: transparency not supported"; + } + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + } + + if ( xml.name() == QLatin1String("index_of_refraction") ) + { + if ( xml.readNextStartElement() ) + { + float indexOfRefraction = processParamOrFloatElement( xml ); + Q_UNUSED( indexOfRefraction ); + qWarning() << "Warning: index_of_refraction not supported ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + } + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + } + // end of lighting scope + + effect->setMaterial( material ); + + switch(effect->lighting()) + { + case QGLColladaFxEffect::PhongLighting: + case QGLColladaFxEffect::LambertLighting: + qWarning() << "Warning: requested lighting not supported, using Blinn-Phong instead"; + case QGLColladaFxEffect::BlinnLighting: + effect->addBlinnPhongLighting(); + case QGLColladaFxEffect::ConstantLighting: + case QGLColladaFxEffect::NoLighting: + default: + break; + } + }; + + while ( xml.name() == QLatin1String("pass") && xml.tokenType() == QXmlStreamReader::StartElement ) + { + processPassElement( xml, resultState, effect); + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + // Make sure to exit cleanly + findEndTag( xml, QLatin1String("technique") ); + + return effect; +} + + + +QGLColladaParam* QGLColladaFxEffectFactory::processNewparamElement( QXmlStreamReader& xml, ResultState* resultState) +{ + QXmlStreamAttributes attributes = xml.attributes(); + QString sidString = attributes.value(QLatin1String("sid")).toString(); + QGLColladaParam* result = 0; + if (xml.readNextStartElement()) + { + if (xml.name().toString().left(5) == QLatin1String("float")) + { + + QVariant floatValues = processFloatList( xml ); + resultState->paramSids[sidString] = floatValues; + + if ( xml.name() == QLatin1String("float") + && ( floatValues.type() != + static_cast<QVariant::Type>(QMetaType::Float ))) + { + qWarning() << "Warning: parsed type incorrectly, expected float ( line" << xml.lineNumber() << ")"; + resultState->paramSids[sidString] = floatValues.value<float>(); + } + else if ( xml.name() == QLatin1String("float2") + && floatValues.type() != QVariant::Vector2D ) + { + qWarning() << "Warning: parsed type incorrectly, expected float2 ( line" << xml.lineNumber() << ")"; + } + else if ( xml.name() == QLatin1String("float3") + && floatValues.type() != QVariant::Vector3D ) + { + qWarning() << "Warning: parsed type incorrectly, expected float3 ( line" << xml.lineNumber() << ")"; + } + else if ( xml.name() == QLatin1String("float4") + && floatValues.type() != QVariant::Vector4D) + { + qWarning() << "Warning: parsed type incorrectly, expected float4 ( line" << xml.lineNumber() << ")"; + } + } else if ( xml.name() == QLatin1String("sampler2D") ) + { + processSampler2DElement( xml , resultState, sidString ); + } else if ( xml.name() == QLatin1String("surface") ) + { + result = processSurfaceElement( xml, resultState, sidString); + } else { + qWarning() << "unrecognized parameter type ( line:" << xml.lineNumber() << ")"; + findEndTag( xml, QLatin1String("newparam") ); + return 0; + } + } + findEndTag(xml, QLatin1String("newparam")); + return result; +} + + +/*! + \internal + the library_images can come after the library_effects, so textures referenced + in effects might not have been defined when the effect was created. Try and + resolve those images now. (Other properties - wrap, mipmap and filters etc + should have been resolved when the texture was originally parsed). + */ +bool QGLColladaFxEffectFactory::resolveTexture2DImage(QGLTexture2D *texture, ResultState *resultState, QString paramName) +{ + if (texture == 0) + { + qWarning() << "Warning: Cannot resolve images for null QGLTexture2D"; + return false; + } + QVariant samplerParam = findParameterVariant(resultState, paramName); + QString surfaceName = samplerParam.value<QString>(); + QImage image; + + if (!surfaceName.isEmpty()) + { + QVariant surfaceParam = findParameterVariant(resultState, surfaceName); + QString initFrom = surfaceParam.value<QString>(); + if (!initFrom.isEmpty()) + { + image = resolveImageURI(resultState, initFrom); + } + } + + // If that's failed, try again with the passed in paramName + if (image.isNull()) + { + image = resolveImageURI(resultState, paramName); + } + + texture->setImage(image); + return !image.isNull(); +} + + + + +Q_DECLARE_METATYPE(QGLTexture2D*); +/*! + \internal + Parses and consumes a texture collada element from \a xml. +*/ +QGLTexture2D* QGLColladaFxEffectFactory::processTextureElement( QXmlStreamReader& xml , ResultState* resultState ) +{ + QGLTexture2D* result = new QGLTexture2D(); + QXmlStreamAttributes attributes = xml.attributes(); + + foreach (QXmlStreamAttribute attribute, attributes) + { + if ( attribute.name() == QLatin1String("texture") ) + { + QString paramName = attribute.value().toString(); + + // in Collada Fx Textures must reference a previously defined + // sampler2D param. + // However, this sampler may refer to images in the library_images, + // which is parsed after the library_effects, so try and resolve + // now, but save failures to try again later. + + if (!resolveTexture2DImage(result, resultState, paramName)) + { + resultState->unresolvedTexture2Ds[result] = paramName; + } + } else if ( attribute.name() == QLatin1String("texcoord") ) + { + // TODO: Create a repository for this sort of data, to be used in creating the shader progams later + // I'm pretty sure the effect is going to need to be passed in. + qWarning() << "texcoord not supported yet: " << attribute.name() << attribute.value() << " ( line" << xml.lineNumber() << ")"; + } else if (attribute.name() == QLatin1String("extra")) + { + qWarning() << "extra elements in texture elements not supported ( line" << xml.lineNumber() << ")"; + } + } + xml.skipCurrentElement(); + + return result; +} + +/*! + \internal + Try and get an image to attach to a texture. The URI could be a reference to + a param in the collada document (which may in turn be a reference), or + file referenced either absolutely or relative to the original dae file. + (It could concievably be a web URL, but that is not handled here.) + */ +QImage QGLColladaFxEffectFactory::resolveImageURI(ResultState *resultState, QString URI) +{ + QImage result; + QString imageFileName; + QString workingURI = URI; + if (workingURI.length() > 0 && workingURI.at(0) == QLatin1Char('#')) + { + workingURI = workingURI.right(workingURI.length() - 1); + } + + QVariant potentialParameter = findParameterVariant(resultState, workingURI); + // Might be parameter itself: + if ( !potentialParameter.value<QImage>().isNull() ) + return potentialParameter.value<QImage>(); + // or might be another URI + if (!potentialParameter.value<QString>().isNull()) + { + imageFileName = potentialParameter.value<QString>(); + } else { + imageFileName = workingURI; + } + + // First try as a relative path. + QString filePath = resultState->sourceDir.path() + QLatin1Char('/') + imageFileName; + result.load(filePath); + if (result.isNull()) + { + // No relative file found, so try as an absolute path + result.load(imageFileName); + } + return result; +} + +/*! + \internal + Parses and consumes an image element from \a xml. +*/ +void QGLColladaFxEffectFactory::processImageElement( QXmlStreamReader& xml, ResultState* resultState ) +{ + // 1.4 has a bunch of optional values in the attributes: + QString sid = xml.attributes().value(QLatin1String("sid")).toString(); + QString id = xml.attributes().value(QLatin1String("id")).toString(); + QString name = xml.attributes().value(QLatin1String("name")).toString(); + + QString height = xml.attributes().value(QLatin1String("height")).toString(); + QString width = xml.attributes().value(QLatin1String("width")).toString(); + QString depth = xml.attributes().value(QLatin1String("depth")).toString(); + + Q_UNUSED(height); + Q_UNUSED(width); + Q_UNUSED(depth); + + QImage result; + + xml.readNextStartElement(); + if (xml.name() == QLatin1String("asset")) + { + qWarning() << "asset element not supported in image elements ( line" << xml.lineNumber() << ")"; + xml.skipCurrentElement(); + xml.readNextStartElement(); + } + + if (xml.name() == QLatin1String("init_from")) + { + QString imageFileName = xml.readElementText().trimmed(); + QDir sourceDir = resultState->sourceDir; + // ignore path information for resources + QString filePath = sourceDir.path() + QLatin1Char('/') + imageFileName; + result.load(filePath); + if (result.isNull()) + { + // Catch resources or files with absolute paths + result.load(imageFileName); + } + if (!sid.isEmpty()) + resultState->paramSids[sid] = result; + if (!id.isEmpty()) + resultState->paramIds[id] = result; + if (!name.isEmpty()) + resultState->paramNames[name] = result; + } + + // exit cleanly + findEndTag( xml, QLatin1String("image")); +} + +QStringList QGLColladaFxEffectFactory::glslProfileFromEffect(QGLColladaFxEffect* effect, QString sid) +{ + Q_UNUSED(effect) + Indent indent; + QStringList result; + result += indent + QLatin1String("<profile_GLSL>"); + { + result += generateCodeElements(effect, sid); + result += indent + QLatin1String("<technique sid=\"") + sid + QLatin1String("\">"); + { + Indent indent; + result += indent + QLatin1String("<pass>"); + result += generateProgramElement(effect, sid); + result += indent + QLatin1String("</pass>"); + } + result += indent + QLatin1String("</technique>"); + } + result += indent + QLatin1String("</profile_GLSL>"); + + return result; +} + +QStringList QGLColladaFxEffectFactory::generateProgramElement(QGLColladaFxEffect* effect, QString techniqueSid) +{ + QStringList result; + QString vertexShaderRefSid = QLatin1String("VertexShaderRefSidRefsCodeOrIncludeAtProfileOrEffectLevel"); + QString fragmentShaderRefSid = QLatin1String("FragmentShaderRefSidRefsCodeOrIncludeAtProfileOrEffectLevel"); + Indent indent; + result += indent + QLatin1String("<program>"); + result += generateShaderElement(effect, techniqueSid + QLatin1String("VertexShader"), techniqueSid + QLatin1String("FragmentShader")); + // 0 or more + result += generateBindAttributeElement( effect ); + // 0 or more + result += generateBindUniformElements( effect ); + result += indent + QLatin1String("</program>"); + return result; +} + +QStringList QGLColladaFxEffectFactory::generateShaderElement( QGLColladaFxEffect* effect, QString vertexShaderRefSid, QString fragmentShaderRefSid ) +{ + Q_UNUSED(effect); + QStringList result; + Indent indent; + result += indent + QLatin1String("<shader stage=\"VERTEX\">"); + { + Indent indent; + result += indent + QLatin1String("<sources>"); + { + // 0 or more <import> elements + Indent indent; + result += indent + QLatin1String("<import ref=\"") + vertexShaderRefSid + QLatin1String("\"/>"); + } + result += indent + QLatin1String("</sources>"); + // 0 or <extra> elements; + } + result += indent + QLatin1String("</shader>"); + + result += indent + QLatin1String("<shader stage=\"FRAGMENT\">"); + { + Indent indent; + result += indent + QLatin1String("<sources>"); + { + Indent indent; + result += indent + QLatin1String("<import ref=\"") + fragmentShaderRefSid + QLatin1String("\"/>"); + } + result += indent + QLatin1String("</sources>"); + // <extra> element(s) here if necessary; + } + result += indent + QLatin1String("</shader>"); + return result; +} + +QStringList QGLColladaFxEffectFactory::generateBindAttributeElement( QGLColladaFxEffect* effect ) +{ + // Currently no need for bind_attribute elements. + Q_UNUSED(effect); + QStringList result; + // Indent indent; + // result += indent + "<bind_attribute>"; + // result += indent + "</bind_attribute>"; + return result; +} + +QStringList generateBindUniformParamElement( QString symbol, QString ref) +{ + QStringList result; + // 0 or more <bind_uniform> elements + Indent indent; + result += indent + QLatin1String("<bind_uniform symbol=\"") + symbol + QLatin1String("\">"); + { + Indent indent; + result += indent + QLatin1String("<param ref=\"") + ref + QLatin1String("\">"); + } + result += indent + QLatin1String("</bind_uniform>"); + return result; +} + +QStringList generateBindUniformParamElement( QString symbol, const QVector3D& value) +{ + QStringList result; + // 0 or more <bind_uniform> elements + Indent indent; + result += indent + QLatin1String("<bind_uniform symbol=\"") + symbol + QLatin1String("\">"); + { + Indent indent; + result += indent + QString(QLatin1String("<float3> %1 %2 %3 </float3>")).arg(value.x()).arg(value.y()).arg(value.z()); + } + result += indent + QLatin1String("</bind_uniform>"); + return result; +} + +QStringList generateBindUniformParamElement( QString symbol, const QColor& value) +{ + QStringList result; + // 0 or more <bind_uniform> elements + Indent indent; + result += indent + QLatin1String("<bind_uniform symbol=\"") + symbol + QLatin1String("\">"); + { + Indent indent; + result += indent + QString(QLatin1String("<float3> %1 %2 %3 </float3>")).arg(value.redF()).arg(value.greenF()).arg(value.blueF()); + } + result += indent + QLatin1String("</bind_uniform>"); + return result; +} + + +QStringList QGLColladaFxEffectFactory::generateBindUniformElements( QGLColladaFxEffect* effect ) +{ + QStringList result; + if (effect == 0) + return result; + // // 0 or more <bind_uniform> elements + // Example uniforms + // result += generateBindUniformParamElement( "exampleRefSymbol", QString("exampleRef")); + // result += generateBindUniformParamElement( "exampleFloat3Symbol", QVector3D(0.1, 0.2, 0.3) ); + + if (effect->material() != 0) + { + QGLMaterial* material = effect->material(); + + // Actual uniforms: + result += generateBindUniformParamElement( QLatin1String("ambientColor"), material->ambientColor()); + result += generateBindUniformParamElement( QLatin1String("diffuseColor"), material->diffuseColor()); + result += generateBindUniformParamElement( QLatin1String("emittedLight"), material->emittedLight()); + result += generateBindUniformParamElement( QLatin1String("objectName"), material->objectName()); + result += generateBindUniformParamElement( QLatin1String("shininess"), material->shininess()); + result += generateBindUniformParamElement( QLatin1String("specularColor"), material->specularColor()); + + effect->supportsPicking(); + + // TODO: Find and store effect uniforms + // effect->bindProgramUniforms(); + } + return result; +} + +QStringList QGLColladaFxEffectFactory::generateCodeElements( QGLColladaFxEffect* effect, QString baseSid ) +{ + QStringList result; + // 0 or more <bind_uniform> elements + Indent indent; + + // put all this on one line to avoid adding carriage returns to the + // shader programs + result += indent + QLatin1String("<code sid=\"") + baseSid + QLatin1String("VertexShader\">") + + effect->vertexShader() + QLatin1String("</code>"); + + result += indent + QLatin1String("<code sid=\"") + baseSid + QLatin1String("FragmentShader\">") + + effect->fragmentShader() + QLatin1String("</code>"); + + return result; +} + +void QGLColladaFxEffectFactory::processProgramElement( QXmlStreamReader& xml, ResultState* resultState, QGLColladaFxEffect* effect ) +{ + // A profile_GLSL shader element is + // 0 or more <shader> + // 0 or more <bind_attribute> + // 0 or more <bind_uniform> + + xml.readNextStartElement(); + + while ( xml.name() == QLatin1String("shader") ) + { + // in profile_GLSL a shader is + // exactly 1 <source> + // 0 or more <extra> + + QString stage = xml.attributes().value(QLatin1String("stage")).toString(); + xml.readNextStartElement(); + if ( xml.name() == QLatin1String("sources") ) + { + // a <sources> element is + // 1 or more <inline> elements + // 0 or more <import> elements + // Note: child elements can appear in any order + + xml.readNextStartElement(); + while ( (xml.name() == QLatin1String("inline") || xml.name() == QLatin1String("import")) && xml.tokenType() == QXmlStreamReader::StartElement) + { + if ( xml.name() == QLatin1String("import")) + { + QString ref = xml.attributes().value(QLatin1String("ref")).toString(); + + QXmlStreamAttribute attr; + if (xml.attributes().count()) + { + attr = xml.attributes().first(); + } + + QVariant param = findParameterVariant(resultState, ref); + if (param.isNull() || param.type() != QVariant::String) + { + qWarning() << "null or unexpected parameter found in import element ( line" + << xml.lineNumber()<<")"; + } + else + { + if (stage == QLatin1String("VERTEX")) + { + effect->setVertexShader( param.value<QString>().toLatin1() ); + } + else if (stage == QLatin1String("FRAGMENT")) + { + effect->setFragmentShader( param.value<QString>().toLatin1() ); + } else + { + qWarning() << "unrecognized shader stage: " + << stage << " ( line" << xml.lineNumber()<<")"; + } + } + + } else if ( xml.name() == QLatin1String("inline")) + { + + } + xml.readNextStartElement(); + } + } else { + qWarning() << "collada parsing error. expected <sources> element ( line" + << xml.lineNumber()<<")"; + } + + if (xml.name() == QLatin1String("extra")) + qWarning() << "Warning: extra element not supported in profile_GLSL <shader> element ( line" << xml.lineNumber()<<")"; + + findEndTag ( xml, QLatin1String("shader")); + xml.readNextStartElement(); + } + + while ( xml.name() == QLatin1String("bind_attribute") ) + { + qWarning() << "Warning: bind_attribute element not supported ( line" << xml.lineNumber()<<")"; + findEndTag ( xml, QLatin1String("bind_attribute")); + xml.readNextStartElement(); + } + + while ( xml.name() == QLatin1String("bind_uniform") ) + { + qWarning() << "Warning: bind_uniform element not supported ( line" << xml.lineNumber()<<")"; + findEndTag ( xml, QLatin1String("bind_uniform")); + xml.readNextStartElement(); + } + + findEndTag(xml, QLatin1String("program")); + return; +} diff --git a/src/threed/effects/qglcolladafxeffectfactory.h b/src/threed/effects/qglcolladafxeffectfactory.h new file mode 100644 index 000000000..e1e31ddb5 --- /dev/null +++ b/src/threed/effects/qglcolladafxeffectfactory.h @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLCOLLADAFXEFFECTFACTORY_H +#define QGLCOLLADAFXEFFECTFACTORY_H + +#include <QVector> +#include <QXmlStreamReader> +#include <QDir> +#include "qgl.h" +#include "qgltexture2d.h" + +#include "qglcolladafxeffect.h" + +class QGLColladaFxEffect; +class QGLColladaImageParam; +class QGLColladaSurfaceParam; +class QGLColladaSampler2DParam; + +Q_DECLARE_METATYPE(QArray<float>) + +typedef struct _ResultState +{ + QMap<QString, QVariant> paramSids; + QMap<QString, QVariant> paramIds; + QMap<QString, QVariant> paramNames; + QDir sourceDir; + QMap<QGLTexture2D*, QString> unresolvedTexture2Ds; +} ResultState; + + + + +class Q_QT3D_EXPORT QGLColladaFxEffectFactory +{ +public: + // Collada parsing functions + static QList<QGLColladaFxEffect*> loadEffectsFromFile(const QString& fileName ); + +protected: + static QList<QGLColladaFxEffect*> loadEffectsFromXml( QXmlStreamReader& xml, QDir homeDirectory = QDir()); + static void processLibraryImagesElement( QXmlStreamReader& xml, ResultState* stateStack ); + static QList<QGLColladaFxEffect*> processLibraryEffectsElement( QXmlStreamReader& xml, ResultState* stateStack ); + static QList<QGLColladaFxEffect*> processEffectElement( QXmlStreamReader& xml, ResultState* stateStack ); + static QList<QGLColladaFxEffect*> processProfileElement( QXmlStreamReader& xml, ResultState* stateStack ); + + static QGLColladaParam* processPassElement( QXmlStreamReader& xml, ResultState* stateStack, QGLColladaFxEffect* effect ); + static QGLColladaFxEffect* processTechniqueElement( QXmlStreamReader& xml, ResultState* stateStack, QString &profileName ); + static QGLColladaParam* processNewparamElement( QXmlStreamReader& xml, ResultState* stateStack ); + static void processImageElement( QXmlStreamReader& xml, ResultState* stateStack ); + static QGLColladaSurfaceParam* processSurfaceElement( QXmlStreamReader& xml, ResultState* stateStack, QString passedInSid = ""); + static void processSampler2DElement( QXmlStreamReader& xml, ResultState* stateStack, QString passedInSid ); + static QGLTexture2D* processTextureElement( QXmlStreamReader& xml , ResultState* stateStack ); + static QVariant processFloatList( QXmlStreamReader& xml ); + static QColor processColorElement( QXmlStreamReader& xml ); + static float processParamOrFloatElement( QXmlStreamReader& xml ); + static QVariant processColorOrTextureElement( QXmlStreamReader& xml ); + QGLColladaFxEffectFactory(); + static void processProgramElement( QXmlStreamReader& xml, ResultState* stateStack, QGLColladaFxEffect* effect ); + + // Collada generation functions +public: + static QString exportEffect(QGLColladaFxEffect *effect, QString effectId, QString techniqueSid); + +protected: + static QStringList glslProfileFromEffect(QGLColladaFxEffect* effect, QString techniqueSid); + static QStringList generateProgramElement(QGLColladaFxEffect* effect, QString techniqueSid); + static QStringList generateShaderElement( QGLColladaFxEffect* effect, QString vertexShaderRefSid, QString fragmentShaderRefSid ); + static QStringList generateBindUniformElement( QGLColladaFxEffect* effect ); + static QStringList generateBindAttributeElement( QGLColladaFxEffect* effect ); + static QStringList generateBindUniformElements( QGLColladaFxEffect* effect ); + static QStringList generateCodeElements( QGLColladaFxEffect* effect, QString baseSid ); + + static QImage resolveImageURI(ResultState *resultState, QString imageFileName); + static bool resolveTexture2DImage(QGLTexture2D *result, ResultState *resultState, QString paramName); +}; + + + +class QGLColladaParam +{ + friend class QGLColladaFxEffectFactory; +public: + enum { + UnknownType = 0, + Sampler2DType, + Texture2DType, + SurfaceType, + ImageType, + UserDefinedType = 100 + }; + + virtual ~QGLColladaParam(); + + int type(); + QVector<float> value(); + QString sid(); + QString id(); + + static QString typeString(int); + +protected: + QGLColladaParam(QString sid, int type); + QString mSid; + QString mId; + int mType; + QVector<float> mValue; +}; + + + +class QGLColladaTextureParam : public QGLColladaParam +{ + friend class QGLColladaFxEffectFactory; +public: + QGLColladaTextureParam(QString sid, QGLTexture2D* texture); + QGLTexture2D* texture(); + QString samplerSid(); +protected: + QGLTexture2D* mTexture; + QString sampler2DSid; + QString texCoordSid; +}; + + + +class QGLColladaSurfaceParam : public QGLColladaParam +{ + friend class QGLColladaFxEffectFactory; +public: + QGLColladaSurfaceParam(QString sid); +protected: + QString mInitFrom; + QString mFormat; + QString mFormatHint; + QString mSize; + QVector<int> mSizeVector; + QPointF mViewportRatio; + int mMipLevels; + bool mMipMapGenerate; + QString mExtra; + QString mGenerator; +}; + + + +class QGLColladaSampler2DParam : public QGLColladaParam +{ + friend class QGLColladaFxEffectFactory; +public: + QGLColladaSampler2DParam(QString sid, QGLTexture2D* sampler); + QGLColladaSampler2DParam(QString sid, QString sourceSid); + QGLTexture2D sampler(); + QString sourceSid(); +protected: + QGLTexture2D* mTexture; + QString mSourceSid; +}; + + + +// "image" isn't really a param, but it shares enough that it seems sensible +// to re-use the QGLColladaParam base. +class QGLColladaImageParam : public QGLColladaParam +{ + friend class QGLColladaFxEffectFactory; +public: + QGLColladaImageParam(QString sid, QImage image); + QImage image(); + QString name(); +protected: + QImage mImage; + QString mName; +}; + +#endif // QGLCOLLADAFXEFFECTFACTORY_H diff --git a/src/threed/effects/qglcolladafxeffectloader.cpp b/src/threed/effects/qglcolladafxeffectloader.cpp new file mode 100644 index 000000000..1eff70399 --- /dev/null +++ b/src/threed/effects/qglcolladafxeffectloader.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglcolladafxeffectloader.h" + +#include <QList> +#include "qglcolladafxeffectfactory.h" +#include "qglcolladafxeffect.h" + +#include <QStringList> + +//Q_DECLARE_METATYPE(QGLColladaFxEffect) + +/*! + \class QGLColladaFxEffectLoader + \brief The QGLColladaFxEffectLoader class is a convenient way to load + effects from collada files for use with Qt3D. + \since 4.8 + \ingroup qt3d + + QGLColladaFxEffectLoader provides a simple class to create and store + QGLColladaEffect objects from Collada Fx files. It currently supports + a simple subset of the Collada 1.5 Fx specification. + + The QGLColladaEffect effects are destroyed automatically when the loader + is destroyed. If finer control over effect lifetime is required, use + QGLColladaFxEffectFactory::loadEffectsFromFile() directly. +*/ + +class QGLColladaFxEffectLoaderPrivate +{ +public: + QList<QGLColladaFxEffect*> effects; + ~QGLColladaFxEffectLoaderPrivate() + { + deleteAndClearEffects(); + } + + void deleteAndClearEffects() + { + while (effects.count()) + { + delete effects.back(); + effects.pop_back(); + } + } + +}; + + +/*! + Constructs an empty QGLColladaFxEffectLoader object. +*/ +QGLColladaFxEffectLoader::QGLColladaFxEffectLoader() : + d_ptr(new QGLColladaFxEffectLoaderPrivate()) +{ +} + +/*! + Destroys the QGLColladaFxEffectLoader and any generated QGLColladaFxEffect + objects. +*/ +QGLColladaFxEffectLoader::~QGLColladaFxEffectLoader() +{ +} + +/*! + Reads the collada file indicated by \a filename and generates + QGLColladaFxEffect objects from it. + + The QGLColladaFxEffectFactory owns all the effects it generates, and destroys + them when it is destroyed, or when a new file is loaded. + + If effects are needed from multiple files, use one QGLColladaFxEffectLoader + per file. + + Returns true if at least one effect was generated. + + \sa effectNames(), effect(), operator[](), QGLColladaFxEffectFactory::loadEffectsFromFile() + */ +bool QGLColladaFxEffectLoader::load(QString filename) +{ + Q_D(QGLColladaFxEffectLoader); + d->deleteAndClearEffects(); + d->effects = QGLColladaFxEffectFactory::loadEffectsFromFile(filename); + return d->effects.count() > 0; +} + +/*! + Returns a list of the sid attributes of effects that have been generated + \sa effect() + */ +QStringList QGLColladaFxEffectLoader::effectNames() +{ + Q_D(QGLColladaFxEffectLoader); + QStringList result; + QGLColladaFxEffect *effect; + foreach (effect, d->effects) + { + result.append(effect->sid()); + } + return result; +} + +/*! + Returns a pointer to the effect with an sid matching \a effectName, or + 0 if no such effect exists. + + \sa load() + */ +QGLColladaFxEffect* QGLColladaFxEffectLoader::effect(QString effectName) +{ + Q_D(QGLColladaFxEffectLoader); + QGLColladaFxEffect* effect; + + foreach (effect, d->effects) + { + if (effect && effect->sid() == effectName) + { + return effect; + } + } + return 0; +} + +/*! + Returns the number of effects the loader has generated and stored. + */ +int QGLColladaFxEffectLoader::count() +{ + Q_D(QGLColladaFxEffectLoader); + return d->effects.count(); +} + +/*! + Returns a pointer to the effect in position \a i. QGLColladaFxEffectLoader + makes no guarantee about the ordering of effects relative to their position + in a collada document, but does not reorder effects once they have been read. + + \sa load() + */ +QGLColladaFxEffect *QGLColladaFxEffectLoader::operator[](int i) +{ + Q_D(QGLColladaFxEffectLoader); + Q_ASSERT(i < d->effects.count()); + + return d->effects.at(i); +} diff --git a/src/threed/effects/qglcolladafxeffectloader.h b/src/threed/effects/qglcolladafxeffectloader.h new file mode 100644 index 000000000..675e0cee4 --- /dev/null +++ b/src/threed/effects/qglcolladafxeffectloader.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLCOLLADAFXEFFECTLOADER_H +#define QGLCOLLADAFXEFFECTLOADER_H + +#include <qglobal.h> +#include <QString> +#include <QStringList> +#include <QtCore/qscopedpointer.h> +#include "qt3dglobal.h" + +class QGLColladaFxEffect; +class QGLColladaFxEffectLoaderPrivate; + +class Q_QT3D_EXPORT QGLColladaFxEffectLoader +{ +public: + QGLColladaFxEffectLoader(); + ~QGLColladaFxEffectLoader(); + bool load(QString filename); + QStringList effectNames(); + QGLColladaFxEffect *effect(QString effectName); + int count(); + QGLColladaFxEffect* operator[](int); +private: + Q_DECLARE_PRIVATE(QGLColladaFxEffectLoader); + QScopedPointer<QGLColladaFxEffectLoaderPrivate> d_ptr; +}; + +#endif // QGLCOLLADAFXEFFECTLOADER_H diff --git a/src/threed/effects/qglflatcoloreffect.cpp b/src/threed/effects/qglflatcoloreffect.cpp new file mode 100644 index 000000000..5aa167db6 --- /dev/null +++ b/src/threed/effects/qglflatcoloreffect.cpp @@ -0,0 +1,350 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglflatcoloreffect_p.h" +#include "qglabstracteffect_p.h" +#include <QtOpenGL/qglshaderprogram.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLFlatColorEffect + \since 4.8 + \brief The QGLFlatColorEffect class provides a standard effect that draws fragments with a flat unlit color. + \ingroup qt3d + \ingroup qt3d::painting + \internal +*/ + +/*! + \class QGLPerVertexColorEffect + \since 4.8 + \brief The QGLPerVertexColorEffect class provides a standard effect that draws fragments with a per-vertex unlit color. + \ingroup qt3d + \ingroup qt3d::painting + \internal +*/ + +class QGLFlatColorEffectPrivate +{ +public: + QGLFlatColorEffectPrivate() + : program(0) + , matrixUniform(-1) + , colorUniform(-1) + , isFixedFunction(false) + { + } + + QGLShaderProgram *program; + int matrixUniform; + int colorUniform; + bool isFixedFunction; +}; + +/*! + Constructs a new flat color effect. +*/ +QGLFlatColorEffect::QGLFlatColorEffect() + : d_ptr(new QGLFlatColorEffectPrivate) +{ +} + +/*! + Destroys this flat color effect. +*/ +QGLFlatColorEffect::~QGLFlatColorEffect() +{ +} + +/*! + \reimp +*/ +bool QGLFlatColorEffect::supportsPicking() const +{ + return true; +} + +/*! + \reimp +*/ +void QGLFlatColorEffect::setActive(QGLPainter *painter, bool flag) +{ +#if defined(QGL_FIXED_FUNCTION_ONLY) + Q_UNUSED(painter); + if (flag) + glEnableClientState(GL_VERTEX_ARRAY); + else + glDisableClientState(GL_VERTEX_ARRAY); +#else + Q_UNUSED(painter); + Q_D(QGLFlatColorEffect); +#if !defined(QGL_SHADERS_ONLY) + if (painter->isFixedFunction()) { + d->isFixedFunction = true; + if (flag) + glEnableClientState(GL_VERTEX_ARRAY); + else + glDisableClientState(GL_VERTEX_ARRAY); + return; + } +#endif + static char const flatColorVertexShader[] = + "attribute highp vec4 vertex;\n" + "uniform highp mat4 matrix;\n" + "void main(void)\n" + "{\n" + " gl_Position = matrix * vertex;\n" + "}\n"; + + static char const flatColorFragmentShader[] = + "uniform mediump vec4 color;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = color;\n" + "}\n"; + + QGLShaderProgram *program = + painter->cachedProgram(QLatin1String("qt.color.flat")); + d->program = program; + if (!program) { + if (!flag) + return; + program = new QGLShaderProgram(); + program->addShaderFromSourceCode(QGLShader::Vertex, flatColorVertexShader); + program->addShaderFromSourceCode(QGLShader::Fragment, flatColorFragmentShader); + program->bindAttributeLocation("vertex", QGL::Position); + if (!program->link()) { + qWarning("QGLFlatColorEffect::setActive(): could not link shader program"); + delete program; + return; + } + painter->setCachedProgram(QLatin1String("qt.color.flat"), program); + d->program = program; + d->colorUniform = program->uniformLocation("color"); + d->matrixUniform = program->uniformLocation("matrix"); + program->bind(); + program->enableAttributeArray(QGL::Position); + } else if (flag) { + d->colorUniform = program->uniformLocation("color"); + d->matrixUniform = program->uniformLocation("matrix"); + program->bind(); + program->enableAttributeArray(QGL::Position); + } else { + program->disableAttributeArray(QGL::Position); + program->release(); + } +#endif +} + +/*! + \reimp +*/ +void QGLFlatColorEffect::update + (QGLPainter *painter, QGLPainter::Updates updates) +{ +#if defined(QGL_FIXED_FUNCTION_ONLY) + painter->updateFixedFunction + (updates & (QGLPainter::UpdateColor | + QGLPainter::UpdateMatrices)); +#else + Q_D(QGLFlatColorEffect); +#if !defined(QGL_SHADERS_ONLY) + if (d->isFixedFunction) { + painter->updateFixedFunction + (updates & (QGLPainter::UpdateColor | + QGLPainter::UpdateMatrices)); + return; + } +#endif + if ((updates & QGLPainter::UpdateColor) != 0) { + if (painter->isPicking()) + d->program->setUniformValue(d->colorUniform, painter->pickColor()); + else + d->program->setUniformValue(d->colorUniform, painter->color()); + } + if ((updates & QGLPainter::UpdateMatrices) != 0) { + QMatrix4x4 proj = painter->projectionMatrix(); + QMatrix4x4 mv = painter->modelViewMatrix(); + d->program->setUniformValue(d->matrixUniform, proj * mv); + } +#endif +} + +class QGLPerVertexColorEffectPrivate +{ +public: + QGLPerVertexColorEffectPrivate() + : program(0) + , matrixUniform(-1) + , isFixedFunction(false) + { + } + + QGLShaderProgram *program; + int matrixUniform; + bool isFixedFunction; +}; + +/*! + Constructs a new per-vertex color effect. +*/ +QGLPerVertexColorEffect::QGLPerVertexColorEffect() + : d_ptr(new QGLPerVertexColorEffectPrivate) +{ +} + +/*! + Destroys this per-vertex color effect. +*/ +QGLPerVertexColorEffect::~QGLPerVertexColorEffect() +{ +} + +/*! + \reimp +*/ +void QGLPerVertexColorEffect::setActive(QGLPainter *painter, bool flag) +{ +#if defined(QGL_FIXED_FUNCTION_ONLY) + Q_UNUSED(painter); + if (flag) { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + } else { + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } +#else + Q_UNUSED(painter); + Q_D(QGLPerVertexColorEffect); +#if !defined(QGL_SHADERS_ONLY) + if (painter->isFixedFunction()) { + d->isFixedFunction = true; + if (flag) { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + } else { + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } + return; + } +#endif + static char const pvColorVertexShader[] = + "attribute highp vec4 vertex;\n" + "attribute mediump vec4 color;\n" + "uniform highp mat4 matrix;\n" + "varying mediump vec4 qColor;\n" + "void main(void)\n" + "{\n" + " gl_Position = matrix * vertex;\n" + " qColor = color;\n" + "}\n"; + + static char const pvColorFragmentShader[] = + "varying mediump vec4 qColor;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = qColor;\n" + "}\n"; + + QGLShaderProgram *program = + painter->cachedProgram(QLatin1String("qt.color.pervertex")); + d->program = program; + if (!program) { + if (!flag) + return; + program = new QGLShaderProgram(); + program->addShaderFromSourceCode(QGLShader::Vertex, pvColorVertexShader); + program->addShaderFromSourceCode(QGLShader::Fragment, pvColorFragmentShader); + program->bindAttributeLocation("vertex", QGL::Position); + program->bindAttributeLocation("color", QGL::Color); + if (!program->link()) { + qWarning("QGLPerVertexColorEffect::setActive(): could not link shader program"); + delete program; + program = 0; + return; + } + painter->setCachedProgram(QLatin1String("qt.color.pervertex"), program); + d->program = program; + d->matrixUniform = program->uniformLocation("matrix"); + program->bind(); + program->enableAttributeArray(QGL::Position); + program->enableAttributeArray(QGL::Color); + } else if (flag) { + d->matrixUniform = program->uniformLocation("matrix"); + program->bind(); + program->enableAttributeArray(QGL::Position); + program->enableAttributeArray(QGL::Color); + } else { + program->disableAttributeArray(QGL::Position); + program->disableAttributeArray(QGL::Color); + program->release(); + } +#endif +} + +/*! + \reimp +*/ +void QGLPerVertexColorEffect::update + (QGLPainter *painter, QGLPainter::Updates updates) +{ +#if defined(QGL_FIXED_FUNCTION_ONLY) + painter->updateFixedFunction(updates & QGLPainter::UpdateMatrices); +#else + Q_UNUSED(painter); + Q_D(QGLPerVertexColorEffect); +#if !defined(QGL_SHADERS_ONLY) + if (d->isFixedFunction) { + painter->updateFixedFunction(updates & QGLPainter::UpdateMatrices); + return; + } +#endif + if ((updates & QGLPainter::UpdateMatrices) != 0) { + d->program->setUniformValue + (d->matrixUniform, painter->combinedMatrix()); + } +#endif +} + +QT_END_NAMESPACE diff --git a/src/threed/effects/qglflatcoloreffect_p.h b/src/threed/effects/qglflatcoloreffect_p.h new file mode 100644 index 000000000..c43bcca0d --- /dev/null +++ b/src/threed/effects/qglflatcoloreffect_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLFLATCOLOREFFECT_P_H +#define QGLFLATCOLOREFFECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglabstracteffect.h" +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_NAMESPACE + +class QGLFlatColorEffectPrivate; +class QGLPerVertexColorEffectPrivate; + +class QGLFlatColorEffect : public QGLAbstractEffect +{ +public: + QGLFlatColorEffect(); + virtual ~QGLFlatColorEffect(); + + bool supportsPicking() const; + void setActive(QGLPainter *painter, bool flag); + void update(QGLPainter *painter, QGLPainter::Updates updates); + +private: + QScopedPointer<QGLFlatColorEffectPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLFlatColorEffect) + Q_DISABLE_COPY(QGLFlatColorEffect) +}; + +class QGLPerVertexColorEffect : public QGLAbstractEffect +{ +public: + QGLPerVertexColorEffect(); + virtual ~QGLPerVertexColorEffect(); + + void setActive(QGLPainter *painter, bool flag); + void update(QGLPainter *painter, QGLPainter::Updates updates); + +private: + QScopedPointer<QGLPerVertexColorEffectPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLPerVertexColorEffect) + Q_DISABLE_COPY(QGLPerVertexColorEffect) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/threed/effects/qglflattextureeffect.cpp b/src/threed/effects/qglflattextureeffect.cpp new file mode 100644 index 000000000..307549784 --- /dev/null +++ b/src/threed/effects/qglflattextureeffect.cpp @@ -0,0 +1,373 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglflattextureeffect_p.h" +#include "qglabstracteffect_p.h" +#include "qglext_p.h" +#include <QtOpenGL/qglshaderprogram.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLFlatTextureEffect + \since 4.8 + \brief The QGLFlatTextureEffect class provides a standard effect that draws fragments with a flat unlit texture. + \ingroup qt3d + \ingroup qt3d::painting + \internal +*/ + +/*! + \class QGLFlatDecalTextureEffect + \since 4.8 + \brief The QGLFlatDecalTextureEffect class provides a standard effect that decals fragments with a flat unlit texture. + \ingroup qt3d + \ingroup qt3d::painting + \internal +*/ + +class QGLFlatTextureEffectPrivate +{ +public: + QGLFlatTextureEffectPrivate() + : program(0) + , matrixUniform(-1) + , isFixedFunction(false) + { + } + + QGLShaderProgram *program; + int matrixUniform; + bool isFixedFunction; +}; + +/*! + Constructs a new flat texture effect. +*/ +QGLFlatTextureEffect::QGLFlatTextureEffect() + : d_ptr(new QGLFlatTextureEffectPrivate) +{ +} + +/*! + Destroys this flat texture effect. +*/ +QGLFlatTextureEffect::~QGLFlatTextureEffect() +{ +} + +#if !defined(QGL_FIXED_FUNCTION_ONLY) + +static char const flatTexVertexShader[] = + "attribute highp vec4 vertex;\n" + "attribute highp vec4 texcoord;\n" + "uniform highp mat4 matrix;\n" + "varying highp vec4 qt_TexCoord0;\n" + "void main(void)\n" + "{\n" + " gl_Position = matrix * vertex;\n" + " qt_TexCoord0 = texcoord;\n" + "}\n"; + +static char const flatTexFragmentShader[] = + "uniform sampler2D tex;\n" + "varying highp vec4 qt_TexCoord0;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = texture2D(tex, qt_TexCoord0.st);\n" + "}\n"; + +static char const flatDecalFragmentShader[] = + "uniform sampler2D tex;\n" + "uniform mediump vec4 color;\n" + "varying highp vec4 qt_TexCoord0;\n" + "void main(void)\n" + "{\n" + " mediump vec4 col = texture2D(tex, qt_TexCoord0.st);\n" + " gl_FragColor = vec4(clamp(color.rgb * (1.0 - col.a) + col.rgb, 0.0, 1.0), color.a);\n" + "}\n"; + +#endif + +/*! + \reimp +*/ +void QGLFlatTextureEffect::setActive(QGLPainter *painter, bool flag) +{ +#if defined(QGL_FIXED_FUNCTION_ONLY) + Q_UNUSED(painter); + if (flag) { + glEnableClientState(GL_VERTEX_ARRAY); + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glEnable(GL_TEXTURE_2D); + } else { + glDisableClientState(GL_VERTEX_ARRAY); + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); + } +#else + Q_UNUSED(painter); + Q_D(QGLFlatTextureEffect); +#if !defined(QGL_SHADERS_ONLY) + if (painter->isFixedFunction()) { + d->isFixedFunction = true; + if (flag) { + glEnableClientState(GL_VERTEX_ARRAY); + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glEnable(GL_TEXTURE_2D); + } else { + glDisableClientState(GL_VERTEX_ARRAY); + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); + } + return; + } +#endif + QGLShaderProgram *program = + painter->cachedProgram(QLatin1String("qt.texture.flat.replace")); + d->program = program; + if (!program) { + if (!flag) + return; + program = new QGLShaderProgram(); + program->addShaderFromSourceCode(QGLShader::Vertex, flatTexVertexShader); + program->addShaderFromSourceCode(QGLShader::Fragment, flatTexFragmentShader); + program->bindAttributeLocation("vertex", QGL::Position); + program->bindAttributeLocation("texcoord", QGL::TextureCoord0); + if (!program->link()) { + qWarning("QGLFlatTextureEffect::setActive(): could not link shader program"); + delete program; + program = 0; + return; + } + painter->setCachedProgram + (QLatin1String("qt.texture.flat.replace"), program); + d->program = program; + d->matrixUniform = program->uniformLocation("matrix"); + program->bind(); + program->setUniformValue("tex", 0); + program->enableAttributeArray(QGL::Position); + program->enableAttributeArray(QGL::TextureCoord0); + } else if (flag) { + d->matrixUniform = program->uniformLocation("matrix"); + program->bind(); + program->setUniformValue("tex", 0); + program->enableAttributeArray(QGL::Position); + program->enableAttributeArray(QGL::TextureCoord0); + } else { + program->disableAttributeArray(QGL::Position); + program->disableAttributeArray(QGL::TextureCoord0); + program->release(); + } +#endif +} + +/*! + \reimp +*/ +void QGLFlatTextureEffect::update + (QGLPainter *painter, QGLPainter::Updates updates) +{ +#if defined(QGL_FIXED_FUNCTION_ONLY) + painter->updateFixedFunction(updates & QGLPainter::UpdateMatrices); +#else + Q_D(QGLFlatTextureEffect); +#if !defined(QGL_SHADERS_ONLY) + if (d->isFixedFunction) { + painter->updateFixedFunction(updates & QGLPainter::UpdateMatrices); + return; + } +#endif + if ((updates & QGLPainter::UpdateMatrices) != 0) { + d->program->setUniformValue + (d->matrixUniform, painter->combinedMatrix()); + } +#endif +} + +class QGLFlatDecalTextureEffectPrivate +{ +public: + QGLFlatDecalTextureEffectPrivate() + : program(0) + , matrixUniform(-1) + , colorUniform(-1) + , isFixedFunction(false) + { + } + + QGLShaderProgram *program; + int matrixUniform; + int colorUniform; + bool isFixedFunction; +}; + +/*! + Constructs a new flat decal texture effect. +*/ +QGLFlatDecalTextureEffect::QGLFlatDecalTextureEffect() + : d_ptr(new QGLFlatDecalTextureEffectPrivate) +{ +} + +/*! + Destroys this flat decal texture effect. +*/ +QGLFlatDecalTextureEffect::~QGLFlatDecalTextureEffect() +{ +} + +/*! + \reimp +*/ +void QGLFlatDecalTextureEffect::setActive(QGLPainter *painter, bool flag) +{ +#if defined(QGL_FIXED_FUNCTION_ONLY) + Q_UNUSED(painter); + if (flag) { + glEnableClientState(GL_VERTEX_ARRAY); + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + glEnable(GL_TEXTURE_2D); + } else { + glDisableClientState(GL_VERTEX_ARRAY); + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); + } +#else + Q_UNUSED(painter); + Q_D(QGLFlatDecalTextureEffect); +#if !defined(QGL_SHADERS_ONLY) + if (painter->isFixedFunction()) { + d->isFixedFunction = true; + if (flag) { + glEnableClientState(GL_VERTEX_ARRAY); + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + glEnable(GL_TEXTURE_2D); + } else { + glDisableClientState(GL_VERTEX_ARRAY); + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); + } + } +#endif + QGLShaderProgram *program = + painter->cachedProgram(QLatin1String("qt.texture.flat.decal")); + d->program = program; + if (!program) { + if (!flag) + return; + program = new QGLShaderProgram(); + program->addShaderFromSourceCode(QGLShader::Vertex, flatTexVertexShader); + program->addShaderFromSourceCode(QGLShader::Fragment, flatDecalFragmentShader); + program->bindAttributeLocation("vertex", QGL::Position); + program->bindAttributeLocation("texcoord", QGL::TextureCoord0); + if (!program->link()) { + qWarning("QGLFlatDecalTextureEffect::setActive(): could not link shader program"); + delete program; + program = 0; + return; + } + painter->setCachedProgram + (QLatin1String("qt.texture.flat.decal"), program); + d->program = program; + d->matrixUniform = program->uniformLocation("matrix"); + d->colorUniform = program->uniformLocation("color"); + program->bind(); + program->setUniformValue("tex", 0); + program->enableAttributeArray(QGL::Position); + program->enableAttributeArray(QGL::TextureCoord0); + } else if (flag) { + d->matrixUniform = program->uniformLocation("matrix"); + d->colorUniform = program->uniformLocation("color"); + program->bind(); + program->setUniformValue("tex", 0); + program->enableAttributeArray(QGL::Position); + program->enableAttributeArray(QGL::TextureCoord0); + } else { + program->disableAttributeArray(QGL::Position); + program->disableAttributeArray(QGL::TextureCoord0); + program->release(); + } +#endif +} + +/*! + \reimp +*/ +void QGLFlatDecalTextureEffect::update + (QGLPainter *painter, QGLPainter::Updates updates) +{ +#if defined(QGL_FIXED_FUNCTION_ONLY) + painter->updateFixedFunction + (updates & (QGLPainter::UpdateColor | + QGLPainter::UpdateMatrices)); +#else + Q_D(QGLFlatDecalTextureEffect); +#if !defined(QGL_SHADERS_ONLY) + if (d->isFixedFunction) { + painter->updateFixedFunction + (updates & (QGLPainter::UpdateColor | + QGLPainter::UpdateMatrices)); + return; + } +#endif + if ((updates & QGLPainter::UpdateColor) != 0) + d->program->setUniformValue(d->colorUniform, painter->color()); + if ((updates & QGLPainter::UpdateMatrices) != 0) { + d->program->setUniformValue + (d->matrixUniform, painter->combinedMatrix()); + } +#endif +} + +QT_END_NAMESPACE diff --git a/src/threed/effects/qglflattextureeffect_p.h b/src/threed/effects/qglflattextureeffect_p.h new file mode 100644 index 000000000..9672a94f9 --- /dev/null +++ b/src/threed/effects/qglflattextureeffect_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLFLATTEXTUREEFFECT_P_H +#define QGLFLATTEXTUREEFFECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglabstracteffect.h" +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_NAMESPACE + +class QGLFlatTextureEffectPrivate; +class QGLFlatDecalTextureEffectPrivate; + +class QGLFlatTextureEffect : public QGLAbstractEffect +{ +public: + QGLFlatTextureEffect(); + virtual ~QGLFlatTextureEffect(); + + void setActive(QGLPainter *painter, bool flag); + void update(QGLPainter *painter, QGLPainter::Updates updates); + +private: + QScopedPointer<QGLFlatTextureEffectPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLFlatTextureEffect) + Q_DISABLE_COPY(QGLFlatTextureEffect) +}; + +class QGLFlatDecalTextureEffect : public QGLAbstractEffect +{ +public: + QGLFlatDecalTextureEffect(); + virtual ~QGLFlatDecalTextureEffect(); + + void setActive(QGLPainter *painter, bool flag); + void update(QGLPainter *painter, QGLPainter::Updates updates); + +private: + QScopedPointer<QGLFlatDecalTextureEffectPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLFlatDecalTextureEffect) + Q_DISABLE_COPY(QGLFlatDecalTextureEffect) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/threed/effects/qgllitmaterialeffect.cpp b/src/threed/effects/qgllitmaterialeffect.cpp new file mode 100644 index 000000000..6461e86b5 --- /dev/null +++ b/src/threed/effects/qgllitmaterialeffect.cpp @@ -0,0 +1,571 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgllitmaterialeffect_p.h" +#include "qglabstracteffect_p.h" +#include "qglext_p.h" +#include <QtOpenGL/qglshaderprogram.h> +#include <QtCore/qfile.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLLitMaterialEffect + \since 4.8 + \brief The QGLLitMaterialEffect class provides a standard effect that draws fragments with a lit material. + \ingroup qt3d + \ingroup qt3d::painting + \internal +*/ + +#if !defined(QGL_FIXED_FUNCTION_ONLY) + +static char const litMaterialVertexShader[] = + "attribute highp vec4 vertex;\n" + "attribute highp vec3 normal;\n" + "uniform highp mat4 matrix;\n" + "uniform highp mat4 modelView;\n" + "uniform highp mat3 normalMatrix;\n" + "void main(void)\n" + "{\n" + " gl_Position = matrix * vertex;\n" + " highp vec4 tvertex = modelView * vertex;\n" + " highp vec3 norm = normalize(normalMatrix * normal);\n" + " qLightVertex(tvertex, norm);\n" + "}\n"; + +static char const litMaterialFragmentShader[] = +#if !defined(QT_OPENGL_ES) + "varying mediump vec4 qColor;\n" + "varying mediump vec4 qSecondaryColor;\n" + "\n" + "void main(void)\n" + "{\n" + " gl_FragColor = clamp(qColor + vec4(qSecondaryColor.xyz, 0.0), 0.0, 1.0);\n" + "}\n"; +#else + "varying mediump vec4 qCombinedColor;\n" + "\n" + "void main(void)\n" + "{\n" + " gl_FragColor = qCombinedColor;\n" + "}\n"; +#endif + +// Algorithm from section 2.14.1 of OpenGL 2.1 specification. +static char const litMaterialLightingShader[] = +#if !defined(QT_OPENGL_ES) +"uniform mediump vec3 sdli;\n" // Direction of the light (must be normalized). +"uniform mediump vec3 pli;\n" // Position of the light +"uniform mediump float pliw;\n" // 0 for directional, 1 for positional. +"uniform mediump float srli;\n" // Spotlight exponent for the light +"uniform mediump float crli;\n" // Spotlight cutoff for the light +"uniform mediump float ccrli;\n" // Cosine of spotlight cutoff for the light +"uniform mediump float k0;\n" // Constant attentuation factor for the light +"uniform mediump float k1;\n" // Linear attentuation factor for the light +"uniform mediump float k2;\n" // Quadratic attentuation factor for the light +"uniform mediump vec4 acm[2];\n" // Ambient color of the material and light +"uniform mediump vec4 dcm[2];\n" // Diffuse color of the material and light +"uniform mediump vec4 scm[2];\n" // Specular color of the material and light +"uniform mediump vec4 ecm[2];\n" // Emissive color and ambient scene color +"uniform mediump float srm[2];\n"// Specular exponent of the material +"uniform bool viewerAtInfinity;\n" // Light model indicates viewer at infinity +"uniform bool twoSided;\n" // Light model indicates two-sided lighting + +"varying mediump vec4 qColor;\n" +"varying mediump vec4 qSecondaryColor;\n" + +"void qLightVertex(vec4 vertex, vec3 normal)\n" +"{\n" +" int i, material;\n" +" vec3 toEye, toLight, h;\n" +" float angle, spot, attenuation;\n" +" vec4 color, scolor;\n" +" vec4 adcomponent, scomponent;\n" + + // Determine which material to use. +" if (!twoSided || normal.z >= 0.0) {\n" +" material = 0;\n" +" } else {\n" +" material = 1;\n" +" normal = -normal;\n" +" }\n" + + // Start with the material's emissive color and the ambient scene color, + // which have been combined into the ecm parameter by the C++ code. +" color = ecm[material];\n" +" scolor = vec4(0, 0, 0, 0);\n" + + // Vector from the vertex to the eye position (i.e. the origin). +" if (viewerAtInfinity)\n" +" toEye = vec3(0, 0, 1);\n" +" else\n" +" toEye = normalize(-vertex.xyz);\n" + + // Determine the cosine of the angle between the normal and the + // vector from the vertex to the light. +" if (pliw == 0.0)\n" +" toLight = normalize(pli);\n" +" else\n" +" toLight = normalize(pli - vertex.xyz);\n" +" angle = max(dot(normal, toLight), 0.0);\n" + + // Calculate the ambient and diffuse light components. +" adcomponent = acm[material] + angle * dcm[material];\n" + + // Calculate the specular light components. +" if (angle != 0.0) {\n" +" h = normalize(toLight + toEye);\n" +" angle = max(dot(normal, h), 0.0);\n" +" if (srm[material] != 0.0)\n" +" scomponent = pow(angle, srm[material]) * scm[material];\n" +" else\n" +" scomponent = scm[material];\n" +" } else {\n" +" scomponent = vec4(0, 0, 0, 0);\n" +" }\n" + + // Apply the spotlight angle and exponent. +" if (crli != 180.0) {\n" +" spot = max(dot(normalize(vertex.xyz - pli), sdli), 0.0);\n" +" if (spot < ccrli) {\n" +" adcomponent = vec4(0, 0, 0, 0);\n" +" scomponent = vec4(0, 0, 0, 0);\n" +" } else {\n" +" spot = pow(spot, srli);\n" +" adcomponent *= spot;\n" +" scomponent *= spot;\n" +" }\n" +" }\n" + + // Apply attenuation to the colors. +" if (pliw != 0.0) {\n" +" attenuation = k0;\n" +" if (k1 != 0.0 || k2 != 0.0) {\n" +" float len = length(pli - vertex.xyz);\n" +" attenuation += k1 * len + k2 * len * len;\n" +" }\n" +" color += adcomponent / attenuation;\n" +" scolor += scomponent / attenuation;\n" +" } else {\n" +" color += adcomponent;\n" +" scolor += scomponent;\n" +" }\n" + + // Generate the final output colors. +" float alpha = dcm[material].a;\n" +" qColor = vec4(clamp(color.rgb, 0.0, 1.0), alpha);\n" +" qSecondaryColor = clamp(scolor, 0.0, 1.0);\n" +"}\n"; +#else +"uniform mediump vec3 sdli;\n" // Direction of the light (must be normalized). +"uniform mediump vec3 pli;\n" // Position of the light +"uniform mediump float pliw;\n" // 0 for directional, 1 for positional. +"uniform mediump float srli;\n" // Spotlight exponent for the light +"uniform mediump float crli;\n" // Spotlight cutoff for the light +"uniform mediump float ccrli;\n" // Cosine of spotlight cutoff for the light +"uniform mediump vec4 acm;\n" // Ambient color of the material and light +"uniform mediump vec4 dcm;\n" // Diffuse color of the material and light +"uniform mediump vec4 scm;\n" // Specular color of the material and light +"uniform mediump vec4 ecm;\n" // Emissive color and ambient scene color +"uniform mediump float srm;\n" // Specular exponent of the material +"uniform bool viewerAtInfinity;\n" // Light model indicates viewer at infinity + +"varying mediump vec4 qColor;\n" +"varying mediump vec4 qSecondaryColor;\n" +"varying mediump vec4 qCombinedColor;\n" + +"void qLightVertex(vec4 vertex, vec3 normal)\n" +"{\n" +" vec3 toEye, toLight, h;\n" +" float angle, spot;\n" +" vec4 color, scolor;\n" + + // Vector from the vertex to the eye position (i.e. the origin). +" if (viewerAtInfinity)\n" +" toEye = vec3(0, 0, 1);\n" +" else\n" +" toEye = normalize(-vertex.xyz);\n" + + // Determine the cosine of the angle between the normal and the + // vector from the vertex to the light. +" if (pliw == 0.0)\n" +" toLight = normalize(pli);\n" +" else\n" +" toLight = normalize(pli - vertex.xyz);\n" +" angle = max(dot(normal, toLight), 0.0);\n" + + // Calculate the ambient and diffuse light components. +" color = acm + angle * dcm;\n" + + // Calculate the specular light components. +" if (angle != 0.0) {\n" +" h = normalize(toLight + toEye);\n" +" angle = max(dot(normal, h), 0.0);\n" +" if (srm != 0.0)\n" +" scolor = pow(angle, srm) * scm;\n" +" else\n" +" scolor = scm;\n" +" } else {\n" +" scolor = vec4(0, 0, 0, 0);\n" +" }\n" + + // Apply the spotlight angle and exponent. +" if (crli != 180.0) {\n" +" spot = max(dot(normalize(vertex.xyz - pli), sdli), 0.0);\n" +" if (spot < ccrli) {\n" +" color = vec4(0, 0, 0, 0);\n" +" scolor = vec4(0, 0, 0, 0);\n" +" } else {\n" +" spot = pow(spot, srli);\n" +" color *= spot;\n" +" scolor *= spot;\n" +" }\n" +" }\n" + + // Generate the final output colors. +" color += ecm;\n" +" float alpha = dcm.a;\n" + // Note: Normally, the qCombinedColor is ignored, and per-pixel + // value is calculated. + // If OPENGL_ES is defined, qColor and qSecondaryColor are ignored, + // and qCombinedColor is calculated here to speed up the fragment shader. +" qColor = vec4(clamp(color.rgb, 0.0, 1.0), alpha);\n" +" qSecondaryColor = clamp(scolor, 0.0, 1.0);\n" +" qCombinedColor = clamp(qColor + vec4(qSecondaryColor.xyz, 0.0), 0.0, 1.0);\n" +"}\n"; +#endif + +static QByteArray createVertexSource(const char *lighting, const char *extra) +{ + QByteArray contents(lighting); + return contents + extra; +} + +static inline QVector4D colorToVector4(const QColor& color) +{ + return QVector4D(color.redF(), color.greenF(), + color.blueF(), color.alphaF()); +} + +// Combine a material and light color into a single color. +static inline QVector4D colorToVector4 + (const QColor &color, const QColor &lightColor) +{ + return QVector4D(color.redF() * lightColor.redF(), + color.greenF() * lightColor.greenF(), + color.blueF() * lightColor.blueF(), + color.alphaF() * lightColor.alphaF()); +} + +#endif + +class QGLLitMaterialEffectPrivate +{ +public: + QGLLitMaterialEffectPrivate() + : program(0) + , matrixUniform(-1) + , modelViewUniform(-1) + , normalMatrixUniform(-1) + , textureMode(0) +#if !defined(QGL_FIXED_FUNCTION_ONLY) + , vertexShader(litMaterialVertexShader) + , fragmentShader(litMaterialFragmentShader) +#else + , vertexShader(0) + , fragmentShader(0) +#endif + , programName(QLatin1String("qt.color.material")) + , isFixedFunction(false) + { + } + + QGLShaderProgram *program; + int matrixUniform; + int modelViewUniform; + int normalMatrixUniform; + GLenum textureMode; + const char *vertexShader; + const char *fragmentShader; + QString programName; + bool isFixedFunction; +}; + +/*! + Constructs a new lit material effect. +*/ +QGLLitMaterialEffect::QGLLitMaterialEffect() + : d_ptr(new QGLLitMaterialEffectPrivate) +{ +} + +/*! + \internal +*/ +QGLLitMaterialEffect::QGLLitMaterialEffect + (GLenum mode, const char *vshader, const char *fshader, + const QString& programName) + : d_ptr(new QGLLitMaterialEffectPrivate) +{ + Q_D(QGLLitMaterialEffect); + d->textureMode = mode; + d->vertexShader = vshader; + d->fragmentShader = fshader; + d->programName = programName; +} + +/*! + Destroys this lit material effect. +*/ +QGLLitMaterialEffect::~QGLLitMaterialEffect() +{ +} + +/*! + \reimp +*/ +void QGLLitMaterialEffect::setActive(QGLPainter *painter, bool flag) +{ +#if defined(QGL_FIXED_FUNCTION_ONLY) + Q_UNUSED(painter); + Q_D(QGLLitMaterialEffect); + if (flag) { + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + if (d->textureMode) { + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, d->textureMode); + glEnable(GL_TEXTURE_2D); + } + } else { + glDisable(GL_LIGHTING); + glDisable(GL_LIGHT0); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + if (d->textureMode) { + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); + } + } +#else + Q_UNUSED(painter); + Q_D(QGLLitMaterialEffect); +#if !defined(QGL_SHADERS_ONLY) + if (painter->isFixedFunction()) { + d->isFixedFunction = true; + if (flag) { + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + if (d->textureMode) { + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, d->textureMode); + glEnable(GL_TEXTURE_2D); + } + } else { + glDisable(GL_LIGHTING); + glDisable(GL_LIGHT0); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + if (d->textureMode) { + qt_gl_ClientActiveTexture(GL_TEXTURE0); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); + } + } + return; + } +#endif + QGLShaderProgram *program = painter->cachedProgram(d->programName); + d->program = program; + if (!program) { + if (!flag) + return; + program = new QGLShaderProgram(); + program->addShaderFromSourceCode(QGLShader::Vertex, createVertexSource(litMaterialLightingShader, d->vertexShader)); + program->addShaderFromSourceCode(QGLShader::Fragment, d->fragmentShader); + program->bindAttributeLocation("vertex", QGL::Position); + program->bindAttributeLocation("normal", QGL::Normal); + if (d->textureMode != 0) + program->bindAttributeLocation("texcoord", QGL::TextureCoord0); + if (!program->link()) { + qWarning("QGLLitMaterialEffect::setActive(): could not link shader program"); + delete program; + program = 0; + return; + } + painter->setCachedProgram(d->programName, program); + d->program = program; + d->matrixUniform = program->uniformLocation("matrix"); + d->modelViewUniform = program->uniformLocation("modelView"); + d->normalMatrixUniform = program->uniformLocation("normalMatrix"); + program->bind(); + if (d->textureMode != 0) { + program->setUniformValue("tex", 0); + program->enableAttributeArray(QGL::TextureCoord0); + } + program->enableAttributeArray(QGL::Position); + program->enableAttributeArray(QGL::Normal); + } else if (flag) { + d->matrixUniform = program->uniformLocation("matrix"); + d->modelViewUniform = program->uniformLocation("modelView"); + d->normalMatrixUniform = program->uniformLocation("normalMatrix"); + program->bind(); + if (d->textureMode != 0) { + program->setUniformValue("tex", 0); + program->enableAttributeArray(QGL::TextureCoord0); + } + program->enableAttributeArray(QGL::Position); + program->enableAttributeArray(QGL::Normal); + } else { + program->disableAttributeArray(QGL::Position); + program->disableAttributeArray(QGL::Normal); + if (d->textureMode != 0) + program->disableAttributeArray(QGL::TextureCoord0); + program->release(); + } +#endif +} + +/*! + \reimp +*/ +void QGLLitMaterialEffect::update + (QGLPainter *painter, QGLPainter::Updates updates) +{ +#if defined(QGL_FIXED_FUNCTION_ONLY) + painter->updateFixedFunction + (updates & (QGLPainter::UpdateMatrices | + QGLPainter::UpdateLights | + QGLPainter::UpdateMaterials)); +#else + Q_D(QGLLitMaterialEffect); +#if !defined(QGL_SHADERS_ONLY) + if (d->isFixedFunction) { + painter->updateFixedFunction + (updates & (QGLPainter::UpdateMatrices | + QGLPainter::UpdateLights | + QGLPainter::UpdateMaterials)); + return; + } +#endif + QGLShaderProgram *program = d->program; + if ((updates & QGLPainter::UpdateMatrices) != 0) { + program->setUniformValue(d->matrixUniform, painter->combinedMatrix()); + program->setUniformValue(d->modelViewUniform, painter->modelViewMatrix()); + program->setUniformValue(d->normalMatrixUniform, painter->normalMatrix()); + } + const QGLLightParameters *lparams = painter->mainLight(); + QMatrix4x4 ltransform = painter->mainLightTransform(); + const QGLLightModel *model = painter->lightModel(); + if ((updates & (QGLPainter::UpdateLights | QGLPainter::UpdateMaterials)) != 0) { + // Set the uniform variables for the light. + program->setUniformValue + ("sdli", lparams->eyeSpotDirection(ltransform).normalized()); + QVector4D pli = lparams->eyePosition(ltransform); + program->setUniformValue("pli", QVector3D(pli.x(), pli.y(), pli.z())); + program->setUniformValue("pliw", GLfloat(pli.w())); + program->setUniformValue("srli", GLfloat(lparams->spotExponent())); + program->setUniformValue("crli", GLfloat(lparams->spotAngle())); + program->setUniformValue("ccrli", GLfloat(lparams->spotCosAngle())); +#if !defined(QT_OPENGL_ES) + // Attenuation is not supported under ES, for performance. + program->setUniformValue("k0", GLfloat(lparams->constantAttenuation())); + program->setUniformValue("k1", GLfloat(lparams->linearAttenuation())); + program->setUniformValue("k2", GLfloat(lparams->quadraticAttenuation())); +#endif + + // Set the uniform variables for the light model. +#if !defined(QT_OPENGL_ES) + program->setUniformValue("twoSided", (int)(model->model() == QGLLightModel::TwoSided)); +#endif + program->setUniformValue("viewerAtInfinity", (int)(model->viewerPosition() == QGLLightModel::ViewerAtInfinity)); +#if !defined(QT_OPENGL_ES) + if (d->textureMode != 0) + program->setUniformValue("separateSpecular", (int)(model->colorControl() == QGLLightModel::SeparateSpecularColor)); +#endif + + // Set the uniform variables for the front and back materials. +#if defined(QT_OPENGL_ES) + static const int MaxMaterials = 1; +#else + static const int MaxMaterials = 2; +#endif + QVector4D acm[MaxMaterials]; + QVector4D dcm[MaxMaterials]; + QVector4D scm[MaxMaterials]; + QVector4D ecm[MaxMaterials]; + float srm[MaxMaterials]; + const QGLMaterial *mparams = painter->faceMaterial(QGL::FrontFaces); + acm[0] = colorToVector4(mparams->ambientColor(), lparams->ambientColor()); + dcm[0] = colorToVector4(mparams->diffuseColor(), lparams->diffuseColor()); + scm[0] = colorToVector4(mparams->specularColor(), lparams->specularColor()); + ecm[0] = colorToVector4(mparams->emittedLight()) + + colorToVector4(mparams->ambientColor(), + model->ambientSceneColor()); + srm[0] = (float)(mparams->shininess()); +#if !defined(QT_OPENGL_ES) + mparams = painter->faceMaterial(QGL::BackFaces); + acm[1] = colorToVector4(mparams->ambientColor(), lparams->ambientColor()); + dcm[1] = colorToVector4(mparams->diffuseColor(), lparams->diffuseColor()); + scm[1] = colorToVector4(mparams->specularColor(), lparams->specularColor()); + ecm[1] = colorToVector4(mparams->emittedLight()) + + colorToVector4(mparams->ambientColor(), + model->ambientSceneColor()); + srm[1] = (float)(mparams->shininess()); +#endif + program->setUniformValueArray("acm", (const GLfloat *)acm, MaxMaterials, 4); + program->setUniformValueArray("dcm", (const GLfloat *)dcm, MaxMaterials, 4); + program->setUniformValueArray("scm", (const GLfloat *)scm, MaxMaterials, 4); + program->setUniformValueArray("ecm", (const GLfloat *)ecm, MaxMaterials, 4); + program->setUniformValueArray("srm", srm, MaxMaterials, 1); + } +#endif +} + +QT_END_NAMESPACE diff --git a/src/threed/effects/qgllitmaterialeffect_p.h b/src/threed/effects/qgllitmaterialeffect_p.h new file mode 100644 index 000000000..bca37096e --- /dev/null +++ b/src/threed/effects/qgllitmaterialeffect_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLLITMATERIALEFFECT_P_H +#define QGLLITMATERIALEFFECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglabstracteffect.h" + +QT_BEGIN_NAMESPACE + +class QGLLitMaterialEffectPrivate; + +class QGLLitMaterialEffect : public QGLAbstractEffect +{ +public: + QGLLitMaterialEffect(); + virtual ~QGLLitMaterialEffect(); + + void setActive(QGLPainter *painter, bool flag); + void update(QGLPainter *painter, QGLPainter::Updates updates); + +protected: + QGLLitMaterialEffect + (GLenum mode, const char *vshader, const char *fshader, + const QString& programName); + +private: + QScopedPointer<QGLLitMaterialEffectPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLLitMaterialEffect) + Q_DISABLE_COPY(QGLLitMaterialEffect) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/threed/effects/qgllittextureeffect.cpp b/src/threed/effects/qgllittextureeffect.cpp new file mode 100644 index 000000000..457576f5a --- /dev/null +++ b/src/threed/effects/qgllittextureeffect.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgllittextureeffect_p.h" +#include "qglabstracteffect_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLLitTextureEffect + \since 4.8 + \brief The QGLLitTextureEffect class provides a standard effect base class for drawing fragments with a lit texture. + \ingroup qt3d + \ingroup qt3d::painting + \internal + + \sa QGLLitDecalTextureEffect, QGLLitModulateTextureEffect +*/ + +/*! + \class QGLLitDecalTextureEffect + \since 4.8 + \brief The QGLLitDecalTextureEffect class provides a standard effect for drawing fragments with a texture decaled over a lit material. + \ingroup qt3d + \ingroup qt3d::painting + \internal +*/ + +/*! + \class QGLLitModulateTextureEffect + \since 4.8 + \brief The QGLLitModulateTextureEffect class provides a standard effect for drawing fragments with a texture modulated with a lit material. + \ingroup qt3d + \ingroup qt3d::painting + \internal +*/ + +/*! + \internal +*/ +QGLLitTextureEffect::QGLLitTextureEffect + (GLenum mode, const char *vshader, const char *fshader, + const QString& programName) + : QGLLitMaterialEffect(mode, vshader, fshader, programName) +{ +} + +/*! + Destroys this lit texture effect. +*/ +QGLLitTextureEffect::~QGLLitTextureEffect() +{ +} + +#if !defined(QGL_FIXED_FUNCTION_ONLY) + +static char const litTextureVertexShader[] = + "attribute highp vec4 vertex;\n" + "attribute highp vec3 normal;\n" + "attribute highp vec4 texcoord;\n" + "uniform highp mat4 matrix;\n" + "uniform highp mat4 modelView;\n" + "uniform highp mat3 normalMatrix;\n" + "varying highp vec4 qt_TexCoord0;\n" + "void main(void)\n" + "{\n" + " gl_Position = matrix * vertex;\n" + " highp vec4 tvertex = modelView * vertex;\n" + " highp vec3 norm = normalize(normalMatrix * normal);\n" + " qLightVertex(tvertex, norm);\n" + " qt_TexCoord0 = texcoord;\n" + "}\n"; + +static char const litDecalFragmentShader[] = + "uniform sampler2D tex;\n" +#if defined(QT_OPENGL_ES) + "varying mediump vec4 qCombinedColor;\n" +#else + "uniform bool separateSpecular;\n" + "varying mediump vec4 qColor;\n" + "varying mediump vec4 qSecondaryColor;\n" +#endif + "varying highp vec4 qt_TexCoord0;\n" + "\n" + "void main(void)\n" + "{\n" + " mediump vec4 col = texture2D(tex, qt_TexCoord0.st);\n" +#if defined(QT_OPENGL_ES) + " gl_FragColor = vec4(clamp(qCombinedColor.rgb * (1.0 - col.a) + col.rgb * col.a, 0.0, 1.0), qCombinedColor.a);\n" +#else + " if (separateSpecular) {\n" + " gl_FragColor = vec4(clamp(qColor.rgb * (1.0 - col.a) + col.rgb * col.a + qSecondaryColor.xyz, 0.0, 1.0), qColor.a);\n" + " } else {\n" + " mediump vec4 lcolor = clamp(qColor + vec4(qSecondaryColor.xyz, 0.0), 0.0, 1.0);\n" + " gl_FragColor = vec4(clamp(lcolor.rgb * (1.0 - col.a) + col.rgb * col.a, 0.0, 1.0), lcolor.a);\n" + " }\n" +#endif + "}\n"; + +static char const litModulateFragmentShader[] = + "uniform sampler2D tex;\n" +#if defined(QT_OPENGL_ES) + "varying mediump vec4 qCombinedColor;\n" +#else + "uniform bool separateSpecular;\n" + "varying mediump vec4 qColor;\n" + "varying mediump vec4 qSecondaryColor;\n" +#endif + "varying highp vec4 qt_TexCoord0;\n" + "\n" + "void main(void)\n" + "{\n" + " mediump vec4 col = texture2D(tex, qt_TexCoord0.st);\n" +#if defined(QT_OPENGL_ES) + " gl_FragColor = col * qCombinedColor;\n" +#else + " if (separateSpecular) {\n" + " gl_FragColor = clamp(col * qColor + vec4(qSecondaryColor.xyz, 0.0), 0.0, 1.0);\n" + " } else {\n" + " mediump vec4 lcolor = clamp(qColor + vec4(qSecondaryColor.xyz, 0.0), 0.0, 1.0);\n" + " gl_FragColor = col * lcolor;\n" + " }\n" +#endif + "}\n"; + +#endif + +#ifndef GL_MODULATE +#define GL_MODULATE 0x2100 +#endif +#ifndef GL_DECAL +#define GL_DECAL 0x2101 +#endif + +/*! + Constructs a new lit decal texture effect. +*/ +QGLLitDecalTextureEffect::QGLLitDecalTextureEffect() +#if defined(QGL_FIXED_FUNCTION_ONLY) + : QGLLitTextureEffect(GL_DECAL, 0, 0, QString()) +#else + : QGLLitTextureEffect(GL_DECAL, + litTextureVertexShader, litDecalFragmentShader, + QLatin1String("qt.texture.litdecal")) +#endif +{ +} + +/*! + Destroys this lit decal texture effect. +*/ +QGLLitDecalTextureEffect::~QGLLitDecalTextureEffect() +{ +} + +/*! + Constructs a new lit modulate texture effect. +*/ +QGLLitModulateTextureEffect::QGLLitModulateTextureEffect() +#if defined(QGL_FIXED_FUNCTION_ONLY) + : QGLLitTextureEffect(GL_MODULATE, 0, 0, QString()) +#else + : QGLLitTextureEffect(GL_MODULATE, + litTextureVertexShader, litModulateFragmentShader, + QLatin1String("qt.texture.litmodulate")) +#endif +{ +} + +/*! + Destroys this lit modulate texture effect. +*/ +QGLLitModulateTextureEffect::~QGLLitModulateTextureEffect() +{ +} + +QT_END_NAMESPACE diff --git a/src/threed/effects/qgllittextureeffect_p.h b/src/threed/effects/qgllittextureeffect_p.h new file mode 100644 index 000000000..82dbb977b --- /dev/null +++ b/src/threed/effects/qgllittextureeffect_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLLITTEXTUREEFFECT_P_H +#define QGLLITTEXTUREEFFECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgllitmaterialeffect_p.h" + +QT_BEGIN_NAMESPACE + +class QGLLitTextureEffect : public QGLLitMaterialEffect +{ +public: + virtual ~QGLLitTextureEffect(); + +protected: + QGLLitTextureEffect(GLenum mode, const char *vshader, const char *fshader, + const QString& programName); +}; + +class QGLLitDecalTextureEffect : public QGLLitTextureEffect +{ +public: + QGLLitDecalTextureEffect(); + virtual ~QGLLitDecalTextureEffect(); +}; + +class QGLLitModulateTextureEffect : public QGLLitTextureEffect +{ +public: + QGLLitModulateTextureEffect(); + virtual ~QGLLitModulateTextureEffect(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/threed/effects/qglshaderprogrameffect.cpp b/src/threed/effects/qglshaderprogrameffect.cpp new file mode 100644 index 000000000..17883df49 --- /dev/null +++ b/src/threed/effects/qglshaderprogrameffect.cpp @@ -0,0 +1,1027 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglshaderprogrameffect.h" +#include "qglabstracteffect_p.h" +#include <QtOpenGL/qglshaderprogram.h> +#include <QtCore/qfile.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLShaderProgramEffect + \since 4.8 + \brief The QGLShaderProgramEffect class provides applications with the ability to use shader programs written in GLSL as effects for 3D rendering. + \ingroup qt3d + \ingroup qt3d::painting + + \section1 Writing portable shaders + + Shader programs can be difficult to reuse across OpenGL implementations + because of varying levels of support for standard vertex attributes and + uniform variables. In particular, GLSL/ES lacks all of the + standard variables that are present on desktop OpenGL systems: + \c{gl_Vertex}, \c{gl_Normal}, \c{gl_Color}, and so on. Desktop OpenGL + lacks the variable qualifiers \c{highp}, \c{mediump}, and \c{lowp}. + + QGLShaderProgramEffect is built on top of + \l{http://doc.qt.nokia.com/4.7/qglshaderprogram.html}{QGLShaderProgram}, + which makes the process of writing portable shaders easier by + prefixing all shader programs with the following lines on desktop OpenGL: + + \code + #define highp + #define mediump + #define lowp + \endcode + + This makes it possible to run most GLSL/ES shader programs + on desktop systems. The programmer should also restrict themselves + to just features that are present in GLSL/ES, and avoid + standard variable names that only work on the desktop. + + QGLShaderProgramEffect also defines some standard attribute and uniform + variable names that all shaders are expected to use. The following + sections define these standard names. + + \section1 Attributes + + QGLShaderProgramEffect provides a standard set of 8 named vertex + attributes that can be provided via QGLPainter::setVertexBundle(): + + \table + \header \o Shader Variable \o Mesh Attribute \o Purpose + \row \o \c qt_Vertex \o QGL::Position + \o The primary position of the vertex. + \row \o \c qt_Normal \o QGL::Normal + \o The normal at each vertex, for lit material effects. + \row \o \c qt_Color \o QGL::Color + \o The color at each vertex, for per-vertex color effects. + \row \o \c qt_MultiTexCoord0 \o QGL::TextureCoord0 + \o The texture co-ordinate at each vertex for texture unit 0. + \row \o \c qt_MultiTexCoord1 \o QGL::TextureCoord1 + \o Secondary texture co-ordinate at each vertex. + \row \o \c qt_MultiTexCoord2 \o QGL::TextureCoord2 + \o Tertiary texture co-ordinate at each vertex. + \row \o \c qt_Custom0 \o QGL::CustomVertex0 + \o First custom vertex attribute that can be used for any + user-defined purpose. + \row \o \c qt_Custom1 \o QGL::CustomVertex1 + \o Second custom vertex attribute that can be used for any + user-defined purpose. + \endtable + + These attributes may be used in the vertexShader(), as in the following + example of a simple texture shader: + + \code + attribute highp vec4 qt_Vertex; + attribute highp vec4 qt_MultiTexCoord0; + uniform mediump mat4 qt_ModelViewProjectionMatrix; + varying highp vec4 qt_TexCoord0; + + void main(void) + { + gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; + qt_TexCoord0 = qt_MultiTexCoord0; + } + \endcode + + \section1 Uniform variables + + QGLShaderProgramEffect provides a standard set of uniform variables for + common values from the QGLPainter environment: + + \table + \header \o Shader Variable \o Purpose + \row \o \c qt_ModelViewProjectionMatrix + \o Combination of the modelview and projection matrices into a + single 4x4 matrix. + \row \o \c qt_ModelViewMatrix + \o Modelview matrix without the projection. This is typically + used for performing calculations in eye co-ordinates. + \row \o \c qt_ProjectionMatrix + \o Projection matrix without the modelview. + \row \o \c qt_NormalMatrix + \o Normal matrix, which is the transpose of the inverse of the + top-left 3x3 part of the modelview matrix. This is typically + used in lighting calcuations to transform \c qt_Normal. + \row \o \c qt_WorldMatrix + \o Modelview matrix without the eye position and orientation + component. See QGLPainter::worldMatrix() for further + information. + \row \o \c qt_Texture0 + \o Sampler corresponding to the texture on unit 0. + \row \o \c qt_Texture1 + \o Sampler corresponding to the texture on unit 1. + \row \o \c qt_Texture2 + \o Sampler corresponding to the texture on unit 2. + \row \o \c qt_Color + \o Set to the value of the QGLPainter::color() property. + This is typically used for flat-color shaders that do + not involve lighting. Note that this is different from + the \c qt_Color attribute, which provides per-vertex colors. + \endtable + + The above variables are usually declared in the shaders as follows + (where \c highp may be replaced with \c mediump or \c lowp depending + upon the shader's precision requirements): + + \code + uniform highp mat4 qt_ModelViewProjectionMatrix; + uniform highp mat4 qt_ModelViewMatrix; + uniform highp mat4 qt_ProjectionMatrix; + uniform highp mat3 qt_NormalMatrix; + uniform sampler2D qt_Texture0; + uniform sampler2D qt_Texture1; + uniform sampler2D qt_Texture2; + uniform highp vec4 qt_Color; + \endcode + + \section1 Material parameters + + QGLShaderProgramEffect will provide information about the front and + back materials from QGLPainter::faceMaterial() if the + \c qt_Materials array is present in the shader code. + The array should be declared as follows: + + \code + struct qt_MaterialParameters { + mediump vec4 emission; + mediump vec4 ambient; + mediump vec4 diffuse; + mediump vec4 specular; + mediump float shininess; + }; + uniform qt_MaterialParameters qt_Materials[2]; + \endcode + + The front material will be provided as index 0 and the back + material will be provided as index 1. If the shader only + needs a single material, then the \c qt_Material variable + can be declared instead: + + \code + uniform qt_MaterialParameters qt_Material; + \endcode + + The front material will be provided as the value of \c qt_Material + and the back material will be ignored. + + Note: the \c emission parameter is actually QGLMaterial::emittedLight() + combined with the QGLLightModel::ambientSceneColor() and + QGLMaterial::ambientColor(). This helps simplify lighting shaders. + + \section1 Lighting parameters + + QGLShaderProgramEffect will provide information about the current lights + specified on the QGLPainter if the \c qt_Lights array is present + in the shader code. The array should be declared as follows: + + \code + struct qt_LightParameters { + mediump vec4 ambient; + mediump vec4 diffuse; + mediump vec4 specular; + mediump vec4 position; + mediump vec3 spotDirection; + mediump float spotExponent; + mediump float spotCutoff; + mediump float spotCosCutoff; + mediump float constantAttenuation; + mediump float linearAttenuation; + mediump float quadraticAttenuation; + }; + const int qt_MaxLights = 8; + uniform qt_LightParameters qt_Lights[qt_MaxLights]; + uniform int qt_NumLights; + \endcode + + As shown, up to 8 lights can be supported at once. Usually this is + more lights than most shaders need, and so the user can change the + \c qt_MaxLights constant to a smaller value if they wish. Be sure + to also call setMaximumLights() to tell QGLShaderProgramEffect about + the new light count limit. + + The \c qt_NumLights uniform variable will be set to the actual number + of lights that are active on the QGLPainter when update() is called. + + Because most shaders will only need a single light, the shader can + declare the \c qt_Light variable instead of the \c qt_Lights array: + + \code + struct qt_SingleLightParameters { + mediump vec4 position; + mediump vec3 spotDirection; + mediump float spotExponent; + mediump float spotCutoff; + mediump float spotCosCutoff; + mediump float constantAttenuation; + mediump float linearAttenuation; + mediump float quadraticAttenuation; + }; + uniform qt_SingleLightParameters qt_Light; + \endcode + + Note that we have omitted the \c ambient, \c diffuse, and \c specular + colors for the single light. QGLShaderProgramEffect will combine the material + and light colors ahead of time into \c qt_Material or \c qt_Materials. + This makes lighting shaders more efficient because they do not have + to compute \c material_color * \c light_color; just \c material_color + is sufficient. + + \section1 Varying variables + + The name and purpose of the varying variables is up to the + author of the vertex and fragment shaders, but the following names + are recommended for texture co-ordinates: + + \table + \header \o Varying Variable \o Purpose + \row \o \c qt_TexCoord0 + \o Texture coordinate for unit 0, copied from the \c qt_MultiTexCoord0 + attribute. + \row \o \c qt_TexCoord1 + \o Texture coordinate for unit 1, copied from the \c qt_MultiTexCoord1 + attribute. + \row \o \c qt_TexCoord2 + \o Texture coordinate for unit 2, copied from the \c qt_MultiTexCoord2 + attribute. + \endtable + + \section1 Lighting shader example + + The following example demonstrates what a lighting shader that + uses a single light, a single material, and a texture might look like. + The shader is quite complex but demonstrates most of the features that + can be found in the lighting implementation of a fixed-function + OpenGL pipeline: + + \code + attribute highp vec4 qt_Vertex; + uniform highp mat4 qt_ModelViewProjectionMatrix; + attribute highp vec3 qt_Normal; + uniform highp mat4 qt_ModelViewMatrix; + uniform highp mat3 qt_NormalMatrix; + attribute highp vec4 qt_MultiTexCoord0; + varying highp vec4 qt_TexCoord0; + + struct qt_MaterialParameters { + mediump vec4 emission; + mediump vec4 ambient; + mediump vec4 diffuse; + mediump vec4 specular; + mediump float shininess; + }; + uniform qt_MaterialParameters qt_Material; + + struct qt_SingleLightParameters { + mediump vec4 position; + mediump vec3 spotDirection; + mediump float spotExponent; + mediump float spotCutoff; + mediump float spotCosCutoff; + mediump float constantAttenuation; + mediump float linearAttenuation; + mediump float quadraticAttenuation; + }; + uniform qt_SingleLightParameters qt_Light; + + varying mediump vec4 litColor; + varying mediump vec4 litSecondaryColor; + + void main(void) + { + gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; + gTexCoord0 = qt_MultiTexCoord0; + + // Calculate the vertex and normal to use for lighting calculations. + highp vec4 vertex = qt_ModelViewMatrix * qt_Vertex; + highp vec3 normal = normalize(qt_NormalMatrix * qt_Normal); + + // Start with the material's emissive color and the ambient scene color, + // which have been combined into the emission parameter. + vec4 color = ggl_Material.emission; + + // Viewer is at infinity. + vec3 toEye = vec3(0, 0, 1); + + // Determine the angle between the normal and the light direction. + vec4 pli = qt_Light.position; + vec3 toLight; + if (pli.w == 0.0) + toLight = normalize(pli.xyz); + else + toLight = normalize(pli.xyz - vertex.xyz); + float angle = max(dot(normal, toLight), 0.0); + + // Calculate the ambient and diffuse light components. + vec4 adcomponent = qt_Material.ambient + angle * qt_Material.diffuse; + + // Calculate the specular light components. + vec4 scomponent; + if (angle != 0.0) { + vec3 h = normalize(toLight + toEye); + angle = max(dot(normal, h), 0.0); + float srm = qt_Material.shininess; + vec4 scm = qt_Material.specular; + if (srm != 0.0) + scomponent = pow(angle, srm) * scm; + else + scomponent = scm; + } else { + scomponent = vec4(0, 0, 0, 0); + } + + // Apply the spotlight angle and exponent. + if (qt_Light.spotCutoff != 180.0) { + float spot = max(dot(normalize(vertex.xyz - pli.xyz), + qt_Light.spotDirection), 0.0); + if (spot < qt_Light.spotCosCutoff) { + adcomponent = vec4(0, 0, 0, 0); + scomponent = vec4(0, 0, 0, 0); + } else { + spot = pow(spot, qt_Light.spotExponent); + adcomponent *= spot; + scomponent *= spot; + } + } + + // Apply attenuation to the colors. + if (pli.w != 0.0) { + float attenuation = qt_Light.constantAttenuation; + float k1 = qt_Light.linearAttenuation; + float k2 = qt_Light.quadraticAttenuation; + if (k1 != 0.0 || k2 != 0.0) { + float len = length(pli.xyz - vertex.xyz); + attenuation += k1 * len + k2 * len * len; + } + color += adcomponent / attenuation; + scolor += scomponent / attenuation; + } else { + color += adcomponent; + scolor += scomponent; + } + + // Generate the final output colors to pass to the fragment shader. + float alpha = qt_Material.diffuse.a; + litColor = vec4(clamp(color.rgb, 0.0, 1.0), alpha); + litSecondaryColor = vec4(clamp(scolor.rgb, 0.0, 1.0), 0.0); + } + \endcode + + The corresponding fragment shader is as follows: + + \code + varying mediump vec4 litColor; + varying mediump vec4 litSecondaryColor; + varying highp vec4 qt_TexCoord0; + + void main(void) + { + vec4 color = litColor * texture2D(qt_Texture0, qt_TexCoord0.st); + gl_FragColor = clamp(color + litSecondaryColor, 0.0, 1.0); + } + \endcode + + \section1 Fixed function operation + + If the OpenGL implementation does not support shaders, then + QGLShaderProgramEffect will fall back to a flat color effect based + on QGLPainter::color(). It is recommended that the application + consult QGLPainter::isFixedFunction() to determine if some + other effect should be used instead. +*/ + +class QGLShaderProgramEffectPrivate +{ +public: + QGLShaderProgramEffectPrivate() + : maximumLights(8) + , attributes(0) + , regenerate(true) + , fixedFunction(false) +#if !defined(QGL_FIXED_FUNCTION_ONLY) + , program(0) + , matrix(-1) + , mvMatrix(-1) + , projMatrix(-1) + , normalMatrix(-1) + , worldMatrix(-1) + , texture0(-1) + , texture1(-1) + , texture2(-1) + , color(-1) + , numLights(-1) + , haveLight(0) + , haveLights(0) + , haveMaterial(0) + , haveMaterials(0) +#endif + { + } + ~QGLShaderProgramEffectPrivate() + { +#if !defined(QGL_FIXED_FUNCTION_ONLY) + delete program; +#endif + } + + QByteArray vertexShader; + QByteArray fragmentShader; + int maximumLights; + int attributes; + bool regenerate; + bool fixedFunction; +#if !defined(QGL_FIXED_FUNCTION_ONLY) + QGLShaderProgram *program; + int matrix; + int mvMatrix; + int projMatrix; + int normalMatrix; + int worldMatrix; + int texture0; + int texture1; + int texture2; + int color; + int numLights; + int haveLight : 1; + int haveLights : 1; + int haveMaterial : 1; + int haveMaterials : 1; + + void setUniformValue + (const char *array, int index, const char *field, GLfloat v); + void setUniformValue + (const char *array, int index, const char *field, const QVector3D &v); + void setUniformValue + (const char *array, int index, const char *field, const QVector4D &v); + void setUniformValue + (const char *array, int index, const char *field, const QColor &v); + + void setLight + (const QGLLightParameters *lparams, const QMatrix4x4 <ransform, + const char *array, int index); + void setMaterial + (const QGLMaterial *mparams, const QGLLightModel *model, + const QGLLightParameters *lparams, const char *array, int index); +#endif +}; + +#if !defined(QGL_FIXED_FUNCTION_ONLY) + +void QGLShaderProgramEffectPrivate::setUniformValue + (const char *array, int index, const char *field, GLfloat v) +{ + char name[128]; + if (index >= 0) + qsnprintf(name, sizeof(name), "%s[%d].%s", array, index, field); + else + qsnprintf(name, sizeof(name), "%s.%s", array, field); + program->setUniformValue(name, v); +} + +void QGLShaderProgramEffectPrivate::setUniformValue + (const char *array, int index, const char *field, const QVector3D &v) +{ + char name[128]; + if (index >= 0) + qsnprintf(name, sizeof(name), "%s[%d].%s", array, index, field); + else + qsnprintf(name, sizeof(name), "%s.%s", array, field); + program->setUniformValue(name, v); +} + +void QGLShaderProgramEffectPrivate::setUniformValue + (const char *array, int index, const char *field, const QVector4D &v) +{ + char name[128]; + if (index >= 0) + qsnprintf(name, sizeof(name), "%s[%d].%s", array, index, field); + else + qsnprintf(name, sizeof(name), "%s.%s", array, field); + program->setUniformValue(name, v); +} + +void QGLShaderProgramEffectPrivate::setUniformValue + (const char *array, int index, const char *field, const QColor &v) +{ + char name[128]; + if (index >= 0) + qsnprintf(name, sizeof(name), "%s[%d].%s", array, index, field); + else + qsnprintf(name, sizeof(name), "%s.%s", array, field); + program->setUniformValue(name, v); +} + +void QGLShaderProgramEffectPrivate::setLight + (const QGLLightParameters *lparams, const QMatrix4x4 <ransform, + const char *array, int index) +{ + if (index >= 0) { + // Single lights embed the color values into the material. + setUniformValue(array, index, "ambient", lparams->ambientColor()); + setUniformValue(array, index, "diffuse", lparams->diffuseColor()); + setUniformValue(array, index, "specular", lparams->specularColor()); + } + setUniformValue + (array, index, "position", lparams->eyePosition(ltransform)); + setUniformValue + (array, index, "spotDirection", + lparams->eyeSpotDirection(ltransform).normalized()); + setUniformValue + (array, index, "spotExponent", GLfloat(lparams->spotExponent())); + setUniformValue + (array, index, "spotCutoff", GLfloat(lparams->spotAngle())); + setUniformValue + (array, index, "spotCosCutoff", GLfloat(lparams->spotCosAngle())); + setUniformValue + (array, index, "constantAttenuation", + GLfloat(lparams->constantAttenuation())); + setUniformValue + (array, index, "linearAttenuation", + GLfloat(lparams->linearAttenuation())); + setUniformValue + (array, index, "quadraticAttenuation", + GLfloat(lparams->quadraticAttenuation())); +} + +static inline QVector4D colorToVector4(const QColor& color) +{ + return QVector4D(color.redF(), color.greenF(), + color.blueF(), color.alphaF()); +} + +// Combine a material and light color into a single color. +static inline QVector4D colorToVector4 + (const QColor &color, const QColor &lightColor) +{ + return QVector4D(color.redF() * lightColor.redF(), + color.greenF() * lightColor.greenF(), + color.blueF() * lightColor.blueF(), + color.alphaF() * lightColor.alphaF()); +} + +void QGLShaderProgramEffectPrivate::setMaterial + (const QGLMaterial *mparams, const QGLLightModel *model, + const QGLLightParameters *lparams, const char *array, int index) +{ + if (lparams) { + setUniformValue + (array, index, "ambient", + colorToVector4(mparams->ambientColor(), lparams->ambientColor())); + setUniformValue + (array, index, "diffuse", + colorToVector4(mparams->diffuseColor(), lparams->diffuseColor())); + setUniformValue + (array, index, "specular", + colorToVector4(mparams->specularColor(), lparams->specularColor())); + } else { + setUniformValue + (array, index, "ambient", mparams->ambientColor()); + setUniformValue + (array, index, "diffuse", mparams->diffuseColor()); + setUniformValue + (array, index, "specular", mparams->specularColor()); + } + setUniformValue + (array, index, "emission", + colorToVector4(mparams->emittedLight()) + + colorToVector4(mparams->ambientColor(), model->ambientSceneColor())); + setUniformValue + (array, index, "shininess", GLfloat(mparams->shininess())); +} + +#endif // !QGL_FIXED_FUNCTION_ONLY + +/*! + Constructs a new shader program effect. This constructor is typically + followed by calls to setVertexShader() and setFragmentShader(). + + Note that a shader program effect will be bound to the QGLContext that + is current when setActive() is called for the first time. After that, + the effect can only be used with that context or any other QGLContext + that shares with it. +*/ +QGLShaderProgramEffect::QGLShaderProgramEffect() + : d_ptr(new QGLShaderProgramEffectPrivate) +{ +} + +/*! + Destroys this shader program effect. +*/ +QGLShaderProgramEffect::~QGLShaderProgramEffect() +{ +} + +/*! + \reimp +*/ +void QGLShaderProgramEffect::setActive(QGLPainter *painter, bool flag) +{ + Q_D(QGLShaderProgramEffect); + +#if !defined(QGL_SHADERS_ONLY) + d->fixedFunction = painter->isFixedFunction(); + if (d->fixedFunction) { + // Fixed function emulation is flat color only. + if (flag) + glEnableClientState(GL_VERTEX_ARRAY); + else + glDisableClientState(GL_VERTEX_ARRAY); + return; + } +#endif + +#if !defined(QGL_FIXED_FUNCTION_ONLY) + static const char *const attributes[] = { + "qt_Vertex", + "qt_Normal", + "qt_Color", + "qt_MultiTexCoord0", + "qt_MultiTexCoord1", + "qt_MultiTexCoord2", + "qt_Custom0", + "qt_Custom1" + }; + const int numAttributes = 8; + Q_UNUSED(painter); + int attr; + if (d->regenerate) { + // The shader source has changed since the last call to setActive(). + delete d->program; + d->program = 0; + d->regenerate = false; + } + if (!d->program) { + if (!flag) + return; + Q_ASSERT(!d->vertexShader.isEmpty()); + Q_ASSERT(!d->fragmentShader.isEmpty()); + d->program = new QGLShaderProgram(); + d->program->addShaderFromSourceCode + (QGLShader::Vertex, d->vertexShader); + d->program->addShaderFromSourceCode + (QGLShader::Fragment, d->fragmentShader); + if (beforeLink()) { + for (attr = 0; attr < numAttributes; ++attr) + d->program->bindAttributeLocation(attributes[attr], attr); + } + if (!d->program->link()) { + qWarning("QGLShaderProgramEffect::setActive(): could not link shader program"); + delete d->program; + d->program = 0; + return; + } + afterLink(); + d->attributes = 0; + for (attr = 0; attr < numAttributes; ++attr) { + // Determine which attributes were actually present in the program. + if (d->program->attributeLocation(attributes[attr]) != -1) + d->attributes |= (1 << attr); + } + if (d->program->attributeLocation("qgl_Vertex") != -1) + qWarning("QGLShaderProgramEffect: qgl_Vertex no longer supported; use qt_Vertex instead"); + d->matrix = d->program->uniformLocation("qt_ModelViewProjectionMatrix"); + d->mvMatrix = d->program->uniformLocation("qt_ModelViewMatrix"); + d->projMatrix = d->program->uniformLocation("qt_ProjectionMatrix"); + d->normalMatrix = d->program->uniformLocation("qt_NormalMatrix"); + d->worldMatrix = d->program->uniformLocation("qt_WorldMatrix"); + d->texture0 = d->program->uniformLocation("qt_Texture0"); + d->texture1 = d->program->uniformLocation("qt_Texture1"); + d->texture2 = d->program->uniformLocation("qt_Texture2"); + d->color = d->program->uniformLocation("qt_Color"); + d->numLights = d->program->uniformLocation("qt_NumLights"); + d->haveLight = + (d->program->uniformLocation("qt_Light.position") != -1); + d->haveLights = + (d->program->uniformLocation("qt_Lights[0].position") != -1); + d->haveMaterial = + (d->program->uniformLocation("qt_Material.diffuse") != -1); + d->haveMaterials = + (d->program->uniformLocation("qt_Materials[0].diffuse") != -1); + } + if (flag) { + d->program->bind(); + for (attr = 0; attr < numAttributes; ++attr) { + if ((d->attributes & (1 << attr)) == 0) + continue; + d->program->enableAttributeArray(attr); + } + if (d->texture0 != -1) + d->program->setUniformValue(d->texture0, 0); + if (d->texture1 != -1) + d->program->setUniformValue(d->texture1, 1); + if (d->texture2 != -1) + d->program->setUniformValue(d->texture2, 2); + } else { + for (attr = 0; attr < int(QGL::UserVertex); ++attr) { + if ((d->attributes & (1 << attr)) != 0) + d->program->disableAttributeArray(attr); + } + d->program->release(); + } +#endif +} + +/*! + \reimp +*/ +void QGLShaderProgramEffect::update(QGLPainter *painter, QGLPainter::Updates updates) +{ + Q_D(QGLShaderProgramEffect); +#if !defined(QGL_SHADERS_ONLY) + if (d->fixedFunction) { + // Fixed function emulation is flat color only. + painter->updateFixedFunction + (updates & (QGLPainter::UpdateColor | QGLPainter::UpdateMatrices)); + return; + } +#endif +#if !defined(QGL_FIXED_FUNCTION_ONLY) + if ((updates & QGLPainter::UpdateColor) != 0 && d->color != -1) + d->program->setUniformValue(d->color, painter->color()); + if ((updates & QGLPainter::UpdateMatrices) != 0) { + if (d->matrix != -1) + d->program->setUniformValue(d->matrix, painter->combinedMatrix()); + } + if ((updates & QGLPainter::UpdateModelViewMatrix) != 0) { + if (d->mvMatrix != -1) + d->program->setUniformValue(d->mvMatrix, painter->modelViewMatrix()); + if (d->normalMatrix != -1) + d->program->setUniformValue(d->normalMatrix, painter->normalMatrix()); + if (d->worldMatrix != -1) + d->program->setUniformValue(d->worldMatrix, painter->worldMatrix()); + } + if ((updates & QGLPainter::UpdateProjectionMatrix) != 0) { + if (d->projMatrix != -1) + d->program->setUniformValue(d->projMatrix, painter->projectionMatrix()); + } + if ((updates & QGLPainter::UpdateLights) != 0) { + if (d->haveLight) { + // Only one light needed so make it the main light. + d->setLight(painter->mainLight(), painter->mainLightTransform(), + "qt_Light", -1); + } else if (d->haveLights) { + // Shader supports multiple light sources. + int numLights = 0; + int maxLightId = painter->maximumLightId(); + if (maxLightId < 0) { + // No lights - re-enable the main light so we have something. + painter->mainLight(); + maxLightId = 0; + } + for (int lightId = 0; lightId <= maxLightId; ++lightId) { + // Is this light currently enabled? + const QGLLightParameters *lparams = painter->light(lightId); + if (!lparams) + continue; + + // Set the parameters for the next shader light number. + d->setLight(lparams, painter->lightTransform(lightId), + "qt_Lights", numLights); + + // Bail out if we've hit the maximum shader light limit. + ++numLights; + if (numLights >= d->maximumLights) + break; + } + if (d->numLights != -1) + d->program->setUniformValue(d->numLights, numLights); + } + } + if ((updates & QGLPainter::UpdateMaterials) != 0 || + ((updates & QGLPainter::UpdateLights) != 0 && d->haveLight)) { + if (d->haveLight) { + // For a single light source, combine the light colors + // into the material colors. + if (d->haveMaterial) { + d->setMaterial(painter->faceMaterial(QGL::FrontFaces), + painter->lightModel(), painter->mainLight(), + "qt_Material", -1); + } else if (d->haveMaterials) { + d->setMaterial(painter->faceMaterial(QGL::FrontFaces), + painter->lightModel(), painter->mainLight(), + "qt_Materials", 0); + d->setMaterial(painter->faceMaterial(QGL::BackFaces), + painter->lightModel(), painter->mainLight(), + "qt_Materials", 1); + } + } else { + // Multiple light sources, so light colors are separate. + if (d->haveMaterial) { + d->setMaterial(painter->faceMaterial(QGL::FrontFaces), + painter->lightModel(), 0, "qt_Material", -1); + } else if (d->haveMaterials) { + d->setMaterial(painter->faceMaterial(QGL::FrontFaces), + painter->lightModel(), 0, "qt_Materials", 0); + d->setMaterial(painter->faceMaterial(QGL::BackFaces), + painter->lightModel(), 0, "qt_Materials", 1); + } + } + } +#endif +} + +/*! + Returns the source code for the vertex shader. + + \sa setVertexShader(), fragmentShader(), setVertexShaderFromFile() +*/ +QByteArray QGLShaderProgramEffect::vertexShader() const +{ + Q_D(const QGLShaderProgramEffect); + return d->vertexShader; +} + +/*! + Sets the \a source code for the vertex shader. + + \sa vertexShader(), setFragmentShader(), setVertexShaderFromFile() +*/ +void QGLShaderProgramEffect::setVertexShader(const QByteArray &source) +{ + Q_D(QGLShaderProgramEffect); + d->vertexShader = source; + d->regenerate = true; +} + +/*! + Sets the source code for the vertex shader to the contents + of \a fileName. + + \sa setVertexShader(), setFragmentShaderFromFile() +*/ +void QGLShaderProgramEffect::setVertexShaderFromFile(const QString &fileName) +{ + Q_D(QGLShaderProgramEffect); + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + d->vertexShader = file.readAll(); + d->regenerate = true; + } else { + qWarning() << "QGLShaderProgramEffect::setVertexShaderFromFile: could not open " << fileName; + } +} + +/*! + Returns the source code for the fragment shader. + + \sa setFragmentShader(), vertexShader() +*/ +QByteArray QGLShaderProgramEffect::fragmentShader() const +{ + Q_D(const QGLShaderProgramEffect); + return d->fragmentShader; +} + +/*! + Sets the source code for the fragment shader to the contents + of \a fileName. + + \sa setFragmentShader(), setVertexShaderFromFile() +*/ +void QGLShaderProgramEffect::setFragmentShaderFromFile(const QString &fileName) +{ + Q_D(QGLShaderProgramEffect); + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + d->fragmentShader = file.readAll(); + d->regenerate = true; + } else { + qWarning() << "QGLShaderProgramEffect::setFragmentShaderFromFile: could not open " << fileName; + } +} + +/*! + Sets the \a source code for the fragment shader. + + \sa fragmentShader(), setVertexShader() +*/ +void QGLShaderProgramEffect::setFragmentShader(const QByteArray &source) +{ + Q_D(QGLShaderProgramEffect); + d->fragmentShader = source; + d->regenerate = true; +} + +/*! + Returns the maximum number of lights that are supported by this + shader program effect. The default value is 8. + + The actual number of lights will be provided to the vertexShader() + as the \c{qt_NumLights} uniform variable, which will always be + less than or equal to maximumLights(). + + \sa setMaximumLights() +*/ +int QGLShaderProgramEffect::maximumLights() const +{ + Q_D(const QGLShaderProgramEffect); + return d->maximumLights; +} + +/*! + Sets the maximum number of lights that are supported by this + shader program effect to \a value. + + \sa maximumLights() +*/ +void QGLShaderProgramEffect::setMaximumLights(int value) +{ + Q_D(QGLShaderProgramEffect); + d->maximumLights = value; +} + +/*! + Returns the shader program object that was created for this effect; + null if setActive() has not been called yet. + + This function can be used by the application to adjust custom + uniform variables after the effect is activated on a QGLPainter: + + \code + painter.setUserEffect(effect); + effect->program()->setUniformValue("springiness", GLfloat(0.5f)); + \endcode +*/ +QGLShaderProgram *QGLShaderProgramEffect::program() const +{ +#if !defined(QGL_FIXED_FUNCTION_ONLY) + Q_D(const QGLShaderProgramEffect); + return d->program; +#else + return 0; +#endif +} + +/*! + Called by setActive() just before the program() is linked. + Returns true if the standard vertex attributes should be bound + by calls to QGLShaderProgram::bindAttributeLocation(). Returns + false if the subclass has already bound the attributes. + + This function can be overridden by subclasses to alter the + vertex attribute bindings, or to add additional shader stages + to program(). + + \sa afterLink() +*/ +bool QGLShaderProgramEffect::beforeLink() +{ + return true; +} + +/*! + Called by setActive() just after the program() is linked. + The default implementation does nothing. + + This function can be overridden by subclasses to resolve uniform + variable locations and cache them for later use in update(). + + \sa beforeLink() +*/ +void QGLShaderProgramEffect::afterLink() +{ +} + +QT_END_NAMESPACE diff --git a/src/threed/effects/qglshaderprogrameffect.h b/src/threed/effects/qglshaderprogrameffect.h new file mode 100644 index 000000000..c77392184 --- /dev/null +++ b/src/threed/effects/qglshaderprogrameffect.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLSHADERPROGRAMEFFECT_H +#define QGLSHADERPROGRAMEFFECT_H + +#include "qglabstracteffect.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGLShaderProgramEffectPrivate; +class QGLShaderProgram; + +class Q_QT3D_EXPORT QGLShaderProgramEffect : public QGLAbstractEffect +{ +public: + QGLShaderProgramEffect(); + virtual ~QGLShaderProgramEffect(); + + void setActive(QGLPainter *painter, bool flag); + void update(QGLPainter *painter, QGLPainter::Updates updates); + + QByteArray vertexShader() const; + void setVertexShader(const QByteArray &source); + void setVertexShaderFromFile(const QString &fileName); + + QByteArray fragmentShader() const; + void setFragmentShader(const QByteArray &source); + void setFragmentShaderFromFile(const QString &fileName); + + int maximumLights() const; + void setMaximumLights(int value); + + QGLShaderProgram *program() const; + +protected: + virtual bool beforeLink(); + virtual void afterLink(); + +private: + QScopedPointer<QGLShaderProgramEffectPrivate> d_ptr; + + Q_DISABLE_COPY(QGLShaderProgramEffect) + Q_DECLARE_PRIVATE(QGLShaderProgramEffect) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/geometry/geometry.pri b/src/threed/geometry/geometry.pri new file mode 100644 index 000000000..5623ac054 --- /dev/null +++ b/src/threed/geometry/geometry.pri @@ -0,0 +1,28 @@ +INCLUDEPATH += $$PWD +VPATH += $$PWD +HEADERS += qglcube.h \ + qglsphere.h \ + qgeometrydata.h \ + qlogicalvertex.h \ + qglbuilder.h \ + qglbezierpatches.h \ + qglmaterialcollection.h \ + qglteapot.h \ + qglcylinder.h \ + qgldome.h +SOURCES += qglcube.cpp \ + qglsphere.cpp \ + qgeometrydata.cpp \ + qglbuilder.cpp \ + qglsection.cpp \ + qglbezierpatches.cpp \ + qglmaterialcollection.cpp \ + qglteapot.cpp \ + qlogicalvertex.cpp \ + qglcylinder.cpp \ + qgldome.cpp +PRIVATE_HEADERS += qglteapot_data_p.h \ + qglbuilder_p.h \ + qglsection_p.h \ + qglteapot_data_p.h \ + qvector_utils_p.h diff --git a/src/threed/geometry/qgeometrydata.cpp b/src/threed/geometry/qgeometrydata.cpp new file mode 100644 index 000000000..f5cedc960 --- /dev/null +++ b/src/threed/geometry/qgeometrydata.cpp @@ -0,0 +1,2025 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeometrydata.h" +#include "qlogicalvertex.h" +#include "qglpainter.h" + +#include <QtOpenGL/qgl.h> +#include <QtCore/qdebug.h> + +/*! + \class QGeometryData + \brief The QGeometryData class encapsulates sets of geometry data. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::geometry + + The QGeometryData class encloses a number of data arrays + that model most typical vertex data needs. The class provides a + store for all of the data types in the QGL::VertexAttribute enumeration. + + \table + \header + \o QGL::VertexAttribute + \o QGeometryData functions + \row + \o QGL::Position + \o appendVertex(), vertex(), vertices() + \row + \o QGL::Normal + \o appendNormal(), normal(), normals() + \row + \o QGL::Color + \o appendColor(), colorRef(), colors() + \row + \o QGL::TextureCoord0 - QGL::TextureCoord3 + \o appendTexCoord(), texCoordRef(), texCoords() + \row + \o QGL::CustomVertex0 - QGL::CustomVertex1, QGL::UserVertex + \o appendAttribute(), vector3DAttribute(), attributes() + \endtable + + Additionally the class provides the following features: + \list + \o appendVertex() for adding a QLogicalVertex() + \o logicalVertexAt() for return the data at an index as a QLogicalVertex() + \o hasField() to find if a particular data type is present + \o normalizeNormals() to reduce all normal vectors to unit length + \o boundingBox() to find the bounds of the geometry + \endlist + + It is up to the user of a QGeometryData instance to ensure that the + data has an equal number of items in each field. For example, if five + vertices are added and only two normals are added, the logical vertex at + position 3 will be corrupt, since it does not have a normal. + + While data is being accumulated the counts of different fields will vary, + since it may be convenient to add several vertices, then several normals, + colors or attributes at a time. However when a logical vertex is + constructed or when the data is sent to the GPU, counts of all fields + must be equal. + + QGeometryData uses explicit sharing with lazy creation of internal + data so that code like: + \code + QGeometryData myData; + if (processed) + myData = processedData(); + \endcode + is very inexpensive, since the first declaration and initialization + does not cause internal data to be created (only to be overwritten by the + assignment operation). + + Since QGeometryData is explicitly shared, variables of type + QGeometryData behave like references, and the underlying data is modified + by calling a non-const function on any variable which shares that data. + + To force an explicit copy call the detach() function. +*/ + +/*! + \typedef QGL::IndexArray + + This is a convenience for either QArray<ushort> (OpenGL/ES) or + QArray<int> (desktop OpenGL). +*/ + +class QGeometryDataPrivate +{ +public: + QGeometryDataPrivate(); + ~QGeometryDataPrivate(); + QGeometryDataPrivate *clone() const; + + QBasicAtomicInt ref; + + QVector3DArray vertices; + QVector3DArray normals; + QArray<QColor4ub> colors; + QList<QCustomDataArray> attributes; + QList<QVector2DArray> textures; + QGL::IndexArray indices; + QGLVertexBundle vertexBundle; + QGLIndexBuffer indexBuffer; + bool uploadsViable; + bool modified; + QBox3D bb; + static const int ATTR_CNT = 32; + quint32 fields; + qint8 key[ATTR_CNT]; + quint8 size[ATTR_CNT]; + int count; + int reserved; + bool boxValid; + QGeometryData::BufferStrategy bufferStrategy; +}; + +QGeometryDataPrivate::QGeometryDataPrivate() + : uploadsViable(true) + , modified(false) + , fields(0) + , count(0) + , reserved(-1) + , boxValid(true) + , bufferStrategy(QGeometryData::BufferIfPossible | QGeometryData::KeepClientData) +{ + ref = 0; + qMemSet(key, -1, ATTR_CNT); + qMemSet(size, 0, ATTR_CNT); +} + +QGeometryDataPrivate::~QGeometryDataPrivate() +{ +} + +QGeometryDataPrivate *QGeometryDataPrivate::clone() const +{ + QGeometryDataPrivate *temp = new QGeometryDataPrivate; + temp->vertices = vertices; + temp->normals = normals; + temp->colors = colors; + temp->attributes = attributes; + temp->textures = textures; + temp->indices = indices; + temp->vertexBundle = vertexBundle; + temp->indexBuffer = indexBuffer; + temp->uploadsViable = uploadsViable; + temp->modified = modified; + temp->bb = bb; + temp->fields = fields; + qMemCopy(temp->key, key, ATTR_CNT); + qMemCopy(temp->size, size, ATTR_CNT); + temp->count = count; + temp->reserved = reserved; + temp->boxValid = boxValid; + temp->bufferStrategy = bufferStrategy; + return temp; +} + +/*! + \fn quint32 QGL::fieldMask(QGL::VertexAttribute attribute) + \relates QGeometryData + Returns an unsigned integer mask from the \a attribute. + + \sa QGeometryData::fields() +*/ + +/*! + \enum QGeometryData::BufferStrategyFlags + + This enum serves to describe how management of the data is handled + with respect to vertex buffer objects. The strategies are essentially a + combination of whether the client data is kept around after it has been + successfully uploaded to the GPU; and whether an upload is attempted at + all. + + If the data set is very small it may be pointless to use up a VBO, hence + in this case KeepClientData may be used resulting in no attempt to upload + the data and client side arrays used instead. + + \value InvalidStrategy No valid strategy has been specified. + \value KeepClientData Keep the client data, even after successful upload to the GPU. + \value BufferIfPossible Try to upload the data to the GPU. +*/ + +/*! + Construct an empty QGeometryData +*/ +QGeometryData::QGeometryData() + : d(0) +{ +} + +/*! + Construct QGeometryData as a copy of \a other +*/ +QGeometryData::QGeometryData(const QGeometryData &other) + : d(other.d) +{ + if (d) + d->ref.ref(); +} + +/*! + Construct an empty QGeometryData with the \a fields enabled. +*/ +QGeometryData::QGeometryData(quint32 fields) + : d(new QGeometryDataPrivate) +{ + d->ref.ref(); + const quint32 mask = 0x01; + for (int field = 0; fields; ++field, fields >>= 1) + { + if (!(mask & fields)) continue; + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + enableField(attr); + } +} + +/*! + Destroys this QGeometryData recovering any resources. +*/ +QGeometryData::~QGeometryData() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Assigns this QGeometryData to be a copy of \a other. +*/ +QGeometryData &QGeometryData::operator=(const QGeometryData &other) +{ + if (d != other.d) + { + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + } + return *this; +} + +/*! + Appends the geometry in \a data to this. If this is empty, then all + fields of \a data are appended; otherwise (when this has existing fields) + only those fields that exist in both are appended. + + This does not change the indices - to reference the new geometry add + indices via the appendIndices() functions. +*/ +void QGeometryData::appendGeometry(const QGeometryData &data) +{ + if (data.d && data.count()) + { + detach(); + d->modified = true; + d->boxValid = false; + int cnt = data.d->count; + const quint32 mask = 0x01; + quint32 fields = d->fields | data.fields(); + d->fields = fields; + for (int field = 0; fields; ++field, fields >>= 1) + { + if (mask & fields) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + enableField(attr); // might not be enabled if we had NO fields + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + d->vertices.append(data.d->vertices); + else if (attr == QGL::Normal) + d->normals.append(data.d->normals); + else // colors + d->colors.append(data.d->colors); + } + else if (attr < QGL::CustomVertex0) + { + d->textures[d->key[attr]].append(data.texCoords(attr)); + } + else + { + d->attributes[d->key[attr]].append(data.attributes(attr)); + } + } + } + d->count += cnt; + } +} + +/*! + Appends all the data fields in QLogicalVertex \a v to this + QGeometryData object. +*/ +int QGeometryData::appendVertex(const QLogicalVertex &v) +{ + create(); + d->modified = true; + if (d->boxValid) + d->bb.unite(v.vertex()); + quint32 fields = v.fields(); + const quint32 mask = 0x01; + for (int field = 0; fields; ++field, fields >>= 1) + { + if (mask & fields) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + appendVertex(v.vertex()); + else if (attr == QGL::Normal) + appendNormal(v.normal()); + else + appendColor(v.color()); + } + else if (attr < QGL::CustomVertex0) + { + appendTexCoord(v.texCoord(attr), attr); + } + else + { + appendAttribute(v.attribute(attr), attr); + } + } + } + return d->count - 1; +} + +/*! + Returns a QLogicalVertex that references the \a{i}'th logical vertex + of this geometry. +*/ +QLogicalVertex QGeometryData::logicalVertexAt(int i) const +{ + return QLogicalVertex(*this, i); +} + +/*! + Normalize all the normal vectors in this geometry to unit length. +*/ +void QGeometryData::normalizeNormals() +{ + check(); + if (d) // nothng to do if its null + { + create(); + d->modified = true; + if (hasField(QGL::Normal)) + { + for (int i = 0; i < d->normals.count(); ++i) + d->normals[i].normalize(); + } + } +} + +/*! + Calculate and return a bounding box for the vertex data in this geometry. +*/ +QBox3D QGeometryData::boundingBox() const +{ + QBox3D box; + if (d) + { + if (d->boxValid) + { + box = d->bb; + } + else + { + for (int i = 0; i < d->count; ++i) + box.unite(d->vertices.at(i)); + d->bb = box; + } + } + return box; +} + +/*! + Returns the coordinates of the center of the geometry. + + The center is calculated as the centroid or geometric barycenter + of the vertices (the average of the vertices). For a convex hull this + is guaranteed to be inside the figure. +*/ +QVector3D QGeometryData::center() const +{ + QVector3D center; + for (int i = 0; i < d->vertices.count(); ++i) + center += d->vertices.at(i); + return center / (float)d->vertices.count(); +} + +/*! + Returns a copy of this geometry data with elements in reverse order. +*/ +QGeometryData QGeometryData::reversed() const +{ + QGeometryData r; + for (int i = count() - 1; i >= 0; --i) + r.appendVertex(logicalVertexAt(i)); + return r; +} + +/*! + Returns a copy of this geometry data with QGL::Position data translated by + the vector \a t. The other fields are unchanged. +*/ +QGeometryData QGeometryData::translated(const QVector3D &t) const +{ + QGeometryData r(*this); + r.detach(); + for (int i = 0; i < count(); ++i) + { + r.vertex(i) = r.vertexAt(i) + t; + } + return r; +} + +/*! + Modifies this geometry data by generating texture data based on QGL::Position + values. If \a orientation is Qt::Horizontal (the default) then x-coordinate + values are generated, and y-coordinate values are set to 0.0; otherwise + y-coordinate values are generated and x-coordinate values are set to 0.0. + The values are appended to the texture coordinate \a field. + + The method of calculation is based on the assumption that the vertex data + is a list of extents which span across the texture space horizontally, from + x = 0.0 to x = 1.0, in the case of Qt::Horizontal; or vertically in the + case of Qt::Vertical. The texture space of 1.0 is divided up proportionately + by the length of each extent. + + \image texture-coords-gen.png + + In this diagram the large blue numbers are the lengths of each extent, and + the texture coordinates generated are shown as \c{t(7/16, 1)} and so on. + + Thus the texture coordinate t0 for vertex v0, is 0.0; t1 for vertex v1 is + \c{(v1 - v0).length() / totalLength} and so on. + + The code to produce the texture coordinates for the quads in the image is: + \code + QGeometryData top; + + // add data to the primitive + top.appendVertex(QVector3D(0.0, 0.0, 0.0)); + top.appendVertex(QVector3D(6.0, 3.6, 0.0)); // (v1 - v0).length() = 7.0 + top.appendVertex(QVector3D(10.0, 0.6, 0.0)); // (v2 - v1).length() = 5.0 + top.appendVertex(QVector3D(13.0, 3.24, 0.0)); // (v3 - v2).length() = 4.0 + + // generate x (Qt::Horizontal) texture coordinates over the primitive + top.generateTextureCoordinates(); // spread over 7 + 5 + 4 = 16 + + // make a copy translated down, the copy has y texture coordinates all 0 + QGeometryData bottom = top.translated(QVector3D(0, 0, -1)); + + // now modify the top so its y texture coordinates are all 1 + for (int i = 0; i < top.count(); ++i) + top.texCoordRef(QGL::TextureCoord0).setY(1.0); + + displayList->addQuadsZipped(top, bottom); + \endcode +*/ +void QGeometryData::generateTextureCoordinates(Qt::Orientation orientation, QGL::VertexAttribute field) +{ + QArray<qreal> extents; + extents.append(0.0); + qreal totalExtents = 0.0; + QArray<QVector3D> v = vertices(); + for (int i = 0; i < v.count() - 1; ++i) + { + int n = (i + 1) % v.count(); + QVector3D e = v[n] - v[i]; + qreal extent = e.length(); + totalExtents += extent; + extents.append(totalExtents); + } + if (hasField(field)) + clear(field); + if (orientation == Qt::Horizontal) + { + for (int i = 0; i < v.count(); ++i) + appendTexCoord(QVector2D(extents[i] / totalExtents, 0.0), field); + } + else + { + for (int i = 0; i < v.count(); ++i) + appendTexCoord(QVector2D(0.0, extents[i] / totalExtents), field); + } +} + +/*! + Returns a QGeometryData instance containing alternating vertices from + this geometry and \a other. The resulting geometry contains N vertices + where \c{N == qMin(count(), other.count())}, and has only the fields + that are in both geometries. +*/ +QGeometryData QGeometryData::interleavedWith(const QGeometryData &other) const +{ + QGeometryData res; + check(); + other.check(); + if (d && other.d) + { + int cnt = qMax(d->count, other.d->count); + const quint32 mask = 0x01; + quint32 fields = d->fields & other.d->fields; + for (int field = 0; fields; ++field, fields >>= 1) + { + if (mask & fields) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + res.enableField(attr); + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + { + QArray<QVector3D> tmp; + for (int i = 0; i < cnt; ++i) + { + tmp.append(d->vertices.at(i)); + tmp.append(other.d->vertices.at(i)); + } + res.d->vertices = tmp; + } + else if (attr == QGL::Normal) + { + QArray<QVector3D> tmp; + for (int i = 0; i < cnt; ++i) + { + tmp.append(d->normals.at(i)); + tmp.append(other.d->normals.at(i)); + } + res.d->normals = tmp; + } + else // colors + { + QArray<QColor4ub> tmp; + for (int i = 0; i < cnt; ++i) + { + tmp.append(d->colors.at(i)); + tmp.append(other.d->colors.at(i)); + } + res.d->colors = tmp; + } + } + else if (attr < QGL::CustomVertex0) + { + QArray<QVector2D> tmp; + const QArray<QVector2D> txa = d->textures.at(d->key[attr]); + const QArray<QVector2D> txb = other.d->textures.at(other.d->key[attr]); + for (int i = 0; i < cnt; ++i) + { + tmp.append(txa.at(i)); + tmp.append(txb.at(i)); + } + res.d->textures[d->key[attr]] = tmp; + } + else + { + QCustomDataArray tmp; + const QCustomDataArray ata = d->attributes.at(d->key[attr]); + const QCustomDataArray atb = other.d->attributes.at(other.d->key[attr]); + for (int i = 0; i < cnt; ++i) + { + tmp.append(ata.at(i)); + tmp.append(atb.at(i)); + } + res.d->attributes[d->key[attr]] = tmp; + } + } + } + res.d->count = cnt * 2; + } + return res; +} + +/*! + Sets this QGeometryData to contain alternating vertices from + this geometry and \a other. The resulting geometry contains \c{N * 2} vertices + where \c{N == qMin(count(), other.count())}, and has only the fields + that are in both geometries. +*/ +void QGeometryData::interleaveWith(const QGeometryData &other) +{ + check(); + other.check(); + if (d && other.d) + { + create(); + d->modified = true; + d->boxValid = false; + int cnt = qMin(d->count, other.d->count); + const quint32 mask = 0x01; + quint32 fields = d->fields & other.d->fields; + for (int field = 0; fields; ++field, fields >>= 1) + { + if (mask & fields) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + { + QArray<QVector3D> tmp; + for (int i = 0; i < cnt; ++i) + { + tmp.append(d->vertices.at(i)); + tmp.append(other.d->vertices.at(i)); + } + d->vertices = tmp; + } + else if (attr == QGL::Normal) + { + QArray<QVector3D> tmp; + for (int i = 0; i < cnt; ++i) + { + tmp.append(d->normals.at(i)); + tmp.append(other.d->normals.at(i)); + } + d->normals = tmp; + } + else // colors + { + QArray<QColor4ub> tmp; + for (int i = 0; i < cnt; ++i) + { + tmp.append(d->colors.at(i)); + tmp.append(other.d->colors.at(i)); + } + d->colors = tmp; + } + } + else if (attr < QGL::CustomVertex0) + { + QArray<QVector2D> tmp; + const QArray<QVector2D> txa = d->textures.at(d->key[attr]); + const QArray<QVector2D> txb = other.d->textures.at(other.d->key[attr]); + for (int i = 0; i < cnt; ++i) + { + tmp.append(txa.at(i)); + tmp.append(txb.at(i)); + } + d->textures[d->key[attr]] = tmp; + } + else + { + QCustomDataArray tmp; + const QCustomDataArray ata = d->attributes.at(d->key[attr]); + const QCustomDataArray atb = other.d->attributes.at(other.d->key[attr]); + for (int i = 0; i < cnt; ++i) + { + tmp.append(ata.at(i)); + tmp.append(atb.at(i)); + } + d->attributes[d->key[attr]] = tmp; + } + } + } + d->count = cnt * 2; + } +} + +/*! + Clear all data structures. The actual fields are retained, but they + have no contents. + \code + QGeometryData data; + data.appendVertex(a); + data.appendTexCoord(t); + + // prints "1" + qDebug() << data.count(); + + // x == a + QVector3D x = data.vertexAt(0); + + quint32 flds = QGL::fieldMask(QGL::Position) | QGL::fieldMask(QGL::TextureCoord0); + qDebug() << (flds == data.fields()); // prints "true" + + data.clear(); + qDebug() << data.count(); // prints "0" + QVector3D x = data.vertexAt(0); // asserts - no data in vertices + qDebug() << (flds == data.fields()); // still prints "true" + \endcode + + To clear a specific field and its data use \c{data.clear(field)} below. + + To clear all fields and data, simply set this to an empty geometry: + \code + data = QGeometryData(); + \endcode + */ +void QGeometryData::clear() +{ + if (d) + { + create(); + d->modified = true; + d->bb = QBox3D(); + d->boxValid = true; + const quint32 mask = 0x01; + quint32 fields = d->fields; + for (int field = 0; fields; ++field, fields >>= 1) + { + if (mask & fields) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + d->vertices.clear(); + else if (attr == QGL::Normal) + d->normals.clear(); + else + d->colors.clear(); + } + else if (attr < QGL::CustomVertex0) + { + d->textures[d->key[field]].clear(); + } + else + { + d->attributes[d->key[field]].clear(); + } + } + } + d->count = 0; + } +} + +/*! + Clears the data from \a field, and removes the field. After this call + hasField() will return false for this field. +*/ +void QGeometryData::clear(QGL::VertexAttribute field) +{ + if (d && (QGL::fieldMask(field) & d->fields)) + { + create(); + d->modified = true; + if (field == QGL::Position) + { + d->bb = QBox3D(); + d->boxValid = true; + } + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + d->vertices.clear(); + else if (attr == QGL::Normal) + d->normals.clear(); + else + d->colors.clear(); + } + else if (attr < QGL::CustomVertex0) + { + d->textures[d->key[field]].clear(); + } + else + { + d->attributes[d->key[field]].clear(); + } + d->key[field] = -1; + d->fields = d->fields & ~QGL::fieldMask(field); + } +} + +/*! + Sets the geometry data to handle an \a amount of data. This is generally + not required unless its anticipated that a large amount of data will be + appended and realloc overhead is desired to be avoided. If \a amount is + less than the amount already reserved, or if this object has + more than the \a amount of data items, then this function exits without + doing anything. This function will never delete data. +*/ +void QGeometryData::reserve(int amount) +{ + if (d && (d->reserved > amount || d->reserved < d->count)) + return; + create(); + d->reserved = amount; + const quint32 mask = 0x01; + quint32 fields = d->fields; + for (int field = 0; fields; ++field, fields >>= 1) + { + if (mask & fields) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + d->vertices.reserve(amount); + else if (attr == QGL::Normal) + d->normals.reserve(amount); + else + d->colors.reserve(amount); + } + else if (attr < QGL::CustomVertex0) + { + d->textures[d->key[field]].reserve(amount); + } + else + { + d->attributes[d->key[field]].reserve(amount); + } + } + } +} + +/*! + Draws this geometry on the \a painter, from \a start for \a count elements + in \a mode. The drawing \a mode is by default QGL::Triangles. This function + Also calls the upload() method to ensure that the geometry is resident on + the graphics hardware if appropriate. + + If the geometry is a point or line, then the \a drawWidth value specified the + width/size of the line/point. +*/ +void QGeometryData::draw(QGLPainter *painter, int start, int count, GLenum mode, qreal drawWidth) +{ + if (d && d->indices.size() && d->count) + { + upload(); + painter->clearAttributes(); + if (mode==QGL::Points) { +#if !defined(QT_OPENGL_ES_2) + ::glPointSize(drawWidth); +#endif + } else if (mode==QGL::LineStrip || mode == QGL::Lines) { + ::glLineWidth(drawWidth); + } + painter->setVertexBundle(d->vertexBundle); + if (count == 0) + count = d->indexBuffer.indexCount(); + painter->draw(QGL::DrawingMode(mode), d->indexBuffer, start, count); + } +} + +/*! + Uploads this geometry data to the graphics hardware if appropriate. If the + data is already uploaded and has not been modified since it was last + uploaded, then this function does nothing. + + If the bufferStrategy() does not specify QGL::BufferIfPossible then this + function does nothing. + + If the data was successfully uploaded, and the bufferStrategy() does not + specify QGL::KeepClientData then the data will be removed with a call to + the clear() function. + + If the data was successfully uploaded, on this call or previously, then this + function will return true. Otherwise it returns false. +*/ +bool QGeometryData::upload() +{ + bool vboUploaded = false; + bool iboUploaded = false; + + if (!d) + return false; + if (!d->modified) + return d->vertexBundle.isUploaded() && d->indexBuffer.isUploaded(); + + check(); + + // Need to recreate the buffers from the modified data. + d->vertexBundle = QGLVertexBundle(); + d->indexBuffer = QGLIndexBuffer(); + + // Copy the geometry data to the vertex buffer. + const quint32 mask = 0x01; + quint32 fields = d->fields; + for (int field = 0; fields; ++field, fields >>= 1) + { + if (!(mask & fields)) + continue; + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + if (attr == QGL::Position) + d->vertexBundle.addAttribute(attr, d->vertices); + else if (attr == QGL::Normal) + d->vertexBundle.addAttribute(attr, d->normals); + else if (attr == QGL::Color) + d->vertexBundle.addAttribute(attr, d->colors); + else if (attr < QGL::CustomVertex0) + d->vertexBundle.addAttribute(attr, d->textures.at(d->key[field])); + else + d->vertexBundle.addAttribute(attr, d->attributes.at(d->key[field])); + } + + // Upload the buffer if requested, otherwise keep it client-side. + // Note: QGLVertexBundle will act as a client-side buffer if not uploaded. + if ((d->bufferStrategy & BufferIfPossible) != 0) + { + if (d->vertexBundle.upload()) + vboUploaded = true; + } + + // Copy the geometry data to the index buffer and upload if requested. + d->indexBuffer.setIndexes(d->indices); + if ((d->bufferStrategy & BufferIfPossible) != 0) + { + if (d->indexBuffer.upload()) + iboUploaded = true; + } + + d->modified = false; + + if (!(d->bufferStrategy & KeepClientData) && vboUploaded && iboUploaded) + clear(); + + return vboUploaded && iboUploaded; +} + +/*! + Sets the buffer \a strategy for this geometry. + + \sa bufferStrategy() +*/ +void QGeometryData::setBufferStrategy(QGeometryData::BufferStrategy strategy) +{ + if (!d || d->bufferStrategy != strategy) + { + create(); + d->modified = true; + d->bufferStrategy = strategy; + } +} + +/*! + Returns the buffer strategy for this geometry. The default is + \c{QGL::BufferIfPossible | QGL::KeepClientData}. + + \sa setBufferStrategy() +*/ +QGeometryData::BufferStrategy QGeometryData::bufferStrategy() const +{ + if (d) + return d->bufferStrategy; + return InvalidStrategy; +} + +/*! + Returns a reference to the vertex buffer for this geometry. + + \sa indexBuffer() +*/ +QGLVertexBundle QGeometryData::vertexBundle() const +{ + return d->vertexBundle; +} + +/*! + Returns a reference to the index buffer for this geometry. + + \sa vertexBundle() +*/ +QGLIndexBuffer QGeometryData::indexBuffer() const +{ + return d->indexBuffer; +} + +/*! + Appends \a index to the vertex index array. + + \sa appendIndices(), indices() +*/ +void QGeometryData::appendIndex(int index) +{ + create(); + d->modified = true; + d->indices.append(index); +} + +/*! + Appends \a index1, \a index2, and \a index3 to the geometry's + index array. + + \sa appendIndex(), indices() +*/ +void QGeometryData::appendIndices(int index1, int index2, int index3) +{ + create(); + d->modified = true; + d->indices.append(index1, index2, index3); +} + +/*! + Returns the index array that was created by appendIndex(). + + \sa appendIndex(), appendIndices() +*/ +QGL::IndexArray QGeometryData::indices() const +{ + if (d) + return d->indices; + else + return QGL::IndexArray(); +} + +/*! + Appends the \a indices to the geometry's index array. +*/ +void QGeometryData::appendIndices(const QGL::IndexArray &indices) +{ + create(); + d->modified = true; + d->indices.append(indices); +} + +/*! + Append the point \a v0 to this geometry data as a position field. +*/ +void QGeometryData::appendVertex(const QVector3D &v0) +{ + create(); + d->modified = true; + enableField(QGL::Position); + d->vertices.append(v0); + if (d->boxValid) + d->bb.unite(v0); + d->count = qMax(d->count, d->vertices.count()); +} + +/*! + Append the points \a v0 and \a v1 to this geometry data as position fields. +*/ +void QGeometryData::appendVertex(const QVector3D &v0, const QVector3D &v1) +{ + create(); + d->modified = true; + enableField(QGL::Position); + d->vertices.append(v0, v1); + if (d->boxValid) + { + d->bb.unite(v0); + d->bb.unite(v1); + } + d->count = qMax(d->count, d->vertices.count()); +} + +/*! + Append the points \a v0, \a v1 and \a v2 to this geometry data as position fields. +*/ +void QGeometryData::appendVertex(const QVector3D &v0, const QVector3D &v1, const QVector3D &v2) +{ + create(); + d->modified = true; + enableField(QGL::Position); + d->vertices.append(v0, v1, v2); + if (d->boxValid) + { + d->bb.unite(v0); + d->bb.unite(v1); + d->bb.unite(v2); + } + d->count = qMax(d->count, d->vertices.count()); +} + +/*! + Append the points \a v0, \a v1, \a v2 and \a v3 to this geometry data as position fields. +*/ +void QGeometryData::appendVertex(const QVector3D &v0, const QVector3D &v1, const QVector3D &v2, const QVector3D &v3) +{ + create(); + d->modified = true; + enableField(QGL::Position); + d->vertices.append(v0, v1, v2, v3); + if (d->boxValid) + { + d->bb.unite(v0); + d->bb.unite(v1); + d->bb.unite(v2); + d->bb.unite(v3); + } + d->count = qMax(d->count, d->vertices.count()); +} + +/*! + Append the float \a a0 to this geometry data, as an attribute \a field. +*/ +void QGeometryData::appendAttribute(float a0, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + d->attributes[d->key[field]].append(a0); + d->count = qMax(d->count, d->attributes[d->key[field]].count()); +} + +/*! + Append the float \a a0 and \a a1 to this geometry data, as an attribute \a field. +*/ +void QGeometryData::appendAttribute(float a0, float a1, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + d->attributes[d->key[field]].append(a0, a1); + d->count = qMax(d->count, d->attributes[d->key[field]].count()); +} + +/*! + Append the floats \a a0, \a a1 and \a a2 to this geometry data, as attribute \a field. +*/ +void QGeometryData::appendAttribute(float a0, float a1, float a2, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + d->attributes[d->key[field]].append(a0, a1, a2); + d->count = qMax(d->count, d->attributes[d->key[field]].count()); +} + +/*! + Append the floats \a a0, \a a1, \a a2 and \a a3 to this geometry data, as attribute \a field. +*/ +void QGeometryData::appendAttribute(float a0, float a1, float a2, float a3, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + d->attributes[d->key[field]].append(a0, a1, a2, a3); + d->count = qMax(d->count, d->attributes[d->key[field]].count()); +} + +/*! + Append the 2D point \a a to this geometry data, as an attribute \a field. +*/ +void QGeometryData::appendAttribute(const QVector2D &a, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + if (d->attributes.at(d->key[field]).isEmpty()) + d->attributes[d->key[field]].setElementType(QCustomDataArray::Vector2D); + d->attributes[d->key[field]].append(a); + d->count = qMax(d->count, d->attributes[d->key[field]].count()); +} + +/*! + Append the 3D point \a v to this geometry data, as an attribute \a field. +*/ +void QGeometryData::appendAttribute(const QVector3D &v, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + if (d->attributes.at(d->key[field]).isEmpty()) + d->attributes[d->key[field]].setElementType(QCustomDataArray::Vector3D); + d->attributes[d->key[field]].append(v); + d->count = qMax(d->count, d->attributes[d->key[field]].count()); +} + +/*! + Append the variant value \a a to this geometry data, as an attribute \a field. +*/ +void QGeometryData::appendAttribute(const QVariant &a, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + if (d->attributes.at(d->key[field]).isEmpty()) + { + // floats and doubles get handled "automatically" - float is default + if (a.type() == QVariant::Vector2D) + d->attributes[d->key[field]].setElementType(QCustomDataArray::Vector2D); + else if (a.type() == QVariant::Vector3D) + d->attributes[d->key[field]].setElementType(QCustomDataArray::Vector3D); + else if (a.type() == QVariant::Vector4D) + d->attributes[d->key[field]].setElementType(QCustomDataArray::Vector4D); + else if (a.type() == QVariant::Color) + d->attributes[d->key[field]].setElementType(QCustomDataArray::Color); + else + Q_ASSERT_X(false, "QGeometryData::appendAttribute", "bad type"); + } + d->attributes[d->key[field]].append(a); + d->count = qMax(d->count, d->attributes[d->key[field]].count()); +} + +/*! + Append the vector \a n0 to this geometry data, as a lighting normal. +*/ +void QGeometryData::appendNormal(const QVector3D &n0) +{ + create(); + d->modified = true; + enableField(QGL::Normal); + d->normals.append(n0); + d->count = qMax(d->count, d->normals.count()); +} + +/*! + Append the vectors \a n0 and \a n1 to this geometry data, as lighting normals. +*/ +void QGeometryData::appendNormal(const QVector3D &n0, const QVector3D &n1) +{ + create(); + d->modified = true; + enableField(QGL::Normal); + d->normals.append(n0, n1); + d->count = qMax(d->count, d->normals.count()); +} + +/*! + Append the vectors \a n0, \a n1 and \a n2 to this geometry data, as lighting normals. +*/ +void QGeometryData::appendNormal(const QVector3D &n0, const QVector3D &n1, const QVector3D &n2) +{ + create(); + d->modified = true; + enableField(QGL::Normal); + d->normals.append(n0, n1, n2); + d->count = qMax(d->count, d->normals.count()); +} + +/*! + Append the vectors \a n0, \a n1, \a n2 and \a n3 to this geometry data, as lighting normals. +*/ +void QGeometryData::appendNormal(const QVector3D &n0, const QVector3D &n1, const QVector3D &n2, const QVector3D &n3) +{ + create(); + d->modified = true; + enableField(QGL::Normal); + d->normals.append(n0, n1, n2, n3); + d->count = qMax(d->count, d->normals.count()); +} + +/*! + Append the point \a t0 to this geometry data, as an texture \a field. +*/ +void QGeometryData::appendTexCoord(const QVector2D &t0, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + d->textures[d->key[field]].append(t0); + d->count = qMax(d->count, d->textures[d->key[field]].count()); +} + +/*! + Append the points \a t0 and \a t1 to this geometry data, as texture \a{field}s. +*/ +void QGeometryData::appendTexCoord(const QVector2D &t0, const QVector2D &t1, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + d->textures[d->key[field]].append(t0, t1); + d->count = qMax(d->count, d->textures[d->key[field]].count()); +} + +/*! + Append the points \a t0, \a t1 and \a t2 to this geometry data, as texture \a{field}s. +*/ +void QGeometryData::appendTexCoord(const QVector2D &t0, const QVector2D &t1, const QVector2D &t2, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + d->textures[d->key[field]].append(t0, t1, t2); + d->count = qMax(d->count, d->textures[d->key[field]].count()); +} + +/*! + Append the points \a t0, \a t1, \a t2 and \a t3 to this geometry data, as texture \a{field}s. +*/ +void QGeometryData::appendTexCoord(const QVector2D &t0, const QVector2D &t1, const QVector2D &t2, const QVector2D &t3, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + enableField(field); + d->textures[d->key[field]].append(t0, t1, t2, t3); + d->count = qMax(d->count, d->textures[d->key[field]].count()); +} + +/*! + Append the color \a c0 to this geometry data, as an color field. +*/ +void QGeometryData::appendColor(const QColor4ub &c0) +{ + create(); + d->modified = true; + enableField(QGL::Color); + d->colors.append(c0); + d->count = qMax(d->count, d->colors.count()); +} + +/*! + Append the color \a c0 and \a c1 to this geometry data, as color fields. +*/ +void QGeometryData::appendColor(const QColor4ub &c0, const QColor4ub &c1) +{ + create(); + d->modified = true; + enableField(QGL::Color); + d->colors.append(c0, c1); + d->count = qMax(d->count, d->colors.count()); +} + +/*! + Append the color \a c0, \a c1 and \a c2 to this geometry data, as color fields. +*/ +void QGeometryData::appendColor(const QColor4ub &c0, const QColor4ub &c1, const QColor4ub &c2) +{ + create(); + d->modified = true; + enableField(QGL::Color); + d->colors.append(c0, c1, c2); + d->count = qMax(d->count, d->colors.count()); +} + +/*! + Append the color \a c0, \a c1, \a c2 and \a c3 to this geometry data, as color fields. +*/ +void QGeometryData::appendColor(const QColor4ub &c0, const QColor4ub &c1, const QColor4ub &c2, const QColor4ub &c3) +{ + create(); + d->modified = true; + enableField(QGL::Color); + d->colors.append(c0, c1, c2, c3); + d->count = qMax(d->count, d->colors.count()); +} + +/*! + Append the points in \a ary to this geometry data as position fields. +*/ +void QGeometryData::appendVertexArray(const QVector3DArray &ary) +{ + if (ary.count()) + { + create(); + d->modified = true; + d->boxValid = false; + enableField(QGL::Position); + d->vertices.append(ary); + d->count = qMax(d->count, d->vertices.count()); + } +} + +/*! + Append the points in \a ary to this geometry data, as an attribute \a field entries. +*/ +void QGeometryData::appendAttributeArray(const QCustomDataArray &ary, QGL::VertexAttribute field) +{ + if (ary.count()) + { + create(); + d->modified = true; + enableField(field); + d->attributes[d->key[field]].append(ary); + d->count = qMax(d->count, d->attributes[d->key[field]].count()); + } +} + +/*! + Append the vectors in \a ary to this geometry data, as lighting normals. +*/ +void QGeometryData::appendNormalArray(const QVector3DArray &ary) +{ + if (ary.count()) + { + create(); + d->modified = true; + enableField(QGL::Normal); + d->normals.append(ary); + d->count = qMax(d->count, d->normals.count()); + } +} + +/*! + Append the 2D points in \a ary to this geometry data, as texture \a field entries. +*/ +void QGeometryData::appendTexCoordArray(const QVector2DArray &ary, QGL::VertexAttribute field) +{ + if (ary.count()) + { + create(); + d->modified = true; + enableField(field); + d->textures[d->key[field]].append(ary); + d->count = qMax(d->count, d->textures[d->key[field]].count()); + } +} + +/*! + Append the colors in \a ary to this geometry data, as color fields. +*/ +void QGeometryData::appendColorArray(const QArray<QColor4ub> &ary) +{ + if (ary.count()) + { + create(); + d->modified = true; + enableField(QGL::Color); + d->colors.append(ary); + d->count = qMax(d->count, d->colors.count()); + } +} + +/*! + Returns a modifiable reference to the vertex data at index \a i. +*/ +QVector3D &QGeometryData::vertex(int i) +{ + create(); + d->modified = true; + d->boxValid = false; + return d->vertices[i]; +} + +/*! + Returns a copy of the vertex position data. +*/ +QVector3DArray QGeometryData::vertices() const +{ + if (d) + return d->vertices; + return QArray<QVector3D>(); +} + +/*! + \internal + Returns a pointer to the vertex data. +*/ +const QVector3DArray *QGeometryData::vertexData() const +{ + if (d) + return &d->vertices; + return 0; +} + + +/*! + Returns a non-modifiable reference to the vertex position data at index \a i. +*/ +const QVector3D &QGeometryData::vertexAt(int i) const +{ + Q_ASSERT(hasField(QGL::Position)); + return d->vertices.at(i); +} + +/*! + Returns a modifiable reference to the normal data at index \a i. +*/ +QVector3D &QGeometryData::normal(int i) +{ + create(); + d->modified = true; + return d->normals[i]; +} + +/*! + Returns a non-modifiable reference to the normal data at index \a i. +*/ +const QVector3D &QGeometryData::normalAt(int i) const +{ + Q_ASSERT(hasField(QGL::Normal)); + return d->normals.at(i); +} + +/*! + Returns a copy of the lighting normal data. +*/ +QVector3DArray QGeometryData::normals() const +{ + if (d) + return d->normals; + return QArray<QVector3D>(); +} + +/*! + Returns a modifiable reference to the color data at index \a i. +*/ +QColor4ub &QGeometryData::color(int i) +{ + create(); + d->modified = true; + return d->colors[i]; +} + +/*! + Returns a non-modifiable reference to the color data at index \a i. +*/ +const QColor4ub &QGeometryData::colorAt(int i) const +{ + Q_ASSERT(hasField(QGL::Color)); + return d->colors.at(i); +} + +/*! + Returns a copy of the color data. +*/ +QArray<QColor4ub> QGeometryData::colors() const +{ + if (d) + return d->colors; + return QArray<QColor4ub>(); +} + +/*! + Returns a modifiable reference to the \a field texture coordinate data at index \a i. +*/ +QVector2D &QGeometryData::texCoord(int i, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + return d->textures[d->key[field]][i]; +} + +/*! + Returns a copy of the \a field texture coordinate data. +*/ +QVector2DArray QGeometryData::texCoords(QGL::VertexAttribute field) const +{ + return hasField(field) ? d->textures.at(d->key[field]) : QVector2DArray(); +} + +/*! + Returns a non-modifiable reference to the texture coordinate data at index \a i for \a field. +*/ +const QVector2D &QGeometryData::texCoordAt(int i, QGL::VertexAttribute field) const +{ + Q_ASSERT(hasField(field)); + return d->textures.at(d->key[field]).at(i); +} + +/*! + Returns a modifiable reference to the float \a field attribute data at index \a i. +*/ +float &QGeometryData::floatAttribute(int i, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + QCustomDataArray &ary = d->attributes[d->key[field]]; + Q_ASSERT(ary.elementType() == QCustomDataArray::Float); + return ary.m_array[i]; +} + +/*! + Returns a modifiable reference to the 2D vector \a field attribute data at index \a i. +*/ +QVector2D &QGeometryData::vector2DAttribute(int i, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + QCustomDataArray &ary = d->attributes[d->key[field]]; + Q_ASSERT(ary.elementType() == QCustomDataArray::Vector2D); + float *data = ary.m_array.data(); + QVector2D *v = reinterpret_cast<QVector2D*>(data + i*2); + return *v; +} + +/*! + Returns a modifiable reference to the 3D vector \a field attribute data at index \a i. +*/ +QVector3D &QGeometryData::vector3DAttribute(int i, QGL::VertexAttribute field) +{ + create(); + d->modified = true; + QCustomDataArray &ary = d->attributes[d->key[field]]; + Q_ASSERT(ary.elementType() == QCustomDataArray::Vector3D); + float *data = ary.m_array.data(); + QVector3D *v = reinterpret_cast<QVector3D*>(data + i*2); + return *v; +} + +/*! + Returns a copy of the \a field attribute data. +*/ +QCustomDataArray QGeometryData::attributes(QGL::VertexAttribute field) const +{ + return hasField(field) ? d->attributes.at(d->key[field]) : QCustomDataArray(); +} + +/*! + Returns a copy of the float \a field attribute data at index \a i. +*/ +float QGeometryData::floatAttributeAt(int i, QGL::VertexAttribute field) const +{ + Q_ASSERT(hasField(field)); + return d->attributes.at(d->key[field]).floatAt(i); +} + +/*! + Returns a copy of the 2D vector \a field attribute data at index \a i. +*/ +QVector2D QGeometryData::vector2DAttributeAt(int i, QGL::VertexAttribute field) const +{ + Q_ASSERT(hasField(field)); + return d->attributes.at(d->key[field]).vector2DAt(i); +} + +/*! + Returns a copy of the 3D vector \a field attribute data at index \a i. +*/ +QVector3D QGeometryData::vector3DAttributeAt(int i, QGL::VertexAttribute field) const +{ + Q_ASSERT(hasField(field)); + return d->attributes.at(d->key[field]).vector3DAt(i); +} + +/*! + Returns the attribute value for the \a field, suitable for passing + to QGLPainter. + + \sa QGLPainter::setVertexAttribute() +*/ +QGLAttributeValue QGeometryData::attributeValue(QGL::VertexAttribute field) const +{ + if (hasField(field)) + { + if (field < QGL::TextureCoord0) + { + if (field == QGL::Position) + return QGLAttributeValue(d->vertices); + else if (field == QGL::Normal) + return QGLAttributeValue(d->normals); + else if (field == QGL::Color) + return QGLAttributeValue(d->colors); + } + else + { + if (field < QGL::CustomVertex0) + return QGLAttributeValue(d->textures.at(d->key[field])); + else + return QGLAttributeValue(d->attributes.at(d->key[field])); + } + } + return QGLAttributeValue(); +} + +/*! + Returns true if this geometry has the field corresponding to \a attr. Note + that it is still possible for no data to have been added for that field. +*/ +bool QGeometryData::hasField(QGL::VertexAttribute attr) const +{ + if (d) + return d->key[attr] != -1; + return false; +} + +/*! + Enables this geometry to contain data of type \a field. Generally it is + not necessary to call this function since it is called by all the append + functions. +*/ +void QGeometryData::enableField(QGL::VertexAttribute field) +{ + if (d && d->key[field] != -1) + return; + create(); + d->modified = true; + Q_ASSERT(field < d->ATTR_CNT); // don't expand that enum too much + d->fields |= (1 << field); + switch (field) + { + case QGL::Position: + d->key[QGL::Position] = 0; + d->size[QGL::Position] = 3; + if (d->reserved > 0) + d->vertices.reserve(d->reserved); + break; + case QGL::Normal: + d->key[QGL::Normal] = 1; + d->size[QGL::Normal] = 3; + if (d->reserved > 0) + d->normals.reserve(d->reserved); + break; + case QGL::Color: + d->key[QGL::Color] = 2; + d->size[QGL::Color] = 1; + if (d->reserved > 0) + d->colors.reserve(d->reserved); + break; + case QGL::TextureCoord0: + case QGL::TextureCoord1: + case QGL::TextureCoord2: + d->textures.append(QVector2DArray()); + d->key[field] = d->textures.count() - 1; + d->size[field] = 2; + if (d->reserved > 0) + d->textures[d->key[field]].reserve(d->reserved); + break; + default: + // Custom and User vertex attributes. + d->attributes.append(QCustomDataArray()); + d->key[field] = d->attributes.count() - 1; + d->size[field] = d->attributes.at(d->key[field]).elementSize(); + if (d->reserved > 0) + d->attributes[d->key[field]].reserve(d->reserved); + break; + } +} + +/*! + Return a bit-mask of the supported fields in this geometry. The + QGL::VertexAttribute enum can be recovered from this bit-mask by + \code + quint32 fields = fields(); + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(fields); + \endcode +*/ +quint32 QGeometryData::fields() const +{ + if (d) + return d->fields; + return 0; +} + +/*! + Returns the count of logical vertices stored. This is effectively + the max() of QArray::count() over all of the enabled data types. +*/ +int QGeometryData::count() const +{ + if (d) + return d->count; + return 0; +} + +/*! + Returns the count of data stored in \a field. This will always be at + most count(), but could be less. +*/ +int QGeometryData::count(QGL::VertexAttribute field) const +{ + int result = 0; + if (d && (QGL::fieldMask(field) & d->fields)) + { + if (field < QGL::TextureCoord0) + { + if (field == QGL::Position) + result = d->vertices.count(); + else if (field == QGL::Normal) + result = d->normals.count(); + else + result = d->colors.count(); + } + else if (field < QGL::CustomVertex0) + { + result = d->textures[d->key[field]].count(); + } + else + { + result = d->attributes[d->key[field]].count(); + } + } + return result; +} + +/*! + Returns true if this geometry is identical to the \a other; and false otherwise. +*/ +bool QGeometryData::operator==(const QGeometryData &other) const +{ + bool isEqual = false; + if (d) + { + if (d == other.d) + { + isEqual = true; + } + else + { + if (other.d && d->fields == other.d->fields && d->count == other.d->count) + { + const quint32 mask = 0x01; + quint32 fields = d->fields; + isEqual = true; + for (int field = 0; fields && isEqual; ++field, fields >>= 1) + { + if (mask & fields) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + isEqual = (d->vertices == other.d->vertices); + else if (attr == QGL::Normal) + isEqual = (d->normals == other.d->normals); + else // colors + isEqual = (d->colors == other.d->colors); + } + else if (attr < QGL::CustomVertex0) + { + isEqual = (d->textures.at(d->key[attr]) == other.d->textures.at(d->key[attr])); + } + else + { + QArray<float> me = d->attributes.at(d->key[attr]).toFloatArray(); + QArray<float> him = other.d->attributes.at(d->key[attr]).toFloatArray(); + isEqual = (me == him); + } + } + } + } + } + } + else + { + isEqual = other.isNull(); + } + return isEqual; +} + +/*! + Returns true if this geometry is empty - that is it contains no vertices + or other data - and returns false otherwise. If an existing geometry has + been made empty by a call to clear() then this will be true (but isNull() + will be false). + + \sa isNull() +*/ +bool QGeometryData::isEmpty() const +{ + bool empty = true; + if (d) + empty = d->count == 0; + return empty; +} + +/*! + Returns true if this geometry is uninitialized - that is it contains no + internal data structures. A newly constructed QGeometryData object is + null until some data is added or changed. + + \sa isEmpty() +*/ +bool QGeometryData::isNull() const +{ + return d == NULL; +} + +/*! + Returns the number of index values stored in this geometry data. + + This value is exactly the same as indices().size() (but does not + incur the copy). +*/ +int QGeometryData::indexCount() const +{ + if (d) + return d->indices.size(); + return 0; +} + +void QGeometryData::create() +{ + if (!d) // lazy creation of data block + { + d = new QGeometryDataPrivate; + d->ref.ref(); + } +} + +/*! + Force this geometry to ensure it has its own unshared internal data + block, making a copy in the case that it is currently shared. +*/ +void QGeometryData::detach() +{ + create(); + if (d->ref > 1) // being shared, must detach + { + QGeometryDataPrivate *temp = d->clone(); + d->ref.deref(); + d = temp; + d->ref.ref(); + } +} + +/*! + \fn quint64 QGeometryData::id() const + Return an opaque value that can be used to identify which data block is + being used by this QGeometryData instance. See the class documentation + relating to explicit sharing. +*/ + +#ifndef QT_NO_DEBUG +void QGeometryData::check() const +{ + if (!d) + return; + const quint32 mask = 0x01; + quint32 fields = d->fields; + for (int field = 0; fields; ++field, fields >>= 1) + { + if (mask & fields) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + { + if (d->vertices.count() < d->count) + qWarning("QGeometryData - expected %d vertices, only %d found!", + d->count, d->vertices.count()); + } + else if (attr == QGL::Normal) + { + if (d->normals.count() < d->count) + qWarning("QGeometryData - expected %d normals, only %d found!", + d->count, d->normals.count()); + } + else + { + if (d->colors.count() < d->count) + qWarning("QGeometryData - expected %d colors, only %d found!", + d->count, d->colors.count()); + } + } + else if (attr < QGL::CustomVertex0) + { + if (d->textures.at(d->key[field]).count() < d->count) + qWarning("QGeometryData - expected %d texture coordinates for" + "QGL::TextureCoord%d, only %d found!", + d->count, field - QGL::TextureCoord0, d->textures.at(d->key[field]).count()); + } + else + { + if (d->attributes.at(d->key[field]).count() < d->count) + qWarning("QGeometryData - expected %d attributes for" + "QGL::CustomVertex%d, only %d found!", + d->count, field - QGL::CustomVertex0, d->attributes.at(d->key[field]).count()); + } + } + } +} +#endif + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QGeometryData &vertices) +{ + dbg << "QGeometryData" << &vertices << " size:" << vertices.count() +#ifndef QT_NO_DEBUG + << "data block id:" << vertices.id() +#endif + ; + quint32 fields = vertices.fields(); + const quint32 mask = 0x01; + for (int field = 0; fields; ++field, fields >>= 1) + { + if (mask & fields) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + { + dbg << " vertices:" << vertices.count(attr); + dbg << vertices.vertices(); + } + else if (attr == QGL::Normal) + { + dbg << " normals:" << vertices.count(attr); + dbg << vertices.normals(); + } + else + { + dbg << " colors:" << vertices.count(attr); + dbg << vertices.colors(); + } + } + else if (attr < QGL::CustomVertex0) + { + dbg << " textures:" << (attr - QGL::TextureCoord0) << vertices.count(attr); + dbg << vertices.texCoords(attr); + } + else + { + dbg << " custom:" << (attr - QGL::CustomVertex0) << vertices.count(attr); + dbg << vertices.texCoords(attr); + } + } + } + if (vertices.indexCount() > 0) + { + dbg << " indices:" << vertices.indices(); + } + return dbg; +} +#endif diff --git a/src/threed/geometry/qgeometrydata.h b/src/threed/geometry/qgeometrydata.h new file mode 100644 index 000000000..051915e32 --- /dev/null +++ b/src/threed/geometry/qgeometrydata.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMETRYDATA_H +#define QGEOMETRYDATA_H + +#include "qcolor4ub.h" +#include "qglnamespace.h" +#include "qglindexbuffer.h" +#include "qglvertexbundle.h" +#include "qglattributevalue.h" +#include "qcustomdataarray.h" +#include "qbox3d.h" +#include "qarray.h" +#include "qvector2darray.h" +#include "qvector3darray.h" + +QT_BEGIN_NAMESPACE + +class QGeometryDataPrivate; +class QLogicalVertex; +class QGLPainter; + +namespace QGL +{ + inline quint32 fieldMask(QGL::VertexAttribute f) { return (quint32)0x01 << f; } + +#if defined(QT_OPENGL_ES) + typedef QArray<ushort> IndexArray; +#else + typedef QArray<uint> IndexArray; +#endif +}; + +class Q_QT3D_EXPORT QGeometryData +{ +public: + QGeometryData(); + QGeometryData(const QGeometryData &); + QGeometryData(quint32 fields); + ~QGeometryData(); + + QGeometryData &operator=(const QGeometryData &); + + void appendGeometry(const QGeometryData &data); + int appendVertex(const QLogicalVertex &v); + void normalizeNormals(); + QBox3D boundingBox() const; + QVector3D center() const; + + QGeometryData reversed() const; + QGeometryData translated(const QVector3D &) const; + void generateTextureCoordinates(Qt::Orientation orientation = Qt::Horizontal, + QGL::VertexAttribute attribute = QGL::TextureCoord0); + QGeometryData interleavedWith(const QGeometryData &other) const; + void interleaveWith(const QGeometryData &other); + void clear(); + void clear(QGL::VertexAttribute); + void reserve(int amount); + void draw(QGLPainter *painter, int start, int count, GLenum mode = QGL::Triangles, qreal drawWidth=1.0); + bool upload(); + enum BufferStrategyFlags + { + InvalidStrategy = 0x00, + KeepClientData = 0x01, + BufferIfPossible = 0x02, + }; + Q_DECLARE_FLAGS(BufferStrategy, BufferStrategyFlags) + void setBufferStrategy(BufferStrategy strategy); + BufferStrategy bufferStrategy() const; + QGLVertexBundle vertexBundle() const; + QGLIndexBuffer indexBuffer() const; + + void appendIndex(int index); + void appendIndices(int index1, int index2, int index3); + void appendIndices(const QGL::IndexArray &indices); + QGL::IndexArray indices() const; + + void appendVertex(const QVector3D &v0); + void appendVertex(const QVector3D &v0, const QVector3D &v1); + void appendVertex(const QVector3D &v0, const QVector3D &v1, const QVector3D &v2); + void appendVertex(const QVector3D &v0, const QVector3D &v1, const QVector3D &v2, const QVector3D &v3); + + void appendAttribute(float a, QGL::VertexAttribute field = QGL::CustomVertex0); + void appendAttribute(float a, float b, QGL::VertexAttribute field = QGL::CustomVertex0); + void appendAttribute(float a, float b, float c, QGL::VertexAttribute field = QGL::CustomVertex0); + void appendAttribute(float a, float b, float c, float d, QGL::VertexAttribute field = QGL::CustomVertex0); + void appendAttribute(const QVector2D &a, QGL::VertexAttribute field = QGL::CustomVertex0); + void appendAttribute(const QVector3D &a, QGL::VertexAttribute field = QGL::CustomVertex0); + void appendAttribute(const QVariant &a, QGL::VertexAttribute field = QGL::CustomVertex0); + + void appendNormal(const QVector3D &n0); + void appendNormal(const QVector3D &n0, const QVector3D &n1); + void appendNormal(const QVector3D &n0, const QVector3D &n1, const QVector3D &n2); + void appendNormal(const QVector3D &n0, const QVector3D &n1, const QVector3D &n2, const QVector3D &n3); + + void appendTexCoord(const QVector2D &t0, QGL::VertexAttribute field = QGL::TextureCoord0); + void appendTexCoord(const QVector2D &t0, const QVector2D &t1, QGL::VertexAttribute field = QGL::TextureCoord0); + void appendTexCoord(const QVector2D &t0, const QVector2D &t1, const QVector2D &t2, QGL::VertexAttribute field = QGL::TextureCoord0); + void appendTexCoord(const QVector2D &t0, const QVector2D &t1, const QVector2D &t2, const QVector2D &t3, QGL::VertexAttribute field = QGL::TextureCoord0); + + void appendColor(const QColor4ub &c0); + void appendColor(const QColor4ub &c0, const QColor4ub &c1); + void appendColor(const QColor4ub &c0, const QColor4ub &c1, const QColor4ub &c2); + void appendColor(const QColor4ub &c0, const QColor4ub &c1, const QColor4ub &c2, const QColor4ub &c3); + + void appendVertexArray(const QVector3DArray &ary); + void appendAttributeArray(const QCustomDataArray &ary, QGL::VertexAttribute field = QGL::CustomVertex0); + void appendNormalArray(const QVector3DArray &ary); + void appendTexCoordArray(const QVector2DArray &ary, QGL::VertexAttribute field = QGL::TextureCoord0); + void appendColorArray(const QArray<QColor4ub> &ary); + + QLogicalVertex logicalVertexAt(int i) const; + + QVector3DArray vertices() const; + QVector3D &vertex(int i); + const QVector3D &vertexAt(int i) const; + + QVector3DArray normals() const; + QVector3D &normal(int i); + const QVector3D &normalAt(int i) const; + + QArray<QColor4ub> colors() const; + QColor4ub &color(int i); + const QColor4ub &colorAt(int i) const; + + QVector2DArray texCoords(QGL::VertexAttribute field = QGL::TextureCoord0) const; + QVector2D &texCoord(int i, QGL::VertexAttribute field = QGL::TextureCoord0); + const QVector2D &texCoordAt(int i, QGL::VertexAttribute field = QGL::TextureCoord0) const; + + float &floatAttribute(int i, QGL::VertexAttribute field = QGL::CustomVertex0); + QVector2D &vector2DAttribute(int i, QGL::VertexAttribute field = QGL::CustomVertex0); + QVector3D &vector3DAttribute(int i, QGL::VertexAttribute field = QGL::CustomVertex0); + QCustomDataArray attributes(QGL::VertexAttribute field = QGL::CustomVertex0) const; + float floatAttributeAt(int i, QGL::VertexAttribute field = QGL::CustomVertex0) const; + QVector2D vector2DAttributeAt(int i, QGL::VertexAttribute field = QGL::CustomVertex0) const; + QVector3D vector3DAttributeAt(int i, QGL::VertexAttribute field = QGL::CustomVertex0) const; + + QGLAttributeValue attributeValue(QGL::VertexAttribute field) const; + bool hasField(QGL::VertexAttribute field) const; + void enableField(QGL::VertexAttribute field); + quint32 fields() const; + int count() const; + int count(QGL::VertexAttribute field) const; + int indexCount() const; + bool operator==(const QGeometryData &other) const; + bool isEmpty() const; + bool isNull() const; + void detach(); +#ifndef QT_NO_DEBUG + quint64 id() const { return (quint64)d; } +#endif +protected: + const QVector3DArray *vertexData() const; +private: + void create(); +#ifndef QT_NO_DEBUG + void check() const; +#else + void check() const {} +#endif + friend class QLogicalVertex; + + QGeometryDataPrivate *d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeometryData::BufferStrategy); + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QGeometryData &vertices); +#endif + +QT_END_NAMESPACE + +#endif // QGEOMETRYDATA_H diff --git a/src/threed/geometry/qglbezierpatches.cpp b/src/threed/geometry/qglbezierpatches.cpp new file mode 100644 index 000000000..49842c653 --- /dev/null +++ b/src/threed/geometry/qglbezierpatches.cpp @@ -0,0 +1,815 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglbezierpatches.h" +#include "qglbuilder.h" +#include "qray3d.h" +#include "qtriangle3d.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLBezierPatches + \brief The QGLBezierPatches class represents 3D geometry as a set of Bezier bicubic patches. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::geometry + + Bezier bicubic patches represent a curved 3D surface by four fixed + control points at indices 0, 3, 12, and 15, together with twelve + additional floating control points that define the surface + curvature. Bezier geometry objects are made up of one or more + such patches to define the surface of an object. + + The application specifies the vertex position data to the + constructor, and can optionally provide an index array. + The class interprets groups of 16 vertices as the control + points for successive patches. + + A mesh defined by QGLBezierPatches is subdivided into flat + triangles for rendering when the \c{<<} operator is used + to add the patches to a QGLBuilder. + + Many curved 3D objects can be defined as being made up of Bezier + bicubic patches, stitched together into a mesh. The most famous + Bezier bicubic object is probably the classic 3D "Utah Teapot", + first rendered in 1975. The QGLTeapot class provides a built-in + implementation of this object for testing purposes. + + If texture co-ordinates are supplied via setTextureCoords(), + then patch texture co-ordinates will be derived from the + specified values as the patches are subdivided. Otherwise, + QGLBezierPatches will generate texture co-ordinates for each + patch based on the default square from (0, 0) to (1, 1). + The first vertex in the patch corresponds to (0, 0), + and the opposite vertex in the patch corresponds to (1, 1). + + \sa QGLBuilder, QGLTeapot +*/ + +class QGLBezierPatchesPrivate +{ +public: + QGLBezierPatchesPrivate() + : subdivisionDepth(4) {} + QGLBezierPatchesPrivate(const QGLBezierPatchesPrivate *other) + : positions(other->positions) + , textureCoords(other->textureCoords) + , subdivisionDepth(other->subdivisionDepth) {} + + void copy(const QGLBezierPatchesPrivate *other) + { + positions = other->positions; + textureCoords = other->textureCoords; + subdivisionDepth = other->subdivisionDepth; + } + + void subdivide(QGLBuilder *list) const; + qreal intersection + (const QRay3D &ray, bool anyIntersection, QVector2D *texCoord, int *patch) const; + + QVector3DArray positions; + QVector2DArray textureCoords; + int subdivisionDepth; +}; + +// Temporary patch data for performing sub-divisions. +class QGLBezierPatch +{ +public: + // Control points for this mesh. + QVector3D points[16]; + + // Triangle mesh indices of the control points at each corner. + int indices[4]; + + QVector3D normal(qreal s, qreal t) const; + void convertToTriangles + (QGeometryData *prim, + qreal xtex, qreal ytex, qreal wtex, qreal htex); + void subDivide(QGLBezierPatch &patch1, QGLBezierPatch &patch2, + QGLBezierPatch &patch3, QGLBezierPatch &patch4); + void createNewCorners(QGLBezierPatch &patch1, QGLBezierPatch &patch2, + QGLBezierPatch &patch3, QGLBezierPatch &patch4, + QGeometryData *prim, + qreal xtex, qreal ytex, qreal wtex, qreal htex); + void recursiveSubDivide + (QGeometryData *prim, + int depth, qreal xtex, qreal ytex, qreal wtex, qreal htex); + qreal intersection + (qreal result, int depth, const QRay3D &ray, bool anyIntersection, + qreal xtex, qreal ytex, qreal wtex, qreal htex, QVector2D *tc); +}; + +static int const cornerOffsets[] = {0, 3, 12, 15}; +static qreal const cornerS[] = {0.0f, 1.0f, 0.0f, 1.0f}; +static qreal const cornerT[] = {0.0f, 0.0f, 1.0f, 1.0f}; + +// Helper functions for calculating the components of the Bernstein +// polynomial and its derivative that make up the surface. +static inline qreal b0(qreal v) +{ + return (1.0f - v) * (1.0f - v) * (1.0f - v); +} +static inline qreal b1(qreal v) +{ + return 3.0f * v * (1.0f - v) * (1.0f - v); +} +static inline qreal b2(qreal v) +{ + return 2.0f * v * v * (1.0f - v); +} +static inline qreal b3(qreal v) +{ + return v * v * v; +} +static inline qreal db0(qreal v) +{ + return -3.0f * (1.0f - v) * (1.0f - v); +} +static inline qreal db1(qreal v) +{ + return -6.0f * v * (1.0f - v) + 3.0f * (1.0f - v) * (1.0f - v); +} +static inline qreal db2(qreal v) +{ + return -3.0f * v * v + 6.0f * v * (1.0f - v); +} +static inline qreal db3(qreal v) +{ + return 3.0f * v * v; +} + +// Compute the normal at a specific point in the patch. +// The s and t values vary between 0 and 1. +QVector3D QGLBezierPatch::normal(qreal s, qreal t) const +{ + qreal a[4]; + qreal b[4]; + qreal tx, ty, tz; + qreal sx, sy, sz; + + // Compute the derivative of the surface in t. + a[0] = b0(s); + a[1] = b1(s); + a[2] = b2(s); + a[3] = b3(s); + b[0] = db0(t); + b[1] = db1(t); + b[2] = db2(t); + b[3] = db3(t); + tx = 0.0f; + ty = 0.0f; + tz = 0.0f; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + tx += a[i] * points[j * 4 + i].x() * b[j]; + ty += a[i] * points[j * 4 + i].y() * b[j]; + tz += a[i] * points[j * 4 + i].z() * b[j]; + } + } + + // Compute the derivative of the surface in s. + a[0] = db0(s); + a[1] = db1(s); + a[2] = db2(s); + a[3] = db3(s); + b[0] = b0(t); + b[1] = b1(t); + b[2] = b2(t); + b[3] = b3(t); + sx = 0.0f; + sy = 0.0f; + sz = 0.0f; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + sx += a[i] * points[j * 4 + i].x() * b[j]; + sy += a[i] * points[j * 4 + i].y() * b[j]; + sz += a[i] * points[j * 4 + i].z() * b[j]; + } + } + + // The normal is the cross-product of the two derivatives, + // normalized to a unit vector. + QVector3D n = QVector3D::normal(QVector3D(sx, sy, sz), QVector3D(tx, ty, tz)); + if (n.isNull()) { + // A zero normal may occur if one of the patch edges is zero-length. + // We correct for this by substituting an overall patch normal that + // we compute from two of the sides that are not zero in length. + QVector3D sides[4]; + QVector3D vectors[2]; + sides[0] = points[3] - points[0]; + sides[1] = points[15] - points[3]; + sides[2] = points[12] - points[15]; + sides[3] = points[0] - points[12]; + int i = 0; + int j = 0; + vectors[0] = QVector3D(1.0f, 0.0f, 0.0f); + vectors[1] = QVector3D(0.0f, 1.0f, 0.0f); + while (i < 2 && j < 4) { + if (sides[j].isNull()) + ++j; + else + vectors[i++] = sides[j++]; + } + n = QVector3D::normal(vectors[0], vectors[1]); + } + return n; +} + +// Convert this patch into flat triangles. +void QGLBezierPatch::convertToTriangles + (QGeometryData *prim, + qreal xtex, qreal ytex, qreal wtex, qreal htex) +{ + // The edges are considered ok if they have a non-zero length. + // Zero-length edges can occur in triangular-shaped patches. + // There is no point generating triangles along such edges. + bool edge1ok = (points[0] != points[3]); + bool edge2ok = (points[0] != points[12]); + bool edge3ok = (points[12] != points[15]); + bool edge4ok = (points[15] != points[3]); + + // Find the mid-point on the patch by averaging the corners. + QVector3D mid = (points[0] + points[3] + points[12] + points[15]) / 4.0f; + + // Allocate a triangle mesh vertex for the mid-point. + int midIndex = prim->count(); + prim->appendVertex(mid); + prim->appendNormal(normal(0.5f, 0.5f)); + prim->appendTexCoord + (QVector2D(xtex + wtex / 2.0f, ytex + htex / 2.0f)); + + // Divide the patch into 4 triangles pointing at the center. + if (edge1ok) + prim->appendIndices(indices[0], indices[1], midIndex); + if (edge2ok) + prim->appendIndices(indices[2], indices[0], midIndex); + if (edge3ok) + prim->appendIndices(indices[3], indices[2], midIndex); + if (edge4ok) + prim->appendIndices(indices[1], indices[3], midIndex); +} + +// Sub-divide a Bezier curve (p1, p2, p3, p4) into two new +// Bezier curves (l1, l2, l3, l4) and (r1, r2, r3, r4). +static void subDivideBezierCurve + (const QVector3D &p1, const QVector3D &p2, + const QVector3D &p3, const QVector3D &p4, + QVector3D &l1, QVector3D &l2, QVector3D &l3, QVector3D &l4, + QVector3D &r1, QVector3D &r2, QVector3D &r3, QVector3D &r4) +{ + l1 = p1; + l2 = (p1 + p2) / 2.0f; + QVector3D h = (p2 + p3) / 2.0f; + l3 = (l2 + h) / 2.0f; + r3 = (p3 + p4) / 2.0f; + r2 = (h + r3) / 2.0f; + l4 = r1 = (l3 + r2) / 2.0f; + r4 = p4; +} + +// Sub-divide this patch into four new patches. The triangle mesh +// is used to allocate vertices for the corners of the new patches. +void QGLBezierPatch::subDivide + (QGLBezierPatch &patch1, QGLBezierPatch &patch2, + QGLBezierPatch &patch3, QGLBezierPatch &patch4) +{ + // Sub-divide the Bezier curves for the control rows to create + // four rows of 8 control points. These define the left and + // right halves of the patch. + QVector3D row1[8]; + QVector3D row2[8]; + QVector3D row3[8]; + QVector3D row4[8]; + subDivideBezierCurve + (points[0], points[1], points[2], points[3], + row1[0], row1[1], row1[2], row1[3], row1[4], row1[5], row1[6], row1[7]); + subDivideBezierCurve + (points[4], points[5], points[6], points[7], + row2[0], row2[1], row2[2], row2[3], row2[4], row2[5], row2[6], row2[7]); + subDivideBezierCurve + (points[8], points[9], points[10], points[11], + row3[0], row3[1], row3[2], row3[3], row3[4], row3[5], row3[6], row3[7]); + subDivideBezierCurve + (points[12], points[13], points[14], points[15], + row4[0], row4[1], row4[2], row4[3], row4[4], row4[5], row4[6], row4[7]); + + // Now sub-divide the 8 columns to create the four new patches. + subDivideBezierCurve + (row1[0], row2[0], row3[0], row4[0], + patch1.points[0], patch1.points[4], patch1.points[8], patch1.points[12], + patch3.points[0], patch3.points[4], patch3.points[8], patch3.points[12]); + subDivideBezierCurve + (row1[1], row2[1], row3[1], row4[1], + patch1.points[1], patch1.points[5], patch1.points[9], patch1.points[13], + patch3.points[1], patch3.points[5], patch3.points[9], patch3.points[13]); + subDivideBezierCurve + (row1[2], row2[2], row3[2], row4[2], + patch1.points[2], patch1.points[6], patch1.points[10], patch1.points[14], + patch3.points[2], patch3.points[6], patch3.points[10], patch3.points[14]); + subDivideBezierCurve + (row1[3], row2[3], row3[3], row4[3], + patch1.points[3], patch1.points[7], patch1.points[11], patch1.points[15], + patch3.points[3], patch3.points[7], patch3.points[11], patch3.points[15]); + subDivideBezierCurve + (row1[4], row2[4], row3[4], row4[4], + patch2.points[0], patch2.points[4], patch2.points[8], patch2.points[12], + patch4.points[0], patch4.points[4], patch4.points[8], patch4.points[12]); + subDivideBezierCurve + (row1[5], row2[5], row3[5], row4[5], + patch2.points[1], patch2.points[5], patch2.points[9], patch2.points[13], + patch4.points[1], patch4.points[5], patch4.points[9], patch4.points[13]); + subDivideBezierCurve + (row1[6], row2[6], row3[6], row4[6], + patch2.points[2], patch2.points[6], patch2.points[10], patch2.points[14], + patch4.points[2], patch4.points[6], patch4.points[10], patch4.points[14]); + subDivideBezierCurve + (row1[7], row2[7], row3[7], row4[7], + patch2.points[3], patch2.points[7], patch2.points[11], patch2.points[15], + patch4.points[3], patch4.points[7], patch4.points[11], patch4.points[15]); +} + +void QGLBezierPatch::createNewCorners + (QGLBezierPatch &patch1, QGLBezierPatch &patch2, + QGLBezierPatch &patch3, QGLBezierPatch &patch4, + QGeometryData *prim, + qreal xtex, qreal ytex, qreal wtex, qreal htex) +{ + // Add vertices for the new patch corners we have created. + qreal hwtex = wtex / 2.0f; + qreal hhtex = htex / 2.0f; + int topPointIndex = prim->count(); + int leftPointIndex = topPointIndex + 1; + int midPointIndex = topPointIndex + 2; + int rightPointIndex = topPointIndex + 3; + int bottomPointIndex = topPointIndex + 4; + + prim->appendVertex(patch1.points[3]); + prim->appendNormal(normal(0.5f, 0.0f)); + prim->appendTexCoord(QVector2D(xtex + hwtex, ytex)); + + prim->appendVertex(patch1.points[12]); + prim->appendNormal(normal(0.0f, 0.5f)); + prim->appendTexCoord(QVector2D(xtex, ytex + hhtex)); + + prim->appendVertex(patch1.points[15]); + prim->appendNormal(normal(0.5f, 0.5f)); + prim->appendTexCoord(QVector2D(xtex + hwtex, ytex + hhtex)); + + prim->appendVertex(patch2.points[15]); + prim->appendNormal(normal(1.0f, 0.5f)); + prim->appendTexCoord(QVector2D(xtex + wtex, ytex + hhtex)); + + prim->appendVertex(patch3.points[15]); + prim->appendNormal(normal(0.5f, 1.0f)); + prim->appendTexCoord(QVector2D(xtex + hwtex, ytex + htex)); + + // Copy the indices for the corners of the new patches. + patch1.indices[0] = indices[0]; + patch1.indices[1] = topPointIndex; + patch1.indices[2] = leftPointIndex; + patch1.indices[3] = midPointIndex; + patch2.indices[0] = topPointIndex; + patch2.indices[1] = indices[1]; + patch2.indices[2] = midPointIndex; + patch2.indices[3] = rightPointIndex; + patch3.indices[0] = leftPointIndex; + patch3.indices[1] = midPointIndex; + patch3.indices[2] = indices[2]; + patch3.indices[3] = bottomPointIndex; + patch4.indices[0] = midPointIndex; + patch4.indices[1] = rightPointIndex; + patch4.indices[2] = bottomPointIndex; + patch4.indices[3] = indices[3]; +} + +// Recursively sub-divide a patch into triangles. +void QGLBezierPatch::recursiveSubDivide + (QGeometryData *prim, + int depth, qreal xtex, qreal ytex, qreal wtex, qreal htex) +{ + if (depth <= 1) { + convertToTriangles(prim, xtex, ytex, wtex, htex); + } else { + QGLBezierPatch patch1, patch2, patch3, patch4; + subDivide(patch1, patch2, patch3, patch4); + createNewCorners(patch1, patch2, patch3, patch4, prim, xtex, ytex, wtex, htex); + --depth; + qreal hwtex = wtex / 2.0f; + qreal hhtex = htex / 2.0f; + patch1.recursiveSubDivide(prim, depth, xtex, ytex, hwtex, hhtex); + patch2.recursiveSubDivide(prim, depth, xtex + hwtex, ytex, hwtex, hhtex); + patch3.recursiveSubDivide(prim, depth, xtex, ytex + hhtex, hwtex, hhtex); + patch4.recursiveSubDivide(prim, depth, xtex + hwtex, ytex + hhtex, hwtex, hhtex); + } +} + +void QGLBezierPatchesPrivate::subdivide(QGLBuilder *list) const +{ + QGeometryData prim; + int count = positions.size(); + for (int posn = 0; (posn + 15) < count; posn += 16) { + // Construct a QGLBezierPatch object from the next high-level patch. + QGLBezierPatch patch; + int vertex; + for (int vertex = 0; vertex < 16; ++vertex) + patch.points[vertex] = positions[posn + vertex]; + QVector2D tex1, tex2; + if (!textureCoords.isEmpty()) { + tex1 = textureCoords[(posn / 16) * 2]; + tex2 = textureCoords[(posn / 16) * 2 + 1]; + } else { + tex1 = QVector2D(0.0f, 0.0f); + tex2 = QVector2D(1.0f, 1.0f); + } + qreal xtex = tex1.x(); + qreal ytex = tex1.y(); + qreal wtex = tex2.x() - xtex; + qreal htex = tex2.y() - ytex; + for (int corner = 0; corner < 4; ++corner) { + vertex = posn + cornerOffsets[corner]; + QVector3D n = patch.normal(cornerS[corner], cornerT[corner]); + patch.indices[corner] = prim.count(); + prim.appendVertex(patch.points[cornerOffsets[corner]]); + prim.appendNormal(n); + prim.appendTexCoord + (QVector2D(xtex + wtex * cornerS[corner], + ytex + htex * cornerT[corner])); + } + + // Subdivide the patch and generate the final triangles. + patch.recursiveSubDivide(&prim, subdivisionDepth, + xtex, ytex, wtex, htex); + } + list->addTriangles(prim); +} + +static inline qreal combineResults(qreal result, qreal t) +{ + if (qIsNaN(result)) + return t; + if (t >= 0.0f) + return result < 0.0f ? t : qMin(result, t); + else + return result >= 0.0f ? result : qMax(result, t); +} + +qreal QGLBezierPatch::intersection + (qreal result, int depth, const QRay3D& ray, bool anyIntersection, + qreal xtex, qreal ytex, qreal wtex, qreal htex, QVector2D *tc) +{ + // Check the convex hull of the patch for an intersection. + // If no intersection with the convex hull, then there is + // no point subdividing this patch further. + QBox3D box; + for (int point = 0; point < 16; ++point) + box.unite(points[point]); + if (!box.intersects(ray)) + return result; + + // Are we at the lowest point of subdivision yet? + if (depth <= 1) { + // Divide the patch into two triangles and intersect with those. + QTriangle3D triangle1(points[0], points[3], points[12]); + qreal t = triangle1.intersection(ray); + if (!qIsNaN(t)) { + result = combineResults(result, t); + if (result == t) { + QVector2D uv = triangle1.uv(ray.point(t)); + QVector2D tp(xtex, ytex); + QVector2D tq(xtex + wtex, ytex); + QVector2D tr(xtex, ytex + htex); + *tc = uv.x() * tp + uv.y() * tq + (1 - uv.x() - uv.y()) * tr; + } + } else { + QTriangle3D triangle2(points[3], points[15], points[12]); + qreal t = triangle2.intersection(ray); + if (!qIsNaN(t)) { + result = combineResults(result, t); + if (result == t) { + QVector2D uv = triangle2.uv(ray.point(t)); + QVector2D tp(xtex + wtex, ytex); + QVector2D tq(xtex + wtex, ytex + htex); + QVector2D tr(xtex, ytex + htex); + *tc = uv.x() * tp + uv.y() * tq + (1 - uv.x() - uv.y()) * tr; + } + } + } + } else { + // Subdivide the patch to find the point of intersection. + QGLBezierPatch patch1, patch2, patch3, patch4; + subDivide(patch1, patch2, patch3, patch4); + --depth; + qreal hwtex = wtex / 2.0f; + qreal hhtex = htex / 2.0f; + result = patch1.intersection + (result, depth, ray, anyIntersection, + xtex, ytex, hwtex, hhtex, tc); + if (anyIntersection && !qIsNaN(result)) + return result; + result = patch2.intersection + (result, depth, ray, anyIntersection, + xtex + hwtex, ytex, hwtex, hhtex, tc); + if (anyIntersection && !qIsNaN(result)) + return result; + result = patch3.intersection + (result, depth, ray, anyIntersection, + xtex, ytex + hhtex, hwtex, hhtex, tc); + if (anyIntersection && !qIsNaN(result)) + return result; + result = patch4.intersection + (result, depth, ray, anyIntersection, + xtex + hwtex, ytex + hhtex, hwtex, hhtex, tc); + } + return result; +} + +qreal QGLBezierPatchesPrivate::intersection + (const QRay3D &ray, bool anyIntersection, QVector2D *texCoord, int *bestPatch) const +{ + int count = positions.size(); + qreal result = qSNaN(); + QVector2D tc; + if (bestPatch) + *bestPatch = -1; + for (int posn = 0; (posn + 15) < count; posn += 16) { + QGLBezierPatch patch; + for (int vertex = 0; vertex < 16; ++vertex) + patch.points[vertex] = positions[posn + vertex]; + QVector2D tex1, tex2; + if (!textureCoords.isEmpty()) { + tex1 = textureCoords[(posn / 16) * 2]; + tex2 = textureCoords[(posn / 16) * 2 + 1]; + } else { + tex1 = QVector2D(0.0f, 0.0f); + tex2 = QVector2D(1.0f, 1.0f); + } + qreal xtex = tex1.x(); + qreal ytex = tex1.y(); + qreal wtex = tex2.x() - xtex; + qreal htex = tex2.y() - ytex; + qreal prev = result; + result = patch.intersection + (result, subdivisionDepth, ray, anyIntersection, + xtex, ytex, wtex, htex, &tc); + if (bestPatch && result != prev) + *bestPatch = posn / 16; + if (anyIntersection && !qIsNaN(result)) + break; + } + if (texCoord && !qIsNaN(result)) + *texCoord = tc; + return result; +} + +/*! + Constructs an empty Bezier patch list. + + \sa setPositions() +*/ +QGLBezierPatches::QGLBezierPatches() + : d_ptr(new QGLBezierPatchesPrivate()) +{ +} + +/*! + Constructs a copy of \a other. + + \sa operator=() +*/ +QGLBezierPatches::QGLBezierPatches(const QGLBezierPatches &other) + : d_ptr(new QGLBezierPatchesPrivate(other.d_ptr.data())) +{ +} + +/*! + Destroys this Bezier patch list. +*/ +QGLBezierPatches::~QGLBezierPatches() +{ +} + +/*! + Assigns \a other to this Bezier patch list. +*/ +QGLBezierPatches &QGLBezierPatches::operator= + (const QGLBezierPatches &other) +{ + if (this != &other) + d_ptr->copy(other.d_ptr.data()); + return *this; +} + +/*! + Returns the positions of the vertices in the Bezier patches. + + \sa setPositions(), textureCoords() +*/ +QVector3DArray QGLBezierPatches::positions() const +{ + Q_D(const QGLBezierPatches); + return d->positions; +} + +/*! + Sets the \a positions of the vertices in the Bezier patches. + + \sa positions(), setTextureCoords() +*/ +void QGLBezierPatches::setPositions(const QVector3DArray &positions) +{ + Q_D(QGLBezierPatches); + d->positions = positions; +} + +/*! + Returns the texture co-ordinates for the Bezier patches. + Each patch consumes two elements from the texture + co-ordinate array, defining the opposite corners. + + The default is an empty array, which indicates that each + patch will generate texture co-ordinates in the range + (0, 0) to (1, 1). + + \sa setTextureCoords(), positions() +*/ +QVector2DArray QGLBezierPatches::textureCoords() const +{ + Q_D(const QGLBezierPatches); + return d->textureCoords; +} + +/*! + Sets the texture co-ordinates for the Bezier patches to + the elements of \a textureCoords. Each patch consumes + two elements from \a textureCoords, defining the opposite + corners. + + If \a textureCoords is empty, then each patch will generate + texture co-ordinates in the range (0, 0) to (1, 1). + + \sa textureCoords(), setPositions() +*/ +void QGLBezierPatches::setTextureCoords(const QVector2DArray &textureCoords) +{ + Q_D(QGLBezierPatches); + d->textureCoords = textureCoords; +} + +/*! + Returns the depth of subdivision to use when converting the + Bezier geometry into triangles. The default value is 4. + + \sa setSubdivisionDepth() +*/ +int QGLBezierPatches::subdivisionDepth() const +{ + Q_D(const QGLBezierPatches); + return d->subdivisionDepth; +} + +/*! + Sets the depth of subdivision to use when converting the + Bezier geometry into triangles to \a value. + + \sa subdivisionDepth() +*/ +void QGLBezierPatches::setSubdivisionDepth(int value) +{ + Q_D(QGLBezierPatches); + d->subdivisionDepth = value; +} + +/*! + Transforms the positions() in this Bezier geometry object + according to \a matrix. + + \sa transformed() +*/ +void QGLBezierPatches::transform(const QMatrix4x4 &matrix) +{ + Q_D(QGLBezierPatches); + d->positions.transform(matrix); +} + +/*! + Returns a new Bezier geometry object that results from transforming + this object's positions() according to \a matrix. + + \sa transform() +*/ +QGLBezierPatches QGLBezierPatches::transformed(const QMatrix4x4 &matrix) const +{ + QGLBezierPatches result(*this); + result.d_ptr->positions.transform(matrix); + return result; +} + +/*! + Returns true if \a ray intersects this Bezier geometry object; + false otherwise. + + \sa intersection() +*/ +bool QGLBezierPatches::intersects(const QRay3D &ray) const +{ + Q_D(const QGLBezierPatches); + return !qIsNaN(d->intersection(ray, true, 0, 0)); +} + +/*! + Returns the t value at which \a ray intersects this Bezier + geometry object, or not-a-number if there is no intersection. + + When the \a ray intersects this object, the return value is a + parametric value that can be passed to QRay3D::point() to determine + the actual intersection point, as shown in the following example: + + \code + qreal t = patches.intersection(ray); + QVector3D pt; + if (qIsNaN(t)) { + qWarning("no intersection occurred"); + else + pt = ray.point(t); + \endcode + + If \a ray intersects the object multiple times, the returned + t will be the smallest t value, corresponding to the first + intersection of the \a ray with the object. The t value may + be negative if the first intersection occurs in the reverse + direction of \a ray. + + The intersection is determined by subdividing the patches into + triangles and intersecting with those triangles. A pruning + algorithm is used to discard patches whose convex hull do not + intersect with \a ray. + + If \a texCoord is not null, then it will return the texture + co-ordinate of the intersection point. + + If \a patch is not null, then it will return the index of the + patch that contains the intersection, or -1 if there is no + intersection. + + \sa intersects() +*/ +qreal QGLBezierPatches::intersection(const QRay3D &ray, QVector2D *texCoord, int *patch) const +{ + Q_D(const QGLBezierPatches); + return d->intersection(ray, false, texCoord, patch); +} + +/*! + \relates QGLBezierPatches + + Subdivides the Bezier patch data in \a patches into triangles + and adds them to the specified display \a list. +*/ +QGLBuilder &operator<<(QGLBuilder &list, const QGLBezierPatches &patches) +{ + patches.d_ptr->subdivide(&list); + return list; +} + +QT_END_NAMESPACE diff --git a/src/threed/geometry/qglbezierpatches.h b/src/threed/geometry/qglbezierpatches.h new file mode 100644 index 000000000..3bab8f5ff --- /dev/null +++ b/src/threed/geometry/qglbezierpatches.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLBEZIERPATCHES_H +#define QGLBEZIERPATCHES_H + +#include "qvector2darray.h" +#include "qvector3darray.h" +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLBezierPatchesPrivate; +class QGLBuilder; +class QRay3D; + +class Q_QT3D_EXPORT QGLBezierPatches +{ +public: + QGLBezierPatches(); + QGLBezierPatches(const QGLBezierPatches &other); + virtual ~QGLBezierPatches(); + + QGLBezierPatches &operator=(const QGLBezierPatches &other); + + QVector3DArray positions() const; + void setPositions(const QVector3DArray &positions); + + QVector2DArray textureCoords() const; + void setTextureCoords(const QVector2DArray &textureCoords); + + int subdivisionDepth() const; + void setSubdivisionDepth(int value); + + void transform(const QMatrix4x4 &matrix); + QGLBezierPatches transformed(const QMatrix4x4 &matrix) const; + + bool intersects(const QRay3D &ray) const; + qreal intersection(const QRay3D &ray, QVector2D *texCoord = 0, int *patch = 0) const; + +private: + QScopedPointer<QGLBezierPatchesPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLBezierPatches) + + friend Q_QT3D_EXPORT QGLBuilder &operator<<(QGLBuilder &list, const QGLBezierPatches &patches); +}; + +Q_QT3D_EXPORT QGLBuilder &operator<<(QGLBuilder &list, const QGLBezierPatches &patches); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/geometry/qglbuilder.cpp b/src/threed/geometry/qglbuilder.cpp new file mode 100644 index 000000000..d3be13899 --- /dev/null +++ b/src/threed/geometry/qglbuilder.cpp @@ -0,0 +1,1378 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglbuilder.h" +#include "qglbuilder_p.h" +#include "qglsection_p.h" +#include "qglmaterialcollection.h" +#include "qglpainter.h" +#include "qgeometrydata.h" +#include "qvector_utils_p.h" + +#include <QtGui/qvector2d.h> + +#include <QtCore/qdebug.h> + +/*! + \class QGLBuilder + \brief The QGLBuilder class constructs geometry for efficient display. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::geometry + + \tableofcontents + + Use a QGLBuilder to build up vertex, index, texture and other data + during application initialization. The finalizedSceneNode() function + returns an optimized scene which can be efficiently and flexibly + displayed during frames of rendering. It is suited to writing loaders + for 3D models, and for programatically creating geometry. + + \section1 Geometry Building + + QGLBuilder makes the job of getting triangles on the GPU simple. It + calculates indices and normals for you, then uploads the data. While + it has addQuads() and other functions to deal with quads, all data is + represented as triangles for portability. + + The simplest way to use QGLBuilder is to send a set of geometry + values to it using QGeometryData in the constructor: + + \code + MyView::MyView() : QGLView() + { + // in the constructor construct a builder on the stack + QGLBuilder builder; + QGeometryData triangle; + QVector3D a(2, 2, 0); + QVector3D b(-2, 2, 0); + QVector3D c(0, -2, 0); + triangle.appendVertex(a, b, c); + + // When adding geometry, QGLBuilder automatically creates lighting normals + builder << triangle; + + // obtain the scene from the builder + m_scene = builder.finalizedSceneNode(); + + // apply effects at app initialization time + QGLMaterial *mat = new QGLMaterial; + mat->setDiffuseColor(Qt::red); + m_scene->setMaterial(mat); + } + \endcode + + Then during rendering the scene is used to display the results: + \code + MyView::paintGL(QGLPainter *painter) + { + m_scene->draw(painter); + } + \endcode + + QGLBuilder automatically generates index values and normals + on-the-fly during geometry building. During building, simply send + primitives to the builder as a sequence of vertices, and + vertices that are the same will be referenced by a single index + automatically. + + Primitives will have standard normals generated automatically + based on vertex winding. + + Consider the following code for OpenGL to draw a quad with corner + points A, B, C and D : + + \code + float vertices[12] = + { + -1.0, -1.0, -1.0, // A + 1.0, -1.0, -1.0, // B + 1.0, 1.0, 1.0, // C + -1.0, 1.0, 1.0 // D + }; + float normals[12] = { 0.0f }; + for (int i = 0; i < 12; i += 3) + { + normals[i] = 0.0; + normals[i+1] = -sqrt(2.0); + normals[i+2] = sqrt(2.0); + } + GLuint indices[6] = { + 0, 1, 2, // triangle A-B-C + 0, 2, 3 // triangle A-C-D + }; + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vertices); + glNormalPointer(3, GL_FLOAT, 0, normals); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, indices); + \endcode + + With QGLBuilder this code becomes: + + \code + float vertices[12] = + { + -1.0, -1.0, -1.0, // A + 1.0, -1.0, -1.0, // B + 1.0, 1.0, 1.0, // C + -1.0, 1.0, 1.0 // D + }; + QGLBuilder quad; + QGeometryData data; + data.appendVertexArray(QArray<QVector3D>::fromRawData( + reinterpret_cast<const QVector3D*>(vertices), 4)); + quad.addQuads(data); + \endcode + + The data primitive is added to the list, as two triangles, indexed to + removed the redundant double storage of B & C - just the same as the + OpenGL code. + + QGLBuilder will also calculate a normal for the quad and apply it + to the vertices. + + In this trivial example the indices are easily calculated, however + in more complex geometry it is easy to introduce bugs by trying + to manually control indices. Extra work is required to generate, + track and store the index values correctly. + + Bugs such as trying to index two vertices with different data - + one with texture data and one without - into one triangle can + easily result. The picture becomes more difficult when smoothing + groups are introduced - see below. + + Using indices is always preferred since it saves space on the GPU, + and makes the geometry perform faster during application run time. + + \section2 Removing Epsilon Errors + + Where vertices are generated by modelling packages or tools, or + during computation in code, very frequently rounding errors will + result in several vertices being generated that are actually + the same vertex but are separated by tiny amounts. At best these + duplications waste space on the GPU but at worst can introduce + visual artifacts that mar the image displayed. + + Closing paths, generating solids of rotation, or moving model + sections out and back can all introduce these types of epsilon + errors, resulting in "cracks" or artifacts on display. + + QGLBuilder's index generation process uses a fuzzy match that + coalesces all vertex values at a point - even if they are out by + a tiny amount - and references them with a single index. + + \section2 Lighting Normals and Null Triangles + + QGLBuilder functions calculate lighting normals, when building + geometry. This saves the application programmer from having to write + code to calculate them. Normals for each triangle (a, b, c) are + calculated as the QVector3D::normal(a, b, c). + + If lighting normals are explicitly supplied when using QGLBuilder, + then this calculation is not done. This may save on build time. + + As an optimization, QGLBuilder skips null triangles, that is ones + with zero area, where it can. Such triangles generate no fragments on + the GPU, and thus do not display but nonetheless can take up space + and processing power. + + Null triangles can easily occur when calculating vertices results + in two vertices coinciding, or three vertices lying on the same line. + + This skipping is done using the lighting normals cross-product. If the + cross-product is a null vector then the triangle is null. + + When lighting normals are specified explicitly the skipping + optimization is suppressed, so if for some reason null triangles are + required to be retained, then specify normals for each logical vertex. + + See the documentation below of the individual addTriangle() and other + functions for more details. + + \section2 Raw Triangle Mode + + Where generation of indices and normals is not needed - for example if + porting an existing application, it is possible to do a raw import of + triangle data, without using any of QGLBuilder's processing. + + To do this ensure that indices are placed in the QGeometryData passed to + the addTriangles() function, and this will trigger \bold{raw triangle} mode. + + When adding triangles in this way ensure that all appropriate values + have been correctly set, and that the normals, indices and other data + are correctly calculated, since no checking is done. + + When writing new applications, simply leave construction of normals and + indices to the QGLBuilder + + \section1 Rendering and QGLSceneNode items. + + QGLSceneNodes are used to manage application of local transformations, + materials and effects. + + QGLBuilder generates a root level QGLSceneNode, which can be accessed + with the sceneNode() function. Under this a new node is created for + each section of geometry, and also by using pushNode() and popNode(). + + To organize geometry for painting with different materials and effects + call the newNode() function: + + \code + QGLSceneNode *box = builder.newNode(); + box->setMaterial(wood); + \endcode + + Many nodes may be created this way, but they will be optimized into + a small number of buffers under the one scene when the + finalizedSceneNode() function is called. + + \image soup.png + + Here the front can is a set of built geometry and the other two are + scene nodes that reference it, without copying any geometry. + + \snippet builder/builder.cpp 0 + + QGLSceneNodes can be used after the builder is created to cheaply + copy and redisplay the whole scene. Or to reference parts of the geometry + use the functions newNode() or pushNode() and popNode() to manage + QGLSceneNode generation while building geometry. + + To draw the resulting built geometry simply call the draw method of the + build geometry. + + \snippet builder/builder.cpp 1 + + Call the \l{QGLSceneNode::palette()}{palette()} function on the sceneNode() + to get the QGLMaterialCollection for the node, and place textures + and materials into it. + + Built geometry will typically share the one palette. Either create a + palette, and pass it to the \l{QGLBuilder::QGLBuilder()}{constructor}; + or pass no arguments to the constructor and the QGLBuilder + will create a palette: + + \snippet builder/builder.cpp 2 + + These may then be applied as needed throughout the building of the + geometry using the integer reference, \c{canMat} in the above code. + + See the QGLSceneNode documentation for more. + + \section1 Using Sections + + During initialization of the QGLBuilder, while accumulating + geometry, the geometry data in a QGLBuilder is placed into + sections - there must be at least one section. + + Call the newSection() function to create a new section: + + \snippet builder/builder.cpp 3 + + Here separate sections for the rounded outside cylinder and flat top and + bottom of the soup can model makes for the appearance of a sharp edge + between them. If the sides and top and bottom were in the same section + QGLBuilder would attempt to average the normals around the edge resulting + in an unrealistic effect. + + In 3D applications this concept is referred to as + \l{http://www.google.com/search?smoothing+groups}{smoothing groups}. Within + a section (smoothing group) all normals are averaged making it appear + as one smoothly shaded surface. + + The can has 3 smoothing groups - bottom, top and sides. + + This mesh of a Q is a faceted model - it has 0 smoothing groups: + + \image faceted-q.png + + To create geometry with a faceted appearance call newSection() with + an argument of QGL::Faceted thus \c{newSection(QGL::Faceted)}. + + Faceted geometry is suitable for small models, where hard edges are + desired between every face - a dice, gem or geometric solid for example. + + If no section has been created when geometry is added a new section is + created automatically. This section will have its smoothing set + to QGL::Smooth. + + To create a faceted appearance rather than accepting the automatically + created section the << operator can also be used: + + \code + QGLBuilder builder; + QGeometryData triangles; + triangles.appendVertices(a, b, c); + builder << QGL::Faceted << triangles; + \endcode + + \section2 Geometry Data in a Section + + Management of normals and vertices for smoothing, and other data is + handled automatically by the QGLBuilder instance. + + Within a section, incoming geometry data will be coalesced and + indices created to reference the fewest possible copies of the vertex + data. For example, in smooth geometry all copies of a vertex are + coalesced into one, and referenced by indices. + + One of the few exceptions to this is the case where texture data forms + a \i seam and a copy of a vertex must be created to carry the two + texture coordinates either side of the seam. + + \image texture-seam.png + + Coalescing has the effect of packing geometry data into the + smallest space possible thus improving cache coherence and performance. + + Again all this is managed automatically by QGLBuilder and all + that is required is to create smooth or faceted sections, and add + geometry to them. + + Each QGLSection references a contiguous range of vertices in a + QGLBuilder. + + \section1 Finalizing and Retrieving the Scene + + Once the geometry has been accumulated in the QGLBuilder instance, the + finalizedSceneNode() method must be called to retrieve the optimized + scene. This function serves to normalize the geometry and optimize + it for display. + + While it may be convenient to get pointers to sub nodes in the scene + during construction, it is important to retrieve the root of the scene + so that the memory consumed by the scene can be recovered. The builder + will create a QGLMaterialCollection; and there may be geometry, materials + and other resources: these are all parented onto the root scene node. + These can easily be recovered by deleting the root scene node: + + \code + MyView::MyView() : QGLView() + { + // in the constructor construct a builder on the stack + QGLBuilder builder; + + // add geometry as shown above + builder << triangles; + + // obtain the scene from the builder & take ownership + m_scene = builder.finalizedSceneNode(); + } + + MyView::~MyView() + { + // recover all scene resources + delete m_scene; + } + \endcode + + Alternatively set the scene's parent to ensure resource recovery + \c{m_scene->setParent(this)}. + + +*/ + +QGLBuilderPrivate::QGLBuilderPrivate(QGLBuilder *parent) + : currentSection(0) + , currentNode(0) + , rootNode(0) + , defThreshold(5) + , q(parent) +{ +} + +QGLBuilderPrivate::~QGLBuilderPrivate() +{ + qDeleteAll(sections); + if (rootNode) + { + qWarning("Destroying QGLBuilder but finalizedSceneNode() not called"); + delete rootNode; + } +} + +/*! + Construct a new QGLBuilder using \a materials for the palette. If the + \a materials argument is null, then a new palette is created. +*/ +QGLBuilder::QGLBuilder(QGLMaterialCollection *materials) + : dptr(new QGLBuilderPrivate(this)) +{ + dptr->rootNode = new QGLSceneNode; + if (!materials) + materials = new QGLMaterialCollection(dptr->rootNode); + dptr->rootNode->setPalette(materials); +} + +/*! + Destroys this QGLBuilder recovering any resources. +*/ +QGLBuilder::~QGLBuilder() +{ + delete dptr; +} + +/*! + Helper function to calculate the normal for and set it on vertices + in \a i, \a j and \a k in triangle data \a p. If the triangle in + data \a p is a null triangle (area == 0) then the function returns + false, otherwise it returns true. +*/ +static inline void setNormals(int i, int j, int k, QGeometryData &p, const QVector3D &n) +{ + p.normal(i) = n; + p.normal(j) = n; + p.normal(k) = n; +} + +static bool qCalculateNormal(int i, int j, int k, QGeometryData &p, QVector3D *vec = 0) +{ + QVector3D norm; + QVector3D *n = &norm; + if (vec) + n = vec; + bool nullTriangle = false; + *n = QVector3D::crossProduct(p.vertexAt(j) - p.vertexAt(i), + p.vertexAt(k) - p.vertexAt(j)); + if (qFskIsNull(n->x())) + n->setX(0.0f); + if (qFskIsNull(n->y())) + n->setY(0.0f); + if (qFskIsNull(n->z())) + n->setZ(0.0f); + if (n->isNull()) + { + nullTriangle = true; + } + else + { + setNormals(i, j, k, p, *n); + } + return nullTriangle; +} + +/*! + \internal + Helper function to actually add the vertices to geometry. +*/ +void QGLBuilderPrivate::addTriangle(int i, int j, int k, + const QGeometryData &p, int &count) +{ + if (currentSection == 0) + q->newSection(); + QLogicalVertex a(p, i); + QLogicalVertex b(p, j); + QLogicalVertex c(p, k); + currentSection->append(a, b, c); + count += 3; +} + +/*! + Add \a triangles - a series of one or more triangles - to this builder. + + The data is broken into groups of 3 vertices, each processed as a triangle. + + If \a triangles has less than 3 vertices this function exits without + doing anything. Any vertices at the end of the list under a multiple + of 3 are ignored. + + If no normals are supplied in \a triangles, a normal is calculated; as + the cross-product \c{(b - a) x (c - a)}, for each group of 3 + logical vertices \c{a(triangle, i), b(triangle, i+1), c(triangle, i+2)}. + + In the case of a degenerate triangle, where the cross-product is null, + that triangle is skipped. Supplying normals suppresses this behaviour + (and means any degenerate triangles will be added to the geometry). + + \bold{Raw Triangle Mode} + + If \a triangles has indices specified then no processing of any kind is + done and all the geometry is simply dumped in to the builder. + + This \bold{raw triangle} mode is for advanced use, and it is assumed that + the user knows what they are doing, in particular that the indices + supplied are correct, and normals are supplied and correct. + + Normals are not calculated in raw triangle mode, and skipping of null + triangles is likewise not performed. See the section on + \l{raw-triangle-mode}{raw triangle mode} + in the class documentation above. + + \sa addQuads(), operator>>() +*/ +void QGLBuilder::addTriangles(const QGeometryData &triangles) +{ + if (triangles.count() < 3) + return; + if (triangles.indexCount() > 0) + { + // raw triangle mode + if (dptr->currentSection == 0) + newSection(); + dptr->currentSection->appendGeometry(triangles); + dptr->currentSection->appendIndices(triangles.indices()); + dptr->currentNode->setCount(dptr->currentNode->count() + triangles.indexCount()); + } + else + { + QGeometryData t = triangles; + bool calcNormal = !t.hasField(QGL::Normal); + if (calcNormal) + { + QVector3DArray nm(t.count()); + t.appendNormalArray(nm); + } + bool skip = false; + int k = 0; + for (int i = 0; i < t.count() - 2; i += 3) + { + if (calcNormal) + skip = qCalculateNormal(i, i+1, i+2, t); + if (!skip) + dptr->addTriangle(i, i+1, i+2, t, k); + } + dptr->currentNode->setCount(dptr->currentNode->count() + k); + } +} + +/*! + Add \a quads - a series of one or more quads - to this builder. + + If \a quads has less than four vertices this function exits without + doing anything. + + One normal per quad is calculated if \a quads does not have normals. + For this reason quads should have all four vertices in the same plane. + If the vertices do not lie in the same plane, use addTriangleStrip() + to add two adjacent triangles instead. + + Since internally \l{geometry-building}{quads are stored as two triangles}, + each quad is actually divided in half into two triangles. + + Degenerate triangles are skipped in the same way as addTriangles(). + + \sa addTriangles(), addTriangleStrip() +*/ +void QGLBuilder::addQuads(const QGeometryData &quads) +{ + if (quads.count() < 4) + return; + QGeometryData q = quads; + bool calcNormal = !q.hasField(QGL::Normal); + if (calcNormal) + { + QVector3DArray nm(q.count()); + q.appendNormalArray(nm); + } + bool skip = false; + int k = 0; + QVector3D norm; + for (int i = 0; i < q.count(); i += 4) + { + if (calcNormal) + skip = qCalculateNormal(i, i+1, i+2, q, &norm); + if (!skip) + dptr->addTriangle(i, i+1, i+2, q, k); + if (skip) + skip = qCalculateNormal(i, i+2, i+3, q, &norm); + if (!skip) + { + if (calcNormal) + setNormals(i, i+2, i+3, q, norm); + dptr->addTriangle(i, i+2, i+3, q, k); + } + } + dptr->currentNode->setCount(dptr->currentNode->count() + k); +} + +/*! + Adds to this section a set of connected triangles defined by \a fan. + + N triangular faces are generated, where \c{N == fan.count() - 2}. Each + face contains the 0th vertex in \a fan, followed by the i'th and i+1'th + vertex - where i takes on the values from 1 to \c{fan.count() - 1}. + + If \a fan has less than three vertices this function exits without + doing anything. + + This function is similar to the OpenGL mode GL_TRIANGLE_FAN. It + generates a number of triangles all sharing one common vertex, which + is the 0'th vertex of the \a fan. + + Normals are calculated as for addTriangle(), given the above ordering. + There is no requirement or assumption that all triangles lie in the + same plane. Degenerate triangles are skipped in the same way as + addTriangles(). + + \sa addTriangulatedFace() +*/ +void QGLBuilder::addTriangleFan(const QGeometryData &fan) +{ + if (fan.count() < 3) + return; + QGeometryData f = fan; + bool calcNormal = !f.hasField(QGL::Normal); + if (calcNormal) + { + QVector3DArray nm(f.count()); + f.appendNormalArray(nm); + } + int k = 0; + bool skip = false; + for (int i = 1; i < f.count() - 1; ++i) + { + if (calcNormal) + skip = qCalculateNormal(0, i, i+1, f); + if (!skip) + dptr->addTriangle(0, i, i+1, f, k); + } + dptr->currentNode->setCount(dptr->currentNode->count() + k); +} + +/*! + Adds to this section a set of connected triangles defined by \a strip. + + N triangular faces are generated, where \c{N == strip.count() - 2}. + The triangles are generated from vertices 0, 1, & 2, then 2, 1 & 3, + then 2, 3 & 4, and so on. In other words every second triangle has + the first and second vertices switched, as a new triangle is generated + from each successive set of three vertices. + + If \a strip has less than three vertices this function exits without + doing anything. + + Normals are calculated as for addTriangle(), given the above ordering. + + This function is very similar to the OpenGL mode GL_TRIANGLE_STRIP. It + generates triangles along a strip whose two sides are the even and odd + vertices. + + \sa addTriangulatedFace() +*/ +void QGLBuilder::addTriangleStrip(const QGeometryData &strip) +{ + if (strip.count() < 3) + return; + QGeometryData s = strip; + bool calcNormal = !s.hasField(QGL::Normal); + if (calcNormal) + { + QVector3DArray nm(s.count()); + s.appendNormalArray(nm); + } + bool skip = false; + int k = 0; + for (int i = 0; i < s.count() - 2; ++i) + { + if (i % 2) + { + if (calcNormal) + skip = qCalculateNormal(i+1, i, i+2, s); + if (!skip) + dptr->addTriangle(i+1, i, i+2, s, k); + } + else + { + if (calcNormal) + skip = qCalculateNormal(i, i+1, i+2, s); + if (!skip) + dptr->addTriangle(i, i+1, i+2, s, k); + } + } + dptr->currentNode->setCount(dptr->currentNode->count() + k); +} + +/*! + Adds to this section a set of quads defined by \a strip. + + If \a strip has less than four vertices this function exits without + doing anything. + + The first quad is formed from the 0'th, 2'nd, 3'rd and 1'st vertices. + The second quad is formed from the 2'nd, 4'th, 5'th and 3'rd vertices, + and so on, as shown in this diagram: + + \image quads.png + + One normal per quad is calculated if \a strip does not have normals. + For this reason quads should have all four vertices in the same plane. + If the vertices do not lie in the same plane, use addTriangles() instead. + + Since internally \l{geometry-building}{quads are stored as two triangles}, + each quad is actually divided in half into two triangles. + + Degenerate triangles are skipped in the same way as addTriangles(). + + \sa addQuads(), addTriangleStrip() +*/ +void QGLBuilder::addQuadStrip(const QGeometryData &strip) +{ + if (strip.count() < 4) + return; + QGeometryData s = strip; + bool calcNormal = !s.hasField(QGL::Normal); + if (calcNormal) + { + QVector3DArray nm(s.count()); + s.appendNormalArray(nm); + } + bool skip = false; + QVector3D norm; + int k = 0; + for (int i = 0; i < s.count() - 3; i += 2) + { + if (calcNormal) + skip = qCalculateNormal(i, i+2, i+3, s, &norm); + if (!skip) + dptr->addTriangle(i, i+2, i+3, s, k); + if (skip) + skip = qCalculateNormal(i, i+3, i+1, s, &norm); + if (!skip) + { + if (calcNormal) + setNormals(i, i+3, i+1, s, norm); + dptr->addTriangle(i, i+3, i+1, s, k); + } + } + dptr->currentNode->setCount(dptr->currentNode->count() + k); +} + +/*! + Adds to this section a polygonal face made of triangular sub-faces, + defined by \a face. The 0'th vertex is used for the center, while + the subsequent vertices form the perimeter of the face, which must + at minimum be a triangle. + + If \a face has less than four vertices this function exits without + doing anything. + + This function provides functionality similar to the OpenGL mode GL_POLYGON, + except it divides the face into sub-faces around a \bold{central point}. + The center and perimeter vertices must lie in the same plane (unlike + triangle fan). If they do not normals will be incorrectly calculated. + + \image triangulated-face.png + + Here the sub-faces are shown divided by green lines. Note how this + function handles some re-entrant (non-convex) polygons, whereas + addTriangleFan will not support such polygons. + + If required, the center point can be calculated using the center() function + of QGeometryData: + + \code + QGeometryData face; + face.appendVertex(perimeter.center()); // perimeter is a QGeometryData + face.appendVertices(perimeter); + builder.addTriangulatedFace(face); + \endcode + + N sub-faces are generated where \c{N == face.count() - 2}. + + Each triangular sub-face consists of the center; followed by the \c{i'th} + and \c{((i + 1) % N)'th} vertex. The last face generated then is + \c{(center, face[N - 1], face[0]}, the closing face. Note that the closing + face is automatically created, unlike addTriangleFan(). + + If no normals are supplied in the vertices of \a face, normals are + calculated as per addTriangle(). One normal is calculated, since a + faces vertices lie in the same plane. + + Degenerate triangles are skipped in the same way as addTriangles(). + + \sa addTriangleFan(), addTriangles() +*/ +void QGLBuilder::addTriangulatedFace(const QGeometryData &face) +{ + if (face.count() < 4) + return; + QGeometryData f; + f.appendGeometry(face); + int cnt = f.count(); + bool calcNormal = !f.hasField(QGL::Normal); + if (calcNormal) + { + QVector3DArray nm(cnt); + f.appendNormalArray(nm); + } + bool skip = false; + QVector3D norm; + int k = 0; + for (int i = 1; i < cnt; ++i) + { + int n = i + 1; + if (n == cnt) + n = 1; + if (calcNormal) + { + skip = qCalculateNormal(0, i, n, f); + if (norm.isNull() && !skip) + { + norm = f.normalAt(0); + for (int i = 0; i < cnt; ++i) + f.normal(i) = norm; + } + } + if (!skip) + dptr->addTriangle(0, i, n, f, k); + } + dptr->currentNode->setCount(dptr->currentNode->count() + k); +} + +/*! + Add a series of quads by 'interleaving' \a top and \a bottom. + + This function behaves like quadStrip(), where the odd-numbered vertices in + the input primitive are from \a top and the even-numbered vertices from + \a bottom. + + It is trivial to do extrusions using this function: + + \code + // create a series of quads for an extruded edge along -Y + addQuadsInterleaved(topEdge, topEdge.translated(QVector3D(0, -1, 0)); + \endcode + + N quad faces are generated where \c{N == min(top.count(), bottom.count() - 1}. + If \a top or \a bottom has less than 2 elements, this functions does + nothing. + + Each face is formed by the \c{i'th} and \c{(i + 1)'th} + vertices of \a bottom, followed by the \c{(i + 1)'th} and \c{i'th} + vertices of \a top. + + If the vertices in \a top and \a bottom are the perimeter vertices of + two polygons then this function can be used to generate quads which form + the sides of a \l{http://en.wikipedia.org/wiki/Prism_(geometry)}{prism} + with the polygons as the prisms top and bottom end-faces. + + \image quad-extrude.png + + In the diagram above, the \a top is shown in orange, and the \a bottom in + dark yellow. The first generated quad, (a, b, c, d) is generated in + the order shown by the blue arrow. + + To create such a extruded prismatic solid, complete with top and bottom cap + polygons, given just the top edge do this: + \code + QGeometryData top = buildTopEdge(); + QGeometryData bottom = top.translated(QVector3D(0, 0, -1)); + builder.addQuadsInterleaved(top, bottom); + builder.addTriangulatedFace(top); + builder.addTriangulatedFace(bottom.reversed()); + \endcode + The \a bottom QGeometryData must be \bold{reversed} so that the correct + winding for an outward facing polygon is obtained. +*/ +void QGLBuilder::addQuadsInterleaved(const QGeometryData &top, + const QGeometryData &bottom) +{ + if (top.count() < 2 || bottom.count() < 2) + return; + QGeometryData zipped = bottom.interleavedWith(top); + bool calcNormal = !zipped.hasField(QGL::Normal); + if (calcNormal) + { + QVector3DArray nm(zipped.count()); + zipped.appendNormalArray(nm); + } + bool skip = false; + QVector3D norm; + int k = 0; + for (int i = 0; i < zipped.count() - 2; i += 2) + { + if (calcNormal) + skip = qCalculateNormal(i, i+2, i+3, zipped, &norm); + if (!skip) + dptr->addTriangle(i, i+2, i+3, zipped, k); + if (skip) + skip = qCalculateNormal(i, i+3, i+1, zipped, &norm); + if (!skip) + { + if (calcNormal) + setNormals(i, i+3, i+1, zipped, norm); + dptr->addTriangle(i, i+3, i+1, zipped, k); + } + } + dptr->currentNode->setCount(dptr->currentNode->count() + k); +} + +/*! + \fn void QGLBuilder::addPane(QSizeF size) + Convenience function to create a quad centered on the origin, + lying in the Z=0 plane, with width (x dimension) and height + (y dimension) specified by \a size. +*/ + +/*! + \fn void QGLBuilder::addPane(qreal size) + Convenience method to add a single quad of dimensions \a size wide by + \a size high in the z = 0 plane, centered on the origin. The quad has + texture coordinates of (0, 0) at the bottom left and (1, 1) at the top + right. The default value for \a size is 1.0, resulting in a quad + from QVector3D(-0.5, -0.5, 0.0) to QVector3D(0.5, 0.5, 0.0). +*/ + +/*! + \internal +*/ +void QGLBuilderPrivate::adjustSectionNodes(QGLSection *sec, + int offset, const QGeometryData &geom) +{ + QList<QGLSceneNode*> children = sec->nodes(); + QList<QGLSceneNode*>::iterator it = children.begin(); + QList<QGLSceneNode*> deleted; + for ( ; it != children.end(); ++it) + adjustNodeTree(*it, offset, geom, deleted); +} + +/*! + \internal + Adjust \a top by incrementing its start by \a offset, and setting its + geometry to \a geom. Find the cumulative total of indexes - + QGLSceneNode::count() - for \a top and all its children. If this total is + equal to zero, then delete that node. +*/ +int QGLBuilderPrivate::adjustNodeTree(QGLSceneNode *top, + int offset, const QGeometryData &geom, + QList<QGLSceneNode*> &deleted) +{ + int totalItems = 0; + if (top && !deleted.contains(top)) + { + top->setStart(top->start() + offset); + top->setGeometry(geom); + totalItems = top->count(); + QList<QGLSceneNode*> children = top->children(); + QList<QGLSceneNode*>::iterator it = children.begin(); + for ( ; it != children.end(); ++it) + { + totalItems += adjustNodeTree(*it, offset, geom, deleted); + } + if (totalItems == 0 && top->objectName().isEmpty()) + { + delete top; + deleted.append(top); + } + } + return totalItems; +} + +/*! + \internal + Returns a count of all the items referenced by this node + and all its children. +*/ +static int recursiveCount(QGLSceneNode *top) +{ + int totalItems = 0; + if (top) + { + totalItems = top->count(); + QList<QGLSceneNode*> children = top->children(); + QList<QGLSceneNode*>::const_iterator it = children.constBegin(); + for ( ; it != children.constEnd(); ++it) + totalItems += recursiveCount(*it); + } + return totalItems; +} + +static int nodeCount(const QList<QGLSceneNode*> &list) +{ + int total = 0; + QList<QGLSceneNode*>::const_iterator it = list.constBegin(); + for ( ; it != list.constEnd(); ++it) + total += recursiveCount(*it); + return total; +} + +static inline void warnIgnore(int secCount, QGLSection *s, int vertCount, int nodeCount, + const char *msg) +{ + qWarning("Ignoring section %d (%p) with %d vertices and" + " %d indexes - %s", secCount, s, vertCount, nodeCount, msg); +} + +/*! + Finish the building of this geometry, optimize it for rendering, and return a + pointer to the detached top-level scene node (root node). + + Since the scene is detached from the builder object, the builder itself + may be deleted or go out of scope while the scene lives on: + + \code + void MyView::MyView() + { + QGLBuilder builder; + // construct geometry + m_thing = builder.finalizedSceneNode(); + } + + void MyView::~MyView() + { + delete m_thing; + } + + void MyView::paintGL() + { + m_thing->draw(painter); + } + \endcode + + The root node will have a child node for each section that was created + during geometry building. + + This method must be called exactly once after building the scene. + + \bold{Calling code takes ownership of the scene.} In particular take care + to either explicitly destroy the scene when it is no longer needed - as shown + above. + + For more complex applications parent each finalized scene node onto a QObject + so it will be implictly cleaned up by Qt. If you use QGLSceneNode::setParent() + to do this, you can save an explicit call to addNode() since if setParent() + detects that the new parent is a QGLSceneNode it will call addNode() for you: + + \code + // here a top level node for the app is created, and parented to the view + QGLSceneNode *topNode = new QGLSceneNode(this); + + QGLBuilder b1; + // build geometry + + QGLSceneNode *thing = b1.finalizedSceneNode(); + + // does a QObject::setParent() to manage memory, and also adds to the scene + // graph, so no need to call topNode->addNode(thing) + thing->setParent(topNode); + + QGLBuilder b2; + // build more geometry + QGLSceneNode *anotherThing = b2.finalizedSceneNode(); + + // again parent on get addNode for free + anotherThing->setParent(topNode); + \endcode + + If this builder is destroyed without calling this method to take + ownership of the scene, a warning will be printed on the console and the + scene will be deleted. If this method is called more than once, on the + second and subsequent calls a warning is printed and NULL is returned. + + This function does the following: + \list + \o packs all geometry data from sections into QGLSceneNode instances + \o recalculates QGLSceneNode start() and count() for the scene + \o deletes all QGLBuilder's internal data structures + \o returns the top level scene node that references the geometry + \o sets the internal pointer to the top level scene node to NULL + \endlist + + \sa sceneNode() +*/ +QGLSceneNode *QGLBuilder::finalizedSceneNode() +{ + if (dptr->rootNode == 0) + { + qWarning("QGLBuilder::finalizedSceneNode() called twice"); + return 0; + } + QGeometryData g; + QMap<quint32, QGeometryData> geos; + QMap<QGLSection*, int> offsets; + for (int i = 0; i < dptr->sections.count(); ++i) + { + // pack sections that have the same fields into one geometry + QGLSection *s = dptr->sections.at(i); + QGL::IndexArray indices = s->indices(); + int icnt = indices.size(); + int ncnt = nodeCount(s->nodes()); + int scnt = s->count(); + if (scnt == 0 || icnt == 0 || ncnt == 0) + { + if (!qgetenv("Q_WARN_EMPTY_MESH").isEmpty()) + { + if (ncnt == 0) + warnIgnore(scnt, s, icnt, ncnt, "nodes empty"); + else if (scnt == 0) + warnIgnore(scnt, s, icnt, ncnt, "geometry count zero"); + else + warnIgnore(scnt, s, icnt, ncnt, "index count zero"); + } + continue; + } + s->normalizeNormals(); + int sectionOffset = 0; + int sectionIndexOffset = 0; + if (geos.contains(s->fields())) + { + QGeometryData &gd = geos[s->fields()]; + sectionOffset = gd.count(); + sectionIndexOffset = gd.indexCount(); + offsets.insert(s, sectionIndexOffset); + gd.appendGeometry(*s); + for (int i = 0; i < icnt; ++i) + indices[i] += sectionOffset; + gd.appendIndices(indices); + } + else + { + g = QGeometryData(*s); + geos.insert(s->fields(), g); + } + } + while (dptr->sections.count() > 0) + { + QGLSection *s = dptr->sections.takeFirst(); + dptr->adjustSectionNodes(s, offsets[s], geos[s->fields()]); + delete s; + } + QGLSceneNode *tmp = dptr->rootNode; + dptr->rootNode = 0; // indicates root node detached + return tmp; +} + +/*! + Creates a new section with smoothing mode set to \a smooth. By default + \a smooth is QGL::Smooth. + + A section must be created before any geometry or new nodes can be added + to the builder. However one is created automatically by addTriangle() + and the other add functions; and also by newNode(), pushNode() or popNode() + if needed. + + The internal node stack - see pushNode() and popNode() - is cleared, + and a new top-level QGLSceneNode is created for this section by calling + newNode(). + + \sa newNode(), pushNode() +*/ +void QGLBuilder::newSection(QGL::Smoothing smooth) +{ + new QGLSection(this, smooth); // calls addSection +} + +void QGLBuilder::addSection(QGLSection *sec) +{ + dptr->currentSection = sec; + sec->setMapThreshold(dptr->defThreshold); + dptr->sections.append(sec); + dptr->nodeStack.clear(); + newNode(); +} + +/*! + \internal + Returns the current section, in which new geometry is being added. +*/ +QGLSection *QGLBuilder::currentSection() const +{ + return dptr->currentSection; +} + +/*! + \internal + Returns a list of the sections of the geometry in this builder. +*/ +QList<QGLSection*> QGLBuilder::sections() const +{ + return dptr->sections; +} + +/*! + \internal + Test function only. +*/ +void QGLBuilder::setDefaultThreshold(int t) +{ + dptr->defThreshold = t; +} + +/*! + Returns the root scene node of the geometry created by this builder. + + \sa newNode(), newSection() +*/ +QGLSceneNode *QGLBuilder::sceneNode() +{ + return dptr->rootNode; +} + +/*! + Creates a new QGLSceneNode and makes it current. A pointer to the new + node is returned. The node is added into the scene at the same level + as the currentNode(). + + The node is set to reference the geometry starting from the next + vertex created, such that currentNode()->start() will return the + index of this next vertex. + + \sa newSection() +*/ +QGLSceneNode *QGLBuilder::newNode() +{ + if (dptr->currentSection == 0) + { + newSection(); // calls newNode() + return dptr->currentNode; + } + QGLSceneNode *parentNode = dptr->rootNode; + if (dptr->nodeStack.count() > 0) + parentNode = dptr->nodeStack.last(); + dptr->currentNode = new QGLSceneNode(parentNode); + dptr->currentNode->setPalette(parentNode->palette()); + dptr->currentNode->setStart(dptr->currentSection->indexCount()); + if (dptr->nodeStack.count() == 0) + dptr->currentSection->addNode(dptr->currentNode); + return dptr->currentNode; +} + +/*! + Returns a pointer to the current scene node, within the current section. + + If there is no current section then newSection() will be called to + create one. + + \sa newNode(), newSection() +*/ +QGLSceneNode *QGLBuilder::currentNode() +{ + if (dptr->currentSection == 0) + newSection(); // calls newNode() + return dptr->currentNode; +} + +/*! + Creates a new scene node that is a child of the current node and, + makes it the current node. A pointer to the new node is returned. + The previous current node is saved on a stack and it may + be made current again by calling popNode(). + + \sa popNode(), newNode() +*/ +QGLSceneNode *QGLBuilder::pushNode() +{ + if (dptr->currentSection == 0) + newSection(); // calls newNode() + QGLSceneNode *parentNode = dptr->currentNode; + dptr->nodeStack.append(parentNode); + dptr->currentNode = new QGLSceneNode(parentNode); + dptr->currentNode->setStart(dptr->currentSection->indexCount()); + dptr->currentNode->setPalette(parentNode->palette()); + return dptr->currentNode; +} + +/*! + Removes the node from the top of the stack, makes a copy of it, and + makes the copy current. + + If the stack is empty, behaviour is undefined. In debug mode, calling + this function when the stack is empty will cause an assert. + + A pointer to the new current node is returned. + + The node is set to reference the geometry starting from the next + vertex created, such that QGLSceneNode::start() will return the + index of this next vertex. + + \sa pushNode(), newNode() +*/ +QGLSceneNode *QGLBuilder::popNode() +{ + if (dptr->currentSection == 0) + newSection(); // calls newNode() + int cnt = dptr->currentSection->indexCount(); + QGLSceneNode *s = dptr->nodeStack.takeLast(); // assert here + QGLSceneNode *parentNode = dptr->rootNode; + if (dptr->nodeStack.count() > 0) + parentNode = dptr->nodeStack.last(); + dptr->currentNode = s->cloneNoChildren(parentNode); + dptr->currentNode->setStart(cnt); + dptr->currentNode->setCount(0); + dptr->currentNode->setPalette(parentNode->palette()); + if (dptr->nodeStack.count() == 0) + dptr->currentSection->addNode(dptr->currentNode); + return dptr->currentNode; +} + +/*! + Returns the palette for this builder. This is the QGLMaterialCollection + pointer that was passed to the constructor; or if that was null a new + QGLMaterialCollection. This function returns the same result as + \c{sceneNode()->palette()}. + + \sa sceneNode() +*/ +QGLMaterialCollection *QGLBuilder::palette() +{ + return dptr->rootNode->palette(); +} + +/*! + \relates QGLBuilder + Convenience operator for creating a new section in \a builder with \a smoothing. + + \code + // equivalent to builder.newSection(QGL::Faceted) + builder << QGL::Faceted; + \endcode +*/ +QGLBuilder& operator<<(QGLBuilder& builder, const QGL::Smoothing& smoothing) +{ + builder.newSection(smoothing); + return builder; +} + +/*! + \relates QGLBuilder + Convenience operator for adding \a triangles to the \a builder. + + \code + // equivalent to builder.addTriangles(triangles); + builder << triangles; + \endcode +*/ +QGLBuilder& operator<<(QGLBuilder& builder, const QGeometryData& triangles) +{ + builder.addTriangles(triangles); + return builder; +} diff --git a/src/threed/geometry/qglbuilder.h b/src/threed/geometry/qglbuilder.h new file mode 100644 index 000000000..8c278e192 --- /dev/null +++ b/src/threed/geometry/qglbuilder.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLBuilder_H +#define QGLBuilder_H + +#include <QtCore/qvector.h> +#include <QtCore/qlist.h> +#include <QtGui/qvector3d.h> +#include <QtOpenGL/qgl.h> + +#include "qglnamespace.h" +#include "qglscenenode.h" +#include "qglattributevalue.h" +#include "qgeometrydata.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLSection; +class QGLMaterialCollection; +class QGLBuilderPrivate; +class QGLPainter; + +class Q_QT3D_EXPORT QGLBuilder +{ +public: + explicit QGLBuilder(QGLMaterialCollection *materials = 0); + virtual ~QGLBuilder(); + + // section management + void newSection(QGL::Smoothing sm = QGL::Smooth); + + // scene management + QGLSceneNode *sceneNode(); + QGLSceneNode *currentNode(); + QGLSceneNode *newNode(); + QGLSceneNode *pushNode(); + QGLSceneNode *popNode(); + QGLMaterialCollection *palette(); + QGLSceneNode *finalizedSceneNode(); + + // geometry building by primitive + void addTriangles(const QGeometryData &triangle); + void addQuads(const QGeometryData &quad); + void addTriangleFan(const QGeometryData &fan); + void addTriangleStrip(const QGeometryData &strip); + void addTriangulatedFace(const QGeometryData &face); + void addQuadStrip(const QGeometryData &strip); + void addQuadsInterleaved(const QGeometryData &top, + const QGeometryData &bottom); + inline void addPane(qreal size = 1.0f); + inline void addPane(QSizeF size); + +protected: + // internal and test functions + QGLSection *currentSection() const; + QList<QGLSection*> sections() const; + void setDefaultThreshold(int); + +private: + Q_DISABLE_COPY(QGLBuilder); + void addSection(QGLSection *section); + + friend class QGLSection; + + QGLBuilderPrivate *dptr; +}; + +inline void QGLBuilder::addPane(qreal size) +{ + addPane(QSizeF(size, size)); +} + +inline void QGLBuilder::addPane(QSizeF size) +{ + QSizeF f = size / 2.0f; + QVector2D a(-f.width(), -f.height()); + QVector2D b(f.width(), -f.height()); + QVector2D c(f.width(), f.height()); + QVector2D d(-f.width(), f.height()); + QVector2D ta(0.0f, 0.0f); + QVector2D tb(1.0f, 0.0f); + QVector2D tc(1.0f, 1.0f); + QVector2D td(0.0f, 1.0f); + QGeometryData quad; + quad.appendVertex(a, b, c, d); + quad.appendTexCoord(ta, tb, tc, td); + addQuads(quad); +} + +Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGL::Smoothing& smoothing); +Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGeometryData& triangles); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGLBuilder_H diff --git a/src/threed/geometry/qglbuilder_p.h b/src/threed/geometry/qglbuilder_p.h new file mode 100644 index 000000000..bcbd16883 --- /dev/null +++ b/src/threed/geometry/qglbuilder_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLBuilder_P_H +#define QGLBuilder_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglbuilder.h" + +#include <QtCore/qmap.h> +#include <QPointer> + +QT_BEGIN_NAMESPACE + +class QGLBuilder; +class QGLSection; +class QGeometryData; + +class QGLBuilderPrivate +{ +public: + QGLBuilderPrivate(QGLBuilder *parent); + ~QGLBuilderPrivate(); + inline void setDirty(bool dirty = true); + void addTriangle(int a, int b, int c, const QGeometryData &p, int &count); + void adjustSectionNodes(QGLSection *sec, int offset, const QGeometryData &geom); + int adjustNodeTree(QGLSceneNode *top, int offset, const QGeometryData &geom, + QList<QGLSceneNode*> &deleted); + + QList<QGLSection*> sections; + QGLSection *currentSection; + QList<QGLSceneNode*> nodeStack; + QGLSceneNode *currentNode; + QGLSceneNode *rootNode; + int defThreshold; + QGLBuilder *q; +}; + +QT_END_NAMESPACE + +#endif // QGLBuilder_P_H diff --git a/src/threed/geometry/qglcube.cpp b/src/threed/geometry/qglcube.cpp new file mode 100644 index 000000000..5f5bda118 --- /dev/null +++ b/src/threed/geometry/qglcube.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglcube.h" +#include "qglbuilder.h" +#include "qvector3darray.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLCube + \brief The QGLCube class represents the geometry of simple six-sided cube in 3D space. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::geometry + + The following example adds a cube of 2 units on a side to a + geometry builder, and draws it at (10, 25, 0) in a QGLPainter: + + \code + QGLBuilder list; + list.newSection(QGL::Faceted); + list << QGLCube(2); + painter->translate(10, 25, 0); + list.draw(painter); + \endcode + + QGLCube will create a default set of texture coordinates that shows + the same texture of all six faces. +*/ + +/*! + \fn QGLCube::QGLCube(qreal size) + + Constructs the geometry for a regular cube of \a size + units on a side. +*/ + +/*! + \fn qreal QGLCube::size() const + + Returns the size of this cube. + + \sa setSize() +*/ + +/*! + \fn void QGLCube::setSize(qreal size) + + Sets the \a size of this cube. + + \sa size() +*/ + +static const int vertexDataLen = 6 * 4 * 3; + +static const float vertexData[vertexDataLen] = { + -0.5f, -0.5f, -0.5f, + -0.5f, -0.5f, 0.5f, + -0.5f, 0.5f, 0.5f, + -0.5f, 0.5f, -0.5f, + + -0.5f, 0.5f, -0.5f, + -0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, -0.5f, + + 0.5f, 0.5f, -0.5f, + 0.5f, 0.5f, 0.5f, + 0.5f, -0.5f, 0.5f, + 0.5f, -0.5f, -0.5f, + + 0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, 0.5f, + -0.5f, -0.5f, 0.5f, + -0.5f, -0.5f, -0.5f, + + 0.5f, -0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + -0.5f, 0.5f, 0.5f, + -0.5f, -0.5f, 0.5f, + + 0.5f, 0.5f, -0.5f, + 0.5f, -0.5f, -0.5f, + -0.5f, -0.5f, -0.5f, + -0.5f, 0.5f, -0.5f +}; + +static const int texCoordDataLen = 4 * 2; + +static const float texCoordData[texCoordDataLen] = { + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f +}; + +/*! + \relates QGLCube + + Builds the geometry for \a cube within the specified + geometry \a builder. + + This operator specifies the positions, and 2D texture + co-ordinates for all of the vertices that make up the cube. + Normals will be calculated by the \a builder, depending on its + current section's smoothing setting. +*/ +QGLBuilder& operator<<(QGLBuilder& builder, const QGLCube& cube) +{ + QGeometryData op; + + QVector3DArray vrts = QVector3DArray::fromRawData( + reinterpret_cast<const QVector3D *>(vertexData), vertexDataLen / 3); + if (cube.size() != 1.0f) + vrts.scale(cube.size()); + + op.appendVertexArray(vrts); + + QVector2DArray texx = QVector2DArray::fromRawData( + reinterpret_cast<const QVector2D *>(texCoordData), texCoordDataLen / 2); + + for (int i = 0; i < 6; ++i) + op.appendTexCoordArray(texx); + + builder.addQuads(op); + return builder; +} + +QT_END_NAMESPACE diff --git a/src/threed/geometry/qglcube.h b/src/threed/geometry/qglcube.h new file mode 100644 index 000000000..19eebc26c --- /dev/null +++ b/src/threed/geometry/qglcube.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLCUBE_H +#define QGLCUBE_H + +#include "qt3dglobal.h" + +#include <QtGui/qvector2d.h> +#include "qvector2darray.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLBuilder; + +class Q_QT3D_EXPORT QGLCube +{ +public: + explicit QGLCube(qreal size = 1.0f) : m_size(size) {} + + qreal size() const { return m_size; } + void setSize(qreal size) { m_size = size; } + +private: + qreal m_size; +}; + +Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGLCube& cube); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/geometry/qglcylinder.cpp b/src/threed/geometry/qglcylinder.cpp new file mode 100644 index 000000000..30b4f34cc --- /dev/null +++ b/src/threed/geometry/qglcylinder.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglcylinder.h" +#include "qglbuilder.h" +#include "qvector2darray.h" +#include "qvector3darray.h" +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLCylinder + \brief The QGLCylinder class represents the geometry of a simple cylinder/cone in 3D space. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::geometry + + The following example creates a cone with a top diameter of 1 unit, + a bottom diameter of of 2 units in diameter and height of 3 units. + + It then draws it at (10, 25, 0) in a QGLPainter: + + \code + QGLBuilder builder; + builder << QGLCylinder(1.0,2.0,3.0); + QGLSceneNode *node = builder.finalizedSceneNode(); + + painter.translate(10, 25, 0); + node->draw(&painter); + \endcode + + Note that the bottom circle of the cylinder will always be centred at (0,0,0) + unless otherwise transformed after cylinder creation. + + The QGLCylinder class specifies positions, normals and 2D texture + co-ordinates for all of the vertices that make up the cylinder. + + The texture co-ordinates are fixed at construction time. This + is because constructing the cylinder can involve generating additional + vertices which need to interpolate the texture co-ordinates of their + neighboring vertices. + + The QGLCylinder is divided into slices and layers. The slices value + indicate number of triangular sections into which the top and bottom + circles of the cylinder are broken into. Consequently it also sets the + number of facets which run the length of the cylinder. More slices + results in a smoother circumference. + + The layers value indicates the number of longitudinal sections the + cylinder is broken into. Fewer layers means that the side facets of the + cylinder will be made up of fewer, very long, triangles, while a higher + number of layers will produce many and smaller triangles. Often it is + desirable to avoid large triangles as they may cause inefficiencies in + texturing/lighting on certain platforms. + + The end-caps and sides of the cylinder are independent sections of the + scene-graph, and so may be textured separately. + + Textures are wrapped around the sides of thecylinder in such a way that + the texture may distort across the x axis if the top and bottom diameters + of the cylinder differ (ie. the cylinder forms a truncated cone). Textures + begin and end at the centre points of the top and bottom end-caps of the + cylinder. This wrapping means that textures on either end-cap may be + distorted. + + Texture coordinates are assigned as shown below. + + \image cylinder-texture-coords.png + + It is worth noting that the cylinder class can, in fact, be used to generate + any regular solid polygonal prism. A rectangular prism can be created, for + example, by creating a 4 sided cylinder. Likewise a hexagonal prism is + simply a 6 sided cylinder. + + With this knowledge, and an understanding of the texture coordinate mapping, + it is possible to make custom textures which will be usable with these + three dimensional objects. + + \sa QGLBuilder +*/ + + +/*! + \fn QGLCylinder::QGLCylinder(qreal diameterTop, qreal diameterBase , qreal height, int slices, int layers, bool top, bool base) + + Constructs the geometry for a cylinder with top of diameter \a diameterTop, + a base of diameter \a diameterBase, and a height of \a height. + + The resultant mesh will be divided around the vertical axis of the cylinder + into \a slices individual wedges, and shall be formed of \a layers stacked + to form the cylinder. + + If the values for \a top or \a base are true, then the cylinder will be + created with solid endcaps. Otherwise, it shall form a hollow pipe. + + units on a side. +*/ + + +/*! + \fn qreal QGLCylinder::diameterTop() const + + Returns the diameter of the top of the cylinder. + + The default value is 1. + + \sa setDiameterTop() +*/ + +/*! + \fn void QGLCylinder::setDiameterTop(qreal diameter) + + Sets the diameter of the top of this cylinder to \a diameter. + + \sa diameterTop() +*/ + +/*! + \fn qreal QGLCylinder::diameterBottom() const + + Returns the diameter of the bottom of the cylinder. + + The default value is 1. + + \sa setDiameterBottom() +*/ + +/*! + \fn void QGLCylinder::setDiameterBottom(qreal diameter) + + Sets the diameter of the bottom of this cylinder to \a diameter. + + \sa diameterBottom() +*/ + +/*! + \fn qreal QGLCylinder::height() const + + Returns the height of the cylinder. + + The default value is 1.0 + + \sa setDiameterBottom() +*/ + +/*! + \fn void QGLCylinder::setHeight(qreal height) + + Sets the height of this cylinder to \a height. + + \sa diameterBottom() +*/ + + +/*! + \fn int QGLCylinder::slices() const + + Returns the number of triangular slices the cylinder is divided into + around its polar axis. + + The default is 6. + + \sa setSlices() +*/ + +/*! + \fn int QGLCylinder::setSlices(int slices) + + Sets the number of triangular \a slices the cylinder is divided into + around its polar axis. + + \sa slices() +*/ + +/*! + \fn int QGLCylinder::layers() const + + Returns the number of cylindrical layers the cylinder is divided into + along its height. + + The default is 3. + + \sa setLayers() +*/ + +/*! + \fn int QGLCylinder::setLayers(int layers) + + Sets the number of stacked \a layers the cylinder is divided into + along its height. + + \sa layers() +*/ + +/*! + \fn bool QGLCylinder::topEnabled() const + + Returns true if the top of the cyclinder will be created when + building the mesh. + + The default is true. + + \sa setTopEnabled() +*/ + +/*! + \fn void QGLCylinder::setTopEnabled(bool top) + + Set whether the top end-cap of the cylinder will be created when + building the mesh. If \a top is true, the end-cap will be created. + + \sa topEnabled() +*/ + +/*! + \fn bool QGLCylinder::baseEnabled() const + + Returns true if the base of the cyclinder will be created when + building the mesh. + + The default is true. + + \sa setBaseEnabled() +*/ + +/*! + \fn void QGLCylinder::setBaseEnabled(bool base) + + Set whether the base end-cap of the cylinder will be created when + building the mesh. If \a base is true, the end-cap will be created. + + \sa baseEnabled() +*/ + +/*! + \relates QGLCylinder + + Builds the geometry for \a cylinder within the specified + geometry \a builder. +*/ + +QGLBuilder& operator<<(QGLBuilder& builder, const QGLCylinder& cylinder) +{ + /* ASSERT(cylinder.diameterBottom()>=0 && + cylinder.diameterTop()>=0 && + cylinder.height()>0);*/ + + qreal numSlices = qreal(cylinder.slices()); + qreal numLayers = qreal(cylinder.layers()); + qreal topRadius = cylinder.diameterTop()/2.0; + qreal bottomRadius = cylinder.diameterBottom()/2.0; + + qreal angle = 0; + qreal angleIncrement = (2.0 * M_PI) / numSlices; + qreal radius = topRadius; + qreal radiusIncrement = qreal(bottomRadius-topRadius)/ numLayers; + qreal height = qreal(cylinder.height()); + qreal heightDecrement = height/numLayers; + + qreal textureHeight = 1.0; + qreal textureDecrement = 1.0/numLayers; + + QGeometryData oldLayer; + + //Generate vertices for the next layer of cylinder + for (int layerCount=0; layerCount<=cylinder.layers(); layerCount++) { + QGeometryData newLayer; + + //Generate a circle of vertices for this layer. + for (int i=0; i<cylinder.slices(); i++) + { + newLayer.appendVertex(QVector3D(radius * qCos(angle), + radius * qSin(angle), + height)); + angle+=angleIncrement; + } + angle = 0; + // Generate texture coordinates (including an extra seam vertex for textures). + newLayer.appendVertex(newLayer.vertex(0)); + newLayer.generateTextureCoordinates(); + for (int i = 0; i < newLayer.count(); i++) newLayer.texCoord(i).setY(textureHeight); + + //Special cases for top end-cap + if (layerCount==0 && cylinder.topEnabled()) { + //Draw end-cap at top + QGeometryData top; + builder.newSection(); + builder.currentNode()->setObjectName("Cylinder Top"); + top.appendVertex(newLayer.center()); + top.appendVertexArray(newLayer.vertices()); + //Generate a circle of texture vertices for this layer. + top.appendTexCoord(QVector2D(0.5,0.5)); + + for (int i=1; i<top.count(); i++) + { + top.appendTexCoord(QVector2D(0.5*qCos(angle)+0.5, 0.5*qSin(angle)+0.5)); + angle+=angleIncrement; + } + angle = 0; + builder.addTriangulatedFace(top); + } + + + //Add a new cylinder layer to the mesh + if (layerCount>0) + { + //If it's the first section, create a cylinder sides section + if (layerCount==1) { + builder.newSection(); + builder.currentNode()->setObjectName("Cylinder Sides"); + } + builder.addQuadsInterleaved(oldLayer, newLayer); + } + + //Special cases for bottom end-cap + if (layerCount==cylinder.layers() && cylinder.baseEnabled()) { + //Draw end-cap at bottom + QGeometryData base; + builder.newSection(); + builder.currentNode()->setObjectName("Cylinder Base"); + base.appendVertexArray(newLayer.vertices()); + base.appendVertex(newLayer.center()); + //Generate a circle of texture vertices for this layer. + for (int i=1; i<base.count(); i++) + { + base.appendTexCoord(QVector2D(0.5*qCos(angle)+0.5, 0.5*qSin(angle)+0.5)); + angle+=angleIncrement; + } + base.appendTexCoord(QVector2D(0.5,0.5)); + angle = 0; + + //we need to reverse the above to draw it properly - windings! + builder.addTriangulatedFace(base.reversed()); + } + + //Keep the current layer for drawing the next segment of the cylinder + oldLayer.clear(); + oldLayer.appendGeometry(newLayer); + radius+=radiusIncrement; + height-=heightDecrement; + textureHeight-=textureDecrement; + } + + return builder; +} diff --git a/src/threed/geometry/qglcylinder.h b/src/threed/geometry/qglcylinder.h new file mode 100644 index 000000000..51d2cfba6 --- /dev/null +++ b/src/threed/geometry/qglcylinder.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLCYLINDER_H +#define QGLCYLINDER_H + +#include "qt3dglobal.h" +#include "qglmaterialcollection.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLBuilder; +class QVector2D; + +class Q_QT3D_EXPORT QGLCylinder +{ +public: + explicit QGLCylinder(qreal diameterTop = 1.0f, qreal diameterBase = 1.0f, qreal height = 1.0f, int slices = 6, int layers = 3, bool top = true, bool base = true) + : m_diameterTop(diameterTop), m_diameterBottom(diameterBase), m_height(height), m_slices(slices), m_layers(layers), m_top(top), m_base(base) {} + + //Cylinder dimensions + qreal diameterTop() const {return m_diameterTop;} + void setDiameterTop(qreal diameter) {m_diameterTop=diameter;} + + qreal diameterBottom() const {return m_diameterBottom;} + void setDiameterBottom(qreal diameter) {m_diameterBottom=diameter;} + + qreal height() const {return m_height;} + void setHeight(qreal height) {m_height = height;} + + //Cylinder geometrical subdivisions + int slices() const {return m_slices;} + void setSlices(int slices) {m_slices = slices;} + + int layers() const {return m_layers;} + void setLayers(int layers) {m_layers = layers;} + + //End-caps attached? + bool topEnabled() const {return m_top;} + void setTopEnabled(bool top) {m_top = top;} + + bool baseEnabled() const {return m_base;} + void setBaseEnabled(bool base) {m_base = base;} + +protected: + qreal m_diameterTop; + qreal m_diameterBottom; + qreal m_height; + + int m_slices; + int m_layers; + + bool m_top; + bool m_base; +}; + +Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGLCylinder& cylinder); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGLCYLINDER_H diff --git a/src/threed/geometry/qgldome.cpp b/src/threed/geometry/qgldome.cpp new file mode 100644 index 000000000..5cd18da01 --- /dev/null +++ b/src/threed/geometry/qgldome.cpp @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgldome.h" +#include "qglbuilder.h" +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLDome + \brief The QGLDome class represents the geometry of a simple hemisphere in 3D space. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::geometry + + The following example creates a dome of 2 units in diameter and + draws it at (10, 25, 0) in a QGLPainter: + + \code + QGLBuilder builder; + builder << QGLDome(2); + QGLSceneNode *node = builder.finalizedSceneNode(); + + painter.translate(10, 25, 0); + node->draw(&painter); + \endcode + + The QGLDome class specifies positions, normals and 2D texture + co-ordinates for all of the vertices that make up the sphere. + + The texture co-ordinates are fixed at construction time. This + is because constructing the sphere can involve generating additional + vertices which need to interpolate the texture co-ordinates of their + neighboring vertices. + + The default mode of QGLDome is half of a "UV sphere", which divides + the object up into longitudinal and latitudinal sections. The longitudinal + slices meet at the pole, which in a single unit dome is defined to + be at (0, 0, +0.5) and (0, 0, -0.5). This choice is the simplest to + texture map as the texture will only distort along the x-axis of the + 2D texture. However the density of vertices is significantly higher at + the poles than it is elsewhere on the sphere and is a poor choice if a + uniform density of pixels from the texture map is required. + + \sa QGLBuilder +*/ + +/*! + \fn QGLDome::QGLDome(qreal diameter, int depth, bool base) + + Creates a dome of \a diameter across (default is 1). When the dome + is recursively subdivided into triangles, it will be subdivided no more + than \a depth times (between 1 and 5, default is 3). + + If \a base is true, the dome will be drawn with a bottom circle, creating + an enclosed solid. +*/ + +/*! + Destroys this dome object. +*/ +QGLDome::~QGLDome() +{ +} + +/*! + \fn qreal QGLDome::diameter() const + + Returns the diameter of this dome. The default is 1. + + \sa setDiameter() +*/ + +/*! + \fn void QGLDome::setDiameter(qreal diameter) + + Sets the diameter of this dome to \a diameter. + + \sa diameter() +*/ + +/*! + \fn int QGLDome::subdivisionDepth() const + + Returns the maximum depth when this hemisphere is subdivided into + triangles. The default is 3. The following picture shows the effect + of depth values between 1 and 5 for a UV sphere (hemisphere subdivision + depth shares this scheme). + + \image sphere-detail.png + + \sa setSubdivisionDepth(), QGLSphere::subdivisionDepth() +*/ + +/*! + \fn void QGLDome::setSubdivisionDepth(int depth) + + Sets the maximum \a depth when this hemisphere is subdivided into triangles. + + \sa subdivisionDepth() +*/ + +/*! + \fn bool QGLDome::baseEnabled() const + + Returns true if the base of the dome will be created when + building the mesh. + + The default is true. + + \sa setBaseEnabled() +*/ + +/*! + \fn void QGLDome::setBaseEnabled(bool base) + + Set whether the bottom of the dome will be created when + building the mesh. If \a base is true, the end-cap will be + created. + + \sa baseEnabled() +*/ + +/*! + \relates QGLDome + + Builds the geometry for \a dome within the specified + geometry \a builder. +*/ +QGLBuilder& operator<<(QGLBuilder& builder, const QGLDome& dome) +{ + qreal radius = dome.diameter() / 2.0f; + + // Determine the number of slices and stacks to generate. + int divisions = dome.subdivisionDepth(); + if (divisions < 1) + divisions = 1; + else if (divisions > 5) + divisions = 5; + int stacks = 2 * (1 << divisions); + int slices = 2 * stacks; + stacks = stacks>>1; + + // Precompute sin/cos values for the slices and stacks. + const int maxSlices = 4 * (1 << 5) + 1; + const int maxStacks = 2 * (1 << 5) + 1; + qreal sliceSin[maxSlices]; + qreal sliceCos[maxSlices]; + qreal stackSin[maxStacks]; + qreal stackCos[maxStacks]; + for (int slice = 0; slice < slices; ++slice) { + qreal angle = 2 * M_PI * slice / slices; + sliceSin[slice] = qFastSin(angle); + sliceCos[slice] = qFastCos(angle); + } + sliceSin[slices] = sliceSin[0]; // Join first and last slice. + sliceCos[slices] = sliceCos[0]; + + const qreal halfPi=M_PI/2.0; + + for (int stack = 0; stack <= stacks; ++stack) { + qreal angle = halfPi * stack / stacks; + stackSin[stack] = qFastSin(angle); + stackCos[stack] = qFastCos(angle); + } + stackSin[0] = 0.0f; // Come to a point at the poles. + stackSin[stacks] = 1.0f; + + builder.newSection(); + builder.currentNode()->setObjectName("Dome"); + // Create the stacks for the dome part of the dome + for (int stack = 0; stack < stacks; ++stack) { + QGeometryData prim; + qreal z = radius * stackCos[stack]; + qreal nextz = radius * stackCos[stack + 1]; + qreal s = stackSin[stack]; + qreal nexts = stackSin[stack + 1]; + qreal c = stackCos[stack]; + qreal nextc = stackCos[stack + 1]; + qreal r = radius * s; + qreal nextr = radius * nexts; + for (int slice = 0; slice <= slices; ++slice) { + prim.appendVertex(QVector3D(nextr * sliceSin[slice], nextr * sliceCos[slice], nextz)); + prim.appendNormal(QVector3D(sliceSin[slice] * nexts, sliceCos[slice] * nexts, nextc)); + prim.appendTexCoord(QVector2D(1.0f - qreal(slice) / slices, 1.0f - qreal(stack + 1) / stacks)); + + prim.appendVertex(QVector3D(r * sliceSin[slice], r * sliceCos[slice], z)); + prim.appendNormal(QVector3D(sliceSin[slice] * s, sliceCos[slice] * s, c)); + prim.appendTexCoord(QVector2D(1.0f - qreal(slice) / slices, 1.0f - qreal(stack) / stacks)); + } + builder.addQuadStrip(prim); + } + + if (dome.baseEnabled()) { + //Draw end-cap at bottom + builder.newSection(); + builder.currentNode()->setObjectName("Base"); + + //Generate a circle of vertices for this layer. + QGeometryData tempBase; + + tempBase.appendVertex(QVector3D(0,0,0)); + tempBase.appendTexCoord(QVector2D(0.5,0.5)); + for (int slice=0; slice<=slices+1; slice++) + { + tempBase.appendVertex(QVector3D(radius * sliceCos[slice], radius * sliceSin[slice], 0)); + tempBase.appendTexCoord(QVector2D(0.5*sliceCos[slice]+0.5, 0.5*sliceSin[slice]+0.5)); + } + + //we need to reverse the above to draw it properly - windings! + builder.addTriangulatedFace(tempBase.reversed()); + } + return builder; +} + +QT_END_NAMESPACE + diff --git a/src/threed/geometry/qgldome.h b/src/threed/geometry/qgldome.h new file mode 100644 index 000000000..23d86f508 --- /dev/null +++ b/src/threed/geometry/qgldome.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLDOME_H +#define QGLDOME_H + +#include "qt3dglobal.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLBuilder; + +class Q_QT3D_EXPORT QGLDome +{ +public: + explicit QGLDome(qreal diameter = 1.0f, int depth = 3, bool baseEnabled = true) + : m_diameter(diameter), m_subdivisionDepth(depth), m_baseEnabled(baseEnabled) {} + virtual ~QGLDome(); + + qreal diameter() const { return m_diameter; } + void setDiameter(qreal diameter) { m_diameter = diameter; } + + int subdivisionDepth() const { return m_subdivisionDepth; } + void setSubdivisionDepth(int depth) { m_subdivisionDepth = depth; } + + bool baseEnabled() const {return m_baseEnabled; } + void setBaseEnabled(bool baseEnabled) {m_baseEnabled = baseEnabled;} + +private: + qreal m_diameter; + int m_subdivisionDepth; + bool m_baseEnabled; +}; + +Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGLDome& dome); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/geometry/qglmaterialcollection.cpp b/src/threed/geometry/qglmaterialcollection.cpp new file mode 100644 index 000000000..674dac70f --- /dev/null +++ b/src/threed/geometry/qglmaterialcollection.cpp @@ -0,0 +1,415 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglmaterialcollection.h" +#include "qglmaterial_p.h" +#include <QtCore/qlist.h> +#include <QtCore/qhash.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLMaterialCollection + \brief The QGLMaterialCollection class manages groups of materials. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::enablers + + Managing more complex 3d graphics with several materials is easier when the + materials can be referred to as a collection. This is the role of the + QGLMaterialCollection class. + + Plug-ins implementing 3D formats may make the materials defined in + the format available to the application via a QGLMaterialCollection. + + The collection is also optimised for the case where many small objects + must refer to materials - such as faces in a mesh, or particles. In + this case the materials can be specified as a short data type using an + offset into the collection, rather than the material name. + + When building up a collection, meshes that refer to the various materials + can check off which ones are used by calling markMaterialAsUsed(), and then + remove spurious unused materials by calling removeUnusedMaterials(). This + technique is suitable for models loaded from a model file where a large + number of materials may be specified but only a few of those materials + are used by the particular mesh selected from the scene. + + To make a material available from a collection, call addMaterial(). To + retrieve a material from the collection call removeMaterial(). + + The collection takes ownership of the QGLMaterial + objects passed to it by the addMaterial() function. These + objects will be destroyed when the collection is destroyed. +*/ + +class QGLMaterialCollectionPrivate +{ +public: + QGLMaterialCollectionPrivate() + { + } + + QList<QGLMaterial *> materials; + QHash<QString, int> materialNames; +}; + +/*! + Construct a new empty QGLMaterialCollection object. The \a parent + is set as the parent of this object. +*/ +QGLMaterialCollection::QGLMaterialCollection(QObject *parent) + : QObject(parent) + , d_ptr(new QGLMaterialCollectionPrivate) +{ +} + +/*! + Destroy this collection. All material objects referred to by this + collection will be destroyed. +*/ +QGLMaterialCollection::~QGLMaterialCollection() +{ + // The QGLMaterial QObject's are reparented to the collection + // when addMaterial() is called, so the QObject destructor + // will take care of cleaning them up for us. +} + +/*! + Returns a pointer to the material corresponding to \a index; or null + if \a index is out of range or the material has been removed. + + Here's an example of searching for a material with a given ambient + \c{color} in the collection \c{materials}: + + \code + for (int colorIndex; colorIndex < materials->size(); ++colorIndex) { + if (material(colorIndex) && + material(colorIndex)->ambientColor() == color) + break; + } + if (colorIndex < materials->size()) + myObject->setMaterial(colorIndex); + \endcode +*/ +QGLMaterial *QGLMaterialCollection::material(int index) const +{ + Q_D(const QGLMaterialCollection); + return d->materials.value(index, 0); +} + +/*! + \overload + + Returns the material associated with \a name in this collection; + null if \a name is not present or the material has been removed. +*/ +QGLMaterial *QGLMaterialCollection::material(const QString &name) const +{ + Q_D(const QGLMaterialCollection); + int index = d->materialNames.value(name, -1); + if (index >= 0) + return d->materials[index]; + else + return 0; +} + +/*! + Returns true if this collection contains \a material; false otherwise. + + \sa indexOf() +*/ +bool QGLMaterialCollection::contains(QGLMaterial *material) const +{ + return material && material->d_func()->collection == this; +} + +/*! + \overload + + Returns true if this collection contains a material called \a name; + false otherwise. + + \sa indexOf() +*/ +bool QGLMaterialCollection::contains(const QString &name) const +{ + Q_D(const QGLMaterialCollection); + return d->materialNames.contains(name); +} + +/*! + Returns the index of \a material in this collection; -1 if + \a material is not present in this collection. + + \sa contains() +*/ +int QGLMaterialCollection::indexOf(QGLMaterial *material) const +{ + if (material && material->d_func()->collection == this) + return material->d_func()->index; + else + return -1; +} + +/*! + \overload + + Returns the index of the material called \a name in this collection; + -1 if \a name is not present in this collection. + + \sa contains() +*/ +int QGLMaterialCollection::indexOf(const QString &name) const +{ + Q_D(const QGLMaterialCollection); + return d->materialNames.value(name, -1); +} + +/*! + Returns the name of the material at \a index in this material collection; + a null QString if \a index is out of range. +*/ +QString QGLMaterialCollection::materialName(int index) const +{ + Q_D(const QGLMaterialCollection); + if (index >= 0 && index < d->materials.count()) { + QGLMaterial *material = d->materials[index]; + if (material) { + // Use the name in the private data block just in case the + // application has modified objectName() since adding. + return material->d_func()->name; + } + } + return QString(); +} + +/*! + Returns true if the material at \a index in this collection has been + marked as used by markMaterialAsUsed(). + + \sa markMaterialAsUsed() +*/ +bool QGLMaterialCollection::isMaterialUsed(int index) const +{ + QGLMaterial *mat = material(index); + if (mat) + return mat->d_func()->used; + else + return false; +} + +/*! + Flags the material corresponding to the \a index as used. Some model files + may contain a range of materials, applying to various objects in the scene. + + When a particular object is loaded from the file, many of those + materials may not be used in that object. This wastes space, + with many spurious materials being stored. + + Use this method during model loading or construction to mark off + materials that have been used. Materials so marked will not + be removed by removeUnusedMaterials(). + + \sa removeUnusedMaterials(), isMaterialUsed() +*/ +void QGLMaterialCollection::markMaterialAsUsed(int index) +{ + QGLMaterial *mat = material(index); + if (mat) + mat->d_func()->used = true; +} + +/*! + Removes and deletes materials which have not been marked as used. + + \sa markMaterialAsUsed(), isMaterialUsed() +*/ +void QGLMaterialCollection::removeUnusedMaterials() +{ + Q_D(QGLMaterialCollection); + for (int index = 0; index < d->materials.size(); ++index) { + QGLMaterial *material = d->materials[index]; + if (material && !material->d_func()->used) + delete removeMaterial(index); + } +} + +/*! + Adds \a material to this collection and returns its new index. The + collection takes ownership of the material and will delete it when the + collection is destroyed. Initially the \a material is marked as unused. + + The QObject::objectName() of \a material at the time addMaterial() + is called will be used as the material's name within this collection. + Changes to the object name after the material is added are ignored. + + If \a material is already present in this collection, then this + function will return the index that was previously assigned. + + Returns -1 if \a material has been added to another collection. + + \sa removeMaterial(), markMaterialAsUsed() +*/ +int QGLMaterialCollection::addMaterial(QGLMaterial *material) +{ + Q_D(QGLMaterialCollection); + Q_ASSERT(material); + + // Allocate a new index for the material. + int index = d->materials.count(); + + // Record the index in the private data attached to the material. + // This allows us to find the material's index quickly later. + QGLMaterialPrivate *dm = material->d_func(); + if (dm->collection) { + if (dm->collection == this) + return dm->index; + return -1; + } + dm->collection = this; + dm->index = index; + dm->name = material->objectName(); + dm->used = false; + + // Add the material to this collection. + material->setParent(this); + d->materials.append(material); + if (!dm->name.isEmpty()) + d->materialNames[dm->name] = index; + connect(material, SIGNAL(destroyed()), this, SLOT(materialDeleted())); + return index; +} + +/*! + Removes all instances of \a material from this collection. + The \a material object is not deleted and can be reused. + + Does nothing if \a material is null or not a member of + this collection. + + \sa addMaterial() +*/ +void QGLMaterialCollection::removeMaterial(QGLMaterial *material) +{ + Q_D(QGLMaterialCollection); + if (!material) + return; + + // Check the material's owning collection. + QGLMaterialPrivate *dm = material->d_func(); + if (dm->collection != this) + return; + + // Remove the material from the collection. + d->materials[dm->index] = 0; + if (!dm->name.isEmpty()) + d->materialNames.remove(dm->name); + material->setParent(0); + + // Detach from the owning collection. + dm->collection = 0; + dm->index = -1; +} + +/*! + Removes the material at \a index from this collection, and returns + a pointer to the material. + + Since the collection is designed for fast lookup by index, the + the stored material pointer is set to null but the \a index + otherwise remains valid. +*/ +QGLMaterial *QGLMaterialCollection::removeMaterial(int index) +{ + Q_D(QGLMaterialCollection); + + // Bail out if the material is invalid. + if (index < 0 || index >= d->materials.count()) + return 0; + QGLMaterial *material = d->materials[index]; + if (!material) + return 0; + + // Remove the material from the collection. + QGLMaterialPrivate *dm = material->d_func(); + d->materials[index] = 0; + if (!dm->name.isEmpty()) + d->materialNames.remove(dm->name); + material->setParent(0); + + // Detach from the owning collection. + dm->collection = 0; + dm->index = -1; + return material; +} + +/*! + Returns true if this collection is empty, false otherwise. + + \sa size() +*/ +bool QGLMaterialCollection::isEmpty() const +{ + Q_D(const QGLMaterialCollection); + return d->materials.isEmpty(); +} + +/*! + Returns the number of (possibly null) materials in this collection. + Null materials result from calling removeMaterial(). + + \sa isEmpty() +*/ +int QGLMaterialCollection::size() const +{ + Q_D(const QGLMaterialCollection); + return d->materials.size(); +} + +/*! + \internal + Responds to the destroyed() signal by calling removeMaterial() on the + material about to be deleted; +*/ +void QGLMaterialCollection::materialDeleted() +{ + removeMaterial(qobject_cast<QGLMaterial *>(sender())); +} diff --git a/src/threed/geometry/qglmaterialcollection.h b/src/threed/geometry/qglmaterialcollection.h new file mode 100644 index 000000000..52ec79bec --- /dev/null +++ b/src/threed/geometry/qglmaterialcollection.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLMATERIALCOLLECTION_H +#define QGLMATERIALCOLLECTION_H + +#include <QtCore/qobject.h> + +#include "qt3dglobal.h" +#include "qglmaterial.h" +#include "qgltexture2d.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLMaterialCollectionPrivate; + +class Q_QT3D_EXPORT QGLMaterialCollection : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGLMaterialCollection) + Q_DISABLE_COPY(QGLMaterialCollection) +public: + QGLMaterialCollection(QObject *parent = 0); + virtual ~QGLMaterialCollection(); + + QGLMaterial *material(int index) const; + QGLMaterial *material(const QString &name) const; + + bool contains(QGLMaterial *material) const; + bool contains(const QString &name) const; + + int indexOf(QGLMaterial *material) const; + int indexOf(const QString &name) const; + + QString materialName(int index) const; + + bool isMaterialUsed(int index) const; + void markMaterialAsUsed(int index); + void removeUnusedMaterials(); + + int addMaterial(QGLMaterial *material); + void removeMaterial(QGLMaterial *material); + QGLMaterial *removeMaterial(int index); + + bool isEmpty() const; + int size() const; + +private Q_SLOTS: + void materialDeleted(); + +private: + QScopedPointer<QGLMaterialCollectionPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGLMATERIALCOLLECTION_H diff --git a/src/threed/geometry/qglsection.cpp b/src/threed/geometry/qglsection.cpp new file mode 100644 index 000000000..3775af4ee --- /dev/null +++ b/src/threed/geometry/qglsection.cpp @@ -0,0 +1,696 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglsection_p.h" +#include "qglbuilder_p.h" +#include "qarray.h" +#include "qvector_utils_p.h" + +#include <QtGui/qvector3d.h> +#include <QtCore/qdebug.h> +#include <QtCore/qpointer.h> +#include <QtCore/qmap.h> +#include <QtCore/qbitarray.h> + +#include <limits.h> + +/*! + \internal + \class QGLSection + \brief The QGLSection class clusters like geometry in a QGLBuilder. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::geometry + + QGLSection instances partition a QGLBuilder into related sections, + while the builder is being initialized with geometry data. + + Once the builder is initialized, and geometry building is complete + the QGLSection instances are destroyed and the data is uploaded to the + graphics hardware. + + The QGLSection class is a work horse for the QGLBuilder, and it + takes care of automatically managing vertex data. As such + for usual use cases, its functionality will not need to be referenced + directly. For low-level access to geometry, QGLSection provides a + range of accessors to reference geometry data during scene building. + + Within a section, incoming geometry data will be coalesced and + indexes created to reference the fewest possible copies of the vertex + data. For example, in smooth geometry all copies of a vertex are + coalesced into one, and referenced by indices - except in the case + where texture data forms a \i seam and a copy must be created to carry + the two texture coordinates of the seam. + + This is handled automatically by QGLSection, to pack data into the + smallest space possible thus improving cache coherence and performance. + + All the vertices in a QGLSection are treated with the same + \l{QGL::Smoothing}{smoothing}, and have the same + \l{QLogicalVertex::Type}{data types}. + + Each QGLSection references a contiguous range of vertices in a + QGLBuilder. + + A QGLBuilder instance has the \l{QGLBuilder::newSection()}{newSection()} + function which creates a new QGLSection to reference its data. Use this + to construct new QGLSection instances, or alternatively construct + a new QGLSection() and pass a non-null QGLBuilder pointer. + + These functions all return QVector values. QVector instances are + implicitly shared, thus the copies are inexpensive unless a + non-const function is called on them, triggering a copy-on-write. + + Generally for adding geometry, use append(). This function simply + calls virtual protected functions appendSmooth() (for smoothed vertices) + and appendFaceted() (for faceted vertices). See QGLBuilder for a + discussion of smoothing. +*/ + +// allow QVector3D's to be stored in a QMap +inline bool operator<(const QVector3D &a, const QVector3D &b) +{ + if (qFskCompare(a.x(), b.x())) + { + if (qFskCompare(a.y(), b.y())) + { + if (qFskCompare(a.z(), b.z())) + { + return false; // equal so not less-than + } + else + { + return a.z() < b.z(); + } + } + else + { + return a.y() < b.y(); + } + } + else + { + return a.x() < b.x(); + } +} + +static inline bool qSameDirection(const QVector3D &a , const QVector3D &b) +{ + bool res = false; + if (!a.isNull() && !b.isNull()) + { + float dot = QVector3D::dotProduct(a, b); + res = qFskCompare((qreal)dot, a.length() * b.length()); + } + return res; +} + +class QGLSectionPrivate +{ +public: + QGLSectionPrivate(const QVector3DArray *ary) + : vec_data(ary) + , it(vec_map.end()) + , map_threshold(5) + , number_mapped(0) + , start_ptr(-1) + , end_ptr(-1) + { + normIndices.fill(-1, 32); + } + + ~QGLSectionPrivate() {} + + bool normalAccumulated(int index, const QVector3D &norm) const + { + if (index >= normIndices.size()) + return false; + int ptr = normIndices.at(index); + while (ptr != -1) + { + int val_ptr = normPtrs.at(ptr); + //if (normValues.at(val_ptr) == norm) + if (qSameDirection(normValues.at(val_ptr), norm)) + return true; + ptr = normPtrs.at(ptr+1); + } + return false; + } + + void accumulateNormal(int index, const QVector3D &norm) + { + int new_norm_index = normValues.size(); + normValues.append(norm); + if (normIndices.size() <= index) + { + int old_size = normIndices.size(); + normIndices.extend(32); + for (int i = old_size; i < normIndices.size(); ++i) + normIndices[i] = -1; + } + int new_norm_ptr = normPtrs.size(); + normPtrs.append(new_norm_index); // even ptrs point to vector value + normPtrs.append(-1); // odd ptrs point to next in normPtr linked list + if (normIndices.at(index) == -1) + { + normIndices[index] = new_norm_ptr; + } + else + { + int norm_ptr = normIndices.at(index); + while (normPtrs.at(norm_ptr + 1) != -1) + { + norm_ptr = normPtrs.at(norm_ptr + 1); + } + normPtrs[norm_ptr+1] = new_norm_ptr; + } + } + + void mapVertex(const QVector3D &v, int ix) + { + Q_UNUSED(ix); + Q_UNUSED(v); + static bool seeded = false; + if (!seeded) + qsrand(31415); + Q_ASSERT(vec_data->at(ix) == v); + if ((vec_data->size() - number_mapped) > map_threshold) + { + int to_map = vec_data->size() - number_mapped; + QArray<int, 100> shuffle(to_map, -1); + for (int i = number_mapped, k = 0; i < vec_data->size(); ++i, ++k) + shuffle[k] = i; + for (int n = to_map; n > 1; --n) + { + int k = qrand() % n; + int tmp = shuffle[k]; + shuffle[k] = shuffle[n - 1]; + shuffle[n - 1] = tmp; + } + for (int i = 0; i < to_map; ++i) + vec_map.insertMulti(vec_data->at(shuffle.at(i)), shuffle.at(i)); + number_mapped += to_map; + } + } + + int nextIndex() + { + int result = -1; + if (end_ptr != -1) + { + // first look through the unmapped items + while (start_ptr <= end_ptr && result == -1) + { + // search from the end and beginning, favouring the end - most often + // its in the last few we added, sometimes in the first ones + if (qFskCompare(vec_data->at(end_ptr--), target)) + result = end_ptr+1; + else if (start_ptr <= end_ptr && qFskCompare(vec_data->at(end_ptr--), target)) + result = end_ptr+1; + else if (start_ptr <= end_ptr && qFskCompare(vec_data->at(start_ptr++), target)) + result = start_ptr-1; + } + // if that found nothing, have a look at the map + if (result == -1) + { + start_ptr = -1; + end_ptr = -1; + it = vec_map.constEnd(); + if (vec_map.size() > 0) + it = vec_map.find(target); + } + } + if (it != vec_map.constEnd()) + { + // if there was something in the map see if its still on target + if (qFskCompare(it.key(), target)) + { + result = it.value(); + ++it; // increment to find more insertMulti instances + } + else + { + // not on target - flag that we're done here + it = vec_map.constEnd(); + } + } + return result; + } + + int findVertex(const QVector3D &v) + { + end_ptr = vec_data->size() - 1; // last one not in QMap + start_ptr = number_mapped; // first one not in QMap + target = v; + return nextIndex(); + } + + // mapper + int index; + QVector3D target; + const QVector3DArray *vec_data; + QMap<QVector3D, int> vec_map; + QMap<int, int> index_map; + QMap<QVector3D,int>::const_iterator it; + int map_threshold; // if more than this is unmapped, do a mapping run + int number_mapped; // how many vertices have been mapped + int start_ptr; + int end_ptr; + + QArray<int, 32> normIndices; + QArray<int, 32> normPtrs; + QArray<QVector3D, 32> normValues; + + QList<QGLSceneNode*> nodes; +}; + +/*! + \internal + Construct a new QGLSection on \a builder, and with smoothing \a s. + By default the smoothing is QGL::Smooth. + + See QGLBuilder for a discussion of smoothing. + + The pointer \a list must be non-null, and in debug mode, unless QT_NO_DEBUG is + defined, this function will assert if \a list is null. + + The following lines of code have identical effect: + \code + QGLSection *s = myDisplayList->newSection(QGL::Faceted); + QGLSection *s2 = new QGLSection(myDisplayList, QGL::Faceted); + \endcode +*/ +QGLSection::QGLSection(QGLBuilder *builder, QGL::Smoothing s) + : m_smoothing(s) + , d(0) +{ + Q_ASSERT(builder); + enableField(QGL::Position); + Q_ASSERT(vertexData()); + d = new QGLSectionPrivate(vertexData()); + builder->addSection(this); +} + +/*! + \internal + Destroy this QGLSection, recovering any resources. +*/ +QGLSection::~QGLSection() +{ + delete d; +} + +/*! + \internal + Reserve capacity for \a amount items. This may avoid realloc + overhead when a large number of items will be appended. +*/ +void QGLSection::reserve(int amount) +{ + QGeometryData::reserve(amount); + d->normIndices.reserve(amount); + d->normPtrs.reserve(amount * 2); + d->normValues.reserve(amount); +} + +/*! + \internal + Adds the logical vertices \a a, \a b and \c to this section. All + should have the same fields. This function is exactly equivalent to + \code + append(a); append(b); append(c); + \endcode + + \sa appendSmooth(), appendFaceted() +*/ +void QGLSection::append(const QLogicalVertex &a, const QLogicalVertex &b, const QLogicalVertex &c) +{ + Q_ASSERT(a.fields() == b.fields() && b.fields() == c.fields()); + if (!a.hasField(QGL::Normal)) + { + appendFaceted(a, b, c); + } + else + { + if (m_smoothing == QGL::Smooth) + appendSmooth(a, b, c); + else + appendFaceted(a, b, c); + } +} + +/*! + \internal + Adds the logical vertex \a lv to this section. + + Otherwise, if the \a lv does have a lighting normal; then the + vertex processing depends on the smoothing property of this section. + If this section has smoothing QGL::Smooth, then the append will be done + by calling appendSmooth(); or if this section has smoothing QGL::Faceted, + then the append will be done by calling appendFaceted(). + + \sa appendSmooth(), appendFaceted() +*/ +void QGLSection::append(const QLogicalVertex &lv) +{ + if (!lv.hasField(QGL::Normal)) + { + appendFaceted(lv); + } + else + { + if (m_smoothing == QGL::Smooth) + appendSmooth(lv); + else + appendFaceted(lv); + } +} + +static bool qCompareByAttributes(const QLogicalVertex &a, const QLogicalVertex &b) +{ + static const quint32 ATTRS_AND_TEXTURES = (0xFFFFFFFF << QGL::TextureCoord0); + quint32 af = a.fields() & ATTRS_AND_TEXTURES; + quint32 bf = b.fields() & ATTRS_AND_TEXTURES; + if (af != bf) + return false; + quint32 flds = af | bf; + const quint32 mask = 0x01; + flds >>= QGL::TextureCoord0; + for (int i = QGL::TextureCoord0; flds; ++i, flds >>= 1) + { + if (flds & mask) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(i); + if (attr < QGL::CustomVertex0) + { + if (!qFskCompare(a.texCoord(attr), b.texCoord(attr))) + return false; + } + else + { + QVariant v1 = a.attribute(attr); + QVariant v2 = b.attribute(attr); + if (v1.type() == (QVariant::Type)QMetaType::Float) + return qFskCompare(v1.toFloat(), v2.toFloat()); + else if (v1.type() == QVariant::Vector2D) + return qFskCompare(qVariantValue<QVector2D>(v1), qVariantValue<QVector2D>(v2)); + else if (v1.type() == QVariant::Vector3D) + return qFskCompare(qVariantValue<QVector3D>(v1), qVariantValue<QVector3D>(v2)); + else + return v1 == v2; + } + } + } + return true; +} + +int QGLSection::appendOne(const QLogicalVertex &lv) +{ +#ifndef QT_NO_DEBUG_STREAM + if (count() && lv.fields() != fields()) + { + qDebug() << "Warning: adding" << lv << "fields:" << lv.fields() + << "fields do not match existing:" << fields() + << "create new section first?"; + } +#endif + int index = appendVertex(lv); + d->mapVertex(lv.vertex(), index); + appendIndex(index); + return index; +} + +/*! + \internal + Adds the logical vertex \a lv to this section of a builder. + + Two QLogicalVertex instances a and b are treated as being duplicates for + the purpose of smoothing, if \c{qFuzzyCompare(a.vertex(), b.vertex())} is + true + + All duplicate occurrences of a vertex are coalesced, that is replaced + by a GL index referencing the one copy. + + In order to draw \a lv as part of a smooth continuous surface, with + no distinct edge, duplicates of vertex \a lv are coalesced into one + (within this section) and the normal for that one set to the average of + the incoming unique normals. + + The incoming vertex \a lv is not treated as a duplicate if \a lv has + different texture coordinates or attributes. This occurs for example + in the case of a texture seam, where two different texture coordinates + are required at the same point on the geometry. + + In that case a new duplicate vertex is added to carry the unique + texture coordinates or attributes. When new vertex copies are added in + this way all copies receive the averaged normals. + + Call this function to add the vertices of a smooth face to the section + of a builder, or use: + + \code + myDisplayList->newSection(QGLBuilder::Smooth); + myDisplayList->addTriangle(a, b, c); + \endcode + + In smooth surfaces, the vertex and its normal is only sent to the + graphics hardware once (not once per face), thus smooth geometry may + consume fewer resources. + + \sa appendFaceted(), updateTexCoord(), QGLBuilder::newSection() +*/ +void QGLSection::appendSmooth(const QLogicalVertex &lv) +{ + Q_ASSERT(lv.hasField(QGL::Position)); + Q_ASSERT(lv.hasField(QGL::Normal)); + + int found_index = d->findVertex(lv.vertex()); + bool coalesce = false; + if (found_index == -1) + { + int newIndex = appendOne(lv); + d->accumulateNormal(newIndex, lv.normal()); + } + else + { + while (!coalesce && found_index != -1) + { + if (qCompareByAttributes(lv, logicalVertexAt(found_index))) + coalesce = true; + else + found_index = d->nextIndex(); + } + if (!coalesce) // texture or attributes prevented coalesce + { + // new vert to carry tex/attrib data + d->accumulateNormal(appendOne(lv), lv.normal()); + } + else + { + appendIndex(found_index); + while (found_index != -1) + { + if (!d->normalAccumulated(found_index, lv.normal())) + { + normal(found_index) += lv.normal(); + d->accumulateNormal(found_index, lv.normal()); + } + found_index = d->nextIndex(); + } + } + } +} + + +void QGLSection::appendSmooth(const QLogicalVertex &lv, int index) +{ + Q_ASSERT(lv.hasField(QGL::Position)); + Q_ASSERT(lv.hasField(QGL::Normal)); + + int found_index = -1; + QMap<int, int>::const_iterator it = d->index_map.constFind(index); + if (it != d->index_map.constEnd()) + found_index = it.value(); + if (found_index == -1) + { + int newIndex = appendVertex(lv); + d->index_map.insert(index, newIndex); + appendIndex(newIndex); + d->accumulateNormal(newIndex, lv.normal()); + } + else + { + appendIndex(found_index); + if (!d->normalAccumulated(found_index, lv.normal())) + { + normal(found_index) += lv.normal(); + d->accumulateNormal(found_index, lv.normal()); + } + } +} + +/*! + \internal + Add the logical vertex \a lv to this section of a builder. + + The vertex will be drawn as a distinct edge, instead of just part of a + continuous smooth surface. To acheive this the vertex value of \a lv + is duplicated for each unique normal in the current section. + + Note that duplication is only for unique normals: if a vertex is already + present with a given normal it is coalesced and simply referenced by index. + As for appendSmooth() vertices are not coalesced in this way if \a lv + has a different texture coordinate or attribute than its duplicate. + + In faceted surfaces, the vertex is sent to the graphics hardware once for + each normal it has, and thus may consume more resources. + + \sa appendSmooth(), updateTexCoord(), QGLBuilder::newSection() +*/ +void QGLSection::appendFaceted(const QLogicalVertex &lv) +{ + Q_ASSERT(lv.hasField(QGL::Position)); + int found_index = d->findVertex(lv.vertex()); + bool coalesce = false; + while (!coalesce && found_index != -1) + { + if (logicalVertexAt(found_index) == lv) + coalesce = true; + else + found_index = d->nextIndex(); + } + if (coalesce) // found + { + appendIndex(found_index); + } + else + { + appendOne(lv); + } +} + +/*! + \internal + Returns the current map threshold for this section. The threshold is the + point at which the section switches from using a plain QArray - with + linear performance ie O(n) - to using a QMap - with approx O(log n). These + structures are used for looking up vertices during the index generation and + normals calculation. + + The default value is 50. + + \sa setMapThreshold() +*/ +int QGLSection::mapThreshold() const +{ + return d->map_threshold; +} + +/*! + \internal + Sets the current map threshold to \a t for this section. + + \sa mapThreshold() +*/ +void QGLSection::setMapThreshold(int t) +{ + d->map_threshold = t; +} + +/*! + \internal + Returns a list of the QGLSceneNode instances associated with this section. +*/ +QList<QGLSceneNode*> QGLSection::nodes() const +{ + return d->nodes; +} + +/*! + \internal + Adds the \a node to the list of QGLSceneNode instances associated with + this section. +*/ +void QGLSection::addNode(QGLSceneNode *node) +{ + d->nodes.append(node); +} + +/*! + \internal + Deletes the \a node from the list of QGLSceneNode instances associated + with this section. Returns true if the \a node was found, false + otherwise. +*/ +bool QGLSection::deleteNode(QGLSceneNode *node) +{ + int ix = d->nodes.indexOf(node); + if (ix != -1) + { + d->nodes.removeAt(ix); + return true; + } + return false; +} + +#ifndef QT_NO_DEBUG_STREAM +/*! + \internal + Output a string representation of \a section to a debug stream \a dbg. + \relates QGLSection +*/ +QDebug operator<<(QDebug dbg, const QGLSection §ion) +{ + dbg.space() + << "QGLSection(" << §ion + << "- count:" << section.count() + << "- smoothing mode:" << (section.smoothing() == QGL::Smooth ? + "QGL::Smooth" : "QGL::Faceted") << "\n"; + QGL::IndexArray indices = section.indices(); + for (int i = 0; i < section.count(); ++i) + { + int ix = indices[i]; + dbg << section.logicalVertexAt(ix) << "\n"; + } + dbg << ")\n"; + return dbg.space(); +} +#endif diff --git a/src/threed/geometry/qglsection_p.h b/src/threed/geometry/qglsection_p.h new file mode 100644 index 000000000..ea48ff1b1 --- /dev/null +++ b/src/threed/geometry/qglsection_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLSECTION_H +#define QGLSECTION_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglpainter.h" +#include "qlogicalvertex.h" +#include "qbox3d.h" +#include "qglnamespace.h" + +#include <QtOpenGL/qgl.h> +#include <QtGui/qmatrix4x4.h> + +QT_BEGIN_NAMESPACE + +class QGLPainter; +class QGLBuilder; +class QGLSectionPrivate; +class QGeometryData; +class QGLSceneNode; + +class Q_QT3D_EXPORT QGLSection : public QGeometryData +{ +public: + QGLSection(QGLBuilder *d, QGL::Smoothing sm = QGL::Smooth); + ~QGLSection(); + + void reserve(int amount); + + void append(const QLogicalVertex &lv); + void append(const QLogicalVertex &a, const QLogicalVertex &b, const QLogicalVertex &c); + void appendSmooth(const QLogicalVertex &lv); + void appendSmooth(const QLogicalVertex &lv, int index); + void appendSmooth(const QLogicalVertex &a, const QLogicalVertex &b, const QLogicalVertex &c) + { + appendSmooth(a); + appendSmooth(b); + appendSmooth(c); + } + void appendFaceted(const QLogicalVertex &lv); + void appendFaceted(const QLogicalVertex &a, const QLogicalVertex &b, const QLogicalVertex &c) + { + appendFaceted(a); + appendFaceted(b); + appendFaceted(c); + } + + inline QGL::Smoothing smoothing() const; + inline void setSmoothing(QGL::Smoothing s); + int mapThreshold() const; + void setMapThreshold(int); + QList<QGLSceneNode*> nodes() const; + void addNode(QGLSceneNode *node); + bool deleteNode(QGLSceneNode *node); +private: + Q_DISABLE_COPY(QGLSection); + friend class QGLBuilder; + + int appendOne(const QLogicalVertex &vertex); + + QGL::Smoothing m_smoothing; + QGLSectionPrivate *d; +}; + +inline QGL::Smoothing QGLSection::smoothing() const +{ + return m_smoothing; +} + +inline void QGLSection::setSmoothing(QGL::Smoothing s) +{ + m_smoothing = s; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QGLSection §ion); +#endif + +QT_END_NAMESPACE + +#endif // QGLSECTION_H diff --git a/src/threed/geometry/qglsphere.cpp b/src/threed/geometry/qglsphere.cpp new file mode 100644 index 000000000..9a8976ac3 --- /dev/null +++ b/src/threed/geometry/qglsphere.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglsphere.h" +#include "qglbuilder.h" +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLSphere + \brief The QGLSphere class represents the geometry of a simple sphere in 3D space. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::geometry + + The following example creates a sphere of 2 units in diameter and + draws it at (10, 25, 0) in a QGLPainter: + + \code + QGLBuilder builder; + builder << QGLSphere(2); + QGLSceneNode *node = builder.finalizedSceneNode(); + + painter.translate(10, 25, 0); + node->draw(&painter); + \endcode + + The QGLSphere class specifies positions, normals and 2D texture + co-ordinates for all of the vertices that make up the sphere. + + The texture co-ordinates are fixed at construction time. This + is because constructing the sphere can involve generating additional + vertices which need to interpolate the texture co-ordinates of their + neighboring vertices. + + The default mode of QGLSphere is a "UV sphere", which divides the + sphere up into longitudinal and latitudinal sections. The longitudinal + slices meet at the poles, which in a single unit sphere are defined to + be at (0, 0, +0.5) and (0, 0, -0.5). This choice is the simplest to + texture map as the texture will only distort along the x-axis of the + 2D texture. However the density of vertices is significantly higher at + the poles than it is elsewhere on the sphere and is a poor choice if a + uniform density of pixels from the texture map is required. + + \sa QGLBuilder +*/ + +/*! + \fn QGLSphere::QGLSphere(qreal diameter, int depth) + + Creates a sphere of \a diameter across (default is 1). When the sphere + is recursively subdivided into triangles, it will be subdivided no more + than \a depth times (between 1 and 10, default is 5). +*/ + +/*! + Destroys this sphere object. +*/ +QGLSphere::~QGLSphere() +{ +} + +/*! + \fn qreal QGLSphere::diameter() const + + Returns the diameter of this sphere. The default is 1. + + \sa setDiameter() +*/ + +/*! + \fn void QGLSphere::setDiameter(qreal diameter) + + Sets the diameter of this sphere to \a diameter. + + \sa diameter() +*/ + +/*! + \fn int QGLSphere::subdivisionDepth() const + + Returns the maximum depth when this sphere is subdivided into triangles. + The default is 5. The following picture shows the effect of depth + values between 1 and 10 for a UV sphere: + + \image sphere-detail.png + + \table + \header \o Level of Detail \o Number of Triangles + \row \o 1 \o 64 + \row \o 2 \o 128 + \row \o 3 \o 256 + \row \o 4 \o 512 + \row \o 5 \o 1024 + \row \o 6 \o 2048 + \row \o 7 \o 4096 + \row \o 8 \o 8192 + \row \o 9 \o 16384 + \row \o 10 \o 32768 + \endtable + + \sa setSubdivisionDepth() +*/ + +/*! + \fn void QGLSphere::setSubdivisionDepth(int depth) + + Sets the maximum \a depth when this sphere is subdivided into triangles. + + \sa subdivisionDepth() +*/ + +/*! + \relates QGLSphere + + Builds the geometry for \a sphere within the specified + geometry \a builder. +*/ +QGLBuilder& operator<<(QGLBuilder& builder, const QGLSphere& sphere) +{ + qreal radius = sphere.diameter() / 2.0f; + + // Determine the number of slices and stacks to generate. + static int const slicesAndStacks[] = { + 8, 4, + 8, 8, + 16, 8, + 16, 16, + 32, 16, + 32, 32, + 64, 32, + 64, 64, + 128, 64, + 128, 128 + }; + int divisions = sphere.subdivisionDepth(); + if (divisions < 1) + divisions = 1; + else if (divisions > 10) + divisions = 10; + int stacks = slicesAndStacks[divisions * 2 - 1]; + int slices = slicesAndStacks[divisions * 2 - 2]; + + // Precompute sin/cos values for the slices and stacks. + const int maxSlices = 128 + 1; + const int maxStacks = 128 + 1; + qreal sliceSin[maxSlices]; + qreal sliceCos[maxSlices]; + qreal stackSin[maxStacks]; + qreal stackCos[maxStacks]; + for (int slice = 0; slice < slices; ++slice) { + qreal angle = 2 * M_PI * slice / slices; + sliceSin[slice] = qFastSin(angle); + sliceCos[slice] = qFastCos(angle); + } + sliceSin[slices] = sliceSin[0]; // Join first and last slice. + sliceCos[slices] = sliceCos[0]; + for (int stack = 0; stack <= stacks; ++stack) { + qreal angle = M_PI * stack / stacks; + stackSin[stack] = qFastSin(angle); + stackCos[stack] = qFastCos(angle); + } + stackSin[0] = 0.0f; // Come to a point at the poles. + stackSin[stacks] = 0.0f; + + // Create the stacks. + for (int stack = 0; stack < stacks; ++stack) { + QGeometryData prim; + qreal z = radius * stackCos[stack]; + qreal nextz = radius * stackCos[stack + 1]; + qreal s = stackSin[stack]; + qreal nexts = stackSin[stack + 1]; + qreal c = stackCos[stack]; + qreal nextc = stackCos[stack + 1]; + qreal r = radius * s; + qreal nextr = radius * nexts; + for (int slice = 0; slice <= slices; ++slice) { + prim.appendVertex + (QVector3D(nextr * sliceSin[slice], + nextr * sliceCos[slice], nextz)); + prim.appendNormal + (QVector3D(sliceSin[slice] * nexts, + sliceCos[slice] * nexts, nextc)); + prim.appendTexCoord + (QVector2D(1.0f - qreal(slice) / slices, + 1.0f - qreal(stack + 1) / stacks)); + + prim.appendVertex + (QVector3D(r * sliceSin[slice], + r * sliceCos[slice], z)); + prim.appendNormal + (QVector3D(sliceSin[slice] * s, + sliceCos[slice] * s, c)); + prim.appendTexCoord + (QVector2D(1.0f - qreal(slice) / slices, + 1.0f - qreal(stack) / stacks)); + } + builder.addQuadStrip(prim); + } + + return builder; +} + +QT_END_NAMESPACE diff --git a/src/threed/geometry/qglsphere.h b/src/threed/geometry/qglsphere.h new file mode 100644 index 000000000..784f9db4c --- /dev/null +++ b/src/threed/geometry/qglsphere.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLSPHERE_H +#define QGLSPHERE_H + +#include "qt3dglobal.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLBuilder; + +class Q_QT3D_EXPORT QGLSphere +{ +public: + explicit QGLSphere(qreal diameter = 1.0f, int depth = 5) + : m_diameter(diameter), m_subdivisionDepth(depth) {} + virtual ~QGLSphere(); + + qreal diameter() const { return m_diameter; } + void setDiameter(qreal diameter) { m_diameter = diameter; } + + int subdivisionDepth() const { return m_subdivisionDepth; } + void setSubdivisionDepth(int depth) { m_subdivisionDepth = depth; } + +private: + qreal m_diameter; + int m_subdivisionDepth; +}; + +Q_QT3D_EXPORT QGLBuilder& operator<<(QGLBuilder& builder, const QGLSphere& sphere); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/geometry/qglteapot.cpp b/src/threed/geometry/qglteapot.cpp new file mode 100644 index 000000000..fb2419dce --- /dev/null +++ b/src/threed/geometry/qglteapot.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglteapot.h" +#include "qglteapot_data_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLTeapot + \brief The QGLTeapot class represents a 3D teapot object. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::geometry + + The classic 3D "Utah Teapot" was originally drawn by Martin Newell + in 1975. The vertex data was made publicly available by him and + it has been a standard 3D test object ever since. + + For more information on the history of the "Utah Teapot", see + Wikipedia, http://en.wikipedia.org/wiki/Utah_teapot. + + The following example code uses QGLTeapot to draw a teapot of size + 0.5 at the origin: + + \code + QGLBuilder builder; + builder << QGLTeapot(); + teapot = builder.finalizedSceneNode(); + + painter.modelViewMatrix().scale(0.5f); + teapot->draw(painter); + \endcode + + The QGLTeapot object contains a lot of vertex data once it has + been subdivided into triangles. It is recommended that instances + of this class be created at startup, added to a QGLBuilder, + and then the finalized scene node can be reused over and over. + + \sa QGLBezierPatches +*/ + +/*! + Constructs a new 3D teapot geometry object. +*/ +QGLTeapot::QGLTeapot() +{ + QVector3DArray positions; + for (int pindex = 0; pindex < teapotPatchCount * 16; ++pindex) { + int vindex = teapotPatchData[pindex]; + positions.append(teapotBezierVertexData[vindex * 3], + teapotBezierVertexData[vindex * 3 + 1], + teapotBezierVertexData[vindex * 3 + 2]); + } + setPositions(positions); + setSubdivisionDepth(teapotDepth); +} + +/*! + Destroys this teapot geometry object. +*/ +QGLTeapot::~QGLTeapot() +{ +} + +QT_END_NAMESPACE diff --git a/src/threed/geometry/qglteapot.h b/src/threed/geometry/qglteapot.h new file mode 100644 index 000000000..290ff23d9 --- /dev/null +++ b/src/threed/geometry/qglteapot.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLTEAPOT_H +#define QGLTEAPOT_H + +#include "qglbezierpatches.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QGLTeapot : public QGLBezierPatches +{ +public: + QGLTeapot(); + ~QGLTeapot(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/geometry/qglteapot_data_p.h b/src/threed/geometry/qglteapot_data_p.h new file mode 100644 index 000000000..28dbfa7ac --- /dev/null +++ b/src/threed/geometry/qglteapot_data_p.h @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLTEAPOT_DATA_P_H +#define QGLTEAPOT_DATA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglbezierpatches.h" + +// Generated from teapot.txt by meshcvt, depth = 4 + +#define teapotBezierVertexCount 306 +#define teapotBezierVertexStride 3 +#define teapotPatchCount 32 +#define teapotDepth 4 +static float const teapotBezierVertexData[] = { + 0.700000f, 0.450000f, -0.000000f, + 0.700000f, 0.450000f, 0.392000f, + 0.392000f, 0.450000f, 0.700000f, + 0.000000f, 0.450000f, 0.700000f, + 0.668750f, 0.515625f, -0.000000f, + 0.668750f, 0.515625f, 0.374500f, + 0.374500f, 0.515625f, 0.668750f, + 0.000000f, 0.515625f, 0.668750f, + 0.718750f, 0.515625f, -0.000000f, + 0.718750f, 0.515625f, 0.402500f, + 0.402500f, 0.515625f, 0.718750f, + 0.000000f, 0.515625f, 0.718750f, + 0.750000f, 0.450000f, -0.000000f, + 0.750000f, 0.450000f, 0.420000f, + 0.420000f, 0.450000f, 0.750000f, + 0.000000f, 0.450000f, 0.750000f, + -0.392000f, 0.450000f, 0.700000f, + -0.700000f, 0.450000f, 0.392000f, + -0.700000f, 0.450000f, -0.000000f, + -0.374500f, 0.515625f, 0.668750f, + -0.668750f, 0.515625f, 0.374500f, + -0.668750f, 0.515625f, -0.000000f, + -0.402500f, 0.515625f, 0.718750f, + -0.718750f, 0.515625f, 0.402500f, + -0.718750f, 0.515625f, -0.000000f, + -0.420000f, 0.450000f, 0.750000f, + -0.750000f, 0.450000f, 0.420000f, + -0.750000f, 0.450000f, -0.000000f, + -0.700000f, 0.450000f, -0.392000f, + -0.392000f, 0.450000f, -0.700000f, + 0.000000f, 0.450000f, -0.700000f, + -0.668750f, 0.515625f, -0.374500f, + -0.374500f, 0.515625f, -0.668750f, + 0.000000f, 0.515625f, -0.668750f, + -0.718750f, 0.515625f, -0.402500f, + -0.402500f, 0.515625f, -0.718750f, + 0.000000f, 0.515625f, -0.718750f, + -0.750000f, 0.450000f, -0.420000f, + -0.420000f, 0.450000f, -0.750000f, + 0.000000f, 0.450000f, -0.750000f, + 0.392000f, 0.450000f, -0.700000f, + 0.700000f, 0.450000f, -0.392000f, + 0.374500f, 0.515625f, -0.668750f, + 0.668750f, 0.515625f, -0.374500f, + 0.402500f, 0.515625f, -0.718750f, + 0.718750f, 0.515625f, -0.402500f, + 0.420000f, 0.450000f, -0.750000f, + 0.750000f, 0.450000f, -0.420000f, + 0.875000f, 0.187500f, -0.000000f, + 0.875000f, 0.187500f, 0.490000f, + 0.490000f, 0.187500f, 0.875000f, + 0.000000f, 0.187500f, 0.875000f, + 1.000000f, -0.075000f, -0.000000f, + 1.000000f, -0.075000f, 0.560000f, + 0.560000f, -0.075000f, 1.000000f, + 0.000000f, -0.075000f, 1.000000f, + 1.000000f, -0.300000f, -0.000000f, + 1.000000f, -0.300000f, 0.560000f, + 0.560000f, -0.300000f, 1.000000f, + 0.000000f, -0.300000f, 1.000000f, + -0.490000f, 0.187500f, 0.875000f, + -0.875000f, 0.187500f, 0.490000f, + -0.875000f, 0.187500f, -0.000000f, + -0.560000f, -0.075000f, 1.000000f, + -1.000000f, -0.075000f, 0.560000f, + -1.000000f, -0.075000f, -0.000000f, + -0.560000f, -0.300000f, 1.000000f, + -1.000000f, -0.300000f, 0.560000f, + -1.000000f, -0.300000f, -0.000000f, + -0.875000f, 0.187500f, -0.490000f, + -0.490000f, 0.187500f, -0.875000f, + 0.000000f, 0.187500f, -0.875000f, + -1.000000f, -0.075000f, -0.560000f, + -0.560000f, -0.075000f, -1.000000f, + 0.000000f, -0.075000f, -1.000000f, + -1.000000f, -0.300000f, -0.560000f, + -0.560000f, -0.300000f, -1.000000f, + 0.000000f, -0.300000f, -1.000000f, + 0.490000f, 0.187500f, -0.875000f, + 0.875000f, 0.187500f, -0.490000f, + 0.560000f, -0.075000f, -1.000000f, + 1.000000f, -0.075000f, -0.560000f, + 0.560000f, -0.300000f, -1.000000f, + 1.000000f, -0.300000f, -0.560000f, + 1.000000f, -0.525000f, -0.000000f, + 1.000000f, -0.525000f, 0.560000f, + 0.560000f, -0.525000f, 1.000000f, + 0.000000f, -0.525000f, 1.000000f, + 0.750000f, -0.637500f, -0.000000f, + 0.750000f, -0.637500f, 0.420000f, + 0.420000f, -0.637500f, 0.750000f, + 0.000000f, -0.637500f, 0.750000f, + 0.750000f, -0.675000f, -0.000000f, + 0.750000f, -0.675000f, 0.420000f, + 0.420000f, -0.675000f, 0.750000f, + 0.000000f, -0.675000f, 0.750000f, + -0.560000f, -0.525000f, 1.000000f, + -1.000000f, -0.525000f, 0.560000f, + -1.000000f, -0.525000f, -0.000000f, + -0.420000f, -0.637500f, 0.750000f, + -0.750000f, -0.637500f, 0.420000f, + -0.750000f, -0.637500f, -0.000000f, + -0.420000f, -0.675000f, 0.750000f, + -0.750000f, -0.675000f, 0.420000f, + -0.750000f, -0.675000f, -0.000000f, + -1.000000f, -0.525000f, -0.560000f, + -0.560000f, -0.525000f, -1.000000f, + 0.000000f, -0.525000f, -1.000000f, + -0.750000f, -0.637500f, -0.420000f, + -0.420000f, -0.637500f, -0.750000f, + 0.000000f, -0.637500f, -0.750000f, + -0.750000f, -0.675000f, -0.420000f, + -0.420000f, -0.675000f, -0.750000f, + 0.000000f, -0.675000f, -0.750000f, + 0.560000f, -0.525000f, -1.000000f, + 1.000000f, -0.525000f, -0.560000f, + 0.420000f, -0.637500f, -0.750000f, + 0.750000f, -0.637500f, -0.420000f, + 0.420000f, -0.675000f, -0.750000f, + 0.750000f, -0.675000f, -0.420000f, + -0.800000f, 0.262500f, -0.000000f, + -0.800000f, 0.262500f, 0.150000f, + -0.750000f, 0.375000f, 0.150000f, + -0.750000f, 0.375000f, -0.000000f, + -1.150000f, 0.262500f, -0.000000f, + -1.150000f, 0.262500f, 0.150000f, + -1.250000f, 0.375000f, 0.150000f, + -1.250000f, 0.375000f, -0.000000f, + -1.350000f, 0.262500f, -0.000000f, + -1.350000f, 0.262500f, 0.150000f, + -1.500000f, 0.375000f, 0.150000f, + -1.500000f, 0.375000f, -0.000000f, + -1.350000f, 0.150000f, -0.000000f, + -1.350000f, 0.150000f, 0.150000f, + -1.500000f, 0.150000f, 0.150000f, + -1.500000f, 0.150000f, -0.000000f, + -0.750000f, 0.375000f, -0.150000f, + -0.800000f, 0.262500f, -0.150000f, + -1.250000f, 0.375000f, -0.150000f, + -1.150000f, 0.262500f, -0.150000f, + -1.500000f, 0.375000f, -0.150000f, + -1.350000f, 0.262500f, -0.150000f, + -1.500000f, 0.150000f, -0.150000f, + -1.350000f, 0.150000f, -0.150000f, + -1.350000f, 0.037500f, -0.000000f, + -1.350000f, 0.037500f, 0.150000f, + -1.500000f, -0.075000f, 0.150000f, + -1.500000f, -0.075000f, -0.000000f, + -1.250000f, -0.187500f, -0.000000f, + -1.250000f, -0.187500f, 0.150000f, + -1.325000f, -0.281250f, 0.150000f, + -1.325000f, -0.281250f, -0.000000f, + -1.000000f, -0.300000f, 0.150000f, + -0.950000f, -0.450000f, 0.150000f, + -0.950000f, -0.450000f, -0.000000f, + -1.500000f, -0.075000f, -0.150000f, + -1.350000f, 0.037500f, -0.150000f, + -1.325000f, -0.281250f, -0.150000f, + -1.250000f, -0.187500f, -0.150000f, + -0.950000f, -0.450000f, -0.150000f, + -1.000000f, -0.300000f, -0.150000f, + 0.850000f, -0.037500f, -0.000000f, + 0.850000f, -0.037500f, 0.330000f, + 0.850000f, -0.450000f, 0.330000f, + 0.850000f, -0.450000f, -0.000000f, + 1.300000f, -0.037500f, -0.000000f, + 1.300000f, -0.037500f, 0.330000f, + 1.550000f, -0.337500f, 0.330000f, + 1.550000f, -0.337500f, -0.000000f, + 1.150000f, 0.300000f, -0.000000f, + 1.150000f, 0.300000f, 0.125000f, + 1.200000f, 0.262500f, 0.125000f, + 1.200000f, 0.262500f, -0.000000f, + 1.350000f, 0.450000f, -0.000000f, + 1.350000f, 0.450000f, 0.125000f, + 1.650000f, 0.450000f, 0.125000f, + 1.650000f, 0.450000f, -0.000000f, + 0.850000f, -0.450000f, -0.330000f, + 0.850000f, -0.037500f, -0.330000f, + 1.550000f, -0.337500f, -0.330000f, + 1.300000f, -0.037500f, -0.330000f, + 1.200000f, 0.262500f, -0.125000f, + 1.150000f, 0.300000f, -0.125000f, + 1.650000f, 0.450000f, -0.125000f, + 1.350000f, 0.450000f, -0.125000f, + 1.400000f, 0.487500f, -0.000000f, + 1.400000f, 0.487500f, 0.125000f, + 1.762500f, 0.496875f, 0.125000f, + 1.762500f, 0.496875f, -0.000000f, + 1.450000f, 0.487500f, -0.000000f, + 1.450000f, 0.487500f, 0.075000f, + 1.725000f, 0.506250f, 0.075000f, + 1.725000f, 0.506250f, -0.000000f, + 1.400000f, 0.450000f, -0.000000f, + 1.400000f, 0.450000f, 0.075000f, + 1.600000f, 0.450000f, 0.075000f, + 1.600000f, 0.450000f, -0.000000f, + 1.762500f, 0.496875f, -0.125000f, + 1.400000f, 0.487500f, -0.125000f, + 1.725000f, 0.506250f, -0.075000f, + 1.450000f, 0.487500f, -0.075000f, + 1.600000f, 0.450000f, -0.075000f, + 1.400000f, 0.450000f, -0.075000f, + 0.000000f, 0.825000f, -0.000000f, + 0.000000f, 0.825000f, 0.001000f, + 0.001000f, 0.825000f, -0.000000f, + 0.400000f, 0.825000f, -0.000000f, + 0.400000f, 0.825000f, 0.225000f, + 0.225000f, 0.825000f, 0.400000f, + 0.000000f, 0.825000f, 0.400000f, + 0.000000f, 0.675000f, -0.000000f, + 0.100000f, 0.600000f, -0.000000f, + 0.100000f, 0.600000f, 0.056000f, + 0.056000f, 0.600000f, 0.100000f, + 0.000000f, 0.600000f, 0.100000f, + -0.001000f, 0.825000f, -0.000000f, + -0.225000f, 0.825000f, 0.400000f, + -0.400000f, 0.825000f, 0.225000f, + -0.400000f, 0.825000f, -0.000000f, + -0.056000f, 0.600000f, 0.100000f, + -0.100000f, 0.600000f, 0.056000f, + -0.100000f, 0.600000f, -0.000000f, + 0.000000f, 0.825000f, -0.001000f, + -0.400000f, 0.825000f, -0.225000f, + -0.225000f, 0.825000f, -0.400000f, + 0.000000f, 0.825000f, -0.400000f, + -0.100000f, 0.600000f, -0.056000f, + -0.056000f, 0.600000f, -0.100000f, + 0.000000f, 0.600000f, -0.100000f, + 0.225000f, 0.825000f, -0.400000f, + 0.400000f, 0.825000f, -0.225000f, + 0.056000f, 0.600000f, -0.100000f, + 0.100000f, 0.600000f, -0.056000f, + 0.200000f, 0.525000f, -0.000000f, + 0.200000f, 0.525000f, 0.112000f, + 0.112000f, 0.525000f, 0.200000f, + 0.000000f, 0.525000f, 0.200000f, + 0.650000f, 0.525000f, -0.000000f, + 0.650000f, 0.525000f, 0.364000f, + 0.364000f, 0.525000f, 0.650000f, + 0.000000f, 0.525000f, 0.650000f, + 0.650000f, 0.450000f, -0.000000f, + 0.650000f, 0.450000f, 0.364000f, + 0.364000f, 0.450000f, 0.650000f, + 0.000000f, 0.450000f, 0.650000f, + -0.112000f, 0.525000f, 0.200000f, + -0.200000f, 0.525000f, 0.112000f, + -0.200000f, 0.525000f, -0.000000f, + -0.364000f, 0.525000f, 0.650000f, + -0.650000f, 0.525000f, 0.364000f, + -0.650000f, 0.525000f, -0.000000f, + -0.364000f, 0.450000f, 0.650000f, + -0.650000f, 0.450000f, 0.364000f, + -0.650000f, 0.450000f, -0.000000f, + -0.200000f, 0.525000f, -0.112000f, + -0.112000f, 0.525000f, -0.200000f, + 0.000000f, 0.525000f, -0.200000f, + -0.650000f, 0.525000f, -0.364000f, + -0.364000f, 0.525000f, -0.650000f, + 0.000000f, 0.525000f, -0.650000f, + -0.650000f, 0.450000f, -0.364000f, + -0.364000f, 0.450000f, -0.650000f, + 0.000000f, 0.450000f, -0.650000f, + 0.112000f, 0.525000f, -0.200000f, + 0.200000f, 0.525000f, -0.112000f, + 0.364000f, 0.525000f, -0.650000f, + 0.650000f, 0.525000f, -0.364000f, + 0.364000f, 0.450000f, -0.650000f, + 0.650000f, 0.450000f, -0.364000f, + 0.000000f, -0.750000f, -0.000000f, + 0.750000f, -0.675000f, -0.000000f, + 0.750000f, -0.675000f, -0.420000f, + 0.420000f, -0.675000f, -0.750000f, + 0.000000f, -0.675000f, -0.750000f, + 0.750000f, -0.712500f, -0.000000f, + 0.750000f, -0.712500f, -0.420000f, + 0.420000f, -0.712500f, -0.750000f, + 0.000000f, -0.712500f, -0.750000f, + 0.712500f, -0.750000f, -0.000000f, + 0.712500f, -0.750000f, -0.399000f, + 0.399000f, -0.750000f, -0.712500f, + 0.000000f, -0.750000f, -0.712500f, + -0.420000f, -0.675000f, -0.750000f, + -0.750000f, -0.675000f, -0.420000f, + -0.750000f, -0.675000f, -0.000000f, + -0.420000f, -0.712500f, -0.750000f, + -0.750000f, -0.712500f, -0.420000f, + -0.750000f, -0.712500f, -0.000000f, + -0.399000f, -0.750000f, -0.712500f, + -0.712500f, -0.750000f, -0.399000f, + -0.712500f, -0.750000f, -0.000000f, + -0.750000f, -0.675000f, 0.420000f, + -0.420000f, -0.675000f, 0.750000f, + 0.000000f, -0.675000f, 0.750000f, + -0.750000f, -0.712500f, 0.420000f, + -0.420000f, -0.712500f, 0.750000f, + 0.000000f, -0.712500f, 0.750000f, + -0.712500f, -0.750000f, 0.399000f, + -0.399000f, -0.750000f, 0.712500f, + 0.000000f, -0.750000f, 0.712500f, + 0.420000f, -0.675000f, 0.750000f, + 0.750000f, -0.675000f, 0.420000f, + 0.420000f, -0.712500f, 0.750000f, + 0.750000f, -0.712500f, 0.420000f, + 0.399000f, -0.750000f, 0.712500f, + 0.712500f, -0.750000f, 0.399000f +}; + +static ushort const teapotPatchData[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 3, 16, 17, 18, 7, 19, 20, 21, 11, 22, 23, 24, 15, 25, 26, 27, + 18, 28, 29, 30, 21, 31, 32, 33, 24, 34, 35, 36, 27, 37, 38, 39, + 30, 40, 41, 0, 33, 42, 43, 4, 36, 44, 45, 8, 39, 46, 47, 12, + 12, 13, 14, 15, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 15, 25, 26, 27, 51, 60, 61, 62, 55, 63, 64, 65, 59, 66, 67, 68, + 27, 37, 38, 39, 62, 69, 70, 71, 65, 72, 73, 74, 68, 75, 76, 77, + 39, 46, 47, 12, 71, 78, 79, 48, 74, 80, 81, 52, 77, 82, 83, 56, + 56, 57, 58, 59, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 59, 66, 67, 68, 87, 96, 97, 98, 91, 99, 100, 101, 95, 102, 103, 104, + 68, 75, 76, 77, 98, 105, 106, 107, 101, 108, 109, 110, 104, 111, 112, 113, + 77, 82, 83, 56, 107, 114, 115, 84, 110, 116, 117, 88, 113, 118, 119, 92, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 123, 136, 137, 120, 127, 138, 139, 124, 131, 140, 141, 128, 135, 142, 143, 132, + 132, 133, 134, 135, 144, 145, 146, 147, 148, 149, 150, 151, 68, 152, 153, 154, + 135, 142, 143, 132, 147, 155, 156, 144, 151, 157, 158, 148, 154, 159, 160, 68, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 164, 177, 178, 161, 168, 179, 180, 165, 172, 181, 182, 169, 176, 183, 184, 173, + 173, 174, 175, 176, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 176, 183, 184, 173, 188, 197, 198, 185, 192, 199, 200, 189, 196, 201, 202, 193, + 203, 203, 203, 203, 206, 207, 208, 209, 210, 210, 210, 210, 211, 212, 213, 214, + 203, 203, 203, 203, 209, 216, 217, 218, 210, 210, 210, 210, 214, 219, 220, 221, + 203, 203, 203, 203, 218, 223, 224, 225, 210, 210, 210, 210, 221, 226, 227, 228, + 203, 203, 203, 203, 225, 229, 230, 206, 210, 210, 210, 210, 228, 231, 232, 211, + 211, 212, 213, 214, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 214, 219, 220, 221, 236, 245, 246, 247, 240, 248, 249, 250, 244, 251, 252, 253, + 221, 226, 227, 228, 247, 254, 255, 256, 250, 257, 258, 259, 253, 260, 261, 262, + 228, 231, 232, 211, 256, 263, 264, 233, 259, 265, 266, 237, 262, 267, 268, 241, + 269, 269, 269, 269, 278, 279, 280, 281, 274, 275, 276, 277, 270, 271, 272, 273, + 269, 269, 269, 269, 281, 288, 289, 290, 277, 285, 286, 287, 273, 282, 283, 284, + 269, 269, 269, 269, 290, 297, 298, 299, 287, 294, 295, 296, 284, 291, 292, 293, + 269, 269, 269, 269, 299, 304, 305, 278, 296, 302, 303, 274, 293, 300, 301, 270 +}; + +#endif diff --git a/src/threed/geometry/qlogicalvertex.cpp b/src/threed/geometry/qlogicalvertex.cpp new file mode 100644 index 000000000..e1f892c3d --- /dev/null +++ b/src/threed/geometry/qlogicalvertex.cpp @@ -0,0 +1,416 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlogicalvertex.h" +#include "qvector_utils_p.h" + +#include <QtCore/qdebug.h> + +/*! + \class QLogicalVertex + \brief The QLogicalVertex class references QGeometryData at a single vertex. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::geometry + + QLogicalVertex instances are a convenience class for use with + QGeometryData. A QLogicalVertex simply references through to the data + in a QGeometryData for a particular vertex, providing accessors to fetch + position, texture coordinates, and other values. + + Create a QLogicalVertex referring to a particular QGeometryData instance: + \code + QGeometryData data; + data.appendVertex(QVector3D(1, 2, 3)); + + // construct a QLogicalVertex referring to the first vertex in data + // the QGeometryData is implicitly shared with lv + QLogicalVertex lv(data, 0); + // lv.vertex() == QVector3D(1, 2, 3) + \endcode + This is inexpensive and no new storage is allocated for the actual data, + just the reference and index. + + With logical vertices instances referencing large QGeometryData instances, + avoid modifying the instance: + \code + // careful - assigning to a QLogicalVertex which refers to an external + // QGeometryData will result in a possibly expensive copy-on-write + lv.setVertex(3, 2, 1); + \endcode + + Create a QLogicalVertex with its own QGeometryData internally: + \code + QLogicalVertex lv; + // no copy on write here - internal QGeometryData is not shared + lv.setVertex(1, 2, 3); + \endcode + + Assign an instance of QLogicalVertex: + \code + QLogicalVertex lv2; + lv2 = data.logicalVertexAt(0); + \endcode + Although lv2 gets its own internal QGeometryData which is then immediately + thrown away by the assignment, because of lazy initialization in + QGeometryData the cost is negligible. + + Use the fields() and hasField() functions to determine if a particular + field is present in the vertex. Accessing non-existent data will cause + an assert in debug mode (from the underlying QArray), and give + undefined behaviour in release mode. + + \sa QGeometryData, QGLBuilder +*/ + +/*! + \fn QLogicalVertex::QLogicalVertex() + Constructs a new invalid QLogicalVertex which has no data. +*/ + +/*! + \fn QLogicalVertex::QLogicalVertex(QGeometryData data, int index) + Constructs a new QLogicalVertex referencing the \a data at \a index. + Note that if this QLogicalVertex is modified, by calling vertex() or + setNormal() for example, then a copy-on-write for \a data will be + triggered. +*/ + +/*! + \fn QLogicalVertex::QLogicalVertex(const QVector3D &a) + Constructs a new QLogicalVertex with a vertex set to \a a. +*/ + +/*! + \fn QLogicalVertex::QLogicalVertex(const QVector3D &a, const QVector3D &n); + Constructs a new QLogicalVertex with a vertex set to \a a, and normal set to \a n. +*/ + +/*! + \fn QLogicalVertex::QLogicalVertex(const QVector3D &a, const QVector3D &n, const QVector2D &t) + Constructs a new QLogicalVertex with its vertex value set to \a a, normal set + to \a n, and texture set to \a t. By default \a n is the null QVector3D, + and \a t is the InvalidTexCoord. If \a n is null then hasType(QLogicalVertex::Normal) + will return false. Likewise if \a t is the InvalidTexCoord then + hasType(QLogicalVertex::Texture) will return false. +*/ + +/*! + \fn QLogicalVertex::QLogicalVertex(const QVector3D &a, QColor4ub color) + Constructs a new QLogicalVertex with its vertex value set to \a a, + color value set to \a color. +*/ + +/*! + \fn QLogicalVertex::~QLogicalVertex() + Destroys this QLogicalVertex reclaiming any resources. +*/ + +/*! + \fn const QVector3D &QLogicalVertex::vertex() const + Returns a const reference to the vertex value for this vertex. +*/ + +/*! + \fn void QLogicalVertex::setVertex(const QVector3D &v) + Sets the vertex value for this vertex to \a v. +*/ + +/*! + \fn QVector3D &QLogicalVertex::vertex() + Returns a modifiable reference to the vertex value. +*/ + +/*! + \fn QLogicalVertex::operator QVector3D () + Returns a copy of the vertex value, by casting as a QVector3D. This + allows passing of a QLogicalVertex to functions that expect a QVector3D. +*/ + +/*! + \fn QVariant QLogicalVertex::attribute(QGL::VertexAttribute field) const + Returns the attribute value for \a field. The \a field defaults + to QGL::CustomVertex0. +*/ + +/*! + \fn void QLogicalVertex::setAttribute(float value, QGL::VertexAttribute field) + Sets the float attribute \a value at \a field. The \a field + defaults to QGL::CustomVertex0. +*/ + +/*! + \fn void QLogicalVertex::setAttribute(const QVector2D &v, QGL::VertexAttribute field) + Sets the QVector2D attribute \a v at \a field. The \a field + defaults to QGL::CustomVertex0. +*/ + +/*! + \fn void QLogicalVertex::setAttribute(const QVector3D &v, QGL::VertexAttribute field) + Sets the QVector3D attribute \a v at \a field. The \a field + defaults to QGL::CustomVertex0. +*/ + +/*! + \fn float &QLogicalVertex::floatAttribute(QGL::VertexAttribute field) + Returns a modifiable reference to the attribute at \a field, which + must be a float. The \a field defaults to QGL::CustomVertex0. +*/ + +/*! + \fn QVector2D &QLogicalVertex::vector2DAttribute(QGL::VertexAttribute field) + Returns a modifiable reference to the attribute at \a field, which + must be a QVector2D. The \a field defaults to QGL::CustomVertex0. +*/ + +/*! + \fn QVector3D &QLogicalVertex::vector3DAttribute(QGL::VertexAttribute field = QGL::CustomVertex0); + Returns a modifiable reference to the attribute at \a field, which + must be a QVector3D. The \a field defaults to QGL::CustomVertex0. +*/ + +/*! + \fn float QLogicalVertex::floatAttribute(QGL::VertexAttribute field) const + Returns the attribute at \a field. The \a field defaults to QGL::CustomVertex0. + The attribute must be a float value. +*/ + +/*! + \fn QVector2D QLogicalVertex::vector2DAttribute(QGL::VertexAttribute field) const + Returns the attribute at \a field. The \a field defaults to QGL::CustomVertex0. + The attribute must be a QVector2D value. +*/ + +/*! + \fn QVector3D QLogicalVertex::vector3DAttribute(QGL::VertexAttribute field) const + Returns the attribute at \a field. The \a field defaults to QGL::CustomVertex0. + The attribute must be a QVector3D value. +*/ + +/*! + \fn QCustomDataArray::ElementType QLogicalVertex::attributeType(QGL::VertexAttribute field) + Returns the element type for the attribute \a field. +*/ + +/*! + \fn const QVector3D &QLogicalVertex::normal() const + Returns a const reference to the normal value for this vertex. +*/ + +/*! + \fn void QLogicalVertex::setNormal(const QVector3D &n) + Sets the normal value for this vertex to \a n. +*/ + +/*! + \fn QVector3D &QLogicalVertex::normal() + Returns a modifiable reference to the normal value for this vertex. +*/ + +/*! + \fn QVector2D QLogicalVertex::texCoord(QGL::VertexAttribute field) const + Returns a copy of the texture coordinate value at \a field for this vertex. + The \a field defaults to QGL::TextureCoord0. +*/ + +/*! + \fn void QLogicalVertex::setTexCoord(const QVector2D &t, QGL::VertexAttribute field) + Sets the texture coordinate at \a field for this vertex to \a t. + The \a field defaults to QGL::TextureCoord0. +*/ + +/*! + \fn QVector2D &QLogicalVertex::texCoordRef(QGL::VertexAttribute field) + Returns a modifiable reference to the texture coordinate for this vertex. + The \a field defaults to QGL::TextureCoord0. +*/ + +/*! + \fn QColor4ub QLogicalVertex::color() const + Returns a const reference to the color value for this vertex. +*/ + +/*! + \fn void QLogicalVertex::setColor(const QColor4ub &c) + Sets the color value for this vertex to \a c. +*/ + +/*! + \fn QColor4ub &QLogicalVertex::colorRef() + Returns a modifiable reference to the color value for this vertex. +*/ + +/*! + \fn bool QLogicalVertex::hasField(QGL::VertexAttribute type) const + Returns true if this vertex has data field \a type, and false otherwise. + + In general check to see if a logical vertex has a particular field + type before attempting to access it. In debug mode accessing a + non-existent field will cause an assert; but in release mode the + behaviour is undefined. +*/ + +/*! + \fn quint32 QLogicalVertex::fields() const + Returns a bit-mask of the fields in this logical vertex. Test the + fields like this: + \code + if (vertex.fields() & QGL::fieldMask(QGL::TextureCoord0)) + tex = vertex.texCoord(); + \endcode + + \sa QGeometryData::fields() +*/ + +/*! + \fn int QLogicalVertex::index() const + Returns the index at which this logical vertex's data is located in + its associated QGeometryData; or -1 if this vertex is null. +*/ + +/*! + \fn QGeometryData QLogicalVertex::data() const + Returns a copy of the QGeometryData underlying this vertex. Note that + the copy is not expensive in terms of performance due to implicit sharing + unless the copy is modified (causing a copy-on-write). + + \sa QLogicalVertex::index() +*/ + +/*! + \fn QGeometryData data() const + Returns a copy of the QGeometryData associated with this vertex. Note + that as long as the copy is not modified, this method is not expensive. +*/ + +/*! + \fn bool QLogicalVertex::isNull() const + Returns true if this vertex is null. + + \sa QLogicalVertex() +*/ + +/*! + Returns true if \a rhs has exactly the same fields as this logical + vertex, and each of those are equal to the corresponding field of the \a rhs. + + If either are null, then false is returned. +*/ +bool QLogicalVertex::operator==(const QLogicalVertex &rhs) const +{ + if (isNull() || rhs.isNull()) + return false; + if (this == &rhs) + return true; + if (m_data.fields() != rhs.fields()) + return false; + const quint32 mask = 0x01; + quint32 fields = m_data.fields(); + for (int field = 0; fields; ++field, fields >>= 1) + { + if (mask & fields) + { + QGL::VertexAttribute attr = static_cast<QGL::VertexAttribute>(field); + if (attr < QGL::TextureCoord0) + { + if (attr == QGL::Position) + { + if (!qFskCompare(vertex(), rhs.vertex())) + return false; + } + else if (attr == QGL::Normal) + { + if (!qFskCompare(normal(), rhs.normal())) + return false; + } + else + { + if (color() != rhs.color()) + return false; + } + } + else if (attr < QGL::CustomVertex0) + { + if (!qFskCompare(texCoord(attr), rhs.texCoord(attr))) + return false; + } + else + { + if (attribute(attr) != rhs.attribute(attr)) + return false; + } + } + } + return true; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QLogicalVertex &lv) +{ + dbg.nospace(); + dbg << "QLogicalVertex("; + if (lv.isNull()) + { + dbg << " NULL"; + } + else + { + if (lv.hasField(QGL::Position)) + dbg << "V:" << QVector3D(lv.vertex()); + else + dbg << " (No Vertex)"; + if (lv.hasField(QGL::Normal)) + dbg << "N:" << QVector3D(lv.normal()); + else + dbg << " (No Normal)"; + if (lv.hasField(QGL::TextureCoord0)) + dbg << "T:" << QVector2D(lv.texCoord()); + else + dbg << " (No Texture)"; + if (lv.hasField(QGL::Color)) + dbg << "C:" << QColor4ub(lv.color()); + else + dbg << " (No Color)"; + } + dbg << " )"; + return dbg.space(); +} +#endif diff --git a/src/threed/geometry/qlogicalvertex.h b/src/threed/geometry/qlogicalvertex.h new file mode 100644 index 000000000..304aa9b05 --- /dev/null +++ b/src/threed/geometry/qlogicalvertex.h @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLOGICALVERTEX_H +#define QLOGICALVERTEX_H + +#include "qgeometrydata.h" +#include "qcustomdataarray.h" + +QT_BEGIN_NAMESPACE + +// uncomment this to perform heavy checking of QLogicalVertex +// #define QT3D_DEBUG_QLOGICALVERTEX 1 + +class QLogicalVertex +{ +public: + inline QLogicalVertex(); + inline QLogicalVertex(QGeometryData data, int index); + inline QLogicalVertex(const QVector3D &a); + inline QLogicalVertex(const QVector3D &a, const QVector3D &n); + inline QLogicalVertex(const QVector3D &a, const QVector3D &n, const QVector2D &t); + inline QLogicalVertex(const QVector3D &a, QColor4ub color); + ~QLogicalVertex() {} + + inline const QVector3D &vertex() const; + inline void setVertex(const QVector3D &v); + inline QVector3D &vertex(); + + operator QVector3D () { return vertex(); } + + inline QVariant attribute(QGL::VertexAttribute field = QGL::CustomVertex0) const; + inline void setAttribute(float value, QGL::VertexAttribute attr); + inline void setAttribute(const QVector2D &v, QGL::VertexAttribute field = QGL::CustomVertex0); + inline void setAttribute(const QVector3D &v, QGL::VertexAttribute field = QGL::CustomVertex0); + inline float &floatAttribute(QGL::VertexAttribute field = QGL::CustomVertex0); + inline QVector2D &vector2DAttribute(QGL::VertexAttribute field = QGL::CustomVertex0); + inline QVector3D &vector3DAttribute(QGL::VertexAttribute field = QGL::CustomVertex0); + inline float floatAttribute(QGL::VertexAttribute field = QGL::CustomVertex0) const; + inline QVector2D vector2DAttribute(QGL::VertexAttribute field = QGL::CustomVertex0) const; + inline QVector3D vector3DAttribute(QGL::VertexAttribute field = QGL::CustomVertex0) const; + inline QCustomDataArray::ElementType attributeType(QGL::VertexAttribute field = QGL::CustomVertex0); + + inline const QVector3D &normal() const; + inline void setNormal(const QVector3D &n); + inline QVector3D &normal(); + + inline const QColor4ub &color() const; + inline void setColor(const QColor4ub &c); + inline QColor4ub &colorRef(); + + inline const QVector2D &texCoord(QGL::VertexAttribute attr = QGL::TextureCoord0) const; + inline void setTexCoord(const QVector2D &t, QGL::VertexAttribute attr = QGL::TextureCoord0); + inline QVector2D &texCoordRef(QGL::VertexAttribute attr = QGL::TextureCoord0); + + inline bool hasField(QGL::VertexAttribute type) const; + inline quint32 fields() const; + inline int index() const; + inline QGeometryData data() const; + inline bool isNull() const; + + bool operator==(const QLogicalVertex &rhs) const; + +private: + QGeometryData m_data; + int m_index; +}; + +inline QLogicalVertex::QLogicalVertex() + : m_index(-1) +{ +} + +inline QLogicalVertex::QLogicalVertex(QGeometryData data, int index) + : m_data(data) + , m_index(index) +{ + Q_ASSERT(index < data.count()); +#ifdef QT3D_DEBUG_QLOGICALVERTEX + data.check(); +#endif +} + +inline QLogicalVertex::QLogicalVertex(const QVector3D &a) + : m_index(0) +{ + m_data.appendVertex(a); +} + +inline QLogicalVertex::QLogicalVertex(const QVector3D &a, const QVector3D &n) + : m_index(0) +{ + m_data.appendVertex(a); + m_data.appendNormal(n); +} + +inline QLogicalVertex::QLogicalVertex(const QVector3D &a, const QVector3D &n, const QVector2D &t) + : m_index(0) +{ + m_data.appendVertex(a); + m_data.appendNormal(n); + m_data.appendTexCoord(t); +} + +inline QLogicalVertex::QLogicalVertex(const QVector3D &a, QColor4ub color) + : m_index(0) +{ + m_data.appendVertex(a); + m_data.appendColor(color); +} + +inline const QVector3D &QLogicalVertex::vertex() const +{ + return m_data.vertexAt(m_index); +} + +inline void QLogicalVertex::setVertex(const QVector3D &v) +{ + if (m_index == -1) + m_index = 0; + if (m_index == m_data.count(QGL::Position)) + m_data.appendVertex(v); + else + m_data.vertex(m_index) = v; +} + +inline QVector3D &QLogicalVertex::vertex() +{ + return m_data.vertex(m_index); +} + +inline QVariant QLogicalVertex::attribute(QGL::VertexAttribute attr) const +{ + return m_data.attributes(attr).at(m_index); +} + +inline void QLogicalVertex::setAttribute(float v, QGL::VertexAttribute attr) +{ + if (m_index == -1) + m_index = 0; + if (m_index == m_data.count(attr)) + m_data.appendAttribute(v, attr); + else + m_data.floatAttribute(m_index, attr) = v; +} + +inline void QLogicalVertex::setAttribute(const QVector2D &v, QGL::VertexAttribute attr) +{ + if (m_index == -1) + m_index = 0; + if (m_index == m_data.count(attr)) + m_data.appendAttribute(v, attr); + else + m_data.vector2DAttribute(m_index, attr) = v; +} + +inline void QLogicalVertex::setAttribute(const QVector3D &v, QGL::VertexAttribute attr) +{ + if (m_index == -1) + m_index = 0; + if (m_index == m_data.count(attr)) + m_data.appendAttribute(v, attr); + else + m_data.vector3DAttribute(m_index, attr) = v; +} + +inline float &QLogicalVertex::floatAttribute(QGL::VertexAttribute field) +{ + return m_data.floatAttribute(m_index, field); +} + +inline QVector2D &QLogicalVertex::vector2DAttribute(QGL::VertexAttribute field) +{ + return m_data.vector2DAttribute(m_index, field); +} + +inline QVector3D &QLogicalVertex::vector3DAttribute(QGL::VertexAttribute field) +{ + + return m_data.vector3DAttribute(m_index, field); +} + +inline float QLogicalVertex::floatAttribute(QGL::VertexAttribute field) const +{ + return m_data.floatAttributeAt(m_index, field); +} + +inline QVector2D QLogicalVertex::vector2DAttribute(QGL::VertexAttribute field) const +{ + return m_data.vector2DAttributeAt(m_index, field); +} + +inline QVector3D QLogicalVertex::vector3DAttribute(QGL::VertexAttribute field) const +{ + return m_data.vector3DAttributeAt(m_index, field); +} + +inline QCustomDataArray::ElementType QLogicalVertex::attributeType(QGL::VertexAttribute field) +{ + return m_data.attributes(field).elementType(); +} + +inline const QVector3D &QLogicalVertex::normal() const +{ + return m_data.normalAt(m_index); +} + +inline void QLogicalVertex::setNormal(const QVector3D &n) +{ + if (m_index == -1) + m_index = 0; + if (m_index == m_data.count(QGL::Normal)) + m_data.appendNormal(n); + else + m_data.normal(m_index) = n; +} + +inline QVector3D &QLogicalVertex::normal() +{ + return m_data.normal(m_index); +} + +inline const QVector2D &QLogicalVertex::texCoord(QGL::VertexAttribute attr) const +{ + return m_data.texCoordAt(m_index, attr); +} + +inline void QLogicalVertex::setTexCoord(const QVector2D &t, QGL::VertexAttribute attr) +{ + Q_ASSERT(attr >= QGL::TextureCoord0 && attr <= QGL::TextureCoord2); + if (m_index == -1) + m_index = 0; + if (m_index == m_data.count(attr)) + m_data.appendTexCoord(t, attr); + else + m_data.texCoord(m_index, attr) = t; +} + +inline QVector2D &QLogicalVertex::texCoordRef(QGL::VertexAttribute attr) +{ + return m_data.texCoord(m_index, attr); +} + +inline const QColor4ub &QLogicalVertex::color() const +{ + return m_data.colorAt(m_index); +} + +inline void QLogicalVertex::setColor(const QColor4ub &c) +{ + if (m_index == -1) + m_index = 0; + if (m_index == m_data.count(QGL::Color)) + m_data.appendColor(c); + else + m_data.color(m_index) = c; +} + +inline QColor4ub &QLogicalVertex::colorRef() +{ + return m_data.color(m_index); +} + +inline bool QLogicalVertex::hasField(QGL::VertexAttribute attr) const +{ + return m_data.hasField(attr); +} + +inline quint32 QLogicalVertex::fields() const +{ + return m_data.fields(); +} + +inline int QLogicalVertex::index() const +{ + return m_index; +} + +inline bool QLogicalVertex::isNull() const +{ + return (m_index == -1); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QLogicalVertex §ion); +#endif + +QT_END_NAMESPACE + +#endif // QLOGICALVERTEX_H diff --git a/src/threed/geometry/qvector_utils_p.h b/src/threed/geometry/qvector_utils_p.h new file mode 100644 index 000000000..64b436226 --- /dev/null +++ b/src/threed/geometry/qvector_utils_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVECTOR_UTILS_P_H +#define QVECTOR_UTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qvector3d.h> +#include <QtGui/qvector2d.h> + +// Replacement for qFuzzyCompare(QVector3D, QVector3D) and friends, +// for a specific case where the results are going to be rendered +// by the GPU onto a display. +// +// The accuracy of this comparison should not change. Especially it +// should not change when you go from doubles to floats or when +// you get close to zero. If two verts or lighting normals are +// the same to within 5 places of floating point accuracy then they +// will dislay as being on top of each other. +// +// Also this avoids doing 3 floating point multiplications every time +// since the normal qFuzzyIsNull does a mul to scale the epsilon when +// close to zero. + +inline bool qFskIsNull(double d) +{ + return qAbs(d) <= 0.00001; +} + +inline bool qFskIsNull(float f) +{ + return qAbs(f) <= 0.00001f; +} + +inline bool qFskCompare(float a, float b) +{ + return qFskIsNull(a - b); +} + +inline bool qFskCompare(double a, double b) +{ + return qFskIsNull(a - b); +} + +inline bool qFskCompare(const QVector3D &a, const QVector3D &b) +{ + return ( + qFskIsNull(a.x() - b.x()) && + qFskIsNull(a.y() - b.y()) && + qFskIsNull(a.z() - b.z()) + ); +} + +inline bool qFskCompare(const QVector2D &a, const QVector2D &b) +{ + return ( + qFskIsNull(a.x() - b.x()) && + qFskIsNull(a.y() - b.y()) + ); +} + +#endif // QVECTOR_UTILS_P_H diff --git a/src/threed/global/global.pri b/src/threed/global/global.pri new file mode 100644 index 000000000..d911c784a --- /dev/null +++ b/src/threed/global/global.pri @@ -0,0 +1,7 @@ +INCLUDEPATH += $$PWD +VPATH += $$PWD +HEADERS += \ + qt3dglobal.h \ + qglnamespace.h +SOURCES += \ + qglnamespace.cpp diff --git a/src/threed/global/qglnamespace.cpp b/src/threed/global/qglnamespace.cpp new file mode 100644 index 000000000..f62cb6479 --- /dev/null +++ b/src/threed/global/qglnamespace.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglnamespace.h" +#include <QtCore/qsize.h> + +QT_BEGIN_NAMESPACE + +/*! + \namespace QGL + + \brief The QGL namespace contains miscellaneous identifiers + used throughout the Qt3D module. +*/ + +/*! + \enum QGL::VertexAttribute + \since 4.8 + This enum defines the type of vertex attribute to set on an effect with QGLPainter::setVertexAttribute() + + \value Position The primary position of the vertex. + \value Normal The normal at each vertex. + \value Color The color at each vertex. + \value TextureCoord0 The texture co-ordinate at each vertex for texture unit 0. + \value TextureCoord1 The texture co-ordinate at each vertex for texture unit 1. + \value TextureCoord2 The texture co-ordinate at each vertex for texture unit 2. + \value CustomVertex0 First custom vertex attribute. Custom attributes + can be used for any purpose: texture co-ordinates, colors, + or other values of interest to shader programs. + \value CustomVertex1 Second custom vertex attribute. + \value UserVertex First user-assigned vertex attribute. Additional + attributes can be assigned as UserVertex, UserVertex + 1, etc. + Note that on OpenGL/ES 2.0 systems, usually the maximum + number of vertex attributes is 8, which means that user-assigned + vertex attributes will be out of range. +*/ + +/*! + \enum QGL::Face + \since 4.8 + This enum defines the faces to apply an operation to. + + \value FrontFaces Apply the operation to front faces only. + \value BackFaces Apply the operation to back faces only. + \value AllFaces Apply the operation to both front and back faces. +*/ + +/*! + \enum QGL::DrawingMode + \since 4.8 + This enum defines the type of OpenGL primitive to render with QGLPainter::draw(). + + \value Points Draws a point at each of the specified vertices. + \value Lines Draws a series of unconnected line segments, using two + vertices for each line. + \value LineLoop series of connected line segments, from the + first to the last vertex, and then connecting the last + vertex back to the first vertex. + \value LineStrip Draws a series of connected line segments, from the + first to the last vertex. + \value Triangles Draws a series of triangles using three vertices from + the enabled vertex arrays for each triangle. + \value TriangleStrip Draws a series of triangles in a strip, starting + with the first three vertices and then one extra vertex for each + additional triangle. + \value TriangleFan Draws a series of triangles that fan out around the + first vertex in the enabled vertex arrays, starting with the + first three vertices and then one extra vertex for each additional + triangle. + \value LinesAdjacency Draws a series of unconnected lines, using + two vertices for each line to define the positions, and an + additional vertices per line to define adjacent points. + This drawing mode is only supported on OpenGL systems that + have geometry shaders. + \value LineStripAdjacency Draws a series of connected line segments, + from the second to the second last vertex. The first and last + vertices define adjacent points. This drawing mode is only + supported on OpenGL systems that have geometry shaders. + \value TrianglesAdjacency Draws a series of triangles using three + vertices from the enabled vertex arrays for each triangle. + An additional three vertices per triangle are supplied to + define adjacent points. This drawing mode is only supported + on OpenGL systems that have geometry shaders. + \value TriangleStripAdjacency Draws a series of triangles in a strip, + with additional vertices for points adjacent to the strip. + This drawing mode is only supported on OpenGL systems that + have geometry shaders. +*/ + +/*! + \enum QGL::StandardEffect + \since 4.8 + This enum defines a standard drawing effect for use with QGLPainter. + + \value FlatColor Single flat color specified by QGLPainter::setColor() + with no lighting. + \value FlatPerVertexColor Per-vertex flat colors specified by a + QGL::Color vertex array with no lighting. + \value FlatReplaceTexture2D Map a texture across the fragments with + no lighting. The final fragment color is replaced directly + with the texture. The texture is sourced from texture unit 0. + \value FlatDecalTexture2D Map a texture across the fragments, combined + with QGLPainter::color(), and no lighting. The texture is sourced + from texture unit 0. + \value LitMaterial Material colors specified by + QGLPainter::setFaceMaterial() with lighting enabled. It is + assumed that per-vertex normals are provided. Under OpenGL/ES 2.0 + only one light is supported, with single-sided materials, + and no attenuation. + \value LitDecalTexture2D Map a texture across the fragments, combined + with the material color specified by QGLPainter::setFaceMaterial(), + and lighting using the GL_DECAL combination rule. The texture is + sourced from texture unit 0. It is assumed that per-vertex + normals are provided. Under OpenGL/ES 2.0 only one light is + supported, with single-sided materials, and no attenuation. + \value LitModulateTexture2D Map a texture across the fragments, combined + with the material color specified by QGLPainter::setFaceMaterial(), + and lighting using the GL_MODULATE combination rule. The texture + is sourced from texture unit 0. It is assumed that per-vertex + normals are provided. Under OpenGL/ES 2.0 only one light is + supported, with single-sided materials, and no attenuation. +*/ + +/*! + \enum QGL::TextureWrap + \since 4.8 + This enum defines the wrapping mode for texture co-ordinates. + + \value Repeat Ignore the integer part of the texture co-ordinate and + create a repeating pattern using the texture. + \value Clamp Clamps the texture co-ordinate to the range 0 to 1. + This value is not supported in OpenGL/ES, where it will be + treated the same as ClampToEdge. + \value ClampToBorder Clamps the texture co-ordinate to the range + (-1 / 2N) to (1 + 1 / 2N) where N is the size of the texture in + the direction of clamping. This value is supported in OpenGL 1.3 + and higher. In OpenGL versions less than 1.3, this value will + be treated the same as Clamp. In OpenGL/ES, this value will be + treated the same as ClampToEdge. + \value ClampToEdge Clamps the texture co-ordinate to the range + (1 / 2N) to (1 - 1 / 2N) where N is the size of the texture in + the direction of clamping. This value is supported in OpenGL 1.2 + and higher, and in all versions of OpenGL/ES. In OpenGL versions + less than 1.2, this value will be treated the same as Clamp. + \value MirroredRepeat If the integer part of the texture co-ordinate + is even, then use the fractional part. If the integer part of + the texture co-ordinate is odd, then use 1 - the fractional part. + This creates an alternating repeating pattern from the texture. + This value is only supported in OpenGL 1.4 and higher, or + OpenGL/ES 2.0 and higher. On other versions, MirroredRepeat + will be treated the same as Repeat. +*/ + +/*! + \enum QGL::Eye + \since 4.8 + This enum defines the eye that is being rendered by a QGLPainter + when stereo rendering is in effect. + + \value NoEye Do not perform an eye adjustment on the camera because + stereo rendering is not in effect. + \value LeftEye Render the scene from the perspective of the left eye. + \value RightEye Render the scene from the perspective of the right eye. +*/ + +/*! + \enum QGL::Smoothing + \since 4.8 + \relates QGLBuilder + + This enum defines vertex smoothing treatments. + \value NoSmoothing No smoothing processing is performed. + \value Smooth Lighting normals averaged for each face for a smooth appearance. + \value Faceted Lighting normals separate for each face for a faceted appearance. +*/ + +/*! + \enum QGL::Mouse3DKeys + \since 4.8 + This enum defines extra key codes for QKeyEvent related to 3D mice + devices. See QMouse3DEventProvider for further details. + + \value Key_Fit Reset the transform so the model is centered to + fit within the view. + \value Key_TopView Switch to the top view. + \value Key_LeftView Switch to the left view. + \value Key_RightView Switch to the right view. + \value Key_FrontView Switch to the front view. + \value Key_BottomView Switch to the bottom view. + \value Key_BackView Switch to the back view. + \value Key_RotateCW90 Rotate clockwise by 90 degrees. + \value Key_RotateCCW90 Rotate counter-clockwise by 90 degrees. + \value Key_ISO1 Show an isometric view of the model with top, front, + and right sides. + \value Key_ISO2 Show an isometric view of the model with top, back + and left sides. + \value Key_Button1 Special function button 1. + \value Key_Button2 Special function button 2. + \value Key_Button3 Special function button 3. + \value Key_Button4 Special function button 4. + \value Key_Button5 Special function button 5. + \value Key_Button6 Special function button 6. + \value Key_Button7 Special function button 7. + \value Key_Button8 Special function button 8. + \value Key_Button9 Special function button 9. + \value Key_Button10 Special function button 10. + \value Key_Rotations Toggles whether or not the rotation axes on a + 3D mouse will be filtered. + \value Key_Translations Toggles whether or not the translation axes + on a 3D mouse will be filtered. + \value Key_DominantAxis Toggles whether or not 3D mouse movements + should be constrained to the dominant axis. + \value Key_IncreaseSensitivity Increase the sensitivity of the mouse + to wrist movements. + \value Key_DecreaseSensitivity Decrease the sensitivity of the mouse + to wrist movements. +*/ + +/*! + Returns the next power of two that is greater than or + equal to \a value. The \a value must be positive or the + result is undefined. + + This is a convenience function for use with GL texture + handling code. +*/ +int QGL::nextPowerOfTwo(int value) +{ + value--; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + ++value; + return value; +} + +/*! + Returns the next power of two that is greater than or + equal to the width and height components of \a value. + + This is a convenience function for use with GL texture + handling code. +*/ +QSize QGL::nextPowerOfTwo(const QSize& value) +{ + return QSize(nextPowerOfTwo(value.width()), + nextPowerOfTwo(value.height())); +} + +QT_END_NAMESPACE diff --git a/src/threed/global/qglnamespace.h b/src/threed/global/qglnamespace.h new file mode 100644 index 000000000..7b97c84ab --- /dev/null +++ b/src/threed/global/qglnamespace.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLNAMESPACE_H +#define QGLNAMESPACE_H + +#include "qt3dglobal.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QSize; + +namespace QGL +{ + enum VertexAttribute { + Position, + Normal, + Color, + TextureCoord0, + TextureCoord1, + TextureCoord2, + CustomVertex0, + CustomVertex1, + UserVertex + }; + + enum Face + { + FrontFaces = 0x0404, // GL_FRONT + BackFaces = 0x0405, // GL_BACK + AllFaces = 0x0408 // GL_FRONT_AND_BACK + }; + + enum DrawingMode + { + Points = 0x0000, // GL_POINTS + Lines = 0x0001, // GL_LINES + LineLoop = 0x0002, // GL_LINE_LOOP + LineStrip = 0x0003, // GL_LINE_STRIP + Triangles = 0x0004, // GL_TRIANGLES + TriangleStrip = 0x0005, // GL_TRIANGLE_STRIP + TriangleFan = 0x0006, // GL_TRIANGLE_FAN + LinesAdjacency = 0x000A, // GL_LINES_ADJACENCY + LineStripAdjacency = 0x000B, // GL_LINE_STRIP_ADJACENCY + TrianglesAdjacency = 0x000C, // GL_TRIANGLES_ADJACENCY + TriangleStripAdjacency = 0x000D // GL_TRIANGLE_STRIP_ADJACENCY + }; + + enum StandardEffect + { + FlatColor, + FlatPerVertexColor, + FlatReplaceTexture2D, + FlatDecalTexture2D, + LitMaterial, + LitDecalTexture2D, + LitModulateTexture2D + }; + + enum TextureWrap + { + Repeat = 0x2901, // GL_REPEAT + Clamp = 0x2900, // GL_CLAMP + ClampToBorder = 0x812D, // GL_CLAMP_TO_BORDER + ClampToEdge = 0x812F, // GL_CLAMP_TO_EDGE + MirroredRepeat = 0x8370 // GL_MIRRORED_REPEAT + }; + + enum Eye + { + NoEye, + LeftEye, + RightEye + }; + + enum Smoothing + { + NoSmoothing, + Smooth, + Faceted + }; + + enum Mouse3DKeys + { + Key_Fit = 0x01200002, + Key_TopView = 0x01200003, + Key_LeftView = 0x01200004, + Key_RightView = 0x01200005, + Key_FrontView = 0x01200006, + Key_BottomView = 0x01200007, + Key_BackView = 0x01200008, + Key_RotateCW90 = 0x01200009, + Key_RotateCCW90 = 0x0120000a, + Key_ISO1 = 0x0120000b, + Key_ISO2 = 0x0120000c, + Key_Button1 = 0x0120000d, + Key_Button2 = 0x0120000e, + Key_Button3 = 0x0120000f, + Key_Button4 = 0x01200010, + Key_Button5 = 0x01200011, + Key_Button6 = 0x01200012, + Key_Button7 = 0x01200013, + Key_Button8 = 0x01200014, + Key_Button9 = 0x01200015, + Key_Button10 = 0x01200016, + Key_Rotations = 0x0120001b, + Key_Translations = 0x0120001c, + Key_DominantAxis = 0x0120001d, + Key_IncreaseSensitivity = 0x0120001e, + Key_DecreaseSensitivity = 0x0120001f + }; + + Q_QT3D_EXPORT int nextPowerOfTwo(int value); + Q_QT3D_EXPORT QSize nextPowerOfTwo(const QSize& value); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/global/qt3dglobal.h b/src/threed/global/qt3dglobal.h new file mode 100644 index 000000000..1e3b3d793 --- /dev/null +++ b/src/threed/global/qt3dglobal.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DGLOBAL_H +#define QT3DGLOBAL_H + +#include <QtCore/qglobal.h> + +// XXX: Move to qglobal.h eventually. +QT_LICENSED_MODULE(Qt3D) +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) +# if defined(QT_NODLL) +# undef QT_MAKEDLL +# undef QT_DLL +# elif defined(QT_MAKEDLL) /* create a Qt DLL library */ +# if defined(QT_DLL) +# undef QT_DLL +# endif +# if defined(QT_BUILD_QT3D_LIB) +# define Q_QT3D_EXPORT Q_DECL_EXPORT +# else +# define Q_QT3D_EXPORT Q_DECL_IMPORT +# endif +# elif defined(QT_DLL) /* use a Qt DLL library */ +# define Q_QT3D_EXPORT Q_DECL_IMPORT +# endif +#endif +#if !defined(Q_QT3D_EXPORT) +# if defined(QT_SHARED) +# define Q_QT3D_EXPORT Q_DECL_EXPORT +# else +# define Q_QT3D_EXPORT +# endif +#endif + +#endif diff --git a/src/threed/graphicsview/graphicsview.pri b/src/threed/graphicsview/graphicsview.pri new file mode 100644 index 000000000..2c853f02a --- /dev/null +++ b/src/threed/graphicsview/graphicsview.pri @@ -0,0 +1,21 @@ + +INCLUDEPATH += $$PWD +VPATH += $$PWD + +HEADERS += \ + qglgraphicsviewportitem.h \ + qgraphicsbillboardtransform.h \ + qgraphicsembedscene.h \ + qgraphicsrotation3d.h \ + qgraphicsscale3d.h \ + qgraphicstransform3d.h \ + qgraphicstranslation3d.h + +SOURCES += \ + qglgraphicsviewportitem.cpp \ + qgraphicsbillboardtransform.cpp \ + qgraphicsembedscene.cpp \ + qgraphicsrotation3d.cpp \ + qgraphicsscale3d.cpp \ + qgraphicstransform3d.cpp \ + qgraphicstranslation3d.cpp diff --git a/src/threed/graphicsview/qglgraphicsviewportitem.cpp b/src/threed/graphicsview/qglgraphicsviewportitem.cpp new file mode 100644 index 000000000..27acec610 --- /dev/null +++ b/src/threed/graphicsview/qglgraphicsviewportitem.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglgraphicsviewportitem.h" +#include "qglpainter.h" +#include "qglsubsurface.h" +#include <QtGui/qpainter.h> +#include <QtGui/qgraphicsscene.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLGraphicsViewportItem + \brief The QGLGraphicsViewportItem class provides a rectangular viewport for arbitrary OpenGL painting. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::graphicsview +*/ + +class QGLGraphicsViewportItemPrivate : public QObject +{ + Q_OBJECT +public: + QGLGraphicsViewportItemPrivate(QGLGraphicsViewportItem *item) + : q(item) + { + defaultCamera = camera = new QGLCamera(this); + connect(camera, SIGNAL(projectionChanged()), + this, SLOT(cameraChanged())); + connect(camera, SIGNAL(viewChanged()), + this, SLOT(cameraChanged())); + } + + void changeCamera(QGLCamera *c); + void setDefaults(QGLPainter *painter); + + QGLGraphicsViewportItem *q; + QRectF rect; + QGLCamera *camera; + QGLCamera *defaultCamera; + QColor backgroundColor; + +private Q_SLOTS: + void cameraChanged(); +}; + +void QGLGraphicsViewportItemPrivate::changeCamera(QGLCamera *c) +{ + disconnect(camera, SIGNAL(projectionChanged()), + this, SLOT(cameraChanged())); + disconnect(camera, SIGNAL(viewChanged()), + this, SLOT(cameraChanged())); + camera = c; + connect(camera, SIGNAL(projectionChanged()), + this, SLOT(cameraChanged())); + connect(camera, SIGNAL(viewChanged()), + this, SLOT(cameraChanged())); +} + +void QGLGraphicsViewportItemPrivate::cameraChanged() +{ + q->update(); +} + +void QGLGraphicsViewportItemPrivate::setDefaults(QGLPainter *painter) +{ + // Set the default depth buffer options. + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); +#if defined(QT_OPENGL_ES) + glDepthRangef(0.0f, 1.0f); +#else + glDepthRange(0.0f, 1.0f); +#endif + + // Set the default blend options. + glDisable(GL_BLEND); + if (painter->hasOpenGLFeature(QOpenGLFunctions::BlendColor)) + painter->glBlendColor(0, 0, 0, 0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (painter->hasOpenGLFeature(QOpenGLFunctions::BlendEquation)) + painter->glBlendEquation(GL_FUNC_ADD); + else if (painter->hasOpenGLFeature(QOpenGLFunctions::BlendEquationSeparate)) + painter->glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); +} + +/*! + Constructs a new GL graphics viewport and attaches it to \a parent. +*/ +QGLGraphicsViewportItem::QGLGraphicsViewportItem(QGraphicsItem *parent) + : QGraphicsItem(parent), d_ptr(new QGLGraphicsViewportItemPrivate(this)) +{ +} + +/*! + Constructs a new GL graphics viewport and attaches it to \a parent. + The boundingRect() is initially set to \a rect. +*/ +QGLGraphicsViewportItem::QGLGraphicsViewportItem(const QRectF& rect, QGraphicsItem *parent) + : QGraphicsItem(parent), d_ptr(new QGLGraphicsViewportItemPrivate(this)) +{ + d_ptr->rect = rect; +} + +/*! + Constructs a new GL graphics viewport and attaches it to \a parent. + The boundingRect() is initially set to (\a x, \a y, \a w, \a h). +*/ +QGLGraphicsViewportItem::QGLGraphicsViewportItem + (qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent) + : QGraphicsItem(parent), d_ptr(new QGLGraphicsViewportItemPrivate(this)) +{ + d_ptr->rect = QRectF(x, y, w, h); +} + +/*! + Destroys this GL graphics viewport. +*/ +QGLGraphicsViewportItem::~QGLGraphicsViewportItem() +{ +} + +/*! + Returns the rectangular area of the view that is occupied by + this GL graphics viewport. + + \sa setRect(), boundingRect() +*/ +QRectF QGLGraphicsViewportItem::rect() const +{ + Q_D(const QGLGraphicsViewportItem); + return d->rect; +} + +/*! + Sets the rectangular area of the view that is occupied by + this GL graphics viewport to \a rect. + + \sa rect(), boundingRect() +*/ +void QGLGraphicsViewportItem::setRect(const QRectF &rect) +{ + Q_D(QGLGraphicsViewportItem); + if (d->rect == rect) + return; + prepareGeometryChange(); + d->rect = rect; + update(); +} + +/*! + \fn void QGLGraphicsViewportItem::setRect(qreal x, qreal y, qreal w, qreal h) + \overload + + Sets the rectangular area of the view that is occupied by + this GL graphics viewport to (\a x, \a y, \a w, \a h). + + \sa rect(), boundingRect() +*/ + +/*! + Returns the bounding rectangle for this GL graphics viewport, + which is the same as rect(). + + \sa rect() +*/ +QRectF QGLGraphicsViewportItem::boundingRect() const +{ + Q_D(const QGLGraphicsViewportItem); + return d->rect; +} + +/*! + Returns the camera parameters. The camera defines the projection + to apply to convert eye co-ordinates into window co-ordinates, + and the position and orientation of the viewer's eye. + + This item will be updated whenever the camera's parameters change. + + \sa setCamera() +*/ +QGLCamera *QGLGraphicsViewportItem::camera() const +{ + Q_D(const QGLGraphicsViewportItem); + return d->camera; +} + +/*! + Sets the camera parameters to \a camera. The camera defines the + projection to apply to convert eye co-ordinates into window + co-ordinates, and the position and orientation of the viewer's eye. + + If \a camera is null, then the default camera object will be used. + + This function will call update() to force the viewport to update + with the new camera parameters upon the next event loop. Afterwards, + this item will be updated whenever the camera's parameters change. + + \sa camera() +*/ +void QGLGraphicsViewportItem::setCamera(QGLCamera *camera) +{ + Q_D(QGLGraphicsViewportItem); + if (!camera) + camera = d->defaultCamera; + if (d->camera != camera) { + d->changeCamera(camera); + update(); + } +} + +/*! + Returns the background color, which is used to clear the viewport + before calling paintGL(). The default value is an invalid QColor, + which indicates that the viewport should not be cleared. + + \sa setBackgroundColor() +*/ +QColor QGLGraphicsViewportItem::backgroundColor() const +{ + Q_D(const QGLGraphicsViewportItem); + return d->backgroundColor; +} + +/*! + Sets the background \a color, which is used to clear the viewport + before calling paintGL(). + + \sa backgroundColor() +*/ +void QGLGraphicsViewportItem::setBackgroundColor(const QColor& color) +{ + Q_D(QGLGraphicsViewportItem); + d->backgroundColor = color; +} + +/*! + Paints this GL graphics viewport onto \a painter, with the specified + \a option and \a widget parameters. + + This override creates a QGLPainter for \a painter and passes + it to paintGL(). + + \sa paintGL() +*/ +void QGLGraphicsViewportItem::paint + (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_D(QGLGraphicsViewportItem); + + Q_UNUSED(option); + Q_UNUSED(widget); + + if (d->rect.isEmpty()) + return; + + // Initialize a QGLPainter for the surface and bail out if not active. + QGLPainter glpainter; + if (!glpainter.begin(painter)) { + qWarning("GL graphics system is not active; cannot use 3D items"); + return; + } + + // Set up the GL viewport to limit drawing to the bounds of this item. + QRect viewport = painter->deviceTransform().mapRect(rect()).toRect(); + QGLSubsurface surface(glpainter.currentSurface(), viewport); + glpainter.pushSurface(&surface); + + // Set up the desired drawing options. + glDisable(GL_CULL_FACE); + d->setDefaults(&glpainter); + if (d->backgroundColor.isValid()) { + // We clear the background by drawing a triangle fan so + // that the background color will blend with the underlying + // screen content if it has an alpha component. + glDisable(GL_DEPTH_TEST); + if (d->backgroundColor.alpha() != 255) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + QVector2DArray array; + array.append(-1, -1); + array.append(1, -1); + array.append(1, 1); + array.append(-1, 1); + glpainter.projectionMatrix().setToIdentity(); + glpainter.modelViewMatrix().setToIdentity(); + glpainter.setStandardEffect(QGL::FlatColor); + glpainter.setColor(d->backgroundColor); + glpainter.setVertexAttribute(QGL::Position, array); + glpainter.draw(QGL::TriangleFan, 4); + } + glClear(GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + + // Apply the camera. + glpainter.setEye(QGL::NoEye); + glpainter.setCamera(d->camera); + + // Paint the GL contents. + paintGL(&glpainter); + + // Disable the current drawing effect so that QGLPainter will + // forcibly update the GL context the next time QGLPainter is used. + glpainter.disableEffect(); + + // Try to restore the GL state to something paint-engine compatible. + glDisable(GL_CULL_FACE); + d->setDefaults(&glpainter); + glDisable(GL_DEPTH_TEST); + + glpainter.popSurface(); +} + +/*! + \fn void QGLGraphicsViewportItem::paintGL(QGLPainter *painter) + + Paints this GL graphics viewport onto \a painter. This function is called + from paint(). + + \sa paint() +*/ + +QT_END_NAMESPACE + +#include "qglgraphicsviewportitem.moc" diff --git a/src/threed/graphicsview/qglgraphicsviewportitem.h b/src/threed/graphicsview/qglgraphicsviewportitem.h new file mode 100644 index 000000000..c9e12506b --- /dev/null +++ b/src/threed/graphicsview/qglgraphicsviewportitem.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLGRAPHICSVIEWPORTITEM_H +#define QGLGRAPHICSVIEWPORTITEM_H + +#include "qglnamespace.h" +#include <QtGui/qgraphicsitem.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLGraphicsViewportItemPrivate; +class QGLPainter; +class QGLCamera; + +class Q_QT3D_EXPORT QGLGraphicsViewportItem : public QGraphicsItem +{ +public: + QGLGraphicsViewportItem(QGraphicsItem *parent = 0); + QGLGraphicsViewportItem(const QRectF& rect, QGraphicsItem *parent = 0); + QGLGraphicsViewportItem(qreal x, qreal y, qreal w, qreal h, + QGraphicsItem *parent = 0); + ~QGLGraphicsViewportItem(); + + QRectF rect() const; + void setRect(const QRectF &rect); + inline void setRect(qreal x, qreal y, qreal w, qreal h); + + QRectF boundingRect() const; + + QGLCamera *camera() const; + void setCamera(QGLCamera *camera); + + QColor backgroundColor() const; + void setBackgroundColor(const QColor& color); + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + +protected: + virtual void paintGL(QGLPainter *painter) = 0; + +private: + QScopedPointer<QGLGraphicsViewportItemPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLGraphicsViewportItem) + Q_DISABLE_COPY(QGLGraphicsViewportItem) +}; + +void QGLGraphicsViewportItem::setRect(qreal x, qreal y, qreal w, qreal h) +{ + setRect(QRectF(x, y, w, h)); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/graphicsview/qgraphicsbillboardtransform.cpp b/src/threed/graphicsview/qgraphicsbillboardtransform.cpp new file mode 100644 index 000000000..7072e526f --- /dev/null +++ b/src/threed/graphicsview/qgraphicsbillboardtransform.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicsbillboardtransform.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGraphicsBillboardTransform + \brief The QGraphicsBillboardTransform class implements a transformation that causes objects to face the camera. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::graphicsview + + Sometimes it can be useful to make an object face towards the camera + no matter what orientation the scene is in. The common name for + this technique is "billboarding". + + When applied as a transformation, this class will replace the top-left + 3x3 part of the transformation matrix with the identity. This has the + effect of removing the rotation and scale components from the current + world co-ordinate orientation. +*/ + +/*! + \qmlclass BillboardTransform QGraphicsBillboardTransform + \brief The BillboardTransform item implements a transformation that causes objects to face the camera. + \since 4.8 + \ingroup qt3d::qml3d + + Sometimes it can be useful to make an object face towards the camera + no matter what orientation the scene is in. The common name for + this technique is "billboarding". + + When applied as a transformation, this class will replace the top-left + 3x3 part of the transformation matrix with the identity. This has the + effect of removing the rotation and scale components from the current + world co-ordinate orientation. In QML, this can be used as follows + to orient a pane to point towards the viewer: + + \code + Item3D { + mesh: Mesh { source: "pane.obj" } + position: Qt.vector3d(2, 0, -20) + transform: BillboardTransform {} + effect: Effect { texture: "picture.jpg" } + } + \endcode + + Because the billboard transformation will strip any further + alterations to the matrix, it will usually be the last element + in the \c transform list (transformations are applied to the matrix in + reverse order of their appearance in \c transform): + + \code + Item3D { + mesh: Mesh { source: "pane.obj" } + position: Qt.vector3d(2, 0, -20) + transform: [ + Scale3D { scale: 0.5 }, + Rotation3D { angle: 30 }, + BillboardTransform {} + ] + effect: Effect { texture: "picture.jpg" } + } + \endcode + + The \c scale property is applied to the matrix after \c transform has + performed the billboard transformation, so the above can also be written + as follows: + + \code + Item3D { + mesh: Mesh { source: "pane.obj" } + position: Qt.vector3d(2, 0, -20) + scale: 0.5 + transform: [ + Rotation3D { angle: 30 }, + BillboardTransform {} + ] + effect: Effect { texture: "picture.jpg" } + } + \endcode + + By default the billboard transform will cause the object to + face directly at the camera no matter how the world co-ordinate + system is rotated. Sometimes the billboard needs to stay at right + angles to the "ground plane" even if the user's viewpoint is + elevated. This behavior can be enabled using the preserveUpVector + property: + + \code + Pane { + position: Qt.vector3d(2, 0, -20) + transform: BillboardTransform { preserveUpVector: true } + effect: Effect { texture: "picture.jpg" } + } + \endcode + + \sa {Forest Example} +*/ + +class QGraphicsBillboardTransformPrivate +{ +public: + QGraphicsBillboardTransformPrivate() : preserveUpVector(false) {} + + bool preserveUpVector; +}; + +/*! + Construct a billboard transform and attach it to \a parent. +*/ +QGraphicsBillboardTransform::QGraphicsBillboardTransform(QObject *parent) + : QGraphicsTransform3D(parent), d_ptr(new QGraphicsBillboardTransformPrivate) +{ +} + +/*! + Destroy this billboard transform. +*/ +QGraphicsBillboardTransform::~QGraphicsBillboardTransform() +{ +} + +/*! + \property QGraphicsBillboardTransform::preserveUpVector + \brief true to preserve the up orientation. + + The default value for this property is false, which indicates that + the object being transformed should always face directly to the camera + This is also known as a "spherical billboard". + + If the value for this property is true, then the object will have + its up orientation preserved. This is also known as a "cylindrical + billboard". +*/ + +/*! + \qmlproperty bool BillboardTransform::preserveUpVector + + This property specifies whether the billboard transform should + preserve the "up vector" so that objects stay at right angles + to the ground plane in the scene. + + The default value for this property is false, which indicates that + the object being transformed should always face directly to the camera + This is also known as a "spherical billboard". + + If the value for this property is true, then the object will have + its up orientation preserved. This is also known as a "cylindrical + billboard". +*/ + +bool QGraphicsBillboardTransform::preserveUpVector() const +{ + Q_D(const QGraphicsBillboardTransform); + return d->preserveUpVector; +} + +void QGraphicsBillboardTransform::setPreserveUpVector(bool value) +{ + Q_D(QGraphicsBillboardTransform); + if (d->preserveUpVector != value) { + d->preserveUpVector = value; + emit transformChanged(); + emit preserveUpVectorChanged(); + } +} + +/*! + \internal +*/ +void QGraphicsBillboardTransform::applyTo(QMatrix4x4 *matrix) const +{ + Q_D(const QGraphicsBillboardTransform); + if (!d->preserveUpVector) { + // Replace the top-left 3x3 of the matrix with the identity. + // The technique is "Cheating Spherical Billboards", described here: + // http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat + (*matrix)(0, 0) = 1.0f; + (*matrix)(0, 1) = 0.0f; + (*matrix)(0, 2) = 0.0f; + (*matrix)(1, 0) = 0.0f; + (*matrix)(1, 1) = 1.0f; + (*matrix)(1, 2) = 0.0f; + (*matrix)(2, 0) = 0.0f; + (*matrix)(2, 1) = 0.0f; + (*matrix)(2, 2) = 1.0f; + } else { + // Replace some of the top-left 3x3 of the matrix with the identity, + // but leave the up vector component in the second column as-is. + // The technique is "Cheating Cylindrical Billboards", described here: + // http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat1 + (*matrix)(0, 0) = 1.0f; + (*matrix)(0, 2) = 0.0f; + (*matrix)(1, 0) = 0.0f; + (*matrix)(1, 2) = 0.0f; + (*matrix)(2, 0) = 0.0f; + (*matrix)(2, 2) = 1.0f; + } + matrix->optimize(); +} + +/*! + \internal +*/ +QGraphicsTransform3D *QGraphicsBillboardTransform::clone(QObject *parent) const +{ + Q_D(const QGraphicsBillboardTransform); + QGraphicsBillboardTransform *copy = new QGraphicsBillboardTransform(parent); + copy->setPreserveUpVector(d->preserveUpVector); + return copy; +} + +/*! + \fn void QGraphicsBillboardTransform::preserveUpVectorChanged() + + Signal that is emitted when preserveUpVector() changes. +*/ + +QT_END_NAMESPACE diff --git a/src/threed/graphicsview/qgraphicsbillboardtransform.h b/src/threed/graphicsview/qgraphicsbillboardtransform.h new file mode 100644 index 000000000..00b589997 --- /dev/null +++ b/src/threed/graphicsview/qgraphicsbillboardtransform.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSBILLBOARDTRANSFORM_H +#define QGRAPHICSBILLBOARDTRANSFORM_H + +#include "qgraphicstransform3d.h" +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGraphicsBillboardTransformPrivate; + +class Q_QT3D_EXPORT QGraphicsBillboardTransform : public QGraphicsTransform3D +{ + Q_OBJECT + Q_PROPERTY(bool preserveUpVector READ preserveUpVector WRITE setPreserveUpVector NOTIFY preserveUpVectorChanged) +public: + QGraphicsBillboardTransform(QObject *parent = 0); + ~QGraphicsBillboardTransform(); + + bool preserveUpVector() const; + void setPreserveUpVector(bool value); + + void applyTo(QMatrix4x4 *matrix) const; + QGraphicsTransform3D *clone(QObject *parent) const; + +Q_SIGNALS: + void preserveUpVectorChanged(); + +private: + QScopedPointer<QGraphicsBillboardTransformPrivate> d_ptr; + + Q_DISABLE_COPY(QGraphicsBillboardTransform) + Q_DECLARE_PRIVATE(QGraphicsBillboardTransform) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/graphicsview/qgraphicsembedscene.cpp b/src/threed/graphicsview/qgraphicsembedscene.cpp new file mode 100644 index 000000000..cc059e737 --- /dev/null +++ b/src/threed/graphicsview/qgraphicsembedscene.cpp @@ -0,0 +1,469 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicsembedscene.h" +#include "qglnamespace.h" +#include <QtOpenGL/qglframebufferobject.h> +#include <QtGui/qapplication.h> +#include <QtGui/qpainter.h> +#include <QtGui/qevent.h> +#include <QtGui/qgraphicssceneevent.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGraphicsEmbedScene + \brief The QGraphicsEmbedScene class provides a method to render a QGraphicsScene into a texture. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::graphicsview + + \section1 Rendering the scene into a texture + + Embedded scenes are rendered into textures with renderToTexture() + before the main 3D scene painting with QGLPainter starts. The following + example shows the sequence of operations for a QGLWidget: + + \code + void MyGLWidget::paintGL() + { + GLuint textureId = scene.renderToTexture(); + + QGLPainter painter(this); + painter.glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textureId); + ... // draw the 3D object + painter.glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + } + \endcode + + When using QGLView, override the QGLWidget::paintGL() function + explicitly to render scenes into textures before QGLView::paintGL() + is called: + + \code + void MyGLView::paintGL() + { + textureId = scene.renderToTexture(); + QGLView::paintGL(); + } + + void MyGLView::paintGL(QGLPainter *painter) + { + painter->glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textureId); + ... // draw the 3D object + painter->glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + } + \endcode + + The texture is oriented so that (0, 0) corresponds to the bottom-left + of the scene, and (1, 1) corresponds to the top-right of the scene. + The texture may be scaled up to a power of two in size for better + performance. + + \section1 Delivering mouse and keyboard events + + Mouse and keyboard events originate with the widget that is + displaying the 3D scene. The application needs to intercept + these events and decide which 3D object in the scene should + get the event. + + The application then performs an intersection between the 3D + object and a line in 3D space (usually a QRay3D) to determine + the texture co-ordinate of where the user clicked on the object. + How the intersection is determined is application-specific; different + algorithms are needed based on the type of object (cube, sphere, + parameterized mesh, etc). + + Once the texture co-ordinate has been determined, the application + passes the widget event and the texture co-ordinate to deliverEvent(). + QGraphicsEmbedScene will then route the event to the appropriate + graphics item in the embedded scene. + + Keyboard, focus in/out, and other non-mouse events do not need a + texture co-ordinate. Pass (0, 0) as the texture co-ordinate for + those event types. + + \section1 Updating the 3D scene + + The application will probably want to perform an update() whenever + something changes in the embedded scene. The application should + listen for the QGraphicsScene::changed() and + QGraphicsScene::sceneRectChanged() signals. + + \section1 Performance notes + + QGraphicsEmbedScene renders the scene into a framebuffer object, + which will involve GL state switching. The scene will be + effectively triple-buffered: render into the fbo, then render + the fbo into the GL window's back buffer, and finally copy to the + GL window's front buffer. + + If the scene is showing a video item or something else that is + highly animated, then it may be better to render the source into a + texture some other way. + + If the embedded scene is itself a 3D scene drawn with GL, then use + QGLPainter::pushSurface() and a QGLFramebufferObjectSurface instead + of QGraphicsEmbedScene. + + \section1 Limitations + + When rendered into a texture, the scene does not have a QGraphicsView + or a viewport QWidget associated with it. Graphics items that require a + widget rendering surface will not work correctly. +*/ + +class QGraphicsEmbedScenePrivate : public QObject +{ + Q_OBJECT +public: + QGraphicsEmbedScenePrivate(QGraphicsScene *scene, QObject *parent = 0) + : QObject(parent) + , dirty(true) + , fbo(0) + { + format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + + connect(scene, SIGNAL(changed(QList<QRectF>)), this, SLOT(update())); + connect(scene, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(update())); + } + ~QGraphicsEmbedScenePrivate() + { + delete fbo; + } + + bool dirty; + QGLFramebufferObject *fbo; + QGLFramebufferObjectFormat format; + QPoint pressedPos; + +public Q_SLOTS: + void update() { dirty = true; } +}; + +/*! + Constructs an embedded graphics scene and attaches it to \a parent. +*/ +QGraphicsEmbedScene::QGraphicsEmbedScene(QObject *parent) + : QGraphicsScene(parent) + , d_ptr(new QGraphicsEmbedScenePrivate(this)) +{ +} + +/*! + Constructs an embedded graphics scene, using \a sceneRect for its + scene rectangle, and attaches it to \a parent. +*/ +QGraphicsEmbedScene::QGraphicsEmbedScene(const QRectF &sceneRect, QObject *parent) + : QGraphicsScene(sceneRect, parent) + , d_ptr(new QGraphicsEmbedScenePrivate(this)) +{ +} + +/*! + Constructs an embedded graphics scene, using (\a x, \a y, \a width, + \a height) for its scene rectangle, and attaches it to \a parent. +*/ +QGraphicsEmbedScene::QGraphicsEmbedScene(qreal x, qreal y, qreal width, qreal height, QObject *parent) + : QGraphicsScene(x, y, width, height, parent) + , d_ptr(new QGraphicsEmbedScenePrivate(this)) +{ +} + +/*! + Destroys this embedded graphics scene. +*/ +QGraphicsEmbedScene::~QGraphicsEmbedScene() +{ +} + +/*! + Returns the framebuffer object format to use when renderToTexture() + is called. The default is a framebuffer format with depth and + stencil buffers, RGBA format, and no multisampling. + + \sa setFormat() +*/ +QGLFramebufferObjectFormat QGraphicsEmbedScene::format() const +{ + Q_D(const QGraphicsEmbedScene); + return d->format; +} + +/*! + Sets the framebuffer object \a format to use when renderToTexture() + is called. + + \sa format() +*/ +void QGraphicsEmbedScene::setFormat(const QGLFramebufferObjectFormat &format) +{ + Q_D(QGraphicsEmbedScene); + d->format = format; +} + +/*! + Renders this scene to a GL texture and returns the texture identifier. + If the scene has not changed since the last call, then the same + texture identifier will be returned without repainting the scene. + + The \a levelOfDetail is a hint that allows some control over the + texture size. The default value of 1 indicates that the texture size + should be the same or similar to the size of the scene. A value of 2 + would indicate that the texture size should have twice the width and + height. A value of 0.5 would indicate half the size. + + The primary use for \a levelOfDetail is to render the scene with a + greater level of detail when the 3D object showing the texture is + larger on-screen than the scene's natural size. The parameter can + also be used to reduce the level of detail in the texture when the + 3D object is very small. + + It is assumed that a QGLContext is bound when this function is + called. Usually this is the QGLContext that is bound by a QPainter + for 2D painting into a GL surface. Note however that a QGLPainter must + not be active on the QGLContext when this function is called. + + \sa format() +*/ +GLuint QGraphicsEmbedScene::renderToTexture(qreal levelOfDetail) +{ + Q_D(QGraphicsEmbedScene); + + // Determine the fbo size we will need. + QSize size = (sceneRect().size() * levelOfDetail).toSize(); + QSize fboSize = QGL::nextPowerOfTwo(size); + if (fboSize.isEmpty()) + fboSize = QSize(16, 16); + + // Create or re-create the fbo. + if (!d->fbo || d->fbo->size() != fboSize) { + delete d->fbo; + d->fbo = new QGLFramebufferObject(fboSize, d->format); + if (!d->fbo->isValid()) { + delete d->fbo; + d->fbo = 0; + return 0; + } + d->dirty = true; + } + + // Return the previous texture contents if the scene hasn't changed. + if (!d->dirty && d->fbo) + return d->fbo->texture(); + + // Render the scene into the fbo, scaling the QPainter's view + // transform up to the power-of-two fbo size. + QPainter painter(d->fbo); + painter.setWindow(0, 0, size.width(), size.height()); + painter.setViewport(0, 0, fboSize.width(), fboSize.height()); + render(&painter); + painter.end(); + d->dirty = false; + return d->fbo->texture(); +} + +/*! + Delivers \a event to this scene. If \a event is a mouse event, then + \a texCoord indicates the texture co-ordinate on the side of the + 3D object where the user clicked. This \a texCoord is used to determine + the actual scene co-ordinate to deliver: (0, 0) corresponds to the + bottom-left corner of the scene and (1, 1) corresponds to the + top-right corner of the scene. + + The \a event normally originates from a QWidget or QGraphicsItem that + contains the 3D object. The caller performs a ray intersection in + 3D space on the position within \a event to determine the \a texCoord + and then passes \a event on to deliverEvent() for further processing. +*/ +void QGraphicsEmbedScene::deliverEvent(QEvent *event, const QPointF &texCoord) +{ + Q_D(QGraphicsEmbedScene); + + // Map the texture co-ordinate into "screen" co-ordinates. + // Mouse move and release events can extend beyond the boundaries + // of the scene, for "click and drag off-screen" operations. + // Mouse press and double-click events need to be constrained. + QRectF bounds = sceneRect(); + int screenX = qRound(texCoord.x() * bounds.width()); + int screenY = qRound((1.0f - texCoord.y()) * bounds.height()); + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseDoubleClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + if (screenX < 0) + screenX = 0; + else if (screenX >= bounds.width()) + screenX = qRound(bounds.width() - 1); + if (screenY < 0) + screenY = 0; + else if (screenY >= bounds.height()) + screenY = qRound(bounds.height() - 1); + d->pressedPos = QPoint(screenX, screenY); + break; + default: break; + } + + // Convert the event and deliver it to the scene. + switch (event->type()) { + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseRelease: + case QEvent::GraphicsSceneMouseDoubleClick: { + QGraphicsSceneMouseEvent *ev = + static_cast<QGraphicsSceneMouseEvent *>(event); + QGraphicsSceneMouseEvent e(ev->type()); + e.setPos(QPointF(screenX, screenY)); + e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y())); + e.setScreenPos(QPoint(screenX, screenY)); + e.setButtonDownScreenPos(ev->button(), d->pressedPos); + e.setButtonDownScenePos + (ev->button(), QPointF(d->pressedPos.x() + bounds.x(), + d->pressedPos.y() + bounds.y())); + e.setButtons(ev->buttons()); + e.setButton(ev->button()); + e.setModifiers(ev->modifiers()); + e.setAccepted(false); + QApplication::sendEvent(this, &e); + } + break; + +#ifndef QT_NO_WHEELEVENT + case QEvent::GraphicsSceneWheel: { + QGraphicsSceneWheelEvent *ev = + static_cast<QGraphicsSceneWheelEvent *>(event); + QGraphicsSceneWheelEvent e(QEvent::GraphicsSceneWheel); + e.setPos(QPointF(screenX, screenY)); + e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y())); + e.setScreenPos(QPoint(screenX, screenY)); + e.setButtons(ev->buttons()); + e.setModifiers(ev->modifiers()); + e.setDelta(ev->delta()); + e.setOrientation(ev->orientation()); + e.setAccepted(false); + QApplication::sendEvent(this, &e); + } + break; +#endif + + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: { + QMouseEvent *ev = static_cast<QMouseEvent *>(event); + QEvent::Type type; + if (ev->type() == QEvent::MouseButtonPress) + type = QEvent::GraphicsSceneMousePress; + else if (ev->type() == QEvent::MouseButtonRelease) + type = QEvent::GraphicsSceneMouseRelease; + else if (ev->type() == QEvent::MouseButtonDblClick) + type = QEvent::GraphicsSceneMouseDoubleClick; + else + type = QEvent::GraphicsSceneMouseMove; + QGraphicsSceneMouseEvent e(type); + e.setPos(QPointF(screenX, screenY)); + e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y())); + e.setScreenPos(QPoint(screenX, screenY)); + e.setButtonDownScreenPos(ev->button(), d->pressedPos); + e.setButtonDownScenePos + (ev->button(), QPointF(d->pressedPos.x() + bounds.x(), + d->pressedPos.y() + bounds.y())); + e.setButtons(ev->buttons()); + e.setButton(ev->button()); + e.setModifiers(ev->modifiers()); + e.setAccepted(false); + QApplication::sendEvent(this, &e); + } + break; + +#ifndef QT_NO_WHEELEVENT + case QEvent::Wheel: { + QWheelEvent *ev = static_cast<QWheelEvent *>(event); + QGraphicsSceneWheelEvent e(QEvent::GraphicsSceneWheel); + e.setPos(QPointF(screenX, screenY)); + e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y())); + e.setScreenPos(QPoint(screenX, screenY)); + e.setButtons(ev->buttons()); + e.setModifiers(ev->modifiers()); + e.setDelta(ev->delta()); + e.setOrientation(ev->orientation()); + e.setAccepted(false); + QApplication::sendEvent(this, &e); + } + break; +#endif + + default: { + // Send the event directly without any conversion. + // Typically used for keyboard, focus, and enter/leave events. + QApplication::sendEvent(this, event); + } + break; + + } +} + +/*! + \internal +*/ +void QGraphicsEmbedScene::drawBackground(QPainter *painter, const QRectF &rect) +{ + if (backgroundBrush().style() == Qt::NoBrush) { + // Fill the fbo with the transparent color as there won't + // be a window or graphics item drawing a previous background. + painter->save(); + painter->setCompositionMode(QPainter::CompositionMode_Source); + painter->fillRect(rect, Qt::transparent); + painter->restore(); + } else { + QGraphicsScene::drawBackground(painter, rect); + } +} + +QT_END_NAMESPACE + +#include "qgraphicsembedscene.moc" diff --git a/src/threed/graphicsview/qgraphicsembedscene.h b/src/threed/graphicsview/qgraphicsembedscene.h new file mode 100644 index 000000000..44a24d1a9 --- /dev/null +++ b/src/threed/graphicsview/qgraphicsembedscene.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSEMBEDSCENE_H +#define QGRAPHICSEMBEDSCENE_H + +#include "qglnamespace.h" +#include <QtGui/qgraphicsscene.h> +#include <QtOpenGL/qgl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGraphicsEmbedScenePrivate; +class QGLFramebufferObjectFormat; + +class Q_QT3D_EXPORT QGraphicsEmbedScene : public QGraphicsScene +{ + Q_OBJECT +public: + QGraphicsEmbedScene(QObject *parent = 0); + QGraphicsEmbedScene(const QRectF &sceneRect, QObject *parent = 0); + QGraphicsEmbedScene(qreal x, qreal y, qreal width, qreal height, QObject *parent = 0); + virtual ~QGraphicsEmbedScene(); + + QGLFramebufferObjectFormat format() const; + void setFormat(const QGLFramebufferObjectFormat &format); + + GLuint renderToTexture(qreal levelOfDetail = 1.0f); + + void deliverEvent(QEvent *event, const QPointF &texCoord); + +protected: + void drawBackground(QPainter *painter, const QRectF &rect); + +private: + QScopedPointer<QGraphicsEmbedScenePrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGraphicsEmbedScene) + Q_DISABLE_COPY(QGraphicsEmbedScene) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/graphicsview/qgraphicsrotation3d.cpp b/src/threed/graphicsview/qgraphicsrotation3d.cpp new file mode 100644 index 000000000..533de4f75 --- /dev/null +++ b/src/threed/graphicsview/qgraphicsrotation3d.cpp @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicsrotation3d.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGraphicsRotation3D + \brief The QGraphicsRotation3D class supports arbitrary rotation around an axis in 3D space. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::graphicsview + + \sa QGraphicsTranslation3D, QGraphicsScale3D +*/ + +/*! + \qmlclass Rotation3D QGraphicsRotation3D + \brief The Rotation3D item supports arbitrary rotation around an axis in 3D space. + \since 4.8 + \ingroup qt3d::qml3d + + Frequently a user will create and item in the 3d world and immediately + wish to apply a rotation to that item before it is displayed, or, + optionally, perform an animation on that rotation parameter based on + user inputs, or other events. Such an rotation can easily be defined + in QML using the following code: + + \code + Item3D { + id: helicoptor + mesh: {source: "bellUH1.3ds"} + effect: Effect {} + cullFaces: "CullBackFaces" + + transform: [ + Rotation3D { + id: rotate1 + angle: 5 + axis: Qt.vector3d(1, 0, 0) + }, + Rotation3D { + id: rotate2 + angle: 5 + axis: Qt.vector3d(0, 1, 0) + }, + Rotation3D { + id: rotate3 + angle: 45 + axis: Qt.vector3d(0, 0, 1) + } + ] + + SequentialAnimation { + NumberAnimation {target: rotate1; property: "angle"; to : 360.0; duration: 3000; easing:"easeOutQuad" } + } + } + \endcode + + Notice here that we create a list of rotations for the \c transform + property of the container item. By doing this we allow rotations + around each of the axes individually in a manner which is conducive + to animation and interaction. + + Each of the rotations has an \c axis property which is a QVector3D. + This vector contains a value for each of the three components + corresponding to x, y, and z. In the above example, we first + rotate by 5 degrees about the x axis, then 5 degrees about the y + axis, and finally by 45 degrees about the z axis. + + By giving each rotation a unique \c id users can then refer to these + rotations in the QML source in order to perform rotational animations. + + \sa Translation3D, Scale3D +*/ + +class QGraphicsRotation3DPrivate +{ +public: + QGraphicsRotation3DPrivate() : axis(0, 0, 1), angle(0) {} + + QVector3D origin; + QVector3D axis; + qreal angle; +}; + +/*! + Create a 3D rotation transformation and attach it to \a parent. +*/ +QGraphicsRotation3D::QGraphicsRotation3D(QObject *parent) + : QGraphicsTransform3D(parent) + , d_ptr(new QGraphicsRotation3DPrivate) +{ +} + +/*! + Destroy this 3D rotation transformation. +*/ +QGraphicsRotation3D::~QGraphicsRotation3D() +{ +} + +/*! + \property QGraphicsRotation3D::origin + \brief the origin about which to rotate. + + The default value for this property is (0, 0, 0). +*/ + +/*! + \qmlproperty vector3D Rotation3D::origin + + The origin about which to rotate. The default value for this + property is (0, 0, 0). +*/ + +QVector3D QGraphicsRotation3D::origin() const +{ + Q_D(const QGraphicsRotation3D); + return d->origin; +} + +void QGraphicsRotation3D::setOrigin(const QVector3D &value) +{ + Q_D(QGraphicsRotation3D); + if (d->origin != value) { + d->origin = value; + emit transformChanged(); + emit originChanged(); + } +} + +/*! + \property QGraphicsRotation3D::angle + \brief the angle to rotate around the axis, in degrees anti-clockwise. + + The default value for this property is 0. +*/ + +/*! + \qmlproperty real Rotation3D::angle + + The angle to rotate around the axis, in degrees anti-clockwise. + The default value for this property is 0. +*/ + +qreal QGraphicsRotation3D::angle() const +{ + Q_D(const QGraphicsRotation3D); + return d->angle; +} + +void QGraphicsRotation3D::setAngle(qreal value) +{ + Q_D(QGraphicsRotation3D); + if (d->angle != value) { + d->angle = value; + emit transformChanged(); + emit angleChanged(); + } +} + +/*! + \property QGraphicsRotation3D::axis + \brief the axis to rotate around. + + The default value for this property is (0, 0, 1); i.e. the z-axis. +*/ + +/*! + \qmlproperty vector3D Rotation3D::axis + + The axis to rotate around. The default value for this property + is (0, 0, 1); i.e. the z-axis. +*/ + +QVector3D QGraphicsRotation3D::axis() const +{ + Q_D(const QGraphicsRotation3D); + return d->axis; +} + +void QGraphicsRotation3D::setAxis(const QVector3D &value) +{ + Q_D(QGraphicsRotation3D); + if (d->axis != value) { + d->axis = value; + emit transformChanged(); + emit axisChanged(); + } +} + +/*! + \internal +*/ +void QGraphicsRotation3D::applyTo(QMatrix4x4 *matrix) const +{ + Q_D(const QGraphicsRotation3D); + matrix->translate(d->origin); + matrix->rotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z()); + matrix->translate(-d->origin); +} + +/*! + \internal +*/ +QGraphicsTransform3D *QGraphicsRotation3D::clone(QObject *parent) const +{ + Q_D(const QGraphicsRotation3D); + QGraphicsRotation3D *copy = new QGraphicsRotation3D(parent); + copy->setOrigin(d->origin); + copy->setAxis(d->axis); + copy->setAngle(d->angle); + return copy; +} + +/*! + \fn void QGraphicsRotation3D::originChanged() + + Signal that is emitted when origin() changes. +*/ + +/*! + \fn void QGraphicsRotation3D::angleChanged() + + Signal that is emitted when angle() changes. +*/ + +/*! + \fn void QGraphicsRotation3D::axisChanged() + + Signal that is emitted when axis() changes. +*/ + +QT_END_NAMESPACE diff --git a/src/threed/graphicsview/qgraphicsrotation3d.h b/src/threed/graphicsview/qgraphicsrotation3d.h new file mode 100644 index 000000000..cf1690a0a --- /dev/null +++ b/src/threed/graphicsview/qgraphicsrotation3d.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSROTATION3D_H +#define QGRAPHICSROTATION3D_H + +#include "qgraphicstransform3d.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGraphicsRotation3DPrivate; + +class Q_QT3D_EXPORT QGraphicsRotation3D : public QGraphicsTransform3D +{ + Q_OBJECT + Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged) + Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) + Q_PROPERTY(QVector3D axis READ axis WRITE setAxis NOTIFY axisChanged) +public: + QGraphicsRotation3D(QObject *parent = 0); + ~QGraphicsRotation3D(); + + QVector3D origin() const; + void setOrigin(const QVector3D &value); + + qreal angle() const; + void setAngle(qreal value); + + QVector3D axis() const; + void setAxis(const QVector3D &value); + + void applyTo(QMatrix4x4 *matrix) const; + QGraphicsTransform3D *clone(QObject *parent) const; + +Q_SIGNALS: + void originChanged(); + void angleChanged(); + void axisChanged(); + +private: + QScopedPointer<QGraphicsRotation3DPrivate> d_ptr; + + Q_DISABLE_COPY(QGraphicsRotation3D) + Q_DECLARE_PRIVATE(QGraphicsRotation3D) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/graphicsview/qgraphicsscale3d.cpp b/src/threed/graphicsview/qgraphicsscale3d.cpp new file mode 100644 index 000000000..48803b6bf --- /dev/null +++ b/src/threed/graphicsview/qgraphicsscale3d.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicsscale3d.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGraphicsScale3D + \brief The QGraphicsScale3D class supports scaling of items in 3D. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::graphicsview + + \sa QGraphicsRotation3D, QGraphicsTranslation3D +*/ + +/*! + \qmlclass Scale3D QGraphicsScale3D + \brief The Scale3D item supports scaling of items in 3D. + \since 4.8 + \ingroup qt3d::qml3d + + 3D items in QML can have a simple scale applied directly as follows: + + \code + Item3D { + mesh: Mesh { source: "chair.3ds" } + scale: 0.5 + } + \endcode + + An alternative is to use Scale3D to apply a transform directly + to an item as part of a sequence of transformations: + + \code + Item3D { + mesh: Mesh { source: "chair.3ds" } + transform: [ + Translation3D { translate: Qt.vector3d(5, 0, 0) }, + Scale3D { scale: 0.5 } + ] + } + \endcode + + This allows the application writer to control exactly when the + scale occurs relative to other transformations. In the example + above, the item is first translated by 5 units along the x-axis, + and then the co-ordinates are scaled by half. This is distinct + from the following example which scales the object to half its + original size and then translates it by 5 units along the x-axis: + + \code + Item3D { + mesh: Mesh { source: "chair.3ds" } + transform: [ + Scale3D { scale: 0.5 }, + Translation3D { translate: Qt.vector3d(5, 0, 0) } + ] + } + \endcode + + The scale property on the item itself is applied before + any of the transforms. So the previous example is equivalent to: + + \code + Item3D { + mesh: Mesh { source: "chair.3ds" } + scale: 0.5 + transform: [ + Translation3D { translate: Qt.vector3d(5, 0, 0) } + ] + } + \endcode + + Scale values can also affect the x, y, and z axes by different amounts + by using a \c{vector3D} value: + + \code + Item3D { + mesh: Mesh { source: "chair.3ds" } + transform: [ + Scale3D { scale: Qt.vector3d(0.5, 0.2, 1.0) }, + Translation3D { translate: Qt.vector3d(5, 0, 0) } + ] + } + \endcode + + \sa Rotation3D, Translation3D +*/ + +class QGraphicsScale3DPrivate +{ +public: + QGraphicsScale3DPrivate() : scale(1, 1, 1) {} + + QVector3D origin; + QVector3D scale; +}; + +/*! + Construct a 3D scale transform and attach it to \a parent. +*/ +QGraphicsScale3D::QGraphicsScale3D(QObject *parent) + : QGraphicsTransform3D(parent), d_ptr(new QGraphicsScale3DPrivate) +{ +} + +/*! + Destroy this 3D scale transform. +*/ +QGraphicsScale3D::~QGraphicsScale3D() +{ +} + +/*! + \property QGraphicsScale3D::origin + \brief the origin about which to scale. + + The default value for this property is (0, 0, 0). +*/ + +/*! + \qmlproperty vector3D Scale3D::origin + + The origin about which to scale. The default value for this + property is (0, 0, 0). +*/ + +QVector3D QGraphicsScale3D::origin() const +{ + Q_D(const QGraphicsScale3D); + return d->origin; +} + +void QGraphicsScale3D::setOrigin(const QVector3D &value) +{ + Q_D(QGraphicsScale3D); + if (d->origin != value) { + d->origin = value; + emit transformChanged(); + emit originChanged(); + } +} + +/*! + \property QGraphicsScale3D::scale + \brief the amount with which to scale each component. + + The default value for this property is QVector3D(1, 1, 1). +*/ + +/*! + \qmlproperty vector3D Scale3D::scale + + The amount with which to scale each component. The default value for + this property is (1, 1, 1). + + This property can be specified as either a vector3D or a single + floating-point value. A single floating-point value will set + the x, y, and z scale components to the same value. In other words, + the following two transformations are equivalent: + + \code + Scale3D { scale: 2 } + Scale3D { scale: Qt.vector3d(2, 2, 2) } + \endcode +*/ + +QVector3D QGraphicsScale3D::scale() const +{ + Q_D(const QGraphicsScale3D); + return d->scale; +} + +void QGraphicsScale3D::setScale(const QVector3D &value) +{ + Q_D(QGraphicsScale3D); + if (d->scale != value) { + d->scale = value; + emit transformChanged(); + emit scaleChanged(); + } +} + +/*! + \internal +*/ +void QGraphicsScale3D::applyTo(QMatrix4x4 *matrix) const +{ + Q_D(const QGraphicsScale3D); + matrix->translate(d->origin); + matrix->scale(d->scale); + matrix->translate(-d->origin); +} + +/*! + \internal +*/ +QGraphicsTransform3D *QGraphicsScale3D::clone(QObject *parent) const +{ + Q_D(const QGraphicsScale3D); + QGraphicsScale3D *copy = new QGraphicsScale3D(parent); + copy->setOrigin(d->origin); + copy->setScale(d->scale); + return copy; +} + +/*! + \fn void QGraphicsScale3D::originChanged() + + Signal that is emitted when origin() changes. +*/ + +/*! + \fn void QGraphicsScale3D::scaleChanged() + + Signal that is emitted when scale() changes. +*/ + +QT_END_NAMESPACE diff --git a/src/threed/graphicsview/qgraphicsscale3d.h b/src/threed/graphicsview/qgraphicsscale3d.h new file mode 100644 index 000000000..2c648e285 --- /dev/null +++ b/src/threed/graphicsview/qgraphicsscale3d.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSCALE3D_H +#define QGRAPHICSSCALE3D_H + +#include "qgraphicstransform3d.h" +#include <QtCore/qscopedpointer.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGraphicsScale3DPrivate; + +class Q_QT3D_EXPORT QGraphicsScale3D : public QGraphicsTransform3D +{ + Q_OBJECT + Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged) + Q_PROPERTY(QVector3D scale READ scale WRITE setScale NOTIFY scaleChanged) +public: + QGraphicsScale3D(QObject *parent = 0); + ~QGraphicsScale3D(); + + QVector3D origin() const; + void setOrigin(const QVector3D &value); + + QVector3D scale() const; + void setScale(const QVector3D &value); + + void applyTo(QMatrix4x4 *matrix) const; + QGraphicsTransform3D *clone(QObject *parent) const; + +Q_SIGNALS: + void originChanged(); + void scaleChanged(); + +private: + QScopedPointer<QGraphicsScale3DPrivate> d_ptr; + + Q_DISABLE_COPY(QGraphicsScale3D) + Q_DECLARE_PRIVATE(QGraphicsScale3D) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/graphicsview/qgraphicstransform3d.cpp b/src/threed/graphicsview/qgraphicstransform3d.cpp new file mode 100644 index 000000000..8e72d2a62 --- /dev/null +++ b/src/threed/graphicsview/qgraphicstransform3d.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicstransform3d.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGraphicsTransform3D + \brief The QGraphicsTransform3D class is an abstract base class for building 3D transformations. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::graphicsview + + QGraphicsTransform3D lets you create and control advanced transformations + that can be configured independently using specialized properties. + Scene nodes have an associated list of transforms, which are applied + in order, one at a time, to the modelview matrix. Transformations are + computed in true 3D space using QMatrix4x4. + + QGraphicsTransform3D is particularly useful for animations. Whereas + QGLPainter::modelViewMatrix() lets you assign any transform directly, + there is no direct way to interpolate between two different + transformations (e.g., when transitioning between two states, each for + which the item has a different arbitrary transform assigned). Using + QGraphicsTransform3D you can interpolate the property values of each + independent transformation. The resulting operation is then combined into a + single transform which is applied to the modelview matrix during drawing. + + If you want to create your own configurable transformation, you can create + a subclass of QGraphicsTransform3D (or any or the existing subclasses), and + reimplement the pure virtual applyTo() function, which takes a pointer to a + QMatrix4x4. Each operation you would like to apply should be exposed as + properties (e.g., customTransform->setVerticalShear(2.5)). Inside you + reimplementation of applyTo(), you can modify the provided transform + respectively. + + \sa QGraphicsScale3D, QGraphicsRotation3D, QGraphicsTranslation3D + \sa QGraphicsBillboardTransform +*/ + +/*! + \fn QGraphicsTransform3D::QGraphicsTransform3D(QObject *parent) + + Constructs a 3D transformation and attaches it to \a parent. +*/ + +/*! + \fn QGraphicsTransform3D::~QGraphicsTransform3D() + + Destroys this 3D transformation. +*/ + +/*! + \fn void QGraphicsTransform3D::applyTo(QMatrix4x4 *matrix) const + + Applies the effect of this transformation to the specified + modelview \a matrix. +*/ + +/*! + \fn QGraphicsTransform3D *QGraphicsTransform3D::clone(QObject *parent) const + + Clones a copy of this transformation and attaches the clone to \a parent. +*/ + +/*! + \fn void QGraphicsTransform3D::transformChanged() + + Signal that is emitted whenever any of the transformation's + parameters are changed. +*/ + +QT_END_NAMESPACE diff --git a/src/threed/graphicsview/qgraphicstransform3d.h b/src/threed/graphicsview/qgraphicstransform3d.h new file mode 100644 index 000000000..84a544932 --- /dev/null +++ b/src/threed/graphicsview/qgraphicstransform3d.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSTRANSFORM3D_H +#define QGRAPHICSTRANSFORM3D_H + +#include <QtCore/qobject.h> +#include <QtGui/qmatrix4x4.h> +#include "qt3dglobal.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Q_QT3D_EXPORT QGraphicsTransform3D : public QObject +{ + Q_OBJECT +public: + QGraphicsTransform3D(QObject *parent = 0) : QObject(parent) {} + ~QGraphicsTransform3D() {} + + virtual void applyTo(QMatrix4x4 *matrix) const = 0; + virtual QGraphicsTransform3D *clone(QObject *parent = 0) const = 0; + +Q_SIGNALS: + void transformChanged(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/graphicsview/qgraphicstranslation3d.cpp b/src/threed/graphicsview/qgraphicstranslation3d.cpp new file mode 100644 index 000000000..446b61380 --- /dev/null +++ b/src/threed/graphicsview/qgraphicstranslation3d.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicstranslation3d.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGraphicsTranslation3D + \brief The QGraphicsTranslation3D class supports translation of 3D items. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::graphicsview + + QGraphicsTranslation3D is derived directly from QGraphicsTransform, and + provides a \l translate property to specify the 3D vector to + apply to incoming co-ordinates. + + The \l progress property can be used to perform animation along a + translation vector by varying the progress value between 0 and 1. + Overshoot animations are also possible by setting the progress + value to something outside this range. The default progress + value is 1. + + \sa QGraphicsRotation3D, QGraphicsScale3D +*/ + +/*! + \qmlclass Translation3D QGraphicsTranslation3D + \brief The Translation3D item supports translation of items in 3D. + \since 4.8 + \ingroup qt3d::qml3d + + 3D items in QML are typically positioned directly as follows: + + \code + Item3D { + mesh: Mesh { source: "chair.3ds" } + position: Qt.vector3d(0, 5, 10) + } + \endcode + + However, it can sometimes be useful to translate an object along a + vector under the control of an animation. The Translate3D + element can be used for this purpose. The following example + translates the object along a straight-line path 5 units to + the right of its original position, and then back again: + + \code + Item3D { + mesh: Mesh { source: "chair.3ds" } + position: Qt.vector3d(0, 5, 10) + transform: [ + Translation3D { + translate: Qt.vector3d(5, 0, 0) + SequentialAnimation on progress { + running: true + loops: Animation.Infinite + NumberAnimation { to : 1.0; duration: 300 } + NumberAnimation { to : 0.0; duration: 300 } + } + } + ] + } + \endcode + + \sa Rotation3D, Scale3D +*/ + +class QGraphicsTranslation3DPrivate +{ +public: + QGraphicsTranslation3DPrivate() : progress(1.0f) {} + + QVector3D translate; + qreal progress; +}; + +/*! + Constructs a new translation and attaches it to \a parent. +*/ +QGraphicsTranslation3D::QGraphicsTranslation3D(QObject *parent) + : QGraphicsTransform3D(parent), d_ptr(new QGraphicsTranslation3DPrivate) +{ +} + +/*! + Destroys this translation. +*/ +QGraphicsTranslation3D::~QGraphicsTranslation3D() +{ +} + +/*! + \property QGraphicsTranslation3D::translate + \brief the translation to apply to incoming co-ordinates. + + The default value for this property is (0, 0, 0). +*/ + +/*! + \qmlproperty vector3D Translation3D::translate + + The translation to apply to incoming co-ordinates. The default value + for this property is (0, 0, 0). +*/ + +QVector3D QGraphicsTranslation3D::translate() const +{ + Q_D(const QGraphicsTranslation3D); + return d->translate; +} + +void QGraphicsTranslation3D::setTranslate(const QVector3D &value) +{ + Q_D(QGraphicsTranslation3D); + if (d->translate != value) { + d->translate = value; + emit transformChanged(); + emit translateChanged(); + } +} + +/*! + \property QGraphicsTranslation3D::progress + \brief the progress along the translation vector, from 0 to 1. + + The default value for this property is 1. + + This property can be used to perform animation along a translation + vector by varying the progress between 0 and 1. Overshoot animations + are also possible by setting the value to something outside this range. +*/ + +/*! + \qmlproperty real Translation3D::progress + + The progress along the translation vector, from 0 to 1. The default + value for this property is 1. + + This property can be used to perform animation along a translation + vector by varying the progress between 0 and 1. Overshoot animations + are also possible by setting the value to something outside this range. +*/ + +qreal QGraphicsTranslation3D::progress() const +{ + Q_D(const QGraphicsTranslation3D); + return d->progress; +} + +void QGraphicsTranslation3D::setProgress(qreal value) +{ + Q_D(QGraphicsTranslation3D); + if (d->progress != value) { + d->progress = value; + emit transformChanged(); + emit progressChanged(); + } +} + +/*! + \internal +*/ +void QGraphicsTranslation3D::applyTo(QMatrix4x4 *matrix) const +{ + Q_D(const QGraphicsTranslation3D); + matrix->translate(d->translate * d->progress); +} + +/*! + \internal +*/ +QGraphicsTransform3D *QGraphicsTranslation3D::clone(QObject *parent) const +{ + Q_D(const QGraphicsTranslation3D); + QGraphicsTranslation3D *copy = new QGraphicsTranslation3D(parent); + copy->setTranslate(d->translate); + copy->setProgress(d->progress); + return copy; +} + +/*! + \fn void QGraphicsTranslation3D::translateChanged() + + Signal that is emitted when translate() changes. +*/ + +/*! + \fn void QGraphicsTranslation3D::progressChanged() + + Signal that is emitted when progress() changes. +*/ + +QT_END_NAMESPACE diff --git a/src/threed/graphicsview/qgraphicstranslation3d.h b/src/threed/graphicsview/qgraphicstranslation3d.h new file mode 100644 index 000000000..c2a3e1633 --- /dev/null +++ b/src/threed/graphicsview/qgraphicstranslation3d.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSTRANSLATION3D_H +#define QGRAPHICSTRANSLATION3D_H + +#include "qgraphicstransform3d.h" +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGraphicsTranslation3DPrivate; + +class Q_QT3D_EXPORT QGraphicsTranslation3D : public QGraphicsTransform3D +{ + Q_OBJECT + Q_PROPERTY(QVector3D translate READ translate WRITE setTranslate NOTIFY translateChanged) + Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged) +public: + QGraphicsTranslation3D(QObject *parent = 0); + ~QGraphicsTranslation3D(); + + QVector3D translate() const; + void setTranslate(const QVector3D &value); + + qreal progress() const; + void setProgress(qreal value); + + void applyTo(QMatrix4x4 *matrix) const; + QGraphicsTransform3D *clone(QObject *parent) const; + +Q_SIGNALS: + void translateChanged(); + void progressChanged(); + +private: + QScopedPointer<QGraphicsTranslation3DPrivate> d_ptr; + + Q_DISABLE_COPY(QGraphicsTranslation3D) + Q_DECLARE_PRIVATE(QGraphicsTranslation3D) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/materials/materials.pri b/src/threed/materials/materials.pri new file mode 100644 index 000000000..3eb8e0173 --- /dev/null +++ b/src/threed/materials/materials.pri @@ -0,0 +1,18 @@ + +INCLUDEPATH += $$PWD +VPATH += $$PWD + +HEADERS += \ + qglabstractmaterial.h \ + qglcolormaterial.h \ + qglmaterial.h \ + qgltwosidedmaterial.h + +SOURCES += \ + qglabstractmaterial.cpp \ + qglcolormaterial.cpp \ + qglmaterial.cpp \ + qgltwosidedmaterial.cpp + +PRIVATE_HEADERS += \ + qglmaterial_p.h diff --git a/src/threed/materials/qglabstractmaterial.cpp b/src/threed/materials/qglabstractmaterial.cpp new file mode 100644 index 000000000..c0b37dc37 --- /dev/null +++ b/src/threed/materials/qglabstractmaterial.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglabstractmaterial.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLAbstractMaterial + \since 4.8 + \brief The QGLAbstractMaterial class provides a standard interface for rendering surface materials with GL. + \ingroup qt3d + \ingroup qt3d::materials + + Materials are the primary method to specify the surface appearance of an + object, as distinct from the geometry for the object. Materials have an + almost endless variety of parameters: + + \list + \o Properties of the material under various lighting conditions; + i.e., the traditional parameters for ambient, diffuse, specular, etc. + \o Textures in multiple layers, with different combination modes; + decal, modulate, replace, etc. + \o Environmental conditions such as fogging. + \o Alpha values for opacity and blending. + \o Interpolation factors for animated surface effects. + \o etc + \endlist + + QGLAbstractMaterial is the base class for all such materials. + It provides a very simple API to bind() a material to a QGLPainter + when the material needs to be rendered, to release() a material + from a QGLPainter when the material is no longer needed, and to + prepareToDraw(). + + Subclasses of QGLAbstractMaterial implement specific materials. + QGLMaterial provides the traditional ambient, diffuse, specular, etc + parameters for lighting properties. + + Materials are distinct from \i effects, which have the base class + QGLAbstractEffect. Effects are typically shader programs that are + used to render a specific type of material. The material's bind() + function will use QGLPainter::setStandardEffect() or + QGLPainter::setUserEffect() to select the exact effect that is + needed to render the material. + + It is possible that the same material may be rendered with different + effects depending upon the material parameters. For example, a lit + material may select a simpler and more efficient shader program effect + if the material has the default spotlight properties, or if the + QGLPainter only has a single light source specified. + + \sa QGLMaterial, QGLAbstractEffect +*/ + +/*! + Constructs a new material and attaches it to \a parent. +*/ +QGLAbstractMaterial::QGLAbstractMaterial(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys this material. +*/ +QGLAbstractMaterial::~QGLAbstractMaterial() +{ +} + +/*! + Returns the material lighting parameters for rendering the front + faces of fragments with this abstract material. The default + implementation returns null. + + This function is provided as a convenience for applications that + wish to alter the lighting parameters or textures of a material, + without regard to any wrapping that has been performed to add + blending or other options. + + \sa back(), QGLMaterial +*/ +QGLMaterial *QGLAbstractMaterial::front() const +{ + return 0; +} + +/*! + Returns the material lighting parameters for rendering the back + faces of fragments with this abstract material. The default + implementation returns null, which indicates that front() + is also used to render back faces. + + \sa front(), QGLMaterial +*/ +QGLMaterial *QGLAbstractMaterial::back() const +{ + return 0; +} + +/*! + \fn bool QGLAbstractMaterial::isTransparent() const + + Returns true if this material is transparent and will therefore + require the \c{GL_BLEND} mode to be enabled to render the material. + Returns false if the material is fully opaque. +*/ + +/*! + \fn void QGLAbstractMaterial::bind(QGLPainter *painter) + + Binds resources to \a painter that are needed to render this + material; textures, shader programs, blending modes, etc. + + In the following example, lit material parameters and a texture + are bound to the \a painter, for rendering with the standard + QGL::LitModulateTexture2D effect: + + \code + void TexturedLitMaterial::bind(QGLPainter *painter) + { + painter->setStandardEffect(QGL::LitModulateTexture2D); + painter->setFaceMaterial(QGL::AllFaces, material); + painter->glActiveTexture(GL_TEXTURE0); + texture->bind(); + } + \endcode + + Normally the effect is bound to \a painter during the bind() + function. However, some materials may need to use different + effects depending upon which attributes are present in the + geometry. For example, a per-vertex color effect instead of a + uniform color effect if the geometry has the QGL::Color attribute. + The prepareToDraw() function can be overridden to alter the + effect once the specific set of geometry attributes are known. + + \sa release(), prepareToDraw() +*/ + +/*! + \fn void QGLAbstractMaterial::release(QGLPainter *painter, QGLAbstractMaterial *next) + + Releases resources from \a painter that were used to render this + material. The QGLPainter::effect() can be left bound to \a painter, + but other resources such as textures and blending modes + should be disabled. + + If \a next is not null, then it indicates the next material that + will be bound to \a painter. If \a next is the same type of + material as this material, then this function can choose not to + release resources that would be immediately re-bound to + \a painter in the next call to bind(). + + In the following example, texture unit 0 is unbound if \a painter + is about to switch to another effect that is not an instance + of \c{TexturedLitMaterial}: + + \code + void TexturedLitMaterial::release(QGLPainter *painter, QGLAbstractMaterial *next) + { + if (!qobject_cast<TexturedLitMaterial *>(next)) { + painter->glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + } + } + \endcode + + \sa bind(), prepareToDraw() +*/ + +/*! + Prepares to draw geometry to \a painter that has the specified + set of vertex \a attributes. The default implementation + does nothing. + + Multiple effects may be used to render some materials depending upon + the available vertex attributes. For example, if QGL::Color is + present in \a attributes, then a per-vertex color should be used + instead of a single uniform color. + + This function is provided for such materials to have one last + chance during QGLPainter::draw() to alter the QGLPainter::effect() + to something that is tuned for the specific geometry. It can + be assumed that bind() has already been called on this material. + + \sa bind(), release() +*/ +void QGLAbstractMaterial::prepareToDraw(QGLPainter *painter, const QGLAttributeSet &attributes) +{ + Q_UNUSED(painter); + Q_UNUSED(attributes); +} + +/*! + \fn void QGLAbstractMaterial::materialChanged() + + Signal that is emitted when an object that is using this material + should be redrawn because some property on the material has changed. +*/ + +QT_END_NAMESPACE diff --git a/src/threed/materials/qglabstractmaterial.h b/src/threed/materials/qglabstractmaterial.h new file mode 100644 index 000000000..b5a60e5ae --- /dev/null +++ b/src/threed/materials/qglabstractmaterial.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLABSTRACTMATERIAL_H +#define QGLABSTRACTMATERIAL_H + +#include <QtCore/qobject.h> +#include "qglattributeset.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLPainter; +class QGLMaterial; + +class Q_QT3D_EXPORT QGLAbstractMaterial : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(QGLAbstractMaterial) +public: + explicit QGLAbstractMaterial(QObject *parent = 0); + ~QGLAbstractMaterial(); + + virtual QGLMaterial *front() const; + virtual QGLMaterial *back() const; + + virtual bool isTransparent() const = 0; + + virtual void bind(QGLPainter *painter) = 0; + virtual void release(QGLPainter *painter, QGLAbstractMaterial *next) = 0; + virtual void prepareToDraw(QGLPainter *painter, const QGLAttributeSet &attributes); + +Q_SIGNALS: + void materialChanged(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/materials/qglcolormaterial.cpp b/src/threed/materials/qglcolormaterial.cpp new file mode 100644 index 000000000..4a507f12a --- /dev/null +++ b/src/threed/materials/qglcolormaterial.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglcolormaterial.h" +#include "qglpainter.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLColorMaterial + \since 4.8 + \brief The QGLColorMaterial class implements flat or per-vertex color materials for 3D rendering. + \ingroup qt3d + \ingroup qt3d::materials + + When bound to a QGLPainter, QGLColorMaterial will select a flat + color drawing effect, to render fragments with color(), ignoring + any lights or textures that may be active on the QGLPainter state. + If the geometry has the QGL::Color attribute, then a per-vertex + color will be used instead and color() is ignored. + + \sa QGLMaterial +*/ + +class QGLColorMaterialPrivate +{ +public: + QGLColorMaterialPrivate() : color(255, 255, 255, 255) {} + + QColor color; +}; + +/*! + Constructs a new flat color material and attaches it to \a parent. +*/ +QGLColorMaterial::QGLColorMaterial(QObject *parent) + : QGLAbstractMaterial(parent) + , d_ptr(new QGLColorMaterialPrivate) +{ +} + +/*! + Destroys this flat color material. +*/ +QGLColorMaterial::~QGLColorMaterial() +{ +} + +/*! + \property QGLColorMaterial::color + \brief the flat color to use to render the material. The default + color is white. + + If the geometry has per-vertex colors, then this property is ignored. + + \sa colorChanged() +*/ + +QColor QGLColorMaterial::color() const +{ + Q_D(const QGLColorMaterial); + return d->color; +} + +void QGLColorMaterial::setColor(const QColor &color) +{ + Q_D(QGLColorMaterial); + if (d->color != color) { + d->color = color; + emit colorChanged(); + emit materialChanged(); + } +} + +/*! + \reimp +*/ +bool QGLColorMaterial::isTransparent() const +{ + Q_D(const QGLColorMaterial); + return d->color.alpha() != 255; +} + +/*! + \reimp +*/ +void QGLColorMaterial::bind(QGLPainter *painter) +{ + Q_D(const QGLColorMaterial); + painter->setColor(d->color); + // Effect is set during prepareToDraw(). +} + +/*! + \reimp +*/ +void QGLColorMaterial::release(QGLPainter *painter, QGLAbstractMaterial *next) +{ + // No textures or other modes, so nothing to do here. + Q_UNUSED(painter); + Q_UNUSED(next); +} + +/*! + \reimp +*/ +void QGLColorMaterial::prepareToDraw + (QGLPainter *painter, const QGLAttributeSet &attributes) +{ + if (attributes.contains(QGL::Color)) + painter->setStandardEffect(QGL::FlatPerVertexColor); + else + painter->setStandardEffect(QGL::FlatColor); +} + +/*! + \fn void QGLColorMaterial::colorChanged() + + Signal that is emitted when color() changes. + + \sa color() +*/ + +QT_END_NAMESPACE diff --git a/src/threed/materials/qglcolormaterial.h b/src/threed/materials/qglcolormaterial.h new file mode 100644 index 000000000..6ec953ecd --- /dev/null +++ b/src/threed/materials/qglcolormaterial.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLCOLORMATERIAL_H +#define QGLCOLORMATERIAL_H + +#include "qglabstractmaterial.h" +#include <QtGui/qcolor.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLColorMaterialPrivate; + +class Q_QT3D_EXPORT QGLColorMaterial : public QGLAbstractMaterial +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGLColorMaterial) + Q_DISABLE_COPY(QGLColorMaterial) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) +public: + explicit QGLColorMaterial(QObject *parent = 0); + ~QGLColorMaterial(); + + QColor color() const; + void setColor(const QColor &color); + + bool isTransparent() const; + void bind(QGLPainter *painter); + void release(QGLPainter *painter, QGLAbstractMaterial *next); + void prepareToDraw(QGLPainter *painter, const QGLAttributeSet &attributes); + +Q_SIGNALS: + void colorChanged(); + +private: + QScopedPointer<QGLColorMaterialPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/materials/qglmaterial.cpp b/src/threed/materials/qglmaterial.cpp new file mode 100644 index 000000000..4d7d4d683 --- /dev/null +++ b/src/threed/materials/qglmaterial.cpp @@ -0,0 +1,586 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglmaterial.h" +#include "qglmaterial_p.h" +#include "qglpainter.h" +#include "qgltexture2d.h" +#include "qglmaterialcollection.h" +#include "qgllightmodel.h" +#include "qfileinfo.h" + +#include <QtCore/qurl.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLMaterial + \brief The QGLMaterial class describes one-sided material properties for rendering fragments. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::materials + + \sa QGLTwoSidedMaterial +*/ + +/*! + \qmlclass Material QGLMaterial + \brief The Material item describes material properties for OpenGL drawing. + \since 4.8 + \ingroup qt3d::qml3d +*/ + +QGLMaterialPrivate::QGLMaterialPrivate() + : specularColor(0, 0, 0, 255), + emittedLight(0, 0, 0, 255), + shininess(0), + collection(0), + index(-1), + used(false) +{ + ambientColor.setRgbF(0.2f, 0.2f, 0.2f, 1.0f); + diffuseColor.setRgbF(0.8f, 0.8f, 0.8f, 1.0f); +} + +/*! + Constructs a QGLMaterial object with its default values, + and attaches it to \a parent. +*/ +QGLMaterial::QGLMaterial(QObject *parent) + : QGLAbstractMaterial(parent) + , d_ptr(new QGLMaterialPrivate) +{ +} + +/*! + Destroys this QGLMaterial object. +*/ +QGLMaterial::~QGLMaterial() +{ +} + +/*! + \property QGLMaterial::ambientColor + \brief the ambient color of the material. The default value + is (0.2, 0.2, 0.2, 1.0). + + \sa diffuseColor(), specularColor(), ambientColorChanged() +*/ + +/*! + \qmlproperty color Material::ambientColor + The ambient color of the material. The default value + is (0.2, 0.2, 0.2, 1.0). + + \sa diffuseColor, specularColor +*/ + +QColor QGLMaterial::ambientColor() const +{ + Q_D(const QGLMaterial); + return d->ambientColor; +} + +void QGLMaterial::setAmbientColor(const QColor& value) +{ + Q_D(QGLMaterial); + if (d->ambientColor != value) { + d->ambientColor = value; + emit ambientColorChanged(); + emit materialChanged(); + } +} + +/*! + \property QGLMaterial::diffuseColor + \brief the diffuse color of the material. The default value + is (0.8, 0.8, 0.8, 1.0). + + \sa ambientColor(), specularColor(), diffuseColorChanged() +*/ + +/*! + \qmlproperty color Material::diffuseColor + The diffuse color of the material. The default value + is (0.8, 0.8, 0.8, 1.0). + + \sa ambientColor, specularColor +*/ + +QColor QGLMaterial::diffuseColor() const +{ + Q_D(const QGLMaterial); + return d->diffuseColor; +} + +void QGLMaterial::setDiffuseColor(const QColor& value) +{ + Q_D(QGLMaterial); + if (d->diffuseColor != value) { + d->diffuseColor = value; + emit diffuseColorChanged(); + emit materialChanged(); + } +} + +/*! + \property QGLMaterial::specularColor + \brief the specular color of the material. The default value + is (0, 0, 0, 1). + + \sa ambientColor(), diffuseColor(), specularColorChanged() +*/ + +/*! + \qmlproperty color Material::specularColor + The specular color of the material. The default value + is (0, 0, 0, 1). + + \sa ambientColor, diffuseColor +*/ + +QColor QGLMaterial::specularColor() const +{ + Q_D(const QGLMaterial); + return d->specularColor; +} + +void QGLMaterial::setSpecularColor(const QColor& value) +{ + Q_D(QGLMaterial); + if (d->specularColor != value) { + d->specularColor = value; + emit specularColorChanged(); + emit materialChanged(); + } +} + +/*! + \property QGLMaterial::emittedLight + \brief the emitted light intensity of the material. + The default value is (0.0, 0.0, 0.0, 1.0), which indicates + that the material does not emit any light. + + \sa emittedLightChanged() +*/ + +/*! + \qmlproperty color Material::emittedLight + The emitted light intensity of the material. + The default value is (0.0, 0.0, 0.0, 1.0), which indicates + that the material does not emit any light. +*/ + +QColor QGLMaterial::emittedLight() const +{ + Q_D(const QGLMaterial); + return d->emittedLight; +} + +void QGLMaterial::setEmittedLight(const QColor& value) +{ + Q_D(QGLMaterial); + if (d->emittedLight != value) { + d->emittedLight = value; + emit emittedLightChanged(); + emit materialChanged(); + } +} + +/*! + Sets ambientColor() to 20% of \a value, and diffuseColor() to 80% of + \a value. This is a convenience function for quickly setting ambient + and diffuse lighting colors based on a flat color. + + \sa ambientColor(), diffuseColor() +*/ +void QGLMaterial::setColor(const QColor& value) +{ + Q_D(QGLMaterial); + d->ambientColor.setRgbF + (value.redF() * 0.2f, value.greenF() * 0.2f, + value.blueF() * 0.2f, value.alphaF()); + d->diffuseColor.setRgbF + (value.redF() * 0.8f, value.greenF() * 0.8f, + value.blueF() * 0.8f, value.alphaF()); + emit ambientColorChanged(); + emit diffuseColorChanged(); + emit materialChanged(); +} + +/*! + \property QGLMaterial::shininess + \brief the specular exponent of the material, or how shiny it is. + Must be between 0 and 128. The default value is 0. A value outside + this range will be clamped to the range when the property is set. + + \sa shininessChanged() +*/ + +/*! + \qmlproperty real Material::shininess + The specular exponent of the material, or how shiny it is. + Must be between 0 and 128. The default value is 0. A value outside + this range will be clamped to the range when the property is set. +*/ + +qreal QGLMaterial::shininess() const +{ + Q_D(const QGLMaterial); + return d->shininess; +} + +void QGLMaterial::setShininess(qreal value) +{ + Q_D(QGLMaterial); + if (value < 0) + value = 0; + else if (value > 128) + value = 128; + if (d->shininess != value) { + d->shininess = value; + emit shininessChanged(); + emit materialChanged(); + } +} + +/*! + \property QGLMaterial::texture + \brief the 2D texture associated with \a layer on this material; + null if no texture. + + Layer 0 is normally the primary texture associated with the material. + Multiple texture layers may be specified for materials with special + blending effects or to specify ambient, diffuse, or specular colors + pixel-by-pixel. + + \sa texturesChanged() +*/ +QGLTexture2D *QGLMaterial::texture(int layer) const +{ + Q_D(const QGLMaterial); + return d->textures.value(layer, 0); +} + +void QGLMaterial::setTexture(QGLTexture2D *value, int layer) +{ + Q_ASSERT(layer >= 0); + Q_D(QGLMaterial); + QGLTexture2D *prev = d->textures.value(layer, 0); + if (prev != value) { + delete prev; + d->textures[layer] = value; + emit texturesChanged(); + emit materialChanged(); + } +} + +/*! + \property QGLMaterial::textureUrl + \brief URL of the 2D texture associated with \a layer on this material. + + By default \a layer is 0, the primary texture. + + If the URL has not been specified, then this property is a null QUrl. + + Setting this property to a non-empty URL will replace any existing texture + with a new texture based on the image at the given \a url. If that + image is not a valid texture then the new texture will be a null texture. + + If an empty url is set, this has the same effect as \c{setTexture(0)}. + + \sa texture(), setTexture() +*/ +QUrl QGLMaterial::textureUrl(int layer) const +{ + Q_D(const QGLMaterial); + QGLTexture2D *tex = d->textures.value(layer, 0); + if (tex) + return tex->url(); + else + return QUrl(); +} + +void QGLMaterial::setTextureUrl(const QUrl &url, int layer) +{ + Q_ASSERT(layer >= 0); + if (textureUrl(layer) != url) + { + QGLTexture2D *tex = 0; + if (!url.isEmpty()) + { + tex = new QGLTexture2D(this); + tex->setUrl(url); + } + setTexture(tex, layer); + } +} + +/*! + \enum QGLMaterial::TextureCombineMode + This enum defines the mode to use when combining a texture with + the material colors on a QGLMaterial object. + + \value Modulate Modulate the texture with the lighting + conditions to produce a lit texture. + \value Decal Combine the texture with the lighting conditions + to produce a decal effect. + \value Replace Replace with the contents of the texture, + ignoring colors and lighting conditions. +*/ + +/*! + \property QGLMaterial::textureCombineMode + \brief the texture combine mode associated with \a layer on this material. + The default value is \l Modulate. + + \sa texturesChanged() +*/ + +QGLMaterial::TextureCombineMode QGLMaterial::textureCombineMode(int layer) const +{ + Q_D(const QGLMaterial); + return d->textureModes.value(layer, Modulate); +} + +void QGLMaterial::setTextureCombineMode(QGLMaterial::TextureCombineMode mode, int layer) +{ + Q_D(QGLMaterial); + if (d->textureModes.value(layer, Modulate) != mode) { + d->textureModes[layer] = mode; + emit texturesChanged(); + emit materialChanged(); + } +} + +/*! + Returns the number of texture layers associated with this material. + + The return value may be larger than the number of actual texture + layers if some of the intermediate layers are null. For example, + setting layers 0 and 2 will report textureLayerCount() as 3. + The main use of this value is to iterate over all layers. + + \sa texture() +*/ +int QGLMaterial::textureLayerCount() const +{ + Q_D(const QGLMaterial); + int maxLayer = -1; + if (!d->textures.isEmpty()) + maxLayer = qMax(maxLayer, (d->textures.end() - 1).key()); + return maxLayer + 1; +} + +/*! + \reimp + Returns this material. +*/ +QGLMaterial *QGLMaterial::front() const +{ + return const_cast<QGLMaterial *>(this); +} + +/*! + \reimp +*/ +bool QGLMaterial::isTransparent() const +{ + Q_D(const QGLMaterial); + bool transparent = (d->diffuseColor.alpha() != 255); + QMap<int, QGLTexture2D *>::ConstIterator it; + for (it = d->textures.begin(); it != d->textures.end(); ++it) { + TextureCombineMode mode = d->textureModes.value(it.key(), Modulate); + if (mode == Modulate) { + // Texture alpha adds to the current alpha. + if (it.value() && it.value()->hasAlphaChannel()) + transparent = true; + } else if (mode == Replace) { + // Replace the current alpha with the texture's alpha. + if (it.value()) + transparent = it.value()->hasAlphaChannel(); + } + } + return transparent; +} + +/*! + \reimp +*/ +void QGLMaterial::bind(QGLPainter *painter) +{ + painter->setFaceMaterial(QGL::AllFaces, this); + const_cast<QGLLightModel *>(painter->lightModel()) + ->setModel(QGLLightModel::OneSided); // FIXME + bindTextures(painter); +} + +/*! + \internal +*/ +void QGLMaterial::bindTextures(QGLPainter *painter) +{ + Q_D(const QGLMaterial); + QMap<int, QGLTexture2D *>::ConstIterator it; + for (it = d->textures.begin(); it != d->textures.end(); ++it) { + QGLTexture2D *tex = it.value(); + painter->glActiveTexture(GL_TEXTURE0 + it.key()); + if (tex) + tex->bind(); + else + glBindTexture(GL_TEXTURE_2D, 0); + } +} + +/*! + \reimp +*/ +void QGLMaterial::release(QGLPainter *painter, QGLAbstractMaterial *next) +{ + Q_UNUSED(next); + Q_D(const QGLMaterial); + QMap<int, QGLTexture2D *>::ConstIterator it; + for (it = d->textures.begin(); it != d->textures.end(); ++it) { + painter->glActiveTexture(GL_TEXTURE0 + it.key()); + glBindTexture(GL_TEXTURE_2D, 0); + } +} + +/*! + \reimp +*/ +void QGLMaterial::prepareToDraw + (QGLPainter *painter, const QGLAttributeSet &attributes) +{ + bindEffect(painter, attributes, false); +} + +/*! + \internal +*/ +void QGLMaterial::bindEffect + (QGLPainter *painter, const QGLAttributeSet &attributes, bool twoSided) +{ + Q_D(const QGLMaterial); + Q_UNUSED(twoSided); + QGL::StandardEffect effect = QGL::LitMaterial; + if (!d->textures.isEmpty() && attributes.contains(QGL::TextureCoord0)) { + // TODO: different combine modes for each layer. + QGLMaterial::TextureCombineMode mode = + d->textureModes.value(0, Modulate); + if (mode == Replace) + effect = QGL::FlatReplaceTexture2D; + else if (mode == Decal) + effect = QGL::LitDecalTexture2D; + else + effect = QGL::LitModulateTexture2D; + } + painter->setStandardEffect(effect); +} + +/*! + \fn void QGLMaterial::ambientColorChanged() + + This signal is emitted when ambientColor() changes. + + \sa ambientColor(), setAmbientColor(), materialChanged() +*/ + +/*! + \fn void QGLMaterial::diffuseColorChanged() + + This signal is emitted when diffuseColor() changes. + + \sa diffuseColor(), setDiffuseColor(), materialChanged() +*/ + +/*! + \fn void QGLMaterial::specularColorChanged() + + This signal is emitted when specularColor() changes. + + \sa specularColor(), setSpecularColor(), materialChanged() +*/ + +/*! + \fn void QGLMaterial::emittedLightChanged() + + This signal is emitted when emittedLight() changes. + + \sa emittedLight(), setEmittedLight(), materialChanged() +*/ + +/*! + \fn void QGLMaterial::shininessChanged() + + This signal is emitted when shininess() changes. + + \sa shininess(), setShininess(), materialChanged() +*/ + +/*! + \fn void QGLMaterial::texturesChanged() + + This signal is emitted when the texture layers associated with + this material change. + + \sa texture(), setTexture(), materialChanged() +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QGLMaterial &material) +{ + dbg << &material << + "-- Amb:" << material.ambientColor() << + "-- Diff:" << material.diffuseColor() << + "-- Spec:" << material.specularColor() << + "-- Shin:" << material.shininess(); + for (int i = 0; i < material.textureLayerCount(); ++i) + if (material.texture(i) != 0) + dbg << "\n -- Tex" << i << ":" << material.texture(i) + << material.texture(i)->objectName(); + dbg << "\n"; + return dbg; +} +#endif + +QT_END_NAMESPACE diff --git a/src/threed/materials/qglmaterial.h b/src/threed/materials/qglmaterial.h new file mode 100644 index 000000000..74feb9e90 --- /dev/null +++ b/src/threed/materials/qglmaterial.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLMATERIAL_H +#define QGLMATERIAL_H + +#include "qglabstractmaterial.h" +#include <QtCore/qobject.h> +#include <QtCore/qscopedpointer.h> +#include <QtGui/qcolor.h> +#include <QtCore/qurl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLMaterialPrivate; +class QGLTexture2D; +class QGLTextureCube; +class QGLPainter; +class QGLMaterialCollection; +class QGLTwoSidedMaterial; + +class Q_QT3D_EXPORT QGLMaterial : public QGLAbstractMaterial +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGLMaterial) + Q_DISABLE_COPY(QGLMaterial) + Q_ENUMS(TextureCombineMode) + Q_PROPERTY(QColor ambientColor READ ambientColor WRITE setAmbientColor NOTIFY ambientColorChanged) + Q_PROPERTY(QColor diffuseColor READ diffuseColor WRITE setDiffuseColor NOTIFY diffuseColorChanged) + Q_PROPERTY(QColor specularColor READ specularColor WRITE setSpecularColor NOTIFY specularColorChanged) + Q_PROPERTY(QColor emittedLight READ emittedLight WRITE setEmittedLight NOTIFY emittedLightChanged) + Q_PROPERTY(qreal shininess READ shininess WRITE setShininess NOTIFY shininessChanged) + Q_PROPERTY(QGLTexture2D *texture READ texture WRITE setTexture NOTIFY texturesChanged) + Q_PROPERTY(QGLMaterial::TextureCombineMode textureCombineMode READ textureCombineMode WRITE setTextureCombineMode NOTIFY texturesChanged) + Q_PROPERTY(QUrl textureUrl READ textureUrl WRITE setTextureUrl NOTIFY texturesChanged) +public: + explicit QGLMaterial(QObject *parent = 0); + ~QGLMaterial(); + + QColor ambientColor() const; + void setAmbientColor(const QColor& value); + + QColor diffuseColor() const; + void setDiffuseColor(const QColor& value); + + QColor specularColor() const; + void setSpecularColor(const QColor& value); + + QColor emittedLight() const; + void setEmittedLight(const QColor& value); + + void setColor(const QColor& value); + + qreal shininess() const; + void setShininess(qreal value); + + QGLTexture2D *texture(int layer = 0) const; + void setTexture(QGLTexture2D *value, int layer = 0); + + QUrl textureUrl(int layer = 0) const; + void setTextureUrl(const QUrl &url, int layer = 0); + + enum TextureCombineMode + { + Modulate, + Decal, + Replace + }; + + QGLMaterial::TextureCombineMode textureCombineMode(int layer = 0) const; + void setTextureCombineMode(QGLMaterial::TextureCombineMode mode, int layer = 0); + + int textureLayerCount() const; + + QGLMaterial *front() const; + bool isTransparent() const; + void bind(QGLPainter *painter); + void release(QGLPainter *painter, QGLAbstractMaterial *next); + void prepareToDraw(QGLPainter *painter, const QGLAttributeSet &attributes); + +Q_SIGNALS: + void ambientColorChanged(); + void diffuseColorChanged(); + void specularColorChanged(); + void emittedLightChanged(); + void shininessChanged(); + void texturesChanged(); + +private: + friend class QGLMaterialCollection; + friend class QGLTwoSidedMaterial; + + void bindTextures(QGLPainter *painter); + void bindEffect(QGLPainter *painter, const QGLAttributeSet &attributes, bool twoSided); + + QScopedPointer<QGLMaterialPrivate> d_ptr; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QGLMaterial &material); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/materials/qglmaterial_p.h b/src/threed/materials/qglmaterial_p.h new file mode 100644 index 000000000..2084f99f0 --- /dev/null +++ b/src/threed/materials/qglmaterial_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLMATERIAL_P_H +#define QGLMATERIAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglmaterial.h" +#include <QtCore/qmap.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGLMaterialPrivate +{ +public: + explicit QGLMaterialPrivate(); + + QColor ambientColor; + QColor diffuseColor; + QColor specularColor; + QColor emittedLight; + qreal shininess; + QMap<int, QGLTexture2D *> textures; + QMap<int, QGLMaterial::TextureCombineMode> textureModes; + QGLMaterialCollection *collection; + int index; + QString name; + bool used; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/materials/qgltwosidedmaterial.cpp b/src/threed/materials/qgltwosidedmaterial.cpp new file mode 100644 index 000000000..d8d69b227 --- /dev/null +++ b/src/threed/materials/qgltwosidedmaterial.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgltwosidedmaterial.h" +#include "qglpainter.h" +#include "qgllightmodel.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLTwoSidedMaterial + \since 4.8 + \brief The QGLTwoSidedMaterial class implemented two-sided materials for 3D rendering. + \ingroup qt3d + \ingroup qt3d::materials + + Two-sided materials consist of a front() material and a back() + material. The specific material rendered is determined by the + direction faced by a fragment when it is rendered. Fragments + that point towards the viewer are rendered with front(), + and fragments that point away from the viewer are rendered + with back(). In both cases, any textures that are used to + render the material are taken from front(). + + If front() and back() are the same, then the same material + will be used on both sides of the fragment. However, this + is not exactly the same as using a one-sided QGLMaterial in + place of the two-sided material. One-sided materials will + render the back of the fragment as black because the normal + is always pointing away from the viewer. Two-sided materials + reverse the back-facing normal so that back() is lit as + though it was on a front-facing face. + + \sa QGLMaterial +*/ + +class QGLTwoSidedMaterialPrivate +{ +public: + QGLTwoSidedMaterialPrivate() : front(0), back(0), defaultMaterial(0) {} + + QGLMaterial *front; + QGLMaterial *back; + QGLMaterial *defaultMaterial; +}; + +/*! + Constructs a two-sided material object and attaches it to \a parent. + + \sa setFront(), setBack() +*/ +QGLTwoSidedMaterial::QGLTwoSidedMaterial(QObject *parent) + : QGLAbstractMaterial(parent) + , d_ptr(new QGLTwoSidedMaterialPrivate) +{ +} + +/*! + Destroys this two-sided material object. +*/ +QGLTwoSidedMaterial::~QGLTwoSidedMaterial() +{ +} + +/*! + \property QGLTwoSidedMaterial::front + \brief the material for the front side of the object's fragments. + + The default value is null, which will result in a default QGLMaterial + object being used when bind() is called. + + \sa back(), frontChanged(), materialChanged() +*/ + +QGLMaterial *QGLTwoSidedMaterial::front() const +{ + Q_D(const QGLTwoSidedMaterial); + return d->front; +} + +void QGLTwoSidedMaterial::setFront(QGLMaterial *material) +{ + Q_D(QGLTwoSidedMaterial); + if (d->front != material) { + if (d->front && d->front != d->back) { + disconnect(d->front, SIGNAL(materialChanged()), + this, SIGNAL(materialChanged())); + } + d->front = material; + if (d->front && d->front != d->back) { + connect(d->front, SIGNAL(materialChanged()), + this, SIGNAL(materialChanged())); + } + emit frontChanged(); + emit materialChanged(); + } +} + +/*! + \property QGLTwoSidedMaterial::back + \brief the material for the back side of the object's fragments. + + The default value is null, which indicates that front() should + be used on both the front and back sides of the object's fragments. + + Textures are taken from the front() material. Any textures that + appear in the back() material are ignored. Only the material + lighting parameters from back() will be used. + + \sa front(), backChanged(), materialChanged() +*/ + +QGLMaterial *QGLTwoSidedMaterial::back() const +{ + Q_D(const QGLTwoSidedMaterial); + return d->back; +} + +void QGLTwoSidedMaterial::setBack(QGLMaterial *material) +{ + Q_D(QGLTwoSidedMaterial); + if (d->back != material) { + if (d->back && d->back != d->front) { + disconnect(d->back, SIGNAL(materialChanged()), + this, SIGNAL(materialChanged())); + } + d->back = material; + if (d->back && d->back != d->front) { + connect(d->back, SIGNAL(materialChanged()), + this, SIGNAL(materialChanged())); + } + emit backChanged(); + emit materialChanged(); + } +} + +/*! + \reimp +*/ +bool QGLTwoSidedMaterial::isTransparent() const +{ + Q_D(const QGLTwoSidedMaterial); + if (d->front && d->front->isTransparent()) + return true; + return d->back && d->back->isTransparent(); +} + +/*! + \reimp +*/ +void QGLTwoSidedMaterial::bind(QGLPainter *painter) +{ + Q_D(QGLTwoSidedMaterial); + QGLMaterial *front = d->front; + if (!front) { + // We need to have something for the front material. + front = d->defaultMaterial; + if (!front) { + d->defaultMaterial = new QGLMaterial(this); + front = d->defaultMaterial; + } + } + const_cast<QGLLightModel *>(painter->lightModel()) + ->setModel(QGLLightModel::TwoSided); // FIXME + if (d->back && d->back != front) { + painter->setFaceMaterial(QGL::FrontFaces, front); + painter->setFaceMaterial(QGL::BackFaces, d->back); + } else { + painter->setFaceMaterial(QGL::AllFaces, front); + } + front->bindTextures(painter); +} + +/*! + \reimp +*/ +void QGLTwoSidedMaterial::release(QGLPainter *painter, QGLAbstractMaterial *next) +{ + Q_D(const QGLTwoSidedMaterial); + if (d->front) + d->front->release(painter, next); + else if (d->defaultMaterial) + d->defaultMaterial->release(painter, next); +} + +/*! + \reimp +*/ +void QGLTwoSidedMaterial::prepareToDraw + (QGLPainter *painter, const QGLAttributeSet &attributes) +{ + Q_D(QGLTwoSidedMaterial); + QGLMaterial *front = d->front; + if (!front) + front = d->defaultMaterial; + front->bindEffect(painter, attributes, true); +} + +/*! + \fn void QGLTwoSidedMaterial::frontChanged() + + Signal that is emitted when the front() material pointer changes. + + This signal is not emitted when a property on front() changes. + Use materialChanged() for that purpose instead. + + \sa front(), backChanged() +*/ + +/*! + \fn void QGLTwoSidedMaterial::backChanged() + + Signal that is emitted when the back() material pointer changes. + + This signal is not emitted when a property on back() changes. + Use materialChanged() for that purpose instead. + + \sa back(), frontChanged() +*/ + +QT_END_NAMESPACE diff --git a/src/threed/materials/qgltwosidedmaterial.h b/src/threed/materials/qgltwosidedmaterial.h new file mode 100644 index 000000000..65f544e03 --- /dev/null +++ b/src/threed/materials/qgltwosidedmaterial.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLTWOSIDEDMATERIAL_H +#define QGLTWOSIDEDMATERIAL_H + +#include "qglmaterial.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLTwoSidedMaterialPrivate; + +class Q_QT3D_EXPORT QGLTwoSidedMaterial : public QGLAbstractMaterial +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGLTwoSidedMaterial) + Q_DISABLE_COPY(QGLTwoSidedMaterial) + Q_PROPERTY(QGLMaterial *front READ front WRITE setFront NOTIFY frontChanged) + Q_PROPERTY(QGLMaterial *back READ back WRITE setBack NOTIFY backChanged) +public: + explicit QGLTwoSidedMaterial(QObject *parent = 0); + ~QGLTwoSidedMaterial(); + + QGLMaterial *front() const; + void setFront(QGLMaterial *material); + + QGLMaterial *back() const; + void setBack(QGLMaterial *material); + + bool isTransparent() const; + void bind(QGLPainter *painter); + void release(QGLPainter *painter, QGLAbstractMaterial *next); + void prepareToDraw(QGLPainter *painter, const QGLAttributeSet &attributes); + +Q_SIGNALS: + void frontChanged(); + void backChanged(); + +private: + QScopedPointer<QGLTwoSidedMaterialPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/math3d/math3d.pri b/src/threed/math3d/math3d.pri new file mode 100644 index 000000000..25cd3e74f --- /dev/null +++ b/src/threed/math3d/math3d.pri @@ -0,0 +1,12 @@ +INCLUDEPATH += $$PWD +VPATH += $$PWD +HEADERS += qbox3d.h \ + qplane3d.h \ + qray3d.h \ + qsphere3d.h \ + qtriangle3d.h +SOURCES += qbox3d.cpp \ + qplane3d.cpp \ + qray3d.cpp \ + qsphere3d.cpp \ + qtriangle3d.cpp diff --git a/src/threed/math3d/qbox3d.cpp b/src/threed/math3d/qbox3d.cpp new file mode 100644 index 000000000..043f67d8b --- /dev/null +++ b/src/threed/math3d/qbox3d.cpp @@ -0,0 +1,690 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbox3d.h" +#include "qplane3d.h" +#include <QtGui/qmatrix4x4.h> +#include <QtCore/qlist.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QBox3D + \brief The QBox3D class represents an axis-aligned box in 3D space. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::math + + QBox3D can be used to represent the bounding box of objects in a 3D + scene so that they can be easily culled if they are out of view. + + The sides of the box are always aligned with the x, y, and z axes of + the world co-ordinate system. Transforming a box with transformed() + will result in the smallest axis-aligned bounding box that contains + the transformed box. + + Boxes may be null, finite, or infinite. A null box does not occupy + any space and does not intersect with any other box. A finite + box consists of a minimum() and maximum() extent in 3D space. + An infinite box encompasses all points in 3D space. + + The extents of a finite box are also included within the box. + A box with minimum() and maximum() set to the same value + contains a single point. +*/ + +/*! + \fn QBox3D::QBox3D() + + Constructs a null box in 3D space. + + \sa isNull() +*/ + +/*! + \fn QBox3D::QBox3D(const QVector3D& corner1, const QVector3D& corner2) + + Constructs a finite box in 3D space from \a corner1 to \a corner2. + The minimum() and maximum() co-ordinates of the new box are set + to the minimum and maximum x, y, and z values from \a corner1 and + \a corner2. The \a corner1 and \a corner2 values can be any two + opposite corners that define the box. + + \sa isFinite(), minimum(), maximum() +*/ + +/*! + \fn bool QBox3D::isNull() const + + Returns true if this box is null; false otherwise. + + \sa isFinite(), isInfinite(), setToNull() +*/ + +/*! + \fn bool QBox3D::isFinite() const + + Returns true if this box is finite in size; false otherwise. + + \sa isNull(), isInfinite(), setExtents() +*/ + +/*! + \fn bool QBox3D::isInfinite() const + + Returns true if this box is infinite in size; false otherwise. + + \sa isNull(), isFinite(), setToInfinite() +*/ + +/*! + \fn QVector3D QBox3D::minimum() const + + Returns the minimum corner of this box. + + \sa maximum(), setExtents() +*/ + +/*! + \fn QVector3D QBox3D::maximum() const + + Returns the maximum corner of this box. + + \sa minimum(), setExtents() +*/ + +/*! + \fn void QBox3D::setExtents(const QVector3D& corner1, const QVector3D& corner2) + + Sets the extents of this box to a finite region from \a corner1 to + \a corner2. The minimum() and maximum() co-ordinates of the box are + set to the minimum and maximum x, y, and z values from \a corner1 and + \a corner2. The \a corner1 and \a corner2 values can be any two + opposite corners that define the box. + + \sa minimum(), maximum() +*/ + +/*! + \fn void QBox3D::setToNull() + + Sets this box to null. + + \sa isNull() +*/ + +/*! + \fn void QBox3D::setToInfinite() + + Sets this box to be infinite in size. + + \sa isInfinite() +*/ + +/*! + \fn QVector3D QBox3D::size() const + + Returns the finite size of this box. If this box is null or + infinite, the returned value will be zero. + + \sa center(), isNull(), isInfinite() +*/ + +/*! + \fn QVector3D QBox3D::center() const + + Returns the finite center of this box. If this box is null + or infinite, the returned value will be zero. + + \sa size(), isNull(), isInfinite() +*/ + +/*! + \fn bool QBox3D::contains(const QVector3D& point) const + + Returns true if this box contains \a point; false otherwise. + Null boxes do not contain any points and infinite boxes contain + all points. + + Containment is not a strict test: the point is contained if it + lies on one of the faces of the box. + + \sa intersects() +*/ + +/*! + \fn bool QBox3D::contains(const QBox3D& box) const + + Returns true if this box completely contains \a box. If this box + is null, then it will not contain \a box. If this box is infinite, + and \a box is not null, then \a box will be contained within this box. + If \a box is infinite, then this box must also be infinite to contain it. + + \sa intersects() +*/ + +/*! + Returns true if \a box intersects this box; false otherwise. + + \sa intersect(), intersected(), contains() +*/ +bool QBox3D::intersects(const QBox3D& box) const +{ + if (boxtype == Null) + return false; + else if (boxtype == Infinite) + return box.boxtype != Null; + else if (box.boxtype == Null) + return false; + else if (box.boxtype == Infinite) + return true; + + if (maxcorner.x() < box.mincorner.x()) + return false; + if (mincorner.x() > box.maxcorner.x()) + return false; + + if (maxcorner.y() < box.mincorner.y()) + return false; + if (mincorner.y() > box.maxcorner.y()) + return false; + + if (maxcorner.z() < box.mincorner.z()) + return false; + if (mincorner.z() > box.maxcorner.z()) + return false; + + return true; +} + +/*! + Returns true if \a ray intersects this box; false otherwise. + + \sa intersection() +*/ +bool QBox3D::intersects(const QRay3D &ray) const +{ + qreal minimum_t, maximum_t; + return intersection(ray, &minimum_t, &maximum_t); +} + +static inline void trackIntersectionX + (const QBox3D &box, const QRay3D &ray, qreal t, + qreal *minimum_t, qreal *maximum_t, bool *found) +{ + QVector3D point = ray.point(t); + if (point.y() < box.minimum().y() || point.y() > box.maximum().y()) + return; + if (point.z() < box.minimum().z() || point.z() > box.maximum().z()) + return; + if (!(*found)) { + *minimum_t = *maximum_t = t; + *found = true; + } else { + if (t < *minimum_t) + *minimum_t = t; + if (t > *maximum_t) + *maximum_t = t; + } +} + +static inline void trackIntersectionY + (const QBox3D &box, const QRay3D &ray, qreal t, + qreal *minimum_t, qreal *maximum_t, bool *found) +{ + QVector3D point = ray.point(t); + if (point.x() < box.minimum().x() || point.x() > box.maximum().x()) + return; + if (point.z() < box.minimum().z() || point.z() > box.maximum().z()) + return; + if (!(*found)) { + *minimum_t = *maximum_t = t; + *found = true; + } else { + if (t < *minimum_t) + *minimum_t = t; + if (t > *maximum_t) + *maximum_t = t; + } +} + +static inline void trackIntersectionZ + (const QBox3D &box, const QRay3D &ray, qreal t, + qreal *minimum_t, qreal *maximum_t, bool *found) +{ + QVector3D point = ray.point(t); + if (point.x() < box.minimum().x() || point.x() > box.maximum().x()) + return; + if (point.y() < box.minimum().y() || point.y() > box.maximum().y()) + return; + if (!(*found)) { + *minimum_t = *maximum_t = t; + *found = true; + } else { + if (t < *minimum_t) + *minimum_t = t; + if (t > *maximum_t) + *maximum_t = t; + } +} + +/*! + Finds the \a minimum_t and \a maximum_t values where \a ray intersects + this box. Returns true if intersections were found; or false if there + is no intersection. + + If \a minimum_t and \a maximum_t are set to the same value, then the + intersection is at a corner or the volume of the box is zero. + If the t values are negative, then the intersection occurs before the + ray's origin point in the reverse direction of the ray. + + The \a minimum_t and \a maximum_t values can be passed to QRay3D::point() + to determine the actual intersection points, as shown in the following + example: + + \code + qreal minimum_t, maximum_t; + if (box.intersection(ray, &minimum_t, &maximum_t)) { + qDebug() << "intersections at" + << ray.point(minimum_t) << "and" + << ray.point(maximum_t); + } + \endcode + + \sa intersects(), QRay3D::point() +*/ +bool QBox3D::intersection(const QRay3D &ray, qreal *minimum_t, qreal *maximum_t) const +{ + bool found = false; + QVector3D origin = ray.origin(); + QVector3D direction = ray.direction(); + *minimum_t = *maximum_t = qSNaN(); + if (boxtype == Finite) { + if (direction.x() != 0.0f) { + trackIntersectionX + (*this, ray, (mincorner.x() - origin.x()) / direction.x(), + minimum_t, maximum_t, &found); + trackIntersectionX + (*this, ray, (maxcorner.x() - origin.x()) / direction.x(), + minimum_t, maximum_t, &found); + } + if (direction.y() != 0.0f) { + trackIntersectionY + (*this, ray, (mincorner.y() - origin.y()) / direction.y(), + minimum_t, maximum_t, &found); + trackIntersectionY + (*this, ray, (maxcorner.y() - origin.y()) / direction.y(), + minimum_t, maximum_t, &found); + } + if (direction.z() != 0.0f) { + trackIntersectionZ + (*this, ray, (mincorner.z() - origin.z()) / direction.z(), + minimum_t, maximum_t, &found); + trackIntersectionZ + (*this, ray, (maxcorner.z() - origin.z()) / direction.z(), + minimum_t, maximum_t, &found); + } + } + return found; +} + +/*! + Returns the t value at which \a ray first intersects the sides of + this box, or not-a-number if there is no intersection. + + When the \a ray intersects this box, the return value is a + parametric value that can be passed to QRay3D::point() to determine + the actual intersection point, as shown in the following example: + + \code + qreal t = box.intersection(ray); + QVector3D pt; + if (qIsNaN(t)) { + qWarning("no intersection occurred"); + else + pt = ray.point(t); + \endcode + + The \a ray might intersect at two points - as the ray passes through + the box - one on the near side, one on the far side; where near and far + are relative to the origin point of the ray. This function only + returns the near intersection point. + + Only positive values on the ray are considered. This means that if + the origin point of the ray is inside the box, there is only one + solution, not two. To get the other solution, simply change + the sign of the ray's direction vector. If the origin point of + the ray is outside the box, and the direction points away from + the box, then there will be no intersection. + + When the ray does not intersect the box in the positive direction, + or the box is not finite, then the return value is not-a-number. + + \sa intersects(), QRay3D::point() +*/ +qreal QBox3D::intersection(const QRay3D &ray) const +{ + qreal minimum_t, maximum_t; + if (intersection(ray, &minimum_t, &maximum_t)) { + if (minimum_t >= 0.0f) + return minimum_t; + else if (maximum_t >= 0.0f) + return maximum_t; + else + return qSNaN(); + } else { + return qSNaN(); + } +} + +/*! + Intersects this box with \a box. + + \sa intersected(), intersects(), unite() +*/ +void QBox3D::intersect(const QBox3D& box) +{ + // Handle the simple cases first. + if (boxtype == Null) { + // Null intersected with anything is null. + return; + } else if (boxtype == Infinite) { + // Infinity intersected with a box is that box. + *this = box; + return; + } else if (box.boxtype == Null) { + // Anything intersected with null is null. + setToNull(); + return; + } else if (box.boxtype == Infinite) { + // Box intersected with infinity is the box. + return; + } + + // Intersect two finite boxes. + QVector3D min1 = mincorner; + QVector3D max1 = maxcorner; + QVector3D min2 = box.mincorner; + QVector3D max2 = box.maxcorner; + if (min2.x() > min1.x()) + min1.setX(min2.x()); + if (min2.y() > min1.y()) + min1.setY(min2.y()); + if (min2.z() > min1.z()) + min1.setZ(min2.z()); + if (max2.x() < max1.x()) + max1.setX(max2.x()); + if (max2.y() < max1.y()) + max1.setY(max2.y()); + if (max2.z() < max1.z()) + max1.setZ(max2.z()); + if (min1.x() > max1.x() || min1.y() > max1.y() || min1.z() > max1.z()) { + setToNull(); + } else { + mincorner = min1; + maxcorner = max1; + } +} + +/*! + Returns a new box which is the intersection of this box with \a box. + + \sa intersect(), intersects(), united() +*/ +QBox3D QBox3D::intersected(const QBox3D& box) const +{ + QBox3D result(*this); + result.intersect(box); + return result; +} + +/*! + Unites this box with \a point by expanding it to encompass \a point. + If \a point is already contained within this box, then this box + will be unchanged. + + \sa united(), intersect() +*/ +void QBox3D::unite(const QVector3D& point) +{ + if (boxtype == Finite) { + if (point.x() < mincorner.x()) + mincorner.setX(point.x()); + else if (point.x() > maxcorner.x()) + maxcorner.setX(point.x()); + if (point.y() < mincorner.y()) + mincorner.setY(point.y()); + else if (point.y() > maxcorner.y()) + maxcorner.setY(point.y()); + if (point.z() < mincorner.z()) + mincorner.setZ(point.z()); + else if (point.z() > maxcorner.z()) + maxcorner.setZ(point.z()); + } else if (boxtype == Null) { + boxtype = Finite; + mincorner = point; + maxcorner = point; + } +} + +/*! + Unites this box with \a box by expanding this box to encompass the + region defined by \a box. If \a box is already contained within + this box, then this box will be unchanged. + + \sa united(), intersect() +*/ +void QBox3D::unite(const QBox3D& box) +{ + if (box.boxtype == Finite) { + unite(box.minimum()); + unite(box.maximum()); + } else if (box.boxtype == Infinite) { + setToInfinite(); + } +} + +/*! + Returns a new box which unites this box with \a point. The returned + value will be the smallest box that contains both this box and \a point. + + \sa unite(), intersected() +*/ +QBox3D QBox3D::united(const QVector3D& point) const +{ + if (boxtype == Finite) { + QBox3D result(*this); + result.unite(point); + return result; + } else if (boxtype == Null) { + return QBox3D(point, point); + } else { + return *this; + } +} + +/*! + Returns a new box which unites this box with \a box. The returned value + will be the smallest box that contains both this box and \a box. + + \sa unite(), intersected() +*/ +QBox3D QBox3D::united(const QBox3D& box) const +{ + if (boxtype == Finite) { + QBox3D result(*this); + result.unite(box); + return result; + } else if (boxtype == Null) { + return box; + } else { + return *this; + } +} + +/*! + Transforms this box according to \a matrix. Each of the 8 box + corners are transformed and then a new box that encompasses all + of the transformed corner values is created. + + \sa transformed() +*/ +void QBox3D::transform(const QMatrix4x4& matrix) +{ + *this = transformed(matrix); +} + +/*! + Returns this box transformed by \a matrix. Each of the 8 box + corners are transformed and then a new box that encompasses all + of the transformed corner values is returned. + + \sa transform() +*/ +QBox3D QBox3D::transformed(const QMatrix4x4& matrix) const +{ + if (boxtype != Finite) + return *this; + QBox3D result; + result.unite(matrix * mincorner); + result.unite(matrix * QVector3D(mincorner.x(), mincorner.y(), maxcorner.z())); + result.unite(matrix * QVector3D(mincorner.x(), maxcorner.y(), maxcorner.z())); + result.unite(matrix * QVector3D(mincorner.x(), maxcorner.y(), mincorner.z())); + result.unite(matrix * QVector3D(maxcorner.x(), mincorner.y(), mincorner.z())); + result.unite(matrix * QVector3D(maxcorner.x(), maxcorner.y(), mincorner.z())); + result.unite(matrix * QVector3D(maxcorner.x(), mincorner.y(), maxcorner.z())); + result.unite(matrix * maxcorner); + return result; +} + +/*! + \fn bool QBox3D::operator==(const QBox3D& box) const + + Returns true if this box is identical to \a box. +*/ + +/*! + \fn bool QBox3D::operator!=(const QBox3D& box) const + + Returns true if this box is not identical to \a box. +*/ + +/*! + \fn bool qFuzzyCompare(const QBox3D& box1, const QBox3D& box2) + \relates QBox3D + + Returns true if \a box1 and \a box2 are almost equal; false otherwise. +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QBox3D &box) +{ + if (box.isFinite()) { + dbg.nospace() << "QBox3D((" + << box.minimum().x() << ", " << box.minimum().y() << ", " + << box.minimum().z() << ") - (" + << box.maximum().x() << ", " << box.maximum().y() << ", " + << box.maximum().z() << "))"; + return dbg.space(); + } else if (box.isNull()) { + dbg << "QBox3D(null)"; + return dbg; + } else { + dbg << "QBox3D(infinite)"; + return dbg; + } +} + +#endif + +#ifndef QT_NO_DATASTREAM + +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QBox3D &box) + \relates QBox3D + + Writes the given \a box to the given \a stream and returns a + reference to the stream. +*/ + +QDataStream &operator<<(QDataStream &stream, const QBox3D &box) +{ + if (box.isNull()) { + stream << int(0); + } else if (box.isInfinite()) { + stream << int(2); + } else { + stream << int(1); + stream << box.minimum(); + stream << box.maximum(); + } + return stream; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QBox3D &box) + \relates QBox3D + + Reads a 3D box from the given \a stream into the given \a box + and returns a reference to the stream. +*/ + +QDataStream &operator>>(QDataStream &stream, QBox3D &box) +{ + int type; + stream >> type; + if (type == 1) { + QVector3D minimum, maximum; + stream >> minimum; + stream >> maximum; + box = QBox3D(minimum, maximum); + } else if (type == 2) { + box.setToInfinite(); + } else { + box.setToNull(); + } + return stream; +} + +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE diff --git a/src/threed/math3d/qbox3d.h b/src/threed/math3d/qbox3d.h new file mode 100644 index 000000000..a31701ba9 --- /dev/null +++ b/src/threed/math3d/qbox3d.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBOX3D_H +#define QBOX3D_H + +#include "qt3dglobal.h" +#include "qray3d.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QMatrix4x4; + +class Q_QT3D_EXPORT QBox3D +{ +public: + QBox3D(); + QBox3D(const QVector3D& corner1, const QVector3D& corner2); + + bool isNull() const; + bool isFinite() const; + bool isInfinite() const; + + QVector3D minimum() const; + QVector3D maximum() const; + void setExtents(const QVector3D& corner1, const QVector3D& corner2); + + void setToNull(); + void setToInfinite(); + + QVector3D size() const; + QVector3D center() const; + + bool contains(const QVector3D& point) const; + bool contains(const QBox3D& box) const; + + bool intersects(const QRay3D& ray) const; + bool intersection(const QRay3D &ray, qreal *minimum_t, qreal *maximum_t) const; + qreal intersection(const QRay3D& ray) const; + + bool intersects(const QBox3D& box) const; + void intersect(const QBox3D& box); + QBox3D intersected(const QBox3D& box) const; + + void unite(const QVector3D& point); + void unite(const QBox3D& box); + + QBox3D united(const QVector3D& point) const; + QBox3D united(const QBox3D& box) const; + + void transform(const QMatrix4x4& matrix); + QBox3D transformed(const QMatrix4x4& matrix) const; + + bool operator==(const QBox3D& box) const; + bool operator!=(const QBox3D& box) const; + + friend bool qFuzzyCompare(const QBox3D& box1, const QBox3D& box2); + +private: + enum Type + { + Null, + Finite, + Infinite + }; + + QBox3D::Type boxtype; + QVector3D mincorner, maxcorner; +}; + +inline QBox3D::QBox3D() : boxtype(Null), mincorner(0, 0, 0), maxcorner(0, 0, 0) {} + +inline QBox3D::QBox3D(const QVector3D& corner1, const QVector3D& corner2) + : boxtype(Finite), + mincorner(qMin(corner1.x(), corner2.x()), + qMin(corner1.y(), corner2.y()), + qMin(corner1.z(), corner2.z())), + maxcorner(qMax(corner1.x(), corner2.x()), + qMax(corner1.y(), corner2.y()), + qMax(corner1.z(), corner2.z())) {} + +inline bool QBox3D::isNull() const { return (boxtype == Null); } +inline bool QBox3D::isFinite() const { return (boxtype == Finite); } +inline bool QBox3D::isInfinite() const { return (boxtype == Infinite); } + +inline QVector3D QBox3D::minimum() const { return mincorner; } +inline QVector3D QBox3D::maximum() const { return maxcorner; } + +inline void QBox3D::setExtents(const QVector3D& corner1, const QVector3D& corner2) +{ + boxtype = Finite; + mincorner = QVector3D(qMin(corner1.x(), corner2.x()), + qMin(corner1.y(), corner2.y()), + qMin(corner1.z(), corner2.z())); + maxcorner = QVector3D(qMax(corner1.x(), corner2.x()), + qMax(corner1.y(), corner2.y()), + qMax(corner1.z(), corner2.z())); +} + +inline void QBox3D::setToNull() +{ + boxtype = Null; + mincorner = QVector3D(0, 0, 0); + maxcorner = QVector3D(0, 0, 0); +} + +inline void QBox3D::setToInfinite() +{ + boxtype = Infinite; + mincorner = QVector3D(0, 0, 0); + maxcorner = QVector3D(0, 0, 0); +} + +inline QVector3D QBox3D::size() const { return maxcorner - mincorner; } +inline QVector3D QBox3D::center() const { return (mincorner + maxcorner) * 0.5f; } + +inline bool QBox3D::contains(const QVector3D& point) const +{ + if (boxtype == Finite) { + return (point.x() >= mincorner.x() && point.x() <= maxcorner.x() && + point.y() >= mincorner.y() && point.y() <= maxcorner.y() && + point.z() >= mincorner.z() && point.z() <= maxcorner.z()); + } else if (boxtype == Infinite) { + return true; + } else { + return false; + } +} + +inline bool QBox3D::contains(const QBox3D& box) const +{ + if (box.boxtype == Finite) + return contains(box.mincorner) && contains(box.maxcorner); + else if (box.boxtype == Infinite) + return (boxtype == Infinite); + else + return false; +} + +inline bool QBox3D::operator==(const QBox3D& box) const +{ + return (boxtype == box.boxtype && + mincorner == box.mincorner && + maxcorner == box.maxcorner); +} + +inline bool QBox3D::operator!=(const QBox3D& box) const +{ + return (boxtype != box.boxtype || + mincorner != box.mincorner || + maxcorner != box.maxcorner); +} + +inline bool qFuzzyCompare(const QBox3D& box1, const QBox3D& box2) +{ + return box1.boxtype == box2.boxtype && + qFuzzyCompare(box1.mincorner, box2.mincorner) && + qFuzzyCompare(box1.maxcorner, box2.maxcorner); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QBox3D &box); +#endif + +#ifndef QT_NO_DATASTREAM +Q_QT3D_EXPORT QDataStream &operator<<(QDataStream &stream, const QBox3D &box); +Q_QT3D_EXPORT QDataStream &operator>>(QDataStream &stream, QBox3D &box); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QBox3D) + +QT_END_HEADER + +#endif diff --git a/src/threed/math3d/qplane3d.cpp b/src/threed/math3d/qplane3d.cpp new file mode 100644 index 000000000..9b328d28c --- /dev/null +++ b/src/threed/math3d/qplane3d.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplane3d.h" +#include <QtCore/qmath.h> +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QPlane3D + \brief The QPlane3D class models the mathematics of planes in 3D space. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::math + + A plane is defined by an origin() point lying on the plane, and a + normal() vector, which is perpendicular to the surface of the plane. + The normal() vector does not need to be normalized. QPlane3D is an + infinite surface, from which the normal() vector points out perpendicular + from the origin() point. + + \sa QRay3D +*/ + +/*! + \fn QPlane3D::QPlane3D() + + Constructs a default plane object. The defining origin() of the plane + is set to (0, 0, 0) and the normal() vector is to (1, 0, 0). +*/ + +/*! + \fn QPlane3D::QPlane3D(const QVector3D &point, const QVector3D &normal) + + Constructs a new plane, where \a point lies on the plane, and \a normal + is perpendicular to it. The \a normal does not have to be normalized. + If \a normal is zero, the behavior of the plane is undefined. +*/ + +/*! + \fn QPlane3D::QPlane3D(const QVector3D &p, const QVector3D &q, const QVector3D &r) + + Constructs a new plane defined by the three points \a p, \a q, and \a r. + The point \a p is used as the plane's origin() point, and a normal() + is constructed from the cross-product of \a q - \a p and \a r - \a q. +*/ + +/*! + \fn QVector3D QPlane3D::origin() const + + Returns this plane's defining origin point. The default value is (0, 0, 0). + + \sa setOrigin(), normal() +*/ + +/*! + \fn void QPlane3D::setOrigin(const QVector3D& value) + + Set this plane's defining origin point to \a value. + + \sa origin(), setNormal() +*/ + +/*! + \fn QVector3D QPlane3D::normal() const + + Returns this plane's normal vector. The default value is (1, 0, 0). + + \sa setNormal(), origin() +*/ + +/*! + \fn void QPlane3D::setNormal(const QVector3D& value) + + Set this plane's normal vector to \a value. The \a value does + not have to be normalized. If \a value is zero, the behavior + of the plane is undefined. + + \sa normal(), setOrigin() +*/ + +/*! + Returns true if \a point lies in this plane; false otherwise. +*/ +bool QPlane3D::contains(const QVector3D &point) const +{ + return qFuzzyIsNull + (float(QVector3D::dotProduct(m_normal, m_origin - point))); +} + +/*! + Returns true if all of the points on \a ray lie in this plane; + false otherwise. +*/ +bool QPlane3D::contains(const QRay3D &ray) const +{ + return qFuzzyIsNull + (float(QVector3D::dotProduct(m_normal, ray.direction()))) && + contains(ray.origin()); +} + +/*! + Returns true if an intersection of \a ray with this plane exists; + false otherwise. + + \sa intersection() +*/ +bool QPlane3D::intersects(const QRay3D &ray) const +{ + return !qFuzzyIsNull + (float(QVector3D::dotProduct(m_normal, ray.direction()))); +} + +/*! + Returns the t value at which \a ray intersects this plane, or + not-a-number if there is no intersection. + + When the \a ray intersects this plane, the return value is a + parametric value that can be passed to QRay3D::point() to determine + the actual intersection point, as shown in the following example: + + \code + qreal t = plane.intersection(ray); + QVector3D pt; + if (qIsNaN(t)) { + qWarning("no intersection occurred"); + else + pt = ray.point(t); + \endcode + + If the return value is positive, then the QRay3D::origin() of + \a ray begins below the plane and then extends through it. + If the return value is negative, then the origin begins + above the plane. + + There are two failure cases where no single intersection point exists: + + \list + \o when the ray is parallel to the plane (but does not lie on it) + \o the ray lies entirely in the plane (thus every point "intersects") + \endlist + + This method does not distinguish between these two failure cases and + simply returns not-a-number for both. + + \sa intersects() +*/ +qreal QPlane3D::intersection(const QRay3D& ray) const +{ + qreal dotLineAndPlane = QVector3D::dotProduct(m_normal, ray.direction()); + if (qFuzzyIsNull(float(dotLineAndPlane))) { + // degenerate case - ray & plane-normal vectors are at + // 90 degrees to each other, so either plane and ray never meet + // or the ray lies in the plane - return failure case. + return qSNaN(); + } + return QVector3D::dotProduct(m_origin - ray.origin(), m_normal) / + dotLineAndPlane; +} + +/*! + Returns the distance from this plane to \a point. The value will + be positive if \a point is above the plane in the direction + of normal(), and negative if \a point is below the plane. +*/ +qreal QPlane3D::distanceTo(const QVector3D &point) const +{ + return QVector3D::dotProduct(point - m_origin, m_normal) / + m_normal.length(); +} + +/*! + \fn void QPlane3D::transform(const QMatrix4x4 &matrix) + + Transforms this plane using \a matrix, replacing origin() and + normal() with the transformed versions. + + \sa transformed() +*/ + +/*! + \fn QPlane3D QPlane3D::transformed(const QMatrix4x4 &matrix) const + + Returns a new plane that is formed by transforming origin() + and normal() using \a matrix. + + \sa transform() +*/ + +/*! + \fn bool QPlane3D::operator==(const QPlane3D &other) + + Returns true if this plane is the same as \a other; false otherwise. + + \sa operator!=() +*/ + +/*! + \fn bool QPlane3D::operator!=(const QPlane3D &other) + + Returns true if this plane is not the same as \a other; false otherwise. + + \sa operator==() +*/ + +/*! + \fn bool qFuzzyCompare(const QPlane3D &plane1, const QPlane3D &plane2) + \relates QPlane3D + + Returns true if \a plane1 and \a plane2 are almost equal; false otherwise. +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QPlane3D &plane) +{ + dbg.nospace() << "QPlane3D(origin(" + << plane.origin().x() << ", " << plane.origin().y() << ", " + << plane.origin().z() << ") - normal(" + << plane.normal().x() << ", " << plane.normal().y() << ", " + << plane.normal().z() << "))"; + return dbg.space(); +} + +#endif + +#ifndef QT_NO_DATASTREAM + +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QPlane3D &plane) + \relates QPlane3D + + Writes the given \a plane to the given \a stream and returns a + reference to the stream. +*/ + +QDataStream &operator<<(QDataStream &stream, const QPlane3D &plane) +{ + stream << plane.origin(); + stream << plane.normal(); + return stream; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QPlane3D &plane) + \relates QPlane3D + + Reads a 3D plane from the given \a stream into the given \a plane + and returns a reference to the stream. +*/ + +QDataStream &operator>>(QDataStream &stream, QPlane3D &plane) +{ + QVector3D origin, normal; + stream >> origin; + stream >> normal; + plane = QPlane3D(origin, normal); + return stream; +} + +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE diff --git a/src/threed/math3d/qplane3d.h b/src/threed/math3d/qplane3d.h new file mode 100644 index 000000000..e2dcc15ac --- /dev/null +++ b/src/threed/math3d/qplane3d.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLANE3D_H +#define QPLANE3D_H + +#include <QtGui/qvector3d.h> +#include <QtCore/qnumeric.h> +#include "qray3d.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QPlane3D +{ +public: + QPlane3D(); + QPlane3D(const QVector3D &point, const QVector3D &normal); + QPlane3D(const QVector3D &p, const QVector3D &q, const QVector3D &r); + + QVector3D origin() const; + void setOrigin(const QVector3D& value); + + QVector3D normal() const; + void setNormal(const QVector3D& value); + + bool contains(const QVector3D &point) const; + bool contains(const QRay3D &ray) const; + + bool intersects(const QRay3D &ray) const; + qreal intersection(const QRay3D &ray) const; + + qreal distanceTo(const QVector3D &point) const; + + void transform(const QMatrix4x4 &matrix); + QPlane3D transformed(const QMatrix4x4 &matrix) const; + + bool operator==(const QPlane3D &other); + bool operator!=(const QPlane3D &other); + +private: + QVector3D m_origin; + QVector3D m_normal; +}; + +inline QPlane3D::QPlane3D() : m_normal(1.0f, 0.0f, 0.0f) {} + +inline QPlane3D::QPlane3D(const QVector3D &point, const QVector3D &normal) + : m_origin(point), m_normal(normal) +{ +} + +inline QPlane3D::QPlane3D(const QVector3D &p, const QVector3D &q, const QVector3D &r) + : m_origin(p), m_normal(QVector3D::crossProduct(q - p, r - q)) +{ +} + +inline QVector3D QPlane3D::origin() const +{ + return m_origin; +} + +inline void QPlane3D::setOrigin(const QVector3D &value) +{ + m_origin = value; +} + +inline QVector3D QPlane3D::normal() const +{ + return m_normal; +} + +inline void QPlane3D::setNormal(const QVector3D& value) +{ + m_normal = value; +} + +inline void QPlane3D::transform(const QMatrix4x4 &matrix) +{ + m_origin = matrix * m_origin; + m_normal = matrix.mapVector(m_normal); +} + +inline QPlane3D QPlane3D::transformed(const QMatrix4x4 &matrix) const +{ + return QPlane3D(matrix * m_origin, matrix.mapVector(m_normal)); +} + +inline bool QPlane3D::operator==(const QPlane3D &other) +{ + return m_origin == other.origin() && m_normal == other.normal(); +} + +inline bool QPlane3D::operator!=(const QPlane3D &other) +{ + return m_origin != other.origin() || m_normal != other.normal(); +} + +inline bool qFuzzyCompare(const QPlane3D &plane1, const QPlane3D &plane2) +{ + return qFuzzyCompare(plane1.origin(), plane2.origin()) && + qFuzzyCompare(plane1.normal(), plane2.normal()); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QPlane3D &plane); +#endif + +#ifndef QT_NO_DATASTREAM +Q_QT3D_EXPORT QDataStream &operator<<(QDataStream &stream, const QPlane3D &plane); +Q_QT3D_EXPORT QDataStream &operator>>(QDataStream &stream, QPlane3D &plane); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlane3D) + +QT_END_HEADER + +#endif // QPLANE3D_H diff --git a/src/threed/math3d/qray3d.cpp b/src/threed/math3d/qray3d.cpp new file mode 100644 index 000000000..38af18e7b --- /dev/null +++ b/src/threed/math3d/qray3d.cpp @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qray3d.h" +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QRay3D + \brief The QRay3D class defines a directional line in 3D space extending through an origin point. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::math + + A ray is defined by the origin() point and the direction() vector. + Rays are infinite in length, extending out from origin() in + both directions. If the direction() is zero length, then the + behavior of the class is undefined. + + A ray can be thought of as a one-dimensional co-ordinate system. + If the co-ordinate is \bold t then the origin() point is at + \bold t = 0, the point origin() + direction() is at \bold t = 1, + and the point origin() - direction() is at \bold t = -1. + The point() method can be used to obtain the position of a point + within this one-dimensional co-ordinate system. The fromPoint() + method can be used to convert a point into a value in this + one-dimensional co-ordinate system. +*/ + +/*! + \fn QRay3D::QRay3D() + + Construct a default ray with an origin() of (0, 0, 0) and a + direction() of (1, 0, 0). +*/ + +/*! + \fn QRay3D::QRay3D(const QVector3D &origin, const QVector3D &direction) + + Construct a ray given its defining \a origin and \a direction. The + \a direction does not need to be normalized. + + To construct a ray that passes through two points, use the following: + + \code + QRay3D thruAB(pointA, pointB - pointA); + \endcode +*/ + +/*! + \fn QVector3D QRay3D::origin() const + + Returns the origin of this ray. The default value is (0, 0, 0). + + \sa setOrigin(), direction() +*/ + +/*! + \fn void QRay3D::setOrigin(const QVector3D &value) + + Sets the origin point of this ray to \a value. + + \sa origin(), setDirection() + */ + +/*! + \fn QVector3D QRay3D::direction() const + + Returns the direction vector of this ray. The default value is (1, 0, 0). + + \sa setDirection(), origin() +*/ + +/*! + \fn void QRay3D::setDirection(const QVector3D &direction) + + Sets the direction vector of this ray to \a direction. + + \sa direction(), setOrigin() +*/ + +/*! + Returns true if \a point lies on this ray; false otherwise. +*/ +bool QRay3D::contains(const QVector3D &point) const +{ + QVector3D ppVec(point - m_origin); + if (ppVec.isNull()) // point coincides with origin + return true; + qreal dot = QVector3D::dotProduct(ppVec, m_direction); + if (qFuzzyIsNull(float(dot))) + return false; + return qFuzzyCompare(dot*dot, ppVec.lengthSquared() * m_direction.lengthSquared()); +} + +/*! + Returns true if \a ray lies on this ray; false otherwise. If true, + this implies that the two rays are the actually the same, but with + different origin() points or an inverted direction(). +*/ +bool QRay3D::contains(const QRay3D &ray) const +{ + qreal dot = QVector3D::dotProduct(m_direction, ray.direction()); + if (!qFuzzyCompare(dot*dot, m_direction.lengthSquared() * ray.direction().lengthSquared())) + return false; + return contains(ray.origin()); +} + +/*! + \fn QVector3D QRay3D::point(qreal t) const + + Returns the point on the ray defined by moving \a t units + along the ray in the direction of the direction() vector. + Note that \a t may be negative in which case the point returned + will lie behind the origin() point with respect to the + direction() vector. + + The units for \a t are defined by direction(). The return value + is precisely origin() + t * direction(). + + \sa fromPoint(), distanceTo() +*/ + +/*! + Returns the number of direction() units along the ray from origin() + to \a point. Essentially, this function computes the value t, where + \a point = origin() + t * direction(). If \a point is not on the ray, + then the closest point that is on the ray will be used instead. + + If the return value is positive, then \a point lies in front of + the origin() with respect to the direction() vector. If the return + value is negative, then \a point lies behind the origin() with + respect to the direction() vector. + + \sa point(), project() +*/ +qreal QRay3D::fromPoint(const QVector3D &point) const +{ + return QVector3D::dotProduct(point - m_origin, m_direction) / + m_direction.lengthSquared(); +} + +/*! + Returns the projection of \a vector onto this ray. In the + following diagram, the dotted line is the ray, and V is the + \a vector. The return value will be the vector V': + + \image qray3d-project.png + + \sa fromPoint() +*/ +QVector3D QRay3D::project(const QVector3D &vector) const +{ + QVector3D norm = m_direction.normalized(); + return QVector3D::dotProduct(vector, norm) * norm; +} + +/*! + Returns the minimum distance from this ray to \a point, or equivalently + the length of a line perpendicular to this ray which passes through + \a point. If \a point is on the ray, then this function will return zero. + + \sa point() +*/ +qreal QRay3D::distanceTo(const QVector3D &point) const +{ + qreal t = fromPoint(point); + return (point - (m_origin + t * m_direction)).length(); +} + +/*! + \fn void QRay3D::transform(const QMatrix4x4 &matrix) + + Transforms this ray using \a matrix, replacing origin() and + direction() with the transformed versions. + + \sa transformed() +*/ + +/*! + \fn QRay3D QRay3D::transformed(const QMatrix4x4 &matrix) const + + Returns a new ray that is formed by transforming origin() + and direction() using \a matrix. + + \sa transform() +*/ + +/*! + \fn bool QRay3D::operator==(const QRay3D &other) + + Returns true if this ray is the same as \a other; false otherwise. + + \sa operator!=() +*/ + +/*! + \fn bool QRay3D::operator!=(const QRay3D &other) + + Returns true if this ray is not the same as \a other; false otherwise. + + \sa operator==() +*/ + +/*! + \fn bool qFuzzyCompare(const QRay3D &ray1, const QRay3D &ray2) + \relates QRay3D + + Returns true if \a ray1 and \a ray2 are almost equal; false otherwise. +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QRay3D &ray) +{ + dbg.nospace() << "QRay3D(origin(" + << ray.origin().x() << ", " << ray.origin().y() << ", " + << ray.origin().z() << ") - direction(" + << ray.direction().x() << ", " << ray.direction().y() << ", " + << ray.direction().z() << "))"; + return dbg.space(); +} + +#endif + +#ifndef QT_NO_DATASTREAM + +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QRay3D &ray) + \relates QRay3D + + Writes the given \a ray to the given \a stream and returns a + reference to the stream. +*/ + +QDataStream &operator<<(QDataStream &stream, const QRay3D &ray) +{ + stream << ray.origin(); + stream << ray.direction(); + return stream; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QRay3D &ray) + \relates QRay3D + + Reads a 3D ray from the given \a stream into the given \a ray + and returns a reference to the stream. +*/ + +QDataStream &operator>>(QDataStream &stream, QRay3D &ray) +{ + QVector3D origin, direction; + stream >> origin; + stream >> direction; + ray = QRay3D(origin, direction); + return stream; +} + +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE diff --git a/src/threed/math3d/qray3d.h b/src/threed/math3d/qray3d.h new file mode 100644 index 000000000..49883c2a5 --- /dev/null +++ b/src/threed/math3d/qray3d.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRAY3D_H +#define QRAY3D_H + +#include "qt3dglobal.h" +#include <QtGui/qvector3d.h> +#include <QtGui/qmatrix4x4.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QRay3D +{ +public: + QRay3D(); + QRay3D(const QVector3D &origin, const QVector3D &direction); + + QVector3D origin() const; + void setOrigin(const QVector3D & value); + + QVector3D direction() const; + void setDirection(const QVector3D & value); + + bool contains(const QVector3D &point) const; + bool contains(const QRay3D &ray) const; + + QVector3D point(qreal t) const; + qreal fromPoint(const QVector3D &point) const; + + QVector3D project(const QVector3D &vector) const; + + qreal distanceTo(const QVector3D &point) const; + + void transform(const QMatrix4x4 &matrix); + QRay3D transformed(const QMatrix4x4 &matrix) const; + + bool operator==(const QRay3D &other); + bool operator!=(const QRay3D &other); + +private: + QVector3D m_origin; + QVector3D m_direction; + +}; + +inline QRay3D::QRay3D() : m_direction(1.0f, 0.0f, 0.0f) {} + +inline QRay3D::QRay3D(const QVector3D &origin, const QVector3D &direction) + : m_origin(origin) + , m_direction(direction) +{ +} + +inline QVector3D QRay3D::origin() const +{ + return m_origin; +} + +inline void QRay3D::setOrigin(const QVector3D &value) +{ + m_origin = value; +} + +inline QVector3D QRay3D::direction() const +{ + return m_direction; +} + +inline void QRay3D::setDirection(const QVector3D & value) +{ + m_direction = value; +} + +inline QVector3D QRay3D::point(qreal t) const +{ + return m_origin + t * m_direction; +} + +inline void QRay3D::transform(const QMatrix4x4 &matrix) +{ + m_origin = matrix * m_origin; + m_direction = matrix.mapVector(m_direction); +} + +inline QRay3D QRay3D::transformed(const QMatrix4x4 &matrix) const +{ + return QRay3D(matrix * m_origin, matrix.mapVector(m_direction)); +} + +inline bool QRay3D::operator==(const QRay3D &other) +{ + return m_origin == other.origin() && m_direction == other.direction(); +} + +inline bool QRay3D::operator!=(const QRay3D &other) +{ + return m_origin != other.origin() || m_direction != other.direction(); +} + +inline bool qFuzzyCompare(const QRay3D &ray1, const QRay3D &ray2) +{ + return qFuzzyCompare(ray1.origin(), ray2.origin()) && + qFuzzyCompare(ray1.direction(), ray2.direction()); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QRay3D &ray); +#endif + +#ifndef QT_NO_DATASTREAM +Q_QT3D_EXPORT QDataStream &operator<<(QDataStream &stream, const QRay3D &ray); +Q_QT3D_EXPORT QDataStream &operator>>(QDataStream &stream, QRay3D &ray); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QRay3D) + +QT_END_HEADER + +#endif diff --git a/src/threed/math3d/qsphere3d.cpp b/src/threed/math3d/qsphere3d.cpp new file mode 100644 index 000000000..e4b9e6e50 --- /dev/null +++ b/src/threed/math3d/qsphere3d.cpp @@ -0,0 +1,385 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsphere3d.h" +#include "qray3d.h" +#include "qbox3d.h" +#include "qplane3d.h" +#include <QtGui/qmatrix4x4.h> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QSphere3D + \brief The QSphere3D class represents a mathematical sphere in 3D space. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::math + + QSphere3D can be used to represent the bounding regions of objects + in a 3D scene so that they can be easily culled if they are out of view. + It can also be used to assist with collision testing. + + \sa QBox3D +*/ + +/*! + \fn QSphere3D::QSphere3D() + + Constructs a default sphere with a center() of (0, 0, 0) + and radius() of 1. +*/ + +/*! + \fn QSphere3D::QSphere3D(const QVector3D ¢er, qreal radius) + + Constructs a sphere with the specified \a center and \a radius. +*/ + +/*! + \fn QVector3D QSphere3D::center() const + + Returns the center of this sphere. + + \sa setCenter(), radius() +*/ + +/*! + \fn void QSphere3D::setCenter(const QVector3D ¢er) + + Sets the \a center of this sphere. + + \sa center(), setRadius() +*/ + +/*! + \fn qreal QSphere3D::radius() const + + Returns the radius of this sphere. + + \sa setRadius(), center() +*/ + +/*! + \fn void QSphere3D::setRadius(qreal radius) + + Sets the \a radius of this sphere. + + \sa radius(), setCenter() +*/ + +/*! + \fn bool QSphere3D::contains(const QVector3D &point) const + + Returns true if \a point is contained within the bounds of + this sphere; false otherwise. +*/ + +/*! + Returns true if this sphere intersects \a ray; false otherwise. + + \sa intersection() +*/ +bool QSphere3D::intersects(const QRay3D &ray) const +{ + QVector3D centerToOrigin = ray.origin() - m_center; + qreal term1 = ray.direction().lengthSquared(); + qreal term2 = 2.0f * QVector3D::dotProduct(centerToOrigin, ray.direction()); + qreal term3 = centerToOrigin.lengthSquared() - m_radius * m_radius; + qreal det = term2 * term2 - (4.0f * term1 * term3); + return term1 != 0.0f && det >= 0.0f; +} + +/*! + \fn bool QSphere3D::intersects(const QSphere3D &sphere) const + + Returns true if this sphere intersects \a sphere; false otherwise. + + \sa contains() +*/ + +/*! + Returns true if this sphere intersects \a box; false otherwise. +*/ +bool QSphere3D::intersects(const QBox3D &box) const +{ + if (box.isFinite()) { + // Use Arvo's Algorithm to determine if we have an intersection. + qreal dist = 0.0f; + qreal center = m_center.x(); + qreal minval = box.minimum().x(); + qreal maxval = box.maximum().x(); + if (center < minval) + dist += (center - minval) * (center - minval); + else if (center > maxval) + dist += (center - maxval) * (center - maxval); + center = m_center.y(); + minval = box.minimum().y(); + maxval = box.maximum().y(); + if (center < minval) + dist += (center - minval) * (center - minval); + else if (center > maxval) + dist += (center - maxval) * (center - maxval); + center = m_center.z(); + minval = box.minimum().z(); + maxval = box.maximum().z(); + if (center < minval) + dist += (center - minval) * (center - minval); + else if (center > maxval) + dist += (center - maxval) * (center - maxval); + return dist <= (m_radius * m_radius); + } else { + return box.isInfinite(); + } +} + +/*! + Returns true if this sphere intersects \a plane; false otherwise. +*/ +bool QSphere3D::intersects(const QPlane3D &plane) const +{ + return qAbs(plane.distanceTo(m_center)) <= m_radius; +} + +/*! + Finds the \a minimum_t and \a maximum_t values where \a ray intersects + this sphere. Returns true if intersections were found; or false if there + is no intersection. + + If \a minimum_t and \a maximum_t are set to the same value, then \a ray + touches the surface of the sphere at a single point. If the t values are + negative, then the intersection occurs before the ray's origin point + in the reverse direction of the ray. + + The \a minimum_t and \a maximum_t values can be passed to QRay3D::point() + to determine the actual intersection points, as shown in the following + example: + + \code + qreal minimum_t, maximum_t; + if (sphere.intersection(ray, &minimum_t, &maximum_t)) { + qDebug() << "intersections at" + << ray.point(minimum_t) << "and" + << ray.point(maximum_t); + } + \endcode + + \sa intersects(), QRay3D::point() +*/ +bool QSphere3D::intersection(const QRay3D &ray, qreal *minimum_t, qreal *maximum_t) const +{ + QVector3D centerToOrigin = ray.origin() - m_center; + qreal term1 = ray.direction().lengthSquared(); + qreal term2 = 2.0f * QVector3D::dotProduct(centerToOrigin, ray.direction()); + qreal term3 = centerToOrigin.lengthSquared() - m_radius * m_radius; + qreal det = term2 * term2 - (4.0f * term1 * term3); + if (term1 == 0.0f || det < 0.0f) { + *minimum_t = qSNaN(); + *maximum_t = qSNaN(); + return false; + } else if (det == 0.0f) { + *minimum_t = *maximum_t = -term2 / (2.0f * term1); + } else { + qreal sqrtDet = qSqrt(det); + qreal t1 = (-term2 - sqrtDet) / (2.0f * term1); + qreal t2 = (-term2 + sqrtDet) / (2.0f * term1); + if (t1 < t2) { + *minimum_t = t1; + *maximum_t = t2; + } else { + *minimum_t = t2; + *maximum_t = t1; + } + } + return true; +} + +/*! + Returns the t value at which \a ray first intersects the surface of + this sphere, or not-a-number if there is no intersection. + + When the \a ray intersects this sphere, the return value is a + parametric value that can be passed to QRay3D::point() to determine + the actual intersection point, as shown in the following example: + + \code + qreal t = sphere.intersection(ray); + QVector3D pt; + if (qIsNaN(t)) { + qWarning("no intersection occurred"); + else + pt = ray.point(t); + \endcode + + The \a ray might intersect at two points - as the ray passes through + the sphere - one on the near side, one on the far side; where near and far + are relative to the origin point of the ray. This function only + returns the near intersection point. + + Only positive values on the ray are considered. This means that if + the origin point of the ray is inside the sphere, there is only one + solution, not two. To get the other solution, simply change + the sign of the ray's direction vector. If the origin point of + the ray is outside the sphere, and the direction points away from + the sphere, then there will be no intersection. + + When the ray does not intersect the sphere in the positive direction, + then the return value is not-a-number. + + \sa intersects(), QRay3D::point() +*/ +qreal QSphere3D::intersection(const QRay3D &ray) const +{ + qreal minimum_t, maximum_t; + if (intersection(ray, &minimum_t, &maximum_t)) { + if (minimum_t >= 0.0f) + return minimum_t; + else if (maximum_t >= 0.0f) + return maximum_t; + else + return qSNaN(); + } else { + return qSNaN(); + } +} + +/*! + \fn void QSphere3D::transform(const QMatrix4x4 &matrix) + + Transforms this sphere's center() and radius() according to \a matrix. + + It is assumed that \a matrix contains a uniform scale factor in the + x, y, and z directions. Otherwise the radius() in the result is undefined. + + \sa transformed() +*/ + +/*! + Returns the result of transforming this sphere's center() and radius() + according to \a matrix. + + It is assumed that \a matrix contains a uniform scale factor in the + x, y, and z directions. Otherwise the radius() in the result is undefined. + + \sa transform() +*/ +QSphere3D QSphere3D::transformed(const QMatrix4x4 &matrix) const +{ + return QSphere3D(matrix * m_center, + matrix.mapVector(QVector3D(m_radius, 0, 0)).length()); +} + +/*! + \fn bool QSphere3D::operator==(const QSphere3D &sphere) const + + Returns true if this sphere is the same as \a sphere; false otherwise. + + \sa operator!=() +*/ + +/*! + \fn bool QSphere3D::operator!=(const QSphere3D &sphere) const + + Returns true if this sphere is not the same as \a sphere; false otherwise. + + \sa operator==() +*/ + +/*! + \fn bool qFuzzyCompare(const QSphere3D &sphere1, const QSphere3D &sphere2) + \relates QSphere3D + + Returns true if \a sphere1 and \a sphere2 are almost equal; + false otherwise. +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QSphere3D &sphere) +{ + dbg.nospace() << "QSphere3D(center=(" + << sphere.center().x() << ", " << sphere.center().y() << ", " + << sphere.center().z() << "), radius=" << sphere.radius() << ')'; + return dbg.space(); +} + +#endif + +#ifndef QT_NO_DATASTREAM + +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QSphere3D &sphere) + \relates QSphere3D + + Writes the given \a sphere to the given \a stream and returns a + reference to the stream. +*/ + +QDataStream &operator<<(QDataStream &stream, const QSphere3D &sphere) +{ + stream << sphere.center(); + stream << double(sphere.radius()); + return stream; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QSphere3D &sphere) + \relates QSphere3D + + Reads a 3D sphere from the given \a stream into the given \a sphere + and returns a reference to the stream. +*/ + +QDataStream &operator>>(QDataStream &stream, QSphere3D &sphere) +{ + QVector3D center; + double radius; + stream >> center; + stream >> radius; + sphere.setCenter(center); + sphere.setRadius(qreal(radius)); + return stream; +} + +#endif + +QT_END_NAMESPACE diff --git a/src/threed/math3d/qsphere3d.h b/src/threed/math3d/qsphere3d.h new file mode 100644 index 000000000..9f4afe037 --- /dev/null +++ b/src/threed/math3d/qsphere3d.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSPHERE3D_H +#define QSPHERE3D_H + +#include "qt3dglobal.h" +#include <QtGui/qvector3d.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QMatrix4x4; +class QRay3D; +class QBox3D; +class QPlane3D; + +class Q_QT3D_EXPORT QSphere3D +{ +public: + QSphere3D(); + QSphere3D(const QVector3D ¢er, qreal radius); + + QVector3D center() const; + void setCenter(const QVector3D ¢er); + + qreal radius() const; + void setRadius(qreal radius); + + bool contains(const QVector3D &point) const; + + bool intersects(const QRay3D &ray) const; + bool intersects(const QSphere3D &sphere) const; + bool intersects(const QBox3D &box) const; + bool intersects(const QPlane3D &plane) const; + + bool intersection(const QRay3D &ray, qreal *minimum_t, qreal *maximum_t) const; + qreal intersection(const QRay3D &ray) const; + + void transform(const QMatrix4x4 &matrix); + QSphere3D transformed(const QMatrix4x4 &matrix) const; + + bool operator==(const QSphere3D &sphere) const; + bool operator!=(const QSphere3D &sphere) const; + +private: + QVector3D m_center; + qreal m_radius; +}; + +inline QSphere3D::QSphere3D() : m_radius(1.0f) {} + +inline QSphere3D::QSphere3D(const QVector3D ¢er, qreal radius) + : m_center(center), m_radius(radius) {} + +inline QVector3D QSphere3D::center() const +{ + return m_center; +} + +inline void QSphere3D::setCenter(const QVector3D ¢er) +{ + m_center = center; +} + +inline qreal QSphere3D::radius() const +{ + return m_radius; +} + +inline void QSphere3D::setRadius(qreal radius) +{ + m_radius = radius; +} + +inline bool QSphere3D::contains(const QVector3D &point) const +{ + return (point - m_center).lengthSquared() <= (m_radius * m_radius); +} + +inline bool QSphere3D::intersects(const QSphere3D &sphere) const +{ + qreal radsum = sphere.radius() + m_radius; + return (sphere.center() - m_center).lengthSquared() <= (radsum * radsum); +} + +inline void QSphere3D::transform(const QMatrix4x4 &matrix) +{ + *this = transformed(matrix); +} + +inline bool QSphere3D::operator==(const QSphere3D &sphere) const +{ + return m_center == sphere.m_center && m_radius == sphere.m_radius; +} + +inline bool QSphere3D::operator!=(const QSphere3D &sphere) const +{ + return m_center != sphere.m_center || m_radius != sphere.m_radius; +} + +inline bool qFuzzyCompare(const QSphere3D &sphere1, const QSphere3D &sphere2) +{ + return qFuzzyCompare(sphere1.center(), sphere2.center()) && + qFuzzyCompare(sphere1.radius(), sphere2.radius()); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QSphere3D &sphere); +#endif + +#ifndef QT_NO_DATASTREAM +Q_QT3D_EXPORT QDataStream &operator<<(QDataStream &stream, const QSphere3D &sphere); +Q_QT3D_EXPORT QDataStream &operator>>(QDataStream &stream, QSphere3D &sphere); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QSphere3D) + +QT_END_HEADER + +#endif diff --git a/src/threed/math3d/qtriangle3d.cpp b/src/threed/math3d/qtriangle3d.cpp new file mode 100644 index 000000000..f68c85645 --- /dev/null +++ b/src/threed/math3d/qtriangle3d.cpp @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtriangle3d.h" +#include <QtGui/qmatrix4x4.h> +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QTriangle3D + \brief The QTriangle3D class represents a triangle as three points in 3D space. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::math + + A triangle is defined by 3 points in 3D space. Since any 3 points define + a plane, the triangle can be thought of as defining a plane, and forming a + geometric region in that plane. + + If you need a simple plane, with no particular geometry, then + QPlane3D is a more compact and mathematically sufficient class. + + The three points are labelled p(), q() and r() for consistency with + textbook treatments. It is recommended that the points be supplied + in counter-clockwise order for correct orientation of the + triangle's plane(). + + \sa QPlane3D +*/ + +/*! + \fn QTriangle3D::QTriangle3D() + + Constructs a default triangle which lies in the x-z plane, + with the three vertices (0, 0, 0), (1, 0, 0), and (0, 1, 0). +*/ + +/*! + \fn QTriangle3D::QTriangle3D(const QVector3D &p, const QVector3D &q, const QVector3D &r) + + Constructs a triangle with the supplied \a p, \a q and \a r vertices. +*/ + +/*! + \fn QVector3D QTriangle3D::p() const + + Returns the value of the P vertex on the triangle. + + \sa q(), r(), setP() +*/ + +/*! + \fn void QTriangle3D::setP(const QVector3D &point) + + Sets the value of the P vertex on the triangle to \a point. + + \sa setQ(), setR(), p() +*/ + +/*! + \fn QVector3D QTriangle3D::q() const + + Returns the value of the Q vertex on the triangle. + + \sa p(), r(), setQ() +*/ + +/*! + \fn void QTriangle3D::setQ(const QVector3D &point) + + Sets the value of the Q vertex on the triangle \a point. + + \sa setP(), setR(), q() +*/ + + +/*! + \fn QVector3D QTriangle3D::r() const + + Returns the value of the R vertex on the triangle. + + \sa p(), q(), setR() +*/ + +/*! + \fn void QTriangle3D::setR(const QVector3D &point) + + Sets the value of the R vertex on the triangle \a point. + + \sa setP(), setQ(), r() +*/ + +/*! + \fn QPlane3D QTriangle3D::plane() const + + Returns the plane in which the triangle lies. + + \sa QPlane3D +*/ + +/*! + \fn QVector3D QTriangle3D::center() const + + Returns the center of the triangle, which is the geometric average of the + three vertices. +*/ + +/*! + \fn QVector3D QTriangle3D::faceNormal() const + + Returns the vector normal to this triangle, computed from the + cross-product of P-Q and Q-R. The result is not normalized. +*/ + +/*! + Returns true if this triangle contains \a point; false otherwise. + To contain the \a point means that: + \list + \o the point lies on the same plane as the triangle, and + \o the point + \list + \o lies either wholly within the triangle, or + \o lies on one of the sides, or + \o coincides with one of the 3 vertices + \endlist + \endlist + + \sa intersects() +*/ +bool QTriangle3D::contains(const QVector3D &point) const +{ + // Check if the point is on the triangle's plane first. + QVector3D normal = QVector3D::crossProduct(m_q - m_p, m_r - m_q); + if (!qFuzzyIsNull(float(QVector3D::dotProduct(normal, m_p - point)))) + return false; + + // Compute the barycentric co-ordinates and use them to determine + // if the point is within the triangle. + QVector2D c = uv(point); + if (c.x() < 0.0f || c.x() > 1.0f) + return false; + if (c.y() < 0.0f || c.y() > 1.0f) + return false; + if ((c.x() + c.y()) > 1.0f) + return false; + return true; +} + +/*! + Returns true if the \a ray intersects this triangle; false otherwise. + + This function will return false if the triangle is degenerate. + + \sa contains(), intersection() +*/ +bool QTriangle3D::intersects(const QRay3D &ray) const +{ + qreal t = plane().intersection(ray); + if (qIsNaN(t)) + return false; + return contains(ray.point(t)); +} + +/*! + Returns the t value at which \a ray intersects this triangle, or + not-a-number if there is no intersection. + + When the \a ray intersects this triangle, the return value is a + parametric value that can be passed to QRay3D::point() to determine + the actual intersection point, as shown in the following example: + + \code + qreal t = triangle.intersection(ray); + QVector3D pt; + if (qIsNaN(t)) { + qWarning("no intersection occurred"); + else + pt = ray.point(t); + \endcode + + \sa intersects(), contains(), QRay3D::point() + */ +qreal QTriangle3D::intersection(const QRay3D &ray) const +{ + qreal t = plane().intersection(ray); + if (qIsNaN(t) || contains(ray.point(t))) + return t; + return qSNaN(); +} + +/*! + Transforms the points of this triangle according to \a matrix. + + \sa transformed() +*/ +void QTriangle3D::transform(const QMatrix4x4 &matrix) +{ + m_p = matrix * m_p; + m_q = matrix * m_q; + m_r = matrix * m_r; +} + +/*! + Returns a new triangle that results from transforming this + one using \a matrix. + + \sa transform() +*/ +QTriangle3D QTriangle3D::transformed(const QMatrix4x4 &matrix) const +{ + return QTriangle3D(matrix * m_p, matrix * m_q, matrix * m_r); +} + +/*! + Returns the (u, v) barycentric co-ordinates of \a point within + this triangle. + + The returned barycentric co-ordinates will be (1, 0) at p(), + (0, 1) at q(), and (0, 0) at r(). Technically, barycentric + co-ordinates have three components with the corners at + (1, 0, 0), (0, 1, 0), and (0, 0, 1). However, the third + component is always equal to (1 - u - v) so we do not return it. + + The typical use case for this function is to convert an intersection + point on a triangle into the texture co-ordinate corresponding to + that point. If \c p, \c q, and \c r are the points on the triangle, + with corresponding texture co-ordinates \c tp, \c tq, and \c tr, + then the texture co-ordinate \c tc of \a point can be determined + by the following code: + + \code + QTriangle3D triangle(p, q, r); + QVector2D uv = triangle.uv(point); + QVector2D tc = uv.x() * tp + uv.y() * tq + (1 - uv.x() - uv.y()) * tr; + \endcode + + \sa contains(), intersection() +*/ +QVector2D QTriangle3D::uv(const QVector3D &point) const +{ + // Algorithm from: http://www.blackpawn.com/texts/pointinpoly/default.html + // More: http://en.wikipedia.org/wiki/Barycentric_coordinates_(mathematics) + QVector3D rq = m_q - m_r; + QVector3D rp = m_p - m_r; + QVector3D pp = point - m_r; + qreal dot_rq_rq = QVector3D::dotProduct(rq, rq); + qreal dot_rq_rp = QVector3D::dotProduct(rq, rp); + qreal dot_rq_pp = QVector3D::dotProduct(rq, pp); + qreal dot_rp_rp = QVector3D::dotProduct(rp, rp); + qreal dot_rp_pp = QVector3D::dotProduct(rp, pp); + qreal det = dot_rq_rq * dot_rp_rp - dot_rq_rp * dot_rq_rp; + if (qFuzzyIsNull(float(det))) { + // The point is probably not in the triangle, or the triangle + // is degenerate. Return an out of range value for (u, v) so + // that contains() will fail when this case happens. + return QVector2D(-1.0f, -1.0f); + } + return QVector2D((dot_rq_rq * dot_rp_pp - dot_rq_rp * dot_rq_pp) / det, + (dot_rp_rp * dot_rq_pp - dot_rq_rp * dot_rp_pp) / det); +} + +/*! + \fn bool QTriangle3D::operator==(const QTriangle3D &other) + + Returns true if this triangle is the same as \a other; false otherwise. + + \sa operator!=() +*/ + +/*! + \fn bool QTriangle3D::operator!=(const QTriangle3D &other) + + Returns true if this triangle is not the same as \a other; false otherwise. + + \sa operator==() +*/ + +/*! + \fn bool qFuzzyCompare(const QTriangle3D &triangle1, const QTriangle3D &triangle2) + \relates QTriangle3D + + Returns true if \a triangle1 and \a triangle2 are almost equal; + false otherwise. +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QTriangle3D &triangle) +{ + dbg.nospace() << "QTriangle3D((" + << triangle.p().x() << ", " << triangle.p().y() << ", " + << triangle.p().z() << "), (" + << triangle.q().x() << ", " << triangle.q().y() << ", " + << triangle.q().z() << "), (" + << triangle.r().x() << ", " << triangle.r().y() << ", " + << triangle.r().z() << "))"; + return dbg.space(); +} + +#endif + +#ifndef QT_NO_DATASTREAM + +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QTriangle3D &triangle) + \relates QTriangle3D + + Writes the given \a triangle to the given \a stream and returns a + reference to the stream. +*/ + +QDataStream &operator<<(QDataStream &stream, const QTriangle3D &triangle) +{ + stream << triangle.p(); + stream << triangle.q(); + stream << triangle.r(); + return stream; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QTriangle3D &triangle) + \relates QTriangle3D + + Reads a 3D triangle from the given \a stream into the given \a triangle + and returns a reference to the stream. +*/ + +QDataStream &operator>>(QDataStream &stream, QTriangle3D &triangle) +{ + QVector3D p, q, r; + stream >> p; + stream >> q; + stream >> r; + triangle = QTriangle3D(p, q, r); + return stream; +} + +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE diff --git a/src/threed/math3d/qtriangle3d.h b/src/threed/math3d/qtriangle3d.h new file mode 100644 index 000000000..cc88f9cd0 --- /dev/null +++ b/src/threed/math3d/qtriangle3d.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTRIANGLE3D_H +#define QTRIANGLE3D_H + +#include <QtGui/qvector3d.h> +#include <QtGui/qvector2d.h> +#include "qray3d.h" +#include "qplane3d.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QMatrix4x4; + +class Q_QT3D_EXPORT QTriangle3D +{ +public: + QTriangle3D(); + QTriangle3D(const QVector3D &p, const QVector3D &q, const QVector3D &r); + + QVector3D p() const; + void setP(const QVector3D & point); + + QVector3D q() const; + void setQ(const QVector3D & point); + + QVector3D r() const; + void setR(const QVector3D & point); + + QPlane3D plane() const; + QVector3D center() const; + QVector3D faceNormal() const; + + bool contains(const QVector3D &point) const; + + bool intersects(const QRay3D &ray) const; + qreal intersection(const QRay3D &ray) const; + + void transform(const QMatrix4x4 &matrix); + QTriangle3D transformed(const QMatrix4x4 &matrix) const; + + QVector2D uv(const QVector3D &point) const; + + bool operator==(const QTriangle3D &other); + bool operator!=(const QTriangle3D &other); + +private: + QVector3D m_p, m_q, m_r; +}; + +inline QTriangle3D::QTriangle3D() + : m_p(0.0f, 0.0f, 0.0f) + , m_q(1.0f, 0.0f, 0.0f) + , m_r(0.0f, 1.0f, 0.0f) {} + +inline QTriangle3D::QTriangle3D(const QVector3D &p, const QVector3D &q, const QVector3D &r) + : m_p(p) + , m_q(q) + , m_r(r) {} + +inline QVector3D QTriangle3D::p() const +{ + return m_p; +} + +inline void QTriangle3D::setP(const QVector3D &point) +{ + m_p = point; +} + +inline QVector3D QTriangle3D::q() const +{ + return m_q; +} + +inline void QTriangle3D::setQ(const QVector3D &point) +{ + m_q = point; +} + +inline QVector3D QTriangle3D::r() const +{ + return m_r; +} + +inline void QTriangle3D::setR(const QVector3D &point) +{ + m_r = point; +} + +inline QPlane3D QTriangle3D::plane() const +{ + return QPlane3D(m_p, m_q, m_r); +} + +inline QVector3D QTriangle3D::center() const +{ + return (m_p + m_q + m_r) / 3.0f; +} + +inline QVector3D QTriangle3D::faceNormal() const +{ + return QVector3D::crossProduct(m_q - m_p, m_r - m_q); +} + +inline bool QTriangle3D::operator==(const QTriangle3D &other) +{ + return m_p == other.m_p && m_q == other.m_q && m_r == other.m_r; +} + +inline bool QTriangle3D::operator!=(const QTriangle3D &other) +{ + return m_p != other.m_p || m_q != other.m_q || m_r != other.m_r; +} + +inline bool qFuzzyCompare + (const QTriangle3D &triangle1, const QTriangle3D &triangle2) +{ + return qFuzzyCompare(triangle1.p(), triangle2.p()) && + qFuzzyCompare(triangle1.q(), triangle2.q()) && + qFuzzyCompare(triangle1.r(), triangle2.r()); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QTriangle3D &triangle); +#endif + +#ifndef QT_NO_DATASTREAM +Q_QT3D_EXPORT QDataStream &operator<<(QDataStream &stream, const QTriangle3D &triangle); +Q_QT3D_EXPORT QDataStream &operator>>(QDataStream &stream, QTriangle3D &triangle); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QTriangle3D) + +QT_END_HEADER + +#endif // QTRIANGLE3D_H diff --git a/src/threed/painting/painting.pri b/src/threed/painting/painting.pri new file mode 100644 index 000000000..8f5b5951b --- /dev/null +++ b/src/threed/painting/painting.pri @@ -0,0 +1,29 @@ + +INCLUDEPATH += $$PWD +VPATH += $$PWD + +HEADERS += \ + qglabstracteffect.h \ + qgllightmodel.h \ + qgllightparameters.h \ + qglpainter.h \ + qmatrix4x4stack.h + +SOURCES += \ + qglabstracteffect.cpp \ + qglext.cpp \ + qgllightmodel.cpp \ + qgllightparameters.cpp \ + qglpainter.cpp \ + qglpickcolors.cpp \ + qmatrix4x4stack.cpp + +PRIVATE_HEADERS += \ + qglpainter_p.h \ + qglpickcolors_p.h \ + qglabstracteffect_p.h \ + qmatrix4x4stack_p.h + +# Don't add this file to PRIVATE_HEADERS because we don't +# want moc to run over it: +# qglext_p.h diff --git a/src/threed/painting/qglabstracteffect.cpp b/src/threed/painting/qglabstracteffect.cpp new file mode 100644 index 000000000..26cacae8b --- /dev/null +++ b/src/threed/painting/qglabstracteffect.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglabstracteffect.h" +#include "qglpainter_p.h" +#include <QtOpenGL/qglshaderprogram.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLAbstractEffect + \since 4.8 + \brief The QGLAbstractEffect class provides a standard interface for rendering surface material effects with GL. + \ingroup qt3d + \ingroup qt3d::painting + + \section1 Vertex attributes + + Vertex attributes for the effect are specified using + QGLPainter::setVertexAttribute() and QGLPainter::setVertexBundle(), + and may be independent of the effect itself. Those functions + will bind standard attributes to specific indexes within the + GL state. For example, the QGL::Position will be bound + to index 0, QGL::TextureCoord0 will be bound to index 3, etc. + + Effect subclasses that use vertex shaders should bind their attributes + to these indexes using QGLShaderProgram::bindAttributeLocation() + just before the program is linked. For example: + + \code + QGLShaderProgram *program = new QGLShaderProgram(); + program->addShaderFromSourceCode(QGLShader::Vertex, vshaderSource); + program->addShaderFromSourceCode(QGLShader::Fragment, fshaderSource); + program->bindAttributeLocation("vertex", QGL::Position); + program->bindAttributeLocation("normal", QGL::Normal); + program->bindAttributeLocation("texcoord", QGL::TextureCoord0); + program->link(); + \endcode + + The QGLShaderProgramEffect class can assist with writing + shader-based effects. It will automatically bind special + variable names, such as \c{qt_Vertex}, \c{qt_MultiTexCoord0}, etc, + to the standard indexes. This alleviates the need for the + application to bind the names itself. +*/ + +/*! + Constructs a new effect object. +*/ +QGLAbstractEffect::QGLAbstractEffect() +{ +} + +/*! + Destroys this effect object. +*/ +QGLAbstractEffect::~QGLAbstractEffect() +{ +} + +/*! + Returns true if this effect supports object picking; false otherwise. + The default implementation returns false, which causes QGLPainter + to use the effect associated with QGL::FlatColor to perform + object picking. + + Effects that support object picking render fragments with + QGLPainter::pickColor() when QGLPainter::isPicking() returns true. + By default, only the effect associated with QGL::FlatColor does this, + rendering the entire fragment with the flat pick color. + + In some cases, rendering the entire fragment with the pick color + may not be appropriate. An alpha-blended icon texture that is + drawn to the screen as a quad may have an irregular shape smaller + than the quad. For picking, the application may not want the + entire quad to be "active" for object selection as it would appear + to allow the user to click off the icon to select it. + + This situation can be handled by implementing an icon rendering + effect that draws the icon normally when QGLPainter::isPicking() + is false, and draws a mask texture defining the outline of the icon + with QGLPainter::pickColor() when QGLPainter::isPicking() is true. + + \sa QGLPainter::setPicking() +*/ +bool QGLAbstractEffect::supportsPicking() const +{ + return false; +} + +/*! + \fn void QGLAbstractEffect::setActive(QGLPainter *painter, bool flag) + + Activates or deactivates this effect on \a painter, + according to \a flag, on the current GL context by selecting + shader programs, setting lighting and material parameters, etc. + + \sa update() +*/ + +/*! + \fn void QGLAbstractEffect::update(QGLPainter *painter, QGLPainter::Updates updates) + + Updates the current GL context with information from \a painter + just prior to the drawing of triangles, quads, etc. + + The \a updates parameter specifies the properties on \a painter + that have changed since the last call to update() or setActive(). + + \sa setActive() +*/ + +QT_END_NAMESPACE diff --git a/src/threed/painting/qglabstracteffect.h b/src/threed/painting/qglabstracteffect.h new file mode 100644 index 000000000..758bdabf6 --- /dev/null +++ b/src/threed/painting/qglabstracteffect.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLABSTRACTEFFECT_H +#define QGLABSTRACTEFFECT_H + +#include "qglpainter.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QGLAbstractEffect +{ +public: + QGLAbstractEffect(); + virtual ~QGLAbstractEffect(); + + virtual bool supportsPicking() const; + virtual void setActive(QGLPainter *painter, bool flag) = 0; + virtual void update(QGLPainter *painter, QGLPainter::Updates updates) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/painting/qglabstracteffect_p.h b/src/threed/painting/qglabstracteffect_p.h new file mode 100644 index 000000000..513c89b9b --- /dev/null +++ b/src/threed/painting/qglabstracteffect_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLABSTRACTEFFECT_P_H +#define QGLABSTRACTEFFECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglabstracteffect.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#if defined(QT_OPENGL_ES_2) +#define QGL_SHADERS_ONLY 1 +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/painting/qglext.cpp b/src/threed/painting/qglext.cpp new file mode 100644 index 000000000..0618d79e4 --- /dev/null +++ b/src/threed/painting/qglext.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglext_p.h" +#include "qopenglfunctions.h" + +#include <QtOpenGL/private/qglextensions_p.h> + +QT_BEGIN_NAMESPACE + +#if !defined(QT_OPENGL_ES) + +typedef void (QT3D_GLF_APIENTRYP q_PFNGLCLIENTACTIVETEXTUREPROC) (GLenum); + +class QGLMultiTextureExtensions +{ +public: + QGLMultiTextureExtensions(const QGLContext * = 0) + { + clientActiveTexture = 0; + multiTextureResolved = false; + } + + q_PFNGLCLIENTACTIVETEXTUREPROC clientActiveTexture; + bool multiTextureResolved; +}; + +Q_GLOBAL_STATIC(QGLResource<QGLMultiTextureExtensions>, qt_multitexture_funcs) + +static QGLMultiTextureExtensions *resolveMultiTextureExtensions + (const QGLContext *ctx) +{ + QGLMultiTextureExtensions *extn = qt_multitexture_funcs()->value(ctx); + if (!(extn->multiTextureResolved)) { + extn->multiTextureResolved = true; + if (!extn->clientActiveTexture) { + extn->clientActiveTexture = (q_PFNGLCLIENTACTIVETEXTUREPROC) + ctx->getProcAddress(QLatin1String("glClientActiveTexture")); + } + if (!extn->clientActiveTexture) { + extn->clientActiveTexture = (q_PFNGLCLIENTACTIVETEXTUREPROC) + ctx->getProcAddress(QLatin1String("glClientActiveTextureARB")); + } + } + return extn; +} + +void qt_gl_ClientActiveTexture(GLenum texture) +{ + const QGLContext *ctx = QGLContext::currentContext(); + if (!ctx) + return; + QGLMultiTextureExtensions *extn = resolveMultiTextureExtensions(ctx); + if (extn->clientActiveTexture) + extn->clientActiveTexture(texture); +} + +#endif + +QT_END_NAMESPACE diff --git a/src/threed/painting/qglext_p.h b/src/threed/painting/qglext_p.h new file mode 100644 index 000000000..d34b499d7 --- /dev/null +++ b/src/threed/painting/qglext_p.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLEXT_P_H +#define QGLEXT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtOpenGL/qgl.h> +#include "qt3dglobal.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +// Provide some useful OpenGL extension definitions. + +#if !defined(QT_OPENGL_ES) + +extern void Q_QT3D_EXPORT qt_gl_ClientActiveTexture(GLenum texture); + +#elif !defined(QT_OPENGL_ES_2) + +#define qt_gl_ClientActiveTexture glClientActiveTexture + +#endif + +class QGLExtensionChecker +{ +public: + QGLExtensionChecker(const char *str) + : gl_extensions(str), gl_extensions_length(qstrlen(str)) + {} + + bool match(const char *str) { + int str_length = qstrlen(str); + const char *extensions = gl_extensions; + int extensions_length = gl_extensions_length; + + while (1) { + // the total length that needs to be matched is the str_length + + // the space character that terminates the extension name + if (extensions_length < str_length + 1) + return false; + if (qstrncmp(extensions, str, str_length) == 0 && extensions[str_length] == ' ') + return true; + + int split_pos = 0; + while (split_pos < extensions_length && extensions[split_pos] != ' ') + ++split_pos; + ++split_pos; // added for the terminating space character + extensions += split_pos; + extensions_length -= split_pos; + } + return false; + } + +private: + const char *gl_extensions; + int gl_extensions_length; +}; + +// Copy of some definitions from <QtOpenGL/private/qgl_p.h> so that +// we can avoid a direct dependency upon private headers in Qt. + +#if QT_VERSION >= 0x040800 + +class QGLContextGroup; + +#if !defined(QGL_P_H) + +class Q_OPENGL_EXPORT QGLContextGroupResourceBase +{ +public: + QGLContextGroupResourceBase(); + virtual ~QGLContextGroupResourceBase(); + void insert(const QGLContext *context, void *value); + void *value(const QGLContext *context); + void cleanup(const QGLContext *context, void *value); + virtual void freeResource(void *value) = 0; + +protected: + QList<QGLContextGroup *> m_groups; + +private: + QAtomicInt active; +}; + +#endif + +template <class T> +class QGLResource : public QGLContextGroupResourceBase +{ +public: + T *value(const QGLContext *context) { + T *resource = reinterpret_cast<T *>(QGLContextGroupResourceBase::value(context)); + if (!resource) { + resource = new T(context); + insert(context, resource); + } + return resource; + } + +protected: + void freeResource(void *resource) { + delete reinterpret_cast<T *>(resource); + } +}; + +#else + +#if !defined(QGL_P_H) + +class Q_OPENGL_EXPORT QGLContextResource +{ +public: + typedef void (*FreeFunc)(void *); + QGLContextResource(FreeFunc f); + ~QGLContextResource(); + void insert(const QGLContext *key, void *value); + void *value(const QGLContext *key); + void cleanup(const QGLContext *ctx, void *value); +private: + FreeFunc free; + QAtomicInt active; +}; + +#endif + +template <class T> +class QGLResource : public QGLContextResource +{ +public: + static void freeResource(void *resource) { + delete reinterpret_cast<T *>(resource); + } + + QGLResource() : QGLContextResource(freeResource) {} + + T *value(const QGLContext *context) { + T *resource = reinterpret_cast<T *>(QGLContextResource::value(context)); + if (!resource) { + resource = new T(context); + insert(context, resource); + } + return resource; + } +}; + +#endif + +#if !defined(QGL_P_H) && !defined(Q_MOC_RUN) + +class Q_OPENGL_EXPORT QGLSignalProxy : public QObject +{ + Q_OBJECT +public: + QGLSignalProxy() : QObject() {} + void emitAboutToDestroyContext(const QGLContext *context) { + emit aboutToDestroyContext(context); + } + static QGLSignalProxy *instance(); +Q_SIGNALS: + void aboutToDestroyContext(const QGLContext *context); +}; + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/painting/qgllightmodel.cpp b/src/threed/painting/qgllightmodel.cpp new file mode 100644 index 000000000..bdcdadb9a --- /dev/null +++ b/src/threed/painting/qgllightmodel.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgllightmodel.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLLightModel + \brief The QGLLightModel class defines the lighting model to use for the scene. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::painting +*/ + +/*! + \qmlclass LightModel QGLLightModel + \brief The LightModel item defines the lighting model to use for the scene. + \since 4.8 + \ingroup qt3d::qml3d + + \sa Light +*/ + +/*! + \enum QGLLightModel::Model + This enum defines the type of lighting model to use: one-sided or two-sided. + + \value OneSided One-sided lighting, with the front face material used for both front and back faces. + \value TwoSided Two-sided lighting, with separate front and back face materials. +*/ + +/*! + \enum QGLLightModel::ColorControl + This enum controls the number of colors to be generated by the lighting computation. + + \value SingleColor A single color is generated by the lighting computation. + \value SeparateSpecularColor A separate specular color computation is + performed and then summed into the pixel color after texture mapping. +*/ + +/*! + \enum QGLLightModel::ViewerPosition + This enum defines the position of the viewer for the purposes of lighting calculations. + + \value ViewerAtInfinity The viewer is at infinity along the -z axis. + \value LocalViewer The viewer is at the local origin in eye coordinates. +*/ + +class QGLLightModelPrivate +{ +public: + QGLLightModelPrivate() + : model(QGLLightModel::OneSided), + colorControl(QGLLightModel::SingleColor), + viewerPosition(QGLLightModel::ViewerAtInfinity) + { + ambientSceneColor.setRgbF(0.2, 0.2, 0.2, 1.0); + } + + QGLLightModel::Model model; + QGLLightModel::ColorControl colorControl; + QGLLightModel::ViewerPosition viewerPosition; + QColor ambientSceneColor; +}; + +/*! + Constructs a light model object with default values and attach + it to \a parent. +*/ +QGLLightModel::QGLLightModel(QObject *parent) + : QObject(parent) + , d_ptr(new QGLLightModelPrivate) +{ +} + +/*! + Destroys this light model. +*/ +QGLLightModel::~QGLLightModel() +{ +} + +/*! + \property QGLLightModel::model + \brief the lighting model to use, either OneSided or TwoSided. + The default is OneSided. + + \sa modelChanged() +*/ + +/*! + \qmlproperty enumeration LightModel::model + The lighting model to use, either OneSided or TwoSided. + The default is OneSided. +*/ + +QGLLightModel::Model QGLLightModel::model() const +{ + Q_D(const QGLLightModel); + return d->model; +} + +void QGLLightModel::setModel(QGLLightModel::Model value) +{ + Q_D(QGLLightModel); + if (d->model != value) { + d->model = value; + emit modelChanged(); + emit lightModelChanged(); + } +} + +/*! + \property QGLLightModel::colorControl + \brief the color control mode, either SingleColor or + SeparateSpecularColor. The default value is SingleColor. + + If SingleColor is specified, then a single color is calculated + by the lighting computation for a vertex. If SeparateSpecularColor + is specified, then a separate specular color computation is + performed and then summed into the pixel color after texture mapping. + + \sa colorControlChanged() +*/ + +/*! + \qmlproperty enumeration LightModel::colorControl + The color control mode, either SingleColor or + SeparateSpecularColor. The default value is SingleColor. + + If SingleColor is specified, then a single color is calculated + by the lighting computation for a vertex. If SeparateSpecularColor + is specified, then a separate specular color computation is + performed and then summed into the pixel color after texture mapping. +*/ + +QGLLightModel::ColorControl QGLLightModel::colorControl() const +{ + Q_D(const QGLLightModel); + return d->colorControl; +} + +void QGLLightModel::setColorControl(QGLLightModel::ColorControl value) +{ + Q_D(QGLLightModel); + if (d->colorControl != value) { + d->colorControl = value; + emit colorControlChanged(); + emit lightModelChanged(); + } +} + +/*! + \property QGLLightModel::viewerPosition + \brief the viewer position, either ViewerAtInfinity or LocalViewer. + The default value is ViewerAtInfinity. + + \sa viewerPositionChanged() +*/ + +/*! + \qmlproperty enumeration LightModel::viewerPosition + The viewer position, either ViewerAtInfinity or LocalViewer. + The default value is ViewerAtInfinity. +*/ + +QGLLightModel::ViewerPosition QGLLightModel::viewerPosition() const +{ + Q_D(const QGLLightModel); + return d->viewerPosition; +} + +void QGLLightModel::setViewerPosition(QGLLightModel::ViewerPosition value) +{ + Q_D(QGLLightModel); + if (d->viewerPosition != value) { + d->viewerPosition = value; + emit viewerPositionChanged(); + emit lightModelChanged(); + } +} + +/*! + \property QGLLightModel::ambientSceneColor + \brief the ambient color of the entire scene. The default value + is (0.2, 0.2, 0.2, 1.0). + + \sa ambientSceneColorChanged() +*/ + +/*! + \qmlproperty color LightModel::ambientSceneColor + The ambient color of the entire scene. The default value + is (0.2, 0.2, 0.2, 1.0). +*/ + +QColor QGLLightModel::ambientSceneColor() const +{ + Q_D(const QGLLightModel); + return d->ambientSceneColor; +} + +void QGLLightModel::setAmbientSceneColor(const QColor& value) +{ + Q_D(QGLLightModel); + if (d->ambientSceneColor != value) { + d->ambientSceneColor = value; + emit ambientSceneColorChanged(); + emit lightModelChanged(); + } +} + +/*! + \fn void QGLLightModel::modelChanged() + + This signal is emitted when model() changes. + + \sa model(), lightModelChanged() +*/ + +/*! + \fn void QGLLightModel::colorControlChanged() + + This signal is emitted when colorControl() changes. + + \sa colorControl(), lightModelChanged() +*/ + +/*! + \fn void QGLLightModel::viewerPositionChanged() + + This signal is emitted when viewerPosition() changes. + + \sa viewerPosition(), lightModelChanged() +*/ + +/*! + \fn void QGLLightModel::ambientSceneColorChanged() + + This signal is emitted when ambientSceneColor() changes. + + \sa ambientSceneColor(), lightModelChanged() +*/ + +/*! + \fn void QGLLightModel::lightModelChanged() + + This signal is emitted when one of model(), colorControl(), + viewerPosition(), or ambientSceneColor() changes. +*/ + +QT_END_NAMESPACE diff --git a/src/threed/painting/qgllightmodel.h b/src/threed/painting/qgllightmodel.h new file mode 100644 index 000000000..2ff307221 --- /dev/null +++ b/src/threed/painting/qgllightmodel.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLLIGHTMODEL_H +#define QGLLIGHTMODEL_H + +#include "qt3dglobal.h" +#include <QtCore/qobject.h> +#include <QtGui/qcolor.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLLightModelPrivate; + +class Q_QT3D_EXPORT QGLLightModel : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGLLightModel) + Q_ENUMS(Model) + Q_ENUMS(ColorControl) + Q_ENUMS(ViewerPosition) + Q_PROPERTY(Model model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(ColorControl colorControl READ colorControl WRITE setColorControl NOTIFY colorControlChanged) + Q_PROPERTY(ViewerPosition viewerPosition READ viewerPosition WRITE setViewerPosition NOTIFY viewerPositionChanged) + Q_PROPERTY(QColor ambientSceneColor READ ambientSceneColor WRITE setAmbientSceneColor NOTIFY ambientSceneColorChanged) +public: + explicit QGLLightModel(QObject *parent = 0); + ~QGLLightModel(); + + enum Model + { + OneSided, + TwoSided + }; + + enum ColorControl + { + SingleColor, + SeparateSpecularColor + }; + + enum ViewerPosition + { + ViewerAtInfinity, + LocalViewer + }; + + QGLLightModel::Model model() const; + void setModel(QGLLightModel::Model value); + + QGLLightModel::ColorControl colorControl() const; + void setColorControl(QGLLightModel::ColorControl value); + + QGLLightModel::ViewerPosition viewerPosition() const; + void setViewerPosition(QGLLightModel::ViewerPosition value); + + QColor ambientSceneColor() const; + void setAmbientSceneColor(const QColor& value); + +Q_SIGNALS: + void modelChanged(); + void colorControlChanged(); + void viewerPositionChanged(); + void ambientSceneColorChanged(); + void lightModelChanged(); + +private: + Q_DISABLE_COPY(QGLLightModel) + + QScopedPointer<QGLLightModelPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/painting/qgllightparameters.cpp b/src/threed/painting/qgllightparameters.cpp new file mode 100644 index 000000000..27530d31d --- /dev/null +++ b/src/threed/painting/qgllightparameters.cpp @@ -0,0 +1,828 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgllightparameters.h" +#include <QtCore/qmath.h> +#include <QtGui/qmatrix4x4.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLLightParameters + \brief The QGLLightParameters class represents the parameters of a light in a 3D scene. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::painting + + The functions in this class are a convenience wrapper for the OpenGL enumerations + which control each light source in a 3D scene. For the general ambient light in a + scene, not from a source refer to QGLLightModel. + + The ambient, diffuse and specular components of the light can be controlled for by + colour, and are set by the following functions: + \list + \o setAmbientColor() + \o setDiffuseColor() + \o setSpecularColor() + \endlist + Other than changing intensity by using darker color values, see below for how to + change the intensity of the light with distance from the lit object. + + Light sources are of two types, directional and positional, described by the + enumeration QGLLightParameters::LightType. By default a light source is directional. + + A directional light source is infinitely distant, such that its rays are all + parallel, to a direction \bold vector. This vector is set by the setDirection() + function. If the light source is not directional when setDirection() is called, its + type is changed to Directional. Directional light sources model real world light + sources like the sun. Such light sources are not located at any particular point + and affect the scene evenly. + + In the OpenGL model-view, a directional light is represented by a 4d vector, with + the 4th value, \c w, set to 0.0f. This means that the spatial data of the light + is not changed during certain transformations, for example translation. + See \l {3D Math Basis} for a fuller explanation of this. + + Calling the setPosition() function defines the light source to be located at a given + \bold point, a finite distance from the lit object. If the light source is not + positional when this function is called its type is changed to Positional. + + A positional light source models a real world lamp, such as a light-bulb or + car headlight. Since it is a finite distance from the lit object the rays diverge + and lighting calculations are thus more complex. + + In OpenGL the light has its spatial data \c w value set to 1.0f, resulting in the + lights position being changed along with other objects in the scene by transformations + such as translation. See \l {3D Math Basis} for more. + + Positional lights are by default point sources, like the light-bulb where light + radiates in all directions. Calling the spotAngle() function sets an angle to + restrict the light from a positional source to a cone like the car headlight. This + makes the light behave like a spotlight, where objects outside of the cone of the + light do not receive any light from that source. + + The spotlight may be further specified by its spotExponent() which dictates the + behaviour of the cutoff at the lighting cone; and by the attenuation functions + which are the terms in the following equation: + \image attenuation.png + here \c d is the distance of the light from the lit object, and A is the attenuation + factor which is a multiplier of the light source, determining its intensity at the + lit object. The terms in the denominator are: + \list + \o constantAttenuation() - default 1.0 + \raw HTML + k<sub>c</sub> + \endraw + \o linearAttenuation() - default 0.0 + \raw HTML + k<sub>l</sub> + \endraw + \o quadraticAttenuation() - default 0.0 + \raw HTML + k<sub>q</sub> + \endraw + \endlist + When these terms are large the light contributed by the source becomes much weaker + with distance from the object. +*/ + +/*! + \qmlclass Light QGLLightParameters + \brief The Light item represents the parameters of a light in a 3D scene. + \since 4.8 + \ingroup qt3d::qml3d + + \sa LightModel +*/ + +class QGLLightParametersPrivate +{ +public: + QGLLightParametersPrivate() : + type(QGLLightParameters::Directional), + position(0.0f, 0.0f, 1.0f), + ambientColor(0, 0, 0, 255), + diffuseColor(255, 255, 255, 255), + specularColor(255, 255, 255, 255), + spotDirection(0.0f, 0.0f, -1.0f), + spotExponent(0.0f), + spotAngle(180.0f), + spotCosAngle(-1.0f), + constantAttenuation(1.0f), + linearAttenuation(0.0f), + quadraticAttenuation(0.0f) + { + } + + QGLLightParameters::LightType type; + QVector3D position; + QColor ambientColor; + QColor diffuseColor; + QColor specularColor; + QVector3D spotDirection; + qreal spotExponent; + qreal spotAngle; + qreal spotCosAngle; + qreal constantAttenuation; + qreal linearAttenuation; + qreal quadraticAttenuation; +}; + + +/*! + \enum QGLLightParameters::LightType + This enum defines the possible types of light. + + \value Directional The default. Directional lights are infinitely + distant from the lit object, and its rays are parallel to a + direction vector. Use setDirection() to make a light Directional. + \value Positional Positional lights are a finite distance from the + lit object. Complex lighting with spot-lights, and attenuation + are enabled with Positional lighting. Use setPosition() to + make a light Positional. +*/ + +/*! + Constructs a new light parameters object with default values + and attaches it to \a parent. +*/ +QGLLightParameters::QGLLightParameters(QObject *parent) + : QObject(parent) + , d_ptr(new QGLLightParametersPrivate) +{ +} + +/*! + Destroys this light parameters object. +*/ +QGLLightParameters::~QGLLightParameters() +{ +} + +/*! + \property QGLLightParameters::type + \brief the type of this light; Positional or Directional. + + \sa position(), direction() +*/ + +/*! + \qmlproperty enumeration Light::type + The type of this light; Positional or Directional. The default + is Directional. + + \sa position, direction +*/ + +QGLLightParameters::LightType QGLLightParameters::type() const +{ + Q_D(const QGLLightParameters); + return d->type; +} + +/*! + \property QGLLightParameters::position + \brief the position of this light if it is Positional; or a null + QVector3D if it is Directional. By default, the light is Directional. + + For a Positional light, the return value is the location in model space + of the light, at some finite distance from the origin. The value is + transformed by the model-view transformation when the light + parameters are applied. + + When the light is Positional OpenGL will obey other parameters relating + to the light's position, such as attenuation and spot angle. + + Setting the position converts the light from Directional to Positional. + + \sa direction(), type(), positionChanged() +*/ + +/*! + \qmlproperty vector3D Light::position + The position of this light if it is Positional; or a zero vector + if it is Directional. By default, the light is Directional. + + For a Positional light, the return value is the location in model space + of the light, at some finite distance from the origin. The value is + transformed by the model-view transformation when the light + parameters are applied. + + When the light is Positional OpenGL will obey other parameters relating + to the light's position, such as attenuation and spot angle. + + Setting the position converts the light from Directional to Positional. + + \sa direction, type +*/ + +QVector3D QGLLightParameters::position() const +{ + Q_D(const QGLLightParameters); + if (d->type == Positional) + return d->position; + else + return QVector3D(); +} + +void QGLLightParameters::setPosition(const QVector3D& point) +{ + Q_D(QGLLightParameters); + if (d->type == Positional) { + if (d->position != point) { + // Only the position() has changed. + d->position = point; + emit positionChanged(); + emit lightChanged(); + } + } else { + // Both the position() and direction() are changed. + d->type = Positional; + d->position = point; + emit positionChanged(); + emit directionChanged(); + emit lightChanged(); + } +} + +/*! + \property QGLLightParameters::direction + \brief the direction of this light if it is Directional; or a null + QVector3D if it is Positional. By default, the light is Directional. + + For a Directional light, the return value is the direction vector of + an infinitely distant directional light, like the sun. + + The default value is (0, 0, 1), for a directional light pointing along + the positive z-axis. + + OpenGL will ignore other parameters such as attenuation and spot angle + when this value is set, since a directional light has no location. + In order to set the direction of a spot light, use the setSpotDirection() + function. + + Setting the direction converts the light from Positional to Directional. + + \sa position(), type(), directionChanged() +*/ + +/*! + \qmlproperty vector3D Light::direction + The direction of this light if it is Directional; or a zero vector + if it is Positional. By default, the light is Directional. + + For a Directional light, the return value is the direction vector of + an infinitely distant directional light, like the sun. + + The default value is (0, 0, 1), for a directional light pointing along + the positive z-axis. + + OpenGL will ignore other parameters such as attenuation and spot angle + when this value is set, since a directional light has no location. + In order to set the direction of a spot light, use the setSpotDirection() + function. + + Setting the direction converts the light from Positional to Directional. + + \sa position, type +*/ + +QVector3D QGLLightParameters::direction() const +{ + Q_D(const QGLLightParameters); + if (d->type == Directional) + return d->position; + else + return QVector3D(); +} + +void QGLLightParameters::setDirection(const QVector3D& value) +{ + Q_D(QGLLightParameters); + if (d->type == Directional) { + if (d->position != value) { + // Only the direction() has changed. + d->position = value; + emit directionChanged(); + emit lightChanged(); + } + } else { + // Both the position() and direction() are changed. + d->type = Directional; + d->position = value; + emit positionChanged(); + emit directionChanged(); + emit lightChanged(); + } +} + +/*! + \property QGLLightParameters::ambientColor + \brief the ambient color of this light. The default value is black. + + \sa diffuseColor(), specularColor(), ambientColorChanged() +*/ + +/*! + \qmlproperty color Light::ambientColor + The ambient color of this light. The default value is black. + + \sa diffuseColor, specularColor +*/ + +QColor QGLLightParameters::ambientColor() const +{ + Q_D(const QGLLightParameters); + return d->ambientColor; +} + +void QGLLightParameters::setAmbientColor(const QColor& value) +{ + Q_D(QGLLightParameters); + if (d->ambientColor != value) { + d->ambientColor = value; + emit ambientColorChanged(); + emit lightChanged(); + } +} + +/*! + \property QGLLightParameters::diffuseColor + \brief the diffuse color of this light. The default value is white. + + \sa ambientColor(), specularColor(), diffuseColorChanged() +*/ + +/*! + \qmlproperty color Light::diffuseColor + The diffuse color of this light. The default value is white. + + \sa ambientColor, specularColor +*/ +QColor QGLLightParameters::diffuseColor() const +{ + Q_D(const QGLLightParameters); + return d->diffuseColor; +} + +void QGLLightParameters::setDiffuseColor(const QColor& value) +{ + Q_D(QGLLightParameters); + if (d->diffuseColor != value) { + d->diffuseColor = value; + emit diffuseColorChanged(); + emit lightChanged(); + } +} + +/*! + \property QGLLightParameters::specularColor + \brief the specular color of this light. The default value is white. + + \sa ambientColor(), diffuseColor(), specularColorChanged() +*/ + +/*! + \qmlproperty color Light::specularColor + The specular color of this light. The default value is white. + + \sa ambientColor, diffuseColor +*/ + +QColor QGLLightParameters::specularColor() const +{ + Q_D(const QGLLightParameters); + return d->specularColor; +} + +void QGLLightParameters::setSpecularColor(const QColor& value) +{ + Q_D(QGLLightParameters); + if (d->specularColor != value) { + d->specularColor = value; + emit specularColorChanged(); + emit lightChanged(); + } +} + +/*! + \property QGLLightParameters::spotDirection + \brief the direction that a spot-light is shining in. + + A spotlight is a positional light that has a value other than the default + for spot angle. The default value is (0, 0, -1). + + This parameter has no effect on a Directional light. + + \sa spotExponent(), spotDirectionChanged() +*/ + +/*! + \qmlproperty vector3D Light::spotDirection + The direction that a spot-light is shining in. + + A spotlight is a positional light that has a value other than the default + for spot angle. The default value is (0, 0, -1). + + This parameter has no effect on a Directional light. + + \sa spotExponent +*/ +QVector3D QGLLightParameters::spotDirection() const +{ + Q_D(const QGLLightParameters); + return d->spotDirection; +} + +void QGLLightParameters::setSpotDirection(const QVector3D& vector) +{ + Q_D(QGLLightParameters); + if (d->spotDirection != vector) { + d->spotDirection = vector; + emit spotDirectionChanged(); + emit lightChanged(); + } +} + +/*! + \property QGLLightParameters::spotExponent + \brief the exponent value between 0 and 128 that indicates the + intensity distribution of a spot-light. The default value is 0, + indicating uniform light distribution. + + This parameter has no effect on a Directional light. + + \sa spotAngle(), setPosition(), spotExponentChanged() +*/ + +/*! + \qmlproperty real Light::spotExponent + The exponent value between 0 and 128 that indicates the + intensity distribution of a spot-light. The default value is 0, + indicating uniform light distribution. + + This parameter has no effect on a Directional light. + + \sa spotAngle, position +*/ + +qreal QGLLightParameters::spotExponent() const +{ + Q_D(const QGLLightParameters); + return d->spotExponent; +} + +void QGLLightParameters::setSpotExponent(qreal value) +{ + Q_D(QGLLightParameters); + if (d->spotExponent != value) { + d->spotExponent = value; + emit spotExponentChanged(); + emit lightChanged(); + } +} + +/*! + \property QGLLightParameters::spotAngle + \brief the angle over which light is spread from this light. + It should be between 0 and 90 for spot lights, and 180 for + uniform light distribution from a remote light source. + The default value is 180. + + This parameter has no effect on a Directional light. + + \sa spotCosAngle(), spotAngleChanged() +*/ + +/*! + \qmlproperty real Light::spotAngle + The angle over which light is spread from this light. + It should be between 0 and 90 for spot lights, and 180 for + uniform light distribution from a remote light source. + The default value is 180. + + This parameter has no effect on a Directional light. +*/ + +qreal QGLLightParameters::spotAngle() const +{ + Q_D(const QGLLightParameters); + return d->spotAngle; +} + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +void QGLLightParameters::setSpotAngle(qreal value) +{ + Q_D(QGLLightParameters); + if (d->spotAngle != value) { + d->spotAngle = value; + if (value != 180.0f) + d->spotCosAngle = qCos(value * M_PI / 180.0f); + else + d->spotCosAngle = -1.0f; + emit spotAngleChanged(); + emit lightChanged(); + } +} + +/*! + Returns the cosine of spotAngle(), or -1 if spotAngle() is 180. + + The cosine of spotAngle() is useful in lighting algorithms. + This function returns a cached copy of the cosine so that it + does not need to be computed every time the lighting parameters + are read. + + \sa spotAngle() +*/ +qreal QGLLightParameters::spotCosAngle() const +{ + Q_D(const QGLLightParameters); + return d->spotCosAngle; +} + +/*! + \property QGLLightParameters::constantAttenuation + \brief the constant light attenuation factor. The default value is 1. + + This parameter has no effect on a Directional light. + + \sa linearAttenuation(), quadraticAttenuation() + \sa constantAttenuationChanged() +*/ + +/*! + \qmlproperty real Light::constantAttenuation + The constant light attenuation factor. The default value is 1. + + This parameter has no effect on a Directional light. + + \sa linearAttenuation, quadraticAttenuation +*/ + +qreal QGLLightParameters::constantAttenuation() const +{ + Q_D(const QGLLightParameters); + return d->constantAttenuation; +} + +void QGLLightParameters::setConstantAttenuation(qreal value) +{ + Q_D(QGLLightParameters); + if (d->constantAttenuation != value) { + d->constantAttenuation = value; + emit constantAttenuationChanged(); + emit lightChanged(); + } +} + +/*! + \property QGLLightParameters::linearAttenuation + \brief the linear light attenuation factor. The default value is 0. + + This parameter has no effect on a Directional light. + + \sa constantAttenuation(), quadraticAttenuation() + \sa linearAttenuationChanged() +*/ + +/*! + \qmlproperty real Light::linearAttenuation + The linear light attenuation factor. The default value is 0. + + This parameter has no effect on a Directional light. + + \sa constantAttenuation, quadraticAttenuation +*/ + +qreal QGLLightParameters::linearAttenuation() const +{ + Q_D(const QGLLightParameters); + return d->linearAttenuation; +} + +void QGLLightParameters::setLinearAttenuation(qreal value) +{ + Q_D(QGLLightParameters); + if (d->linearAttenuation != value) { + d->linearAttenuation = value; + emit linearAttenuationChanged(); + emit lightChanged(); + } +} + +/*! + \property QGLLightParameters::quadraticAttenuation + \brief the quadratic light attenuation factor. The default value is 0. + + This parameter has no effect on a Directional light. + + \sa constantAttenuation(), linearAttenuation() + \sa quadraticAttenuationChanged() +*/ + +/*! + \qmlproperty real Light::quadraticAttenuation + The quadratic light attenuation factor. The default value is 0. + + This parameter has no effect on a Directional light. + + \sa constantAttenuation, linearAttenuation +*/ + +qreal QGLLightParameters::quadraticAttenuation() const +{ + Q_D(const QGLLightParameters); + return d->quadraticAttenuation; +} + +void QGLLightParameters::setQuadraticAttenuation(qreal value) +{ + Q_D(QGLLightParameters); + if (d->quadraticAttenuation != value) { + d->quadraticAttenuation = value; + emit quadraticAttenuationChanged(); + emit lightChanged(); + } +} + +/*! + Returns the position of this light after transforming it from + world co-ordinates to eye co-ordinates using \a transform. + + If the light is Directional, then direction() will be transformed, + after setting the w co-ordinate to 0. If the light is Positional, + then position() will be transformed after setting the w co-ordinate + to 1. + + The returned result is suitable to be applied to the GL_POSITION + property of \c{glLight()}, assuming that the modelview transformation + in the GL context is set to the identity. + + \sa eyeSpotDirection() +*/ +QVector4D QGLLightParameters::eyePosition(const QMatrix4x4& transform) const +{ + Q_D(const QGLLightParameters); + if (d->type == Directional) + return transform * QVector4D(d->position, 0.0f); + else + return transform * QVector4D(d->position, 1.0f); +} + +/*! + Returns the spotDirection() for this light after transforming it + from world co-ordinates to eye co-ordinates using the top-left + 3x3 submatrix within \a transform. + + The returned result is suitable to be applied to the GL_SPOT_DIRECTION + property of \c{glLight()}, assuming that the modelview transformation + in the GL context is set to the identity. + + \sa eyePosition() +*/ +QVector3D QGLLightParameters::eyeSpotDirection + (const QMatrix4x4& transform) const +{ + Q_D(const QGLLightParameters); + return transform.mapVector(d->spotDirection); +} + +/*! + \fn void QGLLightParameters::positionChanged() + + This signal is emitted when position() changes, or when the type() + changes between Directional and Positional. + + \sa position(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::directionChanged() + + This signal is emitted when direction() changes, or when the type() + changes between Directional and Positional. + + \sa direction(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::ambientColorChanged() + + This signal is emitted when ambientColor() changes. + + \sa ambientColor(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::diffuseColorChanged() + + This signal is emitted when diffuseColor() changes. + + \sa diffuseColor(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::specularColorChanged() + + This signal is emitted when specularColor() changes. + + \sa specularColor(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::spotDirectionChanged() + + This signal is emitted when spotDirection() changes. + + \sa spotDirection(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::spotExponentChanged() + + This signal is emitted when spotExponent() changes. + + \sa spotExponent(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::spotAngleChanged() + + This signal is emitted when spotAngle() changes. + + \sa spotAngle(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::constantAttenuationChanged() + + This signal is emitted when constantAttenuation() changes. + + \sa constantAttenuation(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::linearAttenuationChanged() + + This signal is emitted when linearAttenuation() changes. + + \sa linearAttenuation(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::quadraticAttenuationChanged() + + This signal is emitted when quadraticAttenuation() changes. + + \sa quadraticAttenuation(), lightChanged() +*/ + +/*! + \fn void QGLLightParameters::lightChanged() + + This signal is emitted when any of the properties on the light changes. +*/ + +QT_END_NAMESPACE diff --git a/src/threed/painting/qgllightparameters.h b/src/threed/painting/qgllightparameters.h new file mode 100644 index 000000000..7f4a29691 --- /dev/null +++ b/src/threed/painting/qgllightparameters.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLLIGHTPARAMETERS_H +#define QGLLIGHTPARAMETERS_H + +#include "qt3dglobal.h" +#include <QtCore/qobject.h> +#include <QtGui/qcolor.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLLightParametersPrivate; +class QMatrix4x4; + +class Q_QT3D_EXPORT QGLLightParameters : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGLLightParameters) + Q_ENUMS(LightType) + Q_PROPERTY(LightType type READ type) + Q_PROPERTY(QVector3D position READ position WRITE setPosition NOTIFY positionChanged) + Q_PROPERTY(QVector3D direction READ direction WRITE setDirection NOTIFY directionChanged) + Q_PROPERTY(QColor ambientColor READ ambientColor WRITE setAmbientColor NOTIFY ambientColorChanged) + Q_PROPERTY(QColor diffuseColor READ diffuseColor WRITE setDiffuseColor NOTIFY diffuseColorChanged) + Q_PROPERTY(QColor specularColor READ specularColor WRITE setSpecularColor NOTIFY specularColorChanged) + Q_PROPERTY(QVector3D spotDirection READ spotDirection WRITE setSpotDirection NOTIFY spotDirectionChanged) + Q_PROPERTY(qreal spotExponent READ spotExponent WRITE setSpotExponent NOTIFY spotExponentChanged) + Q_PROPERTY(qreal spotAngle READ spotAngle WRITE setSpotAngle NOTIFY spotAngleChanged) + Q_PROPERTY(qreal constantAttenuation READ constantAttenuation WRITE setConstantAttenuation NOTIFY constantAttenuationChanged) + Q_PROPERTY(qreal linearAttenuation READ linearAttenuation WRITE setLinearAttenuation NOTIFY linearAttenuationChanged) + Q_PROPERTY(qreal quadraticAttenuation READ quadraticAttenuation WRITE setQuadraticAttenuation NOTIFY quadraticAttenuationChanged) +public: + enum LightType { + Directional, + Positional + }; + + QGLLightParameters(QObject *parent = 0); + ~QGLLightParameters(); + + QGLLightParameters::LightType type() const; + + QVector3D position() const; + void setPosition(const QVector3D& value); + + QVector3D direction() const; + void setDirection(const QVector3D& value); + + QColor ambientColor() const; + void setAmbientColor(const QColor& value); + + QColor diffuseColor() const; + void setDiffuseColor(const QColor& value); + + QColor specularColor() const; + void setSpecularColor(const QColor& value); + + QVector3D spotDirection() const; + void setSpotDirection(const QVector3D& value); + + qreal spotExponent() const; + void setSpotExponent(qreal value); + + qreal spotAngle() const; + void setSpotAngle(qreal value); + + qreal spotCosAngle() const; + + qreal constantAttenuation() const; + void setConstantAttenuation(qreal value); + + qreal linearAttenuation() const; + void setLinearAttenuation(qreal value); + + qreal quadraticAttenuation() const; + void setQuadraticAttenuation(qreal value); + + QVector4D eyePosition(const QMatrix4x4& transform) const; + QVector3D eyeSpotDirection(const QMatrix4x4& transform) const; + +Q_SIGNALS: + void positionChanged(); + void directionChanged(); + void ambientColorChanged(); + void diffuseColorChanged(); + void specularColorChanged(); + void spotDirectionChanged(); + void spotExponentChanged(); + void spotAngleChanged(); + void constantAttenuationChanged(); + void linearAttenuationChanged(); + void quadraticAttenuationChanged(); + void lightChanged(); + +private: + Q_DISABLE_COPY(QGLLightParameters) + + QScopedPointer<QGLLightParametersPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/painting/qglpainter.cpp b/src/threed/painting/qglpainter.cpp new file mode 100644 index 000000000..b3f6a2c59 --- /dev/null +++ b/src/threed/painting/qglpainter.cpp @@ -0,0 +1,2305 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglpainter.h" +#include "qglpainter_p.h" +#include "qglabstracteffect.h" +#include "qglext_p.h" +#include <QtOpenGL/qglpixelbuffer.h> +#include <QtOpenGL/qglshaderprogram.h> +#include <QtOpenGL/qglframebufferobject.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpaintengine.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmap.h> +#if !defined(QT_NO_THREAD) +#include <QtCore/qthreadstorage.h> +#endif +#include "qglflatcoloreffect_p.h" +#include "qglflattextureeffect_p.h" +#include "qgllitmaterialeffect_p.h" +#include "qgllittextureeffect_p.h" +#include "qglpickcolors_p.h" +#include "qgltexture2d.h" +#include "qgltexturecube.h" +#include "qgeometrydata.h" +#include "qglvertexbundle_p.h" +#include "qmatrix4x4stack_p.h" +#include "qglwidgetsurface.h" +#include "qglpixelbuffersurface.h" +#include "qglpaintersurface_p.h" + +#undef glActiveTexture + +QT_BEGIN_NAMESPACE + +/*! + \class QGLPainter + \brief The QGLPainter class provides portable API's for rendering into a GL context. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::painting + + TBD - lots of TBD + + All QGLPainter instances on a context share the same context state: + matrices, effects, vertex attributes, etc. For example, calling + ortho() on one QGLPainter instance for a context will alter the + projectionMatrix() as seen by the other QGLPainter instances. +*/ + +/*! + \enum QGLPainter::Update + This enum defines the values that were changed since the last QGLPainter::update(). + + \value UpdateColor The color has been updated. + \value UpdateModelViewMatrix The modelview matrix has been updated. + \value UpdateProjectionMatrix The projection matrix has been updated. + \value UpdateMatrices The combination of UpdateModelViewMatrix and + UpdateProjectionMatrix. + \value UpdateLights The lights have been updated. + \value UpdateMaterials The material parameters have been updated. + \value UpdateViewport The viewport needs to be updated because the + drawing surface has changed. + \value UpdateAll All values have been updated. This is specified + when an effect is activated. +*/ + +#define QGLPAINTER_CHECK_PRIVATE() \ + Q_ASSERT_X(d, "QGLPainter", "begin() has not been called or it failed") + +QGLPainterPrivate::QGLPainterPrivate() + : ref(1), + eye(QGL::NoEye), + lightModel(0), + defaultLightModel(0), + defaultLight(0), + frontMaterial(0), + backMaterial(0), + defaultMaterial(0), + frontColorMaterial(0), + backColorMaterial(0), + viewingCube(QVector3D(-1, -1, -1), QVector3D(1, 1, 1)), + color(255, 255, 255, 255), + updates(QGLPainter::UpdateAll), + pick(0), + boundVertexBuffer(0), + boundIndexBuffer(0), + renderSequencer(0), + isFixedFunction(true) // Updated by QGLPainter::begin() +{ + context = 0; + effect = 0; + userEffect = 0; + standardEffect = QGL::FlatColor; + memset(stdeffects, 0, sizeof(stdeffects)); +} + +QGLPainterPrivate::~QGLPainterPrivate() +{ + delete defaultLightModel; + delete defaultLight; + delete defaultMaterial; + delete frontColorMaterial; + delete backColorMaterial; + for (int effect = 0; effect < QGL_MAX_STD_EFFECTS; ++effect) + delete stdeffects[effect]; + delete pick; + qDeleteAll(cachedPrograms); + delete renderSequencer; +} + +QGLPainterPickPrivate::QGLPainterPickPrivate() +{ + isPicking = false; + objectPickId = -1; + pickColorIndex = -1; + pickColor = 0; + defaultPickEffect = new QGLFlatColorEffect(); +} + +QGLPainterPickPrivate::~QGLPainterPickPrivate() +{ + delete defaultPickEffect; +} + +#if !defined(QT_NO_THREAD) + +// QGLContext's are thread-specific, so QGLPainterPrivateCache should be too. + +typedef QThreadStorage<QGLPainterPrivateCache *> QGLPainterPrivateStorage; +Q_GLOBAL_STATIC(QGLPainterPrivateStorage, painterPrivateStorage) +static QGLPainterPrivateCache *painterPrivateCache() +{ + QGLPainterPrivateCache *cache = painterPrivateStorage()->localData(); + if (!cache) { + cache = new QGLPainterPrivateCache(); + painterPrivateStorage()->setLocalData(cache); + } + return cache; +} + +#else + +Q_GLOBAL_STATIC(QGLPainterPrivateCache, painterPrivateCache) + +#endif + +QGLPainterPrivateCache::QGLPainterPrivateCache() +{ + connect(QGLSignalProxy::instance(), + SIGNAL(aboutToDestroyContext(const QGLContext *)), + this, + SLOT(contextDestroyed(const QGLContext *))); +} + +QGLPainterPrivateCache::~QGLPainterPrivateCache() +{ +} + +QGLPainterPrivate *QGLPainterPrivateCache::fromContext + (const QGLContext *context) +{ + QGLPainterPrivate *priv = cache.value(context, 0); + if (priv) + return priv; + priv = new QGLPainterPrivate(); + priv->context = context; + cache.insert(context, priv); + return priv; +} + +QGLPainterPrivateCache *QGLPainterPrivateCache::instance() +{ + return painterPrivateCache(); +} + +void QGLPainterPrivateCache::contextDestroyed(const QGLContext *context) +{ + QGLPainterPrivate *priv = cache.value(context, 0); + if (priv) { + priv->context = 0; + cache.remove(context); + if (!priv->ref.deref()) + delete priv; + } + emit destroyedContext(context); +} + +/*! + Constructs a new GL painter. Call begin() to attach the + painter to a GL context. + + \sa begin() +*/ +QGLPainter::QGLPainter() + : d_ptr(0) +{ +} + +/*! + Constructs a new GL painter and attaches it to \a context. + It is not necessary to call begin() after construction. + + \sa begin() +*/ +QGLPainter::QGLPainter(const QGLContext *context) + : d_ptr(0) +{ + begin(context); +} + +/*! + Constructs a new GL painter and attaches it to the GL + context associated with \a widget. It is not necessary to + call begin() after construction. + + \sa begin(), isActive() +*/ +QGLPainter::QGLPainter(QGLWidget *widget) + : d_ptr(0) +{ + begin(widget); +} + +/*! + Constructs a new GL painter and attaches it to the GL context associated + with \a painter. It is assumed that \a painter is the currently + active painter and that it is associated with the current GL context. + + If \a painter is not using an OpenGL paint engine, then isActive() + will return false; true otherwise. + + This constructor is typically used when mixing regular Qt painting + operations and GL painting operations on a widget that is being + drawn using the OpenGL graphics system. + + \sa begin(), isActive() +*/ +QGLPainter::QGLPainter(QPainter *painter) + : d_ptr(0) +{ + begin(painter); +} + +/*! + Constructs a new GL painter and attaches it to the GL context associated + with \a surface. + + \sa begin(), isActive() +*/ +QGLPainter::QGLPainter(QGLAbstractSurface *surface) + : d_ptr(0) +{ + begin(surface); +} + +/*! + Destroys this GL painter. +*/ +QGLPainter::~QGLPainter() +{ + end(); +} + +/*! + Begins painting on QGLContext::currentContext(). Returns false + if there is no GL context current. + + \sa end() +*/ +bool QGLPainter::begin() +{ + return begin(QGLContext::currentContext()); +} + +/*! + Begins painting on \a context. If painting was already in progress, + then this function will call end() first. The \a context will be + made current if it is not already current. + + Returns true if painting can begin; false otherwise. + + All QGLPainter instances on a context share the same context state: + matrices, the effect(), vertex attributes, etc. For example, + calling ortho() on one QGLPainter instance for a context will + alter the projectionMatrix() as seen by the other QGLPainter instances. + + \sa end(), isActive() +*/ +bool QGLPainter::begin(const QGLContext *context) +{ + if (!context) + return false; + end(); + return begin(context, QGLAbstractSurface::createSurfaceForContext(context)); +} + +/*! + \internal +*/ +bool QGLPainter::begin + (const QGLContext *context, QGLAbstractSurface *surface, + bool destroySurface) +{ + // If we don't have a context specified, then use the one + // that the surface just made current. + if (!context) + context = QGLContext::currentContext(); + + // Find the QGLPainterPrivate for the context, or create a new one. + d_ptr = painterPrivateCache()->fromContext(context); + d_ptr->ref.ref(); + if (d_ptr->renderSequencer) + { + d_ptr->renderSequencer->reset(); + d_ptr->renderSequencer->setPainter(this); + } + + // Activate the main surface for the context. + QGLAbstractSurface *prevSurface; + if (d_ptr->surfaceStack.isEmpty()) { + prevSurface = 0; + } else { + // We are starting a nested begin()/end() scope, so switch + // to the new main surface rather than activate from scratch. + prevSurface = d_ptr->surfaceStack.last().surface; + prevSurface->deactivate(surface); + } + if (!surface->activate(prevSurface)) { + if (prevSurface) + prevSurface->activate(surface); + if (destroySurface) + delete surface; + if (!d_ptr->ref.deref()) + delete d_ptr; + d_ptr = 0; + return false; + } + + // Push a main surface descriptor onto the surface stack. + QGLPainterSurfaceInfo psurf; + psurf.surface = surface; + psurf.destroySurface = destroySurface; + psurf.mainSurface = true; + d_ptr->surfaceStack.append(psurf); + + // Force the matrices to be updated the first time we use them. + d_ptr->modelViewMatrix.setDirty(true); + d_ptr->projectionMatrix.setDirty(true); + + // Initialize the QOpenGLFunctions parent class. + initializeGLFunctions(context); + + // Determine if the OpenGL implementation is fixed-function or not. + d_ptr->isFixedFunction = !hasOpenGLFeature(QOpenGLFunctions::Shaders); + return true; +} + +/*! + Begins painting on the GL context associated with \a widget. + Returns false if \a widget is null. + + \sa end() +*/ +bool QGLPainter::begin(QGLWidget *widget) +{ + if (!widget) + return false; + end(); + return begin(widget->context(), new QGLWidgetSurface(widget)); +} + +/*! + Begins painting on the GL context associated with \a painter. + Returns false if \a painter is not using an OpenGL paint engine. + It is assumed that \a painter is the currently active painter + and that it is associated with the current GL context. + + This function is typically used when mixing regular Qt painting + operations and GL painting operations on a widget that is being + drawn using the OpenGL graphics system. + + \sa end() +*/ +bool QGLPainter::begin(QPainter *painter) +{ + // Validate that the painting is OpenGL-based. + if (!painter) + return false; + QPaintEngine *engine = painter->paintEngine(); + if (!engine) + return false; + if (engine->type() != QPaintEngine::OpenGL && + engine->type() != QPaintEngine::OpenGL2) + return false; + + // Begin GL painting operations. + return begin(0, new QGLPainterSurface(painter)); +} + +/*! + Begins painting to \a surface. Returns false if \a surface is + null or could not be activated. + + \sa end(), QGLAbstractSurface::activate() +*/ +bool QGLPainter::begin(QGLAbstractSurface *surface) +{ + if (!surface) + return false; + end(); + return begin(0, surface, false); +} + +/*! + Ends GL painting. Returns true if painting was ended successfully; + false if this painter was not bound to a GL context. + + The GL context that was bound to this painter will not have + QGLContext::doneCurrent() called on it. It is the responsibility + of the caller to terminate context operations. + + The effect() will be left active in the GL context and will be + assumed to still be active the next time begin() is called. + If this assumption doesn't apply, then call disableEffect() + to disable the effect before calling end(). + + This function will pop all surfaces from the surface stack, + and return currentSurface() to null (the default drawing surface). + + \sa begin(), isActive(), disableEffect() +*/ +bool QGLPainter::end() +{ + Q_D(QGLPainter); + if (!d) + return false; + + // Unbind the current vertex and index buffers. + if (d->boundVertexBuffer) { + QGLBuffer::release(QGLBuffer::VertexBuffer); + d->boundVertexBuffer = 0; + } + if (d->boundIndexBuffer) { + QGLBuffer::release(QGLBuffer::IndexBuffer); + d->boundIndexBuffer = 0; + } + + // Pop surfaces from the surface stack until we reach a + // main surface. Then deactivate the main surface. + int size = d->surfaceStack.size(); + while (size > 0) { + --size; + QGLPainterSurfaceInfo &surf = d->surfaceStack[size]; + if (surf.mainSurface) { + if (size > 0) { + // There are still other surfaces on the stack, probably + // because we are within a nested begin()/end() scope. + // Re-activate the next surface down in the outer scope. + QGLPainterSurfaceInfo &nextSurf = d->surfaceStack[size - 1]; + surf.surface->switchTo(nextSurf.surface); + } else { + // Last surface on the stack, so deactivate it permanently. + surf.surface->deactivate(); + } + if (surf.destroySurface) + delete surf.surface; + break; + } else if (size > 0) { + surf.surface->deactivate(d->surfaceStack[size - 1].surface); + } + } + d->surfaceStack.resize(size); + + // Force a viewport update if we are within a nested begin()/end(). + d->updates |= UpdateViewport; + + // Destroy the QGLPainterPrivate if this is the last reference. + if (!d->ref.deref()) + delete d; + d_ptr = 0; + return true; +} + +/*! + Returns true if this painter is currently bound to a GL context; + false otherwise. + + \sa begin(), end() +*/ +bool QGLPainter::isActive() const +{ + return (d_ptr != 0 && d_ptr->context != 0); +} + +/*! + Returns the GL context that is bound to this painter, or null + if it is not currently bound. +*/ +const QGLContext *QGLPainter::context() const +{ + if (d_ptr) + return d_ptr->context; + else + return 0; +} + +/*! + Returns true if the underlying OpenGL implementation is OpenGL 1.x + or OpenGL/ES 1.x and only supports fixed-function OpenGL operations. + Returns false if the underlying OpenGL implementation is using + GLSL or GLSL/ES shaders. + + If this function returns false, then the built-in effects will + use shaders and QGLPainter will not update the fixed-function + matrices in the OpenGL context when update() is called. + User-supplied effects will need to use shaders also or update + the fixed-function matrices themselves or call updateFixedFunction(). + + \sa update(), updateFixedFunction() +*/ +bool QGLPainter::isFixedFunction() const +{ +#if defined(QT_OPENGL_ES_2) + return false; +#else + Q_D(const QGLPainter); + if (d) + return d->isFixedFunction; + else + return true; +#endif +} + +/*! + Sets the \a color to use to clear the color buffer when \c{glClear()} + is called. +*/ +void QGLPainter::setClearColor(const QColor& color) +{ + glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); +} + +/*! + Sets the scissor \a rect for the current drawing surface + to use when \c{GL_SCISSOR_TEST} is enabled. If \a rect is empty, + then the scissor will be set to clip away all drawing. + + Note that \a rect is in Qt co-ordinates with the origin + at the top-left of the drawing surface's viewport rectangle. + If the currentSurface() is an instance of QGLSubsurface, + then \a rect will be adjusted relative to the subsurface's position. + + \sa currentSurface(), QGLAbstractSurface::viewportGL() +*/ +void QGLPainter::setScissor(const QRect& rect) +{ + if (!rect.isEmpty()) { + // Adjust the rectangle by the position of the surface viewport. + QGLAbstractSurface *surface = currentSurface(); + QRect viewport = surface->viewportGL(); + QRect r(viewport.x() + rect.x(), + viewport.y() + viewport.height() - (rect.y() + rect.height()), + rect.width(), rect.height()); + if (!r.isEmpty()) + glScissor(r.x(), r.y(), r.width(), r.height()); + else + glScissor(0, 0, 0, 0); + } else { + glScissor(0, 0, 0, 0); + } +} + +/*! + Returns a reference to the projection matrix stack. + + It is recommended that setCamera() be used to set the projection + matrix at the beginning of a scene rendering pass so that the + eye position can be adjusted for stereo. + + \sa modelViewMatrix(), combinedMatrix(), setCamera() +*/ +QMatrix4x4Stack& QGLPainter::projectionMatrix() +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return d->projectionMatrix; +} + +/*! + Returns a reference to the modelview matrix stack. + + \sa projectionMatrix(), combinedMatrix(), normalMatrix(), setCamera() + \sa worldMatrix() +*/ +QMatrix4x4Stack& QGLPainter::modelViewMatrix() +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return d->modelViewMatrix; +} + +/*! + \fn QMatrix4x4 QGLPainter::combinedMatrix() const + + Returns the result of multiplying the projectionMatrix() + and the modelViewMatrix(). This combined matrix value is + useful for setting uniform matrix values on shader programs. + + Calling this function is more efficient than calling + projectionMatrix() and modelViewMatrix() separately and + multiplying the return values. + + \sa projectionMatrix(), modelViewMatrix(), normalMatrix() +*/ +QMatrix4x4 QGLPainter::combinedMatrix() const +{ + const QGLPainterPrivate *d = d_func(); + if (!d) + return QMatrix4x4(); + const QMatrix4x4StackPrivate *proj = d->projectionMatrix.d_func(); + const QMatrix4x4StackPrivate *mv = d->modelViewMatrix.d_func(); + return proj->matrix * mv->matrix; +} + +// Inverting the eye transformation will often result in values like +// 1.5e-15 in the world matrix. Clamp these to zero to make worldMatrix() +// more stable when removing the eye component of the modelViewMatrix(). +static inline qreal qt_gl_stablize_value(qreal value) +{ + return (qAbs(value) >= 0.00001f) ? value : 0.0f; +} +static inline QMatrix4x4 qt_gl_stablize_matrix(const QMatrix4x4 &m) +{ + return QMatrix4x4(qt_gl_stablize_value(m(0, 0)), + qt_gl_stablize_value(m(0, 1)), + qt_gl_stablize_value(m(0, 2)), + qt_gl_stablize_value(m(0, 3)), + qt_gl_stablize_value(m(1, 0)), + qt_gl_stablize_value(m(1, 1)), + qt_gl_stablize_value(m(1, 2)), + qt_gl_stablize_value(m(1, 3)), + qt_gl_stablize_value(m(2, 0)), + qt_gl_stablize_value(m(2, 1)), + qt_gl_stablize_value(m(2, 2)), + qt_gl_stablize_value(m(2, 3)), + qt_gl_stablize_value(m(3, 0)), + qt_gl_stablize_value(m(3, 1)), + qt_gl_stablize_value(m(3, 2)), + qt_gl_stablize_value(m(3, 3))); +} + +/*! + Returns the world matrix, which is the modelViewMatrix() without + the eye transformation that was set in the previous call to + setCamera(). + + In the following example, the \c{world} variable will be set to the + translation and scale component of the modelview transformation, + without the "look at" component from the camera: + + \code + painter.setCamera(camera); + painter.modelViewMatrix().translate(0.0f, 5.0f, 0.0f); + painter.modelViewMatrix().scale(1.5f); + QMatrix4x4 world = painter.worldMatrix(); + \endcode + + Note: the world matrix is determined by multiplying the inverse of + the camera's look at component with the current modelview matrix. + Thus, the result may not be precisely the same as constructing a + matrix from translate and scale operations starting with the identity. + + \sa modelViewMatrix(), setCamera() +*/ +QMatrix4x4 QGLPainter::worldMatrix() const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return qt_gl_stablize_matrix + (d->inverseEyeMatrix * d->modelViewMatrix.top()); +} + +/*! + \fn QMatrix3x3 QGLPainter::normalMatrix() const + + Returns the normal matrix corresponding to modelViewMatrix(). + + The normal matrix is the transpose of the inverse of the top-left + 3x3 part of the 4x4 modelview matrix. If the 3x3 sub-matrix is not + invertible, this function returns the identity. + + \sa modelViewMatrix(), combinedMatrix() +*/ +QMatrix3x3 QGLPainter::normalMatrix() const +{ + const QGLPainterPrivate *d = d_func(); + if (!d) + return QMatrix3x3(); + const QMatrix4x4StackPrivate *mv = d->modelViewMatrix.d_func(); + return mv->matrix.normalMatrix(); +} + +/*! + Returns the camera eye that is currently being used for stereo + rendering. The default is QGL::NoEye. + + The eye is used to adjust the camera position by a small amount + when setCamera() is called. + + \sa setEye(), setCamera() +*/ +QGL::Eye QGLPainter::eye() const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return d->eye; +} + +/*! + Sets the camera \a eye that is currently being used for stereo + rendering. + + The \a eye is used to adjust the camera position by a small amount + when setCamera() is called. + + \sa eye(), setCamera() +*/ +void QGLPainter::setEye(QGL::Eye eye) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + d->eye = eye; +} + +/*! + Sets the modelViewMatrix() and projectionMatrix() to the view + defined by \a camera. If eye() is not QGL::NoEye, then the view + will be adjusted for the camera's eye separation. + + This function is typically called at the beginning of a scene rendering + pass to initialize the modelview and projection matrices. + + \sa eye(), modelViewMatrix(), projectionMatrix(), worldMatrix() +*/ +void QGLPainter::setCamera(const QGLCamera *camera) +{ + Q_ASSERT(camera); + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + QMatrix4x4 lookAt = camera->modelViewMatrix(d->eye); + d->modelViewMatrix = lookAt; + d->projectionMatrix = camera->projectionMatrix(aspectRatio()); + d->inverseEyeMatrix = lookAt.inverted(); +} + +/*! + Returns true if \a point is outside the current viewing volume. + This is used to perform object culling checks. +*/ +bool QGLPainter::isCullable(const QVector3D& point) const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + QVector3D projected = d->modelViewMatrix * point; + projected = d->projectionMatrix * projected; + return !d->viewingCube.contains(projected); +} + +/*! + Returns true if \a box is completely outside the current viewing volume. + This is used to perform object culling checks. +*/ +bool QGLPainter::isCullable(const QBox3D& box) const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + QBox3D projected = box.transformed(d->modelViewMatrix); + if (projected.minimum().z() >= 0.0f || projected.maximum().z() >= 0.0f) { + // The box crosses the eye line in the view. Don't do the + // projection or the math will go all strange with a + // perspective projection. Just assume that it is cullable + // if it passes the eye line, and hence is definitely outside + // the viewing volume. Note that it is possible that the box is + // half in front of the eye and half behind, which we handle now + // by truncating the box at the eye plane. + // + // If the projection is orthographic, we don't need to do this. + // Orthographic projections have the last row set to (0, 0, 0, 1). + QMatrix4x4 *proj = &(d->projectionMatrix.d_ptr->matrix); + if ((*proj)(3, 0) != 0.0f || (*proj)(3, 1) != 0.0f || + (*proj)(3, 2) != 0.0f || (*proj)(3, 3) != 1.0f) { + if (projected.minimum().z() >= 0.0f) + return true; + projected.setExtents(projected.minimum(), + QVector3D(projected.maximum().x(), + projected.maximum().y(), 0.0f)); + } + } + projected.transform(d->projectionMatrix); + return !d->viewingCube.intersects(projected); +} + +/*! + Returns the current render order sequencer. + + \sa QGLRenderSequencer +*/ +QGLRenderSequencer *QGLPainter::renderSequencer() +{ + Q_D(QGLPainter); + if (!d->renderSequencer) + d->renderSequencer = new QGLRenderSequencer(this); + return d->renderSequencer; +} + +/*! + Returns the aspect ratio of the viewport for adjusting projection + transformations. +*/ +qreal QGLPainter::aspectRatio() const +{ + return currentSurface()->aspectRatio(); +} + +/*! + Returns the current effect that is in use, which is userEffect() + if it is not null, or the effect object associated with + standardEffect() otherwise. + + If isPicking() is true, then this will return the effect object + that is being used to generate pick colors. + + \sa userEffect(), standardEffect(), isPicking() +*/ +QGLAbstractEffect *QGLPainter::effect() const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + d->ensureEffect(const_cast<QGLPainter *>(this)); + return d->effect; +} + +/*! + Returns the user-defined effect that is being used for drawing + operations, or null if standardEffect() is in use. + + \sa setUserEffect(), standardEffect(), effect() +*/ +QGLAbstractEffect *QGLPainter::userEffect() const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return d->userEffect; +} + +/*! + Sets a user-defined \a effect to use for drawing operations + in the current GL context. If \a effect is null, this will + disable user-defined effects and return to using standardEffect(). + + \sa effect(), draw(), setStandardEffect() +*/ +void QGLPainter::setUserEffect(QGLAbstractEffect *effect) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (d->userEffect == effect) + return; + if (d->effect) + d->effect->setActive(this, false); + d->userEffect = effect; + if (effect && (!d->pick || !d->pick->isPicking)) { + d->effect = effect; + d->effect->setActive(this, true); + d->updates = UpdateAll; + } else { + // Revert to the effect associated with standardEffect(). + d->effect = 0; + d->ensureEffect(this); + } +} + +/*! + Returns the standard effect to use for rendering fragments in + the current GL context when userEffect() is null. + + \sa setStandardEffect(), userEffect() +*/ +QGL::StandardEffect QGLPainter::standardEffect() const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return d->standardEffect; +} + +/*! + Sets a standard \a effect to use for rendering fragments + in the current GL context. This will also set userEffect() + to null. If \a effect is an invalid value, then the behavior + of QGL::FlatColor will be used instead. + + \sa standardEffect(), setUserEffect() +*/ +void QGLPainter::setStandardEffect(QGL::StandardEffect effect) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (d->standardEffect == effect && d->effect && d->userEffect == 0) + return; + if (d->effect) + d->effect->setActive(this, false); + d->standardEffect = effect; + d->userEffect = 0; + d->effect = 0; + d->ensureEffect(this); +} + +/*! + Disables the current effect and sets userEffect() to null. + Unlike setUserEffect() this not activate the standardEffect() + until the next time effect() is called. + + This function can be used to disable all effect-based drawing + operations prior to performing raw GL calls. The next time + effect() is called on this QGLPainter, the standardEffect() + will be reactivated. An effect can also be reactivated by + calling setUserEffect() or setStandardEffect(). + + \sa userEffect(), standardEffect() +*/ +void QGLPainter::disableEffect() +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (d->effect) + d->effect->setActive(this, false); + d->userEffect = 0; + d->effect = 0; +} + +/*! + Returns the cached shader program associated with \a name; or null + if \a name is not currently associated with a shader program. + + \sa setCachedProgram() +*/ +QGLShaderProgram *QGLPainter::cachedProgram(const QString& name) const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return d->cachedPrograms.value(name, 0); +} + +/*! + Sets the cached shader \a program associated with \a name. + + Effect objects can use this function to store pre-compiled + and pre-linked shader programs in the painter for future + use by the same effect. The \a program will be destroyed + when context() is destroyed. + + If \a program is null, then the program associated with \a name + will be destroyed. If \a name is already present as a cached + program, then it will be replaced with \a program. + + Names that start with "\c{qt.}" are reserved for use by Qt's + internal effects. + + \sa cachedProgram() +*/ +void QGLPainter::setCachedProgram + (const QString& name, QGLShaderProgram *program) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + QGLShaderProgram *current = d->cachedPrograms.value(name, 0); + if (current != program) { + if (program) + d->cachedPrograms[name] = program; + else + d->cachedPrograms.remove(name); + delete current; + } +} + +void QGLPainterPrivate::createEffect(QGLPainter *painter) +{ + if (userEffect) { + if (!pick || !pick->isPicking) { + effect = userEffect; + effect->setActive(painter, true); + updates = QGLPainter::UpdateAll; + return; + } + if (userEffect->supportsPicking()) { + effect = userEffect; + effect->setActive(painter, true); + updates = QGLPainter::UpdateAll; + return; + } + effect = pick->defaultPickEffect; + effect->setActive(painter, true); + updates = QGLPainter::UpdateAll; + return; + } + if (uint(standardEffect) >= QGL_MAX_STD_EFFECTS) + effect = stdeffects[int(QGL::FlatColor)]; + else + effect = stdeffects[int(standardEffect)]; + if (!effect) { + switch (standardEffect) { + case QGL::FlatColor: default: + effect = new QGLFlatColorEffect(); + break; + case QGL::FlatPerVertexColor: + effect = new QGLPerVertexColorEffect(); + break; + case QGL::FlatReplaceTexture2D: + effect = new QGLFlatTextureEffect(); + break; + case QGL::FlatDecalTexture2D: + effect = new QGLFlatDecalTextureEffect(); + break; + case QGL::LitMaterial: + effect = new QGLLitMaterialEffect(); + break; + case QGL::LitDecalTexture2D: + effect = new QGLLitDecalTextureEffect(); + break; + case QGL::LitModulateTexture2D: + effect = new QGLLitModulateTextureEffect(); + break; + } + if (uint(standardEffect) >= QGL_MAX_STD_EFFECTS) + stdeffects[int(QGL::FlatColor)] = effect; + else + stdeffects[int(standardEffect)] = effect; + } + if (!pick || !pick->isPicking || effect->supportsPicking()) { + effect->setActive(painter, true); + } else { + effect = pick->defaultPickEffect; + effect->setActive(painter, true); + } + updates = QGLPainter::UpdateAll; +} + +/*! + Returns the last color that was set with setColor(). The default + value is (1, 1, 1, 1). + + \sa setColor() +*/ +QColor QGLPainter::color() const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return d->color; +} + +/*! + Sets the default fragment \a color for effects associated + with this painter. This function does not apply the color + to the effect until update() is called. + + \sa color(), update() +*/ +void QGLPainter::setColor(const QColor& color) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + d->color = color; + d->updates |= UpdateColor; +} + +static void qt_gl_setVertexAttribute(QGL::VertexAttribute attribute, const QGLAttributeValue& value) +{ +#if !defined(QT_OPENGL_ES_2) + switch (attribute) { + case QGL::Position: + glVertexPointer(value.tupleSize(), value.type(), + value.stride(), value.data()); + break; + + case QGL::Normal: + if (value.tupleSize() == 3) + glNormalPointer(value.type(), value.stride(), value.data()); + break; + + case QGL::Color: + glColorPointer(value.tupleSize(), value.type(), + value.stride(), value.data()); + break; + +#ifdef GL_TEXTURE_COORD_ARRAY + case QGL::TextureCoord0: + case QGL::TextureCoord1: + case QGL::TextureCoord2: + { + int unit = (int)(attribute - QGL::TextureCoord0); + qt_gl_ClientActiveTexture(GL_TEXTURE0 + unit); + glTexCoordPointer(value.tupleSize(), value.type(), + value.stride(), value.data()); + if (unit != 0) // Stay on unit 0 between requests. + qt_gl_ClientActiveTexture(GL_TEXTURE0); + } + break; +#endif + + default: break; + } +#else + Q_UNUSED(attribute); + Q_UNUSED(value); +#endif +} + +/*! + Returns the set of vertex attributes that have been set on the + painter state by setVertexAttribute() and setVertexBundle() + since the last call to clearAttributes(). + + The most common use for this function is to determine if specific + attributes have been supplied on the painter so as to adjust the + current drawing effect accordingly. The following example will + use a lit texture effect if texture co-ordinates were provided + in the vertex bundle, or a simple lit material effect if + texture co-ordinates were not provided: + + \code + painter.clearAttributes(); + painter.setVertexBundle(bundle); + if (painter.attributes().contains(QGL::TextureCoord0)) + painter.setStandardEffect(QGL::LitModulateTexture2D); + else + painter.setStandardEffect(QGL::LitMaterial); + \endcode + + It is important to clear the attributes before setting the vertex + bundle, so that attributes from a previous bundle will not leak + through. Multiple vertex bundles may be supplied if they contain + different parts of the same logical piece of geometry. + + \sa clearAttributes(), setVertexBundle() +*/ +QGLAttributeSet QGLPainter::attributes() const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return d->attributeSet; +} + +/*! + Clears the set of vertex attributes that have been set on the + painter state by setVertexAttribute() and setVertexBundle(). + See the documentation for attributes() for more information. + + \sa attributes() +*/ +void QGLPainter::clearAttributes() +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + d->attributeSet.clear(); +} + +/*! + Sets a vertex \a attribute on the current GL context to \a value. + + The vertex attribute is bound to the GL state on the index + corresponding to \a attribute. For example, QGL::Position + will be bound to index 0, QGL::TextureCoord0 will be bound + to index 3, etc. + + Vertex attributes are independent of the effect() and can be + bound once and then used with multiple effects. + + If this is the first attribute in a new piece of geometry, + it is recommended that clearAttributes() be called before this + function. This will inform QGLPainter that a new piece of geometry + is being provided and that the previous geometry is now invalid. + See the documentation for attributes() for more information. + + \sa setVertexBundle(), draw(), clearAttributes(), attributes() +*/ +void QGLPainter::setVertexAttribute + (QGL::VertexAttribute attribute, const QGLAttributeValue& value) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + d->ensureEffect(this); + if (d->boundVertexBuffer) { + QGLBuffer::release(QGLBuffer::VertexBuffer); + d->boundVertexBuffer = 0; + } + if (d->isFixedFunction) { + qt_gl_setVertexAttribute(attribute, value); + } else { + glVertexAttribPointer(GLuint(attribute), value.tupleSize(), + value.type(), GL_TRUE, + value.stride(), value.data()); + } + d->attributeSet.insert(attribute); +} + +/*! + Sets the vertex attributes on the current GL context that are + stored in \a buffer. + + The vertex attributes are bound to the GL state on the indexes + that are specified within \a buffer; QGL::Position will be + bound to index 0, QGL::TextureCoord0 will be bound to index 3, etc. + + Vertex attributes are independent of the effect() and can be + bound once and then used with multiple effects. + + It is recommended that clearAttributes() be called before this + function to inform QGLPainter that a new piece of geometry is + being provided and that the previous geometry is now invalid. + See the documentation for attributes() for more information. + + \sa setVertexAttribute(), draw(), clearAttributes(), attributes() +*/ +void QGLPainter::setVertexBundle(const QGLVertexBundle& buffer) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + d->ensureEffect(this); + QGLVertexBundlePrivate *bd = const_cast<QGLVertexBundlePrivate *>(buffer.d_func()); + if (bd->buffer.isCreated()) { + GLuint id = bd->buffer.bufferId(); + if (id != d->boundVertexBuffer) { + bd->buffer.bind(); + d->boundVertexBuffer = id; + } + } else if (d->boundVertexBuffer) { + QGLBuffer::release(QGLBuffer::VertexBuffer); + d->boundVertexBuffer = 0; + } + for (int index = 0; index < bd->attributes.size(); ++index) { + QGLVertexBundleAttribute *attr = bd->attributes[index]; + if (d->isFixedFunction) { + qt_gl_setVertexAttribute(attr->attribute, attr->value); + } else { + glVertexAttribPointer(GLuint(attr->attribute), + attr->value.tupleSize(), + attr->value.type(), GL_TRUE, + attr->value.stride(), attr->value.data()); + } + } + d->attributeSet.unite(buffer.attributes()); +} + +/*! + Updates the projection matrix, modelview matrix, and lighting + conditions in the currently active effect() object by calling + QGLAbstractEffect::update(). Also updates \c{glViewport()} + to cover the currentSurface() if necessary. + + Normally this function is called automatically by draw(). + However, if the user wishes to use raw GL functions to draw fragments, + it will be necessary to explicitly call this function to ensure that + the matrix state and lighting conditions have been set on the + active effect(). + + Note that this function informs the effect that an update is needed. + It does not change the GL state itself, except for \c{glViewport()}. + In particular, the modelview and projection matrices in the + fixed-function pipeline are not changed unless the effect or + application calls updateFixedFunction(). + + \sa setUserEffect(), projectionMatrix(), modelViewMatrix() + \sa draw(), updateFixedFunction() +*/ +void QGLPainter::update() +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + d->ensureEffect(this); + QGLPainter::Updates updates = d->updates; + d->updates = 0; + if (d->modelViewMatrix.isDirty()) { + updates |= UpdateModelViewMatrix; + d->modelViewMatrix.setDirty(false); + } + if (d->projectionMatrix.isDirty()) { + updates |= UpdateProjectionMatrix; + d->projectionMatrix.setDirty(false); + } + if ((updates & UpdateViewport) != 0) { + QRect viewport = currentSurface()->viewportGL(); + glViewport(viewport.x(), viewport.y(), + viewport.width(), viewport.height()); + } + if (updates != 0) + d->effect->update(this, updates); +} + +#if !defined(QT_OPENGL_ES_2) + +static void setLight(int light, const QGLLightParameters *parameters, + const QMatrix4x4& transform) +{ + GLfloat params[4]; + + QColor color = parameters->ambientColor(); + params[0] = color.redF(); + params[1] = color.greenF(); + params[2] = color.blueF(); + params[3] = color.alphaF(); + glLightfv(light, GL_AMBIENT, params); + + color = parameters->diffuseColor(); + params[0] = color.redF(); + params[1] = color.greenF(); + params[2] = color.blueF(); + params[3] = color.alphaF(); + glLightfv(light, GL_DIFFUSE, params); + + color = parameters->specularColor(); + params[0] = color.redF(); + params[1] = color.greenF(); + params[2] = color.blueF(); + params[3] = color.alphaF(); + glLightfv(light, GL_SPECULAR, params); + + QVector4D vector = parameters->eyePosition(transform); + params[0] = vector.x(); + params[1] = vector.y(); + params[2] = vector.z(); + params[3] = vector.w(); + glLightfv(light, GL_POSITION, params); + + QVector3D spotDirection = parameters->eyeSpotDirection(transform); + params[0] = spotDirection.x(); + params[1] = spotDirection.y(); + params[2] = spotDirection.z(); + glLightfv(light, GL_SPOT_DIRECTION, params); + + params[0] = parameters->spotExponent(); + glLightfv(light, GL_SPOT_EXPONENT, params); + + params[0] = parameters->spotAngle(); + glLightfv(light, GL_SPOT_CUTOFF, params); + + params[0] = parameters->constantAttenuation(); + glLightfv(light, GL_CONSTANT_ATTENUATION, params); + + params[0] = parameters->linearAttenuation(); + glLightfv(light, GL_LINEAR_ATTENUATION, params); + + params[0] = parameters->quadraticAttenuation(); + glLightfv(light, GL_QUADRATIC_ATTENUATION, params); +} + +static void setMaterial(int face, const QGLMaterial *parameters) +{ + GLfloat params[17]; + + QColor mcolor = parameters->ambientColor(); + params[0] = mcolor.redF(); + params[1] = mcolor.greenF(); + params[2] = mcolor.blueF(); + params[3] = mcolor.alphaF(); + + mcolor = parameters->diffuseColor(); + params[4] = mcolor.redF(); + params[5] = mcolor.greenF(); + params[6] = mcolor.blueF(); + params[7] = mcolor.alphaF(); + + mcolor = parameters->specularColor(); + params[8] = mcolor.redF(); + params[9] = mcolor.greenF(); + params[10] = mcolor.blueF(); + params[11] = mcolor.alphaF(); + + mcolor = parameters->emittedLight(); + params[12] = mcolor.redF(); + params[13] = mcolor.greenF(); + params[14] = mcolor.blueF(); + params[15] = mcolor.alphaF(); + + params[16] = parameters->shininess(); + + glMaterialfv(face, GL_AMBIENT, params); + glMaterialfv(face, GL_DIFFUSE, params + 4); + glMaterialfv(face, GL_SPECULAR, params + 8); + glMaterialfv(face, GL_EMISSION, params + 12); + glMaterialfv(face, GL_SHININESS, params + 16); +} + +#endif // !QT_OPENGL_ES_2 + +/*! + Updates the fixed-function pipeline with the current painting + state according to the flags in \a updates. + + This function is intended for use by effects in their + QGLAbstractEffect::update() override if they are using the + fixed-function pipeline. It can also be used by user + applications if they need the QGLPainter state to be + set in the fixed-function pipeline. + + If the OpenGL implementation does not have a fixed-function + pipeline, e.g. OpenGL/ES 2.0, this function does nothing. + + \sa update() +*/ +void QGLPainter::updateFixedFunction(QGLPainter::Updates updates) +{ +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(updates); +#else + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if ((updates & QGLPainter::UpdateColor) != 0) { + QColor color; + if (isPicking()) + color = pickColor(); + else + color = this->color(); + glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF()); + } + if ((updates & QGLPainter::UpdateModelViewMatrix) != 0) { + const QMatrix4x4 &matrix = d->modelViewMatrix.top(); + glMatrixMode(GL_MODELVIEW); + if (sizeof(qreal) == sizeof(GLfloat)) { + glLoadMatrixf(reinterpret_cast<const GLfloat *> + (matrix.constData())); + } else { + GLfloat mat[16]; + const qreal *m = matrix.constData(); + for (int index = 0; index < 16; ++index) + mat[index] = m[index]; + glLoadMatrixf(mat); + } + } + if ((updates & QGLPainter::UpdateProjectionMatrix) != 0) { + const QMatrix4x4 &matrix = d->projectionMatrix.top(); + glMatrixMode(GL_PROJECTION); + if (sizeof(qreal) == sizeof(GLfloat)) { + glLoadMatrixf(reinterpret_cast<const GLfloat *> + (matrix.constData())); + } else { + GLfloat mat[16]; + const qreal *m = matrix.constData(); + for (int index = 0; index < 16; ++index) + mat[index] = m[index]; + glLoadMatrixf(mat); + } + } + if ((updates & QGLPainter::UpdateLights) != 0) { + // Save the current modelview matrix and load the identity. + // We need to apply the light in the modelview transformation + // that was active when the light was specified. + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + // Enable the main light. + const QGLLightParameters *params = mainLight(); + setLight(GL_LIGHT0, params, mainLightTransform()); + + // Restore the previous modelview transformation. + glPopMatrix(); + + // Set up the light model parameters if at least one light is enabled. + const QGLLightModel *lightModel = this->lightModel(); + GLfloat values[4]; +#ifdef GL_LIGHT_MODEL_TWO_SIDE + if (lightModel->model() == QGLLightModel::TwoSided) + values[0] = 1.0f; + else + values[0] = 0.0f; + glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, values); +#endif +#ifdef GL_LIGHT_MODEL_COLOR_CONTROL + if (lightModel->colorControl() == QGLLightModel::SeparateSpecularColor) + values[0] = GL_SEPARATE_SPECULAR_COLOR; + else + values[0] = GL_SINGLE_COLOR; + glLightModelfv(GL_LIGHT_MODEL_COLOR_CONTROL, values); +#endif +#ifdef GL_LIGHT_MODEL_LOCAL_VIEWER + if (lightModel->viewerPosition() == QGLLightModel::LocalViewer) + values[0] = 1.0f; + else + values[0] = 0.0f; + glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, values); +#endif +#ifdef GL_LIGHT_MODEL_AMBIENT + QColor color = lightModel->ambientSceneColor(); + values[0] = color.redF(); + values[1] = color.blueF(); + values[2] = color.greenF(); + values[3] = color.alphaF(); + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, values); +#endif + } + if ((updates & QGLPainter::UpdateMaterials) != 0) { + const QGLMaterial *frontMaterial = faceMaterial(QGL::FrontFaces); + const QGLMaterial *backMaterial = faceMaterial(QGL::BackFaces); + if (frontMaterial == backMaterial) { + setMaterial(GL_FRONT_AND_BACK, frontMaterial); + } else { + setMaterial(GL_FRONT, frontMaterial); + setMaterial(GL_BACK, backMaterial); + } + } +#endif +} + +/*! + Draws primitives using \a count vertices from the arrays specified + by setVertexAttribute(). The type of primitive to draw is specified + by \a mode. + + This operation will consume \a count values from the + enabled arrays, starting at \a index. + + \sa update() +*/ +void QGLPainter::draw(QGL::DrawingMode mode, int count, int index) +{ + update(); + glDrawArrays((GLenum)mode, index, count); +} + +/*! + \overload + + Draws primitives using vertices from the arrays specified by + setVertexAttribute(). The type of primitive to draw is + specified by \a mode. + + This operation will consume \a count elements of \a indices, + which are used to index into the enabled arrays. + + \sa update() +*/ +void QGLPainter::draw(QGL::DrawingMode mode, const ushort *indices, int count) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + update(); + if (d->boundIndexBuffer) { + QGLBuffer::release(QGLBuffer::IndexBuffer); + d->boundIndexBuffer = 0; + } + glDrawElements(GLenum(mode), count, GL_UNSIGNED_SHORT, indices); +} + +/*! + Pushes \a surface onto the surface stack and makes it the current + drawing surface for context(). If \a surface is null, then the + current drawing surface will be set to the main surface (e.g. the window). + + Note: the \a surface object must remain valid until popped from + the stack or end() is called. All surfaces are popped from + the stack by end(). + + The UpdateViewport flag will be set to indicate that the + \c{glViewport()} should be adjusted to the extents of \a surface + when update() is next called. + + \sa popSurface(), currentSurface(), setSurface() + \sa QGLAbstractSurface::activate() +*/ +void QGLPainter::pushSurface(QGLAbstractSurface *surface) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + Q_ASSERT(surface); + if (!surface) { + // Find the most recent main surface for this painter. + int size = d->surfaceStack.size(); + while (size > 0 && !d->surfaceStack[size - 1].mainSurface) + --size; + if (!size) + return; // Shouldn't happen, but be safe anyway. + surface = d->surfaceStack[size - 1].surface; + } + Q_ASSERT(!d->surfaceStack.isEmpty()); // Should have a main surface. + QGLAbstractSurface *current = d->surfaceStack.top().surface; + QGLPainterSurfaceInfo psurf; + psurf.surface = surface; + psurf.destroySurface = false; + psurf.mainSurface = false; + d->surfaceStack.append(psurf); + current->switchTo(surface); + d->updates |= UpdateViewport; +} + +/*! + Pops the top-most drawing surface from the surface stack + and returns it. The next object on the stack will be made + the current drawing surface for context(). Returns null if the + surface stack is already at the main surface (e.g. the window). + + The UpdateViewport flag will be set to indicate that the + \c{glViewport()} should be adjusted to the new surface extents + when update() is next called. + + \sa pushSurface(), currentSurface(), setSurface() +*/ +QGLAbstractSurface *QGLPainter::popSurface() +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + Q_ASSERT(!d->surfaceStack.isEmpty()); // Should have a main surface. + QGLPainterSurfaceInfo &surf = d->surfaceStack.top(); + if (surf.mainSurface) + return 0; + QGLAbstractSurface *surface = surf.surface; + d->surfaceStack.pop(); + Q_ASSERT(!d->surfaceStack.isEmpty()); // Should have a main surface. + QGLAbstractSurface *nextSurface = d->surfaceStack.top().surface; + surface->switchTo(nextSurface); + d->updates |= UpdateViewport; + return surface; +} + +/*! + Sets the top-most drawing surface on the surface stack to \a surface + and activate it. + + Note: if the top-most drawing surface is the main surface specified + during begin(), then this function will perform a pushSurface() + instead. Typically this function is used to replace the last + surface that was pushed onto the stack and avoid doing popSurface() + followed by pushSurface(). The main surface cannot be replaced + in this manner. + + The UpdateViewport flag will be set to indicate that the + \c{glViewport()} should be adjusted to the extents of \a surface + when update() is next called. + + \sa pushSurface(), popSurface(), currentSurface() +*/ +void QGLPainter::setSurface(QGLAbstractSurface *surface) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + Q_ASSERT(surface); + Q_ASSERT(!d->surfaceStack.isEmpty()); // Should have a main surface. + QGLPainterSurfaceInfo &surf = d->surfaceStack.top(); + if (surf.mainSurface) { + pushSurface(surface); + return; + } + QGLAbstractSurface *oldSurface = surf.surface; + surf.surface = surface; + oldSurface->switchTo(surface); + d->updates |= UpdateViewport; +} + +/*! + Returns the current drawing surface. + + \sa pushSurface(), popSurface(), setSurface() +*/ +QGLAbstractSurface *QGLPainter::currentSurface() const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + Q_ASSERT(!d->surfaceStack.isEmpty()); // Should have a main surface. + return d->surfaceStack.top().surface; +} + +/*! + Returns the current lighting model. + + \sa setLightModel() +*/ +const QGLLightModel *QGLPainter::lightModel() const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (!d->lightModel) { + if (!d->defaultLightModel) + d->defaultLightModel = new QGLLightModel(); + d->lightModel = d->defaultLightModel; + } + return d->lightModel; +} + +/*! + Sets the current lighting model to \a value. If \a value is + null, then the default lighting model parameters will be used. + + The light settings in the GL server will not be changed until + update() is called. + + \sa lightModel() +*/ +void QGLPainter::setLightModel(const QGLLightModel *value) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + d->lightModel = value; + d->updates |= QGLPainter::UpdateLights; +} + +/*! + Returns the parameters for the main light in the scene. + + The light parameters are specified in world co-ordinates at + the point when setMainLight() was called. The mainLightTransform() + must be applied to obtain eye co-ordinates. + + This function is a convenience that returns the light with + identifier 0. If light 0 is not currently enabled, then a + default light is added to the painter with an identity + transform and then returned as the main light. + + \sa setMainLight(), mainLightTransform(), addLight() +*/ +const QGLLightParameters *QGLPainter::mainLight() const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (d->lights.isEmpty()) { + if (!d->defaultLight) + d->defaultLight = new QGLLightParameters(); + d->lights.append(d->defaultLight); + d->lightTransforms.append(QMatrix4x4()); + } else if (!d->lights[0]) { + if (!d->defaultLight) + d->defaultLight = new QGLLightParameters(); + d->lights[0] = d->defaultLight; + d->lightTransforms[0] = QMatrix4x4(); + } + return d->lights[0]; +} + +/*! + Sets the \a parameters for the main light in the scene. + The mainLightTransform() is set to the current modelViewMatrix(). + + Light parameters are stored in world co-ordinates, not eye co-ordinates. + The mainLightTransform() specifies the transformation to apply to + convert the world co-ordinates into eye co-ordinates when the light + is used. + + Note: the \a parameters may be ignored by effect() if it + has some other way to determine the lighting conditions. + + The light settings in the GL server will not be changed until + update() is called. + + This function is a convenience that sets the light with + identifier 0. If \a parameters is null, then light 0 + will be removed. + + \sa mainLight(), mainLightTransform(), addLight() +*/ +void QGLPainter::setMainLight(const QGLLightParameters *parameters) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (d->lights.isEmpty()) { + if (parameters) { + d->lights.append(parameters); + d->lightTransforms.append(modelViewMatrix()); + d->updates |= QGLPainter::UpdateLights; + } + } else if (parameters) { + d->lights[0] = parameters; + d->lightTransforms[0] = modelViewMatrix(); + d->updates |= QGLPainter::UpdateLights; + } else { + removeLight(0); + } +} + +/*! + Sets the \a parameters for the main light in the scene, and set + mainLightTransform() to \a transform. + + Light parameters are stored in world co-ordinates, not eye co-ordinates. + The \a transform specifies the transformation to apply to convert the + world co-ordinates into eye co-ordinates when the light is used. + + Note: the \a parameters may be ignored by effect() if it + has some other way to determine the lighting conditions. + + The light settings in the GL server will not be changed until + update() is called. + + This function is a convenience that sets the light with + identifier 0. If \a parameters is null, then light 0 + will be removed. + + \sa mainLight(), mainLightTransform() +*/ +void QGLPainter::setMainLight + (const QGLLightParameters *parameters, const QMatrix4x4& transform) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (d->lights.isEmpty()) { + if (parameters) { + d->lights.append(parameters); + d->lightTransforms.append(transform); + d->updates |= QGLPainter::UpdateLights; + } + } else if (parameters) { + d->lights[0] = parameters; + d->lightTransforms[0] = transform; + d->updates |= QGLPainter::UpdateLights; + } else { + removeLight(0); + } +} + +/*! + Returns the modelview transformation matrix for the main light that + was set at the time setMainLight() was called. + + The light transform may be used by later painting operations to + convert the light from world co-ordinates into eye co-ordinates. + The eye transformation is set when the light is specified. + + This function is a convenience that returns the tranform for the + light with identifier 0. If light 0 is not enabled, then the + function returns the identity matrix. + + \sa mainLight(), setMainLight(), addLight() +*/ +QMatrix4x4 QGLPainter::mainLightTransform() const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (!d->lights.isEmpty() && d->lights[0]) + return d->lightTransforms[0]; + else + return QMatrix4x4(); +} + +/*! + Adds a light to this painter, with the specified \a parameters. + The lightTransform() for the light is set to the current + modelViewMatrix(). Returns an identifier for the light. + + Light parameters are stored in world co-ordinates, not eye co-ordinates. + The lightTransform() specifies the transformation to apply to + convert the world co-ordinates into eye co-ordinates when the light + is used. + + Note: the \a parameters may be ignored by effect() if it + has some other way to determine the lighting conditions. + + The light settings in the GL server will not be changed until + update() is called. + + \sa removeLight(), light(), mainLight() +*/ +int QGLPainter::addLight(const QGLLightParameters *parameters) +{ + return addLight(parameters, modelViewMatrix()); +} + +/*! + Adds a light to this painter, with the specified \a parameters. + The lightTransform() for the light is set to \a transform. + Returns an identifier for the light. + + Light parameters are stored in world co-ordinates, not eye co-ordinates. + The \a transform specifies the transformation to apply to + convert the world co-ordinates into eye co-ordinates when the light + is used. + + Note: the \a parameters may be ignored by effect() if it + has some other way to determine the lighting conditions. + + The light settings in the GL server will not be changed until + update() is called. + + \sa removeLight(), light(), mainLight() +*/ +int QGLPainter::addLight(const QGLLightParameters *parameters, const QMatrix4x4 &transform) +{ + Q_ASSERT(parameters); + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + int lightId = 0; + while (lightId < d->lights.size() && d->lights[lightId] != 0) + ++lightId; + if (lightId < d->lights.size()) { + d->lights[lightId] = parameters; + d->lightTransforms[lightId] = transform; + } else { + d->lights.append(parameters); + d->lightTransforms.append(transform); + } + d->updates |= QGLPainter::UpdateLights; + return lightId; +} + +/*! + Removes the light with the specified \a lightId. + + \sa addLight(), light() +*/ +void QGLPainter::removeLight(int lightId) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (lightId >= 0 && lightId < d->lights.size()) { + d->lights[lightId] = 0; + if (lightId >= (d->lights.size() - 1)) { + do { + d->lights.resize(lightId); + d->lightTransforms.resize(lightId); + --lightId; + } while (lightId >= 0 && d->lights[lightId] == 0); + } + d->updates |= QGLPainter::UpdateLights; + } +} + +/*! + Returns the maximum light identifier currently in use on this painter; + or -1 if there are no lights. + + It is possible that some light identifiers less than maximumLightId() + may be invalid because the lights have been removed. Use the following + code to locate all enabled lights: + + \code + int maxLightId = painter.maximumLightId(); + for (int lightId = 0; index <= maxLightId; ++index) { + const QGLLightParameters *params = painter.light(lightId); + if (params) { + ... + } + } + \endcode + + \sa addLight(), light() +*/ +int QGLPainter::maximumLightId() const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return d->lights.size() - 1; +} + +/*! + Returns the parameters for the light with the identifier \a lightId; + or null if \a lightId is not valid or has been removed. + + \sa addLight(), removeLight(), lightTransform() +*/ +const QGLLightParameters *QGLPainter::light(int lightId) const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (lightId >= 0 && lightId < d->lights.size()) + return d->lights[lightId]; + else + return 0; +} + +/*! + Returns the modelview transformation for the light with the identifier + \a lightId; or the identity matrix if \a lightId is not valid or has + been removed. + + \sa addLight(), removeLight(), light() +*/ +QMatrix4x4 QGLPainter::lightTransform(int lightId) const +{ + Q_D(const QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (lightId >= 0 && lightId < d->lights.size() && d->lights[lightId]) + return d->lightTransforms[lightId]; + else + return QMatrix4x4(); +} + +/*! + Returns the material that is used for drawing \a face on polygons. + If \a face is QGL::FrontFaces or QGL::AllFaces, then the front + material is returned. If \a face is QGL::BackFaces, then the + back material is returned. + + \sa setFaceMaterial(), setFaceColor() +*/ +const QGLMaterial *QGLPainter::faceMaterial(QGL::Face face) const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (face == QGL::BackFaces) { + if (!d->backMaterial) { + if (!d->defaultMaterial) + d->defaultMaterial = new QGLMaterial(); + d->backMaterial = d->defaultMaterial; + } + return d->backMaterial; + } else { + if (!d->frontMaterial) { + if (!d->defaultMaterial) + d->defaultMaterial = new QGLMaterial(); + d->frontMaterial = d->defaultMaterial; + } + return d->frontMaterial; + } +} + +/*! + Sets the material that is used for drawing \a face on polygons + to \a value. If \a face is QGL::FrontFaces, then the front + material is set. If \a face is QGL::BackFaces, then the + back material is set. If \a face is QGL::AllFaces, then both + the front and back materials are set. + + If \a value is null, then the \a face material will be set to + the default material properties. + + The material settings in the GL server will not be changed until + update() is called. + + \sa faceMaterial(), setFaceColor() +*/ +void QGLPainter::setFaceMaterial + (QGL::Face face, const QGLMaterial *value) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (face == QGL::FrontFaces) { + if (d->frontMaterial == value) + return; + d->frontMaterial = value; + } else if (face == QGL::BackFaces) { + if (d->backMaterial == value) + return; + d->backMaterial = value; + } else { + if (d->frontMaterial == value && d->backMaterial == value) + return; + d->frontMaterial = value; + d->backMaterial = value; + } + d->updates |= QGLPainter::UpdateMaterials; +} + +static QGLMaterial *createColorMaterial + (QGLMaterial *prev, const QColor& color) +{ + QGLMaterial *material; + if (prev) + material = prev; + else + material = new QGLMaterial(); + material->setColor(color); + return material; +} + +/*! + Sets the material that is used for drawing \a face on polygons + to \a color. This is a convenience function for setting materials + to simple colors. + + The RGB components of the ambient color of the material will be set + to 20% of \a color, and the RGB components of the diffuse color of the + material will be set to 80% of \a color. The alpha components of + the ambient and diffuse material colors will both be set to the + alpha component of \a color. + + If \a face is QGL::FrontFaces, then the front material is set. + If \a face is QGL::BackFaces, then the back material is set. + If \a face is QGL::AllFaces, then both the front and back + materials are set. + + The material settings in the GL server will not be changed until + update() is called. + + \sa faceMaterial(), setFaceMaterial() +*/ +void QGLPainter::setFaceColor(QGL::Face face, const QColor& color) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (face == QGL::FrontFaces) { + d->frontColorMaterial = + createColorMaterial(d->frontColorMaterial, color); + d->frontMaterial = d->frontColorMaterial; + } else if (face == QGL::BackFaces) { + d->backColorMaterial = + createColorMaterial(d->backColorMaterial, color); + d->backMaterial = d->backColorMaterial; + } else { + d->frontColorMaterial = + createColorMaterial(d->frontColorMaterial, color); + d->backColorMaterial = + createColorMaterial(d->backColorMaterial, color); + d->frontMaterial = d->frontColorMaterial; + d->backMaterial = d->backColorMaterial; + } + d->updates |= QGLPainter::UpdateMaterials; +} + +/*! + Returns true if this painter is in object picking mode; + false if this painter is in normal rendering mode. + + \sa setPicking(), objectPickId() +*/ +bool QGLPainter::isPicking() const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return (d->pick ? d->pick->isPicking : false); +} + +/*! + Enables or disables object picking mode according to \a value. + + If \a value is true, then the effect() will be overridden with a + simple flat color effect that renders objects with pickColor(). + These colors can be read back later with pickObject(). + + \sa isPicking(), objectPickId(), pickObject() +*/ +void QGLPainter::setPicking(bool value) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (!d->pick) + d->pick = new QGLPainterPickPrivate(); + if (d->pick->isPicking != value) { + // Switch to/from the pick effect. + d->pick->isPicking = value; + if (d->effect) + d->effect->setActive(this, false); + d->effect = 0; + d->ensureEffect(this); + } +} + +/*! + Returns the current object pick identifier. The default value + is -1 which indicates that rendered objects should not have a + pickColor() associated with them. + + \sa setObjectPickId(), clearPickObjects(), pickObject() +*/ +int QGLPainter::objectPickId() const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + return (d->pick ? d->pick->objectPickId : -1); +} + +/*! + Sets the current object pick identifier to \a value. If \a value + is -1, then subsequent objects will be rendered without a pickColor(). + + If value is not -1, then the pickColor() is changed to a color + that represents that object pick identifier. If \a value has been + seen previously, then the same pickColor() as last time will + be returned. + + The function call will be ignored if isPicking() is false. + + \sa objectPickId(), clearPickObjects(), pickObject() +*/ +void QGLPainter::setObjectPickId(int value) +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (!d->pick || !d->pick->isPicking) + return; + d->pick->objectPickId = value; + if (value != -1) { + QRgb color = d->pick->pickObjectToColor.value(value, 0); + if (!color) { + color = qt_qgl_pick_color(d->pick->pickColorIndex++); + d->pick->pickObjectToColor[value] = color; + d->pick->pickColorToObject[color] = value; + } + d->pick->pickColor = color; + d->updates |= UpdateColor; + } else { + d->pick->pickColor = 0; + d->updates |= UpdateColor; + } +} + +/*! + Clears the objectPickId() to pickColor() mappings that + were used previously. This will also set objectPickId() + to -1 and pickColor() to (0, 0, 0, 1). + + The function call will be ignored if isPicking() is false. + + \sa objectPickId(), pickColor() +*/ +void QGLPainter::clearPickObjects() +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (d->pick && d->pick->isPicking) { + d->pick->pickObjectToColor.clear(); + d->pick->pickColorToObject.clear(); + d->pick->pickColorIndex = 0; + d->pick->objectPickId = -1; + d->pick->pickColor = 0; + d->updates |= UpdateColor; + } +} + +/*! + Returns the current pick color to use to render the object + associated with objectPickId(). The returned color will + be (0, 0, 0, 1) if objectPickId() is -1. + + \sa objectPickId(), clearPickObjects() +*/ +QColor QGLPainter::pickColor() const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + if (d->pick) { + QColor color; + color.setRgb(d->pick->pickColor); + return color; + } else { + return Qt::black; + } +} + +/*! + Picks the color at (\a x, \a y) in the color buffer and + returns the objectPickId() that corresponds to that color. + Returns -1 if (\a x, \a y) is not positioned over a + recognized object. The origin (0, 0) is assumed to be + the bottom-left corner of the drawing surface. + + \sa objectPickId() +*/ +int QGLPainter::pickObject(int x, int y) const +{ + Q_D(QGLPainter); + QGLPAINTER_CHECK_PRIVATE(); + + if (!d->pick) + { + return -1; + } + + // Fetch the color at the specified pixel. + unsigned char data[4] = {0, 0, 0, 0}; + glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data); + QRgb color = qRgb(data[0], data[1], data[2]); + + // Normalize the color to account for floating-point rounding. + color = qt_qgl_normalize_pick_color(color); // XXX: detect RGB444 screens. + + // Map the color back to an object identifier. + return d->pick->pickColorToObject.value(color, -1); +} + +QT_END_NAMESPACE diff --git a/src/threed/painting/qglpainter.h b/src/threed/painting/qglpainter.h new file mode 100644 index 000000000..5d805f0bf --- /dev/null +++ b/src/threed/painting/qglpainter.h @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLPAINTER_H +#define QGLPAINTER_H + +#include <QtOpenGL/qgl.h> +#include "qglnamespace.h" +#include <QtGui/qvector2d.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qmatrix4x4.h> +#include "qbox3d.h" +#include "qopenglfunctions.h" +#include "qglvertexbundle.h" +#include "qglindexbuffer.h" +#include "qgllightmodel.h" +#include "qgllightparameters.h" +#include "qglmaterial.h" +#include "qglabstractsurface.h" +#include "qmatrix4x4stack.h" +#include "qglcamera.h" +#include "qvector2darray.h" +#include "qvector3darray.h" +#include "qvector4darray.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLAbstractEffect; +class QGLPainterPrivate; +class QGLTexture2D; +class QGLTextureCube; +class QGeometryData; +class QGLShaderProgram; +class QGLFramebufferObject; +class QGLSceneNode; +class QGLRenderSequencer; +class QGLAbstractSurface; + +class Q_QT3D_EXPORT QGLPainter : public QOpenGLFunctions +{ +public: + QGLPainter(); + explicit QGLPainter(const QGLContext *context); + explicit QGLPainter(QGLWidget *widget); + explicit QGLPainter(QPainter *painter); + explicit QGLPainter(QGLAbstractSurface *surface); + virtual ~QGLPainter(); + + bool begin(); + bool begin(const QGLContext *context); + bool begin(QGLWidget *widget); + bool begin(QPainter *painter); + bool begin(QGLAbstractSurface *surface); + bool end(); + bool isActive() const; + + const QGLContext *context() const; + + bool isFixedFunction() const; + + enum Update + { + UpdateColor = 0x00000001, + UpdateModelViewMatrix = 0x00000002, + UpdateProjectionMatrix = 0x00000004, + UpdateMatrices = 0x00000006, + UpdateLights = 0x00000008, + UpdateMaterials = 0x00000010, + UpdateViewport = 0x00000020, + UpdateAll = 0x7FFFFFFF + }; + Q_DECLARE_FLAGS(Updates, Update); + + void setClearColor(const QColor& color); + + void setScissor(const QRect& rect); + + QMatrix4x4Stack& projectionMatrix(); + QMatrix4x4Stack& modelViewMatrix(); + QMatrix4x4 combinedMatrix() const; + QMatrix3x3 normalMatrix() const; + QMatrix4x4 worldMatrix() const; + + QGL::Eye eye() const; + void setEye(QGL::Eye eye); + + void setCamera(const QGLCamera *camera); + + bool isCullable(const QVector3D& point) const; + bool isCullable(const QBox3D& box) const; + QGLRenderSequencer *renderSequencer(); + + qreal aspectRatio() const; + + QGLAbstractEffect *effect() const; + + QGLAbstractEffect *userEffect() const; + void setUserEffect(QGLAbstractEffect *effect); + + QGL::StandardEffect standardEffect() const; + void setStandardEffect(QGL::StandardEffect effect); + + void disableEffect(); + + QGLShaderProgram *cachedProgram(const QString& name) const; + void setCachedProgram(const QString& name, QGLShaderProgram *program); + + QColor color() const; + void setColor(const QColor& color); + + QGLAttributeSet attributes() const; + void clearAttributes(); + + void setVertexAttribute + (QGL::VertexAttribute attribute, const QGLAttributeValue& value); + void setVertexBundle(const QGLVertexBundle& buffer); + + void update(); + void updateFixedFunction(QGLPainter::Updates updates); + + void draw(QGL::DrawingMode mode, int count, int index = 0); + void draw(QGL::DrawingMode mode, const ushort *indices, int count); + void draw(QGL::DrawingMode mode, const QGLIndexBuffer& indices); + virtual void draw(QGL::DrawingMode mode, const QGLIndexBuffer& indices, int offset, int count); + + void pushSurface(QGLAbstractSurface *surface); + QGLAbstractSurface *popSurface(); + void setSurface(QGLAbstractSurface *surface); + QGLAbstractSurface *currentSurface() const; + + const QGLLightModel *lightModel() const; + void setLightModel(const QGLLightModel *value); + + const QGLLightParameters *mainLight() const; + void setMainLight(const QGLLightParameters *parameters); + void setMainLight + (const QGLLightParameters *parameters, const QMatrix4x4& transform); + QMatrix4x4 mainLightTransform() const; + + int addLight(const QGLLightParameters *parameters); + int addLight(const QGLLightParameters *parameters, const QMatrix4x4 &transform); + void removeLight(int lightId); + + int maximumLightId() const; + const QGLLightParameters *light(int lightId) const; + QMatrix4x4 lightTransform(int lightId) const; + + const QGLMaterial *faceMaterial(QGL::Face face) const; + void setFaceMaterial(QGL::Face face, const QGLMaterial *value); + void setFaceColor(QGL::Face face, const QColor& color); + + bool isPicking() const; + void setPicking(bool value); + + int objectPickId() const; + void setObjectPickId(int value); + void clearPickObjects(); + + QColor pickColor() const; + + int pickObject(int x, int y) const; + +private: + Q_DISABLE_COPY(QGLPainter) + + QGLPainterPrivate *d_ptr; + + QGLPainterPrivate *d_func() const { return d_ptr; } + + friend class QGLAbstractEffect; + + bool begin(const QGLContext *context, QGLAbstractSurface *surface, + bool destroySurface = true); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGLPainter::Updates) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/painting/qglpainter_p.h b/src/threed/painting/qglpainter_p.h new file mode 100644 index 000000000..82f901797 --- /dev/null +++ b/src/threed/painting/qglpainter_p.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLPAINTER_P_H +#define QGLPAINTER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglpainter.h" +#include "qglrendersequencer.h" + +#include <QtCore/qatomic.h> +#include <QtCore/qmap.h> +#include <QtCore/qstack.h> + +QT_BEGIN_NAMESPACE + +#define QGL_MAX_LIGHTS 32 +#define QGL_MAX_STD_EFFECTS 16 + +class QGLPainterPickPrivate +{ +public: + QGLPainterPickPrivate(); + ~QGLPainterPickPrivate(); + + bool isPicking; + int objectPickId; + int pickColorIndex; + QRgb pickColor; + QMap<int, QRgb> pickObjectToColor; + QMap<QRgb, int> pickColorToObject; + QGLAbstractEffect *defaultPickEffect; +}; + +struct QGLPainterSurfaceInfo +{ + QGLAbstractSurface *surface; + bool destroySurface; + bool mainSurface; +}; + +class QGLPainterPrivate +{ +public: + QGLPainterPrivate(); + ~QGLPainterPrivate(); + + QAtomicInt ref; + const QGLContext *context; + QMatrix4x4Stack projectionMatrix; + QMatrix4x4Stack modelViewMatrix; + QMatrix4x4 inverseEyeMatrix; + QGL::Eye eye; + QGLAbstractEffect *effect; + QGLAbstractEffect *userEffect; + QGL::StandardEffect standardEffect; + QGLAbstractEffect *stdeffects[QGL_MAX_STD_EFFECTS]; + const QGLLightModel *lightModel; + QGLLightModel *defaultLightModel; + QGLLightParameters *defaultLight; + QArray<const QGLLightParameters *> lights; + QArray<QMatrix4x4> lightTransforms; + const QGLMaterial *frontMaterial; + const QGLMaterial *backMaterial; + QGLMaterial *defaultMaterial; + QGLMaterial *frontColorMaterial; + QGLMaterial *backColorMaterial; + QBox3D viewingCube; + QColor color; + QGLPainter::Updates updates; + QGLPainterPickPrivate *pick; + QMap<QString, QGLShaderProgram *> cachedPrograms; + QStack<QGLPainterSurfaceInfo> surfaceStack; + GLuint boundVertexBuffer; + GLuint boundIndexBuffer; + QGLRenderSequencer *renderSequencer; + bool isFixedFunction; + QGLAttributeSet attributeSet; + + inline void ensureEffect(QGLPainter *painter) + { if (!effect) createEffect(painter); } + void createEffect(QGLPainter *painter); +}; + +class QGLPainterPrivateCache : public QObject +{ + Q_OBJECT +public: + QGLPainterPrivateCache(); + ~QGLPainterPrivateCache(); + + QMap<const QGLContext *, QGLPainterPrivate *> cache; + + QGLPainterPrivate *fromContext(const QGLContext *context); + + static QGLPainterPrivateCache *instance(); + +public Q_SLOTS: + void contextDestroyed(const QGLContext *context); + +Q_SIGNALS: + void destroyedContext(const QGLContext *context); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/threed/painting/qglpickcolors.cpp b/src/threed/painting/qglpickcolors.cpp new file mode 100644 index 000000000..ff94ea40a --- /dev/null +++ b/src/threed/painting/qglpickcolors.cpp @@ -0,0 +1,1125 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if !defined(QGL_GENERATOR_PROGRAM) + +#include "qglpickcolors_p.h" + +QT_BEGIN_NAMESPACE + +// The following tables are generated by the program listed at the +// end of this source file. + +static int const pickColors[4096] = { + 0xffffff, 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff, + 0xff8000, 0x80ff00, 0x8000ff, 0xff0080, 0x0080ff, 0x00ff80, 0xff80ff, + 0x80ffff, 0xffff80, 0x80ff80, 0xff8080, 0x8080ff, 0x808080, 0x800000, + 0x008000, 0x000080, 0x808000, 0x800080, 0x008080, 0xff80c0, 0x80c0ff, + 0xc0ff80, 0xffc080, 0x80ffc0, 0xc080ff, 0xffc000, 0xc0ff00, 0xc000ff, + 0xff00c0, 0x00c0ff, 0x00ffc0, 0xffc0ff, 0xc0ffff, 0xffffc0, 0xc0ffc0, + 0xffc0c0, 0xc0c0ff, 0x80c000, 0xc08000, 0xc00080, 0x8000c0, 0x00c080, + 0x0080c0, 0x80c080, 0xc08080, 0x8080c0, 0xc080c0, 0x80c0c0, 0xc0c080, + 0xc0c0c0, 0xc00000, 0x00c000, 0x0000c0, 0xc0c000, 0xc000c0, 0x00c0c0, + 0xff8040, 0x8040ff, 0x40ff80, 0xff4080, 0x80ff40, 0x4080ff, 0xffc040, + 0xc040ff, 0x40ffc0, 0xff40c0, 0xc0ff40, 0x40c0ff, 0xff4000, 0x40ff00, + 0x4000ff, 0xff0040, 0x0040ff, 0x00ff40, 0xff40ff, 0x40ffff, 0xffff40, + 0x40ff40, 0xff4040, 0x4040ff, 0x80c040, 0xc04080, 0x4080c0, 0x8040c0, + 0xc08040, 0x40c080, 0x804000, 0x408000, 0x400080, 0x800040, 0x004080, + 0x008040, 0x804080, 0x408080, 0x808040, 0x408040, 0x804040, 0x404080, + 0xc04000, 0x40c000, 0x4000c0, 0xc00040, 0x0040c0, 0x00c040, 0xc040c0, + 0x40c0c0, 0xc0c040, 0x40c040, 0xc04040, 0x4040c0, 0x404040, 0x400000, + 0x004000, 0x000040, 0x404000, 0x400040, 0x004040, 0xff80e0, 0x80e0ff, + 0xe0ff80, 0xffe080, 0x80ffe0, 0xe080ff, 0xffc0e0, 0xc0e0ff, 0xe0ffc0, + 0xffe0c0, 0xc0ffe0, 0xe0c0ff, 0xff40e0, 0x40e0ff, 0xe0ff40, 0xffe040, + 0x40ffe0, 0xe040ff, 0xffe000, 0xe0ff00, 0xe000ff, 0xff00e0, 0x00e0ff, + 0x00ffe0, 0xffe0ff, 0xe0ffff, 0xffffe0, 0xe0ffe0, 0xffe0e0, 0xe0e0ff, + 0x80c0e0, 0xc0e080, 0xe080c0, 0x80e0c0, 0xc080e0, 0xe0c080, 0x8040e0, + 0x40e080, 0xe08040, 0x80e040, 0x4080e0, 0xe04080, 0x80e000, 0xe08000, + 0xe00080, 0x8000e0, 0x00e080, 0x0080e0, 0x80e080, 0xe08080, 0x8080e0, + 0xe080e0, 0x80e0e0, 0xe0e080, 0xc040e0, 0x40e0c0, 0xe0c040, 0xc0e040, + 0x40c0e0, 0xe040c0, 0xc0e000, 0xe0c000, 0xe000c0, 0xc000e0, 0x00e0c0, + 0x00c0e0, 0xc0e0c0, 0xe0c0c0, 0xc0c0e0, 0xe0c0e0, 0xc0e0e0, 0xe0e0c0, + 0x40e000, 0xe04000, 0xe00040, 0x4000e0, 0x00e040, 0x0040e0, 0x40e040, + 0xe04040, 0x4040e0, 0xe040e0, 0x40e0e0, 0xe0e040, 0xe0e0e0, 0xe00000, + 0x00e000, 0x0000e0, 0xe0e000, 0xe000e0, 0x00e0e0, 0xff8060, 0x8060ff, + 0x60ff80, 0xff6080, 0x80ff60, 0x6080ff, 0xffc060, 0xc060ff, 0x60ffc0, + 0xff60c0, 0xc0ff60, 0x60c0ff, 0xff4060, 0x4060ff, 0x60ff40, 0xff6040, + 0x40ff60, 0x6040ff, 0xffe060, 0xe060ff, 0x60ffe0, 0xff60e0, 0xe0ff60, + 0x60e0ff, 0xff6000, 0x60ff00, 0x6000ff, 0xff0060, 0x0060ff, 0x00ff60, + 0xff60ff, 0x60ffff, 0xffff60, 0x60ff60, 0xff6060, 0x6060ff, 0x80c060, + 0xc06080, 0x6080c0, 0x8060c0, 0xc08060, 0x60c080, 0x804060, 0x406080, + 0x608040, 0x806040, 0x408060, 0x604080, 0x80e060, 0xe06080, 0x6080e0, + 0x8060e0, 0xe08060, 0x60e080, 0x806000, 0x608000, 0x600080, 0x800060, + 0x006080, 0x008060, 0x806080, 0x608080, 0x808060, 0x608060, 0x806060, + 0x606080, 0xc04060, 0x4060c0, 0x60c040, 0xc06040, 0x40c060, 0x6040c0, + 0xc0e060, 0xe060c0, 0x60c0e0, 0xc060e0, 0xe0c060, 0x60e0c0, 0xc06000, + 0x60c000, 0x6000c0, 0xc00060, 0x0060c0, 0x00c060, 0xc060c0, 0x60c0c0, + 0xc0c060, 0x60c060, 0xc06060, 0x6060c0, 0x40e060, 0xe06040, 0x6040e0, + 0x4060e0, 0xe04060, 0x60e040, 0x406000, 0x604000, 0x600040, 0x400060, + 0x006040, 0x004060, 0x406040, 0x604040, 0x404060, 0x604060, 0x406060, + 0x606040, 0xe06000, 0x60e000, 0x6000e0, 0xe00060, 0x0060e0, 0x00e060, + 0xe060e0, 0x60e0e0, 0xe0e060, 0x60e060, 0xe06060, 0x6060e0, 0x606060, + 0x600000, 0x006000, 0x000060, 0x606000, 0x600060, 0x006060, 0xff80a0, + 0x80a0ff, 0xa0ff80, 0xffa080, 0x80ffa0, 0xa080ff, 0xffc0a0, 0xc0a0ff, + 0xa0ffc0, 0xffa0c0, 0xc0ffa0, 0xa0c0ff, 0xff40a0, 0x40a0ff, 0xa0ff40, + 0xffa040, 0x40ffa0, 0xa040ff, 0xffe0a0, 0xe0a0ff, 0xa0ffe0, 0xffa0e0, + 0xe0ffa0, 0xa0e0ff, 0xff60a0, 0x60a0ff, 0xa0ff60, 0xffa060, 0x60ffa0, + 0xa060ff, 0xffa000, 0xa0ff00, 0xa000ff, 0xff00a0, 0x00a0ff, 0x00ffa0, + 0xffa0ff, 0xa0ffff, 0xffffa0, 0xa0ffa0, 0xffa0a0, 0xa0a0ff, 0x80c0a0, + 0xc0a080, 0xa080c0, 0x80a0c0, 0xc080a0, 0xa0c080, 0x8040a0, 0x40a080, + 0xa08040, 0x80a040, 0x4080a0, 0xa04080, 0x80e0a0, 0xe0a080, 0xa080e0, + 0x80a0e0, 0xe080a0, 0xa0e080, 0x8060a0, 0x60a080, 0xa08060, 0x80a060, + 0x6080a0, 0xa06080, 0x80a000, 0xa08000, 0xa00080, 0x8000a0, 0x00a080, + 0x0080a0, 0x80a080, 0xa08080, 0x8080a0, 0xa080a0, 0x80a0a0, 0xa0a080, + 0xc040a0, 0x40a0c0, 0xa0c040, 0xc0a040, 0x40c0a0, 0xa040c0, 0xc0e0a0, + 0xe0a0c0, 0xa0c0e0, 0xc0a0e0, 0xe0c0a0, 0xa0e0c0, 0xc060a0, 0x60a0c0, + 0xa0c060, 0xc0a060, 0x60c0a0, 0xa060c0, 0xc0a000, 0xa0c000, 0xa000c0, + 0xc000a0, 0x00a0c0, 0x00c0a0, 0xc0a0c0, 0xa0c0c0, 0xc0c0a0, 0xa0c0a0, + 0xc0a0a0, 0xa0a0c0, 0x40e0a0, 0xe0a040, 0xa040e0, 0x40a0e0, 0xe040a0, + 0xa0e040, 0x4060a0, 0x60a040, 0xa04060, 0x40a060, 0x6040a0, 0xa06040, + 0x40a000, 0xa04000, 0xa00040, 0x4000a0, 0x00a040, 0x0040a0, 0x40a040, + 0xa04040, 0x4040a0, 0xa040a0, 0x40a0a0, 0xa0a040, 0xe060a0, 0x60a0e0, + 0xa0e060, 0xe0a060, 0x60e0a0, 0xa060e0, 0xe0a000, 0xa0e000, 0xa000e0, + 0xe000a0, 0x00a0e0, 0x00e0a0, 0xe0a0e0, 0xa0e0e0, 0xe0e0a0, 0xa0e0a0, + 0xe0a0a0, 0xa0a0e0, 0x60a000, 0xa06000, 0xa00060, 0x6000a0, 0x00a060, + 0x0060a0, 0x60a060, 0xa06060, 0x6060a0, 0xa060a0, 0x60a0a0, 0xa0a060, + 0xa0a0a0, 0xa00000, 0x00a000, 0x0000a0, 0xa0a000, 0xa000a0, 0x00a0a0, + 0xff8020, 0x8020ff, 0x20ff80, 0xff2080, 0x80ff20, 0x2080ff, 0xffc020, + 0xc020ff, 0x20ffc0, 0xff20c0, 0xc0ff20, 0x20c0ff, 0xff4020, 0x4020ff, + 0x20ff40, 0xff2040, 0x40ff20, 0x2040ff, 0xffe020, 0xe020ff, 0x20ffe0, + 0xff20e0, 0xe0ff20, 0x20e0ff, 0xff6020, 0x6020ff, 0x20ff60, 0xff2060, + 0x60ff20, 0x2060ff, 0xffa020, 0xa020ff, 0x20ffa0, 0xff20a0, 0xa0ff20, + 0x20a0ff, 0xff2000, 0x20ff00, 0x2000ff, 0xff0020, 0x0020ff, 0x00ff20, + 0xff20ff, 0x20ffff, 0xffff20, 0x20ff20, 0xff2020, 0x2020ff, 0x80c020, + 0xc02080, 0x2080c0, 0x8020c0, 0xc08020, 0x20c080, 0x804020, 0x402080, + 0x208040, 0x802040, 0x408020, 0x204080, 0x80e020, 0xe02080, 0x2080e0, + 0x8020e0, 0xe08020, 0x20e080, 0x806020, 0x602080, 0x208060, 0x802060, + 0x608020, 0x206080, 0x80a020, 0xa02080, 0x2080a0, 0x8020a0, 0xa08020, + 0x20a080, 0x802000, 0x208000, 0x200080, 0x800020, 0x002080, 0x008020, + 0x802080, 0x208080, 0x808020, 0x208020, 0x802020, 0x202080, 0xc04020, + 0x4020c0, 0x20c040, 0xc02040, 0x40c020, 0x2040c0, 0xc0e020, 0xe020c0, + 0x20c0e0, 0xc020e0, 0xe0c020, 0x20e0c0, 0xc06020, 0x6020c0, 0x20c060, + 0xc02060, 0x60c020, 0x2060c0, 0xc0a020, 0xa020c0, 0x20c0a0, 0xc020a0, + 0xa0c020, 0x20a0c0, 0xc02000, 0x20c000, 0x2000c0, 0xc00020, 0x0020c0, + 0x00c020, 0xc020c0, 0x20c0c0, 0xc0c020, 0x20c020, 0xc02020, 0x2020c0, + 0x40e020, 0xe02040, 0x2040e0, 0x4020e0, 0xe04020, 0x20e040, 0x406020, + 0x602040, 0x204060, 0x402060, 0x604020, 0x206040, 0x40a020, 0xa02040, + 0x2040a0, 0x4020a0, 0xa04020, 0x20a040, 0x402000, 0x204000, 0x200040, + 0x400020, 0x002040, 0x004020, 0x402040, 0x204040, 0x404020, 0x204020, + 0x402020, 0x202040, 0xe06020, 0x6020e0, 0x20e060, 0xe02060, 0x60e020, + 0x2060e0, 0xe0a020, 0xa020e0, 0x20e0a0, 0xe020a0, 0xa0e020, 0x20a0e0, + 0xe02000, 0x20e000, 0x2000e0, 0xe00020, 0x0020e0, 0x00e020, 0xe020e0, + 0x20e0e0, 0xe0e020, 0x20e020, 0xe02020, 0x2020e0, 0x60a020, 0xa02060, + 0x2060a0, 0x6020a0, 0xa06020, 0x20a060, 0x602000, 0x206000, 0x200060, + 0x600020, 0x002060, 0x006020, 0x602060, 0x206060, 0x606020, 0x206020, + 0x602020, 0x202060, 0xa02000, 0x20a000, 0x2000a0, 0xa00020, 0x0020a0, + 0x00a020, 0xa020a0, 0x20a0a0, 0xa0a020, 0x20a020, 0xa02020, 0x2020a0, + 0x202020, 0x200000, 0x002000, 0x000020, 0x202000, 0x200020, 0x002020, + 0xff80f0, 0x80f0ff, 0xf0ff80, 0xfff080, 0x80fff0, 0xf080ff, 0xffc0f0, + 0xc0f0ff, 0xf0ffc0, 0xfff0c0, 0xc0fff0, 0xf0c0ff, 0xff40f0, 0x40f0ff, + 0xf0ff40, 0xfff040, 0x40fff0, 0xf040ff, 0xffe0f0, 0xe0f0ff, 0xf0ffe0, + 0xfff0e0, 0xe0fff0, 0xf0e0ff, 0xff60f0, 0x60f0ff, 0xf0ff60, 0xfff060, + 0x60fff0, 0xf060ff, 0xffa0f0, 0xa0f0ff, 0xf0ffa0, 0xfff0a0, 0xa0fff0, + 0xf0a0ff, 0xff20f0, 0x20f0ff, 0xf0ff20, 0xfff020, 0x20fff0, 0xf020ff, + 0xfff000, 0xf0ff00, 0xf000ff, 0xff00f0, 0x00f0ff, 0x00fff0, 0xfff0ff, + 0xf0ffff, 0xfffff0, 0xf0fff0, 0xfff0f0, 0xf0f0ff, 0x80c0f0, 0xc0f080, + 0xf080c0, 0x80f0c0, 0xc080f0, 0xf0c080, 0x8040f0, 0x40f080, 0xf08040, + 0x80f040, 0x4080f0, 0xf04080, 0x80e0f0, 0xe0f080, 0xf080e0, 0x80f0e0, + 0xe080f0, 0xf0e080, 0x8060f0, 0x60f080, 0xf08060, 0x80f060, 0x6080f0, + 0xf06080, 0x80a0f0, 0xa0f080, 0xf080a0, 0x80f0a0, 0xa080f0, 0xf0a080, + 0x8020f0, 0x20f080, 0xf08020, 0x80f020, 0x2080f0, 0xf02080, 0x80f000, + 0xf08000, 0xf00080, 0x8000f0, 0x00f080, 0x0080f0, 0x80f080, 0xf08080, + 0x8080f0, 0xf080f0, 0x80f0f0, 0xf0f080, 0xc040f0, 0x40f0c0, 0xf0c040, + 0xc0f040, 0x40c0f0, 0xf040c0, 0xc0e0f0, 0xe0f0c0, 0xf0c0e0, 0xc0f0e0, + 0xe0c0f0, 0xf0e0c0, 0xc060f0, 0x60f0c0, 0xf0c060, 0xc0f060, 0x60c0f0, + 0xf060c0, 0xc0a0f0, 0xa0f0c0, 0xf0c0a0, 0xc0f0a0, 0xa0c0f0, 0xf0a0c0, + 0xc020f0, 0x20f0c0, 0xf0c020, 0xc0f020, 0x20c0f0, 0xf020c0, 0xc0f000, + 0xf0c000, 0xf000c0, 0xc000f0, 0x00f0c0, 0x00c0f0, 0xc0f0c0, 0xf0c0c0, + 0xc0c0f0, 0xf0c0f0, 0xc0f0f0, 0xf0f0c0, 0x40e0f0, 0xe0f040, 0xf040e0, + 0x40f0e0, 0xe040f0, 0xf0e040, 0x4060f0, 0x60f040, 0xf04060, 0x40f060, + 0x6040f0, 0xf06040, 0x40a0f0, 0xa0f040, 0xf040a0, 0x40f0a0, 0xa040f0, + 0xf0a040, 0x4020f0, 0x20f040, 0xf04020, 0x40f020, 0x2040f0, 0xf02040, + 0x40f000, 0xf04000, 0xf00040, 0x4000f0, 0x00f040, 0x0040f0, 0x40f040, + 0xf04040, 0x4040f0, 0xf040f0, 0x40f0f0, 0xf0f040, 0xe060f0, 0x60f0e0, + 0xf0e060, 0xe0f060, 0x60e0f0, 0xf060e0, 0xe0a0f0, 0xa0f0e0, 0xf0e0a0, + 0xe0f0a0, 0xa0e0f0, 0xf0a0e0, 0xe020f0, 0x20f0e0, 0xf0e020, 0xe0f020, + 0x20e0f0, 0xf020e0, 0xe0f000, 0xf0e000, 0xf000e0, 0xe000f0, 0x00f0e0, + 0x00e0f0, 0xe0f0e0, 0xf0e0e0, 0xe0e0f0, 0xf0e0f0, 0xe0f0f0, 0xf0f0e0, + 0x60a0f0, 0xa0f060, 0xf060a0, 0x60f0a0, 0xa060f0, 0xf0a060, 0x6020f0, + 0x20f060, 0xf06020, 0x60f020, 0x2060f0, 0xf02060, 0x60f000, 0xf06000, + 0xf00060, 0x6000f0, 0x00f060, 0x0060f0, 0x60f060, 0xf06060, 0x6060f0, + 0xf060f0, 0x60f0f0, 0xf0f060, 0xa020f0, 0x20f0a0, 0xf0a020, 0xa0f020, + 0x20a0f0, 0xf020a0, 0xa0f000, 0xf0a000, 0xf000a0, 0xa000f0, 0x00f0a0, + 0x00a0f0, 0xa0f0a0, 0xf0a0a0, 0xa0a0f0, 0xf0a0f0, 0xa0f0f0, 0xf0f0a0, + 0x20f000, 0xf02000, 0xf00020, 0x2000f0, 0x00f020, 0x0020f0, 0x20f020, + 0xf02020, 0x2020f0, 0xf020f0, 0x20f0f0, 0xf0f020, 0xf0f0f0, 0xf00000, + 0x00f000, 0x0000f0, 0xf0f000, 0xf000f0, 0x00f0f0, 0xff80b0, 0x80b0ff, + 0xb0ff80, 0xffb080, 0x80ffb0, 0xb080ff, 0xffc0b0, 0xc0b0ff, 0xb0ffc0, + 0xffb0c0, 0xc0ffb0, 0xb0c0ff, 0xff40b0, 0x40b0ff, 0xb0ff40, 0xffb040, + 0x40ffb0, 0xb040ff, 0xffe0b0, 0xe0b0ff, 0xb0ffe0, 0xffb0e0, 0xe0ffb0, + 0xb0e0ff, 0xff60b0, 0x60b0ff, 0xb0ff60, 0xffb060, 0x60ffb0, 0xb060ff, + 0xffa0b0, 0xa0b0ff, 0xb0ffa0, 0xffb0a0, 0xa0ffb0, 0xb0a0ff, 0xff20b0, + 0x20b0ff, 0xb0ff20, 0xffb020, 0x20ffb0, 0xb020ff, 0xfff0b0, 0xf0b0ff, + 0xb0fff0, 0xffb0f0, 0xf0ffb0, 0xb0f0ff, 0xffb000, 0xb0ff00, 0xb000ff, + 0xff00b0, 0x00b0ff, 0x00ffb0, 0xffb0ff, 0xb0ffff, 0xffffb0, 0xb0ffb0, + 0xffb0b0, 0xb0b0ff, 0x80c0b0, 0xc0b080, 0xb080c0, 0x80b0c0, 0xc080b0, + 0xb0c080, 0x8040b0, 0x40b080, 0xb08040, 0x80b040, 0x4080b0, 0xb04080, + 0x80e0b0, 0xe0b080, 0xb080e0, 0x80b0e0, 0xe080b0, 0xb0e080, 0x8060b0, + 0x60b080, 0xb08060, 0x80b060, 0x6080b0, 0xb06080, 0x80a0b0, 0xa0b080, + 0xb080a0, 0x80b0a0, 0xa080b0, 0xb0a080, 0x8020b0, 0x20b080, 0xb08020, + 0x80b020, 0x2080b0, 0xb02080, 0x80f0b0, 0xf0b080, 0xb080f0, 0x80b0f0, + 0xf080b0, 0xb0f080, 0x80b000, 0xb08000, 0xb00080, 0x8000b0, 0x00b080, + 0x0080b0, 0x80b080, 0xb08080, 0x8080b0, 0xb080b0, 0x80b0b0, 0xb0b080, + 0xc040b0, 0x40b0c0, 0xb0c040, 0xc0b040, 0x40c0b0, 0xb040c0, 0xc0e0b0, + 0xe0b0c0, 0xb0c0e0, 0xc0b0e0, 0xe0c0b0, 0xb0e0c0, 0xc060b0, 0x60b0c0, + 0xb0c060, 0xc0b060, 0x60c0b0, 0xb060c0, 0xc0a0b0, 0xa0b0c0, 0xb0c0a0, + 0xc0b0a0, 0xa0c0b0, 0xb0a0c0, 0xc020b0, 0x20b0c0, 0xb0c020, 0xc0b020, + 0x20c0b0, 0xb020c0, 0xc0f0b0, 0xf0b0c0, 0xb0c0f0, 0xc0b0f0, 0xf0c0b0, + 0xb0f0c0, 0xc0b000, 0xb0c000, 0xb000c0, 0xc000b0, 0x00b0c0, 0x00c0b0, + 0xc0b0c0, 0xb0c0c0, 0xc0c0b0, 0xb0c0b0, 0xc0b0b0, 0xb0b0c0, 0x40e0b0, + 0xe0b040, 0xb040e0, 0x40b0e0, 0xe040b0, 0xb0e040, 0x4060b0, 0x60b040, + 0xb04060, 0x40b060, 0x6040b0, 0xb06040, 0x40a0b0, 0xa0b040, 0xb040a0, + 0x40b0a0, 0xa040b0, 0xb0a040, 0x4020b0, 0x20b040, 0xb04020, 0x40b020, + 0x2040b0, 0xb02040, 0x40f0b0, 0xf0b040, 0xb040f0, 0x40b0f0, 0xf040b0, + 0xb0f040, 0x40b000, 0xb04000, 0xb00040, 0x4000b0, 0x00b040, 0x0040b0, + 0x40b040, 0xb04040, 0x4040b0, 0xb040b0, 0x40b0b0, 0xb0b040, 0xe060b0, + 0x60b0e0, 0xb0e060, 0xe0b060, 0x60e0b0, 0xb060e0, 0xe0a0b0, 0xa0b0e0, + 0xb0e0a0, 0xe0b0a0, 0xa0e0b0, 0xb0a0e0, 0xe020b0, 0x20b0e0, 0xb0e020, + 0xe0b020, 0x20e0b0, 0xb020e0, 0xe0f0b0, 0xf0b0e0, 0xb0e0f0, 0xe0b0f0, + 0xf0e0b0, 0xb0f0e0, 0xe0b000, 0xb0e000, 0xb000e0, 0xe000b0, 0x00b0e0, + 0x00e0b0, 0xe0b0e0, 0xb0e0e0, 0xe0e0b0, 0xb0e0b0, 0xe0b0b0, 0xb0b0e0, + 0x60a0b0, 0xa0b060, 0xb060a0, 0x60b0a0, 0xa060b0, 0xb0a060, 0x6020b0, + 0x20b060, 0xb06020, 0x60b020, 0x2060b0, 0xb02060, 0x60f0b0, 0xf0b060, + 0xb060f0, 0x60b0f0, 0xf060b0, 0xb0f060, 0x60b000, 0xb06000, 0xb00060, + 0x6000b0, 0x00b060, 0x0060b0, 0x60b060, 0xb06060, 0x6060b0, 0xb060b0, + 0x60b0b0, 0xb0b060, 0xa020b0, 0x20b0a0, 0xb0a020, 0xa0b020, 0x20a0b0, + 0xb020a0, 0xa0f0b0, 0xf0b0a0, 0xb0a0f0, 0xa0b0f0, 0xf0a0b0, 0xb0f0a0, + 0xa0b000, 0xb0a000, 0xb000a0, 0xa000b0, 0x00b0a0, 0x00a0b0, 0xa0b0a0, + 0xb0a0a0, 0xa0a0b0, 0xb0a0b0, 0xa0b0b0, 0xb0b0a0, 0x20f0b0, 0xf0b020, + 0xb020f0, 0x20b0f0, 0xf020b0, 0xb0f020, 0x20b000, 0xb02000, 0xb00020, + 0x2000b0, 0x00b020, 0x0020b0, 0x20b020, 0xb02020, 0x2020b0, 0xb020b0, + 0x20b0b0, 0xb0b020, 0xf0b000, 0xb0f000, 0xb000f0, 0xf000b0, 0x00b0f0, + 0x00f0b0, 0xf0b0f0, 0xb0f0f0, 0xf0f0b0, 0xb0f0b0, 0xf0b0b0, 0xb0b0f0, + 0xb0b0b0, 0xb00000, 0x00b000, 0x0000b0, 0xb0b000, 0xb000b0, 0x00b0b0, + 0xff8050, 0x8050ff, 0x50ff80, 0xff5080, 0x80ff50, 0x5080ff, 0xffc050, + 0xc050ff, 0x50ffc0, 0xff50c0, 0xc0ff50, 0x50c0ff, 0xff4050, 0x4050ff, + 0x50ff40, 0xff5040, 0x40ff50, 0x5040ff, 0xffe050, 0xe050ff, 0x50ffe0, + 0xff50e0, 0xe0ff50, 0x50e0ff, 0xff6050, 0x6050ff, 0x50ff60, 0xff5060, + 0x60ff50, 0x5060ff, 0xffa050, 0xa050ff, 0x50ffa0, 0xff50a0, 0xa0ff50, + 0x50a0ff, 0xff2050, 0x2050ff, 0x50ff20, 0xff5020, 0x20ff50, 0x5020ff, + 0xfff050, 0xf050ff, 0x50fff0, 0xff50f0, 0xf0ff50, 0x50f0ff, 0xffb050, + 0xb050ff, 0x50ffb0, 0xff50b0, 0xb0ff50, 0x50b0ff, 0xff5000, 0x50ff00, + 0x5000ff, 0xff0050, 0x0050ff, 0x00ff50, 0xff50ff, 0x50ffff, 0xffff50, + 0x50ff50, 0xff5050, 0x5050ff, 0x80c050, 0xc05080, 0x5080c0, 0x8050c0, + 0xc08050, 0x50c080, 0x804050, 0x405080, 0x508040, 0x805040, 0x408050, + 0x504080, 0x80e050, 0xe05080, 0x5080e0, 0x8050e0, 0xe08050, 0x50e080, + 0x806050, 0x605080, 0x508060, 0x805060, 0x608050, 0x506080, 0x80a050, + 0xa05080, 0x5080a0, 0x8050a0, 0xa08050, 0x50a080, 0x802050, 0x205080, + 0x508020, 0x805020, 0x208050, 0x502080, 0x80f050, 0xf05080, 0x5080f0, + 0x8050f0, 0xf08050, 0x50f080, 0x80b050, 0xb05080, 0x5080b0, 0x8050b0, + 0xb08050, 0x50b080, 0x805000, 0x508000, 0x500080, 0x800050, 0x005080, + 0x008050, 0x805080, 0x508080, 0x808050, 0x508050, 0x805050, 0x505080, + 0xc04050, 0x4050c0, 0x50c040, 0xc05040, 0x40c050, 0x5040c0, 0xc0e050, + 0xe050c0, 0x50c0e0, 0xc050e0, 0xe0c050, 0x50e0c0, 0xc06050, 0x6050c0, + 0x50c060, 0xc05060, 0x60c050, 0x5060c0, 0xc0a050, 0xa050c0, 0x50c0a0, + 0xc050a0, 0xa0c050, 0x50a0c0, 0xc02050, 0x2050c0, 0x50c020, 0xc05020, + 0x20c050, 0x5020c0, 0xc0f050, 0xf050c0, 0x50c0f0, 0xc050f0, 0xf0c050, + 0x50f0c0, 0xc0b050, 0xb050c0, 0x50c0b0, 0xc050b0, 0xb0c050, 0x50b0c0, + 0xc05000, 0x50c000, 0x5000c0, 0xc00050, 0x0050c0, 0x00c050, 0xc050c0, + 0x50c0c0, 0xc0c050, 0x50c050, 0xc05050, 0x5050c0, 0x40e050, 0xe05040, + 0x5040e0, 0x4050e0, 0xe04050, 0x50e040, 0x406050, 0x605040, 0x504060, + 0x405060, 0x604050, 0x506040, 0x40a050, 0xa05040, 0x5040a0, 0x4050a0, + 0xa04050, 0x50a040, 0x402050, 0x205040, 0x504020, 0x405020, 0x204050, + 0x502040, 0x40f050, 0xf05040, 0x5040f0, 0x4050f0, 0xf04050, 0x50f040, + 0x40b050, 0xb05040, 0x5040b0, 0x4050b0, 0xb04050, 0x50b040, 0x405000, + 0x504000, 0x500040, 0x400050, 0x005040, 0x004050, 0x405040, 0x504040, + 0x404050, 0x504050, 0x405050, 0x505040, 0xe06050, 0x6050e0, 0x50e060, + 0xe05060, 0x60e050, 0x5060e0, 0xe0a050, 0xa050e0, 0x50e0a0, 0xe050a0, + 0xa0e050, 0x50a0e0, 0xe02050, 0x2050e0, 0x50e020, 0xe05020, 0x20e050, + 0x5020e0, 0xe0f050, 0xf050e0, 0x50e0f0, 0xe050f0, 0xf0e050, 0x50f0e0, + 0xe0b050, 0xb050e0, 0x50e0b0, 0xe050b0, 0xb0e050, 0x50b0e0, 0xe05000, + 0x50e000, 0x5000e0, 0xe00050, 0x0050e0, 0x00e050, 0xe050e0, 0x50e0e0, + 0xe0e050, 0x50e050, 0xe05050, 0x5050e0, 0x60a050, 0xa05060, 0x5060a0, + 0x6050a0, 0xa06050, 0x50a060, 0x602050, 0x205060, 0x506020, 0x605020, + 0x206050, 0x502060, 0x60f050, 0xf05060, 0x5060f0, 0x6050f0, 0xf06050, + 0x50f060, 0x60b050, 0xb05060, 0x5060b0, 0x6050b0, 0xb06050, 0x50b060, + 0x605000, 0x506000, 0x500060, 0x600050, 0x005060, 0x006050, 0x605060, + 0x506060, 0x606050, 0x506050, 0x605050, 0x505060, 0xa02050, 0x2050a0, + 0x50a020, 0xa05020, 0x20a050, 0x5020a0, 0xa0f050, 0xf050a0, 0x50a0f0, + 0xa050f0, 0xf0a050, 0x50f0a0, 0xa0b050, 0xb050a0, 0x50a0b0, 0xa050b0, + 0xb0a050, 0x50b0a0, 0xa05000, 0x50a000, 0x5000a0, 0xa00050, 0x0050a0, + 0x00a050, 0xa050a0, 0x50a0a0, 0xa0a050, 0x50a050, 0xa05050, 0x5050a0, + 0x20f050, 0xf05020, 0x5020f0, 0x2050f0, 0xf02050, 0x50f020, 0x20b050, + 0xb05020, 0x5020b0, 0x2050b0, 0xb02050, 0x50b020, 0x205000, 0x502000, + 0x500020, 0x200050, 0x005020, 0x002050, 0x205020, 0x502020, 0x202050, + 0x502050, 0x205050, 0x505020, 0xf0b050, 0xb050f0, 0x50f0b0, 0xf050b0, + 0xb0f050, 0x50b0f0, 0xf05000, 0x50f000, 0x5000f0, 0xf00050, 0x0050f0, + 0x00f050, 0xf050f0, 0x50f0f0, 0xf0f050, 0x50f050, 0xf05050, 0x5050f0, + 0xb05000, 0x50b000, 0x5000b0, 0xb00050, 0x0050b0, 0x00b050, 0xb050b0, + 0x50b0b0, 0xb0b050, 0x50b050, 0xb05050, 0x5050b0, 0x505050, 0x500000, + 0x005000, 0x000050, 0x505000, 0x500050, 0x005050, 0xff80d0, 0x80d0ff, + 0xd0ff80, 0xffd080, 0x80ffd0, 0xd080ff, 0xffc0d0, 0xc0d0ff, 0xd0ffc0, + 0xffd0c0, 0xc0ffd0, 0xd0c0ff, 0xff40d0, 0x40d0ff, 0xd0ff40, 0xffd040, + 0x40ffd0, 0xd040ff, 0xffe0d0, 0xe0d0ff, 0xd0ffe0, 0xffd0e0, 0xe0ffd0, + 0xd0e0ff, 0xff60d0, 0x60d0ff, 0xd0ff60, 0xffd060, 0x60ffd0, 0xd060ff, + 0xffa0d0, 0xa0d0ff, 0xd0ffa0, 0xffd0a0, 0xa0ffd0, 0xd0a0ff, 0xff20d0, + 0x20d0ff, 0xd0ff20, 0xffd020, 0x20ffd0, 0xd020ff, 0xfff0d0, 0xf0d0ff, + 0xd0fff0, 0xffd0f0, 0xf0ffd0, 0xd0f0ff, 0xffb0d0, 0xb0d0ff, 0xd0ffb0, + 0xffd0b0, 0xb0ffd0, 0xd0b0ff, 0xff50d0, 0x50d0ff, 0xd0ff50, 0xffd050, + 0x50ffd0, 0xd050ff, 0xffd000, 0xd0ff00, 0xd000ff, 0xff00d0, 0x00d0ff, + 0x00ffd0, 0xffd0ff, 0xd0ffff, 0xffffd0, 0xd0ffd0, 0xffd0d0, 0xd0d0ff, + 0x80c0d0, 0xc0d080, 0xd080c0, 0x80d0c0, 0xc080d0, 0xd0c080, 0x8040d0, + 0x40d080, 0xd08040, 0x80d040, 0x4080d0, 0xd04080, 0x80e0d0, 0xe0d080, + 0xd080e0, 0x80d0e0, 0xe080d0, 0xd0e080, 0x8060d0, 0x60d080, 0xd08060, + 0x80d060, 0x6080d0, 0xd06080, 0x80a0d0, 0xa0d080, 0xd080a0, 0x80d0a0, + 0xa080d0, 0xd0a080, 0x8020d0, 0x20d080, 0xd08020, 0x80d020, 0x2080d0, + 0xd02080, 0x80f0d0, 0xf0d080, 0xd080f0, 0x80d0f0, 0xf080d0, 0xd0f080, + 0x80b0d0, 0xb0d080, 0xd080b0, 0x80d0b0, 0xb080d0, 0xd0b080, 0x8050d0, + 0x50d080, 0xd08050, 0x80d050, 0x5080d0, 0xd05080, 0x80d000, 0xd08000, + 0xd00080, 0x8000d0, 0x00d080, 0x0080d0, 0x80d080, 0xd08080, 0x8080d0, + 0xd080d0, 0x80d0d0, 0xd0d080, 0xc040d0, 0x40d0c0, 0xd0c040, 0xc0d040, + 0x40c0d0, 0xd040c0, 0xc0e0d0, 0xe0d0c0, 0xd0c0e0, 0xc0d0e0, 0xe0c0d0, + 0xd0e0c0, 0xc060d0, 0x60d0c0, 0xd0c060, 0xc0d060, 0x60c0d0, 0xd060c0, + 0xc0a0d0, 0xa0d0c0, 0xd0c0a0, 0xc0d0a0, 0xa0c0d0, 0xd0a0c0, 0xc020d0, + 0x20d0c0, 0xd0c020, 0xc0d020, 0x20c0d0, 0xd020c0, 0xc0f0d0, 0xf0d0c0, + 0xd0c0f0, 0xc0d0f0, 0xf0c0d0, 0xd0f0c0, 0xc0b0d0, 0xb0d0c0, 0xd0c0b0, + 0xc0d0b0, 0xb0c0d0, 0xd0b0c0, 0xc050d0, 0x50d0c0, 0xd0c050, 0xc0d050, + 0x50c0d0, 0xd050c0, 0xc0d000, 0xd0c000, 0xd000c0, 0xc000d0, 0x00d0c0, + 0x00c0d0, 0xc0d0c0, 0xd0c0c0, 0xc0c0d0, 0xd0c0d0, 0xc0d0d0, 0xd0d0c0, + 0x40e0d0, 0xe0d040, 0xd040e0, 0x40d0e0, 0xe040d0, 0xd0e040, 0x4060d0, + 0x60d040, 0xd04060, 0x40d060, 0x6040d0, 0xd06040, 0x40a0d0, 0xa0d040, + 0xd040a0, 0x40d0a0, 0xa040d0, 0xd0a040, 0x4020d0, 0x20d040, 0xd04020, + 0x40d020, 0x2040d0, 0xd02040, 0x40f0d0, 0xf0d040, 0xd040f0, 0x40d0f0, + 0xf040d0, 0xd0f040, 0x40b0d0, 0xb0d040, 0xd040b0, 0x40d0b0, 0xb040d0, + 0xd0b040, 0x4050d0, 0x50d040, 0xd04050, 0x40d050, 0x5040d0, 0xd05040, + 0x40d000, 0xd04000, 0xd00040, 0x4000d0, 0x00d040, 0x0040d0, 0x40d040, + 0xd04040, 0x4040d0, 0xd040d0, 0x40d0d0, 0xd0d040, 0xe060d0, 0x60d0e0, + 0xd0e060, 0xe0d060, 0x60e0d0, 0xd060e0, 0xe0a0d0, 0xa0d0e0, 0xd0e0a0, + 0xe0d0a0, 0xa0e0d0, 0xd0a0e0, 0xe020d0, 0x20d0e0, 0xd0e020, 0xe0d020, + 0x20e0d0, 0xd020e0, 0xe0f0d0, 0xf0d0e0, 0xd0e0f0, 0xe0d0f0, 0xf0e0d0, + 0xd0f0e0, 0xe0b0d0, 0xb0d0e0, 0xd0e0b0, 0xe0d0b0, 0xb0e0d0, 0xd0b0e0, + 0xe050d0, 0x50d0e0, 0xd0e050, 0xe0d050, 0x50e0d0, 0xd050e0, 0xe0d000, + 0xd0e000, 0xd000e0, 0xe000d0, 0x00d0e0, 0x00e0d0, 0xe0d0e0, 0xd0e0e0, + 0xe0e0d0, 0xd0e0d0, 0xe0d0d0, 0xd0d0e0, 0x60a0d0, 0xa0d060, 0xd060a0, + 0x60d0a0, 0xa060d0, 0xd0a060, 0x6020d0, 0x20d060, 0xd06020, 0x60d020, + 0x2060d0, 0xd02060, 0x60f0d0, 0xf0d060, 0xd060f0, 0x60d0f0, 0xf060d0, + 0xd0f060, 0x60b0d0, 0xb0d060, 0xd060b0, 0x60d0b0, 0xb060d0, 0xd0b060, + 0x6050d0, 0x50d060, 0xd06050, 0x60d050, 0x5060d0, 0xd05060, 0x60d000, + 0xd06000, 0xd00060, 0x6000d0, 0x00d060, 0x0060d0, 0x60d060, 0xd06060, + 0x6060d0, 0xd060d0, 0x60d0d0, 0xd0d060, 0xa020d0, 0x20d0a0, 0xd0a020, + 0xa0d020, 0x20a0d0, 0xd020a0, 0xa0f0d0, 0xf0d0a0, 0xd0a0f0, 0xa0d0f0, + 0xf0a0d0, 0xd0f0a0, 0xa0b0d0, 0xb0d0a0, 0xd0a0b0, 0xa0d0b0, 0xb0a0d0, + 0xd0b0a0, 0xa050d0, 0x50d0a0, 0xd0a050, 0xa0d050, 0x50a0d0, 0xd050a0, + 0xa0d000, 0xd0a000, 0xd000a0, 0xa000d0, 0x00d0a0, 0x00a0d0, 0xa0d0a0, + 0xd0a0a0, 0xa0a0d0, 0xd0a0d0, 0xa0d0d0, 0xd0d0a0, 0x20f0d0, 0xf0d020, + 0xd020f0, 0x20d0f0, 0xf020d0, 0xd0f020, 0x20b0d0, 0xb0d020, 0xd020b0, + 0x20d0b0, 0xb020d0, 0xd0b020, 0x2050d0, 0x50d020, 0xd02050, 0x20d050, + 0x5020d0, 0xd05020, 0x20d000, 0xd02000, 0xd00020, 0x2000d0, 0x00d020, + 0x0020d0, 0x20d020, 0xd02020, 0x2020d0, 0xd020d0, 0x20d0d0, 0xd0d020, + 0xf0b0d0, 0xb0d0f0, 0xd0f0b0, 0xf0d0b0, 0xb0f0d0, 0xd0b0f0, 0xf050d0, + 0x50d0f0, 0xd0f050, 0xf0d050, 0x50f0d0, 0xd050f0, 0xf0d000, 0xd0f000, + 0xd000f0, 0xf000d0, 0x00d0f0, 0x00f0d0, 0xf0d0f0, 0xd0f0f0, 0xf0f0d0, + 0xd0f0d0, 0xf0d0d0, 0xd0d0f0, 0xb050d0, 0x50d0b0, 0xd0b050, 0xb0d050, + 0x50b0d0, 0xd050b0, 0xb0d000, 0xd0b000, 0xd000b0, 0xb000d0, 0x00d0b0, + 0x00b0d0, 0xb0d0b0, 0xd0b0b0, 0xb0b0d0, 0xd0b0d0, 0xb0d0d0, 0xd0d0b0, + 0x50d000, 0xd05000, 0xd00050, 0x5000d0, 0x00d050, 0x0050d0, 0x50d050, + 0xd05050, 0x5050d0, 0xd050d0, 0x50d0d0, 0xd0d050, 0xd0d0d0, 0xd00000, + 0x00d000, 0x0000d0, 0xd0d000, 0xd000d0, 0x00d0d0, 0xff8070, 0x8070ff, + 0x70ff80, 0xff7080, 0x80ff70, 0x7080ff, 0xffc070, 0xc070ff, 0x70ffc0, + 0xff70c0, 0xc0ff70, 0x70c0ff, 0xff4070, 0x4070ff, 0x70ff40, 0xff7040, + 0x40ff70, 0x7040ff, 0xffe070, 0xe070ff, 0x70ffe0, 0xff70e0, 0xe0ff70, + 0x70e0ff, 0xff6070, 0x6070ff, 0x70ff60, 0xff7060, 0x60ff70, 0x7060ff, + 0xffa070, 0xa070ff, 0x70ffa0, 0xff70a0, 0xa0ff70, 0x70a0ff, 0xff2070, + 0x2070ff, 0x70ff20, 0xff7020, 0x20ff70, 0x7020ff, 0xfff070, 0xf070ff, + 0x70fff0, 0xff70f0, 0xf0ff70, 0x70f0ff, 0xffb070, 0xb070ff, 0x70ffb0, + 0xff70b0, 0xb0ff70, 0x70b0ff, 0xff5070, 0x5070ff, 0x70ff50, 0xff7050, + 0x50ff70, 0x7050ff, 0xffd070, 0xd070ff, 0x70ffd0, 0xff70d0, 0xd0ff70, + 0x70d0ff, 0xff7000, 0x70ff00, 0x7000ff, 0xff0070, 0x0070ff, 0x00ff70, + 0xff70ff, 0x70ffff, 0xffff70, 0x70ff70, 0xff7070, 0x7070ff, 0x80c070, + 0xc07080, 0x7080c0, 0x8070c0, 0xc08070, 0x70c080, 0x804070, 0x407080, + 0x708040, 0x807040, 0x408070, 0x704080, 0x80e070, 0xe07080, 0x7080e0, + 0x8070e0, 0xe08070, 0x70e080, 0x806070, 0x607080, 0x708060, 0x807060, + 0x608070, 0x706080, 0x80a070, 0xa07080, 0x7080a0, 0x8070a0, 0xa08070, + 0x70a080, 0x802070, 0x207080, 0x708020, 0x807020, 0x208070, 0x702080, + 0x80f070, 0xf07080, 0x7080f0, 0x8070f0, 0xf08070, 0x70f080, 0x80b070, + 0xb07080, 0x7080b0, 0x8070b0, 0xb08070, 0x70b080, 0x805070, 0x507080, + 0x708050, 0x807050, 0x508070, 0x705080, 0x80d070, 0xd07080, 0x7080d0, + 0x8070d0, 0xd08070, 0x70d080, 0x807000, 0x708000, 0x700080, 0x800070, + 0x007080, 0x008070, 0x807080, 0x708080, 0x808070, 0x708070, 0x807070, + 0x707080, 0xc04070, 0x4070c0, 0x70c040, 0xc07040, 0x40c070, 0x7040c0, + 0xc0e070, 0xe070c0, 0x70c0e0, 0xc070e0, 0xe0c070, 0x70e0c0, 0xc06070, + 0x6070c0, 0x70c060, 0xc07060, 0x60c070, 0x7060c0, 0xc0a070, 0xa070c0, + 0x70c0a0, 0xc070a0, 0xa0c070, 0x70a0c0, 0xc02070, 0x2070c0, 0x70c020, + 0xc07020, 0x20c070, 0x7020c0, 0xc0f070, 0xf070c0, 0x70c0f0, 0xc070f0, + 0xf0c070, 0x70f0c0, 0xc0b070, 0xb070c0, 0x70c0b0, 0xc070b0, 0xb0c070, + 0x70b0c0, 0xc05070, 0x5070c0, 0x70c050, 0xc07050, 0x50c070, 0x7050c0, + 0xc0d070, 0xd070c0, 0x70c0d0, 0xc070d0, 0xd0c070, 0x70d0c0, 0xc07000, + 0x70c000, 0x7000c0, 0xc00070, 0x0070c0, 0x00c070, 0xc070c0, 0x70c0c0, + 0xc0c070, 0x70c070, 0xc07070, 0x7070c0, 0x40e070, 0xe07040, 0x7040e0, + 0x4070e0, 0xe04070, 0x70e040, 0x406070, 0x607040, 0x704060, 0x407060, + 0x604070, 0x706040, 0x40a070, 0xa07040, 0x7040a0, 0x4070a0, 0xa04070, + 0x70a040, 0x402070, 0x207040, 0x704020, 0x407020, 0x204070, 0x702040, + 0x40f070, 0xf07040, 0x7040f0, 0x4070f0, 0xf04070, 0x70f040, 0x40b070, + 0xb07040, 0x7040b0, 0x4070b0, 0xb04070, 0x70b040, 0x405070, 0x507040, + 0x704050, 0x407050, 0x504070, 0x705040, 0x40d070, 0xd07040, 0x7040d0, + 0x4070d0, 0xd04070, 0x70d040, 0x407000, 0x704000, 0x700040, 0x400070, + 0x007040, 0x004070, 0x407040, 0x704040, 0x404070, 0x704070, 0x407070, + 0x707040, 0xe06070, 0x6070e0, 0x70e060, 0xe07060, 0x60e070, 0x7060e0, + 0xe0a070, 0xa070e0, 0x70e0a0, 0xe070a0, 0xa0e070, 0x70a0e0, 0xe02070, + 0x2070e0, 0x70e020, 0xe07020, 0x20e070, 0x7020e0, 0xe0f070, 0xf070e0, + 0x70e0f0, 0xe070f0, 0xf0e070, 0x70f0e0, 0xe0b070, 0xb070e0, 0x70e0b0, + 0xe070b0, 0xb0e070, 0x70b0e0, 0xe05070, 0x5070e0, 0x70e050, 0xe07050, + 0x50e070, 0x7050e0, 0xe0d070, 0xd070e0, 0x70e0d0, 0xe070d0, 0xd0e070, + 0x70d0e0, 0xe07000, 0x70e000, 0x7000e0, 0xe00070, 0x0070e0, 0x00e070, + 0xe070e0, 0x70e0e0, 0xe0e070, 0x70e070, 0xe07070, 0x7070e0, 0x60a070, + 0xa07060, 0x7060a0, 0x6070a0, 0xa06070, 0x70a060, 0x602070, 0x207060, + 0x706020, 0x607020, 0x206070, 0x702060, 0x60f070, 0xf07060, 0x7060f0, + 0x6070f0, 0xf06070, 0x70f060, 0x60b070, 0xb07060, 0x7060b0, 0x6070b0, + 0xb06070, 0x70b060, 0x605070, 0x507060, 0x706050, 0x607050, 0x506070, + 0x705060, 0x60d070, 0xd07060, 0x7060d0, 0x6070d0, 0xd06070, 0x70d060, + 0x607000, 0x706000, 0x700060, 0x600070, 0x007060, 0x006070, 0x607060, + 0x706060, 0x606070, 0x706070, 0x607070, 0x707060, 0xa02070, 0x2070a0, + 0x70a020, 0xa07020, 0x20a070, 0x7020a0, 0xa0f070, 0xf070a0, 0x70a0f0, + 0xa070f0, 0xf0a070, 0x70f0a0, 0xa0b070, 0xb070a0, 0x70a0b0, 0xa070b0, + 0xb0a070, 0x70b0a0, 0xa05070, 0x5070a0, 0x70a050, 0xa07050, 0x50a070, + 0x7050a0, 0xa0d070, 0xd070a0, 0x70a0d0, 0xa070d0, 0xd0a070, 0x70d0a0, + 0xa07000, 0x70a000, 0x7000a0, 0xa00070, 0x0070a0, 0x00a070, 0xa070a0, + 0x70a0a0, 0xa0a070, 0x70a070, 0xa07070, 0x7070a0, 0x20f070, 0xf07020, + 0x7020f0, 0x2070f0, 0xf02070, 0x70f020, 0x20b070, 0xb07020, 0x7020b0, + 0x2070b0, 0xb02070, 0x70b020, 0x205070, 0x507020, 0x702050, 0x207050, + 0x502070, 0x705020, 0x20d070, 0xd07020, 0x7020d0, 0x2070d0, 0xd02070, + 0x70d020, 0x207000, 0x702000, 0x700020, 0x200070, 0x007020, 0x002070, + 0x207020, 0x702020, 0x202070, 0x702070, 0x207070, 0x707020, 0xf0b070, + 0xb070f0, 0x70f0b0, 0xf070b0, 0xb0f070, 0x70b0f0, 0xf05070, 0x5070f0, + 0x70f050, 0xf07050, 0x50f070, 0x7050f0, 0xf0d070, 0xd070f0, 0x70f0d0, + 0xf070d0, 0xd0f070, 0x70d0f0, 0xf07000, 0x70f000, 0x7000f0, 0xf00070, + 0x0070f0, 0x00f070, 0xf070f0, 0x70f0f0, 0xf0f070, 0x70f070, 0xf07070, + 0x7070f0, 0xb05070, 0x5070b0, 0x70b050, 0xb07050, 0x50b070, 0x7050b0, + 0xb0d070, 0xd070b0, 0x70b0d0, 0xb070d0, 0xd0b070, 0x70d0b0, 0xb07000, + 0x70b000, 0x7000b0, 0xb00070, 0x0070b0, 0x00b070, 0xb070b0, 0x70b0b0, + 0xb0b070, 0x70b070, 0xb07070, 0x7070b0, 0x50d070, 0xd07050, 0x7050d0, + 0x5070d0, 0xd05070, 0x70d050, 0x507000, 0x705000, 0x700050, 0x500070, + 0x007050, 0x005070, 0x507050, 0x705050, 0x505070, 0x705070, 0x507070, + 0x707050, 0xd07000, 0x70d000, 0x7000d0, 0xd00070, 0x0070d0, 0x00d070, + 0xd070d0, 0x70d0d0, 0xd0d070, 0x70d070, 0xd07070, 0x7070d0, 0x707070, + 0x700000, 0x007000, 0x000070, 0x707000, 0x700070, 0x007070, 0xff8030, + 0x8030ff, 0x30ff80, 0xff3080, 0x80ff30, 0x3080ff, 0xffc030, 0xc030ff, + 0x30ffc0, 0xff30c0, 0xc0ff30, 0x30c0ff, 0xff4030, 0x4030ff, 0x30ff40, + 0xff3040, 0x40ff30, 0x3040ff, 0xffe030, 0xe030ff, 0x30ffe0, 0xff30e0, + 0xe0ff30, 0x30e0ff, 0xff6030, 0x6030ff, 0x30ff60, 0xff3060, 0x60ff30, + 0x3060ff, 0xffa030, 0xa030ff, 0x30ffa0, 0xff30a0, 0xa0ff30, 0x30a0ff, + 0xff2030, 0x2030ff, 0x30ff20, 0xff3020, 0x20ff30, 0x3020ff, 0xfff030, + 0xf030ff, 0x30fff0, 0xff30f0, 0xf0ff30, 0x30f0ff, 0xffb030, 0xb030ff, + 0x30ffb0, 0xff30b0, 0xb0ff30, 0x30b0ff, 0xff5030, 0x5030ff, 0x30ff50, + 0xff3050, 0x50ff30, 0x3050ff, 0xffd030, 0xd030ff, 0x30ffd0, 0xff30d0, + 0xd0ff30, 0x30d0ff, 0xff7030, 0x7030ff, 0x30ff70, 0xff3070, 0x70ff30, + 0x3070ff, 0xff3000, 0x30ff00, 0x3000ff, 0xff0030, 0x0030ff, 0x00ff30, + 0xff30ff, 0x30ffff, 0xffff30, 0x30ff30, 0xff3030, 0x3030ff, 0x80c030, + 0xc03080, 0x3080c0, 0x8030c0, 0xc08030, 0x30c080, 0x804030, 0x403080, + 0x308040, 0x803040, 0x408030, 0x304080, 0x80e030, 0xe03080, 0x3080e0, + 0x8030e0, 0xe08030, 0x30e080, 0x806030, 0x603080, 0x308060, 0x803060, + 0x608030, 0x306080, 0x80a030, 0xa03080, 0x3080a0, 0x8030a0, 0xa08030, + 0x30a080, 0x802030, 0x203080, 0x308020, 0x803020, 0x208030, 0x302080, + 0x80f030, 0xf03080, 0x3080f0, 0x8030f0, 0xf08030, 0x30f080, 0x80b030, + 0xb03080, 0x3080b0, 0x8030b0, 0xb08030, 0x30b080, 0x805030, 0x503080, + 0x308050, 0x803050, 0x508030, 0x305080, 0x80d030, 0xd03080, 0x3080d0, + 0x8030d0, 0xd08030, 0x30d080, 0x807030, 0x703080, 0x308070, 0x803070, + 0x708030, 0x307080, 0x803000, 0x308000, 0x300080, 0x800030, 0x003080, + 0x008030, 0x803080, 0x308080, 0x808030, 0x308030, 0x803030, 0x303080, + 0xc04030, 0x4030c0, 0x30c040, 0xc03040, 0x40c030, 0x3040c0, 0xc0e030, + 0xe030c0, 0x30c0e0, 0xc030e0, 0xe0c030, 0x30e0c0, 0xc06030, 0x6030c0, + 0x30c060, 0xc03060, 0x60c030, 0x3060c0, 0xc0a030, 0xa030c0, 0x30c0a0, + 0xc030a0, 0xa0c030, 0x30a0c0, 0xc02030, 0x2030c0, 0x30c020, 0xc03020, + 0x20c030, 0x3020c0, 0xc0f030, 0xf030c0, 0x30c0f0, 0xc030f0, 0xf0c030, + 0x30f0c0, 0xc0b030, 0xb030c0, 0x30c0b0, 0xc030b0, 0xb0c030, 0x30b0c0, + 0xc05030, 0x5030c0, 0x30c050, 0xc03050, 0x50c030, 0x3050c0, 0xc0d030, + 0xd030c0, 0x30c0d0, 0xc030d0, 0xd0c030, 0x30d0c0, 0xc07030, 0x7030c0, + 0x30c070, 0xc03070, 0x70c030, 0x3070c0, 0xc03000, 0x30c000, 0x3000c0, + 0xc00030, 0x0030c0, 0x00c030, 0xc030c0, 0x30c0c0, 0xc0c030, 0x30c030, + 0xc03030, 0x3030c0, 0x40e030, 0xe03040, 0x3040e0, 0x4030e0, 0xe04030, + 0x30e040, 0x406030, 0x603040, 0x304060, 0x403060, 0x604030, 0x306040, + 0x40a030, 0xa03040, 0x3040a0, 0x4030a0, 0xa04030, 0x30a040, 0x402030, + 0x203040, 0x304020, 0x403020, 0x204030, 0x302040, 0x40f030, 0xf03040, + 0x3040f0, 0x4030f0, 0xf04030, 0x30f040, 0x40b030, 0xb03040, 0x3040b0, + 0x4030b0, 0xb04030, 0x30b040, 0x405030, 0x503040, 0x304050, 0x403050, + 0x504030, 0x305040, 0x40d030, 0xd03040, 0x3040d0, 0x4030d0, 0xd04030, + 0x30d040, 0x407030, 0x703040, 0x304070, 0x403070, 0x704030, 0x307040, + 0x403000, 0x304000, 0x300040, 0x400030, 0x003040, 0x004030, 0x403040, + 0x304040, 0x404030, 0x304030, 0x403030, 0x303040, 0xe06030, 0x6030e0, + 0x30e060, 0xe03060, 0x60e030, 0x3060e0, 0xe0a030, 0xa030e0, 0x30e0a0, + 0xe030a0, 0xa0e030, 0x30a0e0, 0xe02030, 0x2030e0, 0x30e020, 0xe03020, + 0x20e030, 0x3020e0, 0xe0f030, 0xf030e0, 0x30e0f0, 0xe030f0, 0xf0e030, + 0x30f0e0, 0xe0b030, 0xb030e0, 0x30e0b0, 0xe030b0, 0xb0e030, 0x30b0e0, + 0xe05030, 0x5030e0, 0x30e050, 0xe03050, 0x50e030, 0x3050e0, 0xe0d030, + 0xd030e0, 0x30e0d0, 0xe030d0, 0xd0e030, 0x30d0e0, 0xe07030, 0x7030e0, + 0x30e070, 0xe03070, 0x70e030, 0x3070e0, 0xe03000, 0x30e000, 0x3000e0, + 0xe00030, 0x0030e0, 0x00e030, 0xe030e0, 0x30e0e0, 0xe0e030, 0x30e030, + 0xe03030, 0x3030e0, 0x60a030, 0xa03060, 0x3060a0, 0x6030a0, 0xa06030, + 0x30a060, 0x602030, 0x203060, 0x306020, 0x603020, 0x206030, 0x302060, + 0x60f030, 0xf03060, 0x3060f0, 0x6030f0, 0xf06030, 0x30f060, 0x60b030, + 0xb03060, 0x3060b0, 0x6030b0, 0xb06030, 0x30b060, 0x605030, 0x503060, + 0x306050, 0x603050, 0x506030, 0x305060, 0x60d030, 0xd03060, 0x3060d0, + 0x6030d0, 0xd06030, 0x30d060, 0x607030, 0x703060, 0x306070, 0x603070, + 0x706030, 0x307060, 0x603000, 0x306000, 0x300060, 0x600030, 0x003060, + 0x006030, 0x603060, 0x306060, 0x606030, 0x306030, 0x603030, 0x303060, + 0xa02030, 0x2030a0, 0x30a020, 0xa03020, 0x20a030, 0x3020a0, 0xa0f030, + 0xf030a0, 0x30a0f0, 0xa030f0, 0xf0a030, 0x30f0a0, 0xa0b030, 0xb030a0, + 0x30a0b0, 0xa030b0, 0xb0a030, 0x30b0a0, 0xa05030, 0x5030a0, 0x30a050, + 0xa03050, 0x50a030, 0x3050a0, 0xa0d030, 0xd030a0, 0x30a0d0, 0xa030d0, + 0xd0a030, 0x30d0a0, 0xa07030, 0x7030a0, 0x30a070, 0xa03070, 0x70a030, + 0x3070a0, 0xa03000, 0x30a000, 0x3000a0, 0xa00030, 0x0030a0, 0x00a030, + 0xa030a0, 0x30a0a0, 0xa0a030, 0x30a030, 0xa03030, 0x3030a0, 0x20f030, + 0xf03020, 0x3020f0, 0x2030f0, 0xf02030, 0x30f020, 0x20b030, 0xb03020, + 0x3020b0, 0x2030b0, 0xb02030, 0x30b020, 0x205030, 0x503020, 0x302050, + 0x203050, 0x502030, 0x305020, 0x20d030, 0xd03020, 0x3020d0, 0x2030d0, + 0xd02030, 0x30d020, 0x207030, 0x703020, 0x302070, 0x203070, 0x702030, + 0x307020, 0x203000, 0x302000, 0x300020, 0x200030, 0x003020, 0x002030, + 0x203020, 0x302020, 0x202030, 0x302030, 0x203030, 0x303020, 0xf0b030, + 0xb030f0, 0x30f0b0, 0xf030b0, 0xb0f030, 0x30b0f0, 0xf05030, 0x5030f0, + 0x30f050, 0xf03050, 0x50f030, 0x3050f0, 0xf0d030, 0xd030f0, 0x30f0d0, + 0xf030d0, 0xd0f030, 0x30d0f0, 0xf07030, 0x7030f0, 0x30f070, 0xf03070, + 0x70f030, 0x3070f0, 0xf03000, 0x30f000, 0x3000f0, 0xf00030, 0x0030f0, + 0x00f030, 0xf030f0, 0x30f0f0, 0xf0f030, 0x30f030, 0xf03030, 0x3030f0, + 0xb05030, 0x5030b0, 0x30b050, 0xb03050, 0x50b030, 0x3050b0, 0xb0d030, + 0xd030b0, 0x30b0d0, 0xb030d0, 0xd0b030, 0x30d0b0, 0xb07030, 0x7030b0, + 0x30b070, 0xb03070, 0x70b030, 0x3070b0, 0xb03000, 0x30b000, 0x3000b0, + 0xb00030, 0x0030b0, 0x00b030, 0xb030b0, 0x30b0b0, 0xb0b030, 0x30b030, + 0xb03030, 0x3030b0, 0x50d030, 0xd03050, 0x3050d0, 0x5030d0, 0xd05030, + 0x30d050, 0x507030, 0x703050, 0x305070, 0x503070, 0x705030, 0x307050, + 0x503000, 0x305000, 0x300050, 0x500030, 0x003050, 0x005030, 0x503050, + 0x305050, 0x505030, 0x305030, 0x503030, 0x303050, 0xd07030, 0x7030d0, + 0x30d070, 0xd03070, 0x70d030, 0x3070d0, 0xd03000, 0x30d000, 0x3000d0, + 0xd00030, 0x0030d0, 0x00d030, 0xd030d0, 0x30d0d0, 0xd0d030, 0x30d030, + 0xd03030, 0x3030d0, 0x703000, 0x307000, 0x300070, 0x700030, 0x003070, + 0x007030, 0x703070, 0x307070, 0x707030, 0x307030, 0x703030, 0x303070, + 0x303030, 0x300000, 0x003000, 0x000030, 0x303000, 0x300030, 0x003030, + 0xff8090, 0x8090ff, 0x90ff80, 0xff9080, 0x80ff90, 0x9080ff, 0xffc090, + 0xc090ff, 0x90ffc0, 0xff90c0, 0xc0ff90, 0x90c0ff, 0xff4090, 0x4090ff, + 0x90ff40, 0xff9040, 0x40ff90, 0x9040ff, 0xffe090, 0xe090ff, 0x90ffe0, + 0xff90e0, 0xe0ff90, 0x90e0ff, 0xff6090, 0x6090ff, 0x90ff60, 0xff9060, + 0x60ff90, 0x9060ff, 0xffa090, 0xa090ff, 0x90ffa0, 0xff90a0, 0xa0ff90, + 0x90a0ff, 0xff2090, 0x2090ff, 0x90ff20, 0xff9020, 0x20ff90, 0x9020ff, + 0xfff090, 0xf090ff, 0x90fff0, 0xff90f0, 0xf0ff90, 0x90f0ff, 0xffb090, + 0xb090ff, 0x90ffb0, 0xff90b0, 0xb0ff90, 0x90b0ff, 0xff5090, 0x5090ff, + 0x90ff50, 0xff9050, 0x50ff90, 0x9050ff, 0xffd090, 0xd090ff, 0x90ffd0, + 0xff90d0, 0xd0ff90, 0x90d0ff, 0xff7090, 0x7090ff, 0x90ff70, 0xff9070, + 0x70ff90, 0x9070ff, 0xff3090, 0x3090ff, 0x90ff30, 0xff9030, 0x30ff90, + 0x9030ff, 0xff9000, 0x90ff00, 0x9000ff, 0xff0090, 0x0090ff, 0x00ff90, + 0xff90ff, 0x90ffff, 0xffff90, 0x90ff90, 0xff9090, 0x9090ff, 0x80c090, + 0xc09080, 0x9080c0, 0x8090c0, 0xc08090, 0x90c080, 0x804090, 0x409080, + 0x908040, 0x809040, 0x408090, 0x904080, 0x80e090, 0xe09080, 0x9080e0, + 0x8090e0, 0xe08090, 0x90e080, 0x806090, 0x609080, 0x908060, 0x809060, + 0x608090, 0x906080, 0x80a090, 0xa09080, 0x9080a0, 0x8090a0, 0xa08090, + 0x90a080, 0x802090, 0x209080, 0x908020, 0x809020, 0x208090, 0x902080, + 0x80f090, 0xf09080, 0x9080f0, 0x8090f0, 0xf08090, 0x90f080, 0x80b090, + 0xb09080, 0x9080b0, 0x8090b0, 0xb08090, 0x90b080, 0x805090, 0x509080, + 0x908050, 0x809050, 0x508090, 0x905080, 0x80d090, 0xd09080, 0x9080d0, + 0x8090d0, 0xd08090, 0x90d080, 0x807090, 0x709080, 0x908070, 0x809070, + 0x708090, 0x907080, 0x803090, 0x309080, 0x908030, 0x809030, 0x308090, + 0x903080, 0x809000, 0x908000, 0x900080, 0x800090, 0x009080, 0x008090, + 0x809080, 0x908080, 0x808090, 0x908090, 0x809090, 0x909080, 0xc04090, + 0x4090c0, 0x90c040, 0xc09040, 0x40c090, 0x9040c0, 0xc0e090, 0xe090c0, + 0x90c0e0, 0xc090e0, 0xe0c090, 0x90e0c0, 0xc06090, 0x6090c0, 0x90c060, + 0xc09060, 0x60c090, 0x9060c0, 0xc0a090, 0xa090c0, 0x90c0a0, 0xc090a0, + 0xa0c090, 0x90a0c0, 0xc02090, 0x2090c0, 0x90c020, 0xc09020, 0x20c090, + 0x9020c0, 0xc0f090, 0xf090c0, 0x90c0f0, 0xc090f0, 0xf0c090, 0x90f0c0, + 0xc0b090, 0xb090c0, 0x90c0b0, 0xc090b0, 0xb0c090, 0x90b0c0, 0xc05090, + 0x5090c0, 0x90c050, 0xc09050, 0x50c090, 0x9050c0, 0xc0d090, 0xd090c0, + 0x90c0d0, 0xc090d0, 0xd0c090, 0x90d0c0, 0xc07090, 0x7090c0, 0x90c070, + 0xc09070, 0x70c090, 0x9070c0, 0xc03090, 0x3090c0, 0x90c030, 0xc09030, + 0x30c090, 0x9030c0, 0xc09000, 0x90c000, 0x9000c0, 0xc00090, 0x0090c0, + 0x00c090, 0xc090c0, 0x90c0c0, 0xc0c090, 0x90c090, 0xc09090, 0x9090c0, + 0x40e090, 0xe09040, 0x9040e0, 0x4090e0, 0xe04090, 0x90e040, 0x406090, + 0x609040, 0x904060, 0x409060, 0x604090, 0x906040, 0x40a090, 0xa09040, + 0x9040a0, 0x4090a0, 0xa04090, 0x90a040, 0x402090, 0x209040, 0x904020, + 0x409020, 0x204090, 0x902040, 0x40f090, 0xf09040, 0x9040f0, 0x4090f0, + 0xf04090, 0x90f040, 0x40b090, 0xb09040, 0x9040b0, 0x4090b0, 0xb04090, + 0x90b040, 0x405090, 0x509040, 0x904050, 0x409050, 0x504090, 0x905040, + 0x40d090, 0xd09040, 0x9040d0, 0x4090d0, 0xd04090, 0x90d040, 0x407090, + 0x709040, 0x904070, 0x409070, 0x704090, 0x907040, 0x403090, 0x309040, + 0x904030, 0x409030, 0x304090, 0x903040, 0x409000, 0x904000, 0x900040, + 0x400090, 0x009040, 0x004090, 0x409040, 0x904040, 0x404090, 0x904090, + 0x409090, 0x909040, 0xe06090, 0x6090e0, 0x90e060, 0xe09060, 0x60e090, + 0x9060e0, 0xe0a090, 0xa090e0, 0x90e0a0, 0xe090a0, 0xa0e090, 0x90a0e0, + 0xe02090, 0x2090e0, 0x90e020, 0xe09020, 0x20e090, 0x9020e0, 0xe0f090, + 0xf090e0, 0x90e0f0, 0xe090f0, 0xf0e090, 0x90f0e0, 0xe0b090, 0xb090e0, + 0x90e0b0, 0xe090b0, 0xb0e090, 0x90b0e0, 0xe05090, 0x5090e0, 0x90e050, + 0xe09050, 0x50e090, 0x9050e0, 0xe0d090, 0xd090e0, 0x90e0d0, 0xe090d0, + 0xd0e090, 0x90d0e0, 0xe07090, 0x7090e0, 0x90e070, 0xe09070, 0x70e090, + 0x9070e0, 0xe03090, 0x3090e0, 0x90e030, 0xe09030, 0x30e090, 0x9030e0, + 0xe09000, 0x90e000, 0x9000e0, 0xe00090, 0x0090e0, 0x00e090, 0xe090e0, + 0x90e0e0, 0xe0e090, 0x90e090, 0xe09090, 0x9090e0, 0x60a090, 0xa09060, + 0x9060a0, 0x6090a0, 0xa06090, 0x90a060, 0x602090, 0x209060, 0x906020, + 0x609020, 0x206090, 0x902060, 0x60f090, 0xf09060, 0x9060f0, 0x6090f0, + 0xf06090, 0x90f060, 0x60b090, 0xb09060, 0x9060b0, 0x6090b0, 0xb06090, + 0x90b060, 0x605090, 0x509060, 0x906050, 0x609050, 0x506090, 0x905060, + 0x60d090, 0xd09060, 0x9060d0, 0x6090d0, 0xd06090, 0x90d060, 0x607090, + 0x709060, 0x906070, 0x609070, 0x706090, 0x907060, 0x603090, 0x309060, + 0x906030, 0x609030, 0x306090, 0x903060, 0x609000, 0x906000, 0x900060, + 0x600090, 0x009060, 0x006090, 0x609060, 0x906060, 0x606090, 0x906090, + 0x609090, 0x909060, 0xa02090, 0x2090a0, 0x90a020, 0xa09020, 0x20a090, + 0x9020a0, 0xa0f090, 0xf090a0, 0x90a0f0, 0xa090f0, 0xf0a090, 0x90f0a0, + 0xa0b090, 0xb090a0, 0x90a0b0, 0xa090b0, 0xb0a090, 0x90b0a0, 0xa05090, + 0x5090a0, 0x90a050, 0xa09050, 0x50a090, 0x9050a0, 0xa0d090, 0xd090a0, + 0x90a0d0, 0xa090d0, 0xd0a090, 0x90d0a0, 0xa07090, 0x7090a0, 0x90a070, + 0xa09070, 0x70a090, 0x9070a0, 0xa03090, 0x3090a0, 0x90a030, 0xa09030, + 0x30a090, 0x9030a0, 0xa09000, 0x90a000, 0x9000a0, 0xa00090, 0x0090a0, + 0x00a090, 0xa090a0, 0x90a0a0, 0xa0a090, 0x90a090, 0xa09090, 0x9090a0, + 0x20f090, 0xf09020, 0x9020f0, 0x2090f0, 0xf02090, 0x90f020, 0x20b090, + 0xb09020, 0x9020b0, 0x2090b0, 0xb02090, 0x90b020, 0x205090, 0x509020, + 0x902050, 0x209050, 0x502090, 0x905020, 0x20d090, 0xd09020, 0x9020d0, + 0x2090d0, 0xd02090, 0x90d020, 0x207090, 0x709020, 0x902070, 0x209070, + 0x702090, 0x907020, 0x203090, 0x309020, 0x902030, 0x209030, 0x302090, + 0x903020, 0x209000, 0x902000, 0x900020, 0x200090, 0x009020, 0x002090, + 0x209020, 0x902020, 0x202090, 0x902090, 0x209090, 0x909020, 0xf0b090, + 0xb090f0, 0x90f0b0, 0xf090b0, 0xb0f090, 0x90b0f0, 0xf05090, 0x5090f0, + 0x90f050, 0xf09050, 0x50f090, 0x9050f0, 0xf0d090, 0xd090f0, 0x90f0d0, + 0xf090d0, 0xd0f090, 0x90d0f0, 0xf07090, 0x7090f0, 0x90f070, 0xf09070, + 0x70f090, 0x9070f0, 0xf03090, 0x3090f0, 0x90f030, 0xf09030, 0x30f090, + 0x9030f0, 0xf09000, 0x90f000, 0x9000f0, 0xf00090, 0x0090f0, 0x00f090, + 0xf090f0, 0x90f0f0, 0xf0f090, 0x90f090, 0xf09090, 0x9090f0, 0xb05090, + 0x5090b0, 0x90b050, 0xb09050, 0x50b090, 0x9050b0, 0xb0d090, 0xd090b0, + 0x90b0d0, 0xb090d0, 0xd0b090, 0x90d0b0, 0xb07090, 0x7090b0, 0x90b070, + 0xb09070, 0x70b090, 0x9070b0, 0xb03090, 0x3090b0, 0x90b030, 0xb09030, + 0x30b090, 0x9030b0, 0xb09000, 0x90b000, 0x9000b0, 0xb00090, 0x0090b0, + 0x00b090, 0xb090b0, 0x90b0b0, 0xb0b090, 0x90b090, 0xb09090, 0x9090b0, + 0x50d090, 0xd09050, 0x9050d0, 0x5090d0, 0xd05090, 0x90d050, 0x507090, + 0x709050, 0x905070, 0x509070, 0x705090, 0x907050, 0x503090, 0x309050, + 0x905030, 0x509030, 0x305090, 0x903050, 0x509000, 0x905000, 0x900050, + 0x500090, 0x009050, 0x005090, 0x509050, 0x905050, 0x505090, 0x905090, + 0x509090, 0x909050, 0xd07090, 0x7090d0, 0x90d070, 0xd09070, 0x70d090, + 0x9070d0, 0xd03090, 0x3090d0, 0x90d030, 0xd09030, 0x30d090, 0x9030d0, + 0xd09000, 0x90d000, 0x9000d0, 0xd00090, 0x0090d0, 0x00d090, 0xd090d0, + 0x90d0d0, 0xd0d090, 0x90d090, 0xd09090, 0x9090d0, 0x703090, 0x309070, + 0x907030, 0x709030, 0x307090, 0x903070, 0x709000, 0x907000, 0x900070, + 0x700090, 0x009070, 0x007090, 0x709070, 0x907070, 0x707090, 0x907090, + 0x709090, 0x909070, 0x309000, 0x903000, 0x900030, 0x300090, 0x009030, + 0x003090, 0x309030, 0x903030, 0x303090, 0x903090, 0x309090, 0x909030, + 0x909090, 0x900000, 0x009000, 0x000090, 0x909000, 0x900090, 0x009090, + 0xff8010 +}; + +static unsigned char const pickMapComponent[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static unsigned char const pickMapComponent444[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x00, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0x00, 0x00, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0x00, 0x00, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +#define MAX_PICK_COLORS 4096 + +// Get the value at "index" to use for color picking. Returns a zero +// QRgb when the table has been exhausted. +QRgb qt_qgl_pick_color(int index) +{ + if (index >= 0 && index < MAX_PICK_COLORS) + return QRgb(pickColors[index] | 0xff000000); + else + return 0; +} + +// Normalize a color that was picked out of a screen color buffer +// so that it is a better match for something that was generated +// by qt_qgl_pick_color(). Rounding discrepancies in the +// low bits due to floating-point conversions are factored out. +QRgb qt_qgl_normalize_pick_color(QRgb color, bool is444) +{ + int red, green, blue; + if (!is444) { + // RGB565, RGB555, and RGB888 screens (alpha is ignored). + red = pickMapComponent[qRed(color)]; + green = pickMapComponent[qGreen(color)]; + blue = pickMapComponent[qBlue(color)]; + } else { + // RGB444 screens need a little more care when normalizing. + red = pickMapComponent444[qRed(color)]; + green = pickMapComponent444[qGreen(color)]; + blue = pickMapComponent444[qBlue(color)]; + } + return qRgb(red, green, blue); +} + +QT_END_NAMESPACE + +#else // QGL_GENERATOR_PROGRAM + +#include <stdio.h> + +static unsigned char singlePatterns[] = { + 1, 1, 1, + + 1, 0, 0, + 0, 1, 0, + 0, 0, 1, + 1, 1, 0, + 1, 0, 1, + 0, 1, 1 +}; +#define NUM_SINGLE_PATTERNS 7 + +static unsigned char doublePatterns[] = { + 1, 2, 0, + 2, 1, 0, + 2, 0, 1, + 1, 0, 2, + 0, 2, 1, + 0, 1, 2, + + 1, 2, 1, + 2, 1, 1, + 1, 1, 2, + 2, 1, 2, + 1, 2, 2, + 2, 2, 1 +}; +#define NUM_DOUBLE_PATTERNS 12 + +static unsigned char triplePatterns[] = { + 1, 2, 3, + 2, 3, 1, + 3, 1, 2, + 1, 3, 2, + 2, 1, 3, + 3, 2, 1 +}; +#define NUM_TRIPLE_PATTERNS 6 + +static unsigned char values[] = { + 0x00, + 0xff, 0x80, 0xc0, 0x40, 0xe0, 0x60, 0xa0, 0x20, + 0xf0, 0xb0, 0x50, 0xd0, 0x70, 0x30, 0x90, 0x10 +}; +#define NUM_VALUES 16 +#define NUM_VALUES_444 10 + +#define MAX_GENERATE 4096 + +static unsigned char used[17][17][17]; +static int generated = 0; + +static void genPattern(int red, int green, int blue) +{ + ++red; + ++green; + ++blue; + if (used[red][green][blue] || generated >= MAX_GENERATE) + return; + used[red][green][blue] = 1; + if ((generated % 7) == 0) + printf("\n "); + printf("0x%02x%02x%02x", values[red], values[green], values[blue]); + ++generated; + if (generated < MAX_GENERATE && (generated % 7) != 0) + printf(", "); + else if (generated < MAX_GENERATE) + printf(","); +} + +static void genSinglePatterns(int value) +{ + int index, red, green, blue; + for (index = 0; index < NUM_SINGLE_PATTERNS; ++index) { + if (singlePatterns[index * 3] == 0) + red = -1; + else + red = value; + if (singlePatterns[index * 3 + 1] == 0) + green = -1; + else + green = value; + if (singlePatterns[index * 3 + 2] == 0) + blue = -1; + else + blue = value; + genPattern(red, green, blue); + } +} + +static void genDoublePatterns(int value1, int value2) +{ + int index, red, green, blue; + for (index = 0; index < NUM_DOUBLE_PATTERNS; ++index) { + if (doublePatterns[index * 3] == 0) + red = -1; + else if (doublePatterns[index * 3] == 1) + red = value1; + else + red = value2; + if (doublePatterns[index * 3 + 1] == 0) + green = -1; + else if (doublePatterns[index * 3 + 1] == 1) + green = value1; + else + green = value2; + if (doublePatterns[index * 3 + 2] == 0) + blue = -1; + else if (doublePatterns[index * 3 + 2] == 1) + blue = value1; + else + blue = value2; + genPattern(red, green, blue); + } +} + +static void genTriplePatterns(int value1, int value2, int value3) +{ + int index, red, green, blue; + for (index = 0; index < NUM_TRIPLE_PATTERNS; ++index) { + if (triplePatterns[index * 3] == 0) + red = -1; + else if (triplePatterns[index * 3] == 1) + red = value1; + else if (triplePatterns[index * 3] == 2) + red = value2; + else + red = value3; + if (triplePatterns[index * 3 + 1] == 0) + green = -1; + else if (triplePatterns[index * 3 + 1] == 1) + green = value1; + else if (triplePatterns[index * 3 + 1] == 2) + green = value2; + else + green = value3; + if (triplePatterns[index * 3 + 2] == 0) + blue = -1; + else if (triplePatterns[index * 3 + 2] == 1) + blue = value1; + else if (triplePatterns[index * 3 + 2] == 2) + blue = value2; + else + blue = value3; + genPattern(red, green, blue); + } +} + +static void genPatternRange(int limit) +{ + // This will generate 4912 unique color values which are + // reasonably well-spaced in the RGB color cube. + int first, second, third; + for (first = 0; first < limit; ++first) { + genSinglePatterns(first); + for (second = first + 1; second < limit; ++second) { + genDoublePatterns(first, second); + for (third = second + 1; third < limit; ++third) { + genTriplePatterns(first, second, third); + } + } + } +} + +static void generateComponentMap(void) +{ + int map[256]; + int index, value, index2; + + for (index = 0; index < 256; ++index) + map[index] = 0; + + for (index = 0; index < NUM_VALUES; ++index) { + value = values[index + 1]; + for (index2 = value - 8; index2 < (value + 8); ++index2) { + if (index2 >= 0 && index2 < 256) + map[index2] = value; + } + } + + for (index = 0; index < 256; ++index) { + if ((index % 8) == 0) + printf(" "); + printf("0x%02x", map[index]); + if (index < 255) + printf(","); + if ((index % 8) == 7) + printf("\n"); + else if (index < 255) + printf(" "); + } + + // Validate the reversibility of RGB565 and RGB555 mappings. + for (index = 0; index < 17; ++index) { + // Integer truncation test - 5-bit for red and blue (and green RGB555). + value = values[index] * 31 / 255; + index2 = value * 255 / 31; + if (values[index] != map[index2]) { + fprintf(stderr, "RGB565 (i5) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + + // Integer truncation test - 6-bit for green. + value = values[index] * 63 / 255; + index2 = value * 255 / 63; + if (values[index] != map[index2]) { + fprintf(stderr, "RGB565 (i6) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + + // Floating point rounding test - 5-bit for red and blue. + value = (int)((values[index] * 31.0 / 255.0) + 0.5); + index2 = (int)((value * 255.0 / 31.0) + 0.5); + if (values[index] != map[index2]) { + fprintf(stderr, "RGB565 (f5) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + + // Floating point rounding test - 6-bit for green. + value = (int)((values[index] * 63.0 / 255.0) + 0.5); + index2 = (int)((value * 255.0 / 63.0) + 0.5); + if (values[index] != map[index2]) { + fprintf(stderr, "RGB565 (f6) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + + // Test 5-bit to 8-bit conversion using doubling (ABCDE -> ABCDEABC). + value = values[index] * 31 / 255; + index2 = (value << 3) | (value >> 2); + if (values[index] != map[index2]) { + fprintf(stderr, "RGB565 (di5) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + value = (int)((values[index] * 31.0 / 255.0) + 0.5); + index2 = (value << 3) | (value >> 2); + if (values[index] != map[index2]) { + fprintf(stderr, "RGB565 (df5) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + + // Test 6-bit to 8-bit conversion using doubling (ABCDEF -> ABCDEFAB). + value = values[index] * 63 / 255; + index2 = (value << 2) | (value >> 4); + if (values[index] != map[index2]) { + fprintf(stderr, "RGB565 (di6) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + value = (int)((values[index] * 63.0 / 255.0) + 0.5); + index2 = (value << 2) | (value >> 4); + if (values[index] != map[index2]) { + fprintf(stderr, "RGB565 (df6) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + } +} + +static void generateComponentMap444(void) +{ + int map[256]; + int index, value, index2; + + for (index = 0; index < 256; ++index) + map[index] = 0; + + // Populate mappings for integer conversion with truncation. + for (index = 0; index < NUM_VALUES_444; ++index) { + value = values[index + 1] * 15 / 255; + value = value * 255 / 15; + if (value > 255) + value = 255; + else if (value < 0) + value = 0; + for (index2 = value - 8; index2 < (value + 7); ++index2) { + if (index2 >= 0 && index2 < 256) + map[index2] = values[index + 1]; + } + } + + // Add some extra mappings for floating-point conversion with rounding. + for (index = 0; index < NUM_VALUES_444; ++index) { + value = (int)((values[index + 1] * 15.0 / 255.0) + 0.5); + value = (int)((value * 255.0 / 15.0) + 0.5); + if (value > 255) + value = 255; + else if (value < 0) + value = 0; + for (index2 = value - 8; index2 < (value + 7); ++index2) { + if (index2 >= 0 && index2 < 256 && map[index2] == 0) + map[index2] = values[index + 1]; + } + } + + for (index = 0; index < 256; ++index) { + if ((index % 8) == 0) + printf(" "); + printf("0x%02x", map[index]); + if (index < 255) + printf(","); + if ((index % 8) == 7) + printf("\n"); + else if (index < 255) + printf(" "); + } + + // Validate the reversibility of RGB444 mappings. + for (index = 0; index <= NUM_VALUES_444; ++index) { + // Integer truncation test. + value = values[index] * 15 / 255; + index2 = value * 255 / 15; + if (values[index] != map[index2]) { + fprintf(stderr, "RGB444 (i) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + + // Floating point rounding test. + value = (int)((values[index] * 15.0 / 255.0) + 0.5); + index2 = (int)((value * 255.0 / 15.0) + 0.5); + if (values[index] != map[index2]) { + fprintf(stderr, "RGB444 (f) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + + // Test 4-bit to 8-bit conversion using doubling (ABCD -> ABCDABCD). + value = values[index] * 15 / 255; + index2 = value | (value << 4); + if (values[index] != map[index2]) { + fprintf(stderr, "RGB444 (di) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + value = (int)((values[index] * 15.0 / 255.0) + 0.5); + index2 = value | (value << 4); + if (values[index] != map[index2]) { + fprintf(stderr, "RGB444 (df) failure: 0x%02X -> 0x%02X -> 0x%02X\n", + values[index], index2, map[index2]); + } + } +} + +int main(int argc, char *argv[]) +{ + int limit; + + // Run the generator multiple times using more and more of + // the elements in the "values" table, and limit to a maximum + // of 1024 colors. + // + // This will sort the output so that colors that involve elements + // early in the table will be generated first. All combinations + // of early elements are exhausted before moving onto later values. + // + // The result of this sorting should be to maximize the spacing + // between colors that appear early in the generated output. + // This should produce better results for color picking on + // low-end devices with RGB565, RGB555, and RGB444 displays. + printf("static int const pickColors[%d] = {", MAX_GENERATE); + for (limit = 1; limit <= NUM_VALUES; ++limit) + genPatternRange(limit); + printf("\n};\n\n"); + + // Generate a component mapping table for mapping 8-bit RGB + // components to the nearest normalized value. + printf("static unsigned char const pickMapComponent[256] = {\n"); + generateComponentMap(); + printf("};\n\n"); + + // Generate a separate mapping table for RGB444, which needs a + // little more care to deal with truncation errors. + printf("static unsigned char const pickMapComponent444[256] = {\n"); + generateComponentMap444(); + printf("};\n\n"); + + printf("#define MAX_PICK_COLORS %d\n\n", MAX_GENERATE); + + return 0; +} + +#endif // QGL_GENERATOR_PROGRAM diff --git a/src/threed/painting/qglpickcolors_p.h b/src/threed/painting/qglpickcolors_p.h new file mode 100644 index 000000000..90c060af4 --- /dev/null +++ b/src/threed/painting/qglpickcolors_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLPICKCOLORS_P_H +#define QGLPICKCOLORS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtGui/qrgb.h> + +QT_BEGIN_NAMESPACE + +QRgb qt_qgl_pick_color(int index); +QRgb qt_qgl_normalize_pick_color(QRgb color, bool is444 = false); + +#endif diff --git a/src/threed/painting/qmatrix4x4stack.cpp b/src/threed/painting/qmatrix4x4stack.cpp new file mode 100644 index 000000000..fb38fa0df --- /dev/null +++ b/src/threed/painting/qmatrix4x4stack.cpp @@ -0,0 +1,380 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmatrix4x4stack.h" +#include "qmatrix4x4stack_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QMatrix4x4Stack + \brief The QMatrix4x4Stack class manages stacks of transformation matrices in GL applications. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::painting + + Transformation matrices are one of the basic building blocks of + 3D applications, allowing object models to be positioned, scaled, + rotated, and projected onto the screen. + + GL systems support several standard kinds of matrices, particularly + modelview and projection matrices. These matrices are typically + organized into stacks, which allow the current matrix state to be + saved with push() and restored later with pop(). + + QMatrix4x4Stack assists QGLPainter with the management of matrix + stacks, providing operations to set and modify transformation + matrices in each of the standard matrix stacks. + + In the following example, a standard orthographic projection matrix for a + view is set via the QGLPainter::projectionMatrix() stack, and + then a modelview matrix is set via the QGLPainter::modelViewMatrix() + stack to scale and translate an object prior to drawing: + + \code + QGLPainter painter(this); + + QMatrix4x4 projm; + projm.ortho(window->rect()); + painter.projectionMatrix() = projm; + + painter.modelViewMatrix().setToIdentity(); + painter.modelViewMatrix().translate(-1.0f, 2.0f, 0.0f); + painter.modelViewMatrix().scale(0.5f); + \endcode + + Later, the application can save the current modelview matrix state + and draw a different object with a different modelview matrix: + + \code + painter.modelViewMatrix().push(); + painter.modelViewMatrix().setToIdentity(); + painter.modelViewMatrix().scale(2.0f); + \endcode + + For efficiency, the matrix values are kept client-side until they + are needed by a QGLPainter::draw() operation. Until then, changes + to the matrix will not be reflected in the GL server. The application + can force the GL server to update the server with a call to + QGLPainter::update(). + + QMatrix4x4Stack is supported on all GL platforms, including OpenGL/ES 2.0 + which doesn't support matrix stacks natively. On that platform, the + matrix stack is simulated in client memory. When the application + selects a shader program to draw under OpenGL/ES 2.0, it calls + top() to obtain the actual value to be set on the shader program. + + \sa QGLPainter +*/ + +/*! + Creates a matrix stack. +*/ +QMatrix4x4Stack::QMatrix4x4Stack() + : d_ptr(new QMatrix4x4StackPrivate) +{ +} + +/*! + Destroy this matrix stack. +*/ +QMatrix4x4Stack::~QMatrix4x4Stack() +{ +} + +/*! + Pushes the current matrix onto the matrix stack. The matrix can + be restored with pop(). The new top of stack will have the + same value as the previous top of stack. + + The depths of the traditional \c{GL_MODELVIEW} and \c{GL_PROJECTION} + matrix stacks in the GL server are system-dependent and easy to + overflow in nested rendering code using \c{glPushMatrix()}. + By contrast, the push() function provides an arbitrary-sized stack + in client memory. + + \sa pop(), top() +*/ +void QMatrix4x4Stack::push() +{ + Q_D(QMatrix4x4Stack); + d->stack.push(d->matrix); +} + +/*! + Pops the top-most matrix from this matrix stack and sets the + current matrix to the next value down. Does nothing if the + matrix stack contains a single entry. + + \sa push() +*/ +void QMatrix4x4Stack::pop() +{ + Q_D(QMatrix4x4Stack); + if (!d->stack.isEmpty()) + d->matrix = d->stack.pop(); + d->isDirty = true; +} + +/*! + Set the matrix at the top of this matrix stack to the identity matrix. + + \sa operator=() +*/ +void QMatrix4x4Stack::setToIdentity() +{ + Q_D(QMatrix4x4Stack); + d->matrix.setToIdentity(); + d->isDirty = true; +} + +/*! + Returns a const reference to the current matrix at the top of this + matrix stack. This is typically used to fetch the matrix so it can + be set on user-defined shader programs. + + \sa operator=() +*/ +const QMatrix4x4 &QMatrix4x4Stack::top() const +{ + Q_D(const QMatrix4x4Stack); + return d->matrix; +} + +/*! + \fn QMatrix4x4Stack::operator const QMatrix4x4 &() const + + Returns a const reference to the current matrix at the top of + this matrix stack. + + \sa top() +*/ + +/*! + Assigns \a matrix to the matrix at the top of this matrix stack. + + \sa top() +*/ +QMatrix4x4Stack& QMatrix4x4Stack::operator=(const QMatrix4x4& matrix) +{ + Q_D(QMatrix4x4Stack); + d->matrix = matrix; + d->isDirty = true; + return *this; +} + +/*! + Multiplies the matrix at the top of this matrix stack by \a matrix. + + \sa top() +*/ +QMatrix4x4Stack& QMatrix4x4Stack::operator*=(const QMatrix4x4& matrix) +{ + Q_D(QMatrix4x4Stack); + d->matrix *= matrix; + d->isDirty = true; + return *this; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that translates coordinates by (\a x, \a y, \a z). The following example + translates the modelview matrix by (1, -3, 0): + + \code + QGLPainter painter(this); + painter.modelViewMatrix().translate(1.0f, -3.0f, 0.0f); + \endcode + + \sa scale(), rotate() +*/ +void QMatrix4x4Stack::translate(qreal x, qreal y, qreal z) +{ + Q_D(QMatrix4x4Stack); + d->matrix.translate(x, y, z); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix statck by another + that translates coordinates by the components of \a vector. + + \sa scale(), rotate() +*/ +void QMatrix4x4Stack::translate(const QVector3D& vector) +{ + Q_D(QMatrix4x4Stack); + d->matrix.translate(vector); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that scales coordinates by the components \a x, \a y, and \a z. + The following example scales the modelview matrix by (1, 2, 1): + + \code + QGLPainter painter(this); + painter.modelViewMatrix().scale(1.0f, 2.0f, 1.0f); + \endcode + + \sa translate(), rotate() +*/ +void QMatrix4x4Stack::scale(qreal x, qreal y, qreal z) +{ + Q_D(QMatrix4x4Stack); + d->matrix.scale(x, y, z); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that scales coordinates by the given \a factor. The following example + scales the modelview matrix by a factor of 2: + + \code + QGLPainter painter(this); + painter.modelViewMatrix().scale(2.0f); + \endcode + + \sa translate(), rotate() +*/ +void QMatrix4x4Stack::scale(qreal factor) +{ + Q_D(QMatrix4x4Stack); + d->matrix.scale(factor); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that scales coordinates by the components of \a vector. + + \sa translate(), rotate() +*/ +void QMatrix4x4Stack::scale(const QVector3D& vector) +{ + Q_D(QMatrix4x4Stack); + d->matrix.scale(vector); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that rotates coordinates through \a angle degrees about the vector + (\a x, \a y, \a z). The following example rotates the modelview + matrix by 45 degress about the vector (1, -3, 0): + + \code + QGLPainter painter(this); + painter.modelViewMatrix().rotate(45.0f, 1.0f, -3.0f, 0.0f); + \endcode + + \sa scale(), translate() +*/ +void QMatrix4x4Stack::rotate(qreal angle, qreal x, qreal y, qreal z) +{ + Q_D(QMatrix4x4Stack); + d->matrix.rotate(angle, x, y, z); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by another + that rotates coordinates through \a angle degrees about \a vector. + + \sa scale(), translate() +*/ +void QMatrix4x4Stack::rotate(qreal angle, const QVector3D& vector) +{ + Q_D(QMatrix4x4Stack); + d->matrix.rotate(angle, vector); + d->isDirty = true; +} + +/*! + Multiplies the current matrix at the top of this matrix stack by the + \a quaternion. Thus \c {painter->modelViewMatrix().rotate(quaternion)} + is equivalent to the following code: + \code + QMatrix4x4 mat; + mat.rotate(quaternion); + painter->modelViewMatrix() *= mat; + \endcode + which rotates coordinates according to the given \a quaternion. + + \sa scale(), translate() +*/ +void QMatrix4x4Stack::rotate(const QQuaternion &quaternion) +{ + Q_D(QMatrix4x4Stack); + d->matrix.rotate(quaternion); + d->isDirty = true; +} + +/*! + Returns true if the top of this matrix stack has been modified; + false otherwise. + + \sa setDirty() +*/ +bool QMatrix4x4Stack::isDirty() const +{ + Q_D(const QMatrix4x4Stack); + return d->isDirty; +} + +/*! + Sets the \a dirty flag on this matrix stack, which indicates + if it has been modified. + + A matrix stack may also be set to dirty by translate(), + scale(), operator*(), etc. + + \sa isDirty() +*/ +void QMatrix4x4Stack::setDirty(bool dirty) +{ + Q_D(QMatrix4x4Stack); + d->isDirty = dirty; +} + +QT_END_NAMESPACE diff --git a/src/threed/painting/qmatrix4x4stack.h b/src/threed/painting/qmatrix4x4stack.h new file mode 100644 index 000000000..1e392455c --- /dev/null +++ b/src/threed/painting/qmatrix4x4stack.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMATRIX4X4STACK_H +#define QMATRIX4X4STACK_H + +#include "qt3dglobal.h" +#include <QtGui/qmatrix4x4.h> +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QMatrix4x4StackPrivate; + +class Q_QT3D_EXPORT QMatrix4x4Stack +{ +public: + QMatrix4x4Stack(); + ~QMatrix4x4Stack(); + + const QMatrix4x4 &top() const; + + void push(); + void pop(); + + void setToIdentity(); + + void translate(qreal x, qreal y, qreal z); + void translate(const QVector3D& vector); + void scale(qreal x, qreal y, qreal z); + void scale(qreal factor); + void scale(const QVector3D& vector); + void rotate(qreal angle, qreal x, qreal y, qreal z); + void rotate(qreal angle, const QVector3D& vector); + void rotate(const QQuaternion &quaternion); + + QMatrix4x4Stack& operator=(const QMatrix4x4& matrix); + QMatrix4x4Stack& operator*=(const QMatrix4x4& matrix); + + operator const QMatrix4x4 &() const; + + bool isDirty() const; + void setDirty(bool dirty); + +private: + Q_DISABLE_COPY(QMatrix4x4Stack) + Q_DECLARE_PRIVATE(QMatrix4x4Stack) + + QScopedPointer<QMatrix4x4StackPrivate> d_ptr; + + friend class QGLPainter; +}; + +inline QMatrix4x4Stack::operator const QMatrix4x4 &() const +{ + return top(); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/painting/qmatrix4x4stack_p.h b/src/threed/painting/qmatrix4x4stack_p.h new file mode 100644 index 000000000..9c96d7869 --- /dev/null +++ b/src/threed/painting/qmatrix4x4stack_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMATRIX4X4STACK_P_H +#define QMATRIX4X4STACK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/qmatrix4x4.h> +#include <QtCore/qstack.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QMatrix4x4StackPrivate +{ +public: + QMatrix4x4StackPrivate() : isDirty(true) {} + + QMatrix4x4 matrix; + QStack<QMatrix4x4> stack; + bool isDirty; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/scene/qglabstractscene.cpp b/src/threed/scene/qglabstractscene.cpp new file mode 100644 index 000000000..9f18749a0 --- /dev/null +++ b/src/threed/scene/qglabstractscene.cpp @@ -0,0 +1,527 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglabstractscene.h" +#include "qglsceneformatplugin.h" +#include "qglpicknode.h" + +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/private/qfactoryloader_p.h> +#include <QtCore/qlibraryinfo.h> +#include <QtNetwork/qnetworkreply.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qdebug.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdir.h> +#include <QtCore/qpluginloader.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLAbstractScene + \brief The QGLAbstractScene class represents a 3D scene consisting of zero or more QGLSceneNode instances. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::scene + + Scenes are typically created by 3D modelling packages and then loaded + into the application via a QGLSceneFormatPlugin; but they can also be + constructed programatically. The functions in this + class provide access to major scene objects so that they can be + applied or drawn whenever the application decides. + + QGLAbstractScene presents a very simple model of a 3D scene. Subclasses + implement the specific scene graph and object representations in a + manner specific to the package's format. Subclasses may also provide + functionality to create new objects at runtime, but this is not required. + + The scene is defined to consist of a number of important objects + of the following types: + + \list + \o Camera objects define a viewing position in world-coordinates and a + projection that maps 3D world co-ordinates to 2D screen co-ordinates. + Camera objects should inherit from QGLCamera. + \o Main objects designate the major elements of the scene besides + cameras, lights, and effects. Usually they inherit from QGLSceneNode. + \o Light objects define positions and parameters for lighting the scene. + Light objects should inherit from QGLLightParameters. + \o Effect objects define materials, shaders, and textures for use in + rendering the surface of objects. Normally effects are activated + automatically when main objects are drawn. But effects can be used + independently if the 3D format is acting as a library of effects. + \o Mesh objects define geometry information independently of effects. + Normally meshes are drawn automatically with an appropriate effect + when main objects are drawn. But meshes can be used independently + if the 3D format is acting as a library of meshes. Mesh objects + should inherit from QGLSceneNode. + \endlist + + Typically, the full scene represented by an external model format + is not interesting to the application. 3D modelling packages + regularly insert cameras, lights, effects, and other library + objects that are useful to the modelling package, but not the + application. The mainNode() is usually the most interesting + to applications. + + QGLAbstractScene makes it easy to access the major scene elements + with object(), objects(), and mainNode(). + + There are many other kinds of objects in the scene that may not + be accessible via QGLAbstractScene because they are not considered + "important" enough. For example, a file that contains the data + for a skateboard object would contain many objects for the board, + wheels, texturing effects, animations, and so on. The application + may only be interested in the skateboard as a whole, and not its + sub-components. The skateboard would be considered an important + main object in this case, which can be easily accessed and + incorporated into the application's logic. + + Each Subclass needs to provide its own policy for deciding which + objects are considered "important". + + \sa QGLSceneNode, QGLSceneFormatPlugin +*/ + +class QGLAbstractScenePrivate +{ +public: + QGLAbstractScenePrivate() + : picking(false), nextPickId(-1), pickNodesDirty(true) {} + bool picking; + int nextPickId; + QList<QGLPickNode*> pickNodes; + QSet<QGLSceneNode*> pickable; + bool pickNodesDirty; +}; + +/*! + Constructs a 3D scene and attaches it to \a parent. +*/ +QGLAbstractScene::QGLAbstractScene(QObject *parent) + : QObject(parent) + , d_ptr(new QGLAbstractScenePrivate) +{ +} + +/*! + Destroys this 3D scene. +*/ +QGLAbstractScene::~QGLAbstractScene() +{ +} + +/*! + \internal +*/ +void QGLAbstractScene::childEvent(QChildEvent *event) +{ + Q_D(QGLAbstractScene); + if (event->type() == QEvent::ChildAdded) + d->pickNodesDirty = true; +} + +/*! + Sets this scene to be pickable if \a enable is true, otherwise picking + is disabled. If the scene is set to be pickable, pick nodes are + generated by calling generatePickNodes(). + + \sa generatePickNodes(), pickable() +*/ +void QGLAbstractScene::setPickable(bool enable) +{ + Q_D(QGLAbstractScene); + if (enable != d->picking) + { + d->picking = enable; + if (d->picking) + generatePickNodes(); + } +} + +/*! + Returns true if this scene is pickable. + + \sa setPickable() +*/ +bool QGLAbstractScene::pickable() const +{ + Q_D(const QGLAbstractScene); + return d->picking; +} + +/*! + Generates QGLPickNode instances for important QGLSceneNode instances that are + pickable. Objects that are either not important or not pickable can + be omitted. The default implementation simply generates pick nodes + for every top level object of type QGLSceneNode. + + Sub-classes may implement different schemes for picking. When doing + so parent the QGLPickNode objects onto the scene, so that they will + appear in the list returned by pickNodes() + + \sa pickNodes(), setPickable() +*/ +void QGLAbstractScene::generatePickNodes() +{ + Q_D(QGLAbstractScene); + QList<QObject *> objs = objects(); + QList<QObject *>::iterator it = objs.begin(); + d->pickNodes.clear(); + for ( ; it != objs.end(); ++it) + { + QGLSceneNode *n = qobject_cast<QGLSceneNode *>(*it); + if (d) { + if (!d->pickable.contains(n)) { + n->setPickNode(new QGLPickNode(this)); + d->pickable.insert(n); + } + d->pickNodes.append(n->pickNode()); + } + } +} + +/*! + Increments and returns the next available pick id for this scene. +*/ +int QGLAbstractScene::nextPickId() +{ + return ++d_ptr->nextPickId; +} + +/*! + Returns a list of the pick nodes that have been parented onto this + scene. + + \sa generatePickNodes() +*/ +QList<QGLPickNode *> QGLAbstractScene::pickNodes() const +{ + if (d_ptr->pickNodesDirty) + { + const_cast<QGLAbstractScene*>(this)->generatePickNodes(); + d_ptr->pickNodesDirty = false; + } + return d_ptr->pickNodes; +} + +/*! + \fn QList<QObject *> QGLAbstractScene::objects() const + + Returns a list of all objects in the scene which are considered + important. + + Important objects will typically be the main mesh object, cameras, + lights, and other top-level objects. Sub-meshes and effects + are normally not considered important unless the scene is + acting as a library of meshes and effects. + + \sa objectNames(), object(), mainNode() +*/ + +/*! + Returns a list of the names of all objects in the scene which + are considered important, and which have non-empty names + associated with them. + + The default implementation calls objects() and then compiles a list + of all non-empty object names. + + \sa objects() +*/ +QStringList QGLAbstractScene::objectNames() const +{ + QList<QObject *> objs = objects(); + QStringList names; + for (int index = 0; index < objs.count(); ++index) { + QObject *object = objs.at(index); + if (object) { + QString name = object->objectName(); + if (!name.isEmpty()) + names += name; + } + } + return names; +} + +/*! + Returns the scene object that has the specified \a name; + or null if the object was not found. + + The default implementation searches objects() for an object that + matches \a name. + + \sa objects() +*/ +QObject *QGLAbstractScene::object(const QString& name) const +{ + if (name.isEmpty()) + return 0; + QList<QObject *> objs = objects(); + for (int index = 0; index < objs.count(); ++index) { + QObject *object = objs.at(index); + if (object && object->objectName() == name) + return object; + } + return 0; +} + + +/*! + \fn QGLSceneNode *QGLAbstractScene::mainNode() const + + Returns the main mesh node in the scene, or null if the scene + does not contain a main mesh node. + + \sa objects() +*/ + +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QGLSceneFormatFactoryInterface_iid, QLatin1String("/sceneformats"))) +#endif + +/*! + Loads a scene from \a device in the specified \a format using + the registered scene format plugins. If \a format is an empty + string, then the format will be autodetected from the filename + extension of \a device. The \a url specifies the location of + the data in \a device so that relative resources can be located. + + The \a options string is passed to the underlying format loader + and its meaning and format depend on the loader. For example the + format string for the .3ds loader accepts the following options: + \list + \o ForceSmooth - average normals for a smooth appearance + \o ForceFaceted - per face normals for a faceted appearance + \o NativeIndices - map native indices for poorly smoothed models + \o CorrectNormals - fix inverted normals on models with bad windings + \o CorrectAcute - fix normals on models that smooth acute angles + \endlist + + The options may be specified globally for the whole model, or just + for a particular mesh. + + In this example smoothing is forced on globally, and native indices + are used on just the mesh called "BattCoverMesh". + + \code + QString op = "ForceSmooth BattCoverMesh=NativeIndices"; + QString file = "music-player.3ds"; + QGLAbstractScene *scene = QGLAbstractScene::loadScene(file, QString(), op); + \endcode + + Returns the scene object, or null if the scene could not be loaded + or the \a format was not supported by any of the plugins. + + \sa QGLSceneFormatPlugin +*/ +QGLAbstractScene *QGLAbstractScene::loadScene + (QIODevice *device, const QUrl& url, const QString& format, const QString &options) +{ +#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + if (!device) + return 0; + + QFactoryLoader *l = loader(); + QStringList keys = l->keys(); + + // If the format is not specified, then use the filename/url extension. + QString fmt = format; + if (fmt.isEmpty()) { + QFile *file = qobject_cast<QFile *>(device); + QString name; + if (file) { + name = file->fileName(); + } else { + QNetworkReply *reply = qobject_cast<QNetworkReply *>(device); + if (reply) + name = reply->url().path(); + } + int dot = name.lastIndexOf(QLatin1Char('.')); + QString suffix = name.mid(dot+1).toLower(); + int index = keys.indexOf(suffix); + if (index >= 0) + fmt = suffix; + } + + // Find the plugin that handles the format and ask it to create a handler. + if (QGLSceneFormatFactoryInterface *factory + = qobject_cast<QGLSceneFormatFactoryInterface*> + (l->instance(fmt))) { + QGLSceneFormatHandler *handler = factory->create(device, url, fmt); + if (handler) { + handler->setDevice(device); + handler->setUrl(url); + handler->setFormat(format); + if (!options.isEmpty()) + handler->decodeOptions(options); + QGLAbstractScene *scene = handler->read(); + delete handler; + return scene; + } + } + + // If we get here, then the format is not supported by any of the plugins. +#ifndef QT_NO_DEBUG + qWarning("Could not create handler for format %s" + "- check plugins are installed correctly in %s", + qPrintable(fmt), + qPrintable(QLibraryInfo::location(QLibraryInfo::PluginsPath))); +#endif + return 0; +#else // QT_NO_LIBRARY || QT_NO_SETTINGS + Q_UNUSED(device); + Q_UNUSED(url); + Q_UNUSED(format); + return 0; +#endif // QT_NO_LIBRARY || QT_NO_SETTINGS +} + +/*! + Loads a scene from \a fileName in the specified \a format, with the + supplied \a options, and using the registered scene format plugins. + + If \a format is an empty string, then the format will be autodetected + from the extension of \a fileName. + + The \a options string is passed to the underlying format loader + and its meaning and format depend on the loader. See the doc above + for loadScene() for details on the 3ds format options. + + Returns the scene object, or null if the scene could not be loaded + or the \a format was not supported by any of the plugins. + + \sa QGLSceneFormatPlugin +*/ +QGLAbstractScene *QGLAbstractScene::loadScene + (const QString& fileName, const QString& format, const QString &options) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) + { + if (options.contains(QLatin1String("ShowWarnings"))) + qWarning("Could not read %s", qPrintable(fileName)); + return 0; + } + QFileInfo fi(fileName); + QUrl url = QUrl::fromLocalFile(fi.absoluteFilePath()); + return loadScene(&file, url, format, options); +} + +/*! + \enum QGLAbstractScene::FormatListType + This enum specifies the format of the list returned by the supportedFormats() function. + + \value AsFilter Return a format list that may be used as a filter. + \value AsSuffix Return a format list that is simply the filename suffixes. +*/ + +/*! + Returns a list of all supported formats known by currently available + sceneformat plugins, in the format type \a t. + + If \a t is QGLAbstractScene::AsFilter then the result may be passed + to QDir::setNameFilters(), or used in other filters. This is the default. + + For example to create a file dialog to load model files use this: + \code + QString modelsDir = QDir::toNativeSeperators(QDir::homePath()); + QString filter = tr("Models (%1)").arg(QAbstractScene::supportedFormats().join(" ")); + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open File"), modelsDir, filter)); + \endcode + + Otherwise (when \a t is QGLAbstractScene::AsSuffix) it is simply a list + of file name suffixes. + + Note that this function may be expensive to + call since it scans for available plugins, and loads each one it + finds to get an accurate report of formats supported at run-time. +*/ +QStringList QGLAbstractScene::supportedFormats(QGLAbstractScene::FormatListType t) +{ + QStringList formats; + QSet<QString> formatSet; + QSet<QString> dirSet; + QStringList pluginPaths = QCoreApplication::libraryPaths(); + QStringList::const_iterator it = pluginPaths.constBegin(); + for ( ; it != pluginPaths.constEnd(); ++it) + { + QString path = *it; + QDir sceneformatDir(path + QLatin1String("/sceneformats")); + path = sceneformatDir.absolutePath(); + if (!sceneformatDir.exists() || dirSet.contains(path)) + continue; + dirSet.insert(path); + sceneformatDir.setFilter(QDir::Files); + QStringList entries = sceneformatDir.entryList(); + QStringList::const_iterator fit = entries.constBegin(); + for ( ; fit != entries.constEnd(); ++fit) + { + QString fi = *fit; + QPluginLoader loader(sceneformatDir.absoluteFilePath(fi)); + QObject *inst = loader.instance(); + QGLSceneFormatFactoryInterface *iface = qobject_cast<QGLSceneFormatFactoryInterface*>(inst); + if (iface) + { + QStringList formatKeys = iface->keys(); + QStringList::const_iterator kit = formatKeys.constBegin(); + for ( ; kit != formatKeys.constEnd(); ++kit) + { + QString k = *kit; + if (!formatSet.contains(k) && !k.contains("/")) // dont add mime-type keys + { + if (t == AsFilter) + k.prepend("*."); + formatSet.insert(k); + formats.append(k); + } + } + } + } + } + return formats; +} + + +QT_END_NAMESPACE diff --git a/src/threed/scene/qglabstractscene.h b/src/threed/scene/qglabstractscene.h new file mode 100644 index 000000000..72b3d4828 --- /dev/null +++ b/src/threed/scene/qglabstractscene.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLABSTRACTSCENE_H +#define QGLABSTRACTSCENE_H + +#include "qt3dglobal.h" +#include "qglscenenode.h" + +#include <QtCore/qstringlist.h> +#include <QtCore/qurl.h> +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLAbstractScenePrivate; +class QIODevice; +class QGLPickNode; + +class Q_QT3D_EXPORT QGLAbstractScene : public QObject +{ + Q_OBJECT +public: + explicit QGLAbstractScene(QObject *parent = 0); + virtual ~QGLAbstractScene(); + + virtual void setPickable(bool enable); + virtual bool pickable() const; + virtual void generatePickNodes(); + QList<QGLPickNode *> pickNodes() const; + int nextPickId(); + + virtual QList<QObject *> objects() const = 0; + virtual QStringList objectNames() const; + virtual QObject *object(const QString& name) const; + virtual QGLSceneNode *mainNode() const = 0; + + static QGLAbstractScene *loadScene + (QIODevice *device, const QUrl& url, const QString& format = QString(), + const QString& options = QString()); + static QGLAbstractScene *loadScene + (const QString& fileName, const QString& format = QString(), + const QString& options = QString()); + + enum FormatListType { + AsFilter, AsSuffix + }; + + static QStringList supportedFormats(FormatListType t = AsFilter); + +protected: + void childEvent(QChildEvent * event); + +private: + QScopedPointer<QGLAbstractScenePrivate> d_ptr; + + Q_DISABLE_COPY(QGLAbstractScene) + Q_DECLARE_PRIVATE(QGLAbstractScene) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/scene/qglpicknode.cpp b/src/threed/scene/qglpicknode.cpp new file mode 100644 index 000000000..06a4245b3 --- /dev/null +++ b/src/threed/scene/qglpicknode.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglpicknode.h" +#include "qglabstractscene.h" + +#include <QtGui/qevent.h> + +/*! + \class QGLPickNode + \brief The QGLPickNode class enables picking for objects in a 3D scene. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::scene + + QGLPickNode is a QObject sub-class, relatively light-weight, that can + shadow QSceneObject instances and provide picking capability. The + QGLPickNode instance receives events sent to it by picking views, such + as the QGLView class, and emits relevant signals in response. + + QGLSceneObjects wanting to support picking, should implement the methods + \list + \o QGLSceneNode::pickNode() + \o QGLSceneNode::setPickNode() + \endlist + and also implement rendering such that the QGLPainter has the relevant + \l{QGLPainter::setObjectPickId()}{pick id function} called. + + These functions are already implemented for the QGLSceneNode object type. + + Picking can then be simply enabled by calling the QGLAbstractScene function + setPickable() to turn on picking and generate pick nodes for the relevant + scene objects. + + In order to respond to picking, the view class should simply register the + picknodes, and connect a relevant slot to the pick nodes signal. + + For the QGLView class that code is simply: + \code + QList<QGLPickNode *>nodes = manager->pickNodes(); + foreach (QGLPickNode *node, nodes) + { + registerObject(node->id(), node); + connect(node, SIGNAL(clicked()), + this, SLOT(objectPicked())); + } + \endcode + + The pick nodes should be unregistered with similar code for the QGLView if + switching to a different scene. + + To be able to recover the relevant QGLSceneNode inside the called slot, + the object may be set onto the QGLPickNode with the setTarget() function, + and recovered with target() inside the slot, since the QGLPickNode will + be the sender() for the slot. + + QGLSceneNode already sets itself as the target during the pick enable step. + + \sa QGLSceneNode, QGLView +*/ + +/*! + Construct a new QGLPickNode object managed by and parented onto + the \a parent scene. The nextPickId() function will be called on + \a parent to assign a pick id to this new node. +*/ +QGLPickNode::QGLPickNode(QGLAbstractScene *parent) : + QObject(parent), m_id(-1), m_target(0) +{ + if (parent) + m_id = parent->nextPickId(); +} + +/*! + \fn int QGLPickNode::id() const + Returns the unique id for this node. This value is assigned by + the parent scene for this node and should be different for every + node in the scene. Returns -1 if the id has not been set yet. + + \sa setId() +*/ + +/*! + \fn void QGLPickNode::setId(int id) + Sets the unique \a id for this node. Generally this function should not + be needed, since the constructor causes a unique id to be obtained + from the parent scene. This function exists mainly for testing + purposes. + + \sa id() +*/ + +/*! + \fn QGLSceneNode *QGLPickNode::target() const + Returns the QGLSceneNode which is the target of this pick node; + null if the target has not been set yet. + + \sa setTarget() +*/ + +/*! + \fn void QGLPickNode::setTarget(QGLSceneNode *target) + Sets the \a target for this pick node. + + \sa target() +*/ + +/*! + \fn void QGLPickNode::pressed() + Signal emitted when the scene object for this node has the mouse pressed + while the cursor is on the object in the scene. +*/ + +/*! + \fn void QGLPickNode::released() + Signal emitted when the scene object for this node has the mouse released + while the cursor is on the object in the scene. +*/ + +/*! + \fn void QGLPickNode::clicked() + Signal emitted when the scene object for this node has the mouse pressed + and then released while the cursor is on the object in the scene. +*/ + +/*! + \fn void QGLPickNode::doubleClicked() + Signal emitted when the scene object for this node has the mouse clicked + twice in succession while the cursor is on the object in the scene. +*/ + +/*! + \fn void QGLPickNode::hoverChanged() + Signal emitted when the scene object for this node has the mouse moved + into or out of this object in the scene. +*/ + +/*! + \internal +*/ +bool QGLPickNode::event(QEvent *e) +{ + // ripped off from teaservice demo, but not before the same code + // was ripped off and put in item3d.cpp - those should probably all + // use this implementation here + if (e->type() == QEvent::MouseButtonPress) + { + QMouseEvent *me = (QMouseEvent *)e; + if (me->button() == Qt::LeftButton) + emit pressed(); + } + else if (e->type() == QEvent::MouseButtonRelease) + { + QMouseEvent *me = (QMouseEvent *)e; + if (me->button() == Qt::LeftButton) + { + emit released(); + if (me->x() >= 0) // Positive: inside object, Negative: outside. + emit clicked(); + } + } + else if (e->type() == QEvent::MouseButtonDblClick) + { + emit doubleClicked(); + } + else if (e->type() == QEvent::Enter) + { + //m_hovering = true; + emit hoverChanged(); + } + else if (e->type() == QEvent::Leave) + { + //m_hovering = false; + emit hoverChanged(); + } + return QObject::event(e); +} diff --git a/src/threed/scene/qglpicknode.h b/src/threed/scene/qglpicknode.h new file mode 100644 index 000000000..c4c90e1a3 --- /dev/null +++ b/src/threed/scene/qglpicknode.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLPICKNODE_H +#define QGLPICKNODE_H + +#include <QtCore/qobject.h> +#include "qt3dglobal.h" + +class QGLAbstractScene; +class QGLSceneNode; +class QEvent; + +class Q_QT3D_EXPORT QGLPickNode : public QObject +{ + Q_OBJECT +public: + explicit QGLPickNode(QGLAbstractScene *parent = 0); + int id() const { return m_id; } + void setId(int id) { m_id = id; } + + QGLSceneNode *target() const { return m_target; } + void setTarget(QGLSceneNode *target) { m_target = target; } + +Q_SIGNALS: + void pressed(); + void released(); + void clicked(); + void doubleClicked(); + void hoverChanged(); + +public Q_SLOTS: + +protected: + bool event(QEvent *e); + int m_id; + QGLSceneNode *m_target; +}; + +#endif // QGLPICKNODE_H diff --git a/src/threed/scene/qglrenderorder.cpp b/src/threed/scene/qglrenderorder.cpp new file mode 100644 index 000000000..d5250de18 --- /dev/null +++ b/src/threed/scene/qglrenderorder.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglrenderorder.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLRenderOrder + \brief The QGLRenderOrder class represents an order of a scene node during rendering. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::scene + + The QGLRenderOrder class works with the QGLRenderSequencer to optimize + the rendering order of scene nodes. + + The class encapsulates the ordering with which QGLSceneNodes appear in the + rendering of a scene. Every scene node that has the same rendering properties + maps to the same QGLRenderOrder. + + QGLRenderOrder instances are based on a scene node, and the path through + the scene graph by which the node was reached. This is necessary since the + same node may appear with different rendering properties in multiple + different places in the rendering graph. + + For example a node holding a model of a pawn chess piece may appear in + several different places on a board, some with a white material and some + with a black material. + + To capture this concept QGLRenderOrder instances are constructed from a + pointer to a QGLSceneNode, and a QGLRenderState instance. + + A render order then calculates the effective values of its various attributes + based on both the node, and the state. + + Custom render orders may be created by sub-classing QGLRenderOrderComparator + and reimplementing the following methods: + \list + \i isEqualTo() + \i isLessThan() + \endlist + + By default all nodes which have the same effect type are rendered together, + and then within that, those nodes which have the same material are + rendered together. + + \sa QGLRenderOrderComparator +*/ + +/*! + \fn QGLRenderOrder::QGLRenderOrder(const QGLSceneNode *node, const QGLRenderState &state) + Creates a new QGLRenderOrder instance that encapsulates the order in this + render pass represented by the given \a node and \a state. The \a node + defaults to NULL, and the \a state defaults to a default constructed + invalid QGLRenderState. +*/ + +/*! + \fn QGLRenderOrder::~QGLRenderOrder() + Destroys this QGLRenderOrder and recovers any resources. +*/ + +/*! + Returns a hash value representing the effect set onto the node for + this render order. +*/ +uint QGLRenderOrder::effectHash() const +{ + quint64 result = 0; + if (effectiveHasEffect()) + { + QGLAbstractEffect *eff = effectiveUserEffect(); + if (eff) + result = reinterpret_cast<quint64>(eff); + else + result = effectiveStandardEffect(); + Q_ASSERT(result); + } + return qHash(result); +} + +/*! + Returns true if this QGLRenderOrder is equal to the \a rhs, otherwise + returns false. Reimplement this function when creating a sub-class of + QGLRenderOrder. + + \sa isLessThan() +*/ +bool QGLRenderOrder::isEqual(const QGLRenderOrder &rhs) const +{ + if (this == &rhs) + return true; + bool result = false; + bool thisHasEffect = effectiveHasEffect(); + bool thatHasEffect = rhs.effectiveHasEffect(); + if (thisHasEffect && thatHasEffect) + { + QGLAbstractEffect *eff = effectiveUserEffect(); + if (eff) + result = (eff == rhs.effectiveUserEffect()); + else + result = (effectiveStandardEffect() == rhs.effectiveStandardEffect()); + } + else + { + result = (thisHasEffect == thatHasEffect); + } + if (result) + { + result = (effectiveMaterial() == rhs.effectiveMaterial()); + } + if (result) + { + result = (effectiveBackMaterial() == rhs.effectiveBackMaterial()); + } + return result; +} + +/*! + Returns true if this QGLRenderOrder is less than the \a rhs, otherwise + returns false. Reimplement this function when creating a sub-class of + QGLRenderOrder. + + The default implementation sorts first by effect, second by material (front + then back). + + Sorting by material is ordered by pointer comparison. + + Sorting by effect is based on the following order, from lowest to + highest: + \list + \o No effect - hasEffect() == true + \o Standard effect - ordered by numerical value, eg QGL::FlatColor < QGL::LitMaterial + \o User effect - ordered by pointer comparison + \endlist + So a node with \c{hasEffect() == false} node is \i{less than} a node with + a custom user effect, for example. + + \sa isEqual() +*/ +bool QGLRenderOrder::isLessThan(const QGLRenderOrder &rhs) const +{ + bool result = false; + bool thisHasEffect = effectiveHasEffect(); + bool thatHasEffect = rhs.effectiveHasEffect(); + if (thisHasEffect && thatHasEffect) + { + QGLAbstractEffect *eff = effectiveUserEffect(); + if (eff) + result = (eff < rhs.effectiveUserEffect()); + else + result = !rhs.effectiveUserEffect() && + (effectiveStandardEffect() < rhs.effectiveStandardEffect()); + } + else + { + result = !thisHasEffect; + } + if (!result) + { + result = (effectiveMaterial() < rhs.effectiveMaterial()); + } + if (!result) + { + result = (effectiveBackMaterial() < rhs.effectiveBackMaterial()); + } + return result; +} + +/*! + \fn bool QGLRenderOrder::isValid() const + Returns true if this is a valid QGLRenderOrder, that is it was + initialized with a non-null QGLSceneNode. +*/ + +/*! + \fn bool QGLRenderOrder::operator!=(const QGLRenderOrder &rhs) const + Returns true if this QGLRenderOrder is not equal to the \a rhs, otherwise + returns false. This function simply returns \c{!isEqual(rhs)}. +*/ + +/*! + \fn bool QGLRenderOrder::bool operator==(const QGLRenderOrder &rhs) const + Returns true if this QGLRenderOrder is equal to the \a rhs, otherwise + returns false. This function simply returns \c{isEqual(rhs)}. +*/ + +/*! + \fn bool QGLRenderOrder::operator<(const QGLRenderOrder &rhs) const + Returns true if this QGLRenderOrder is less than to the \a rhs, otherwise + returns false. This function simply returns \c{isLessThan(rhs)}. +*/ + +/*! + \fn const QGLSceneNode *QGLRenderOrder::node() const + Returns a pointer to the scene node for which the render order is held by + this QGLRenderOrder instance. This is simply the value passed to the + constructor. +*/ + +/*! + \fn QGLRenderState QGLRenderOrder::state() const + Returns a pointer to the render state for this order. This is simply the + value passed to the constructor. +*/ + +/*! + \fn void QGLRenderOrder::setState(const QGLRenderState &state) + Sets the \a state for this order. +*/ + +/*! + \fn QGLAbstractEffect *QGLRenderOrder::effectiveUserEffect() const + Returns the effective user effect of the node set for this render + order, taking into account any effect inherited from parent nodes + as specified by the render state(). + + \sa state(), effectiveHasEffect() +*/ + +/*! + \fn QGL::StandardEffect QGLRenderOrder::effectiveStandardEffect() const + Returns the effective standard effect of the node set for this render + order, taking into account any effect inherited from parent nodes + as specified by the render state(). + + \sa state(), effectiveHasEffect() +*/ + +/*! + \fn QGLMaterial *QGLRenderOrder::effectiveMaterial() const + Returns the effective material of the node set for this render + order, taking into account any effect inherited from parent nodes + as specified by the render state(). + + \sa state(), effectiveBackMaterial() +*/ + +/*! + \fn QGLMaterial *QGLRenderOrder::effectiveBackMaterial() const + Returns the effective back material of the node set for this render + order, taking into account any effect inherited from parent nodes + as specified by the render state(). + + \sa state(), effectiveMaterial() +*/ + +/*! + \fn bool QGLRenderOrder::effectiveHasEffect() const + Returns the effective value of whether an effect is set on the node + set for this render order, taking into account any effect inherited + from parent nodes as specified by the render state(). + + \sa state() +*/ + +/*! + \fn uint qHash(const QGLRenderOrder &order) + \relates QGLRenderOrder + Returns a hash value representation of the \a order. +*/ + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QGLRenderOrder &order) +{ + if (order.isValid()) + dbg << "QGLRenderOrder for node:" << order.node() + << "-- effect hash:" << order.effectHash() + << "-- material:" << order.node()->material() + << "-- back material:" << order.node()->backMaterial(); + else + dbg << "QGLRenderOrder -- invalid"; + return dbg; +} + +#endif + + +QT_END_NAMESPACE diff --git a/src/threed/scene/qglrenderorder.h b/src/threed/scene/qglrenderorder.h new file mode 100644 index 000000000..3a1071a4a --- /dev/null +++ b/src/threed/scene/qglrenderorder.h @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGLRENDERORDER_H +#define QGLRENDERORDER_H + +#include "qglscenenode.h" +#include "qglrenderstate.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QGLRenderOrder +{ +public: + explicit QGLRenderOrder(const QGLSceneNode *node = 0, const QGLRenderState &state = QGLRenderState()) + : m_node(node) + , m_state(state) + { + } + ~QGLRenderOrder() {} + + uint effectHash() const; + bool isEqual(const QGLRenderOrder &rhs) const; + bool isLessThan(const QGLRenderOrder &rhs) const; + inline bool isValid() const; + + inline bool operator!=(const QGLRenderOrder &rhs) const; + inline bool operator==(const QGLRenderOrder &rhs) const; + inline bool operator<(const QGLRenderOrder &rhs) const; + + inline const QGLSceneNode *node() const; + inline void setState(const QGLRenderState &state); + inline QGLRenderState state() const; + inline QGLAbstractEffect *effectiveUserEffect() const; + inline QGL::StandardEffect effectiveStandardEffect() const; + inline QGLMaterial *effectiveMaterial() const; + inline QGLMaterial *effectiveBackMaterial() const; + inline bool effectiveHasEffect() const; +private: + const QGLSceneNode *m_node; + QGLRenderState m_state; +}; + + +inline bool QGLRenderOrder::isValid() const +{ + return m_node; +} + +inline bool QGLRenderOrder::operator!=(const QGLRenderOrder &rhs) const +{ + return !isEqual(rhs); +} + +inline bool QGLRenderOrder::operator==(const QGLRenderOrder &rhs) const +{ + return isEqual(rhs); +} + +inline bool QGLRenderOrder::operator<(const QGLRenderOrder &rhs) const +{ + return isLessThan(rhs); +} + +inline const QGLSceneNode *QGLRenderOrder::node() const +{ + return m_node; +} + +inline QGLRenderState QGLRenderOrder::state() const +{ + return m_state; +} + +inline void QGLRenderOrder::setState(const QGLRenderState &state) +{ + m_state = state; +} + +inline QGLAbstractEffect *QGLRenderOrder::effectiveUserEffect() const +{ + QGLAbstractEffect *result = 0; + if (m_node) + { + if (m_node->userEffect()) + result = m_node->userEffect(); + else if (m_state.userEffect()) + result = m_state.userEffect(); + } + return result; +} + +inline QGL::StandardEffect QGLRenderOrder::effectiveStandardEffect() const +{ + QGL::StandardEffect result = QGL::FlatColor; + if (m_node) + { + if (m_node->hasEffect()) + result = m_node->effect(); + else if (m_state.hasEffect()) + result = m_state.standardEffect(); + } + return result; +} + +inline QGLMaterial *QGLRenderOrder::effectiveMaterial() const +{ + QGLMaterial *result = 0; + if (m_node) + { + if (m_node->material()) + result = m_node->material(); + else if (m_state.material()) + result = m_state.material(); + } + return result; +} + +inline QGLMaterial *QGLRenderOrder::effectiveBackMaterial() const +{ + QGLMaterial *result = 0; + if (m_node) + { + if (m_node->backMaterial()) + result = m_node->backMaterial(); + else if (m_state.backMaterial()) + result = m_state.backMaterial(); + } + return result; +} + +inline bool QGLRenderOrder::effectiveHasEffect() const +{ + bool result = false; + if (m_node) + { + if (m_node->hasEffect()) + result = true; + else + result = m_state.hasEffect(); + } + return result; +} + +inline uint qHash(const QGLRenderOrder &order) +{ + quint64 result = order.effectHash(); + return result ^ reinterpret_cast<quint64>(order.effectiveMaterial()); +} + +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QGLRenderOrder &order); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGLRENDERORDER_H diff --git a/src/threed/scene/qglrenderordercomparator.cpp b/src/threed/scene/qglrenderordercomparator.cpp new file mode 100644 index 000000000..36da2838a --- /dev/null +++ b/src/threed/scene/qglrenderordercomparator.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglrenderordercomparator.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLRenderOrderComparator + \brief The QGLRenderOrderComparator class compares QGLRenderOrder instances. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::scene + + The QGLRenderOrderComparator class works with the QGLRenderSequencer and + QGLRenderOrder classes to optimize the rendering order of scene nodes. + + This class is responsible for comparing QGLRenderOrder instances for + the QGLRenderSequencer. + + Custom render orders may be created by sub-classing QGLRenderOrderComparator + and reimplementing the following method: + \list + \i bool operator()(const QGLRenderOrder &lhs, const QGLRenderOrder &rhs) + \endlist + + Then set an instance of your class onto QGLPainter: + + \code + void MyView::initializeGL(QGLPainter *painter) + { + painter->renderSequencer()->setRenderOrderComparator(new MyRenderOrderComparator); + } + + void MyView::paintGL(QGLPainter *painter) + { + // draw uses QGLRenderOrder sub-class instances from m_renderOrderFactory + complexScene->draw(painter); + } + \endcode + + See the QGLRenderOrder class documentation for more details. + + \sa QGLRenderOrder +*/ + +/*! + \fn QGLRenderOrderComparator::QGLRenderOrderComparator() + Construct a new QGLRenderOrderComparator. +*/ + +/*! + \fn QGLRenderOrderComparator::~QGLRenderOrderComparator() + Destroys this QGLRenderOrderComparator, recovering any resources. +*/ + +/*! + Returns true if the \a lhs render order is less than the \a rhs; + otherwise returns false. + + Reimplement this function when creating custom render orders. +*/ +bool QGLRenderOrderComparator::isLessThan(const QGLRenderOrder &lhs, const QGLRenderOrder &rhs) +{ + bool result = false; + bool lhsHasEffect = lhs.effectiveHasEffect(); + bool rhsHasEffect = rhs.effectiveHasEffect(); + if (lhsHasEffect && rhsHasEffect) + { + QGLAbstractEffect *eff = lhs.effectiveUserEffect(); + if (eff) + result = (eff < rhs.effectiveUserEffect()); + else + result = !rhs.effectiveUserEffect() && + (lhs.effectiveStandardEffect() < rhs.effectiveStandardEffect()); + } + else + { + result = !lhsHasEffect; + } + if (!result) + { + result = (lhs.effectiveMaterial() < rhs.effectiveMaterial()); + } + if (!result) + { + result = (lhs.effectiveBackMaterial() < rhs.effectiveBackMaterial()); + } + return result; +} + +/*! + Returns true if the \a lhs render order is equal to the \a rhs; + otherwise returns false. + + Reimplement this function when creating custom render orders. +*/ +bool QGLRenderOrderComparator::isEqualTo(const QGLRenderOrder &lhs, const QGLRenderOrder &rhs) +{ + bool result = false; + bool lhsHasEffect = lhs.effectiveHasEffect(); + bool rhsHasEffect = rhs.effectiveHasEffect(); + if (lhsHasEffect && rhsHasEffect) + { + QGLAbstractEffect *eff = lhs.effectiveUserEffect(); + if (eff) + result = (eff == rhs.effectiveUserEffect()); + else + result = (lhs.effectiveStandardEffect() == rhs.effectiveStandardEffect()); + } + else + { + result = (lhsHasEffect == rhsHasEffect); + } + if (result) + { + result = (lhs.effectiveMaterial() == rhs.effectiveMaterial()); + } + if (result) + { + result = (lhs.effectiveBackMaterial() == rhs.effectiveBackMaterial()); + } + return result; +} + +QT_END_NAMESPACE diff --git a/src/threed/scene/qglrenderordercomparator.h b/src/threed/scene/qglrenderordercomparator.h new file mode 100644 index 000000000..e71102751 --- /dev/null +++ b/src/threed/scene/qglrenderordercomparator.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLRenderOrderComparator_H +#define QGLRenderOrderComparator_H + +#include "qglrenderorder.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLSceneNode; + +class Q_QT3D_EXPORT QGLRenderOrderComparator +{ +public: + QGLRenderOrderComparator() {} + virtual ~QGLRenderOrderComparator() {} + + virtual bool isLessThan(const QGLRenderOrder &lhs, const QGLRenderOrder &rhs); + virtual bool isEqualTo(const QGLRenderOrder &lhs, const QGLRenderOrder &rhs); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGLRenderOrderComparator_H diff --git a/src/threed/scene/qglrendersequencer.cpp b/src/threed/scene/qglrendersequencer.cpp new file mode 100644 index 000000000..b819cabf9 --- /dev/null +++ b/src/threed/scene/qglrendersequencer.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglrendersequencer.h" +#include "qglrenderorder.h" +#include "qglpainter.h" +#include "qglrenderordercomparator.h" +#include "qglrenderstate.h" + +#include <QtCore/qstack.h> + +/*! + \class QGLRenderSequencer + \brief The QGLRenderSequencer class orders the rendering of QGLSceneNode instances. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::scene + + The QGLRenderSequencer class works with the QGLRenderOrderComparator and + QGLRenderOrder classes to optimize the rendering order of scene nodes. + + In general instances of this class are managed by QGLPainter and it should + not be necessary to explicitly create or manipulate them. + + The render sequencer works by tracking instances of QGLRenderOrder objects + in a queue. As the scene graph is traversed during a call to a top-level + node's QGLSceneNode::draw() function, the sequencer adds one QGLRenderOrder + to the queue for each unique combination of rendering attributes. + + The top level scene graph node loops once for each unique combination - it + does this in QGLSceneNode::draw() by calling nextInSequence(). At + each iteration, a current QGLRenderOrder is maintained, and only nodes + matching that order - as determined by \l{QGLRenderOrder::operator==()} - + are rendered in that pass. Non-matching nodes are added to a queue in the + order specified by \l{QGLRenderOrder::operator<()}. + + + Once an iteration/pass of + the scene graph is done, the next order is pulled from the front of the queue + and the current QGLRenderOrder is set to it. + + Since the rendering attributes at a node are a function both of that node, + and attributes inherited from its parents, and since a given node may appear + multiple times at different places in the scene, it can thus have different + attributes and orders in each place. So there is no one-to-one mapping + between nodes and attributes. + + To deal with this, QGLRenderOrder mappings are discovered during rendering. + There is no discovery pass. First, the initial QGLRenderOrder is lazily set + when the first geometry is actually drawn to the GPU - latching in that order + as the first current order. From that point, orders discovered that are + distinct from the current one are skipped in this rendering pass - by returning + false from renderInSequence() - and are instead added to the queue for rendering + on a subsequent pass. + + When the final pass has been made, renderInSequence() returns false to the + top level QGLSceneNode, indicating that looping over passes is complete. + + \sa QGLRenderOrder +*/ + +class QGLRenderSequencerPrivate +{ +public: + QGLRenderSequencerPrivate(QGLPainter *painter); + ~QGLRenderSequencerPrivate(); + QGLSceneNode *top; + QLinkedList<QGLRenderOrder> queue; + QStack<QGLRenderState> stack; + QSet<QGLRenderOrder> exclude; + QGLRenderOrder current; + QGLPainter *painter; + QGLRenderOrderComparator *compare; + bool latched; +}; + +QGLRenderSequencerPrivate::QGLRenderSequencerPrivate(QGLPainter *painter) + : top(0) + , current(QGLRenderOrder()) + , painter(painter) + , compare(new QGLRenderOrderComparator) + , latched(false) +{ +} + +QGLRenderSequencerPrivate::~QGLRenderSequencerPrivate() +{ + delete compare; +} + +/*! + Construct a new QGLRenderSequencer for the \a painter. +*/ +QGLRenderSequencer::QGLRenderSequencer(QGLPainter *painter) + : d(new QGLRenderSequencerPrivate(painter)) +{ +} + +/*! + Sets the render sequencer to operate on \a painter. +*/ +void QGLRenderSequencer::setPainter(QGLPainter *painter) +{ + d->painter = painter; +} + +/*! + Returns the current top node of the rendering tree, or NULL if the + sequencer has been reset. +*/ +QGLSceneNode *QGLRenderSequencer::top() const +{ + return d->top; +} + +/*! + Sets the current top node of the rendering tree to \a top. +*/ +void QGLRenderSequencer::setTop(QGLSceneNode *top) +{ + d->top = top; +} + +/*! + Reset this sequencer to start from the top of the scene graph again. + After this call the top() function will return NULL, and any scene + node passed to the renderInSequence() function will be treated as the + top of the scene graph. Also effects and materials will be ignored + until latched in - QGLPainter::draw() will call latch() to achieve this. + + \sa top() +*/ +void QGLRenderSequencer::reset() +{ + d->top = 0; + d->latched = false; + d->exclude.clear(); + d->stack.clear(); + d->current = QGLRenderOrder(); +} + +/*! + Returns true if there is a next rendering state in the queue; and if there + is a new order will be pulled from the queue, and its rendering attributes + - materials, effects and so on - will be applied to the painter for this + sequencer. Returns false when no more rendering states are queued and + scene is completely rendered. +*/ +bool QGLRenderSequencer::nextInSequence() +{ + bool nextAvailable = true; + if (d->queue.size() > 0) + { + // process thru next render order + d->current = d->queue.takeFirst(); + } + else + { + // end top level loop + nextAvailable = false; + } + return nextAvailable; +} + +/*! + Returns true, when this \a node should be rendered, in the order dictated + by QGLRenderOrder objects generated by the current render order Comparator. + The \a node must be non-NULL. + + When this function returns false, indicating that rendering should be + skipped for the current pass, a check is made to ensure that a state object + for this nodes rendering attributes is queued up for a later pass. + + To customize the ordering, reimplement QGLRenderOrder and + QGLRenderOrderComparator; then set the custom Comparator onto the current + painter using setcompare(). + + \sa reset(), top(), nextInSequence() +*/ +bool QGLRenderSequencer::renderInSequence(QGLSceneNode *node) +{ + Q_ASSERT(node); + Q_ASSERT(d->top); + bool doRender = true; + QGLRenderState state; + if (!d->stack.empty()) + state = d->stack.top(); + QGLRenderOrder o(node, state); + if (!d->current.isValid()) + d->current = o; + if (d->latched && !d->compare->isEqualTo(o, d->current)) + { + if (!d->exclude.contains(o)) + insertNew(o); + doRender = false; + } + else + { + if (!d->latched) + d->exclude.insert(o); + } + return doRender; +} + +/*! + Marks the render state for the \a node as started for this rendering pass. Call + this before rendering \a node, or any child nodes of \a node. + + Once the rendering state is no longer valid, call endState(). + + To actually apply the effective state, as inherited from previous calls to + beginState() call the applyState() function. + + \sa endState(), applyState() +*/ +void QGLRenderSequencer::beginState(QGLSceneNode *node) +{ + QGLRenderState state; + if (!d->stack.empty()) + state = d->stack.top(); + state.updateFrom(node); + d->stack.push(state); +} + +/*! + Marks the render state for the \a node as done for this rendering pass. + + If a node has called beginState(), then this function must be called to maintain + the rendering stack. If this function call is not matched by a previous + beginState() call undefined behaviour may result. In debug mode it may assert. + + \sa beginState(), applyState() +*/ +void QGLRenderSequencer::endState(QGLSceneNode *node) +{ +#ifndef QT_NO_DEBUG_STREAM + const QGLSceneNode *n = d->stack.top().node(); + Q_UNUSED(n); + Q_UNUSED(node); + Q_ASSERT(n == node); +#endif + d->stack.pop(); +} + +/*! + Applies the current rendering state to the painter for this sequencer. + + \sa beginState(), endState() +*/ +void QGLRenderSequencer::applyState() +{ + d->latched = true; + QGLRenderState s = d->stack.top(); + if (s.hasEffect() && !d->painter->isPicking()) + { + if (s.userEffect()) + { + if (d->painter->userEffect() != s.userEffect()) + d->painter->setUserEffect(s.userEffect()); + } + else + { + if (d->painter->userEffect() || + d->painter->standardEffect() != s.standardEffect()) + d->painter->setStandardEffect(s.standardEffect()); + } + } + if (s.material() && !d->painter->isPicking()) + { + QGLMaterial *mat = s.material(); + if (1) //FIXME: d->painter->faceMaterial(QGL::FrontFaces) != mat) + { + d->painter->setFaceMaterial(QGL::FrontFaces, mat); + int texUnit = 0; + for (int i = 0; i < mat->textureLayerCount(); ++i) + { + QGLTexture2D *tex = mat->texture(i); + if (tex) + { + d->painter->glActiveTexture(GL_TEXTURE0 + texUnit); + tex->bind(); + ++texUnit; + } + } + } + } +} + +void QGLRenderSequencer::insertNew(const QGLRenderOrder &order) +{ + QLinkedList<QGLRenderOrder>::iterator it = d->queue.begin(); + for ( ; it != d->queue.end(); ++it) + { + const QGLRenderOrder o = *it; + if (d->compare->isLessThan(order, o)) + break; + } + d->queue.insert(it, order); + d->exclude.insert(order); +} + +/*! + Returns the current render order comparator. By default this is an + instance of the base class QGLRenderOrderComparator. + + \sa setComparator() +*/ +QGLRenderOrderComparator *QGLRenderSequencer::comparator() const +{ + return d->compare; +} + +/*! + Sets the current render order \a comparator. This is only needed if a + custom rendering order is to be created. The argument \a comparator + must be non-null, or undefined behaviour will result. + + Any existing current comparator is destroyed. + + \sa renderInSequence(), comparator() +*/ +void QGLRenderSequencer::setComparator(QGLRenderOrderComparator *comparator) +{ + Q_ASSERT(comparator); + delete d->compare; + d->compare = comparator; +} diff --git a/src/threed/scene/qglrendersequencer.h b/src/threed/scene/qglrendersequencer.h new file mode 100644 index 000000000..a2eeffac6 --- /dev/null +++ b/src/threed/scene/qglrendersequencer.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGLRENDERSEQUENCER_H +#define QGLRENDERSEQUENCER_H + +#include <QtCore/qlinkedlist.h> +#include <QtCore/qset.h> + +#include "qglrenderorder.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLSceneNode; +class QGLPainter; +class QGLRenderOrderComparator; +class QGLRenderSequencerPrivate; + +class Q_QT3D_EXPORT QGLRenderSequencer +{ +public: + explicit QGLRenderSequencer(QGLPainter *painter); + void setPainter(QGLPainter *painter); + bool renderInSequence(QGLSceneNode *node); + bool nextInSequence(); + void beginState(QGLSceneNode *node); + void endState(QGLSceneNode *node); + void reset(); + QGLSceneNode *top() const; + void setTop(QGLSceneNode *top); + QGLRenderOrderComparator *comparator() const; + void setComparator(QGLRenderOrderComparator *comparator); + void applyState(); +private: + void insertNew(const QGLRenderOrder &order); + + QGLRenderSequencerPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGLRENDERSEQUENCER_H diff --git a/src/threed/scene/qglrenderstate.cpp b/src/threed/scene/qglrenderstate.cpp new file mode 100644 index 000000000..8a2fa6cfe --- /dev/null +++ b/src/threed/scene/qglrenderstate.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglrenderstate.h" + +/*! + \class QGLRenderState + \brief The QGLRenderState class encapsulates the states of a rendering pass. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::scene + + The QGLRenderState class works with the QGLRenderOrder class to optimize + the rendering order of scene nodes. + + In general instances of this class are managed by the render sequencer + and it should not be necessary to explicitly create or manipulate them. + + A QGLRenderState instance encapsulates rendering properties for a given + path through the rendering process. The state includes properties such as + effects, and materials; which may either be directly set on a scene node, + or inherited from its parents. + + QGLRenderState may be subclassed in advanced applications to provide a + different inheritance pattern for rendering properties - reimplement the + updateFrom() function to do this. The default implementation simply sets + an effect on the state if the argument node has an effect (found by + calling the QGLSceneNode::hasEffect() function), and sets a material on + the state if the node has a non-null material; otherwise the existing + state is left as it is. + + The render sequencer will ensure that the relevant render state is set + onto the painter at the beginning of the pass which renders all nodes + with that state. + + \sa QGLRenderOrder +*/ + +class QGLRenderStatePrivate : public QSharedData +{ +public: + QGLRenderStatePrivate(); + ~QGLRenderStatePrivate(); + QGLRenderStatePrivate *clone() const; + + QBasicAtomicInt ref; + + bool hasEffect; + QGLMaterial *material; + QGLMaterial *backMaterial; + QGL::StandardEffect standardEffect; + QGLAbstractEffect *userEffect; + const QGLSceneNode *node; +}; + +QGLRenderStatePrivate::QGLRenderStatePrivate() + : hasEffect(false) + , material(0) + , backMaterial(0) + , standardEffect(QGL::FlatColor) + , userEffect(0) + , node(0) +{ + ref = 0; +} + +QGLRenderStatePrivate::~QGLRenderStatePrivate() +{ +} + +QGLRenderStatePrivate *QGLRenderStatePrivate::clone() const +{ + QGLRenderStatePrivate *r = new QGLRenderStatePrivate; + r->hasEffect = hasEffect; + r->material = material; + r->backMaterial = backMaterial; + r->standardEffect = standardEffect; + r->userEffect = userEffect; + r->node = node; + return r; +} + +/*! + Creates a new QGLRenderState empty of any values. +*/ +QGLRenderState::QGLRenderState() + : d(0) +{ +} + +/*! + Creates a new QGLRenderState as a copy of \a other +*/ +QGLRenderState::QGLRenderState(const QGLRenderState &other) + : d(other.d) +{ + if (d) + d->ref.ref(); +} + +/*! + Destroys this QGLRenderState recovering and resources. +*/ +QGLRenderState::~QGLRenderState() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Assigns this QGLRenderState to be a copy of \a rhs. +*/ +QGLRenderState &QGLRenderState::operator=(const QGLRenderState &rhs) +{ + if (d != rhs.d) + { + if (d && !d->ref.deref()) + delete d; + d = rhs.d; + if (d) + d->ref.ref(); + } + return *this; +} + +/*! + Sets the values of this QGLRenderState from the \a node, where + the node has a value. For example if the \a node has an effect + but no material, then this state will have its effect changed to + that of the node, but this state's material will be unchanged. +*/ +void QGLRenderState::updateFrom(const QGLSceneNode *node) +{ + detach(); + if (node->hasEffect()) + { + d->hasEffect = true; + if (node->userEffect()) + d->userEffect = node->userEffect(); + else + d->standardEffect = node->effect(); + } + if (node->material()) + d->material = node->material(); + if (node->backMaterial()) + d->backMaterial = node->backMaterial(); + d->node = node; +} + +/*! + Returns the user effect stored on this QGLRenderState, or null if no + user effect has been set. The default value is null. +*/ +QGLAbstractEffect *QGLRenderState::userEffect() const +{ + QGLAbstractEffect *e = 0; + if (d) + e = d->userEffect; + return e; +} + +/*! + Returns the standard effect stored on this QGLRenderState. The + default value is QGL::FlatColor. To determine if an effect has + been specifically set call hasEffect(). +*/ +QGL::StandardEffect QGLRenderState::standardEffect() const +{ + QGL::StandardEffect e = QGL::FlatColor; + if (d) + e = d->standardEffect; + return e; +} + +/*! + Returns true is there is a valid effect on this QGLRenderState. The + default value is false. +*/ +bool QGLRenderState::hasEffect() const +{ + bool r = false; + if (d) + r = d->hasEffect; + return r; +} + +/*! + Returns the material stored on this QGLRenderState, or null if no + material has been set. The default value is null. +*/ +QGLMaterial *QGLRenderState::material() const +{ + QGLMaterial *m = 0; + if (d) + m = d->material; + return m; +} + +/*! + Returns the back material stored on this QGLRenderState, or null if no + back material has been set. The default value is null. +*/ +QGLMaterial *QGLRenderState::backMaterial() const +{ + QGLMaterial *m = 0; + if (d) + m = d->backMaterial; + return m; +} + +/*! + Returns the node used to populate this QGLRenderState. +*/ +const QGLSceneNode *QGLRenderState::node() const +{ + const QGLSceneNode *s = 0; + if (d) + s = d->node; + return s; +} + +/*! + Returns true if this is a valid representation of a render state, that + is if it has ever been updated from a node; and false otherwise. +*/ +bool QGLRenderState::isValid() const +{ + if (d && d->node) + return true; + return false; +} + +/*! + \fn bool QGLRenderState::operator==(const QGLRenderState &rhs) const + Returns true if this state is equal to \a rhs, that is if each of the + materials, effects and values are equal to those of \a rhs. +*/ +void QGLRenderState::detach() +{ + if (!d) // lazy creation of data block + { + d = new QGLRenderStatePrivate; + d->ref.ref(); + } + else + { + if (d->ref > 1) // being shared, must detach + { + QGLRenderStatePrivate *temp = d->clone(); + d->ref.deref(); + d = temp; + d->ref.ref(); + } + } +} + +/*! + Returns a hash value representing this state. +*/ +uint QGLRenderState::hash() const +{ + const QByteArray bytes((const char *)d, sizeof(d)); + return qHash(bytes); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QGLRenderState &order) +{ + dbg << "QGLRenderState" << &order << "-- user effect:" << order.userEffect() + << "-- standardEffect:" << order.standardEffect() + << "-- hasEffect:" << order.hasEffect() + << "-- material:" << order.material() + << "-- back material:" << order.backMaterial() + << "-- node:" << order.node(); + return dbg; +} + +#endif diff --git a/src/threed/scene/qglrenderstate.h b/src/threed/scene/qglrenderstate.h new file mode 100644 index 000000000..4f2593cc9 --- /dev/null +++ b/src/threed/scene/qglrenderstate.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGLRENDERSTATE_H +#define QGLRENDERSTATE_H + +#include "qglnamespace.h" +#include "qglscenenode.h" + +#include <QtCore/qshareddata.h> + +class QGLAbstractEffect; +class QGLMaterial; + +class QGLRenderStatePrivate; + +class Q_QT3D_EXPORT QGLRenderState +{ +public: + QGLRenderState(); + QGLRenderState(const QGLRenderState &other); + virtual ~QGLRenderState(); + QGLRenderState &operator=(const QGLRenderState &rhs); + virtual void updateFrom(const QGLSceneNode *node); + QGLAbstractEffect *userEffect() const; + QGL::StandardEffect standardEffect() const; + bool hasEffect() const; + QGLMaterial *material() const; + QGLMaterial *backMaterial() const; + const QGLSceneNode *node() const; + uint hash() const; + bool operator==(const QGLRenderState &rhs) const + { + if (userEffect() != rhs.userEffect()) + return false; + if (standardEffect() != rhs.standardEffect()) + return false; + if (hasEffect() != rhs.hasEffect()) + return false; + if (material() != rhs.material()) + return false; + if (backMaterial() != rhs.backMaterial()) + return false; + return true; + } + bool isValid() const; +private: + void detach(); + QGLRenderStatePrivate *d; +}; + +inline uint qHash(const QGLRenderState &s) +{ + return s.hash(); +} + +#ifndef QT_NO_DEBUG_STREAM +#include <QtCore/qdebug.h> +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QGLRenderState &order); +#endif + +#endif // QGLRENDERSTATE_H diff --git a/src/threed/scene/qglsceneformatplugin.cpp b/src/threed/scene/qglsceneformatplugin.cpp new file mode 100644 index 000000000..d1b26b9f7 --- /dev/null +++ b/src/threed/scene/qglsceneformatplugin.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglsceneformatplugin.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLSceneFormatHandler + \brief The QGLSceneFormatHandler class defines the common format I/O interface for loading 3D scene formats. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::scene + + \sa QGLSceneFormatPlugin +*/ + +class QGLSceneFormatHandlerPrivate +{ +public: + QGLSceneFormatHandlerPrivate() + { + device = 0; + } + + QIODevice *device; + QString format; + QUrl url; +}; + +/*! + Constructs a 3D scene format handler. +*/ +QGLSceneFormatHandler::QGLSceneFormatHandler() +{ + d_ptr = new QGLSceneFormatHandlerPrivate(); +} + +/*! + Destroys this 3D scene format handler. +*/ +QGLSceneFormatHandler::~QGLSceneFormatHandler() +{ + delete d_ptr; +} + +/*! + Returns the device currently assigned to the 3D scene format handler. + Returns null if no device has been assigned. + + \sa setDevice() +*/ +QIODevice *QGLSceneFormatHandler::device() const +{ + return d_ptr->device; +} + +/*! + Sets the device for this 3D scene format handler to \a device. + The handler will use this device when reading 3D scenes. + + The device can only be set once and must be set before calling + read(). If you need to read multiple files, construct multiple + instances of the appropriate QGLSceneFormatHandler subclass. + + \sa device() +*/ +void QGLSceneFormatHandler::setDevice(QIODevice *device) +{ + d_ptr->device = device; +} + +/*! + Returns the format that is currently assigned to this 3D scene + format handler. Returns an empty string if no format has been assigned. + + \sa setFormat() +*/ +QString QGLSceneFormatHandler::format() const +{ + return d_ptr->format; +} + +/*! + Sets the format of this 3D scene format handler to \a format. + + \sa format() +*/ +void QGLSceneFormatHandler::setFormat(const QString& format) +{ + d_ptr->format = format; +} + +/*! + Returns the url of the data provided by device(). This is + typically used when the data in device() refers to additional + files that need to be located relative to the original + directory. + + \sa setUrl() +*/ +QUrl QGLSceneFormatHandler::url() const +{ + return d_ptr->url; +} + +/*! + Sets the \a url of the data provided by device(). + + \sa url() +*/ +void QGLSceneFormatHandler::setUrl(const QUrl& url) +{ + d_ptr->url = url; +} + +/*! + Decodes and applies \a options to this handler. Generally this will be + called by the QGLAbstractScene::loadScene() method prior to reading + the model data with read(). Exactly what the string value may contain + and the meaning of the encoded options depends on each individual plugin. + + This default implementation simply does nothing. +*/ +void QGLSceneFormatHandler::decodeOptions(const QString &options) +{ + Q_UNUSED(options); +} + +/*! + \fn QGLAbstractScene *QGLSceneFormatHandler::read() + + Reads a 3D scene from device() and returns it. Returns null if + the format of device() is invalid and a scene could not be read. +*/ + +/*! + \class QGLSceneFormatFactoryInterface + \brief The QGLSceneFormatFactoryInterface class provides the factory interface for QGLSceneFormatPlugin. + \internal +*/ + +/*! + \class QGLSceneFormatPlugin + \brief The QGLSceneFormatPlugin class defines an interface for loading 3D object and scene formats. + \ingroup qt3d + \ingroup qt3d::scene + + \sa QGLSceneFormatHandler +*/ + +/*! + Constructs a 3D scene format plugin with the given \a parent. This is + invoked automatically by the Q_EXPORT_PLUGIN2() macro. +*/ +QGLSceneFormatPlugin::QGLSceneFormatPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys this 3D scene format plugin. This is invoked automatically + when the plugin is unloaded. +*/ +QGLSceneFormatPlugin::~QGLSceneFormatPlugin() +{ +} + +/*! + \fn QStringList QGLSceneFormatPlugin::keys() const + + Returns the list of format keys this plugin supports. These keys + are usually the names of the 3D scene formats that are implemented in + the plugin (e.g., "3ds", "obj", etc). The returned elements must + be in lower case. + + \sa create() +*/ + +/*! + \fn QGLSceneFormatHandler *QGLSceneFormatPlugin::create(QIODevice *device, const QUrl& url, const QString &format) const + + Creates and returns a QGLSceneFormatHandler for handling the data in + \a device according to \a format. The \a url specifies the original + location of the data for resolving relative resource references. + + \sa keys() +*/ + +QT_END_NAMESPACE diff --git a/src/threed/scene/qglsceneformatplugin.h b/src/threed/scene/qglsceneformatplugin.h new file mode 100644 index 000000000..8ea069626 --- /dev/null +++ b/src/threed/scene/qglsceneformatplugin.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLSCENEFORMATPLUGIN_H +#define QGLSCENEFORMATPLUGIN_H + +#include "qt3dglobal.h" +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> +#include <QtCore/qurl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLAbstractScene; +class QGLSceneFormatHandlerPrivate; + +class Q_QT3D_EXPORT QGLSceneFormatHandler +{ +public: + QGLSceneFormatHandler(); + virtual ~QGLSceneFormatHandler(); + + QIODevice *device() const; + void setDevice(QIODevice *device); + + QString format() const; + void setFormat(const QString& format); + + QUrl url() const; + void setUrl(const QUrl& url); + + virtual QGLAbstractScene *read() = 0; + + virtual void decodeOptions(const QString &options); + +private: + QGLSceneFormatHandlerPrivate *d_ptr; +}; + +struct Q_QT3D_EXPORT QGLSceneFormatFactoryInterface : public QFactoryInterface +{ + virtual QGLSceneFormatHandler *create(QIODevice *device, const QUrl& url, const QString &format = QString()) const = 0; +}; + +#define QGLSceneFormatFactoryInterface_iid \ + "com.trolltech.Qt.QGLSceneFormatFactoryInterface" +Q_DECLARE_INTERFACE(QGLSceneFormatFactoryInterface, QGLSceneFormatFactoryInterface_iid) + +class Q_QT3D_EXPORT QGLSceneFormatPlugin : public QObject, public QGLSceneFormatFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QGLSceneFormatFactoryInterface:QFactoryInterface) +public: + explicit QGLSceneFormatPlugin(QObject *parent = 0); + virtual ~QGLSceneFormatPlugin(); + + virtual QStringList keys() const = 0; + virtual QGLSceneFormatHandler *create(QIODevice *device, const QUrl& url, const QString &format = QString()) const = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/scene/qglscenenode.cpp b/src/threed/scene/qglscenenode.cpp new file mode 100644 index 000000000..839afa51e --- /dev/null +++ b/src/threed/scene/qglscenenode.cpp @@ -0,0 +1,1794 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglabstractscene.h" +#include "qglscenenode.h" +#include "qglscenenode_p.h" +#include "qglpicknode.h" +#include "qglpainter.h" +#include "qgeometrydata.h" +#include "qglmaterialcollection.h" +#include "qmatrix4x4.h" +#include "qglrendersequencer.h" +#include "qglabstracteffect.h" +#include "qgraphicstransform3d.h" + +#include <QtGui/qmatrix4x4.h> +#if !defined(QT_NO_THREAD) +#include <QtCore/qthread.h> +#include <QtGui/qapplication.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QGLSceneNode + \brief The QGLSceneNode class defines a node in a 3D scene. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::scene + + QGLSceneNode represents one component of a scene. The QGLSceneNode + class manages materials, effects and transformations for itself and + for its child nodes. + + As a general approach to 3D applications, a tree of QGLSceneNodes may + be constructed, geometry added to them, materials and transformations + applied, all during application initialization; and then by simply + calling the draw() function the scene is easily rendered for each frame. + + \section1 Geometry + + Multiple QGLSceneNodes can reference the same geometry, whilst + applying different transformations and treatments to it. Since + QGLSceneNode is a QObject sub class it cannot be copied directly, so + instead use the clone() function for this purpose. + + A scene node allows referencing into sub-parts of geometry, via the start + and count properties. + + The start index is an offset into the geometry at which drawing will start. + The default start index is 0, so that drawing will start from the beginning + of the geometry. The count dictates how many vertices will be drawn. The + default count is 0, which instructs the underlying geometry to draw all + vertices. + + A node may have no geometry, that is \c{geometry().count() == 0}. This is + useful for example to have one node controlling or collecting together + several child nodes to be manipulated as a unit. + + \section1 Materials + + Also a node may have a local material. This allows drawing the same geometry + with different materials (which includes different textures). + + When accessing a QGLSceneNode via QML, or for simple applications, the + pointer based material functions are convenient and intuitive, saving the + trouble of adding the material pointer to the correct palette: + \list + \i material() + \i setMaterial() + \i backMaterial() + \i setBackMaterial() + \endlist + + For more complex applications; for example building model loaders, or for + larger scenes; where you need to explicitly manage materials via a palette, + use the index based functions: + \list + \i materialIndex() + \i setMaterialIndex() + \i backMaterialIndex() + \i setBackMaterialIndex() + \endlist + + The behaviour of both with respect to faces is the same - if a material() + is specified but no backMaterial() is specified, then the material() is + applied to both faces; if both material() and backMaterial() are non-null + then they are applied to their specific faces. + + \section1 Transformations + + Typically the local transformation matrix is set by the process that + constructed the node: in the case of an imported model, it is likely + to have been specified by the model file. To make individual changes + to the location or orientation of this node, use the position() and + transforms() properties. + + \section1 Scene Graph + + Use childNodes() to obtain the list of child nodes, and add and remove + child nodes by the addNode() and removeNode() methods. If a QGLSceneNode + is constructed with a QGLSceneNode parent, then addNode() will be + called implicitly on the parent. + + A child may be a child multiple times, a child may be under more than one + parent, and several parents may reference the same child. There is however + no protection against cycles, so a child must not be a parent of itself, + even if indirectly. + + A child node for the purposes of rendering means a child added via the + addNode() method. The default QGLSceneNode constructor will check to + see if its parent is a QGLSceneNode and add itself via the addNode() + function if it is. + + To help debug a scene, use the qDumpScene() function to get a printout + on stderr of the entire structure of the scene below the argument node. + + \section1 Debugging Lighting Normals + + The ViewNormals option is an advanced feature for use when inspecting + and debugging models or geometry in a scene. The lighting normals + are displayed as a straight line from the vertex pointing in + the direction of the lighting normal. This is useful for + example to show where normals are inverted or wrongly + calculated. + + The setting of the ViewNormals flag is not propagated to child nodes, + instead set the flag to true for the node or nodes where its + needed. To set the flag on all child nodes use code like: + + \code + foreach (QGLSceneNode *node, scene.allChildren()) + node->setNormalViewEnabled(true); + \endcode + + \image spiky-teapot.png + + \sa QGLAbstractScene +*/ + +/*! + \enum QGLSceneNode::Option + This enum defines option flags for QGLSceneNode. + + \value CullBoundingBox Perform a cull using boundingBox() before + attempting to draw the geometry(). Default is true. + \value ViewNormals Enables the display of lighting normals for + debugging purposes. Default is false. + + \sa setOptions() +*/ + +/*! + Constructs a new scene node and attaches it to \a parent. If parent is + a QGLSceneNode then this node is added to it as a child. +*/ +QGLSceneNode::QGLSceneNode(QObject *parent) + : QObject(parent) + , d_ptr(new QGLSceneNodePrivate()) +{ + QGLSceneNode *sceneParent = qobject_cast<QGLSceneNode*>(parent); + if (sceneParent) + sceneParent->addNode(this); +} + +/*! + \internal + Used by clone(). +*/ +QGLSceneNode::QGLSceneNode(QGLSceneNodePrivate *d, QObject *parent) + : QObject(parent) + , d_ptr(d) +{ + QGLSceneNode *sceneParent = qobject_cast<QGLSceneNode*>(parent); + if (sceneParent) + sceneParent->addNode(this); +} + +/*! + Constructs a new scene node referencing \a geometry and attaches it to + \a parent. If parent is a QGLSceneNode then this node is added to it + as a child. +*/ +QGLSceneNode::QGLSceneNode(const QGeometryData &geometry, QObject *parent) + : QObject(parent) + , d_ptr(new QGLSceneNodePrivate()) +{ + Q_D(QGLSceneNode); + d->geometry = geometry; + QGLSceneNode *sceneParent = qobject_cast<QGLSceneNode*>(parent); + if (sceneParent) + sceneParent->addNode(this); +} + +/*! + Destroys this scene node. +*/ +QGLSceneNode::~QGLSceneNode() +{ + Q_D(QGLSceneNode); + + // Detach ourselves from our children. The children will be + // deleted separately when their QObject::parent() deletes them. + for (int index = 0; index < d->childNodes.count(); ++index) + d->childNodes.at(index)->d_ptr->parentNodes.removeOne(this); + + // Detach ourselves from our remaining parents, and notify them + // to update their bounding boxes. This won't be needed if we + // are recursively destroying a tree of nodes because the parent + // already detached from this node above. + for (int index = 0; index < d->parentNodes.count(); ++index) { + QGLSceneNode *parent = d->parentNodes.at(index); + parent->d_ptr->childNodes.removeOne(this); + parent->invalidateBoundingBox(); + } +} + +/*! + Returns the drawing options associated with this node. + The default is CullBoundingBox. + + \sa setOptions(), setOption() +*/ +QGLSceneNode::Options QGLSceneNode::options() const +{ + Q_D(const QGLSceneNode); + return d->options; +} + +/*! + Sets the drawing \a options associated with this node. + + \sa options(), setOption() +*/ +void QGLSceneNode::setOptions(QGLSceneNode::Options options) +{ + Q_D(QGLSceneNode); + if (d->options != options) { + d->options = options; + emit updated(); + } +} + +/*! + Enables or disables \a option according to \a value. + + \sa options(), setOptions() +*/ +void QGLSceneNode::setOption(QGLSceneNode::Option option, bool value) +{ + Q_D(QGLSceneNode); + QGLSceneNode::Options opts = d->options; + if (value) + opts |= option; + else + opts &= ~option; + if (d->options != opts) { + d->options = opts; + emit updated(); + } +} + +/*! + Returns the geometry associated with this node, or a null QGeometryData + if no geometry has been associated with it. + + \sa setGeometry() +*/ +QGeometryData QGLSceneNode::geometry() const +{ + Q_D(const QGLSceneNode); + return d->geometry; +} + +/*! + Sets the geometry associated with this node to be \a geometry. + Typically the \a geometry will be some type of mesh object. The + default implementation of the QGLSceneNode::draw() method will call + the geometry's draw() method. + + \sa geometry() +*/ +void QGLSceneNode::setGeometry(QGeometryData geometry) +{ + Q_D(QGLSceneNode); + d->geometry = geometry; + emit updated(); +} + +/*! + Returns a bounding box for the portion of the geometry referenced by + this scene node. If the value of start() is 0, and count() is the same + as geometry()->size() then the bounding box will be the same as + geometry()->boundingBox(). However if the scene node only references + some part of the geometry, a bounding box for this section is calculated. + + If this scene node has child nodes then the bounding box will be the + calculated union of the bounding box for this nodes geometry (if any) and + the bounding boxes of the children. + + The calculated value is cached and returned on subsequent calls, but + could be expensive to calculate initially. +*/ +QBox3D QGLSceneNode::boundingBox() const +{ + Q_D(const QGLSceneNode); + if (d->boxValid) + return d->bb; + d->bb = QBox3D(); + if (d->geometry.count() > 0) + { + if (d->start == 0 && (d->count == d->geometry.count() || d->count == 0)) + { + d->bb = d->geometry.boundingBox(); + } + else + { + QGL::IndexArray indices = d->geometry.indices(); + for (int i = d->start; i < (d->start + d->count); ++i) + { + int ix = indices.at(i); + d->bb.unite(d->geometry.vertexAt(ix)); + } + } + } + QList<QGLSceneNode*>::const_iterator it = d->childNodes.constBegin(); + for ( ; it != d->childNodes.constEnd(); ++it) + { + QGLSceneNode *n = *it; + QBox3D b = n->boundingBox(); + d->bb.unite(b); + } + d->bb.transform(transform()); + d->boxValid = true; + return d->bb; +} + +// Calculate the resulting matrix from the position, local transform, +// and list of transforms. +QMatrix4x4 QGLSceneNode::transform() const +{ + Q_D(const QGLSceneNode); + QMatrix4x4 m; + if (!d->translate.isNull()) + m.translate(d->translate); + if (!d->localTransform.isIdentity()) + m *= d->localTransform; + for (int index = d->transforms.size() - 1; index >= 0; --index) + d->transforms.at(index)->applyTo(&m); + return m; +} + +/*! + Returns the local transform associated with this node. If no + local transform has been explicitly set, this method returns a + QMatrix4x4 set to the identity matrix. + + The local transform is typically set during model loading or + geometry construction, and is a feature of the geometry. + + In general to change the location or orientation of the node + use the position() or transforms() properties instead. + + \sa setLocalTransform(), position(), transforms() +*/ +QMatrix4x4 QGLSceneNode::localTransform() const +{ + Q_D(const QGLSceneNode); + return d->localTransform; +} + +/*! + Sets the local transform associated with this node to be \a transform. + The default implementation of the QGLSceneNode::draw() method will + apply this transform to the QGLPainter before drawing any geometry. + + \sa localTransform() +*/ +void QGLSceneNode::setLocalTransform(const QMatrix4x4 &transform) +{ + Q_D(QGLSceneNode); + if (d->localTransform != transform) + { + d->localTransform = transform; + emit updated(); + invalidateTransform(); + } +} + +/*! + \property QGLSceneNode::position + \brief The amounts of x, y and z axis translation for this node. + + Since most nodes are situated relative to \c{(0, 0, 0)} when imported as + part of a model or constructed programatically, the translation is + effectively the position of the model in the scene. + + The x, y and z axis translations can also be specified individually as + separate properties \l x, \l y, and \l z + + \sa x(), y(), z() +*/ +QVector3D QGLSceneNode::position() const +{ + Q_D(const QGLSceneNode); + return d->translate; +} + +void QGLSceneNode::setPosition(const QVector3D &p) +{ + Q_D(QGLSceneNode); + if (p != d->translate) + { + d->translate = p; + emit updated(); + invalidateTransform(); + } +} + +/*! + \property QGLSceneNode::x + \brief The amount of x axis translation for this node. + + \sa position() +*/ +qreal QGLSceneNode::x() const +{ + Q_D(const QGLSceneNode); + return d->translate.x(); +} + +void QGLSceneNode::setX(qreal x) +{ + Q_D(QGLSceneNode); + if (x != d->translate.x()) + { + d->translate.setX(x); + emit updated(); + invalidateTransform(); + } +} + +/*! + \property QGLSceneNode::y + \brief The amount of y axis translation for this node. + + \sa position() +*/ +qreal QGLSceneNode::y() const +{ + Q_D(const QGLSceneNode); + return d->translate.y(); +} + +void QGLSceneNode::setY(qreal y) +{ + Q_D(QGLSceneNode); + if (y != d->translate.y()) + { + d->translate.setY(y); + emit updated(); + invalidateTransform(); + } +} + +/*! + \property QGLSceneNode::z + \brief The amount of z axis translation for this node. + + \sa position() +*/ +qreal QGLSceneNode::z() const +{ + Q_D(const QGLSceneNode); + return d->translate.z(); +} + +void QGLSceneNode::setZ(qreal z) +{ + Q_D(QGLSceneNode); + if (z != d->translate.z()) + { + d->translate.setZ(z); + emit updated(); + invalidateTransform(); + } +} + +/*! + Returns the list of transformations to apply to this node. + The default is the empty list. + + The transformations are applied to the node itself, so a + QGraphicsScale3D followed by a QGraphicsTranslation3D will + first scale the node in its local co-ordinate system, + and then translate the node a new location. + + In the mathematical sense, the transformations are applied to + the modelview matrix in the reverse order in which they appear + in this list. + + The position() is applied after all other transformations + have been applied. + + \sa setTransforms(), addTransform(), position() +*/ +QList<QGraphicsTransform3D *> QGLSceneNode::transforms() const +{ + Q_D(const QGLSceneNode); + return d->transforms; +} + +/*! + Sets the list of transformations to apply to this node to \a transforms. + + The transformations are applied to the node itself, so a + QGraphicsScale3D followed by a QGraphicsTranslation3D will + first scale the node in its local co-ordinate system, + and then translate the node a new location. + + In the mathematical sense, the transformations are applied to + the modelview matrix in the reverse order in which they appear + in \a transforms. + + The position() is applied after all other transformations + have been applied. + + \sa transforms(), addTransform(), position() +*/ +void QGLSceneNode::setTransforms(const QList<QGraphicsTransform3D *> &transforms) +{ + Q_D(QGLSceneNode); + for (int index = 0; index < d->transforms.size(); ++index) { + QGraphicsTransform3D *transform = d->transforms.at(index); + disconnect(transform, SIGNAL(transformChanged()), this, SLOT(transformChanged())); + } + d->transforms.clear(); + for (int index = 0; index < transforms.size(); ++index) { + QGraphicsTransform3D *transform = transforms.at(index); + if (transform) { + connect(transform, SIGNAL(transformChanged()), this, SLOT(transformChanged())); + d->transforms.append(transform); + } + } + emit updated(); + invalidateTransform(); +} + +/*! + Adds a single \a transform to this node, to be applied to the + node after all current members of transformations() have been applied. + + In the mathematical sense, \a transform is applied to the modelview + matrix before the current members of transformations() are applied + in reverse order. + + \sa transforms(), setTransforms() +*/ +void QGLSceneNode::addTransform(QGraphicsTransform3D *transform) +{ + Q_D(QGLSceneNode); + if (!transform) + return; // Avoid nulls getting into the transform list. + connect(transform, SIGNAL(transformChanged()), this, SLOT(transformChanged())); + d->transforms.append(transform); + emit updated(); + invalidateTransform(); +} + +/*! + \internal +*/ +void QGLSceneNode::transformChanged() +{ + invalidateTransform(); + emit updated(); +} + +/*! + Returns the drawing mode to use to render geometry(). The default + is QGL::Triangles. + + \sa setDrawingMode() +*/ +QGL::DrawingMode QGLSceneNode::drawingMode() const +{ + Q_D(const QGLSceneNode); + return d->drawingMode; +} + +/*! + Sets the drawing \a mode to use to render geometry(). + + Note: this function changes the drawing mode, but the underlying + geometry() still consists of the triangles that were added. + Thus, this function is only useful for converting the drawing mode + into QGL::Points to display the geometry() as a point cloud + instead of a triangle mesh. The other enums from QGL::DrawingMode + will give unpredictable results. + + \sa drawingMode() +*/ +void QGLSceneNode::setDrawingMode(QGL::DrawingMode mode) +{ + Q_D(QGLSceneNode); + if (d->drawingMode != mode) + { + d->drawingMode = mode; + emit updated(); + } +} + +/*! + Returns the drawing width for this node. + + Drawing width is used only when drawing lines or points (ie. when + the drawing mode is points, lines, line-strips, etc). + + \sa drawingMode() +*/ +qreal QGLSceneNode::drawingWidth() const +{ + Q_D(const QGLSceneNode); + return d->drawingWidth; +} + +/*! + Sets the drawing \a width to the given value. + + Drawing width is used only when drawing lines or points (ie. when + the drawing mode is points, lines, line-strips, etc). + + \sa drawingMode() +*/ +void QGLSceneNode::setDrawingWidth(qreal width) +{ + Q_D(QGLSceneNode); + d->drawingWidth = width; +} + + +/*! + Returns the local effect associated with this node. The default value + is QGL::FlatColor. If the value of hasEffect() is false, then this + the value of effect() is ignored. + + \sa setEffect(), hasEffect() +*/ +QGL::StandardEffect QGLSceneNode::effect() const +{ + Q_D(const QGLSceneNode); + return d->localEffect; +} + +/*! + Sets the local effect associated with this node to be \a effect. hasEffect() + will return true after calling this method. + + The QGLSceneNode::draw() function will ensure that \a effect is applied to the + QGLPainter before drawing any geometry. + + \sa effect(), hasEffect() +*/ +void QGLSceneNode::setEffect(QGL::StandardEffect effect) +{ + Q_D(QGLSceneNode); + if (d->localEffect != effect || !d->hasEffect) { + d->localEffect = effect; + d->hasEffect = true; + emit updated(); + } +} + +/*! + Returns the user effect associated with this node, or NULL if one is not + set. The default value is NULL. If the value of hasEffect() is false, + then this effect is ignored. + + \sa setUserEffect(), hasEffect() +*/ +QGLAbstractEffect *QGLSceneNode::userEffect() const +{ + Q_D(const QGLSceneNode); + return d->customEffect; +} + +/*! + Sets the local effect associated with this node to be the custom + \a effect. hasEffect() will return true after calling this method. + + This custom effect will supersede any standard effect. + + The default implementation of QGLSceneNode::apply() will set this effect + during initialization of the model. + + The default implementation of the QGLSceneNode::draw() method will + ensure that \a effect is applied to the QGLPainter before drawing + any geometry. + + \sa userEffect(), hasEffect() +*/ +void QGLSceneNode::setUserEffect(QGLAbstractEffect *effect) +{ + Q_D(QGLSceneNode); + if (d->customEffect != effect || !d->hasEffect) { + d->customEffect = effect; + d->hasEffect = true; + emit updated(); + } +} + + + /*! + Returns true if the local effect on this node is enabled, otherwise + returns false. + + \sa setEffectEnabled(), setEffect() + */ +bool QGLSceneNode::hasEffect() const +{ + Q_D(const QGLSceneNode); + return d->hasEffect; +} + +/*! + Sets whether the current value of effect() or userEffect() will be + applied to the QGLPainter prior to drawing. If \a enabled is true, + then the effect is applied, otherwise it is not. + + \sa setEffect(), effect(), hasEffect() +*/ +void QGLSceneNode::setEffectEnabled(bool enabled) +{ + Q_D(QGLSceneNode); + if (d->hasEffect != enabled) { + d->hasEffect = enabled; + emit updated(); + } +} + +/*! + Returns the starting index within geometry() that should be used + to render fragments for this scene node. The default value is 0, + indicating that the 0'th logical vertex in geometry() is the start. + + \sa setStart(), count() +*/ +int QGLSceneNode::start() const +{ + Q_D(const QGLSceneNode); + return d->start; +} + +/*! + Sets the \a start index within geometry() that should be used + to render fragments for this scene node. + + \sa start(), setCount() +*/ +void QGLSceneNode::setStart(int start) +{ + Q_D(QGLSceneNode); + if (start != d->start) + { + d->start = start; + emit updated(); + invalidateBoundingBox(); + } +} + +/*! + Returns the count of the vertices to render from geometry() + for this scene node. The default is zero, meaning that this node + uses all vertices from start() up to the last logical vertex + in the underlying geometry(). + + \sa setCount(), start() +*/ +int QGLSceneNode::count() const +{ + Q_D(const QGLSceneNode); + return d->count; +} + +/*! + Sets the \a count of the vertices to render from geometry() + for this scene node. + + \sa count(), setStart() +*/ +void QGLSceneNode::setCount(int count) +{ + Q_D(QGLSceneNode); + if (count != d->count) + { + d->count = count; + emit updated(); + invalidateBoundingBox(); + } +} + +/*! + Returns the material index for this scene node. + + \sa setMaterialIndex() +*/ +int QGLSceneNode::materialIndex() const +{ + Q_D(const QGLSceneNode); + return d->material; +} + +/*! + Sets the material index for this scene node to \a material. + + \sa materialIndex() +*/ +void QGLSceneNode::setMaterialIndex(int material) +{ + Q_D(QGLSceneNode); + if (d->material != material) { + d->material = material; + emit updated(); + } +} + +/*! + Returns the back material index for this scene node. + + \sa setBackMaterialIndex() +*/ +int QGLSceneNode::backMaterialIndex() const +{ + Q_D(const QGLSceneNode); + return d->backMaterial; +} + +/*! + Sets the back material index for this scene node to \a material. + + \sa materialIndex() +*/ +void QGLSceneNode::setBackMaterialIndex(int material) +{ + Q_D(QGLSceneNode); + if (d->backMaterial != material) { + d->backMaterial = material; + emit updated(); + } +} + +/*! + \property QGLSceneNode::material + \brief This property is a pointer to a QGLMaterial instance for this node. + + This material is applied to all faces if the backMaterial() property + is set to null, which is the default. If the backMaterial() property is non-null + then this material is only applied to the front faces. + + To apply a material to the back faces use the backMaterial() property. + + Getting this property is exactly equivalent to + \c{palette()->material(materialIndex())}. + + Setting this property causes the material if not already in this nodes palette to be + added, and then the corresponding index to be set for this scene node. + + Setting this property is equivalent to: + \code + int index = d->palette->indexOf(material); + if (index == -1) + index = d->palette->addMaterial(material); + setMaterialIndex(index); + \endcode + + If setting this property, when no palette exists one is created, as a + convenience - but this is suitable only for experimental code and for + \bold{very small numbers of nodes}. In debug mode a warning is + printed in this case. + + Generally one common palette should be created, and set on each node. This + also allows nodes to share materials and their textures. + + \sa materialIndex(), setMaterialIndex() +*/ +QGLMaterial *QGLSceneNode::material() const +{ + Q_D(const QGLSceneNode); + if (d->palette) + return d->palette->material(d->material); + return 0; +} + +void QGLSceneNode::setMaterial(QGLMaterial *material) +{ + Q_D(QGLSceneNode); + if (!d->palette) + d->palette = new QGLMaterialCollection(this); + int ix = d->palette->indexOf(material); + if (ix == -1) + ix = d->palette->addMaterial(material); + setMaterialIndex(ix); +} + +/*! + \property QGLSceneNode::backMaterial + \brief This property is a pointer to any QGLMaterial instance for this node's back faces. + + This material is applied to the back faces, if non-null. The default value + of this property is null. When this property is null, any non-null material + set on the material() property will be applied to front and back faces. + + To apply a material to the front faces use the material() property. + + Getting this property is exactly equivalent to + \c{palette()->material(backMaterialIndex())}. + + Setting this property causes the material if not already in this nodes palette to be + added, and then the corresponding index to be set for this scene node. + + Setting this property is exactly equivalent to: + \code + int index = d->palette->indexOf(material); + if (index == -1) + index = d->palette->addMaterial(material); + setBackMaterialIndex(index); + \endcode +*/ +QGLMaterial *QGLSceneNode::backMaterial() const +{ + Q_D(const QGLSceneNode); + if (d->palette) + return d->palette->material(d->backMaterial); + return 0; +} + +void QGLSceneNode::setBackMaterial(QGLMaterial *material) +{ + Q_D(QGLSceneNode); + if (!d->palette) + d->palette = new QGLMaterialCollection(this); + int ix = d->palette->indexOf(material); + if (ix == -1) + ix = d->palette->addMaterial(material); + setBackMaterialIndex(ix); +} +/*! + Returns the palette of materials used by this scene node, or NULL + if no palette has been set. + + \sa setPalette() +*/ +QGLMaterialCollection *QGLSceneNode::palette() const +{ + Q_D(const QGLSceneNode); + return d->palette; +} + +/*! + Sets the palette of materials for this scene node to \a palette. + + \sa palette() +*/ +void QGLSceneNode::setPalette(QGLMaterialCollection *palette) +{ + Q_D(QGLSceneNode); + if (d->palette != palette) { + d->palette = palette; + emit updated(); + } +} + +/*! + Returns a list of the child nodes for this node. This list is not + recursively generated, it includes only the nodes which are + immediate children of this node. + + \sa allChildren() +*/ +QList<QGLSceneNode*> QGLSceneNode::children() const +{ + Q_D(const QGLSceneNode); + return d->childNodes; +} + +/*! + Returns a list including recursively all child nodes under + this node. Each child node only appears once, even if it is included + multiple times in the scene graph. + + \sa children() +*/ +QList<QGLSceneNode*> QGLSceneNode::allChildren() const +{ + Q_D(const QGLSceneNode); + QList<QGLSceneNode*> allSceneNodes; + QList<QGLSceneNode*> gather; + QList<QGLSceneNode*>::const_iterator it = d->childNodes.constBegin(); + for ( ; it != d->childNodes.constEnd(); ++it) + if (!gather.contains(*it)) + gather.append(*it); + while (gather.count() > 0) + { + QGLSceneNode *node = gather.takeFirst(); + if (!allSceneNodes.contains(node)) + { + allSceneNodes.append(node); + gather.append(node->children()); + } + } + return allSceneNodes; +} + +/*! + Adds the \a node to the list of child nodes for this node. + + This function does nothing if \a node is null or is already a child + of this node. If the aim is to have the same geometry displayed several times under a + given node, each time with different transformations, use the clone() + call to create copies of the node and then apply the transformations to + the copies. + + Alternatively, create modifier nodes with the transformations and add the + geometry bearing node to each with addNode(): + \code + QGLBuilder builder; + builder << CarWheel(5.0f); // some car wheel geometry + QGLSceneNode wheel = builder.finalizedSceneNode(); + QGLSceneNode frontLeft = new QGLSceneNode(m_sceneRoot); + frontLeft->addNode(wheel); + frontLeft->setPosition(QVector3D(1.0f, 2.0f, 0.0f)); + QGLSceneNode frontRight = new QGLSceneNode(m_sceneRoot); + frontRight->addNode(wheel); + frontRight->setPosition(QVector3D(-1.0f, 2.0f, 0.0f)); + QGLSceneNode backLeft = new QGLSceneNode(m_sceneRoot); + backLeft->addNode(wheel); + backLeft->setPosition(QVector3D(1.0f, -2.0f, 0.0f)); + QGLSceneNode backRight = new QGLSceneNode(m_sceneRoot); + backRight->addNode(wheel); + backRight->setPosition(QVector3D(-1.0f, -2.0f, 0.0f)); + \endcode + + Because a child node can be validly added to many different nodes, + calling addNode() does not normally affect the QObject::parent() + ownership. However, if \a node does not currently have a + QObject::parent(), the parent will be set to this node. + + \sa removeNode(), clone(), addNodes() +*/ +void QGLSceneNode::addNode(QGLSceneNode *node) +{ + Q_D(QGLSceneNode); + if (!node || node->d_ptr->parentNodes.contains(this)) + return; // Invalid node, or already under this parent. + invalidateBoundingBox(); + d->childNodes.append(node); + node->d_ptr->parentNodes.append(this); + if (!node->parent()) + node->setParent(this); + connect(node, SIGNAL(updated()), this, SIGNAL(updated())); + emit updated(); +} + +/*! + Adds the members of \a nodes to the list of child nodes + for this node. + + \sa addNode(), removeNodes() +*/ +void QGLSceneNode::addNodes(const QList<QGLSceneNode *> &nodes) +{ + Q_D(QGLSceneNode); + for (int index = 0; index < nodes.count(); ++index) { + QGLSceneNode *node = nodes.at(index); + if (!node || node->d_ptr->parentNodes.contains(this)) + continue; // Invalid node, or already under this parent. + d->childNodes.append(node); + node->d_ptr->parentNodes.append(this); + if (!node->parent()) + node->setParent(this); + connect(node, SIGNAL(updated()), this, SIGNAL(updated())); + } + invalidateBoundingBox(); + emit updated(); +} + +/*! + Removes the child node matching \a node from this node. + + If the QObject::parent() ownership of \a node was set to this + node, then its parent will be changed to another parent node if it + had multiple parents. + + If \a node had only a single parent, then its parent will be set to null, + effectively detaching it from the QObject ownership rules of the scene + graph. The caller is then responsible for deleting \a node. + + If the QObject::parent() of \a node was not a scene node parent, + then it will be left unmodified. + + \sa addNode(), removeNodes() +*/ +void QGLSceneNode::removeNode(QGLSceneNode *node) +{ + Q_D(QGLSceneNode); + if (!node || !node->d_ptr->parentNodes.contains(this)) + return; // Invalid node or not attached to this parent. + d->childNodes.removeOne(node); + node->d_ptr->parentNodes.removeOne(this); + if (node->parent() == this) { + // Transfer QObject ownership to another parent, or null. + if (!node->d_ptr->parentNodes.isEmpty()) + node->setParent(node->d_ptr->parentNodes[0]); + else + node->setParent(0); + } + disconnect(node, SIGNAL(updated()), this, SIGNAL(updated())); + invalidateBoundingBox(); + emit updated(); +} + +/*! + Removes the members of \a nodes from the list of child nodes + for this node. + + \sa removeNode(), addNodes() +*/ +void QGLSceneNode::removeNodes(const QList<QGLSceneNode *> &nodes) +{ + Q_D(QGLSceneNode); + for (int index = 0; index < nodes.count(); ++index) { + QGLSceneNode *node = nodes.at(index); + if (!node || !node->d_ptr->parentNodes.contains(this)) + continue; // Invalid node or not attached to this parent. + d->childNodes.removeOne(node); + node->d_ptr->parentNodes.removeOne(this); + if (node->parent() == this) { + // Transfer QObject ownership to another parent, or null. + if (!node->d_ptr->parentNodes.isEmpty()) + node->setParent(node->d_ptr->parentNodes[0]); + else + node->setParent(0); + } + disconnect(node, SIGNAL(updated()), this, SIGNAL(updated())); + } + invalidateBoundingBox(); + emit updated(); +} + +void QGLSceneNode::invalidateBoundingBox() const +{ + Q_D(const QGLSceneNode); + d->boxValid = false; + d->invalidateParentBoundingBox(); +} + +void QGLSceneNode::invalidateTransform() const +{ + invalidateBoundingBox(); +} + +void QGLSceneNode::drawNormalIndicators(QGLPainter *painter) +{ + Q_D(QGLSceneNode); + QVector3DArray verts; + QGL::IndexArray indices = d->geometry.indices(); + for (int i = d->start; i < (d->start + d->count); ++i) + { + int ix = indices[i]; + QVector3D a = d->geometry.vertexAt(ix); + QVector3D b = a + d->geometry.normalAt(ix); + verts.append(a, b); + } + painter->setVertexAttribute(QGL::Position, QGLAttributeValue(verts)); + glLineWidth(2.0f); + painter->draw(QGL::Lines, verts.size()); +} + +const QGLMaterial *QGLSceneNode::setPainterMaterial(int material, QGLPainter *painter, + QGL::Face faces, bool &changedTex) +{ + Q_D(QGLSceneNode); + QGLMaterial *mat = d->palette->material(material); + const QGLMaterial *saveMat = 0; + if (painter->faceMaterial(faces) != mat) + { + saveMat = painter->faceMaterial(faces); + painter->setFaceMaterial(faces, mat); + int texUnit = 0; + for (int i = 0; i < mat->textureLayerCount(); ++i) + { + QGLTexture2D *tex = mat->texture(i); + if (tex) + { + painter->glActiveTexture(GL_TEXTURE0 + texUnit); + tex->bind(); + changedTex = true; + ++texUnit; + } + } + } + return saveMat; +} + +/*! + Draws the geometry of the node onto the \a painter. + + This is the function which performs the actual drawing step in the + draw function below. + + \list + \o calls draw(start, count) on this nodes geometry object (if any) + \endlist + + Override this function to perform special processing on this node, + after transformation & culling are applied and before sequencing of + materials & effects are done; but just before (or after) the + actual draw step. + + This default implementation simply draws the nodes geometry onto + the painter. + + Example: + \code + void MySpecialNode::geometryDraw(QGLPainter *painter) + { + // at this point the node has survived culling, the model-view + // matrix is transformed into this nodes frame of reference, + // materials & effects have been applied as this node appears + // in its place in the render sequence + + doMySpecialProcessing(); + + // call parent implementation to do actual draw + QGLSceneNode::geometryDraw(painter); + } + \endcode +*/ +void QGLSceneNode::drawGeometry(QGLPainter *painter) +{ + Q_D(QGLSceneNode); + if (d->count && d->geometry.count() > 0) + d->geometry.draw(painter, d->start, d->count, d->drawingMode, d->drawingWidth); +} + +/*! + Draws this scene node on the \a painter. + + In detail this function: + \list + \o ensures the effect specified by effect() is current on the painter + \o sets the nodes materials onto the painter, if valid materials are present + \o moves the model-view to the x, y, z position + \o applies any local transforms() that may be set for this node + \o calls draw() for all the child nodes + \o calls draw(start, count) on this nodes geometry object (if any) + \o restores the geometry's original materials if they were changed + \o restores the model-view matrix if any local transform was applied + \endlist + + Note that the draw() method does \bold not restore the effect. If the first + step above results in a change to the current QGL::Standard effect then it + will remain set to that effect. In general any painting method should + ensure the effect it requires is current. + + The way draw is implemented ensures that this nodes effects, materials and + transformations will apply by default to its child nodes. Transformations + are cumulative, but effects and materials override those of any parent node. +*/ +void QGLSceneNode::draw(QGLPainter *painter) +{ + Q_D(QGLSceneNode); + bool wasTransformed = false; + + QGLRenderSequencer *seq = painter->renderSequencer(); + + if (seq->top() != this) + { + QMatrix4x4 m = transform(); + + if (!m.isIdentity()) + { + painter->modelViewMatrix().push(); + painter->modelViewMatrix() *= m; + wasTransformed = true; + } + + if (d->options & CullBoundingBox) + { + QBox3D bb = boundingBox(); + if (bb.isFinite() && !bb.isNull() && painter->isCullable(bb)) + { + if (wasTransformed) + painter->modelViewMatrix().pop(); + return; + } + } + } + + if (seq->top() == NULL) + { + seq->setTop(this); + while (true) + { + draw(painter); // recursively draw myself for each state + if (!seq->nextInSequence()) + break; + } + seq->reset(); + } + else + { + bool stateEntered = false; + if (d->childNodes.size() > 0) + { + seq->beginState(this); + stateEntered = true; + QList<QGLSceneNode*>::iterator cit = d->childNodes.begin(); + for ( ; cit != d->childNodes.end(); ++cit) + (*cit)->draw(painter); + } + + if (d->count && (d->geometry.count() > 0) && seq->renderInSequence(this)) + { + bool idSaved = false; + int id = -1; + if (d->pickNode && painter->isPicking()) + { + idSaved = true; + id = painter->objectPickId(); + painter->setObjectPickId(d->pickNode->id()); + } + + if (!stateEntered) + { + stateEntered = true; + seq->beginState(this); + } + seq->applyState(); + + drawGeometry(painter); + + if (idSaved) + painter->setObjectPickId(id); + + if (d->options & ViewNormals) + drawNormalIndicators(painter); + } + if (stateEntered) + seq->endState(this); + } + if (wasTransformed) + painter->modelViewMatrix().pop(); +} + +/*! + Returns the pick node for this scene node, if one was set; otherwise + NULL (0) is returned. + + \sa setPickNode() +*/ +QGLPickNode *QGLSceneNode::pickNode() const +{ + Q_D(const QGLSceneNode); + return d->pickNode; +} + +/*! + Sets the pick node for this scene node to \a node. + + \sa pickNode() +*/ +void QGLSceneNode::setPickNode(QGLPickNode *node) +{ + Q_D(QGLSceneNode); + // TODO - resolve recursive picking - not supported by + // color based pick AFAICT + d->pickNode = node; + node->setTarget(this); +} + +/*! + Creates a new QGLSceneNode that is a copy of this scene node, with + \a parent as the parent of the new copy. If parent is NULL then parent + is set to this nodes parent. + + The copy will reference the same underlying geometry, child nodes, and + have all effects, transforms and other properties copied from this node. + The only property that is not copied is pickNode(). + + \sa cloneNoChildren() +*/ +QGLSceneNode *QGLSceneNode::clone(QObject *parent) const +{ + Q_D(const QGLSceneNode); + QGLSceneNode *node = new QGLSceneNode + (new QGLSceneNodePrivate(d), parent ? parent : this->parent()); + for (int index = 0; index < d->transforms.size(); ++index) + node->addTransform(d->transforms.at(index)->clone(node)); + node->addNodes(d->childNodes); + return node; +} + +/*! + Creates a new QGLSceneNode that is a copy of this scene node, with + \a parent as the parent of the new copy. If parent is NULL then parent + is set to this nodes parent. + + The copy will reference the same underlying geometry, and + have all effects, transforms and other properties copied from this node. + The children() and pickNodes() are not cloned. + + \sa clone() +*/ +QGLSceneNode *QGLSceneNode::cloneNoChildren(QObject *parent) const +{ + Q_D(const QGLSceneNode); + QGLSceneNode *node = new QGLSceneNode + (new QGLSceneNodePrivate(d), parent ? parent : this->parent()); + for (int index = 0; index < d->transforms.size(); ++index) + node->addTransform(d->transforms.at(index)->clone(node)); + return node; +} + +/*! + Creates a new QGLSceneNode that is a copy of this scene node, with + \a parent as the parent of the new copy. If parent is NULL then parent + is set to this nodes parent. + + The copy will reference the same underlying geometry and + have all effects, transforms and other properties copied from this node. + + The copy returned will have the same child nodes, except all child nodes + whose objectName() is equal to \a name. + + \sa clone(), only() +*/ +QGLSceneNode *QGLSceneNode::allExcept(const QString &name, QObject *parent) const +{ + Q_D(const QGLSceneNode); + QGLSceneNode *node = cloneNoChildren(parent); + for (int index = 0; index < d->childNodes.count(); ++index) { + QGLSceneNode *child = d->childNodes.at(index); + if (child->objectName() != name) + node->addNode(child); + } + return node; +} + +/*! + Creates a new QGLSceneNode that is a copy of this scene node, with + \a parent as the parent of the new copy. If parent is NULL then parent + is set to this nodes parent. + + The copy will reference the same underlying geometry and + have all effects, transforms and other properties copied from this node. + + The copy returned will have only one child node. This child node will be + the first child node of this one which has its objectName() equal to \a name. + + \sa clone(), allExcept() +*/ +QGLSceneNode *QGLSceneNode::only(const QString &name, QObject *parent) const +{ + Q_D(const QGLSceneNode); + QGLSceneNode *node = cloneNoChildren(parent); + for (int index = 0; index < d->childNodes.count(); ++index) { + QGLSceneNode *child = d->childNodes.at(index); + if (child->objectName() == name) { + node->addNode(child); + break; + } + } + return node; +} + +/*! + Creates a new QGLSceneNode that is a copy of this scene node, with + \a parent as the parent of the new copy. If parent is NULL then parent + is set to this nodes parent. + + The copy will reference the same underlying geometry and + have all effects, transforms and other properties copied from this node. + + The copy returned will have the same child nodes, except all child nodes + whose objectName() is in the list of \a names. + + \sa clone(), only() +*/ +QGLSceneNode *QGLSceneNode::allExcept(const QStringList &names, QObject *parent) const +{ + Q_D(const QGLSceneNode); + QGLSceneNode *node = cloneNoChildren(parent); + QSet<QString> chk = QSet<QString>::fromList(names); + for (int index = 0; index < d->childNodes.count(); ++index) { + QGLSceneNode *child = d->childNodes.at(index); + if (!chk.contains(child->objectName())) + node->addNode(child); + } + return node; +} + +/*! + Creates a new QGLSceneNode that is a copy of this scene node, with + \a parent as the parent of the new copy. If parent is NULL then parent + is set to this nodes parent. + + The copy will reference the same underlying geometry and + have all effects, transforms and other properties copied from this node. + + The copy returned will have only the child nodes from this + whose objectName() is in the list of \a names. + + \sa clone(), allExcept() +*/ +QGLSceneNode *QGLSceneNode::only(const QStringList &names, QObject *parent) const +{ + Q_D(const QGLSceneNode); + QGLSceneNode *node = cloneNoChildren(parent); + QSet<QString> chk = QSet<QString>::fromList(names); + for (int index = 0; index < d->childNodes.count(); ++index) { + QGLSceneNode *child = d->childNodes.at(index); + if (chk.contains(child->objectName())) + node->addNode(child); + } + return node; +} + +/*! + \fn QGLSceneNode::updated() + Signals that some property of this scene node, or one of its children, + has changed in a manner that will require that the node be redrawn. +*/ + +#ifndef QT_NO_DEBUG_STREAM +#include "qglmaterialcollection.h" +#include "qgltexture2d.h" +/*! + \relates QGLSceneNode + Print a description of \a node, and all its descendants, to stderr. Only + available when compiled in debug mode (without QT_NO_DEBUG defined). + The \a indent and \a loop parameters are used internally. +*/ +void qDumpScene(QGLSceneNode *node, int indent, const QSet<QGLSceneNode *> &loop) +{ +#if !defined(QT_NO_THREAD) + QCoreApplication *app = QApplication::instance(); + QThread *appThread = 0; + if (app) + appThread = QApplication::instance()->thread(); +#endif + QSet<QGLSceneNode *> lp = loop; + lp.insert(node); + QString ind; + ind.fill(QLatin1Char(' '), indent * 4); + fprintf(stderr, "\n%s ======== Node: %p - %s =========\n", qPrintable(ind), node, + qPrintable(node->objectName())); +#if !defined(QT_NO_THREAD) + if (appThread && appThread != node->thread()) + fprintf(stderr, "\n%s from thread: %p\n", qPrintable(ind), node->thread()); +#endif + fprintf(stderr, "%s start: %d count: %d children:", qPrintable(ind), node->start(), node->count()); + { + QList<QGLSceneNode*> children = node->children(); + QList<QGLSceneNode*>::const_iterator it = children.constBegin(); + for (int i = 0; it != children.constEnd(); ++it, ++i) + fprintf(stderr, "%d: %p ", i, *it); + } + fprintf(stderr, "\n"); + if (!node->position().isNull()) + { + QVector3D p = node->position(); + fprintf(stderr, "%s position: (%0.4f, %0.4f, %0.4f)\n", qPrintable(ind), + p.x(), p.y(), p.z()); + } + if (node->localTransform().isIdentity()) + { + fprintf(stderr, "%s local transform: identity\n", qPrintable(ind)); + } + else + { + fprintf(stderr, "%s local transform:\n", qPrintable(ind)); + QMatrix4x4 m = node->localTransform(); + for (int i = 0; i < 4; ++i) + fprintf(stderr, "%s %0.4f %0.4f %0.4f %0.4f\n", + qPrintable(ind), m(i, 0), m(i, 1), m(i, 2), m(i, 3)); + } + if (!node->geometry().isEmpty()) + { + fprintf(stderr, "%s geometry: %d indexes, %d vertices\n", + qPrintable(ind), node->geometry().count(), node->geometry().count(QGL::Position)); + } + else + { + fprintf(stderr, "%s geometry: NULL\n", qPrintable(ind)); + } + if (node->materialIndex() != -1) + { + fprintf(stderr, "%s material: %d", qPrintable(ind), node->materialIndex()); + QGLMaterial *mat = node->material(); + QGLMaterialCollection *pal = node->palette(); + if (pal) + fprintf(stderr, "%s palette: %p", qPrintable(ind), pal); + else + fprintf(stderr, "%s no palette", qPrintable(ind)); + if (pal) + { + mat = pal->material(node->materialIndex()); + if (mat) + fprintf(stderr, "%s mat name from pal: %s ", qPrintable(ind), + qPrintable(pal->material(node->materialIndex())->objectName())); + else + fprintf(stderr, "%s indexed material %d does not exist in palette!", + qPrintable(ind), node->materialIndex()); + } + if (mat) + { + if (mat->objectName().isEmpty()) + fprintf(stderr, " -- %p:", mat); + else + fprintf(stderr, " -- \"%s\":", + qPrintable(mat->objectName())); + fprintf(stderr, " Amb: %s - Diff: %s - Spec: %s - Shin: %0.2f\n", + qPrintable(mat->ambientColor().name()), + qPrintable(mat->diffuseColor().name()), + qPrintable(mat->specularColor().name()), + mat->shininess()); + for (int i = 0; i < mat->textureLayerCount(); ++i) + { + if (mat->texture(i) != 0) + { + QGLTexture2D *tex = mat->texture(i); + if (tex->objectName().isEmpty()) + fprintf(stderr, "%s texture %p", qPrintable(ind), tex); + else + fprintf(stderr, "%s texture %s", qPrintable(ind), + qPrintable(tex->objectName())); + QSize sz = tex->size(); + fprintf(stderr, " - size: %d (w) x %d (h)\n", sz.width(), sz.height()); + } + } + } + else + { + fprintf(stderr, "%s - could not find indexed material!!", qPrintable(ind)); + } + } + else + { + fprintf(stderr, "%s material: NONE\n", qPrintable(ind)); + } + + if (node->hasEffect()) + { + if (node->userEffect()) + { + fprintf(stderr, "%s user effect %p\n", qPrintable(ind), + node->userEffect()); + } + else + { + switch (node->effect()) + { + case QGL::FlatColor: + fprintf(stderr, "%s flat color effect\n", qPrintable(ind)); break; + case QGL::FlatPerVertexColor: + fprintf(stderr, "%s flat per vertex color effect\n", qPrintable(ind)); break; + case QGL::FlatReplaceTexture2D: + fprintf(stderr, "%s flat replace texture 2D effect\n", qPrintable(ind)); break; + case QGL::FlatDecalTexture2D: + fprintf(stderr, "%s flat decal texture 2D effect\n", qPrintable(ind)); break; + case QGL::LitMaterial: + fprintf(stderr, "%s lit material effect\n", qPrintable(ind)); break; + case QGL::LitDecalTexture2D: + fprintf(stderr, "%s lit decal texture 2D effect\n", qPrintable(ind)); break; + case QGL::LitModulateTexture2D: + fprintf(stderr, "%s lit modulate texture 2D effect\n", qPrintable(ind)); break; + } + } + } + else + { + fprintf(stderr, "%s no effect set\n", qPrintable(ind)); + } + QList<QGLSceneNode*> children = node->children(); + QList<QGLSceneNode*>::const_iterator it = children.constBegin(); + for ( ; it != children.constEnd(); ++it) + if (!lp.contains(*it)) + qDumpScene(*it, indent + 1); +} + +QDebug operator<<(QDebug dbg, const QGLSceneNode &node) +{ + dbg << &node << "\n start:" << node.start() << " count:" << node.count(); + QList<QGLSceneNode*> children = node.children(); + QList<QGLSceneNode*>::const_iterator it = children.constBegin(); + for ( ; it != children.constEnd(); ++it) + dbg << "\n child:" << *it; + + if (node.localTransform().isIdentity()) + dbg << "\n local transform: identity"; + else + dbg << "\n local transform:\n" << node.localTransform(); + + if (node.geometry().count() > 0) + { + QGLMaterial *mat = node.material(); + QString mdesc; + if (mat) + mdesc = mat->objectName(); + dbg << "\n geometry:" << node.geometry(); + dbg << "\n material" << node.material() << ": " << mat << mdesc; + } + else + { + dbg << "\n geometry: NULL"; + dbg << "\n material" << node.material(); + } + + if (node.hasEffect()) + { + if (node.userEffect()) + { + dbg << "\n user effect"; + } + else + { + switch (node.effect()) + { + case QGL::FlatColor: + dbg << "\n flat color effect"; break; + case QGL::FlatPerVertexColor: + dbg << "\n flat per vertex color effect"; break; + case QGL::FlatReplaceTexture2D: + dbg << "\n flat replace texture 2D effect"; break; + case QGL::FlatDecalTexture2D: + dbg << "\n flat decal texture 2D effect"; break; + case QGL::LitMaterial: + dbg << "\n lit material effect"; break; + case QGL::LitDecalTexture2D: + dbg << "\n lit decal texture 2D effect"; break; + case QGL::LitModulateTexture2D: + dbg << "\n lit modulate texture 2D effect"; break; + } + } + } + else + { + dbg << "\n no effect set"; + } + return dbg; +} + +#endif + +QT_END_NAMESPACE diff --git a/src/threed/scene/qglscenenode.h b/src/threed/scene/qglscenenode.h new file mode 100644 index 000000000..7d172c55a --- /dev/null +++ b/src/threed/scene/qglscenenode.h @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLSCENENODE_H +#define QGLSCENENODE_H + +#include <QtCore/qobject.h> + +#include "qgeometrydata.h" +#include "qglmaterialcollection.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLSceneNodePrivate; +class QGLAbstractEffect; +class QGLPickNode; +class QGraphicsTransform3D; + +class Q_QT3D_EXPORT QGLSceneNode : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGLSceneNode) + Q_PROPERTY(QVector3D position READ position WRITE setPosition NOTIFY updated) + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY updated) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY updated) + Q_PROPERTY(qreal z READ z WRITE setZ NOTIFY updated) + Q_PROPERTY(QGLMaterial *material READ material WRITE setMaterial NOTIFY updated) + Q_PROPERTY(QGLMaterial *backMaterial READ backMaterial WRITE setBackMaterial NOTIFY updated) +public: + explicit QGLSceneNode(QObject *parent = 0); + explicit QGLSceneNode(const QGeometryData &geometry, QObject *parent = 0); + virtual ~QGLSceneNode(); + + enum Option + { + CullBoundingBox = 0x0001, + ViewNormals = 0x0002 + }; + Q_DECLARE_FLAGS(Options, Option) + + QGLSceneNode::Options options() const; + void setOptions(QGLSceneNode::Options options); + void setOption(QGLSceneNode::Option option, bool value); + + QGeometryData geometry() const; + void setGeometry(QGeometryData); + + QBox3D boundingBox() const; + + QMatrix4x4 localTransform() const; + void setLocalTransform(const QMatrix4x4 &); + QVector3D position() const; + void setPosition(const QVector3D &p); + qreal x() const; + void setX(qreal x); + qreal y() const; + void setY(qreal y); + qreal z() const; + void setZ(qreal z); + + QList<QGraphicsTransform3D *> transforms() const; + void setTransforms(const QList<QGraphicsTransform3D *> &transforms); + void addTransform(QGraphicsTransform3D *transform); + + QGL::DrawingMode drawingMode() const; + void setDrawingMode(QGL::DrawingMode mode); + + qreal drawingWidth() const; + void setDrawingWidth(qreal width); + + QGL::StandardEffect effect() const; + void setEffect(QGL::StandardEffect); + + QGLAbstractEffect *userEffect() const; + void setUserEffect(QGLAbstractEffect *effect); + + bool hasEffect() const; + void setEffectEnabled(bool); + + int start() const; + void setStart(int start); + + int count() const; + void setCount(int count); + + int materialIndex() const; + void setMaterialIndex(int material); + int backMaterialIndex() const; + void setBackMaterialIndex(int material); + + QGLMaterial *material() const; + void setMaterial(QGLMaterial *material); + QGLMaterial *backMaterial() const; + void setBackMaterial(QGLMaterial *material); + + QGLMaterialCollection *palette() const; + void setPalette(QGLMaterialCollection *palette); + + QList<QGLSceneNode*> allChildren() const; + QList<QGLSceneNode *> children() const; + + void addNode(QGLSceneNode *node); + void addNodes(const QList<QGLSceneNode *> &nodes); + void removeNode(QGLSceneNode *node); + void removeNodes(const QList<QGLSceneNode *> &nodes); + + virtual void draw(QGLPainter *painter); + + QGLPickNode *pickNode() const; + void setPickNode(QGLPickNode *node); + + Q_INVOKABLE QGLSceneNode *clone(QObject *parent = 0) const; + Q_INVOKABLE QGLSceneNode *cloneNoChildren(QObject *parent = 0) const; + Q_INVOKABLE QGLSceneNode *allExcept(const QString &name, QObject *parent = 0) const; + Q_INVOKABLE QGLSceneNode *only(const QString &name, QObject *parent = 0) const; + Q_INVOKABLE QGLSceneNode *allExcept(const QStringList &names, QObject *parent = 0) const; + Q_INVOKABLE QGLSceneNode *only(const QStringList &names, QObject *parent = 0) const; + +Q_SIGNALS: + void updated(); + +protected: + virtual void drawGeometry(QGLPainter *painter); + +private Q_SLOTS: + void transformChanged(); + +private: + QMatrix4x4 transform() const; + void invalidateBoundingBox() const; + void invalidateTransform() const; + void drawNormalIndicators(QGLPainter *painter); + const QGLMaterial *setPainterMaterial(int material, QGLPainter *painter, + QGL::Face faces, bool &changedTex); + + Q_DISABLE_COPY(QGLSceneNode) + + QScopedPointer<QGLSceneNodePrivate> d_ptr; + + QGLSceneNode(QGLSceneNodePrivate *d, QObject *parent); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGLSceneNode::Options) + +#ifndef QT_NO_DEBUG_STREAM +Q_QT3D_EXPORT void qDumpScene(QGLSceneNode *, int indent = 0, const QSet<QGLSceneNode*> &loop = QSet<QGLSceneNode*>()); + +Q_QT3D_EXPORT QDebug operator<<(QDebug dbg, const QGLSceneNode &node); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/scene/qglscenenode_p.h b/src/threed/scene/qglscenenode_p.h new file mode 100644 index 000000000..37dcb3629 --- /dev/null +++ b/src/threed/scene/qglscenenode_p.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLSCENENODE_P_H +#define QGLSCENENODE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglnamespace.h" +#include "qglscenenode.h" +#include "qgraphicstransform3d.h" + +#include <QtGui/qmatrix4x4.h> +#include <QtCore/qlist.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qset.h> + +class QGLAbstractEffect; +class QGLPickNode; + +class QGLSceneNodePrivate +{ +public: + QGLSceneNodePrivate() + : palette(0) + , localEffect(QGL::FlatColor) // 0 - zero + , customEffect(0) + , hasEffect(false) + , material(-1) + , backMaterial(-1) + , start(0) + , count(0) + , options(QGLSceneNode::CullBoundingBox) + , pickNode(0) + , boxValid(false) + , drawingMode(QGL::Triangles) + { + } + + // This constructor is used by QGLSceneNode::clone(). + QGLSceneNodePrivate(const QGLSceneNodePrivate *other) + : geometry(other->geometry) + , palette(other->palette) + , localTransform(other->localTransform) + , translate(other->translate) + , localEffect(other->localEffect) + , customEffect(other->customEffect) + , hasEffect(other->hasEffect) + , material(other->material) + , backMaterial(other->backMaterial) + , start(other->start) + , count(other->count) + , options(other->options) + , pickNode(0) // Explicitly not cloned. + , bb(other->bb) + , boxValid(other->boxValid) + , drawingMode(other->drawingMode) + , drawingWidth(1.0) + { + } + + inline void invalidateParentBoundingBox() const + { + QList<QGLSceneNode*>::const_iterator it = parentNodes.constBegin(); + for ( ; it != parentNodes.constEnd(); ++it) + (*it)->invalidateBoundingBox(); + } + + QGeometryData geometry; + QGLMaterialCollection *palette; + QMatrix4x4 localTransform; + QVector3D translate; + QList<QGraphicsTransform3D *> transforms; + QGL::StandardEffect localEffect; + QGLAbstractEffect *customEffect; + QList<QGLSceneNode*> childNodes; + QList<QGLSceneNode*> parentNodes; + bool hasEffect; + int material; + int backMaterial; + int start; + int count; + QGLSceneNode::Options options; + QGLPickNode *pickNode; + mutable QBox3D bb; + mutable bool boxValid; + QGL::DrawingMode drawingMode; + qreal drawingWidth; +}; + +#endif // QGLSCENENODE_P_H diff --git a/src/threed/scene/scene.pri b/src/threed/scene/scene.pri new file mode 100644 index 000000000..14d5e989b --- /dev/null +++ b/src/threed/scene/scene.pri @@ -0,0 +1,19 @@ +INCLUDEPATH += $$PWD +VPATH += $$PWD +HEADERS += qglabstractscene.h \ + qglsceneformatplugin.h \ + qglscenenode.h \ + qglpicknode.h \ + qglrendersequencer.h \ + qglrenderorder.h \ + qglrenderordercomparator.h \ + qglrenderstate.h +SOURCES += qglabstractscene.cpp \ + qglsceneformatplugin.cpp \ + qglscenenode.cpp \ + qglpicknode.cpp \ + qglrendersequencer.cpp \ + qglrenderorder.cpp \ + qglrenderordercomparator.cpp \ + qglrenderstate.cpp +PRIVATE_HEADERS += qglscenenode_p.h diff --git a/src/threed/surfaces/qglabstractsurface.cpp b/src/threed/surfaces/qglabstractsurface.cpp new file mode 100644 index 000000000..b840c0bb9 --- /dev/null +++ b/src/threed/surfaces/qglabstractsurface.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglabstractsurface.h" +#include "qglcontextsurface_p.h" +#include "qglframebufferobjectsurface.h" +#include "qglpixelbuffersurface.h" +#include "qglsubsurface.h" +#include "qglwidgetsurface.h" +#include <QtGui/qpaintdevice.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLAbstractSurface + \brief The QGLAbstractSurface class represents an OpenGL drawing surface. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::painting + + OpenGL can be used to draw into a number of different surface types: + windows, pixel buffers (pbuffers), framebuffer objects, and so on. + It is also possible to use only part of a surface by setting + the \c{glViewport()} to restrict drawing to that part. An example + of a subsurface may be the left or right eye image of a stereoscopic + pair that is rendered into the two halves of a window. + + Activating a surface for OpenGL drawing, and deactivating it afterwards + can be quite varied across surface types. Sometimes it is enough + to just make a QGLContext current and set the \c{glViewport()}. + Other times a context must be made current, followed by binding a + framebuffer object, and finally setting the \c{glViewport()}. + + QGLAbstractSurface and its subclasses simplify the activation and + deactivation of surfaces by encapsulating the logic needed to + use a particular kind of surface into activate() and deactivate() + respectively. + + Normally surfaces are activated by calling QGLPainter::pushSurface() + as in the following example of switching drawing to a framebuffer + object: + + \code + QGLPainter painter; + painter.begin(widget); + QGLFramebufferObjectSurface surface(fbo); + painter.pushSurface(&surface); + ... // draw into the fbo + painter.popSurface(); + ... // draw into the widget + \endcode + + QGLPainter maintains a stack of surfaces, starting with the paint + device specified to QGLPainter::begin() (usually a widget). + The QGLPainter::pushSurface() function calls deactivate() on the + current surface, activate() on the new surface, and then adjusts the + \c{glViewport()} to match the value of viewportGL() for the new surface. + When QGLPainter::popSurface() is called, the previous surface + is re-activated and the \c{glViewport()} changed accordingly. + + \sa QGLFramebufferObjectSurface, QGLWidgetSurface, QGLSubsurface + \sa QGLPixelBufferSurface, QGLPainter::pushSurface() +*/ + +/*! + \enum QGLAbstractSurface::SurfaceType + This enum defines the type of a QGLAbstractSurface. + + \value Widget Instance of QGLWidgetSurface. + \value FramebufferObject Instance of QGLFramebufferObjectSurface. + \value PixelBuffer Instance of QGLPixelBufferSurface. + \value Subsurface Instance of QGLSubsurface. + \value User First user-defined surface type for use by applications. +*/ + +/*! + \fn QGLAbstractSurface::QGLAbstractSurface(int surfaceType) + + Constructs an OpenGL drawing surface of the specified \a surfaceType. +*/ + +/*! + Destroys this OpenGL drawing surface. +*/ +QGLAbstractSurface::~QGLAbstractSurface() +{ +} + +/*! + \fn int QGLAbstractSurface::surfaceType() const + + Returns the type of this surface. +*/ + +/*! + \fn QPaintDevice *QGLAbstractSurface::device() const + + Returns the raw device that this surface will draw on. + + If the surface is an instance of QGLSubsurface, then this will + return the device of the surface that underlies the subsurface. + The viewportRect() defines the region to render into. + + \sa viewportRect() +*/ + +/*! + \fn bool QGLAbstractSurface::activate(QGLAbstractSurface *prevSurface) + + Activate this surface by making its context current, and binding + the associated framebuffer object, if any. + + If \a prevSurface is null, then that surface has just been deactivated + in the process of switching to this surface. This may allow activate() + to optimize the transition to avoid unnecessary state changes. + + Returns true if the surface was activated; false otherwise. + + \sa deactivate(), switchTo() +*/ + +/*! + \fn void QGLAbstractSurface::deactivate(QGLAbstractSurface *nextSurface) + + Deactivate this surface from the current context, but leave the + context current. Typically this will release the framebuffer + object associated with the surface. + + If \a nextSurface is null, then that surface will be activated next + in the process of switching away from this surface. This may allow + deactivate() to optimize the transition to avoid unnecessary state + changes. + + \sa activate(), switchTo() +*/ + +/*! + Returns the rectangle of the surface device() that is occupied by + the viewport for this surface. The origin is at the top-left. + + This function calls viewportGL() and then flips the rectangle + upside down using the height of device() so that the origin + is at the top-left instead of the bottom-right. + + \sa viewportGL(), device() +*/ +QRect QGLAbstractSurface::viewportRect() const +{ + QRect view = viewportGL(); + QPaintDevice *dev = device(); + int height; + if (dev->devType() == QInternal::Widget) + height = static_cast<QWidget *>(dev)->height(); + else + height = dev->height(); + return QRect(view.x(), height - (view.y() + view.height()), + view.width(), view.height()); +} + +/*! + \fn QRect QGLAbstractSurface::viewportGL() const + + Returns the rectangle of the surface device() that is occupied by + the viewport for this surface. The origin is at the bottom-left, + which makes the value suitable for passing to \c{glViewport()}: + + \code + QRect viewport = surface->viewportGL(); + glViewport(viewport.x(), viewport.y(), viewport.width(), viewport.height()); + \endcode + + Normally the viewport rectangle is the full extent of the device(), + but it could be smaller if the application only wishes to draw + into a subpart of the device(). An example would be rendering left + and right stereo eye images into the two halves of a QGLWidget. + The eye surfaces would typically be instances of QGLSubsurface. + + \sa viewportRect(), device() +*/ + +/*! + Returns the aspect ratio of viewportGL() after correcting for the + DPI of device(). + + The return value is used to correct perspective and orthographic + projections for the aspect ratio of the drawing surface. Subclasses + may override this function to adjust the return value if the DPI of + device() is not sufficient to determine the aspect ratio. +*/ +qreal QGLAbstractSurface::aspectRatio() const +{ + // Get the size of the current viewport. + QSize size = viewportGL().size(); + if (size.width() == 0 || size.height() == 0 || + size.width() == size.height()) + return 1.0f; + + // Use the device's DPI setting to determine the pixel aspect ratio. + QPaintDevice *device = this->device(); + int dpiX = device->logicalDpiX(); + int dpiY = device->logicalDpiY(); + if (dpiX <= 0 || dpiY <= 0) + dpiX = dpiY = 1; + + // Return the final aspect ratio based on viewport and pixel size. + return ((qreal)(size.width() * dpiY)) / ((qreal)(size.height() * dpiX)); +} + +/*! + Switches from this surface to \a nextSurface by calling deactivate() + on this surface and activate() on \a nextSurface. + + Returns true if \a nextSurface was activated, or false otherwise. + If \a nextSurface could not be activated, then this surface will + remain activated. + + \sa activate(), deactivate() +*/ +bool QGLAbstractSurface::switchTo(QGLAbstractSurface *nextSurface) +{ + if (nextSurface) { + deactivate(nextSurface); + if (nextSurface->activate(this)) + return true; + activate(); + return false; + } else { + deactivate(); + return true; + } +} + +/*! + Creates an OpenGL drawing surface for the specified paint \a device. + Returns null if it is not possible to create a surface for \a device. + + \sa createSurfaceForContext() +*/ +QGLAbstractSurface *QGLAbstractSurface::createSurfaceForDevice + (QPaintDevice *device) +{ + Q_ASSERT(device); + switch (device->devType()) { + case QInternal::Widget: { + QGLWidget *glw = qobject_cast<QGLWidget *> + (static_cast<QWidget *>(device)); + if (glw) + return new QGLWidgetSurface(glw); + else + return 0; + } + case QInternal::Pbuffer: + return new QGLPixelBufferSurface(static_cast<QGLPixelBuffer *>(device)); + case QInternal::FramebufferObject: + return new QGLFramebufferObjectSurface + (static_cast<QGLFramebufferObject *>(device)); + default: + return 0; + } +} + +/*! + Creates an OpenGL drawing surface for the paint device + underlying \a context. If the paint device is not recognized, + then a generic surface will be created that makes \a context + current when the surface is activated. + + \sa createSurfaceForDevice() +*/ +QGLAbstractSurface *QGLAbstractSurface::createSurfaceForContext + (const QGLContext *context) +{ + Q_ASSERT(context); + QGLAbstractSurface *surface = createSurfaceForDevice(context->device()); + if (!surface) + surface = new QGLContextSurface(context); + return surface; +} + +QT_END_NAMESPACE diff --git a/src/threed/surfaces/qglabstractsurface.h b/src/threed/surfaces/qglabstractsurface.h new file mode 100644 index 000000000..aa6aaa5fb --- /dev/null +++ b/src/threed/surfaces/qglabstractsurface.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLABSTRACTSURFACE_H +#define QGLABSTRACTSURFACE_H + +#include "qt3dglobal.h" +#include <QtOpenGL/qgl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QGLAbstractSurface +{ +public: + virtual ~QGLAbstractSurface(); + + enum SurfaceType + { + Widget, + FramebufferObject, + PixelBuffer, + Subsurface, + User = 1000 + }; + + int surfaceType() const { return m_type; } + + virtual QPaintDevice *device() const = 0; + virtual bool activate(QGLAbstractSurface *prevSurface = 0) = 0; + virtual void deactivate(QGLAbstractSurface *nextSurface = 0) = 0; + virtual QRect viewportGL() const = 0; + QRect viewportRect() const; + virtual qreal aspectRatio() const; + + bool switchTo(QGLAbstractSurface *nextSurface); + + static QGLAbstractSurface *createSurfaceForDevice(QPaintDevice *device); + static QGLAbstractSurface *createSurfaceForContext(const QGLContext *context); + +protected: + QGLAbstractSurface(int surfaceType) : m_type(surfaceType) {} + +private: + int m_type; + + Q_DISABLE_COPY(QGLAbstractSurface) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/surfaces/qglcontextsurface.cpp b/src/threed/surfaces/qglcontextsurface.cpp new file mode 100644 index 000000000..6272e712b --- /dev/null +++ b/src/threed/surfaces/qglcontextsurface.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglcontextsurface_p.h" + +QT_BEGIN_NAMESPACE + +QPaintDevice *QGLContextSurface::device() const +{ + return m_context->device(); +} + +bool QGLContextSurface::activate(QGLAbstractSurface *prevSurface) +{ + Q_UNUSED(prevSurface); + if (QGLContext::currentContext() != m_context) + const_cast<QGLContext *>(m_context)->makeCurrent(); + return true; +} + +void QGLContextSurface::deactivate(QGLAbstractSurface *nextSurface) +{ + Q_UNUSED(nextSurface); +} + +QRect QGLContextSurface::viewportGL() const +{ + QPaintDevice *device = m_context->device(); + return QRect(0, 0, device->width(), device->height()); +} + +QT_END_NAMESPACE diff --git a/src/threed/surfaces/qglcontextsurface_p.h b/src/threed/surfaces/qglcontextsurface_p.h new file mode 100644 index 000000000..4cf14ff9c --- /dev/null +++ b/src/threed/surfaces/qglcontextsurface_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLCONTEXTSURFACE_P_H +#define QGLCONTEXTSURFACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglabstractsurface.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGLContextSurface : public QGLAbstractSurface +{ +public: + explicit QGLContextSurface(const QGLContext *context) + : QGLAbstractSurface(502) + , m_context(context) {} + ~QGLContextSurface() {} + + QPaintDevice *device() const; + bool activate(QGLAbstractSurface *prevSurface); + void deactivate(QGLAbstractSurface *nextSurface); + QRect viewportGL() const; + +private: + const QGLContext *m_context; + + Q_DISABLE_COPY(QGLContextSurface) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/surfaces/qgldrawbuffersurface.cpp b/src/threed/surfaces/qgldrawbuffersurface.cpp new file mode 100644 index 000000000..aabbd2c14 --- /dev/null +++ b/src/threed/surfaces/qgldrawbuffersurface.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgldrawbuffersurface_p.h" + +QT_BEGIN_NAMESPACE + +QPaintDevice *QGLDrawBufferSurface::device() const +{ + return m_surface->device(); +} + +bool QGLDrawBufferSurface::activate(QGLAbstractSurface *prevSurface) +{ + if (!m_surface->activate(prevSurface)) + return false; +#if defined(GL_BACK_LEFT) && defined(GL_BACK_RIGHT) + glDrawBuffer(m_buffer); +#endif + return true; +} + +void QGLDrawBufferSurface::deactivate(QGLAbstractSurface *nextSurface) +{ + m_surface->deactivate(nextSurface); +} + +QRect QGLDrawBufferSurface::viewportGL() const +{ + return m_surface->viewportGL(); +} + +QT_END_NAMESPACE diff --git a/src/threed/surfaces/qgldrawbuffersurface_p.h b/src/threed/surfaces/qgldrawbuffersurface_p.h new file mode 100644 index 000000000..5354e2efa --- /dev/null +++ b/src/threed/surfaces/qgldrawbuffersurface_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLDRAWBUFFERSURFACE_P_H +#define QGLDRAWBUFFERSURFACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglabstractsurface.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGLDrawBufferSurface : public QGLAbstractSurface +{ +public: + QGLDrawBufferSurface(QGLAbstractSurface *surface, GLenum buffer) + : QGLAbstractSurface(500) + , m_surface(surface), m_buffer(buffer) {} + ~QGLDrawBufferSurface() {} + + QPaintDevice *device() const; + bool activate(QGLAbstractSurface *prevSurface); + void deactivate(QGLAbstractSurface *nextSurface); + QRect viewportGL() const; + +private: + QGLAbstractSurface *m_surface; + GLenum m_buffer; + + Q_DISABLE_COPY(QGLDrawBufferSurface) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/surfaces/qglframebufferobjectsurface.cpp b/src/threed/surfaces/qglframebufferobjectsurface.cpp new file mode 100644 index 000000000..c4b8539d4 --- /dev/null +++ b/src/threed/surfaces/qglframebufferobjectsurface.cpp @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglframebufferobjectsurface.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLFramebufferObjectSurface + \brief The QGLFramebufferObjectSurface class represents a framebuffer object that is being used as an OpenGL drawing surface. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::painting +*/ + +class QGLFramebufferObjectSurfacePrivate +{ +public: + QGLFramebufferObjectSurfacePrivate + (QGLFramebufferObject *fbo, const QGLContext *ctx) + : framebufferObject(fbo), context(ctx) {} + + QGLFramebufferObject *framebufferObject; + const QGLContext *context; +}; + +/*! + Constructs a default framebuffer object surface. This constructor + should be followed by a call to setFramebufferObject(). +*/ +QGLFramebufferObjectSurface::QGLFramebufferObjectSurface() + : QGLAbstractSurface(QGLAbstractSurface::FramebufferObject) + , d_ptr(new QGLFramebufferObjectSurfacePrivate(0, 0)) +{ +} + +/*! + Constructs a framebuffer object surface for \a fbo and \a context. + If \a context is null, then the framebuffer will be bound to the + current context when activate() is called. +*/ +QGLFramebufferObjectSurface::QGLFramebufferObjectSurface + (QGLFramebufferObject *fbo, const QGLContext *context) + : QGLAbstractSurface(QGLAbstractSurface::FramebufferObject) + , d_ptr(new QGLFramebufferObjectSurfacePrivate(fbo, context)) +{ +} + +/*! + Destroys this framebuffer object surface. +*/ +QGLFramebufferObjectSurface::~QGLFramebufferObjectSurface() +{ +} + +/*! + Returns the context that owns framebufferObject(); or null + if the framebufferObject() should be assumed to be owned by + the current context when activate() is called. + + \sa setContext(), framebufferObject() +*/ +const QGLContext *QGLFramebufferObjectSurface::context() const +{ + Q_D(const QGLFramebufferObjectSurface); + return d->context; +} + +/*! + Sets the \a context that owns framebufferObject(). + + When activate() is called, it checks to see if \a context is sharing + with the current context. If it is, then the framebufferObject() + is directly bound to the current context. Otherwise, \a context + is made current and then framebufferObject() is bound. + + If \a context is null, then framebufferObject() will be bound + to whatever the current context is when activate() is called. + + \sa context() +*/ +void QGLFramebufferObjectSurface::setContext(const QGLContext *context) +{ + Q_D(QGLFramebufferObjectSurface); + d->context = context; +} + +/*! + Returns the framebuffer object for this surface, or null if + it has not been set yet. + + \sa setFramebufferObject(), context() +*/ +QGLFramebufferObject *QGLFramebufferObjectSurface::framebufferObject() const +{ + Q_D(const QGLFramebufferObjectSurface); + return d->framebufferObject; +} + +/*! + Sets the framebuffer object for this surface to \a fbo. + + \sa framebufferObject() +*/ +void QGLFramebufferObjectSurface::setFramebufferObject + (QGLFramebufferObject *fbo) +{ + Q_D(QGLFramebufferObjectSurface); + d->framebufferObject = fbo; +} + +/*! + \reimp +*/ +QPaintDevice *QGLFramebufferObjectSurface::device() const +{ + Q_D(const QGLFramebufferObjectSurface); + return d->framebufferObject; +} + +/*! + \reimp +*/ +bool QGLFramebufferObjectSurface::activate(QGLAbstractSurface *prevSurface) +{ + Q_UNUSED(prevSurface); + Q_D(QGLFramebufferObjectSurface); + if (d->context) { + if (!QGLContext::areSharing(QGLContext::currentContext(), d->context)) + const_cast<QGLContext *>(d->context)->makeCurrent(); + } else { + // If we don't have a current context, then something is wrong. + Q_ASSERT(QGLContext::currentContext()); + } + if (d->framebufferObject) + return d->framebufferObject->bind(); + return false; +} + +/*! + \reimp +*/ +void QGLFramebufferObjectSurface::deactivate(QGLAbstractSurface *nextSurface) +{ + Q_D(QGLFramebufferObjectSurface); + if (d->framebufferObject) { + if (nextSurface && nextSurface->surfaceType() == FramebufferObject) { + // If we are about to switch to another fbo that is on the + // same context, then don't bother doing the release(). + // This saves an unnecessary glBindFramebuffer(0) call. + if (static_cast<QGLFramebufferObjectSurface *>(nextSurface) + ->context() == d->context) + return; + } + d->framebufferObject->release(); + } +} + +/*! + \reimp +*/ +QRect QGLFramebufferObjectSurface::viewportGL() const +{ + Q_D(const QGLFramebufferObjectSurface); + if (d->framebufferObject) + return QRect(QPoint(0, 0), d->framebufferObject->size()); + else + return QRect(); +} + +QT_END_NAMESPACE diff --git a/src/threed/surfaces/qglframebufferobjectsurface.h b/src/threed/surfaces/qglframebufferobjectsurface.h new file mode 100644 index 000000000..df38c3156 --- /dev/null +++ b/src/threed/surfaces/qglframebufferobjectsurface.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLFRAMEBUFFEROBJECTSURFACE_H +#define QGLFRAMEBUFFEROBJECTSURFACE_H + +#include "qglabstractsurface.h" +#include <QtOpenGL/qglframebufferobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLFramebufferObjectSurfacePrivate; + +class Q_QT3D_EXPORT QGLFramebufferObjectSurface : public QGLAbstractSurface +{ +public: + QGLFramebufferObjectSurface(); + explicit QGLFramebufferObjectSurface + (QGLFramebufferObject *fbo, const QGLContext *context = 0); + ~QGLFramebufferObjectSurface(); + + const QGLContext *context() const; + void setContext(const QGLContext *context); + + QGLFramebufferObject *framebufferObject() const; + void setFramebufferObject(QGLFramebufferObject *fbo); + + QPaintDevice *device() const; + bool activate(QGLAbstractSurface *prevSurface = 0); + void deactivate(QGLAbstractSurface *nextSurface = 0); + QRect viewportGL() const; + +private: + QScopedPointer<QGLFramebufferObjectSurfacePrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLFramebufferObjectSurface) + Q_DISABLE_COPY(QGLFramebufferObjectSurface) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/surfaces/qglmaskedsurface.cpp b/src/threed/surfaces/qglmaskedsurface.cpp new file mode 100644 index 000000000..b953c85ab --- /dev/null +++ b/src/threed/surfaces/qglmaskedsurface.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglmaskedsurface_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLMaskedSurface + \brief The QGLMaskedSurface class represents a masked copy of another OpenGL drawing surface. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::painting + \internal + + Masked surfaces are typically used to render red-cyan anaglyph images + into an underlying surface(). For the left eye image, the mask() + is set to RedMask | AlphaMask. Then for the right eye image, the mask() + is set to GreenMask | BlueMask. +*/ + +/*! + \enum QGLMaskedSurface::BufferMaskBit + This enum defines the channels to mask with QGLMaskedSurface. + + \value RedMask Allow the red channel to be written to the color buffer. + \value GreenMask Allow the green channel to be written to the color buffer. + \value BlueMask Allow the blue channel to be written to the color buffer. + \value AlphaMask Allow the alpha channel to be written to the color buffer. +*/ + +class QGLMaskedSurfacePrivate +{ +public: + QGLMaskedSurfacePrivate + (QGLAbstractSurface *surf = 0, + QGLMaskedSurface::BufferMask msk = QGLMaskedSurface::RedMask | + QGLMaskedSurface::GreenMask | + QGLMaskedSurface::BlueMask | + QGLMaskedSurface::AlphaMask) + : surface(surf), mask(msk) {} + + QGLAbstractSurface *surface; + QGLMaskedSurface::BufferMask mask; +}; + +#define MaskedSurfaceType 501 + +/*! + Constructs a masked OpenGL drawing surface with surface() initially + set to null and mask() initially set to allow all channels to be + written to the color buffer. +*/ +QGLMaskedSurface::QGLMaskedSurface() + : QGLAbstractSurface(MaskedSurfaceType) + , d_ptr(new QGLMaskedSurfacePrivate) +{ +} + +/*! + Constructs a masked OpenGL drawing surface that applies \a mask + to \a surface when activate() is called. +*/ +QGLMaskedSurface::QGLMaskedSurface + (QGLAbstractSurface *surface, QGLMaskedSurface::BufferMask mask) + : QGLAbstractSurface(MaskedSurfaceType) + , d_ptr(new QGLMaskedSurfacePrivate(surface, mask)) +{ +} + +/*! + Destroys this masked OpenGL drawing surface. +*/ +QGLMaskedSurface::~QGLMaskedSurface() +{ +} + +/*! + Returns the underlying surface that mask() will be applied to + when activate() is called. + + \sa setSurface(), mask() +*/ +QGLAbstractSurface *QGLMaskedSurface::surface() const +{ + Q_D(const QGLMaskedSurface); + return d->surface; +} + +/*! + Sets the underlying \a surface that mask() will be applied to + when activate() is called. + + \sa surface(), setMask() +*/ +void QGLMaskedSurface::setSurface(QGLAbstractSurface *surface) +{ + Q_D(QGLMaskedSurface); + d->surface = surface; +} + +/*! + Returns the color mask to apply to surface() when activate() + is called. + + \sa setMask(), surface() +*/ +QGLMaskedSurface::BufferMask QGLMaskedSurface::mask() const +{ + Q_D(const QGLMaskedSurface); + return d->mask; +} + +/*! + Sets the color \a mask to apply to surface() when activate() + is called. + + \sa mask(), setSurface() +*/ +void QGLMaskedSurface::setMask(QGLMaskedSurface::BufferMask mask) +{ + Q_D(QGLMaskedSurface); + d->mask = mask; +} + +/*! + \reimp +*/ +QPaintDevice *QGLMaskedSurface::device() const +{ + Q_D(const QGLMaskedSurface); + return d->surface ? d->surface->device() : 0; +} + +/*! + \reimp +*/ +bool QGLMaskedSurface::activate(QGLAbstractSurface *prevSurface) +{ + Q_D(const QGLMaskedSurface); + if (!d->surface || !d->surface->activate(prevSurface)) + return false; + glColorMask((d->mask & RedMask) != 0, (d->mask & GreenMask) != 0, + (d->mask & BlueMask) != 0, (d->mask & AlphaMask) != 0); + return true; +} + +/*! + \reimp +*/ +void QGLMaskedSurface::deactivate(QGLAbstractSurface *nextSurface) +{ + Q_D(QGLMaskedSurface); + if (d->surface) + d->surface->deactivate(nextSurface); + if (nextSurface && nextSurface->surfaceType() == MaskedSurfaceType) { + // If we are about to switch to another masked surface for + // the same underlying surface, then don't bother calling + // glColorMask() for this one. + QGLMaskedSurface *next = static_cast<QGLMaskedSurface *>(nextSurface); + if (d->surface == next->surface()) + return; + } + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); +} + +/*! + \reimp +*/ +QRect QGLMaskedSurface::viewportGL() const +{ + Q_D(const QGLMaskedSurface); + return d->surface ? d->surface->viewportGL() : QRect(); +} + +QT_END_NAMESPACE diff --git a/src/threed/surfaces/qglmaskedsurface_p.h b/src/threed/surfaces/qglmaskedsurface_p.h new file mode 100644 index 000000000..4a04ca32e --- /dev/null +++ b/src/threed/surfaces/qglmaskedsurface_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLMASKEDSURFACE_H +#define QGLMASKEDSURFACE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglabstractsurface.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGLMaskedSurfacePrivate; + +class QGLMaskedSurface : public QGLAbstractSurface +{ +public: + enum BufferMaskBit + { + RedMask = 0x0001, + GreenMask = 0x0002, + BlueMask = 0x0004, + AlphaMask = 0x0008 + }; + Q_DECLARE_FLAGS(BufferMask, BufferMaskBit) + + QGLMaskedSurface(); + QGLMaskedSurface + (QGLAbstractSurface *surface, QGLMaskedSurface::BufferMask mask); + ~QGLMaskedSurface(); + + QGLAbstractSurface *surface() const; + void setSurface(QGLAbstractSurface *surface); + + QGLMaskedSurface::BufferMask mask() const; + void setMask(QGLMaskedSurface::BufferMask mask); + + QPaintDevice *device() const; + bool activate(QGLAbstractSurface *prevSurface = 0); + void deactivate(QGLAbstractSurface *nextSurface = 0); + QRect viewportGL() const; + +private: + QScopedPointer<QGLMaskedSurfacePrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLMaskedSurface) + Q_DISABLE_COPY(QGLMaskedSurface) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGLMaskedSurface::BufferMask) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/surfaces/qglpaintersurface.cpp b/src/threed/surfaces/qglpaintersurface.cpp new file mode 100644 index 000000000..b2bb379fc --- /dev/null +++ b/src/threed/surfaces/qglpaintersurface.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglpaintersurface_p.h" + +QT_BEGIN_NAMESPACE + +QPaintDevice *QGLPainterSurface::device() const +{ + return m_painter->device(); +} + +bool QGLPainterSurface::activate(QGLAbstractSurface *prevSurface) +{ + if (m_painterContext != QGLContext::currentContext()) + const_cast<QGLContext *>(m_painterContext)->makeCurrent(); + if (!prevSurface) + m_painter->beginNativePainting(); + return true; +} + +void QGLPainterSurface::deactivate(QGLAbstractSurface *nextSurface) +{ + if (!nextSurface) + m_painter->endNativePainting(); +} + +QRect QGLPainterSurface::viewportGL() const +{ + QPaintDevice *device = m_painter->device(); + return QRect(0, 0, device->width(), device->height()); +} + +QT_END_NAMESPACE diff --git a/src/threed/surfaces/qglpaintersurface_p.h b/src/threed/surfaces/qglpaintersurface_p.h new file mode 100644 index 000000000..3a6414e27 --- /dev/null +++ b/src/threed/surfaces/qglpaintersurface_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLPAINTERSURFACE_P_H +#define QGLPAINTERSURFACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qglabstractsurface.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGLPainterSurface : public QGLAbstractSurface +{ +public: + explicit QGLPainterSurface(QPainter *painter) + : QGLAbstractSurface(503) + , m_painter(painter), m_painterContext(QGLContext::currentContext()) {} + ~QGLPainterSurface() {} + + QPaintDevice *device() const; + bool activate(QGLAbstractSurface *prevSurface); + void deactivate(QGLAbstractSurface *nextSurface); + QRect viewportGL() const; + +private: + QPainter *m_painter; + const QGLContext *m_painterContext; + + Q_DISABLE_COPY(QGLPainterSurface) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/surfaces/qglpixelbuffersurface.cpp b/src/threed/surfaces/qglpixelbuffersurface.cpp new file mode 100644 index 000000000..33e7a9eff --- /dev/null +++ b/src/threed/surfaces/qglpixelbuffersurface.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglpixelbuffersurface.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLPixelBufferSurface + \brief The QGLPixelBufferSurface class represents a pixel buffer that is being used as an OpenGL drawing surface. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::painting +*/ + +/*! + Constructs a default pixel buffer surface. This constructor + should be followed by a call to setPixelBuffer(). +*/ +QGLPixelBufferSurface::QGLPixelBufferSurface() + : QGLAbstractSurface(QGLAbstractSurface::PixelBuffer) + , m_pbuffer(0) +{ +} + +/*! + Constructs a pixel buffer surface for \a pbuffer. +*/ +QGLPixelBufferSurface::QGLPixelBufferSurface(QGLPixelBuffer *pbuffer) + : QGLAbstractSurface(QGLAbstractSurface::PixelBuffer) + , m_pbuffer(pbuffer) +{ +} + +/*! + Destroys this pixel buffer surface. +*/ +QGLPixelBufferSurface::~QGLPixelBufferSurface() +{ +} + +/*! + Returns the pixel buffer for this surface, or null if + it has not been set yet. + + \sa setPixelBuffer() +*/ +QGLPixelBuffer *QGLPixelBufferSurface::pixelBuffer() const +{ + return m_pbuffer; +} + +/*! + Sets the framebuffer object for this surface to \a pbuffer. + + \sa pixelBuffer() +*/ +void QGLPixelBufferSurface::setPixelBuffer + (QGLPixelBuffer *pbuffer) +{ + m_pbuffer = pbuffer; +} + +/*! + \reimp +*/ +QPaintDevice *QGLPixelBufferSurface::device() const +{ + return m_pbuffer; +} + +/*! + \reimp +*/ +bool QGLPixelBufferSurface::activate(QGLAbstractSurface *prevSurface) +{ + Q_UNUSED(prevSurface); + if (m_pbuffer) + return m_pbuffer->makeCurrent(); + else + return false; +} + +/*! + \reimp +*/ +void QGLPixelBufferSurface::deactivate(QGLAbstractSurface *nextSurface) +{ + // Nothing to do here - leave the context current. + Q_UNUSED(nextSurface); +} + +/*! + \reimp +*/ +QRect QGLPixelBufferSurface::viewportGL() const +{ + if (m_pbuffer) + return QRect(0, 0, m_pbuffer->width(), m_pbuffer->height()); + else + return QRect(); +} + +QT_END_NAMESPACE diff --git a/src/threed/surfaces/qglpixelbuffersurface.h b/src/threed/surfaces/qglpixelbuffersurface.h new file mode 100644 index 000000000..e0e977599 --- /dev/null +++ b/src/threed/surfaces/qglpixelbuffersurface.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLPIXELBUFFERSURFACE_H +#define QGLPIXELBUFFERSURFACE_H + +#include "qglabstractsurface.h" +#include <QtOpenGL/qglpixelbuffer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QGLPixelBufferSurface : public QGLAbstractSurface +{ +public: + QGLPixelBufferSurface(); + explicit QGLPixelBufferSurface(QGLPixelBuffer *pbuffer); + ~QGLPixelBufferSurface(); + + QGLPixelBuffer *pixelBuffer() const; + void setPixelBuffer(QGLPixelBuffer *pbuffer); + + QPaintDevice *device() const; + bool activate(QGLAbstractSurface *prevSurface = 0); + void deactivate(QGLAbstractSurface *nextSurface = 0); + QRect viewportGL() const; + +private: + QGLPixelBuffer *m_pbuffer; + + Q_DISABLE_COPY(QGLPixelBufferSurface) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/surfaces/qglsubsurface.cpp b/src/threed/surfaces/qglsubsurface.cpp new file mode 100644 index 000000000..1da4ffd55 --- /dev/null +++ b/src/threed/surfaces/qglsubsurface.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglsubsurface.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLSubsurface + \brief The QGLSubsurface class represents a sub-surface of another OpenGL drawing surface. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::painting +*/ + +class QGLSubsurfacePrivate +{ +public: + QGLSubsurfacePrivate() : surface(0) {} + QGLSubsurfacePrivate(QGLAbstractSurface *surf, const QRect &rgn) + : surface(surf), region(rgn) {} + + QGLAbstractSurface *surface; + QRect region; +}; + +/*! + Constructs a new subsurface. This constructor should be followed + by calls to setSurface() and setRegion(). +*/ +QGLSubsurface::QGLSubsurface() + : QGLAbstractSurface(QGLAbstractSurface::Subsurface) + , d_ptr(new QGLSubsurfacePrivate) +{ +} + +/*! + Constructs a new subsurface that occupies \a region within + \a surface. The \a region has its origin at the top-left + of \a surface. +*/ +QGLSubsurface::QGLSubsurface + (QGLAbstractSurface *surface, const QRect ®ion) + : QGLAbstractSurface(QGLAbstractSurface::Subsurface) + , d_ptr(new QGLSubsurfacePrivate(surface, region)) +{ +} + +/*! + Destroys this subsurface. +*/ +QGLSubsurface::~QGLSubsurface() +{ +} + +/*! + Returns the surface behind this subsurface, or null if the + surface has not been set. + + \sa setSurface(), region() +*/ +QGLAbstractSurface *QGLSubsurface::surface() const +{ + Q_D(const QGLSubsurface); + return d->surface; +} + +/*! + Sets the \a surface behind this subsurface. + + \sa surface(), setRegion() +*/ +void QGLSubsurface::setSurface(QGLAbstractSurface *surface) +{ + Q_D(QGLSubsurface); + d->surface = surface; +} + +/*! + Returns the region within surface() that is occupied by this + subsurface, relative to the viewportRect() of surface(). + The origin is at the top-left of surface(). + + \sa setRegion(), surface() +*/ +QRect QGLSubsurface::region() const +{ + Q_D(const QGLSubsurface); + return d->region; +} + +/*! + Sets the \a region within surface() that is occupied by this + subsurface, relative to the viewportRect() of surface(). + The origin is at the top-left of surface(). + + \sa region(), setSurface() +*/ +void QGLSubsurface::setRegion(const QRect ®ion) +{ + Q_D(QGLSubsurface); + d->region = region; +} + +/*! + \reimp +*/ +QPaintDevice *QGLSubsurface::device() const +{ + Q_D(const QGLSubsurface); + if (d->surface) + return d->surface->device(); + else + return 0; +} + +/*! + \reimp +*/ +bool QGLSubsurface::activate(QGLAbstractSurface *prevSurface) +{ + Q_D(QGLSubsurface); + if (d->surface) + return d->surface->activate(prevSurface); + else + return false; +} + +/*! + \reimp +*/ +void QGLSubsurface::deactivate(QGLAbstractSurface *nextSurface) +{ + Q_D(QGLSubsurface); + if (d->surface) + d->surface->deactivate(nextSurface); +} + +/*! + \reimp +*/ +QRect QGLSubsurface::viewportGL() const +{ + Q_D(const QGLSubsurface); + if (d->surface) { + // The underlying surface's viewportGL() has its origin + // at the bottom-left, whereas d->region has its origin + // at the top-left. Flip the sub-region and adjust. + QRect rect = d->surface->viewportGL(); + return QRect(rect.x() + d->region.x(), + rect.y() + rect.height() - + (d->region.y() + d->region.height()), + d->region.width(), d->region.height()); + } else { + // Don't know the actual height of the surrounding surface, + // so the best we can do is assume the region is bottom-aligned. + return QRect(d->region.x(), 0, d->region.width(), d->region.height()); + } +} + +QT_END_NAMESPACE diff --git a/src/threed/surfaces/qglsubsurface.h b/src/threed/surfaces/qglsubsurface.h new file mode 100644 index 000000000..fb2da3868 --- /dev/null +++ b/src/threed/surfaces/qglsubsurface.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLSUBSURFACE_H +#define QGLSUBSURFACE_H + +#include "qglabstractsurface.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLSubsurfacePrivate; + +class Q_QT3D_EXPORT QGLSubsurface : public QGLAbstractSurface +{ +public: + QGLSubsurface(); + QGLSubsurface(QGLAbstractSurface *surface, const QRect ®ion); + ~QGLSubsurface(); + + QGLAbstractSurface *surface() const; + void setSurface(QGLAbstractSurface *subSurface); + + QRect region() const; + void setRegion(const QRect ®ion); + + QPaintDevice *device() const; + bool activate(QGLAbstractSurface *prevSurface = 0); + void deactivate(QGLAbstractSurface *nextSurface = 0); + QRect viewportGL() const; + +private: + QScopedPointer<QGLSubsurfacePrivate> d_ptr; + + Q_DECLARE_PRIVATE(QGLSubsurface) + Q_DISABLE_COPY(QGLSubsurface) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/surfaces/qglwidgetsurface.cpp b/src/threed/surfaces/qglwidgetsurface.cpp new file mode 100644 index 000000000..4a10a5159 --- /dev/null +++ b/src/threed/surfaces/qglwidgetsurface.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglwidgetsurface.h" +#include <QtGui/qpainter.h> +#include <QtGui/qpaintengine.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLWidgetSurface + \brief The QGLWidgetSurface class represents a QGLWidget that is begin used as an OpenGL drawing surface. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::painting +*/ + +/*! + Constructs a widget surface. This constructor should be followed + by a call to setWidget(). +*/ +QGLWidgetSurface::QGLWidgetSurface() + : QGLAbstractSurface(QGLAbstractSurface::Widget) + , m_widget(0) +{ +} + +/*! + Constructs a widget surface for \a widget. +*/ +QGLWidgetSurface::QGLWidgetSurface(QGLWidget *widget) + : QGLAbstractSurface(QGLAbstractSurface::Widget) + , m_widget(widget) +{ +} + +/*! + Destroys this widget surface. +*/ +QGLWidgetSurface::~QGLWidgetSurface() +{ +} + +/*! + Returns the widget that is underlying this surface. + + \sa setWidget() +*/ +QGLWidget *QGLWidgetSurface::widget() const +{ + return m_widget; +} + +/*! + Sets the \a widget that is underlying this surface. + + \sa widget() +*/ +void QGLWidgetSurface::setWidget(QGLWidget *widget) +{ + m_widget = widget; +} + +/*! + \reimp +*/ +QPaintDevice *QGLWidgetSurface::device() const +{ + return m_widget; +} + +/*! + \reimp +*/ +bool QGLWidgetSurface::activate(QGLAbstractSurface *prevSurface) +{ + Q_UNUSED(prevSurface); + if (m_widget) { + const QGLContext *context = m_widget->context(); + if (QGLContext::currentContext() != context) + const_cast<QGLContext *>(context)->makeCurrent(); + return true; + } else { + return false; + } +} + +/*! + \reimp +*/ +void QGLWidgetSurface::deactivate(QGLAbstractSurface *nextSurface) +{ + // Nothing to do here - leave the context current. + Q_UNUSED(nextSurface); +} + +/*! + \reimp +*/ +QRect QGLWidgetSurface::viewportGL() const +{ + if (m_widget) + return m_widget->rect(); // Origin assumed to be (0, 0). + else + return QRect(); +} + +QT_END_NAMESPACE diff --git a/src/threed/surfaces/qglwidgetsurface.h b/src/threed/surfaces/qglwidgetsurface.h new file mode 100644 index 000000000..22fff4164 --- /dev/null +++ b/src/threed/surfaces/qglwidgetsurface.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLWIDGETSURFACE_H +#define QGLWIDGETSURFACE_H + +#include "qglabstractsurface.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLWidgetSurfacePrivate; + +class Q_QT3D_EXPORT QGLWidgetSurface : public QGLAbstractSurface +{ +public: + QGLWidgetSurface(); + explicit QGLWidgetSurface(QGLWidget *widget); + ~QGLWidgetSurface(); + + QGLWidget *widget() const; + void setWidget(QGLWidget *widget); + + QPaintDevice *device() const; + bool activate(QGLAbstractSurface *prevSurface = 0); + void deactivate(QGLAbstractSurface *nextSurface = 0); + QRect viewportGL() const; + +private: + QGLWidget *m_widget; + + Q_DISABLE_COPY(QGLWidgetSurface) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/surfaces/surfaces.pri b/src/threed/surfaces/surfaces.pri new file mode 100644 index 000000000..964f924a0 --- /dev/null +++ b/src/threed/surfaces/surfaces.pri @@ -0,0 +1,23 @@ +INCLUDEPATH += $$PWD +VPATH += $$PWD +HEADERS += \ + qglabstractsurface.h \ + qglframebufferobjectsurface.h \ + qglpixelbuffersurface.h \ + qglsubsurface.h \ + qglwidgetsurface.h +SOURCES += \ + qglabstractsurface.cpp \ + qglcontextsurface.cpp \ + qgldrawbuffersurface.cpp \ + qglframebufferobjectsurface.cpp \ + qglmaskedsurface.cpp \ + qglpaintersurface.cpp \ + qglpixelbuffersurface.cpp \ + qglsubsurface.cpp \ + qglwidgetsurface.cpp +PRIVATE_HEADERS += \ + qglcontextsurface_p.h \ + qgldrawbuffersurface_p.h \ + qglmaskedsurface_p.h \ + qglpaintersurface_p.h diff --git a/src/threed/textures/qareaallocator.cpp b/src/threed/textures/qareaallocator.cpp new file mode 100644 index 000000000..d6d92de88 --- /dev/null +++ b/src/threed/textures/qareaallocator.cpp @@ -0,0 +1,877 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qareaallocator.h" +#include "qglnamespace.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAreaAllocator + \brief The QAreaAllocator class provides facilities for allocating sub-regions from a rectangular image. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::textures + \internal + + Performance on a system can sometimes be improved by storing + multiple small images in a single large image. This reduces + memory allocation overhead and GPU state switching costs. + + QAreaAllocator and its subclasses implement standard strategies + for sub-region allocation in images without tying those strategies + to specific technologies such as raster, OpenGL, etc. + + Allocations are managed in a virtual two-dimensional space. + The caller performs the actual texture upload based on the sub-region + that allocate() returns. + + The caller can return a sub-region to the allocation pool with + release(). Note that not all strategies support release(). + + \sa QSimpleAreaAllocator, QGeneralAreaAllocator, QUniformAreaAllocator +*/ + +/*! + \class QSimpleAreaAllocator + \brief The QSimpleAreaAllocator class provides a simple allocation policy for simple-sized sub-allocations. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::textures + \internal + + QSimpleAreaAllocator uses a trivial allocation strategy whereby + sub-regions are allocated in rows, with a new row started each + time the previous row fills up. Space is never reclaimed by + release(). + + This allocator is suitable for use when the allocations will all + be of a similar size and all regions will be discarded at + the same time when the allocator is destroyed. An example would + be a font glyph manager. + + \sa QAreaAllocator, QGeneralAreaAllocator +*/ + +/*! + \class QGeneralAreaAllocator + \brief The QGeneralAreaAllocator class provides a general allocation policy for sub-regions in an image. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::textures + \internal + + QGeneralAreaAllocator can handle arbitrary-sized allocations up to + size(), in any order, and can release() previously allocated regions. + It uses a binary subdivision algorithm on the image, which may result + in fragmentation under heavy load. + + While technically any size sub-region up to size() can be allocated, + once subdivision begins, the sizes that can be allocated will reduce + substantially. It is recommended that incoming requests be size() / 4 + or less for best performance. + + If the sub-region sizes to be allocated are very similar, and release() + is not necessary, then QSimpleAreaAllocator may work better than + QGeneralAreaAllocator. If the sizes are very similar, and + release() is necessary, then QUniformAreaAllocator may work better + than QGeneralAreaAllocator. + + \sa QAreaAllocator, QSimpleAreaAllocator, QUniformAreaAllocator +*/ + +/*! + \class QUniformAreaAllocator + \brief The QUniformAreaAllocator class provides an allocation policy for uniform-sized areas. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::textures + \internal + + QUniformAreaAllocator allocates any size up to uniformSize() + by dividing size() up into a grid of uniformSize() areas. + Areas can be deallocated with release() and returned to the pool. + + This allocator is suitable for use when the allocations will all + be of a similar size. Unlike QSimpleAreaAllocator, this class + can release allocations. + + \sa QAreaAllocator, QSimpleAreaAllocator, QGeneralAreaAllocator +*/ + +/*! + \internal + + Constructs a new area allocator that is initially \a size pixels + in size. + + \sa expand() +*/ +QAreaAllocator::QAreaAllocator(const QSize &size) + : m_size(size) + , m_minAlloc(1, 1) + , m_margin(0, 0) +{ +} + +/*! + \internal + + Destroys this area allocator. +*/ +QAreaAllocator::~QAreaAllocator() +{ +} + +/*! + \fn QSize QAreaAllocator::size() const + \internal + + Returns the current size of the area being used by this allocator. +*/ + +/*! + \fn QSize QAreaAllocator::minimumAllocation() const + \internal + + Returns the minimum allocation size in the x and y directions + for areas returned by allocate(). The default is (1, 1). + + \sa setMinimumAllocation() +*/ + +/*! + \fn void QAreaAllocator::setMinimumAllocation(const QSize &size) + \internal + + Sets the minimum allocation \a size in the x and y directions + for areas returned by allocate(). + + For example, setting the minimum allocation to (8, 8) will force + all allocations to be aligned on an 8-pixel boundary. + + \sa minimumAllocation() +*/ + +/*! + \fn QSize QAreaAllocator::margin() const + \internal + + Returns the margin that should be left between allocated items + in the x and y directions. The default is (0, 0). + + This may be needed when using OpenGL textures because of + rounding errors in the floating-point representation of + texture co-ordinates. Leaving a small margin between allocations + can help avoid adjacent images from bleeding into each other. + + \sa setMargin() +*/ + +/*! + \fn void QAreaAllocator::setMargin(const QSize &margin) + \internal + + Sets the \a margin that should be left between allocated items + in the x and y directions. + + \sa margin() +*/ + +/*! + \internal + + Expands this allocator to encompass the width and height of \a size. + If the area is already larger, this function does nothing. + + The rectangles that were returned for previous allocations will + remain valid. + + \sa expandBy() +*/ +void QAreaAllocator::expand(const QSize &size) +{ + int newWidth = qMax(m_size.width(), size.width()); + int newHeight = qMax(m_size.height(), size.height()); + m_size = QSize(newWidth, newHeight); +} + +/*! + \internal + + Expands this allocator by \a size pixels in the x and y directions. + + For example, expanding by (0, 128) will add 128 additional pixels + of height but will leave the width unchanged. + + \sa expand() +*/ +void QAreaAllocator::expandBy(const QSize &size) +{ + expand(m_size + size); +} + +/*! + \fn QRect QAreaAllocator::allocate(const QSize &size) + \internal + + Allocates \a size pixels from this allocator and returns the rectangle + that should be used by the caller. Returns a null rectangle if + this allocator does not have sufficient space to accommodate \a size. + + \sa release() +*/ + +/*! + \internal + + Allocates and returns a list of rectangles corresponding to the + elements of \a sizes. The returned list will have less elements + than \a sizes if there is insufficient space to accommodate + all of the allocation requests. The values that are in the returned + list will be allocated and need to be passed to release() to + deallocate them. + + The default implementation will call the subclass allocate() once + for each size until all \a sizes have been allocated or an + allocation fails. Subclasses may override this method to + reorder the allocations for best-fit. + + \sa release() +*/ +QList<QRect> QAreaAllocator::allocate(const QList<QSize> &sizes) +{ + QList<QRect> rects; + QRect rect; + for (int index = 0; index < sizes.count(); ++index) { + rect = allocate(sizes[index]); + if (rect.isNull()) + break; + rects.append(rect); + } + return rects; +} + +/*! + \internal + + Releases the space occupied by \a rect back to the allocator. + The default implementation does nothing. + + The \a rect must have been returned by a previous call to allocate(). + Otherwise the behaviour is undefined. + + \sa allocate() +*/ +void QAreaAllocator::release(const QRect &rect) +{ + Q_UNUSED(rect); +} + +/*! + \internal + + Releases the space occupied by the members of \a rects back to + the allocator. The default implementation calls release() for + each rectangle in the list. + + The members of \a rects must have been returned by previous calls + to allocate(). Otherwise the behaviour is undefined. + + \sa allocate() +*/ +void QAreaAllocator::release(const QList<QRect> &rects) +{ + for (int index = 0; index < rects.count(); ++index) + release(rects[index]); +} + +/*! + \internal + + Returns a rough estimate of the number of bytes of overhead that + are currently in use to store the house-keeping data structures + for this area allocator. The default implementation returns zero. +*/ +int QAreaAllocator::overhead() const +{ + return 0; +} + +/*! + \internal + + Returns \a size, after rounding it up to account for + minimumAllocation() and margin(). + + This is a convenience function, provided for subclass overrides + of allocate(). + + \sa allocate() +*/ +QSize QAreaAllocator::roundAllocation(const QSize &size) const +{ + int width = size.width() + m_margin.width(); + int height = size.height() + m_margin.height(); + int extra = width % m_minAlloc.width(); + if (extra) + width += m_minAlloc.width() - extra; + extra = height % m_minAlloc.height(); + if (extra) + height += m_minAlloc.height() - extra; + return QSize(width, height); +} + +/*! + \internal + + Constructs a simple area allocator that is initially \a size pixels + in size. +*/ +QSimpleAreaAllocator::QSimpleAreaAllocator(const QSize &size) + : QAreaAllocator(size) + , m_row(0) + , m_column(0) + , m_rowHeight(0) +{ +} + +/*! + \internal + + Destroys this simple area allocator. +*/ +QSimpleAreaAllocator::~QSimpleAreaAllocator() +{ +} + +/*! + \internal +*/ +QRect QSimpleAreaAllocator::allocate(const QSize &size) +{ + // Round up the allocation size to account for the margin and + // minimum allocation parameters. + QSize rounded = roundAllocation(size); + int width = rounded.width(); + int height = rounded.height(); + + // Bail out if the size is obviously too small or too big. + if (width <= 0 || width > m_size.width()) + return QRect(); + if (height <= 0 || height > (m_size.height() - m_row)) + return QRect(); + + // Do we need to place this allocation on a new row? + int row = m_row; + int column = m_column; + int rowHeight = m_rowHeight; + if ((column + width) > m_size.width()) { + row += m_rowHeight; + column = 0; + rowHeight = 0; + if (height > (m_size.height() - row)) + return QRect(); + } + + // Update the current allocation position. + m_row = row; + m_column = column + width; + m_rowHeight = qMax(rowHeight, height); + + // Return the allocation, using the original size without rounding. + return QRect(column, row, size.width(), size.height()); +} + +/*! + \internal + + Constructs a general area allocator that is initially \a size pixels + in size. The \a size will be rounded up to the next power of two, + to simplify the internal allocation policy. + + This constructor sets minimumAllocation() to (8, 8) to reduce the + housekeeping overhead of the internal data structures. +*/ +QGeneralAreaAllocator::QGeneralAreaAllocator(const QSize &size) + : QAreaAllocator(QGL::nextPowerOfTwo(size)) +{ + m_root = new Node(); + m_root->rect = QRect(0, 0, m_size.width(), m_size.height()); + m_root->largestFree = m_size; + m_root->parent = 0; + m_root->left = 0; + m_root->right = 0; + m_nodeCount = 1; + setMinimumAllocation(QSize(8, 8)); +} + +/*! + \internal + + Destroys this general area allocator. +*/ +QGeneralAreaAllocator::~QGeneralAreaAllocator() +{ + freeNode(m_root); +} + +/*! + \internal +*/ +void QGeneralAreaAllocator::freeNode(Node *node) +{ + if (node) { + freeNode(node->left); + freeNode(node->right); + } + delete node; +} + +/*! + \internal + + The \a size will be rounded up to the next power of two. + Use size() to determine the actual size after expansion. +*/ +void QGeneralAreaAllocator::expand(const QSize &size) +{ + QAreaAllocator::expand(QGL::nextPowerOfTwo(size)); + + if (m_root->rect.size() == m_size) + return; // No change. + if (!m_root->left && m_root->largestFree.width() > 0) { + // No allocations have occurred, so just adjust the root size. + m_root->rect = QRect(0, 0, m_size.width(), m_size.height()); + m_root->largestFree = m_size; + return; + } + + // Add extra nodes above the current root to expand the tree. + Node *oldRoot = m_root; + Split split; + if (m_size.width() >= m_size.height()) + split = SplitOnX; + else + split = SplitOnY; + while (m_root->rect.size() != m_size) { + if (m_root->rect.width() == m_size.width()) + split = SplitOnY; + else if (m_root->rect.height() == m_size.height()) + split = SplitOnX; + Node *parent = new Node(); + Node *right = new Node(); + m_nodeCount += 2; + m_root->parent = parent; + parent->parent = 0; + parent->left = m_root; + parent->right = right; + parent->largestFree = m_root->rect.size(); + right->parent = parent; + right->left = 0; + right->right = 0; + right->largestFree = m_root->rect.size(); + if (split == SplitOnX) { + parent->rect = QRect(m_root->rect.x(), m_root->rect.y(), + m_root->rect.width() * 2, + m_root->rect.height()); + right->rect = QRect(m_root->rect.x() + m_root->rect.width(), + m_root->rect.y(), + m_root->rect.width(), m_root->rect.height()); + } else { + parent->rect = QRect(m_root->rect.x(), m_root->rect.y(), + m_root->rect.width(), + m_root->rect.height() * 2); + right->rect = QRect(m_root->rect.x(), + m_root->rect.y() + m_root->rect.width(), + m_root->rect.width(), m_root->rect.height()); + } + split = (split == SplitOnX ? SplitOnY : SplitOnX); + m_root = parent; + } + updateLargestFree(oldRoot); +} + +static inline bool fitsWithin(const QSize &size1, const QSize &size2) +{ + return size1.width() <= size2.width() && size1.height() <= size2.height(); +} + +/*! + \internal +*/ +QRect QGeneralAreaAllocator::allocate(const QSize &size) +{ + QSize rounded = roundAllocation(size); + rounded = QGL::nextPowerOfTwo(rounded); + if (rounded.width() <= 0 || rounded.width() > m_size.width() || + rounded.height() <= 0 || rounded.height() > m_size.height()) + return QRect(); + QPoint point = allocateFromNode(rounded, m_root); + if (point.x() >= 0) + return QRect(point, size); + else + return QRect(); +} + +/*! + \internal +*/ +QPoint QGeneralAreaAllocator::allocateFromNode(const QSize &size, Node *node) +{ + // Find the best node to insert into, which should be + // a node with the least amount of unused space that is + // big enough to contain the requested size. + while (node != 0) { + // Go down a level and determine if the left or right + // sub-tree contains the best chance of allocation. + Node *left = node->left; + Node *right = node->right; + if (left && fitsWithin(size, left->largestFree)) { + if (right && fitsWithin(size, right->largestFree)) { + if (left->largestFree.width() < right->largestFree.width() || + left->largestFree.height() < right->largestFree.height()) { + // The largestFree values may be a little oversized, + // so try the left sub-tree and then the right sub-tree. + QPoint point = allocateFromNode(size, left); + if (point.x() >= 0) + return point; + else + return allocateFromNode(size, right); + } else { + node = right; + } + } else { + node = left; + } + } else if (right && fitsWithin(size, right->largestFree)) { + node = right; + } else if (left || right) { + // Neither sub-node has enough space to allocate from. + return QPoint(-1, -1); + } else if (fitsWithin(size, node->largestFree)) { + // Do we need to split this node into smaller pieces? + Split split; + if (fitsWithin(QSize(size.width() * 2, size.height() * 2), + node->largestFree)) { + // Split in either direction: choose the inverse of + // the parent node's split direction to try to balance + // out the wasted space as further subdivisions happen. + if (node->parent && + node->parent->left->rect.x() == + node->parent->right->rect.x()) + split = SplitOnX; + else if (node->parent) + split = SplitOnY; + else if (node->rect.width() >= node->rect.height()) + split = SplitOnX; + else + split = SplitOnY; + } else if (fitsWithin(QSize(size.width() * 2, size.height()), + node->largestFree)) { + // Split along the X direction. + split = SplitOnX; + } else if (fitsWithin(QSize(size.width(), size.height() * 2), + node->largestFree)) { + // Split along the Y direction. + split = SplitOnY; + } else { + // Cannot split further - allocate this node. + node->largestFree = QSize(0, 0); + updateLargestFree(node); + return node->rect.topLeft(); + } + + // Split the node, then go around again using the left sub-tree. + node = splitNode(node, split); + } else { + // Cannot possibly fit into this node. + break; + } + } + return QPoint(-1, -1); +} + +/*! + \internal +*/ +QGeneralAreaAllocator::Node *QGeneralAreaAllocator::splitNode + (Node *node, Split split) +{ + Node *left = new Node(); + Node *right = new Node(); + m_nodeCount += 2; + left->parent = node; + left->left = 0; + left->right = 0; + right->parent = node; + right->left = 0; + right->right = 0; + node->left = left; + node->right = right; + if (split == SplitOnX) { + left->rect = QRect(node->rect.x(), node->rect.y(), + node->rect.width() / 2, + node->rect.height()); + right->rect = QRect(left->rect.right() + 1, node->rect.y(), + node->rect.width() / 2, + node->rect.height()); + } else { + left->rect = QRect(node->rect.x(), node->rect.y(), + node->rect.width(), + node->rect.height() / 2); + right->rect = QRect(node->rect.x(), left->rect.bottom() + 1, + node->rect.width(), + node->rect.height() / 2); + } + left->largestFree = left->rect.size(); + right->largestFree = right->rect.size(); + node->largestFree = right->largestFree; + return left; +} + +/*! + \internal +*/ +void QGeneralAreaAllocator::updateLargestFree(Node *node) +{ + while ((node = node->parent) != 0) { + node->largestFree = + QSize(qMax(node->left->largestFree.width(), + node->right->largestFree.width()), + qMax(node->left->largestFree.height(), + node->right->largestFree.height())); + } +} + +/*! + \internal +*/ +void QGeneralAreaAllocator::release(const QRect &rect) +{ + // Locate the node that contains the allocated region. + Node *node = m_root; + QPoint point = rect.topLeft(); + while (node != 0) { + if (node->left && node->left->rect.contains(point)) + node = node->left; + else if (node->right && node->right->rect.contains(point)) + node = node->right; + else if (node->rect.contains(point)) + break; + else + return; // Point is completely outside the tree. + } + if (!node) + return; + + // Mark the node as free and then work upwards through the tree + // recombining and deleting nodes until we reach a sibling + // that is still allocated. + node->largestFree = node->rect.size(); + while (node->parent) { + if (node->parent->left == node) { + if (node->parent->right->largestFree != + node->parent->right->rect.size()) + break; + } else { + if (node->parent->left->largestFree != + node->parent->left->rect.size()) + break; + } + node = node->parent; + freeNode(node->left); + freeNode(node->right); + m_nodeCount -= 2; + node->left = 0; + node->right = 0; + node->largestFree = node->rect.size(); + } + + // Make the rest of our ancestors have the correct "largest free size". + updateLargestFree(node); +} + +/*! + \internal +*/ +int QGeneralAreaAllocator::overhead() const +{ + return m_nodeCount * sizeof(Node); +} + +/*! + \internal + + Constructs a uniform area allocator that is initially \a size pixels + in size. The \a uniformSize specifies the single allocation size + that is supported. All allocate() requests must be \a uniformSize + or less. +*/ +QUniformAreaAllocator::QUniformAreaAllocator + (const QSize &size, const QSize &uniformSize) + : QAreaAllocator(size), m_uniformSize(uniformSize), m_firstFree(0) +{ + Q_ASSERT(uniformSize.width() > 0 && uniformSize.height() > 0); + Q_ASSERT(size.width() >= uniformSize.width() && + size.height() >= uniformSize.height()); + m_gridSize = QSize(size.width() / uniformSize.width(), + size.height() / uniformSize.height()); + int count = m_gridSize.width() * m_gridSize.height(); + m_grid = new int [count]; + for (int index = 0; index < (count - 1); ++index) + m_grid[index] = index + 1; + m_grid[count - 1] = -1; +} + +/*! + \internal + + Destroys this uniform area allocator. +*/ +QUniformAreaAllocator::~QUniformAreaAllocator() +{ + delete [] m_grid; +} + +/*! + \fn QSize QUniformAreaAllocator::uniformSize() const + \internal + + Returns the uniform size of all allocations. + + \sa allocate() +*/ + +/*! + \internal +*/ +void QUniformAreaAllocator::expand(const QSize &size) +{ + QAreaAllocator::expand(size); + + QSize newGridSize = QSize(m_size.width() / m_uniformSize.width(), + m_size.height() / m_uniformSize.height()); + if (m_gridSize == newGridSize) + return; + + // Create a new grid. + int newCount = newGridSize.width() * newGridSize.height(); + int *newGrid = new int [newCount]; + + // Copy across the free blocks from the old grid. + int posn = m_firstFree; + int newFirstFree = -1; + while (posn != -1) { + int x = posn % m_gridSize.width(); + int y = posn / m_gridSize.width(); + int newPosn = x + y * newGridSize.width(); + newGrid[newPosn] = newFirstFree; + newFirstFree = newPosn; + posn = m_grid[posn]; + } + + // Add free blocks within the expanded area of the new grid. + for (int y = 0; y < m_gridSize.height(); ++y) { + int newPosn = y * newGridSize.width() + m_gridSize.width(); + for (int x = m_gridSize.width(); x < newGridSize.width(); ++x) { + newGrid[newPosn] = newFirstFree; + newFirstFree = newPosn; + ++newPosn; + } + } + for (int y = m_gridSize.height(); y < newGridSize.height(); ++y) { + int newPosn = y * newGridSize.width(); + for (int x = 0; x < newGridSize.width(); ++x) { + newGrid[newPosn] = newFirstFree; + newFirstFree = newPosn; + ++newPosn; + } + } + + // Replace the old grid. + delete [] m_grid; + m_grid = newGrid; + m_gridSize = newGridSize; + m_firstFree = newFirstFree; +} + +/*! + \internal +*/ +QRect QUniformAreaAllocator::allocate(const QSize &size) +{ + QSize rounded = roundAllocation(size); + if (rounded.width() > m_uniformSize.width() || + rounded.height() > m_uniformSize.height()) + return QRect(); + int posn = m_firstFree; + if (posn == -1) + return QRect(); + m_firstFree = m_grid[posn]; + int x = posn % m_gridSize.width(); + int y = posn / m_gridSize.width(); + return QRect(x * m_uniformSize.width(), y * m_uniformSize.height(), + size.width(), size.height()); +} + +/*! + \internal +*/ +void QUniformAreaAllocator::release(const QRect &rect) +{ + int x = rect.x() / m_uniformSize.width(); + int y = rect.y() / m_uniformSize.height(); + int posn = x + y * m_gridSize.width(); + Q_ASSERT(posn >= 0 && posn < m_gridSize.width() * m_gridSize.height()); + m_grid[posn] = m_firstFree; + m_firstFree = posn; +} + +/*! + \internal +*/ +int QUniformAreaAllocator::overhead() const +{ + return sizeof(int) * m_gridSize.width() * m_gridSize.height(); +} + +QT_END_NAMESPACE diff --git a/src/threed/textures/qareaallocator.h b/src/threed/textures/qareaallocator.h new file mode 100644 index 000000000..a4d4f3dff --- /dev/null +++ b/src/threed/textures/qareaallocator.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAREAALLOCATOR_P_H +#define QAREAALLOCATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qt3dglobal.h" +#include <QtCore/qsize.h> +#include <QtCore/qrect.h> +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class Q_QT3D_EXPORT QAreaAllocator +{ +public: + QAreaAllocator(const QSize &size); + virtual ~QAreaAllocator(); + + QSize size() const { return m_size; } + + QSize minimumAllocation() const { return m_minAlloc; } + void setMinimumAllocation(const QSize &size) { m_minAlloc = size; } + + QSize margin() const { return m_margin; } + void setMargin(const QSize &margin) { m_margin = margin; } + + virtual void expand(const QSize &size); + void expandBy(const QSize &size); + + virtual QRect allocate(const QSize &size) = 0; + virtual QList<QRect> allocate(const QList<QSize> &sizes); + virtual void release(const QRect &rect); + virtual void release(const QList<QRect> &rects); + + virtual int overhead() const; + +protected: + QSize m_size; + QSize m_minAlloc; + QSize m_margin; + + QSize roundAllocation(const QSize &size) const; +}; + +class Q_QT3D_EXPORT QSimpleAreaAllocator : public QAreaAllocator +{ +public: + QSimpleAreaAllocator(const QSize &size); + virtual ~QSimpleAreaAllocator(); + + QRect allocate(const QSize &size); + +private: + int m_row; + int m_column; + int m_rowHeight; +}; + +class Q_QT3D_EXPORT QGeneralAreaAllocator : public QAreaAllocator +{ +public: + QGeneralAreaAllocator(const QSize &size); + virtual ~QGeneralAreaAllocator(); + + void expand(const QSize &size); + QRect allocate(const QSize &size); + void release(const QRect &rect); + int overhead() const; + +private: + enum Split { SplitOnX, SplitOnY }; + + struct Node + { + QRect rect; + QSize largestFree; + Node *parent; + Node *left; + Node *right; + }; + + Node *m_root; + int m_nodeCount; + + static void freeNode(Node *node); + QPoint allocateFromNode(const QSize &size, Node *node); + Node *splitNode(Node *node, Split split); + static void updateLargestFree(Node *node); +}; + +class Q_QT3D_EXPORT QUniformAreaAllocator : public QAreaAllocator +{ +public: + QUniformAreaAllocator(const QSize &size, const QSize &uniformSize); + virtual ~QUniformAreaAllocator(); + + QSize uniformSize() const { return m_uniformSize; } + + void expand(const QSize &size); + QRect allocate(const QSize &size); + void release(const QRect &rect); + int overhead() const; + +private: + QSize m_uniformSize; + QSize m_gridSize; + int *m_grid; + int m_firstFree; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/textures/qglsharedresource.cpp b/src/threed/textures/qglsharedresource.cpp new file mode 100644 index 000000000..91bef8972 --- /dev/null +++ b/src/threed/textures/qglsharedresource.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglsharedresource_p.h" +#include <QtCore/qmutex.h> +#include <QtCore/qcoreapplication.h> + +QT_BEGIN_NAMESPACE + +#if !defined(Q_MOC_RUN) + +class Q_OPENGL_EXPORT QGLSignalProxy : public QObject +{ + Q_OBJECT +public: + QGLSignalProxy() : QObject() {} + void emitAboutToDestroyContext(const QGLContext *context) { + emit aboutToDestroyContext(context); + } + static QGLSignalProxy *instance(); +Q_SIGNALS: + void aboutToDestroyContext(const QGLContext *context); +}; + +#endif + +class QGLContextInfo +{ +public: + QGLContextInfo(const QGLContext *ctx) : m_context(ctx), m_resources(0) {} + ~QGLContextInfo(); + + const QGLContext *m_context; + QGLSharedResource *m_resources; +}; + +QGLContextInfo::~QGLContextInfo() +{ + // Detach this information block from all of the shared resources + // that used to be owned by it. + QGLSharedResource *resource = m_resources; + while (resource != 0) { + resource->m_contextInfo = 0; + resource->m_id = 0; + resource = resource->m_next; + } +} + +class QGLContextManager : public QObject +{ + Q_OBJECT +public: + QGLContextManager(QObject *parent = 0); + ~QGLContextManager(); + + QMutex managerLock; + + QGLContextInfo *contextInfo(const QGLContext *ctx); + +private Q_SLOTS: + void aboutToDestroyContext(const QGLContext *ctx); + +private: + QList<QGLContextInfo *> m_contexts; +}; + +Q_GLOBAL_STATIC(QGLContextManager, qt_gl_context_manager) + +QGLContextManager::QGLContextManager(QObject *parent) + : QObject(parent) +{ + QGLSignalProxy *proxy = QGLSignalProxy::instance(); + QThread *mainThread = qApp->thread(); + if (thread() != mainThread) { + // The manager and signal proxy have been created for the first + // time in a background thread. For safety, move both objects + // to the main thread. + moveToThread(mainThread); + proxy->moveToThread(mainThread); + } + connect(proxy, SIGNAL(aboutToDestroyContext(const QGLContext *)), + this, SLOT(aboutToDestroyContext(const QGLContext *))); +} + +QGLContextManager::~QGLContextManager() +{ + QMutexLocker locker(&managerLock); + qDeleteAll(m_contexts); +} + +QGLContextInfo *QGLContextManager::contextInfo(const QGLContext *ctx) +{ + QGLContextInfo *info; + for (int index = 0; index < m_contexts.size(); ++index) { + info = m_contexts[index]; + if (info->m_context == ctx) + return info; + } + info = new QGLContextInfo(ctx); + m_contexts.append(info); + return info; +} + +Q_OPENGL_EXPORT const QGLContext *qt_gl_transfer_context(const QGLContext *); + +void QGLContextManager::aboutToDestroyContext(const QGLContext *ctx) +{ + QMutexLocker locker(&managerLock); + int index = 0; + while (index < m_contexts.size()) { + QGLContextInfo *info = m_contexts[index]; + if (info->m_context == ctx) { + const QGLContext *transfer = qt_gl_transfer_context(ctx); + if (transfer) { + // Transfer ownership to another context in the same sharing + // group. This may result in multiple QGLContextInfo objects + // for the same context, which is ok. + info->m_context = transfer; + } else { + // All contexts in the sharing group have been deleted, + // so detach all of the shared resources. + m_contexts.removeAt(index); + delete info; + continue; + } + } + ++index; + } +} + +const QGLContext *QGLSharedResource::context() const +{ + // Hope that the context will not be destroyed in another thread + // while we are doing this so we don't have to acquire the lock. + return m_contextInfo ? m_contextInfo->m_context : 0; +} + +void QGLSharedResource::attach(const QGLContext *context, GLuint id) +{ + Q_ASSERT(!m_contextInfo); + QGLContextManager *manager = qt_gl_context_manager(); + QMutexLocker locker(&(manager->managerLock)); + m_contextInfo = manager->contextInfo(context); + m_id = id; + m_next = m_contextInfo->m_resources; + m_prev = 0; + if (m_contextInfo->m_resources) + m_contextInfo->m_resources->m_prev = this; + m_contextInfo->m_resources = this; +} + +void QGLSharedResource::destroy() +{ + // Detach this resource from the context information block. + QGLContextManager *manager = qt_gl_context_manager(); + const QGLContext *owner = 0; + GLuint id = 0; + manager->managerLock.lock(); + if (m_contextInfo) { + if (m_next) + m_next->m_prev = m_prev; + if (m_prev) + m_prev->m_next = m_next; + else + m_contextInfo->m_resources = m_next; + owner = m_contextInfo->m_context; + id = m_id; + } + m_contextInfo = 0; + m_id = 0; + m_next = 0; + m_prev = 0; + manager->managerLock.unlock(); + + // Switch back to the owning context temporarily and delete the id. + if (owner && id) { + QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext()); + QGLContext *oldContext; + QGLContext *doneContext; + if (currentContext != owner && !QGLContext::areSharing(owner, currentContext)) { + oldContext = currentContext; + doneContext = const_cast<QGLContext *>(owner); + doneContext->makeCurrent(); + } else { + oldContext = 0; + doneContext = 0; + } + m_destroyFunc(id); + if (oldContext) + oldContext->makeCurrent(); + else if (!currentContext && doneContext) + doneContext->doneCurrent(); + } +} + +QT_END_NAMESPACE + +#include "qglsharedresource.moc" diff --git a/src/threed/textures/qglsharedresource_p.h b/src/threed/textures/qglsharedresource_p.h new file mode 100644 index 000000000..9aafe70a8 --- /dev/null +++ b/src/threed/textures/qglsharedresource_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLSHAREDRESOURCE_P_H +#define QGLSHAREDRESOURCE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtOpenGL/qgl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGLContextManager; +class QGLContextInfo; + +class QGLSharedResource +{ +public: + typedef void (*DestroyResourceFunc)(GLuint id); + QGLSharedResource(DestroyResourceFunc destroyFunc) + : m_destroyFunc(destroyFunc), m_contextInfo(0), m_id(0) + , m_next(0), m_prev(0) {} + ~QGLSharedResource() { destroy(); } + + const QGLContext *context() const; + GLuint id() const { return m_id; } + void clearId() { m_id = 0; } + + void attach(const QGLContext *context, GLuint id); + void destroy(); + +private: + DestroyResourceFunc m_destroyFunc; + QGLContextInfo *m_contextInfo; + GLuint m_id; + QGLSharedResource *m_next; + QGLSharedResource *m_prev; + + friend class QGLContextManager; + friend class QGLContextInfo; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/textures/qgltexture2d.cpp b/src/threed/textures/qgltexture2d.cpp new file mode 100644 index 000000000..d45a1dc04 --- /dev/null +++ b/src/threed/textures/qgltexture2d.cpp @@ -0,0 +1,697 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgltexture2d.h" +#include "qgltexture2d_p.h" +#include "qgltextureutils_p.h" +#include "qglpainter_p.h" +#include "qglext_p.h" + +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLTexture2D + \brief The QGLTexture2D class represents a 2D texture object for GL painting operations. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::textures + + QGLTexture2D contains a QImage and settings for texture filters, + wrap modes, and mipmap generation. When bind() is called, this + information is uploaded to the GL server if it has changed since + the last time bind() was called. + + Once a QGLTexture2D object is created, it can be bound to multiple + GL contexts. Internally, a separate texture identifier is created + for each context. This makes QGLTexture2D easier to use than + raw GL texture identifiers because the application does not need + to be as concerned with whether the texture identifier is valid + in the current context. The application merely calls bind() and + QGLTexture2D will create a new texture identifier for the context + if necessary. + + QGLTexture2D internally points to a reference-counted object that + represents the current texture state. If the QGLTexture2D is copied, + the internal pointer is the same. Modifications to one QGLTexture2D + copy will affect all of the other copies in the system. + + The texture identifiers will be destroyed when the last QGLTexture2D + reference is destroyed, or when a context is destroyed that contained a + texture identifier that was created by QGLTexture2D. + + QGLTexture2D can also be used for uploading 1D textures into the + GL server by specifying an image() with a height of 1. + + \sa QGLTextureCube +*/ + +QGLTexture2DPrivate::QGLTexture2DPrivate() +{ + horizontalWrap = QGL::Repeat; + verticalWrap = QGL::Repeat; + bindOptions = QGLContext::DefaultBindOption; +#if !defined(QT_OPENGL_ES) + mipmapSupported = false; + mipmapSupportedKnown = false; +#endif + imageGeneration = 0; + parameterGeneration = 0; + infos = 0; +} + +QGLTexture2DPrivate::~QGLTexture2DPrivate() +{ + // Destroy the texture id's in the GL server in their original contexts. + QGLTexture2DTextureInfo *current = infos; + QGLTexture2DTextureInfo *next; + const QGLContext *currentContext = + const_cast<QGLContext *>(QGLContext::currentContext()); + const QGLContext *firstContext = currentContext; + while (current != 0) { + next = current->next; + if (current->isLiteral) + current->tex.clearId(); // Don't delete literal id's. + delete current; + current = next; + } + if (firstContext != currentContext) { + if (firstContext) + const_cast<QGLContext *>(firstContext)->makeCurrent(); + else if (currentContext) + const_cast<QGLContext *>(currentContext)->doneCurrent(); + } +} + +/*! + Constructs a null texture object and attaches it to \a parent. + + \sa isNull() +*/ +QGLTexture2D::QGLTexture2D(QObject *parent) + : QObject(parent), d_ptr(new QGLTexture2DPrivate()) +{ +} + +/*! + Destroys this texture object. If this object is the last + reference to the underlying GL texture, then the underlying + GL texture will also be deleted. +*/ +QGLTexture2D::~QGLTexture2D() +{ +} + +/*! + Returns true if this texture object is null; that is, image() + is null and textureId() is zero. +*/ +bool QGLTexture2D::isNull() const +{ + Q_D(const QGLTexture2D); + return d->image.isNull() && !d->infos; +} + +/*! + Returns true if this texture has an alpha channel; false if the + texture is fully opaque. +*/ +bool QGLTexture2D::hasAlphaChannel() const +{ + Q_D(const QGLTexture2D); + if (!d->image.isNull()) + return d->image.hasAlphaChannel(); + QGLTexture2DTextureInfo *info = d->infos; + if (info) + return info->tex.hasAlpha(); + return false; +} + +/*! + Returns the size of this texture. If the underlying OpenGL + implementation requires texture sizes to be a power of two, + then this function will return the next power of two equal + to or greater than requestedSize() + + \sa setSize(), requestedSize() +*/ +QSize QGLTexture2D::size() const +{ + Q_D(const QGLTexture2D); + return d->size; +} + +/*! + Sets the size of this texture to \a value. If the underlying + OpenGL implementation requires texture sizes to be a power of + two, then requestedSize() will be set to \a value, and the + actual size will be set to the next power of two equal + to or greater than \a value. Otherwise both size() and + requestedSize() will be set to \a value. + + \sa size(), requestedSize() +*/ +void QGLTexture2D::setSize(const QSize& value) +{ + Q_D(QGLTexture2D); + if (d->requestedSize == value) + return; + if (!(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) + && !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0)) + d->size = QGL::nextPowerOfTwo(value); + else + d->size = value; + d->requestedSize = value; + ++(d->imageGeneration); +} + +/*! + Returns the size that was previously set with setSize() before + it was rounded to a power of two. + + \sa size(), setSize() +*/ +QSize QGLTexture2D::requestedSize() const +{ + Q_D(const QGLTexture2D); + return d->requestedSize; +} + +/*! + Returns the image that is currently associated with this texture. + The image may not have been uploaded into the GL server yet. + Uploads occur upon the next call to bind(). + + \sa setImage() +*/ +QImage QGLTexture2D::image() const +{ + Q_D(const QGLTexture2D); + return d->image; +} + +/*! + Sets the \a image that is associated with this texture. The image + will be uploaded into the GL server the next time bind() is called. + + If setSize() or setImage() has been called previously, then \a image + will be scaled to size() when it is uploaded. + + If \a image is null, then this function is equivalent to clearImage(). + + \sa image(), setSize(), copyImage(), setPixmap() +*/ +void QGLTexture2D::setImage(const QImage& image) +{ + Q_D(QGLTexture2D); + d->compressedData = QByteArray(); // Clear compressed file data. + if (image.isNull()) { + // Don't change the imageGeneration, because we aren't actually + // changing the image in the GL server, only the client copy. + d->image = image; + } else { + if (!d->size.isValid()) + setSize(image.size()); + d->image = image; + ++(d->imageGeneration); + } +} + +/*! + Sets the image that is associated with this texture to \a pixmap. + + This is a convenience that calls setImage() after converting + \a pixmap into a QImage. It may be more efficient on some + platforms than the application calling QPixmap::toImage(). + + \sa setImage() +*/ +void QGLTexture2D::setPixmap(const QPixmap& pixmap) +{ + QImage image = pixmap.toImage(); + if (pixmap.depth() == 16 && !image.hasAlphaChannel()) { + // If the system depth is 16 and the pixmap doesn't have an alpha channel + // then we convert it to RGB16 in the hope that it gets uploaded as a 16 + // bit texture which is much faster to access than a 32-bit one. + image = image.convertToFormat(QImage::Format_RGB16); + } + setImage(image); +} + +/*! + Clears the image() that is associated with this texture, but the + GL texture will retain its current value. This can be used to + release client-side memory that is no longer required once the + image has been uploaded into the GL server. + + The following code will queue \c image to be uploaded, immediately + force it to be uploaded into the current GL context, and then + clear the client copy: + + \code + texture.setImage(image); + texture.bind(); + texture.clearImage() + \endcode + + \sa image(), setImage() +*/ +void QGLTexture2D::clearImage() +{ + Q_D(QGLTexture2D); + d->image = QImage(); +} + +#ifndef GL_GENERATE_MIPMAP_SGIS +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif + +/*! + Sets this texture to the contents of a compressed image file + at \a path. Returns true if the file exists and has a supported + compressed format; false otherwise. + + The DDS, ETC1, PVRTC2, and PVRTC4 compression formats are + supported, assuming that the GL implementation has the + appropriate extension. + + \sa setImage(), setSize() +*/ +bool QGLTexture2D::setCompressedFile(const QString &path) +{ + Q_D(QGLTexture2D); + d->image = QImage(); + QFile f(path); + if (!f.open(QIODevice::ReadOnly)) + { + qWarning("QGLTexture2D::setCompressedFile(%s): File could not be read", + qPrintable(path)); + return false; + } + QByteArray data = f.readAll(); + f.close(); + + bool hasAlpha, isFlipped; + if (!QGLBoundTexture::canBindCompressedTexture + (data.constData(), data.size(), 0, &hasAlpha, &isFlipped)) { + qWarning("QGLTexture2D::setCompressedFile(%s): Format is not supported", + path.toLocal8Bit().constData()); + return false; + } + + QFileInfo fi(path); + d->url = QUrl::fromLocalFile(fi.absoluteFilePath()); + + // The 3DS loader expects the flip state to be set before bind(). + if (isFlipped) + d->bindOptions &= ~QGLContext::InvertedYBindOption; + else + d->bindOptions |= QGLContext::InvertedYBindOption; + + d->compressedData = data; + ++(d->imageGeneration); + return true; +} + +/*! + Returns the url that was last set with setUrl. +*/ +QUrl QGLTexture2D::url() const +{ + Q_D(const QGLTexture2D); + return d->url; +} + +/*! + Sets this texture to have the contents of the image stored at \a url. +*/ +void QGLTexture2D::setUrl(const QUrl &url) +{ + Q_D(QGLTexture2D); + if (d->url == url) + return; + d->url = url; + + if (url.isEmpty()) + { + d->image = QImage(); + } + else + { + if (url.scheme() == QLatin1String("file")) + { + QString fileName = url.toLocalFile(); + if (fileName.endsWith(QLatin1String(".dds"), Qt::CaseInsensitive)) + { + setCompressedFile(fileName); + } + else + { + QImage im(fileName); + if (im.isNull()) + qWarning("Could not load texture: %s", qPrintable(fileName)); + setImage(im); + } + } + else + { + qWarning("Network URLs not yet supported"); + /* + if (d->textureReply) + d->textureReply->deleteLater(); + QNetworkRequest req(d->textureUrl); + req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + d->textureReply = qmlEngine(this)->networkAccessManager()->get(req); + QObject::connect(d->textureReply, SIGNAL(finished()), + this, SLOT(textureRequestFinished())); + */ + } + } +} + +/*! + Copies the contents of \a image to \a offset in this texture + within the current GL context. + + Unlike setImage(), this function copies the image data to the + GL server immediately using \c{glTexSubImage2D()}. This is typically + used to update the contents of a texture after it has been created. + + It is assumed that the application has already called bind() on + this texture to bind it to the current GL context. + + If the texture has been created in multiple contexts, only the + texture identifier for the current context will be updated. + + \sa setImage(), bind() +*/ +void QGLTexture2D::copyImage(const QImage& image, const QPoint& offset) +{ + QImage img = QGLWidget::convertToGLFormat(image); + glTexSubImage2D(GL_TEXTURE_2D, 0, offset.x(), offset.y(), + img.width(), img.height(), GL_RGBA, + GL_UNSIGNED_BYTE, img.bits()); +#if defined(QT_OPENGL_ES_2) + Q_D(QGLTexture2D); + if (d->bindOptions & QGLContext::MipmapBindOption) + glGenerateMipmap(GL_TEXTURE_2D); +#endif +} + +/*! + Returns the options to use when binding the image() to an OpenGL + context for the first time. The default options are + QGLContext::LinearFilteringBindOption | + QGLContext::InvertedYBindOption | QGLContext::MipmapBindOption. + + \sa setBindOptions() +*/ +QGLContext::BindOptions QGLTexture2D::bindOptions() const +{ + Q_D(const QGLTexture2D); + return d->bindOptions; +} + +/*! + Sets the \a options to use when binding the image() to an + OpenGL context. If the image() has already been bound, + then changing the options will cause it to be recreated + from image() the next time bind() is called. + + \sa bindOptions(), bind() +*/ +void QGLTexture2D::setBindOptions(QGLContext::BindOptions options) +{ + Q_D(QGLTexture2D); + if (d->bindOptions != options) { + d->bindOptions = options; + ++(d->imageGeneration); + } +} + +/*! + Returns the wrapping mode for horizontal texture co-ordinates. + The default value is QGL::Repeat. + + \sa setHorizontalWrap(), verticalWrap() +*/ +QGL::TextureWrap QGLTexture2D::horizontalWrap() const +{ + Q_D(const QGLTexture2D); + return d->horizontalWrap; +} + +/*! + Sets the wrapping mode for horizontal texture co-ordinates to \a value. + + If \a value is not supported by the OpenGL implementation, it will be + replaced with a value that is supported. If the application desires a + very specific \a value, it can call horizontalWrap() to check that + the specific value was actually set. + + The \a value will not be applied to the texture in the GL + server until the next call to bind(). + + \sa horizontalWrap(), setVerticalWrap() +*/ +void QGLTexture2D::setHorizontalWrap(QGL::TextureWrap value) +{ + Q_D(QGLTexture2D); + value = qt_gl_modify_texture_wrap(value); + if (d->horizontalWrap != value) { + d->horizontalWrap = value; + ++(d->parameterGeneration); + } +} + +/*! + Returns the wrapping mode for vertical texture co-ordinates. + The default value is QGL::Repeat. + + \sa setVerticalWrap(), horizontalWrap() +*/ +QGL::TextureWrap QGLTexture2D::verticalWrap() const +{ + Q_D(const QGLTexture2D); + return d->verticalWrap; +} + +/*! + Sets the wrapping mode for vertical texture co-ordinates to \a value. + + If \a value is not supported by the OpenGL implementation, it will be + replaced with a value that is supported. If the application desires a + very specific \a value, it can call verticalWrap() to check that + the specific value was actually set. + + The \a value will not be applied to the texture in the GL + server until the next call to bind(). + + \sa verticalWrap(), setHorizontalWrap() +*/ +void QGLTexture2D::setVerticalWrap(QGL::TextureWrap value) +{ + Q_D(QGLTexture2D); + value = qt_gl_modify_texture_wrap(value); + if (d->verticalWrap != value) { + d->verticalWrap = value; + ++(d->parameterGeneration); + } +} + +/*! + Binds this texture to the 2D texture target. + + If this texture object is not associated with an identifier in + the current context, then a new identifier will be created, + and image() uploaded into the GL server. + + If setImage() or setSize() was called since the last upload, + then image() will be re-uploaded to the GL server. + + Returns false if the texture could not be bound for some reason. + + \sa release(), textureId(), setImage() +*/ +bool QGLTexture2D::bind() const +{ + Q_D(const QGLTexture2D); + return const_cast<QGLTexture2DPrivate *>(d)->bind(GL_TEXTURE_2D); +} + +bool QGLTexture2DPrivate::bind(GLenum target) +{ + // Get the current context. If we don't have one, then we + // cannot bind the texture. + const QGLContext *ctx = QGLContext::currentContext(); + if (!ctx) + return false; + + // Find the information block for the context, or create one. + QGLTexture2DTextureInfo *info = infos; + QGLTexture2DTextureInfo *prev = 0; + while (info != 0 && !QGLContext::areSharing(info->tex.context(), ctx)) { + if (info->isLiteral) + return false; // Cannot create extra texture id's for literals. + prev = info; + info = info->next; + } + if (!info) { + info = new QGLTexture2DTextureInfo + (ctx, 0, imageGeneration - 1, parameterGeneration - 1); + if (prev) + prev->next = info; + else + infos = info; + } + + if (!info->tex.textureId() || imageGeneration != info->imageGeneration) { + // Create the texture contents and upload a new image. + info->tex.setOptions(bindOptions); + if (!compressedData.isEmpty()) { + info->tex.bindCompressedTexture + (compressedData.constData(), compressedData.size()); + } else { + info->tex.startUpload(ctx, target, image.size()); + bindImages(info); + info->tex.finishUpload(target); + } + info->imageGeneration = imageGeneration; + } else { + // Bind the existing texture to the texture target. + glBindTexture(target, info->tex.textureId()); + } + + // If the parameter generation has changed, then alter the parameters. + if (parameterGeneration != info->parameterGeneration) { + info->parameterGeneration = parameterGeneration; + q_glTexParameteri(target, GL_TEXTURE_WRAP_S, horizontalWrap); + q_glTexParameteri(target, GL_TEXTURE_WRAP_T, verticalWrap); + } + + // Texture is ready to be used. + return true; +} + +void QGLTexture2DPrivate::bindImages(QGLTexture2DTextureInfo *info) +{ + QSize scaledSize(size); +#if defined(QT_OPENGL_ES_2) + if ((bindOptions & QGLContext::MipmapBindOption) || + horizontalWrap != QGL::ClampToEdge || + verticalWrap != QGL::ClampToEdge) { + // ES 2.0 does not support NPOT textures when mipmaps are in use, + // or if the wrap mode isn't ClampToEdge. + scaledSize = QGL::nextPowerOfTwo(scaledSize); + } +#endif + if (!image.isNull()) + info->tex.uploadFace(GL_TEXTURE_2D, image, scaledSize); + else if (size.isValid()) + info->tex.createFace(GL_TEXTURE_2D, scaledSize); +} + +/*! + Releases the texture associated with the 2D texture target. + This is equivalent to \c{glBindTexture(GL_TEXTURE_2D, 0)}. + + \sa bind() +*/ +void QGLTexture2D::release() const +{ + glBindTexture(GL_TEXTURE_2D, 0); +} + +/*! + Returns the identifier associated with this texture object in + the current context. + + Returns zero if the texture has not previously been bound to + the 2D texture target in the current context with bind(). + + \sa bind() +*/ +GLuint QGLTexture2D::textureId() const +{ + Q_D(const QGLTexture2D); + const QGLContext *ctx = QGLContext::currentContext(); + if (!ctx) + return 0; + QGLTexture2DTextureInfo *info = d->infos; + while (info != 0 && !QGLContext::areSharing(info->tex.context(), ctx)) + info = info->next; + return info ? info->tex.textureId() : 0; +} + +/*! + Constructs a QGLTexture2D object that wraps the supplied literal + texture identifier \a id, with the dimensions specified by \a size. + + The \a id is assumed to have been created by the application in + the current GL context, and it will be destroyed by the application + after the returned QGLTexture2D object is destroyed. + + This function is intended for interfacing to existing code that + uses raw GL texture identifiers. The returned QGLTexture2D can + only be used with the current GL context. + + \sa textureId() +*/ +QGLTexture2D *QGLTexture2D::fromTextureId(GLuint id, const QSize& size) +{ + const QGLContext *ctx = QGLContext::currentContext(); + if (!id || !ctx) + return 0; + + QGLTexture2D *texture = new QGLTexture2D(); + if (!size.isNull()) + texture->setSize(size); + QGLTexture2DTextureInfo *info = new QGLTexture2DTextureInfo + (ctx, id, texture->d_ptr->imageGeneration, + texture->d_ptr->parameterGeneration, true); + texture->d_ptr->infos = info; + return texture; +} + +QT_END_NAMESPACE diff --git a/src/threed/textures/qgltexture2d.h b/src/threed/textures/qgltexture2d.h new file mode 100644 index 000000000..5379c6dfa --- /dev/null +++ b/src/threed/textures/qgltexture2d.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLTEXTURE2D_H +#define QGLTEXTURE2D_H + +#include "qglnamespace.h" +#include <QtOpenGL/qgl.h> +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLTexture2DPrivate; + +class Q_QT3D_EXPORT QGLTexture2D : public QObject +{ + Q_OBJECT +public: + QGLTexture2D(QObject *parent = 0); + ~QGLTexture2D(); + + bool isNull() const; + bool hasAlphaChannel() const; + + QSize size() const; + void setSize(const QSize& value); + QSize requestedSize() const; + + QImage image() const; + void setImage(const QImage& image); + bool setCompressedFile(const QString &path); + QUrl url() const; + void setUrl(const QUrl &url); + + void setPixmap(const QPixmap& pixmap); + + void clearImage(); + + void copyImage(const QImage& image, const QPoint& offset = QPoint(0, 0)); + + QGLContext::BindOptions bindOptions() const; + void setBindOptions(QGLContext::BindOptions options); + + QGL::TextureWrap horizontalWrap() const; + void setHorizontalWrap(QGL::TextureWrap value); + + QGL::TextureWrap verticalWrap() const; + void setVerticalWrap(QGL::TextureWrap value); + + bool bind() const; + void release() const; + + GLuint textureId() const; + + static QGLTexture2D *fromTextureId(GLuint id, const QSize& size); + +private: + QScopedPointer<QGLTexture2DPrivate> d_ptr; + + Q_DISABLE_COPY(QGLTexture2D) + Q_DECLARE_PRIVATE(QGLTexture2D) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/textures/qgltexture2d_p.h b/src/threed/textures/qgltexture2d_p.h new file mode 100644 index 000000000..d226751bd --- /dev/null +++ b/src/threed/textures/qgltexture2d_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLTEXTURE2D_P_H +#define QGLTEXTURE2D_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgltexture2d.h" +#include "qgltextureutils_p.h" +#include "qurl.h" + +#include <QtCore/qatomic.h> + +QT_BEGIN_NAMESPACE + +class QGLTexture2DTextureInfo +{ +public: + QGLTexture2DTextureInfo + (const QGLContext *context, GLuint textureId, uint imageGeneration, + uint parameterGeneration, bool isLiteral = false) + { + if (textureId) + tex.setTextureId(context, textureId); + this->imageGeneration = imageGeneration; + this->parameterGeneration = parameterGeneration; + this->isLiteral = isLiteral; + this->next = 0; + } + + QGLBoundTexture tex; + uint imageGeneration; + uint parameterGeneration; + bool isLiteral; + QGLTexture2DTextureInfo *next; +}; + +class DDSFormat; + +class QGLTexture2DPrivate +{ +public: + QGLTexture2DPrivate(); + ~QGLTexture2DPrivate(); + + QSize size; + QSize requestedSize; + QImage image; + QUrl url; + QByteArray compressedData; + QGLContext::BindOptions bindOptions; + QGL::TextureWrap horizontalWrap; + QGL::TextureWrap verticalWrap; +#if !defined(QT_OPENGL_ES) + bool mipmapSupported; + bool mipmapSupportedKnown; +#endif + uint imageGeneration; + uint parameterGeneration; + QGLTexture2DTextureInfo *infos; + + bool bind(GLenum target); + virtual void bindImages(QGLTexture2DTextureInfo *info); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/threed/textures/qgltexturecube.cpp b/src/threed/textures/qgltexturecube.cpp new file mode 100644 index 000000000..9947d1b65 --- /dev/null +++ b/src/threed/textures/qgltexturecube.cpp @@ -0,0 +1,549 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgltexturecube.h" +#include "qgltexture2d_p.h" +#include "qgltextureutils_p.h" +#include "qglpainter_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGLTextureCube + \brief The QGLTextureCube class represents a cube map texture object for GL painting operations. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::textures + + QGLTextureCube contains six QImage objects for each of the cube + map faces and settings for texture filters, wrap modes, and mipmap + generation. When bind() is called, this information is uploaded to + the GL server if it has changed since the last time bind() was called. + + Once a QGLTextureCube object is created, it can be bound to multiple + GL contexts. Internally, a separate texture identifier is created + for each context. This makes QGLTextureCube easier to use than + raw GL texture identifiers because the application does not need + to be as concerned with whether the texture identifier is valid + in the current context. The application merely calls bind() and + QGLTextureCube will create a new texture identifier for the context + if necessary. + + QGLTextureCube internally points to a reference-counted object that + represents the current texture state. If the QGLTextureCube is copied, + the internal pointer is the same. Modifications to one QGLTextureCube + copy will affect all of the other copies in the system. + + The texture identifiers will be destroyed when the last QGLTextureCube + reference is destroyed, or when a context is destroyed that contained a + texture identifier that was created by QGLTextureCube. + + \sa QGLTexture2D +*/ + +/*! + \enum QGLTextureCube::Face + This enum defines the face of a cube map texture that is affected + by a texture operation on QGLTextureCube instances. + + \value PositiveX The positive X face of the cube map. + \value NegativeX The negative X face of the cube map. + \value PositiveY The positive Y face of the cube map. + \value NegativeY The negative Y face of the cube map. + \value PositiveZ The positive Z face of the cube map. + \value NegativeZ The negative Z face of the cube map. +*/ + +class QGLTextureCubePrivate : public QGLTexture2DPrivate +{ +public: + QGLTextureCubePrivate(); + ~QGLTextureCubePrivate(); + + void bindImages(QGLTexture2DTextureInfo *info); + + QImage otherImages[5]; + uint changedFaces; +}; + +QGLTextureCubePrivate::QGLTextureCubePrivate() +{ + changedFaces = 0; +} + +QGLTextureCubePrivate::~QGLTextureCubePrivate() +{ +} + +void QGLTextureCubePrivate::bindImages(QGLTexture2DTextureInfo *info) +{ + QSize scaledSize(size); +#if defined(QT_OPENGL_ES_2) + if ((bindOptions & QGLContext::MipmapBindOption) || + horizontalWrap != QGL::ClampToEdge || + verticalWrap != QGL::ClampToEdge) { + // ES 2.0 does not support NPOT textures when mipmaps are in use, + // or if the wrap mode isn't ClampToEdge. + scaledSize = QGL::nextPowerOfTwo(scaledSize); + } +#endif + + // Handle the first face. + if (!image.isNull()) + info->tex.uploadFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X, image, scaledSize); + else if (size.isValid()) + info->tex.createFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X, scaledSize); + + // Handle the other faces. + for (int face = 1; face < 6; ++face) { + if (!otherImages[face - 1].isNull()) { + info->tex.uploadFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, + otherImages[face - 1], scaledSize); + } else { + info->tex.createFace(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, scaledSize); + } + } +} + +/*! + Constructs a null texture object. + + \sa isNull() +*/ +QGLTextureCube::QGLTextureCube() + : d_ptr(new QGLTextureCubePrivate()) +{ +} + +/*! + Destroys this texture object. If this object is the last + reference to the underlying GL texture, then the underlying + GL texture will also be deleted. +*/ +QGLTextureCube::~QGLTextureCube() +{ +} + +/*! + Returns true if this texture object is null; that is, all image() + values are null and textureId() is zero. +*/ +bool QGLTextureCube::isNull() const +{ + // TODO + Q_D(const QGLTextureCube); + return !d->infos; +} + +/*! + Returns true if this texture has an alpha channel; false if the + texture is fully opaque. +*/ +bool QGLTextureCube::hasAlphaChannel() const +{ + Q_D(const QGLTextureCube); + if (!d->image.isNull() && d->image.hasAlphaChannel()) + return true; + for (int face = 0; face < 5; ++face) { + if (!d->otherImages[face].isNull()) { + if (d->otherImages[face].hasAlphaChannel()) + return true; + } + } + QGLTexture2DTextureInfo *info = d->infos; + if (info) + return info->tex.hasAlpha(); + return false; +} + +/*! + Returns the size of this texture. If the underlying OpenGL + implementation requires texture sizes to be a power of two, + then this function will return the next power of two equal + to or greater than requestedSize() + + \sa setSize(), requestedSize() +*/ +QSize QGLTextureCube::size() const +{ + Q_D(const QGLTextureCube); + return d->size; +} + +/*! + Sets the size of this texture to \a value. If the underlying + OpenGL implementation requires texture sizes to be a power of + two, then requestedSize() will be set to \a value, and the + actual size will be set to the next power of two equal + to or greater than \a value. Otherwise both size() and + requestedSize() will be set to \a value. + + \sa size(), requestedSize() +*/ +void QGLTextureCube::setSize(const QSize& value) +{ + Q_D(QGLTextureCube); + if (d->requestedSize == value) + return; + if (!(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) && + !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0)) + d->size = QGL::nextPowerOfTwo(value); + else + d->size = value; + d->requestedSize = value; + ++(d->imageGeneration); +} + +/*! + Returns the size that was previously set with setSize() before + it was rounded to a power of two. + + \sa size(), setSize() +*/ +QSize QGLTextureCube::requestedSize() const +{ + Q_D(const QGLTextureCube); + return d->requestedSize; +} + +/*! + Returns the image that is currently associated with the specified + \a face of this cube map texture. The image may not have been + uploaded into the GL server yet. Uploads occur upon the next + call to bind(). + + \sa setImage() +*/ +QImage QGLTextureCube::image(QGLTextureCube::Face face) const +{ + Q_D(const QGLTextureCube); + if (uint(face) >= 6) + return QImage(); + if (face == 0) + return d->image; + else + return d->otherImages[face - 1]; +} + +/*! + Sets the \a image that is associated with this texture on the + specified \a face of the cube map. The image will be uploaded + into the GL server the next time bind() is called. + + If setSize() or setImage() has been called previously, then \a image + will be scaled to size() when it is uploaded. + + If \a image is null, then this function is equivalent to clearImage(). + + \sa image(), setSize(), copyImage() +*/ +void QGLTextureCube::setImage + (QGLTextureCube::Face face, const QImage& image) +{ + Q_D(QGLTextureCube); + if (uint(face) >= 6) + return; + if (image.isNull()) { + // Don't change the imageGeneration, because we aren't actually + // changing the image in the GL server, only the client copy. + if (face == 0) + d->image = image; + else + d->otherImages[face - 1] = image; + } else { + if (!d->size.isValid()) + setSize(image.size()); + if (face == 0) + d->image = image; + else + d->otherImages[face - 1] = image; + ++(d->imageGeneration); + d->changedFaces |= (1 << face); + } +} + +/*! + Clears the image() that is associated with this texture on the + specified \a face of the cube map. The GL texture will retain + its current value. This can be used to release client-side memory + that is no longer required once the image has been uploaded into + the GL server. + + The following code will queue \c image to be uploaded as the + positive X face of the cube map, immediately force it to + be uploaded into the current GL context, and then clear the + client copy: + + \code + texture.setImage(QGLTextureCube::PositiveX, image); + texture.bind(); + texture.clearImage(QGLTextureCube::PositiveX); + \endcode + + \sa image(), setImage() +*/ +void QGLTextureCube::clearImage(QGLTextureCube::Face face) +{ + Q_D(QGLTextureCube); + if (face == 0) + d->image = QImage(); + else + d->otherImages[face - 1] = QImage(); +} + +/*! + Copies the contents of \a image to \a offset in this texture + within the current GL context. The \a face parameter indicates + which face of the cube map should be altered. + + Unlike setImage(), this function copies the image data to the + GL server immediately using \c{glTexSubImage2D()}. This is typically + used to update the contents of a texture after it has been created. + + It is assumed that the application has already called bind() on + this texture to bind it to the current GL context. + + If the texture has been created in multiple contexts, only the + texture identifier for the current context will be updated. + + \sa setImage(), bind() +*/ +void QGLTextureCube::copyImage + (QGLTextureCube::Face face, const QImage& image, const QPoint& offset) +{ + if (uint(face) >= 6) + return; // Invalid face number. + QImage img = QGLWidget::convertToGLFormat(image); + glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + int(face), + 0, offset.x(), offset.y(), + img.width(), img.height(), GL_RGBA, + GL_UNSIGNED_BYTE, img.bits()); +#if defined(QT_OPENGL_ES_2) + Q_D(QGLTextureCube); + if (d->bindOptions & QGLContext::MipmapBindOption) + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); +#endif +} + +/*! + Returns the options to use when binding the image() to an OpenGL + context for the first time. The default options are + QGLContext::LinearFilteringBindOption | + QGLContext::InvertedYBindOption | QGLContext::MipmapBindOption. + + \sa setBindOptions() +*/ +QGLContext::BindOptions QGLTextureCube::bindOptions() const +{ + Q_D(const QGLTextureCube); + return d->bindOptions; +} + +/*! + Sets the \a options to use when binding the image() to an + OpenGL context. If the image() has already been bound, + then changing the options will cause it to be recreated + from image() the next time bind() is called. + + \sa bindOptions(), bind() +*/ +void QGLTextureCube::setBindOptions(QGLContext::BindOptions options) +{ + Q_D(QGLTextureCube); + if (d->bindOptions != options) { + d->bindOptions = options; + ++(d->imageGeneration); + } +} + +/*! + Returns the wrapping mode for horizontal texture co-ordinates. + The default value is QGL::Repeat. + + \sa setHorizontalWrap(), verticalWrap() +*/ +QGL::TextureWrap QGLTextureCube::horizontalWrap() const +{ + Q_D(const QGLTextureCube); + return d->horizontalWrap; +} + +/*! + Sets the wrapping mode for horizontal texture co-ordinates to \a value. + + If \a value is not supported by the OpenGL implementation, it will be + replaced with a value that is supported. If the application desires a + very specific \a value, it can call horizontalWrap() to check that + the specific value was actually set. + + The \a value will not be applied to the texture in the GL + server until the next call to bind(). + + \sa horizontalWrap(), setVerticalWrap() +*/ +void QGLTextureCube::setHorizontalWrap(QGL::TextureWrap value) +{ + Q_D(QGLTextureCube); + value = qt_gl_modify_texture_wrap(value); + if (d->horizontalWrap != value) { + d->horizontalWrap = value; + ++(d->parameterGeneration); + } +} + +/*! + Returns the wrapping mode for vertical texture co-ordinates. + The default value is QGL::Repeat. + + \sa setVerticalWrap(), horizontalWrap() +*/ +QGL::TextureWrap QGLTextureCube::verticalWrap() const +{ + Q_D(const QGLTextureCube); + return d->verticalWrap; +} + +/*! + Sets the wrapping mode for vertical texture co-ordinates to \a value. + + If \a value is not supported by the OpenGL implementation, it will be + replaced with a value that is supported. If the application desires a + very specific \a value, it can call verticalWrap() to check that + the specific value was actually set. + + The \a value will not be applied to the texture in the GL + server until the next call to bind(). + + \sa verticalWrap(), setHorizontalWrap() +*/ +void QGLTextureCube::setVerticalWrap(QGL::TextureWrap value) +{ + Q_D(QGLTextureCube); + value = qt_gl_modify_texture_wrap(value); + if (d->verticalWrap != value) { + d->verticalWrap = value; + ++(d->parameterGeneration); + } +} + +/*! + Binds this texture to the cube map texture target. + + If this texture object is not associated with an identifier in + the current context, then a new identifier will be created, + and the face images will be uploaded into the GL server. + + If setImage() or setSize() was called since the last upload, + then the face images will be re-uploaded to the GL server. + + Returns false if the texture could not be bound for some reason. + + \sa release(), textureId(), setImage() +*/ +bool QGLTextureCube::bind() const +{ + Q_D(const QGLTextureCube); + return const_cast<QGLTextureCubePrivate *>(d)->bind(GL_TEXTURE_CUBE_MAP); +} + +/*! + Releases the texture associated with the cube map texture target. + This is equivalent to \c{glBindTexture(GL_TEXTURE_CUBE_MAP, 0)}. + + \sa bind() +*/ +void QGLTextureCube::release() +{ + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +} + +/*! + Returns the identifier associated with this texture object in + the current context. + + Returns zero if the texture has not previously been bound to + the 2D texture target in the current context with bind(). + + \sa bind() +*/ +GLuint QGLTextureCube::textureId() const +{ + Q_D(const QGLTextureCube); + const QGLContext *ctx = QGLContext::currentContext(); + if (!ctx) + return 0; + QGLTexture2DTextureInfo *info = d->infos; + while (info != 0 && info->tex.context() != ctx) + info = info->next; + return info ? info->tex.textureId() : 0; +} + +/*! + Constructs a QGLTextureCube object that wraps the supplied literal + texture identifier \a id, with the dimensions specified by \a size. + + The \a id is assumed to have been created by the application in + the current GL context, and it will be destroyed by the application + after the returned QGLTextureCube object is destroyed. + + This function is intended for interfacing to existing code that + uses raw GL texture identifiers. The returned QGLTextureCube can + only be used with the current GL context. + + \sa textureId() +*/ +QGLTextureCube *QGLTextureCube::fromTextureId(GLuint id, const QSize& size) +{ + const QGLContext *ctx = QGLContext::currentContext(); + if (!id || !ctx) + return 0; + + QGLTextureCube *texture = new QGLTextureCube(); + if (!size.isNull()) + texture->setSize(size); + QGLTexture2DTextureInfo *info = new QGLTexture2DTextureInfo + (ctx, id, texture->d_ptr->imageGeneration, + texture->d_ptr->parameterGeneration, true); + texture->d_ptr->infos = info; + return texture; +} + +QT_END_NAMESPACE diff --git a/src/threed/textures/qgltexturecube.h b/src/threed/textures/qgltexturecube.h new file mode 100644 index 000000000..dfa445abc --- /dev/null +++ b/src/threed/textures/qgltexturecube.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLTEXTURECUBEMAP_H +#define QGLTEXTURECUBEMAP_H + +#include "qglnamespace.h" +#include <QtOpenGL/qgl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLTextureCubePrivate; + +class Q_QT3D_EXPORT QGLTextureCube +{ +public: + QGLTextureCube(); + ~QGLTextureCube(); + + enum Face + { + PositiveX, + NegativeX, + PositiveY, + NegativeY, + PositiveZ, + NegativeZ + }; + + bool isNull() const; + bool hasAlphaChannel() const; + + QSize size() const; + void setSize(const QSize& value); + QSize requestedSize() const; + + QImage image(QGLTextureCube::Face face) const; + void setImage(QGLTextureCube::Face face, const QImage& image); + void clearImage(QGLTextureCube::Face face); + + void copyImage(QGLTextureCube::Face face, const QImage& image, const QPoint& offset = QPoint(0, 0)); + + QGLContext::BindOptions bindOptions() const; + void setBindOptions(QGLContext::BindOptions options); + + QGL::TextureWrap horizontalWrap() const; + void setHorizontalWrap(QGL::TextureWrap value); + + QGL::TextureWrap verticalWrap() const; + void setVerticalWrap(QGL::TextureWrap value); + + bool bind() const; + static void release(); + + GLuint textureId() const; + + static QGLTextureCube *fromTextureId(GLuint id, const QSize& size); + +private: + QScopedPointer<QGLTextureCubePrivate> d_ptr; + + Q_DISABLE_COPY(QGLTextureCube) + Q_DECLARE_PRIVATE(QGLTextureCube) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/textures/qgltextureutils.cpp b/src/threed/textures/qgltextureutils.cpp new file mode 100644 index 000000000..47c5de489 --- /dev/null +++ b/src/threed/textures/qgltextureutils.cpp @@ -0,0 +1,785 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgltextureutils_p.h" +#include "qglext_p.h" +#include <QtCore/qfile.h> + +QT_BEGIN_NAMESPACE + +QGL::TextureWrap qt_gl_modify_texture_wrap(QGL::TextureWrap value) +{ + switch (value) { +#if defined(QT_OPENGL_ES) + case QGL::Clamp: + value = QGL::ClampToEdge; + break; +#endif +#if !defined(QT_OPENGL_ES) + case QGL::ClampToBorder: + if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_3) + == 0) + value = QGL::Clamp; + break; +#else + case QGL::ClampToBorder: + value = QGL::ClampToEdge; + break; +#endif +#if !defined(QT_OPENGL_ES) + case QGL::ClampToEdge: + if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) + == 0) + value = QGL::Clamp; + break; +#endif +#if !defined(QT_OPENGL_ES) + case QGL::MirroredRepeat: + if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_4) + == 0) + value = QGL::Repeat; + break; +#elif !defined(QT_OPENGL_ES_2) + case QGL::MirroredRepeat: + value = QGL::Repeat; + break; +#endif + default: break; + } + return value; +} + +QGLTextureExtensions::QGLTextureExtensions(const QGLContext *ctx) + : npotTextures(false) + , generateMipmap(false) + , bgraTextureFormat(false) + , ddsTextureCompression(false) + , etc1TextureCompression(false) + , pvrtcTextureCompression(false) + , compressedTexImage2D(0) +{ + Q_UNUSED(ctx); + QGLExtensionChecker extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS))); + if (extensions.match("GL_ARB_texture_non_power_of_two")) + npotTextures = true; + if (extensions.match("GL_SGIS_generate_mipmap")) + generateMipmap = true; + if (extensions.match("GL_EXT_bgra")) + bgraTextureFormat = true; + if (extensions.match("GL_EXT_texture_compression_s3tc")) + ddsTextureCompression = true; + if (extensions.match("GL_OES_compressed_ETC1_RGB8_texture")) + etc1TextureCompression = true; + if (extensions.match("GL_IMG_texture_compression_pvrtc")) + pvrtcTextureCompression = true; +#if defined(QT_OPENGL_ES_2) + npotTextures = true; + generateMipmap = true; +#endif +#if !defined(QT_OPENGL_ES) + if (extensions.match("GL_ARB_texture_compression")) { + compressedTexImage2D = (q_glCompressedTexImage2DARB) + ctx->getProcAddress(QLatin1String("glCompressedTexImage2DARB")); + } +#else + compressedTexImage2D = glCompressedTexImage2D; +#endif +} + +QGLTextureExtensions::~QGLTextureExtensions() +{ +} + +Q_GLOBAL_STATIC(QGLResource<QGLTextureExtensions>, qt_gl_texture_extensions) + +QGLTextureExtensions *QGLTextureExtensions::extensions() +{ + const QGLContext *ctx = QGLContext::currentContext(); + if (!ctx) + return 0; + return qt_gl_texture_extensions()->value(ctx); +} + +static void qt_gl_destroyTextureId(GLuint id) +{ + glDeleteTextures(1, &id); +} + +QGLBoundTexture::QGLBoundTexture() + : m_resource(qt_gl_destroyTextureId) + , m_options(QGLContext::DefaultBindOption) + , m_hasAlpha(false) +{ +} + +QGLBoundTexture::~QGLBoundTexture() +{ +} + +// #define QGL_BIND_TEXTURE_DEBUG + +void QGLBoundTexture::startUpload(const QGLContext *ctx, GLenum target, const QSize &imageSize) +{ + Q_UNUSED(imageSize); + + QGLTextureExtensions *extensions = QGLTextureExtensions::extensions(); + if (!extensions) + return; + +#ifdef QGL_BIND_TEXTURE_DEBUG + printf("QGLBoundTexture::startUpload(), imageSize=(%d,%d), options=%x\n", + imageSize.width(), imageSize.height(), int(m_options)); + time.start(); +#endif + +#ifndef QT_NO_DEBUG + // Reset the gl error stack... + while (glGetError() != GL_NO_ERROR) ; +#endif + + // Create the texture id for the target, which should be one of + // GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP. + GLuint id = m_resource.id(); + if (id) { + glBindTexture(target, 0); // Just in case texture is bound. + m_resource.destroy(); + } + id = 0; + glGenTextures(1, &id); + glBindTexture(target, id); + m_resource.attach(ctx, id); + + GLuint filtering = m_options & QGLContext::LinearFilteringBindOption ? GL_LINEAR : GL_NEAREST; + +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - setting options (%d ms)\n", time.elapsed()); +#endif + q_glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filtering); + + if (QGLContext::currentContext()->format().directRendering() + && extensions->generateMipmap + && (m_options & QGLContext::MipmapBindOption)) + { +#if !defined(QT_OPENGL_ES_2) + glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST); + q_glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); +#else + glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); +#endif + q_glTexParameteri(target, GL_TEXTURE_MIN_FILTER, + m_options & QGLContext::LinearFilteringBindOption + ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST); + } else { + q_glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filtering); + m_options &= ~QGLContext::MipmapBindOption; + } +} + +// map from Qt's ARGB endianness-dependent format to GL's big-endian RGBA layout +static inline void qt_gl_byteSwapImage(QImage &img, GLenum pixel_type) +{ + const int width = img.width(); + const int height = img.height(); + + if (pixel_type == GL_UNSIGNED_INT_8_8_8_8_REV + || (pixel_type == GL_UNSIGNED_BYTE && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) + { + for (int i = 0; i < height; ++i) { + uint *p = (uint *) img.scanLine(i); + for (int x = 0; x < width; ++x) + p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); + } + } else { + for (int i = 0; i < height; ++i) { + uint *p = (uint *) img.scanLine(i); + for (int x = 0; x < width; ++x) + p[x] = (p[x] << 8) | ((p[x] >> 24) & 0xff); + } + } +} + +// #define QGL_BIND_TEXTURE_DEBUG + +void QGLBoundTexture::uploadFace + (GLenum target, const QImage &image, const QSize &scaleSize, GLenum format) +{ + GLenum internalFormat(format); + + // Resolve the texture-related extensions for the current context. + QGLTextureExtensions *extensions = QGLTextureExtensions::extensions(); + if (!extensions) + return; + + // Adjust the image size for scaling and power of two. + QSize size = (!scaleSize.isEmpty() ? scaleSize : image.size()); + if (!extensions->npotTextures) + size = QGL::nextPowerOfTwo(size); + QImage img(image); + if (size != image.size()) { +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - scaling up to %dx%d (%d ms) \n", size.width(), size.height(), time.elapsed()); +#endif + img = img.scaled(size); + } + m_size = size; + + QImage::Format target_format = img.format(); + bool premul = m_options & QGLContext::PremultipliedAlphaBindOption; + GLenum externalFormat; + GLuint pixel_type; + if (extensions->bgraTextureFormat) { + externalFormat = GL_BGRA; + if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2) + pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV; + else + pixel_type = GL_UNSIGNED_BYTE; + } else { + externalFormat = GL_RGBA; + pixel_type = GL_UNSIGNED_BYTE; + } + + switch (target_format) { + case QImage::Format_ARGB32: + if (premul) { + img = img.convertToFormat(target_format = QImage::Format_ARGB32_Premultiplied); +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - converting ARGB32 -> ARGB32_Premultiplied (%d ms) \n", time.elapsed()); +#endif + } + break; + case QImage::Format_ARGB32_Premultiplied: + if (!premul) { + img = img.convertToFormat(target_format = QImage::Format_ARGB32); +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - converting ARGB32_Premultiplied -> ARGB32 (%d ms)\n", time.elapsed()); +#endif + } + break; + case QImage::Format_RGB16: + pixel_type = GL_UNSIGNED_SHORT_5_6_5; + externalFormat = GL_RGB; + internalFormat = GL_RGB; + break; + case QImage::Format_RGB32: + break; + default: + if (img.hasAlphaChannel()) { + img = img.convertToFormat(premul + ? QImage::Format_ARGB32_Premultiplied + : QImage::Format_ARGB32); +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - converting to 32-bit alpha format (%d ms)\n", time.elapsed()); +#endif + } else { + img = img.convertToFormat(QImage::Format_RGB32); +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - converting to 32-bit (%d ms)\n", time.elapsed()); +#endif + } + } + + if (m_options & QGLContext::InvertedYBindOption) { +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - flipping bits over y (%d ms)\n", time.elapsed()); +#endif + if (img.isDetached()) { + int ipl = img.bytesPerLine() / 4; + int h = img.height(); + for (int y=0; y<h/2; ++y) { + int *a = (int *) img.scanLine(y); + int *b = (int *) img.scanLine(h - y - 1); + for (int x=0; x<ipl; ++x) + qSwap(a[x], b[x]); + } + } else { + // Create a new image and copy across. If we use the + // above in-place code then a full copy of the image is + // made before the lines are swapped, which processes the + // data twice. This version should only do it once. + img = img.mirrored(); + } + } + + if (externalFormat == GL_RGBA) { +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - doing byte swapping (%d ms)\n", time.elapsed()); +#endif + // The only case where we end up with a depth different from + // 32 in the switch above is for the RGB16 case, where we set + // the format to GL_RGB + Q_ASSERT(img.depth() == 32); + qt_gl_byteSwapImage(img, pixel_type); + } +#ifdef QT_OPENGL_ES + // OpenGL/ES requires that the internal and external formats be + // identical. + internalFormat = externalFormat; +#endif +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - uploading, image.format=%d, externalFormat=0x%x, internalFormat=0x%x, pixel_type=0x%x\n", + img.format(), externalFormat, internalFormat, pixel_type); +#endif + + const QImage &constRef = img; // to avoid detach in bits()... + glTexImage2D(target, 0, internalFormat, img.width(), img.height(), 0, externalFormat, + pixel_type, constRef.bits()); + + m_hasAlpha = (internalFormat != GL_RGB); +} + +void QGLBoundTexture::createFace + (GLenum target, const QSize &size, GLenum format) +{ + glTexImage2D(target, 0, format, size.width(), + size.height(), 0, format, GL_UNSIGNED_BYTE, 0); + m_hasAlpha = (format != GL_RGB); +} + +void QGLBoundTexture::finishUpload(GLenum target) +{ + Q_UNUSED(target); + +#if defined(QT_OPENGL_ES_2) + // OpenGL/ES 2.0 needs to generate mipmaps after all cubemap faces + // have been uploaded. + if (m_options & QGLContext::MipmapBindOption) { +#ifdef QGL_BIND_TEXTURE_DEBUG + printf(" - generating mipmaps (%d ms)\n", time.elapsed()); +#endif + glGenerateMipmap(target); + } +#endif + +#ifndef QT_NO_DEBUG + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + qWarning(" - texture upload failed, error code 0x%x, enum: %d (%x)\n", error, target, target); + } +#endif + +#ifdef QGL_BIND_TEXTURE_DEBUG + static int totalUploadTime = 0; + totalUploadTime += time.elapsed(); + printf(" - upload done in (%d ms) time=%d\n", time.elapsed(), totalUploadTime); +#endif +} + +// DDS format structure +struct DDSFormat { + quint32 dwSize; + quint32 dwFlags; + quint32 dwHeight; + quint32 dwWidth; + quint32 dwLinearSize; + quint32 dummy1; + quint32 dwMipMapCount; + quint32 dummy2[11]; + struct { + quint32 dummy3[2]; + quint32 dwFourCC; + quint32 dummy4[5]; + } ddsPixelFormat; +}; + +// compressed texture pixel formats +#define FOURCC_DXT1 0x31545844 +#define FOURCC_DXT2 0x32545844 +#define FOURCC_DXT3 0x33545844 +#define FOURCC_DXT4 0x34545844 +#define FOURCC_DXT5 0x35545844 + +#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif + +// PVR header format for container files that store textures compressed +// with the ETC1, PVRTC2, and PVRTC4 encodings. Format information from the +// PowerVR SDK at http://www.imgtec.com/powervr/insider/powervr-sdk.asp +// "PVRTexTool Reference Manual, version 1.11f". +struct PvrHeader +{ + quint32 headerSize; + quint32 height; + quint32 width; + quint32 mipMapCount; + quint32 flags; + quint32 dataSize; + quint32 bitsPerPixel; + quint32 redMask; + quint32 greenMask; + quint32 blueMask; + quint32 alphaMask; + quint32 magic; + quint32 surfaceCount; +}; + +#define PVR_MAGIC 0x21525650 // "PVR!" in little-endian + +#define PVR_FORMAT_MASK 0x000000FF +#define PVR_FORMAT_PVRTC2 0x00000018 +#define PVR_FORMAT_PVRTC4 0x00000019 +#define PVR_FORMAT_ETC1 0x00000036 + +#define PVR_HAS_MIPMAPS 0x00000100 +#define PVR_TWIDDLED 0x00000200 +#define PVR_NORMAL_MAP 0x00000400 +#define PVR_BORDER_ADDED 0x00000800 +#define PVR_CUBE_MAP 0x00001000 +#define PVR_FALSE_COLOR_MIPMAPS 0x00002000 +#define PVR_VOLUME_TEXTURE 0x00004000 +#define PVR_ALPHA_IN_TEXTURE 0x00008000 +#define PVR_VERTICAL_FLIP 0x00010000 + +#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 +#endif + +#ifndef GL_ETC1_RGB8_OES +#define GL_ETC1_RGB8_OES 0x8D64 +#endif + +bool QGLBoundTexture::canBindCompressedTexture + (const char *buf, int len, const char *format, bool *hasAlpha, + bool *isFlipped) +{ + if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) { + // Compressed texture loading only supported on little-endian + // systems such as x86 and ARM at the moment. + return false; + } + if (!format) { + // Auto-detect the format from the header. + if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) { + *hasAlpha = true; + *isFlipped = true; + return true; + } else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) { + const PvrHeader *pvrHeader = + reinterpret_cast<const PvrHeader *>(buf); + *hasAlpha = (pvrHeader->alphaMask != 0); + *isFlipped = ((pvrHeader->flags & PVR_VERTICAL_FLIP) != 0); + return true; + } + } else { + // Validate the format against the header. + if (!qstricmp(format, "DDS")) { + if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) { + *hasAlpha = true; + *isFlipped = true; + return true; + } + } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) { + if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) { + const PvrHeader *pvrHeader = + reinterpret_cast<const PvrHeader *>(buf); + *hasAlpha = (pvrHeader->alphaMask != 0); + *isFlipped = ((pvrHeader->flags & PVR_VERTICAL_FLIP) != 0); + return true; + } + } + } + return false; +} + +bool QGLBoundTexture::bindCompressedTexture + (const char *buf, int len, const char *format) +{ + if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) { + // Compressed texture loading only supported on little-endian + // systems such as x86 and ARM at the moment. + return false; + } +#if !defined(QT_OPENGL_ES) + QGLTextureExtensions *extensions = QGLTextureExtensions::extensions(); + if (!extensions) + return false; + if (!extensions->compressedTexImage2D) { + qWarning("QGLContext::bindTexture(): The GL implementation does " + "not support texture compression extensions."); + return false; + } +#endif + if (!format) { + // Auto-detect the format from the header. + if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) + return bindCompressedTextureDDS(buf, len); + else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) + return bindCompressedTexturePVR(buf, len); + } else { + // Validate the format against the header. + if (!qstricmp(format, "DDS")) { + if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) + return bindCompressedTextureDDS(buf, len); + } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) { + if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) + return bindCompressedTexturePVR(buf, len); + } + } + return false; +} + +bool QGLBoundTexture::bindCompressedTexture + (const QString& fileName, const char *format) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) + return false; + QByteArray contents = file.readAll(); + file.close(); + return bindCompressedTexture + (contents.constData(), contents.size(), format); +} + +bool QGLBoundTexture::bindCompressedTextureDDS(const char *buf, int len) +{ + QGLTextureExtensions *extensions = QGLTextureExtensions::extensions(); + if (!extensions) + return false; + + // Bail out if the necessary extension is not present. + if (!extensions->ddsTextureCompression) { + qWarning("QGLBoundTexture::bindCompressedTextureDDS(): DDS texture compression is not supported."); + return false; + } + + const DDSFormat *ddsHeader = reinterpret_cast<const DDSFormat *>(buf + 4); + if (!ddsHeader->dwLinearSize) { + qWarning("QGLBoundTexture::bindCompressedTextureDDS(): DDS image size is not valid."); + return false; + } + + int blockSize = 16; + GLenum format; + + switch(ddsHeader->ddsPixelFormat.dwFourCC) { + case FOURCC_DXT1: + format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + blockSize = 8; + break; + case FOURCC_DXT3: + format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + break; + case FOURCC_DXT5: + format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + default: + qWarning("QGLBoundTexture::bindCompressedTextureDDS(): DDS image format not supported."); + return false; + } + + const GLubyte *pixels = + reinterpret_cast<const GLubyte *>(buf + ddsHeader->dwSize + 4); + + GLuint id = m_resource.id(); + if (id) { + glBindTexture(GL_TEXTURE_2D, 0); // Just in case it is bound. + m_resource.destroy(); + } + id = 0; + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + q_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + q_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + m_resource.attach(QGLContext::currentContext(), id); + + int size; + int offset = 0; + int available = len - int(ddsHeader->dwSize + 4); + int w = ddsHeader->dwWidth; + int h = ddsHeader->dwHeight; + + // load mip-maps + for (int i = 0; i < (int) ddsHeader->dwMipMapCount; ++i) { + if (w == 0) w = 1; + if (h == 0) h = 1; + + size = ((w+3)/4) * ((h+3)/4) * blockSize; + if (size > available) + break; + extensions->compressedTexImage2D + (GL_TEXTURE_2D, i, format, w, h, 0, size, pixels + offset); + offset += size; + available -= size; + + // half size for each mip-map level + w = w/2; + h = h/2; + } + + // DDS images are not inverted. + m_options &= ~QGLContext::InvertedYBindOption; + + m_size = QSize(ddsHeader->dwWidth, ddsHeader->dwHeight); + m_hasAlpha = false; + return true; +} + +bool QGLBoundTexture::bindCompressedTexturePVR(const char *buf, int len) +{ + QGLTextureExtensions *extensions = QGLTextureExtensions::extensions(); + if (!extensions) + return false; + + // Determine which texture format we will be loading. + const PvrHeader *pvrHeader = reinterpret_cast<const PvrHeader *>(buf); + GLenum textureFormat; + quint32 minWidth, minHeight; + switch (pvrHeader->flags & PVR_FORMAT_MASK) { + case PVR_FORMAT_PVRTC2: + if (pvrHeader->alphaMask) + textureFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + else + textureFormat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + minWidth = 16; + minHeight = 8; + break; + + case PVR_FORMAT_PVRTC4: + if (pvrHeader->alphaMask) + textureFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + else + textureFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + minWidth = 8; + minHeight = 8; + break; + + case PVR_FORMAT_ETC1: + textureFormat = GL_ETC1_RGB8_OES; + minWidth = 4; + minHeight = 4; + break; + + default: + qWarning("QGLBoundTexture::bindCompressedTexturePVR(): PVR image format 0x%x not supported.", int(pvrHeader->flags & PVR_FORMAT_MASK)); + return false; + } + + // Bail out if the necessary extension is not present. + if (textureFormat == GL_ETC1_RGB8_OES) { + if (!extensions->etc1TextureCompression) { + qWarning("QGLBoundTexture::bindCompressedTexturePVR(): ETC1 texture compression is not supported."); + return false; + } + } else { + if (!extensions->pvrtcTextureCompression) { + qWarning("QGLBoundTexture::bindCompressedTexturePVR(): PVRTC texture compression is not supported."); + return false; + } + } + + // Boundary check on the buffer size. + quint32 bufferSize = pvrHeader->headerSize + pvrHeader->dataSize; + if (bufferSize > quint32(len)) { + qWarning("QGLBoundTexture::bindCompressedTexturePVR(): PVR image size is not valid."); + return false; + } + + // Create the texture. + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + GLuint id = m_resource.id(); + if (id) { + glBindTexture(GL_TEXTURE_2D, 0); // Just in case it is bound. + m_resource.destroy(); + } + id = 0; + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + m_resource.attach(QGLContext::currentContext(), id); + if (pvrHeader->mipMapCount) { + if ((m_options & QGLContext::LinearFilteringBindOption) != 0) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } else { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + } + } else if ((m_options & QGLContext::LinearFilteringBindOption) != 0) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + // Load the compressed mipmap levels. + const GLubyte *buffer = + reinterpret_cast<const GLubyte *>(buf + pvrHeader->headerSize); + bufferSize = pvrHeader->dataSize; + quint32 level = 0; + quint32 width = pvrHeader->width; + quint32 height = pvrHeader->height; + while (bufferSize > 0 && level <= pvrHeader->mipMapCount) { + quint32 size = + (qMax(width, minWidth) * qMax(height, minHeight) * + pvrHeader->bitsPerPixel) / 8; + if (size > bufferSize) + break; + extensions->compressedTexImage2D + (GL_TEXTURE_2D, GLint(level), textureFormat, + GLsizei(width), GLsizei(height), 0, GLsizei(size), buffer); + width /= 2; + height /= 2; + buffer += size; + ++level; + } + + // Restore the default pixel alignment for later texture uploads. + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + // Set the invert flag for the texture. The "vertical flip" + // flag in PVR is the opposite sense to our sense of inversion. + if ((pvrHeader->flags & PVR_VERTICAL_FLIP) != 0) + m_options &= ~QGLContext::InvertedYBindOption; + else + m_options |= QGLContext::InvertedYBindOption; + + m_size = QSize(pvrHeader->width, pvrHeader->height); + m_hasAlpha = (pvrHeader->alphaMask != 0); + return true; +} + +QT_END_NAMESPACE diff --git a/src/threed/textures/qgltextureutils_p.h b/src/threed/textures/qgltextureutils_p.h new file mode 100644 index 000000000..2dc7ea4c5 --- /dev/null +++ b/src/threed/textures/qgltextureutils_p.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLTEXTUREUTILS_P_H +#define QGLTEXTUREUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtOpenGL/qgl.h> +#include <QtCore/qdatetime.h> +#include "qglnamespace.h" +#include "qopenglfunctions.h" +#include "qglsharedresource_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif +#ifndef GL_UNSIGNED_SHORT_5_6_5 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#endif +#ifndef GL_UNSIGNED_INT_8_8_8_8_REV +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#endif +#ifndef GL_TEXTURE_CUBE_MAP +#define GL_TEXTURE_CUBE_MAP 0x8513 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_X +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#endif +#ifndef GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#endif + +#ifndef GL_GENERATE_MIPMAP_SGIS +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif + +#if !defined(QT_OPENGL_ES) +#define q_glTexParameteri(target,name,value) \ + glTexParameteri((target), (name), int(value)) +#else +#define q_glTexParameteri(target,name,value) \ + glTexParameterf((target), (name), GLfloat(int(value))) +#endif + +// Modify a wrapping mode to account for platform differences. +QGL::TextureWrap qt_gl_modify_texture_wrap(QGL::TextureWrap value); + +typedef void (QT3D_GLF_APIENTRYP q_glCompressedTexImage2DARB) + (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); + +class QGLTextureExtensions +{ +public: + QGLTextureExtensions(const QGLContext *ctx); + ~QGLTextureExtensions(); + + int npotTextures : 1; + int generateMipmap : 1; + int bgraTextureFormat : 1; + int ddsTextureCompression : 1; + int etc1TextureCompression : 1; + int pvrtcTextureCompression : 1; + q_glCompressedTexImage2DARB compressedTexImage2D; + + static QGLTextureExtensions *extensions(); +}; + +class QGLBoundTexture +{ +public: + QGLBoundTexture(); + ~QGLBoundTexture(); + + const QGLContext *context() const { return m_resource.context(); } + + GLuint textureId() const { return m_resource.id(); } + void setTextureId(const QGLContext *ctx, GLuint id) + { m_resource.attach(ctx, id); } + void clearId() { m_resource.clearId(); } + + QGLContext::BindOptions options() const { return m_options; } + void setOptions(QGLContext::BindOptions options) { m_options = options; } + + QSize size() const { return m_size; } + bool hasAlpha() const { return m_hasAlpha; } + + void startUpload(const QGLContext *ctx, GLenum target, const QSize &imageSize); + void uploadFace(GLenum target, const QImage &image, const QSize &scaleSize, + GLenum format = GL_RGBA); + void createFace(GLenum target, const QSize &size, GLenum format = GL_RGBA); + void finishUpload(GLenum target); + + static bool canBindCompressedTexture + (const char *buf, int len, const char *format, bool *hasAlpha, + bool *isFlipped); + bool bindCompressedTexture + (const QString& fileName, const char *format = 0); + bool bindCompressedTexture + (const char *buf, int len, const char *format = 0); + bool bindCompressedTextureDDS(const char *buf, int len); + bool bindCompressedTexturePVR(const char *buf, int len); + +private: + QGLSharedResource m_resource; + QGLContext::BindOptions m_options; + QSize m_size; + bool m_hasAlpha; + QTime time; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/threed/textures/textures.pri b/src/threed/textures/textures.pri new file mode 100644 index 000000000..c17c1531e --- /dev/null +++ b/src/threed/textures/textures.pri @@ -0,0 +1,15 @@ +INCLUDEPATH += $$PWD +VPATH += $$PWD +HEADERS += \ + qgltexture2d.h \ + qgltexturecube.h \ + qareaallocator.h +SOURCES += \ + qareaallocator.cpp \ + qglsharedresource.cpp \ + qgltexture2d.cpp \ + qgltexturecube.cpp \ + qgltextureutils.cpp +PRIVATE_HEADERS += \ + qglsharedresource_p.h \ + qgltexture2d_p.h diff --git a/src/threed/threed.pri b/src/threed/threed.pri new file mode 100644 index 000000000..84e11aedd --- /dev/null +++ b/src/threed/threed.pri @@ -0,0 +1,13 @@ +include(global/global.pri) +include(painting/painting.pri) +include(arrays/arrays.pri) +include(effects/effects.pri) +include(materials/materials.pri) +include(geometry/geometry.pri) +include(viewing/viewing.pri) +include(math3d/math3d.pri) +include(scene/scene.pri) +include(graphicsview/graphicsview.pri) +include(textures/textures.pri) +include(surfaces/surfaces.pri) +include(api/api.pri) diff --git a/src/threed/threed.pro b/src/threed/threed.pro new file mode 100644 index 000000000..00e8d60c4 --- /dev/null +++ b/src/threed/threed.pro @@ -0,0 +1,67 @@ +TEMPLATE = lib +TARGET = Qt3D$${QT_LIBINFIX} +gcov { + CONFIG += staticlib warn_on + QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage + QMAKE_LFLAGS += -fprofile-arcs -ftest-coverage +} else { + CONFIG += dll warn_on +} +QT += opengl \ + network +DESTDIR = $$[QT_INSTALL_LIBS] + +win32 { + DLLDESTDIR = ../../bin + !static:DEFINES += QT_MAKEDLL + + CONFIG(debug, debug|release) { + TARGET = $$member(TARGET, 0)d + } +} + +symbian { + DEFINES += QT_MAKEDLL + CONFIG += epocallowdlldata + contains(QT_EDITION, OpenSource) { + TARGET.CAPABILITY = LocalServices NetworkServices ReadUserData UserEnvironment WriteUserData + } else { + TARGET.CAPABILITY = All -Tcb + } +} + +include(threed.pri) +PUBLIC_HEADERS = $$HEADERS +HEADERS += $$PRIVATE_HEADERS +DEFINES += QT_BUILD_Qt3D_LIB + +!contains(QT_CONFIG, egl):DEFINES += QT_NO_EGL + +INSTALL_HEADERS = "" +for (hdr, PUBLIC_HEADERS) { + found_vdir = $$PWD + for (vdir, VPATH) { + found_vdir = $$vdir + exists($$found_vdir/$$hdr):break() + } + INSTALL_HEADERS += $$found_vdir/$$hdr +} + +# If Qt has been configured to build frameworks, then the build will put +# the Qt3D library into a framework bundle, so put the headers in the bundle +# as well. Other OS's, or mac without frameworks, install the headers into +# the Qt build tree directly. +macx:CONFIG(qt_framework, qt_framework|qt_no_framework) { + CONFIG += lib_bundle + FRAMEWORK_HEADERS.version = Versions + FRAMEWORK_HEADERS.path = Headers + FRAMEWORK_HEADERS.files = $$INSTALL_HEADERS + QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS +} else { + exportHeaders.input = PUBLIC_HEADERS + exportHeaders.output = $$[QT_INSTALL_HEADERS]/Qt3D/${QMAKE_FILE_IN_BASE}${QMAKE_FILE_EXT} + exportHeaders.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} + exportHeaders.CONFIG += no_link_no_clean + exportHeaders.variable_out = PRE_TARGETDEPS + QMAKE_EXTRA_COMPILERS += exportHeaders +} diff --git a/src/threed/viewing/qglcamera.cpp b/src/threed/viewing/qglcamera.cpp new file mode 100644 index 000000000..3cbb43b1a --- /dev/null +++ b/src/threed/viewing/qglcamera.cpp @@ -0,0 +1,1310 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglcamera.h" +#include "qglpainter.h" +#include <QtGui/qquaternion.h> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLCamera + \brief The QGLCamera class defines the projection to apply to simulate a camera's position, orientation, and optics. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::viewing + + \section1 Modelview and projection transformations + + A QGLCamera instance is applied to the scene in two phases: + modelview transformation and projection transformation. + + During the modelview transformation, the eye(), center(), and + upVector() are used to generate a 4x4 transformation matrix that + reflects the viewer's current position and orientation. + + During the projection transformation, the projectionType(), + nearPlane(), farPlane(), fieldOfView(), and viewSize() are used + to define a viewing volume as a 4x4 transformation matrix. + + The modelview transformation matrix is returned by modelViewMatrix(). + The projection transformation matrix is returned by projectionMatrix(). + + \section1 Positioning and orienting the view + + The viewer position and orientation are defined by eye(), center(), + and upVector(). The location of the viewer in world co-ordinates is + given by eye(), the viewer is looking at the object of interest located + at center(), and the upVector() specifies the direction that should + be considered "up" with respect to the viewer. + + The vector from the eye() to the center() is called the "view vector", + and the cross-product of the view vector and upVector() is called + the "side vector". The view vector specifies the direction the + viewer is looking, and the side vector points off to the right of + the viewer. + + It is recommended that the view vector and upVector() be at right angles + to each other, but this is not required as long as the angle between + them is close to 90 degrees. + + The most common use of view and up vectors that are not at right angles + is to simulate a human eye at a specific height above the ground looking + down at a lower object or up at a higher object. In this case, the + the view vector will not be true horizontal, but the upVector() indicating + the human's upright stance will be true vertical. + + \section1 Zooming the camera image + + There are two ways to zoom the image seen through the camera: either + the camera eye() position can be moved closer to the object of interest, + or the field of view of the camera lens can be changed to make it appear + as though the object is moving closer. + + Changing the eye() position changes the lighting calculation in the + scene because the viewer is in a different position, changing the + angle of light reflection on the object's surface. + + The setFieldOfView() function can be used to simulate the effect of a + camera lens. The smaller the fieldOfView(), the closer the object + will appear. The lighting calculation will be the same as for the + unzoomed scene. + + If fieldOfView() is zero, then a standard perspective frustum of + viewSize() is used to define the viewing volume. The viewSize() + can be adjusted with setViewSize() to zoom the view. A smaller + viewSize() will make the the object appear closer. + + The fieldOfView() or viewSize() is applied as part of the + projectionMatrix(). + + \section1 Rotating the viewer or object of interest + + Rotating a viewer in 3D space is a very delicate process. It is very + easy to construct the rotation incorrectly and end up in a "gimbal lock" + state where further rotations are impossible in certain directions. + + To help alleviate this problem, QGLCamera uses a quaternion-based + approach to generate rotations. A quaternion is a compact representation + of a rotation in 3D space. Rotations can be combined through quaternion + multiplication. More information on quaternions can be found in the + documentation for QQuaternion. + + Before rotating the view, you should first decide the type + of rotation you want to perform: + + \list + \i Tilting or panning a fixed eye to reveal the scene in different + directions and orientations. This is equivalent to mounting a camera + on a fixed tripod and then adjusting the direction of view and + orientation with the tripod controls. + \i Rotating a moving viewer about the object of interest. This is + equivalent to moving the viewer around the object at a fixed distance, + but with the viewer always pointing at the object. + \endlist + + In the QGLCamera class, the first type of rotation is performed with + rotateEye() and the second with rotateCenter(). Each of these functions + take a quaternion argument that defines the type of rotation to perform. + + The tilt(), pan(), and roll() functions return values that can help with + constructing the rotation quaternions to pass to rotateEye() and + rotateCenter(). Tilt and pan are also known as "pitch" and "yaw" in + flight dynamics. + + Three axes of rotation are used to compute the quaternions. The tilt() + quaternion is computed with respect to the side vector, the pan() + quaterion is computed with respect to the upVector(), and the roll() + quaternion is computed with respect to the view vector. + + The following example tilts the direction the eye() is pointing + by 5 degrees, and then pans by 45 degrees: + + \code + camera.rotateEye(camera.tilt(5)); + camera.rotateEye(camera.pan(45)); + \endcode + + The next example performs the two rotations in a single fluid step + (note that the rotation to be performed first is multiplied last): + + \code + camera.rotateEye(camera.pan(45) * camera.tilt(5)); + \endcode + + These two examples will not produce the same visual result, even though + it looks like they might. In the first example, the upVector() is tilted + before the pan() quaternion is computed. In the second example, the pan() + quaternion is computed using the original upVector(). + + This difference in behavior is useful in different situations. Some + applications may wish to perform all rotations relative to the original + viewer orientation, and other applications may wish to perform rotations + relative to the current viewer orientation. These application types + correspond to the second and first examples above. + + \section1 Moving the viewer or object of interest + + The simplest way to move the viewer or object of interest is to call + setEye() or setCenter() respectively and supply a new position in + world co-ordinates. However, this can lead to non-intuitive movements + if the viewer orientation is not aligned with the world co-ordinate axes. + + For example, subtracting 3 from the eye() x co-ordinate will appear to + move the eye left 3 units if the viewer orientation is aligned with the + world co-ordinate axes. But it will not appear to move the eye left 3 + units in any other orientation. + + The translation() function can be used to construct a translation + vector that is aligned with the viewer's current orientation. + Movement in the x direction will move along the side vector, movement in + the y direction will move along upVector(), and movement in the z + direction will move along the view vector. + + The translation() function is useful when implementing operations such + as "step left", "jump up", and so on where the movement should be + interpreted relative to the viewer's current orientation, not the + world co-ordinate axes, + + In other words, the following two lines of code are not equivalent + unless the view is oriented with the world co-ordinate axes: + + \code + camera.translateEye(camera.translation(x, y, z)); + + camera.translateEye(QVector3D(x, y, z)); + \endcode + + The following example translates the eye() position while + keeping the object of interest at the original center(): + + \code + camera.translateEye(camera.translation(x, y, z)); + \endcode + + The following example translates the object of interest at + center() while keeping the eye() position fixed: + + \code + camera.translateCenter(camera.translation(x, y, z)); + \endcode + + The following example translates both the eye() and the center() + by the same amount, which will maintain the original view vector. + + \code + QVector3D vector = camera.translation(x, y, z); + camera.translateEye(vector); + camera.translateCenter(vector); + \endcode + + It is important that the translation vector for center() be computed + before eye() is translated if both eye() and center() must move by the + same amount. The following code translates center() in the viewer + orientation after the eye() is translated: + + \code + camera.translateEye(camera.translation(x, y, z)); + camera.translateCenter(camera.translation(x, y, z)); + \endcode + + Translating both eye() and center() by the same amount can be used + to simulate sliding a viewer past a scene while always looking in the + same direction (for example, filming a scene from a moving vehicle). + An alternative is to fix the viewer and move the scene itself: + the negation of the translation() vector can be applied to the + scene's modelview transformation. + + \section1 Motion tracking + + Viewing of 3D scenes can be enhanced if there is some way to track + the motion of the viewer or the orientation of the display device. + + Applications can use setMotionAdjustment() to alter the position + of the camera to account for the viewer's motion. This indicates + the viewer's position relative to the center of the screen. + The motionAdjustment() vector is used to determine by how much + the camera position should be adjusted. The distance of the viewer + from the screen is ignored. + + On handheld devices that use accelerometers to determine the + orientation of the device, the down vector due to gravity + can be adjusted to serve as a motion tracking vector. + + The output of motion tracking hardware can be very noisy, + with minor fluctuations due to viewer twitch movements or + environmental factors. The application is responsible for + cleaning up the signal and removing these fluctuations before + setMotionAdjustment() is called. + + \section1 Stereo projections + + QGLCamera can adjust the camera position for rendering separate left + and right eye images by setting eyeSeparation() to a non-zero value. + The eyeSeparation() is in world co-ordinates. + + Objects that are placed at center() will coincide in the left and + right eye images, establishing the logical center of the stereo + effect. Objects that are closer to the eye() will be rendered + to appear closer in the stereo effect, and objects that are further + away from eye() than center() will be rendered to appear further away. + + Perspective and Orthographic projections incorporate the + eyeSeparation() into the modelViewMatrix() by altering the + eye() position. + + \sa QGLView, QGLPainter +*/ + +/*! + \qmlclass Camera QGLCamera + \brief The Camera item defines the viewing position and projection for a 3D scene. + \since 4.8 + \ingroup qt3d::qml3d + + Camera instances are defined on a \l Viewport item using the + Viewport::camera property: + + \code + import Qt 4.7 + import Qt3D 1.0 + + Viewport { + width: 640; height: 480 + camera: Camera { + eye: Qt.vector3d(-1, 2, 10) + } + light: Light {} + Item3D { + mesh: Mesh { source: "meshes/teapot.bez" } + effect: Effect {} + } + } + \endcode + + \section1 Positioning and orienting the view + + The viewer position and orientation are defined by \l eye, \l center, + and \l upVector. The location of the viewer in world co-ordinates is + given by \l eye, the viewer is looking at the object of interest located + at \l center, and the \l upVector specifies the direction that should + be considered "up" with respect to the viewer. + + The vector from the \l eye to the \l center is called the "view vector", + and the cross-product of the view vector and \l upVector is called + the "side vector". The view vector specifies the direction the + viewer is looking, and the side vector points off to the right of + the viewer. + + It is recommended that the view vector and \l upVector be at right angles + to each other, but this is not required as long as the angle between + them is close to 90 degrees. + + The most common use of view and up vectors that are not at right angles + is to simulate a human eye at a specific height above the ground looking + down at a lower object or up at a higher object. In this case, the + the view vector will not be true horizontal, but the \l upVector + indicating the human's upright stance will be true vertical. + + \section1 Zooming the camera image + + There are two ways to zoom the image seen through the camera: either + the camera \l eye position can be moved closer to the object of interest, + or the field of view of the camera lens can be changed to make it appear + as though the object is moving closer. + + Changing the \l eye position changes the lighting calculation in the + scene because the viewer is in a different position, changing the + angle of light reflection on the object's surface. + + The \l fieldOfView property function can be used to simulate the effect + of a camera lens. The smaller the \l fieldOfView, the closer the object + will appear. The lighting calculation will be the same as for the + unzoomed scene. + + If \l fieldOfView is zero, then a standard perspective frustum of + is used to define the viewing volume based on the width and height + of the \l Viewport. + + \section1 Stereo projections + + Camera can adjust the camera position for rendering separate left + and right eye images by setting the \l eyeSeparation property + to a non-zero value. The \l eyeSeparation is in world co-ordinates. + + Objects that are placed at \l center will coincide in the left and + right eye images, establishing the logical center of the stereo + effect. Objects that are closer to the \l eye will be rendered + to appear closer in the stereo effect, and objects that are further + away from \l eye than \l center will be rendered to appear further away. + + \sa Viewport +*/ + +class QGLCameraPrivate +{ +public: + QGLCameraPrivate(); + + QGLCamera::ProjectionType projectionType; + qreal fieldOfView; + qreal nearPlane; + qreal farPlane; + QSizeF viewSize; + QSizeF minViewSize; + int screenRotation; + QVector3D eye; + QVector3D upVector; + QVector3D center; + QVector3D viewVector; + qreal eyeSeparation; + QVector3D motionAdjustment; + QQuaternion motionQuaternion; + bool adjustForAspectRatio; +}; + +QGLCameraPrivate::QGLCameraPrivate() + : projectionType(QGLCamera::Perspective), + fieldOfView(0.0f), + nearPlane(5.0f), + farPlane(1000.0f), + viewSize(2.0f, 2.0f), + minViewSize(0.0001f, 0.0001f), + screenRotation(0), + eye(0.0f, 0.0f, 10.0f), + upVector(0.0f, 1.0f, 0.0f), + center(0.0f, 0.0f, 0.0f), + viewVector(0.0f, 0.0f, -10.0f), + eyeSeparation(0.0f), + motionAdjustment(0.0f, 0.0f, 1.0f), + adjustForAspectRatio(true) +{ +} + +/*! + Constructs a QGLCamera with the default properties and + attaches it to \a parent. +*/ +QGLCamera::QGLCamera(QObject *parent) + : QObject(parent), d_ptr(new QGLCameraPrivate) +{ +} + +/*! + Destroys this QGLCamera object. +*/ +QGLCamera::~QGLCamera() +{ + delete d_ptr; +} + +/*! + \enum QGLCamera::ProjectionType + This enum defines the type of view projection to use for a QGLCamera. + + \value Perspective Use a perspective view. + \value Orthographic Use an orthographic view. +*/ + +/*! + \property QGLCamera::projectionType + \brief the projection type for this camera. The default is Perspective. +*/ + +/*! + \qmlproperty enumeration Camera::projectionType + + The projection type for this camera, which is one of: + + \list + \o Perspective Use a perspective view. This is the default. + \o Orthographic Use an orthographic view. + \endlist +*/ + +QGLCamera::ProjectionType QGLCamera::projectionType() const +{ + Q_D(const QGLCamera); + return d->projectionType; +} + +void QGLCamera::setProjectionType(QGLCamera::ProjectionType value) +{ + Q_D(QGLCamera); + if (d->projectionType != value) { + d->projectionType = value; + emit projectionChanged(); + } +} + +/*! + \property QGLCamera::fieldOfView + \brief the field of view in degrees for a perspective projection. + + The default value is zero, which indicates a standard perspective + frustum view volume of viewSize() in size. If the value is not + zero, then viewSize() is ignored. + + This value is ignored if projectionType() is not Perspective. + + \sa viewSize() +*/ + +/*! + \qmlproperty real Camera::fieldOfView + The field of view in degrees for a perspective projection. + + The default value is zero, which indicates a standard perspective + frustum view volume. + + This value is ignored if projectionType is not Perspective. + + \sa projectionType +*/ + +qreal QGLCamera::fieldOfView() const +{ + Q_D(const QGLCamera); + return d->fieldOfView; +} + +void QGLCamera::setFieldOfView(qreal angle) +{ + Q_D(QGLCamera); + if (d->fieldOfView != angle) { + d->fieldOfView = angle; + emit projectionChanged(); + } +} + +/*! + \property QGLCamera::nearPlane + \brief the distance from the eye to the near clipping plane. + The default value is 5. + + \sa farPlane() +*/ + +/*! + \qmlproperty real Camera::nearPlane + The distance from the eye to the near clipping plane. + The default value is 5. + + \sa farPlane +*/ + +qreal QGLCamera::nearPlane() const +{ + Q_D(const QGLCamera); + return d->nearPlane; +} + +void QGLCamera::setNearPlane(qreal value) +{ + Q_D(QGLCamera); + if (d->nearPlane != value) { + d->nearPlane = value; + emit projectionChanged(); + } +} + +/*! + \property QGLCamera::farPlane + \brief the distance from the eye to the far clipping plane. + The default value is 1000. + + \sa nearPlane() +*/ + +/*! + \qmlproperty real Camera::farPlane + The distance from the eye to the far clipping plane. + The default value is 1000. + + \sa nearPlane +*/ + +qreal QGLCamera::farPlane() const +{ + Q_D(const QGLCamera); + return d->farPlane; +} + +void QGLCamera::setFarPlane(qreal value) +{ + Q_D(QGLCamera); + if (d->farPlane != value) { + d->farPlane = value; + emit projectionChanged(); + } +} + +/*! + \property QGLCamera::viewSize + \brief the size of the front of the projection viewing volume. + The viewing volume is assumed to be centered on the origin. + + The default value is (2, 2), which indicates a viewing volume front + from (-1, -1) to (1, 1). + + If the width or height of the viewing volume is negative, then the + co-ordinates will be swapped. For example, a size of (2, -2) will + flip the vertical axis upside down for a viewing volume from + (-1, 1) to (1, -1). + + The view size will be further adjusted by the window's aspect ratio + when projectionMatrix() is called. For best results, the width and + height of the view size should be the same to define an ideal square + viewing volume, which is then extended to the final viewing volume + width and height based on the window's aspect ratio. + + \sa projectionMatrix(), minViewSize() +*/ +QSizeF QGLCamera::viewSize() const +{ + Q_D(const QGLCamera); + return d->viewSize; +} + +void QGLCamera::setViewSize(const QSizeF& size) +{ + Q_D(QGLCamera); + QSizeF sz(size); + if (qAbs(sz.width()) < d->minViewSize.width()) { + if (sz.width() >= 0.0f) + sz.setWidth(d->minViewSize.width()); + else + sz.setWidth(-d->minViewSize.width()); + } + if (qAbs(sz.height()) < d->minViewSize.height()) { + if (sz.height() >= 0.0f) + sz.setHeight(d->minViewSize.height()); + else + sz.setHeight(-d->minViewSize.height()); + } + if (d->viewSize != sz) { + d->viewSize = sz; + emit projectionChanged(); + } +} + +/*! + \property QGLCamera::minViewSize + \brief the minimum size of the front of the projection viewing volume. + + The minimum view size is used to clamp viewSize() when zooming + the camera closer to an object to prevent it "passing through" + the object and causing the scale factor to become infinite. + + The default value is (0.0001, 0.0001). + + \sa projectionMatrix(), viewSize() +*/ +QSizeF QGLCamera::minViewSize() const +{ + Q_D(const QGLCamera); + return d->minViewSize; +} + +void QGLCamera::setMinViewSize(const QSizeF& size) +{ + Q_D(QGLCamera); + if (d->minViewSize != size) { + d->minViewSize = size; + emit projectionChanged(); + } +} + +/*! + \property QGLCamera::screenRotation + \brief the screen rotation angle in degrees. The default + value is 0. If this value is 90 or 270, then the view + will be flipped width for height. The only supported values + are 0, 90, 180, and 270. The screen is rotated around the + positive z axis. + + This setting is intended for simple screen rotations on handheld + devices that can be held in either portrait or landscape orientations. + The entire screen image is rotated so that it can be viewed in a + different device orientation. + + Use rotateEye() or rotateCenter() for more complex rotations + that are not aligned with 0, 90, 180, or 270 degrees. +*/ + +int QGLCamera::screenRotation() const +{ + Q_D(const QGLCamera); + return d->screenRotation; +} + +void QGLCamera::setScreenRotation(int angle) +{ + Q_D(QGLCamera); + if (d->screenRotation != angle) { + d->screenRotation = angle; + emit projectionChanged(); + } +} + +/*! + \property QGLCamera::eye + \brief the position of the viewer's eye. The default value is (0, 0, 10). + + \sa translateEye(), upVector(), center(), eyeSeparation() + \sa motionAdjustment() +*/ + +/*! + \qmlproperty vector3D Camera::eye + The position of the viewer's eye. The default value is (0, 0, 10). + + \sa upVector, center, eyeSeparation +*/ +QVector3D QGLCamera::eye() const +{ + Q_D(const QGLCamera); + return d->eye; +} + +void QGLCamera::setEye(const QVector3D& vertex) +{ + Q_D(QGLCamera); + if (d->eye != vertex) { + d->eye = vertex; + d->viewVector = d->center - d->eye; + emit viewChanged(); + } +} + +/*! + Adjusts the position of the viewer's eye by the components + (\a x, \a y, \a z), where the components are interpreted relative + to the viewer's current orientation. See translation() for more + information. + + This function is accessible to QML on the Camera item. + + \sa eye(), setEye(), translateCenter() +*/ +void QGLCamera::translateEye(qreal x, qreal y, qreal z) +{ + Q_D(QGLCamera); + d->eye += translation(x, y, z); + d->viewVector = d->center - d->eye; + emit viewChanged(); +} + +/*! + \property QGLCamera::upVector + \brief the up vector for the viewer. The default value is (0, 1, 0). + + \sa eye(), center() +*/ + +/*! + \qmlproperty vector3D Camera::upVector + The up vector for the viewer. The default value is (0, 1, 0). + + \sa eye, center +*/ + +QVector3D QGLCamera::upVector() const +{ + Q_D(const QGLCamera); + return d->upVector; +} + +void QGLCamera::setUpVector(const QVector3D& vector) +{ + Q_D(QGLCamera); + if (d->upVector != vector) { + d->upVector = vector; + emit viewChanged(); + } +} + +/*! + \property QGLCamera::center + \brief the center of the view visible from the viewer's position. + The default value is (0, 0, 0). + + \sa translateCenter(), eye(), upVector() +*/ + +/*! + \qmlproperty vector3D Camera::center + The center of the view visible from the viewer's position. + The default value is (0, 0, 0). + + \sa eye, upVector +*/ + +QVector3D QGLCamera::center() const +{ + Q_D(const QGLCamera); + return d->center; +} + +void QGLCamera::setCenter(const QVector3D& vertex) +{ + Q_D(QGLCamera); + if (d->center != vertex) { + d->center = vertex; + d->viewVector = d->center - d->eye; + emit viewChanged(); + } +} + +/*! + Adjusts the center of the view by the components (\a x, \a y, \a z), + where the components are interpreted relative to the viewer's current + orientation. See translation() for more information. + + This function is accessible to QML on the Camera item. + + \sa center(), setCenter(), translateEye() +*/ +void QGLCamera::translateCenter(qreal x, qreal y, qreal z) +{ + Q_D(QGLCamera); + d->center += translation(x, y, z); + d->viewVector = d->center - d->eye; + emit viewChanged(); +} + +/*! + \property QGLCamera::eyeSeparation + \brief the separation between the eyes when stereo viewing is in use, + with eye() specifying the mid-point between the eyes. The default + value is 0. + + \sa eye() +*/ + +/*! + \qmlproperty real Camera::eyeSeparation + The separation between the eyes when stereo viewing is in use, + with \l eye property specifying the mid-point between the eyes. + The default value is 0. + + \sa eye +*/ + +qreal QGLCamera::eyeSeparation() const +{ + Q_D(const QGLCamera); + return d->eyeSeparation; +} + +void QGLCamera::setEyeSeparation(qreal value) +{ + Q_D(QGLCamera); + if (d->eyeSeparation != value) { + d->eyeSeparation = value; + emit viewChanged(); + } +} + +/*! + \property QGLCamera::motionAdjustment + \brief the adjustment vector to apply to the eye() for user motion. + + This property is typically used to implement motion tracking. + It is interpreted as a vector from the center of the screen to the + current position of the viewer. The angle between the motion + adjustment vector and the screen center is used to adjust the + position of the eye() when modelViewMatrix() is called. + + The default value is (0, 0, 1), which indicates a viewer + directly in front of the center of the screen. + + The units for the vector are unspecified. They could be + meters, centimeters, or the force due to gravity in various + directions from an accelerometer. The angle defined + by the vector is used to perform the adjustment, not its + magnitude. + + The output of motion tracking hardware can be very noisy, + with minor fluctuations due to viewer twitch movements or + environmental factors. The application is responsible for + cleaning up the signal and removing these fluctuations before + altering this property. + + \sa eye(), modelViewMatrix() +*/ + +QVector3D QGLCamera::motionAdjustment() const +{ + Q_D(const QGLCamera); + return d->motionAdjustment; +} + +void QGLCamera::setMotionAdjustment(const QVector3D& vector) +{ + Q_D(QGLCamera); + if (d->motionAdjustment != vector) { + d->motionAdjustment = vector; + if (vector.x() == 0.0f && vector.y() == 0.0f) { + // If the vector is centered, then don't perform any rotations. + d->motionQuaternion = QQuaternion(); + } else { + // Determine the pan and tilt angles from the vector. + QVector3D view = -vector.normalized(); + if (view.z() < 0.0f) + view = -view; + qreal xangle = asin(view.x()) * 180.0f / M_PI; + qreal yangle = asin(-view.y()) * 180.0f / M_PI; + + // Construct the pan and tilt quaternions. + if (qFuzzyIsNull(xangle)) + d->motionQuaternion = tilt(yangle); + else if (qFuzzyIsNull(yangle)) + d->motionQuaternion = pan(xangle); + else + d->motionQuaternion = tilt(yangle) * pan(xangle); + } + emit viewChanged(); + } +} + +/*! + \property QGLCamera::adjustForAspectRatio + \brief the adjustment state of the aspect ratio in the viewing volume. + + By default, QGLCamera adjusts the viewing volume for the aspect + ratio of the window so that pixels appear square without the + application needing to adjust viewSize() manually. + + If this property is false, then the aspect ratio adjustment is + not performed. +*/ + +/*! + \qmlproperty bool Camera::adjustForAspectRatio + The adjustment state of the aspect ratio in the viewing volume. + + By default, the camera adjusts the viewing volume for the aspect + ratio of the window so that pixels appear square without the + application needing to adjust the view size manually. + + If this property is false, then the aspect ratio adjustment is + not performed. + + \sa projectionType +*/ + +bool QGLCamera::adjustForAspectRatio() const +{ + Q_D(const QGLCamera); + return d->adjustForAspectRatio; +} + +void QGLCamera::setAdjustForAspectRatio(bool value) +{ + Q_D(QGLCamera); + if (d->adjustForAspectRatio != value) { + d->adjustForAspectRatio = value; + emit viewChanged(); + } +} + +/*! + Returns the quaternion corresponding to tilting the view up or + down by \a angle degrees. The returned quaternion can be applied to + the eye() position with rotateEye() or to the center() position with + rotateCenter(). + + \sa pan(), roll(), rotateEye(), rotateCenter() +*/ +QQuaternion QGLCamera::tilt(qreal angle) const +{ + Q_D(const QGLCamera); + QVector3D side = QVector3D::crossProduct(d->viewVector, d->upVector); + return QQuaternion::fromAxisAndAngle(side, angle); +} + +/*! + Returns the quaternion corresponding to panning the view left or + right by \a angle degrees. The returned quaternion can be applied to + the eye() position with rotateEye() or to the center() position with + rotateCenter(). + + \sa tilt(), roll(), rotateEye(), rotateCenter() +*/ +QQuaternion QGLCamera::pan(qreal angle) const +{ + Q_D(const QGLCamera); + return QQuaternion::fromAxisAndAngle(d->upVector, angle); +} + +/*! + Returns the quaternion corresponding to rolling the view left or + right by \a angle degrees. The returned quaternion can be applied to + the eye() position with rotateEye() or to the center() position with + rotateCenter(). + + \sa tilt(), pan(), rotateEye(), rotateCenter() +*/ +QQuaternion QGLCamera::roll(qreal angle) const +{ + Q_D(const QGLCamera); + return QQuaternion::fromAxisAndAngle(d->viewVector, angle); +} + +/*! + Rotates the orientation of the eye() according to the quaternion \a q. + The eye() will remain in the same position, but the upVector() and + center() may be altered by the rotation. + + \sa rotateCenter(), tilt(), pan(), roll() +*/ +void QGLCamera::rotateEye(const QQuaternion& q) +{ + Q_D(QGLCamera); + d->upVector = q.rotatedVector(d->upVector); + d->viewVector = q.rotatedVector(d->viewVector); + d->center = d->eye + d->viewVector; + emit viewChanged(); +} + +/*! + Rotates the position and orientation of the eye() around center() + according to the quaternion \a q. The center() will remain in the + same position, but the upVector() and eye() may be altered by + the rotation. + + \sa rotateEye(), tilt(), pan(), roll() +*/ +void QGLCamera::rotateCenter(const QQuaternion& q) +{ + Q_D(QGLCamera); + d->upVector = q.rotatedVector(d->upVector); + d->viewVector = q.rotatedVector(d->viewVector); + d->eye = d->center - d->viewVector; + emit viewChanged(); +} + +/*! + Returns a translation vector that can be used to adjust the eye() + or center() by \a x units side-ways, \a y units up, + and \a z units forwards. + + This function is useful when implementing operations such as + "step left", "jump up", and so on where the movement should be + interpreted relative to the viewer's current orientation, not the + world co-ordinate axes. + + The following example moves the eye() 2 units to the right of the + current eye position while keeping the same center() of interest: + + \code + camera.setEye(camera.eye() + camera.translation(2, 0, 0)); + \endcode + + \sa translateEye(), translateCenter() +*/ +QVector3D QGLCamera::translation(qreal x, qreal y, qreal z) const +{ + Q_D(const QGLCamera); + QVector3D vector(0.0f, 0.0f, 0.0f); + if (x != 0.0f) + vector += QVector3D::normal(d->viewVector, d->upVector) * x; + if (y != 0.0f) + vector += d->upVector.normalized() * y; + if (z != 0.0f) + vector += d->viewVector.normalized() * z; + return vector; +} + +/*! + Returns the transformation matrix to apply to the projection matrix + to present the scene as viewed from the camera position. + + The \a aspectRatio specifies the aspect ratio of the window the + camera view is being displayed in. An \a aspectRatio of 1 indicates that + the window is square. An \a aspectRatio greater than 1 indicates that + the window is wider than it is high. An \a aspectRatio less than 1 + indicates that the window is higher than it is wide. + + \sa modelViewMatrix() +*/ +QMatrix4x4 QGLCamera::projectionMatrix(qreal aspectRatio) const +{ + Q_D(const QGLCamera); + QMatrix4x4 m; + if (!d->adjustForAspectRatio) + aspectRatio = 1.0f; + if (d->screenRotation != 0) { + m.rotate((qreal)(d->screenRotation), 0.0f, 0.0f, 1.0f); + if (d->screenRotation == 90 || d->screenRotation == 270) { + if (aspectRatio != 0.0f) + aspectRatio = 1.0f / aspectRatio; + } + } + if (d->projectionType == Perspective && d->fieldOfView != 0.0f) { + m.perspective(d->fieldOfView, aspectRatio, + d->nearPlane, d->farPlane); + } else { + qreal halfWidth = d->viewSize.width() / 2.0f; + qreal halfHeight = d->viewSize.height() / 2.0f; + if (aspectRatio > 1.0f) { + halfWidth *= aspectRatio; + } else if (aspectRatio > 0.0f && aspectRatio < 1.0f) { + halfHeight /= aspectRatio; + } + if (d->projectionType == Perspective) { + m.frustum(-halfWidth, halfWidth, -halfHeight, halfHeight, + d->nearPlane, d->farPlane); + } else { + m.ortho(-halfWidth, halfWidth, -halfHeight, halfHeight, + d->nearPlane, d->farPlane); + } + } + return m; +} + +/*! + Returns the transformation to apply to the modelview matrix + to present the scene as viewed from the eye position. + + The \a eye parameter is used to adjust the camera's position + horizontally by half of eyeSeparation() if \a eye is QGL::LeftEye + or QGL::RightEye. + + \sa projectionMatrix() +*/ +QMatrix4x4 QGLCamera::modelViewMatrix(QGL::Eye eye) const +{ + Q_D(const QGLCamera); + QMatrix4x4 m; + QVector3D adjust; + if (eye == QGL::LeftEye) + adjust = translation(-d->eyeSeparation / 2.0f, 0.0f, 0.0f); + else if (eye == QGL::RightEye) + adjust = translation(d->eyeSeparation / 2.0f, 0.0f, 0.0f); + if (d->motionQuaternion.isIdentity()) { + m.lookAt(d->eye + adjust, d->center, d->upVector); + } else { + QVector3D up = d->motionQuaternion.rotatedVector(d->upVector); + QVector3D view = d->motionQuaternion.rotatedVector(d->viewVector); + m.lookAt(d->center - view + adjust, d->center, up); + } + return m; +} + +/*! + Maps \a point from viewport co-ordinates to eye co-ordinates. + The size of the viewport is given by \a viewportSize, and its + aspect ratio by \a aspectRatio. + + The returned vector will have its x and y components set to the + position of the point on the near plane, and the z component + set to -nearPlane(). + + This function is used for converting a mouse event's position + into eye co-ordinates within the current camera view. +*/ +QVector3D QGLCamera::mapPoint + (const QPoint& point, qreal aspectRatio, const QSize& viewportSize) const +{ + Q_D(const QGLCamera); + + // Rotate the co-ordinate system to account for the screen rotation. + int x = point.x(); + int y = point.y(); + int width = viewportSize.width(); + int height = viewportSize.height(); + if (!d->adjustForAspectRatio) + aspectRatio = 1.0f; + if (d->screenRotation == 90) { + if (aspectRatio != 0.0f) + aspectRatio = 1.0f / aspectRatio; + qSwap(x, y); + qSwap(width, height); + y = height - 1 - y; + } else if (d->screenRotation == 180) { + x = width - 1 - x; + y = height - 1 - y; + } else if (d->screenRotation == 270) { + if (aspectRatio != 0.0f) + aspectRatio = 1.0f / aspectRatio; + qSwap(x, y); + qSwap(width, height); + } + + // Determine the relative distance from the middle of the screen. + // After this xrel and yrel are typically between -1.0 and +1.0 + // (unless the point was outside the viewport). The yrel is + // flipped upside down to account for the incoming co-ordinate + // being left-handed, but the world being right-handed. + qreal xrel, yrel; + if (width) + xrel = (((qreal)(x * 2)) - (qreal)width) / (qreal)width; + else + xrel = 0.0f; + if (height) + yrel = -(((qreal)(y * 2)) - (qreal)height) / (qreal)height; + else + yrel = 0.0f; + + // Reverse the projection and return the point in world co-ordinates. + QMatrix4x4 m = projectionMatrix(aspectRatio); + QMatrix4x4 invm = m.inverted(); + return invm.map(QVector3D(xrel, yrel, -1.0f)); +} + +/*! + \fn void QGLCamera::projectionChanged() + + This signal is emitted when one of projectionType(), fieldOfView(), + nearPlane(), farPlane(), viewSize(), or screenRotation() changes, + indicating a modification to the optical properties of the camera + looking at the scene. + + \sa viewChanged() +*/ + +/*! + \fn void QGLCamera::viewChanged() + + This signal is emitted when one of eye(), upVector(), or center() + changes, indicating a modification to the viewer's position or + orientation. + + \sa projectionChanged() +*/ + +/*! + \enum QGLCamera::RotateOrder + This enum defines the order to perform a tilt, pan, and roll + of a QGLCamera eye or center. + + \value TiltPanRoll Tilt, then pan, then roll. + \value TiltRollPan Tilt, then roll, then pan. + \value PanTiltRoll Pan, then tilt, then roll. + \value PanRollTilt Pan, then roll, then tilt. + \value RollTiltPan Roll, then tilt, then pan. + \value RollPanTilt Roll, then pan, then tilt. +*/ + +/*! + Tilts the center() up or down by \a tiltAngle degrees, + pans the center() left or right by \a panAngle degrees, + and rolls the center() left or right by \a rollAngle degrees, + all in a single fluid movement. The \a order parameter + indicates the order in which to perform the rotations. + + This function is accessible to QML on the Camera item. + It is provided as a convenience for navigation items that + rotate the center in multiple directions at the same time + based on mouse movements. + + \sa tiltPanRollEye() +*/ +void QGLCamera::tiltPanRollCenter + (qreal tiltAngle, qreal panAngle, qreal rollAngle, + QGLCamera::RotateOrder order) +{ + switch (order) { + case QGLCamera::TiltPanRoll: + rotateCenter(roll(rollAngle) * pan(panAngle) * tilt(tiltAngle)); + break; + case QGLCamera::TiltRollPan: + rotateCenter(pan(panAngle) * roll(rollAngle) * tilt(tiltAngle)); + break; + case QGLCamera::PanTiltRoll: + rotateCenter(roll(rollAngle) * tilt(tiltAngle) * pan(panAngle)); + break; + case QGLCamera::PanRollTilt: + rotateCenter(tilt(tiltAngle) * roll(rollAngle) * pan(panAngle)); + break; + case QGLCamera::RollTiltPan: + rotateCenter(pan(panAngle) * tilt(tiltAngle) * roll(rollAngle)); + break; + case QGLCamera::RollPanTilt: + rotateCenter(tilt(tiltAngle) * pan(panAngle) * roll(rollAngle)); + break; + } +} + +/*! + Tilts the eye() up or down by \a tiltAngle degrees, + pans the eye() left or right by \a panAngle degrees, + and rolls the eye() left or right by \a rollAngle degrees, + all in a single fluid movement. The \a order parameter + indicates the order in which to perform the rotations. + + This function is accessible to QML on the Camera item. + It is provided as a convenience for navigation items that + rotate the eye in multiple directions at the same time + based on mouse movements. + + \sa tiltPanRollCenter() +*/ +void QGLCamera::tiltPanRollEye + (qreal tiltAngle, qreal panAngle, qreal rollAngle, + QGLCamera::RotateOrder order) +{ + switch (order) { + case QGLCamera::TiltPanRoll: + rotateEye(roll(rollAngle) * pan(panAngle) * tilt(tiltAngle)); + break; + case QGLCamera::TiltRollPan: + rotateEye(pan(panAngle) * roll(rollAngle) * tilt(tiltAngle)); + break; + case QGLCamera::PanTiltRoll: + rotateEye(roll(rollAngle) * tilt(tiltAngle) * pan(panAngle)); + break; + case QGLCamera::PanRollTilt: + rotateEye(tilt(tiltAngle) * roll(rollAngle) * pan(panAngle)); + break; + case QGLCamera::RollTiltPan: + rotateEye(pan(panAngle) * tilt(tiltAngle) * roll(rollAngle)); + break; + case QGLCamera::RollPanTilt: + rotateEye(tilt(tiltAngle) * pan(panAngle) * roll(rollAngle)); + break; + } +} + +QT_END_NAMESPACE diff --git a/src/threed/viewing/qglcamera.h b/src/threed/viewing/qglcamera.h new file mode 100644 index 000000000..360712030 --- /dev/null +++ b/src/threed/viewing/qglcamera.h @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLCAMERA_H +#define QGLCAMERA_H + +#include "qt3dglobal.h" +#include "qglnamespace.h" +#include <QtCore/qobject.h> +#include <QtCore/qsize.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qmatrix4x4.h> +#include <QtGui/qquaternion.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLCameraPrivate; +class QGLPainter; + +class Q_QT3D_EXPORT QGLCamera : public QObject +{ + Q_OBJECT + Q_ENUMS(ProjectionType RotateOrder) + Q_PROPERTY(ProjectionType projectionType READ projectionType WRITE setProjectionType NOTIFY projectionChanged) + Q_PROPERTY(qreal fieldOfView READ fieldOfView WRITE setFieldOfView NOTIFY projectionChanged) + Q_PROPERTY(qreal nearPlane READ nearPlane WRITE setNearPlane NOTIFY projectionChanged) + Q_PROPERTY(qreal farPlane READ farPlane WRITE setFarPlane NOTIFY projectionChanged) + Q_PROPERTY(QSizeF viewSize READ viewSize WRITE setViewSize NOTIFY projectionChanged SCRIPTABLE false) + Q_PROPERTY(QSizeF minViewSize READ minViewSize WRITE setMinViewSize NOTIFY projectionChanged SCRIPTABLE false) + Q_PROPERTY(int screenRotation READ screenRotation WRITE setScreenRotation NOTIFY projectionChanged SCRIPTABLE false) + Q_PROPERTY(QVector3D eye READ eye WRITE setEye NOTIFY viewChanged) + Q_PROPERTY(QVector3D upVector READ upVector WRITE setUpVector NOTIFY viewChanged) + Q_PROPERTY(QVector3D center READ center WRITE setCenter NOTIFY viewChanged) + Q_PROPERTY(qreal eyeSeparation READ eyeSeparation WRITE setEyeSeparation NOTIFY viewChanged) + Q_PROPERTY(QVector3D motionAdjustment READ motionAdjustment WRITE setMotionAdjustment DESIGNABLE false NOTIFY viewChanged SCRIPTABLE false) + Q_PROPERTY(bool adjustForAspectRatio READ adjustForAspectRatio WRITE setAdjustForAspectRatio NOTIFY viewChanged) +public: + explicit QGLCamera(QObject *parent = 0); + ~QGLCamera(); + + enum ProjectionType + { + Perspective, + Orthographic + }; + + QGLCamera::ProjectionType projectionType() const; + void setProjectionType(QGLCamera::ProjectionType value); + + qreal fieldOfView() const; + void setFieldOfView(qreal angle); + + qreal nearPlane() const; + void setNearPlane(qreal value); + + qreal farPlane() const; + void setFarPlane(qreal value); + + QSizeF viewSize() const; + void setViewSize(const QSizeF& size); + + QSizeF minViewSize() const; + void setMinViewSize(const QSizeF& size); + + int screenRotation() const; + void setScreenRotation(int angle); + + QVector3D eye() const; + void setEye(const QVector3D& vertex); + + QVector3D upVector() const; + void setUpVector(const QVector3D& vector); + + QVector3D center() const; + void setCenter(const QVector3D& vertex); + + qreal eyeSeparation() const; + void setEyeSeparation(qreal value); + + QVector3D motionAdjustment() const; + void setMotionAdjustment(const QVector3D& vector); + + bool adjustForAspectRatio() const; + void setAdjustForAspectRatio(bool value); + + QQuaternion tilt(qreal angle) const; + QQuaternion pan(qreal angle) const; + QQuaternion roll(qreal angle) const; + + void rotateEye(const QQuaternion& q); + void rotateCenter(const QQuaternion& q); + + QVector3D translation(qreal x, qreal y, qreal z) const; + + QMatrix4x4 projectionMatrix(qreal aspectRatio) const; + QMatrix4x4 modelViewMatrix(QGL::Eye eye = QGL::NoEye) const; + + QVector3D mapPoint + (const QPoint& point, qreal aspectRatio, + const QSize& viewportSize) const; + + enum RotateOrder + { + TiltPanRoll, + TiltRollPan, + PanTiltRoll, + PanRollTilt, + RollTiltPan, + RollPanTilt + }; + +public Q_SLOTS: + void translateEye(qreal x, qreal y, qreal z); + void translateCenter(qreal x, qreal y, qreal z); + + void tiltPanRollCenter(qreal tiltAngle, qreal panAngle, qreal rollAngle, + QGLCamera::RotateOrder order = TiltPanRoll); + void tiltPanRollEye(qreal tiltAngle, qreal panAngle, qreal rollAngle, + QGLCamera::RotateOrder order = TiltPanRoll); + +Q_SIGNALS: + void projectionChanged(); + void viewChanged(); + +private: + QGLCameraPrivate *d_ptr; + + QGLCameraPrivate *d_func() const { return d_ptr; } + + Q_DISABLE_COPY(QGLCamera) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/viewing/qglcameraanimation.cpp b/src/threed/viewing/qglcameraanimation.cpp new file mode 100644 index 000000000..50fa6a76b --- /dev/null +++ b/src/threed/viewing/qglcameraanimation.cpp @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglcameraanimation.h" +#include "qglcamera.h" +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLCameraAnimation + \brief The QGLCameraAnimation class implements smooth animations between two camera positions. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::viewing + + QGLCameraAnimation modifies the camera() object to fall on a smooth + path between startEye(), startUpVector(), startCenter() and + endEye(), endUpVector(), endCenter. The animation will attempt to + keep the eye and center at the same relative distance from each other + throughout the animation, as though one object is flying around another. + + \sa QGLCamera +*/ + +class QGLCameraAnimationPrivate +{ +public: + enum PointOfRotation + { + Center, + CenterDual, + Eye, + EyeDual, + Both + }; + + QGLCameraAnimationPrivate() + : camera(0) + , startEye(0.0f, 0.0f, 10.f) + , startUpVector(0.0f, 1.0f, 0.0f) + , startCenter(0.0f, 0.0f, 0.0f) + , endEye(0.0f, 0.0f, 10.f) + , endUpVector(0.0f, 1.0f, 0.0f) + , endCenter(0.0f, 0.0f, 0.0f) + , duration(250) + , dirty(true) + , pointOfRotation(Both) + , lengthStart(1.0f) + , lengthEnd(1.0f) + { + } + + QGLCamera *camera; + QVector3D startEye; + QVector3D startUpVector; + QVector3D startCenter; + QVector3D endEye; + QVector3D endUpVector; + QVector3D endCenter; + int duration; + bool dirty; + QEasingCurve easingCurve; + + // Derived values for use during the animation. + PointOfRotation pointOfRotation; + QVector3D upVectorAxis; + qreal upVectorAngle; + QVector3D pointAxis; + qreal pointAngle; + QVector3D centerTranslate; + QVector3D eyeTranslate; + qreal lengthStart; + qreal lengthEnd; + + static void rotateBetween(const QVector3D &start, const QVector3D &end, + const QVector3D &defaultAxis, + QVector3D *rotationAxis, qreal *rotationAngle); + void deriveRotations(); +}; + +static inline bool fuzzyCompareVectors(const QVector3D &v1, const QVector3D &v2) +{ + return qAbs(v1.x() - v2.x()) <= 0.00001 && + qAbs(v1.y() - v2.y()) <= 0.00001 && + qAbs(v1.z() - v2.z()) <= 0.00001; +} + +// Determine how to rotate between the "start" and "end" vectors. +// We use the cross-product of the two vectors as the axis of rotation +// or "defaultAxis" if the cross-product is zero (180 degree rotation). +void QGLCameraAnimationPrivate::rotateBetween + (const QVector3D &start, const QVector3D &end, const QVector3D &defaultAxis, + QVector3D *rotationAxis, qreal *rotationAngle) +{ + QVector3D nstart = start.normalized(); + QVector3D nend = end.normalized(); + if (qFuzzyCompare(nstart, nend)) { + // Vectors are the same, so no rotation to perform. + *rotationAxis = QVector3D(0, 1, 0); + *rotationAngle = 0.0f; + } else { + QVector3D axis = QVector3D::crossProduct(nstart, nend); + if (qFuzzyIsNull(axis.x()) && qFuzzyIsNull(axis.y()) && + qFuzzyIsNull(axis.z())) { + // Vectors are probably at 180 degrees to each other. + // We can pick either direction; arbitrarily pick anti-clockwise. + *rotationAxis = defaultAxis; + *rotationAngle = 180.0f; + } else { + // Compute the smallest angle between the two vectors + // from the cosine of the angle (i.e. the dot product). + *rotationAxis = axis; + *rotationAngle = + qAcos(QVector3D::dotProduct(nstart, nend)) * 180.0f / M_PI; + } + } +} + +void QGLCameraAnimationPrivate::deriveRotations() +{ + // If the center points are the same, rotate the eye around the center. + // If the eye points are the same, rotate the center around the eye. + // If both are different, then interpolate along linear vectors. + if (qFuzzyCompare(startCenter, endCenter)) { + if (qFuzzyCompare(startEye, endEye)) { + // Center and eye both stay the same: nothing to do. + pointOfRotation = Both; + centerTranslate = QVector3D(); + eyeTranslate = QVector3D(); + } else { + // Centers are the same, rotate the eye position. + pointOfRotation = Center; + rotateBetween(startEye - startCenter, endEye - startCenter, + startUpVector, &pointAxis, &pointAngle); + lengthStart = (startEye - startCenter).length(); + lengthEnd = (endEye - startCenter).length(); + + // Rotate the start up vector to the final position. If it is + // different than the end up vector, we need to perform the + // animation in two steps: rotate eye, then rotate up. + QQuaternion q = + QQuaternion::fromAxisAndAngle(pointAxis, pointAngle); + QVector3D up = q.rotatedVector(startUpVector); + if (!fuzzyCompareVectors(startUpVector, endUpVector) && + !fuzzyCompareVectors(up, endUpVector)) { + pointOfRotation = CenterDual; + rotateBetween(up, endUpVector, endEye - endCenter, + &upVectorAxis, &upVectorAngle); + } + } + } else if (qFuzzyCompare(startEye, endEye)) { + // Eyes are the same, rotate the center position. + pointOfRotation = Eye; + rotateBetween(startCenter - startEye, endCenter - startEye, + startUpVector, &pointAxis, &pointAngle); + lengthStart = (startCenter - startEye).length(); + lengthEnd = (startCenter - endEye).length(); + + // Rotate the start up vector to the final position. If it is + // different than the end up vector, we need to perform the + // animation in two steps: rotate eye, then rotate up. + QQuaternion q = + QQuaternion::fromAxisAndAngle(pointAxis, pointAngle); + QVector3D up = q.rotatedVector(startUpVector); + if (!fuzzyCompareVectors(startUpVector, endUpVector) && + !fuzzyCompareVectors(up, endUpVector)) { + pointOfRotation = EyeDual; + rotateBetween(up, endUpVector, endCenter - endEye, + &upVectorAxis, &upVectorAngle); + } + } else { + // Both center and eye are changing, so perform a linear translation. + pointOfRotation = Both; + centerTranslate = endCenter - startCenter; + eyeTranslate = endEye - startEye; + } + + // If we are doing a single movement, then determine how to + // rotate the up vector during the movement. + if (pointOfRotation != CenterDual) { + rotateBetween(startUpVector, endUpVector, startCenter - startEye, + &upVectorAxis, &upVectorAngle); + } +} + +/*! + Constructs a new camera animation object and attaches it to \a parent. +*/ +QGLCameraAnimation::QGLCameraAnimation(QObject *parent) + : QAbstractAnimation(parent) + , d_ptr(new QGLCameraAnimationPrivate) +{ +} + +/*! + Destroys this camera animation object. +*/ +QGLCameraAnimation::~QGLCameraAnimation() +{ +} + +/*! + \property QGLCameraAnimation::camera + \brief the camera that will be animated by this animation object; + null if the camera has not yet been set. +*/ + +QGLCamera *QGLCameraAnimation::camera() const +{ + Q_D(const QGLCameraAnimation); + return d->camera; +} + +void QGLCameraAnimation::setCamera(QGLCamera *camera) +{ + Q_D(QGLCameraAnimation); + d->camera = camera; +} + +/*! + \property QGLCameraAnimation::startEye + \brief the position of the viewer's eye at the start of the animation. + The default value is (0, 0, 10). + + \sa startUpVector(), startCenter(), endEye() +*/ + +QVector3D QGLCameraAnimation::startEye() const +{ + Q_D(const QGLCameraAnimation); + return d->startEye; +} + +void QGLCameraAnimation::setStartEye(const QVector3D &eye) +{ + Q_D(QGLCameraAnimation); + d->startEye = eye; + d->dirty = true; +} + +/*! + \property QGLCameraAnimation::startUpVector + \brief the up vector for the viewer at the start of the animation. + The default value is (0, 1, 0). + + \sa startEye(), startCenter(), endUpVector() +*/ + +QVector3D QGLCameraAnimation::startUpVector() const +{ + Q_D(const QGLCameraAnimation); + return d->startUpVector; +} + +void QGLCameraAnimation::setStartUpVector(const QVector3D &upVector) +{ + Q_D(QGLCameraAnimation); + d->startUpVector = upVector; + d->dirty = true; +} + +/*! + \property QGLCameraAnimation::startCenter + \brief the center of the view visible from the viewer's position + at the start of the animation. The default value is (0, 0, 0). + + \sa startEye(), startUpVector(), endCenter() +*/ + +QVector3D QGLCameraAnimation::startCenter() const +{ + Q_D(const QGLCameraAnimation); + return d->startCenter; +} + +void QGLCameraAnimation::setStartCenter(const QVector3D ¢er) +{ + Q_D(QGLCameraAnimation); + d->startCenter = center; + d->dirty = true; +} + +/*! + \property QGLCameraAnimation::endEye + \brief the position of the viewer's eye at the end of the animation. + The default value is (0, 0, 10). + + \sa endUpVector(), endCenter(), startEye() +*/ + +QVector3D QGLCameraAnimation::endEye() const +{ + Q_D(const QGLCameraAnimation); + return d->endEye; +} + +void QGLCameraAnimation::setEndEye(const QVector3D &eye) +{ + Q_D(QGLCameraAnimation); + d->endEye = eye; + d->dirty = true; +} + +/*! + \property QGLCameraAnimation::endUpVector + \brief the up vector for the viewer at the end of the animation. + The default value is (0, 1, 0). + + \sa endEye(), endCenter(), startUpVector() +*/ + +QVector3D QGLCameraAnimation::endUpVector() const +{ + Q_D(const QGLCameraAnimation); + return d->endUpVector; +} + +void QGLCameraAnimation::setEndUpVector(const QVector3D &upVector) +{ + Q_D(QGLCameraAnimation); + d->endUpVector = upVector; + d->dirty = true; +} + +/*! + \property QGLCameraAnimation::endCenter + \brief the center of the view visible from the viewer's position + at the end of the animation. The default value is (0, 0, 0). + + \sa endEye(), endUpVector(), startCenter() +*/ + +QVector3D QGLCameraAnimation::endCenter() const +{ + Q_D(const QGLCameraAnimation); + return d->endCenter; +} + +void QGLCameraAnimation::setEndCenter(const QVector3D ¢er) +{ + Q_D(QGLCameraAnimation); + d->endCenter = center; + d->dirty = true; +} + +/*! + \property QGLCameraAnimation::duration + \brief the duration of the animation in milliseconds. The default + value is 250 milliseconds. + + \sa easingCurve() +*/ + +int QGLCameraAnimation::duration() const +{ + Q_D(const QGLCameraAnimation); + return d->duration; +} + +void QGLCameraAnimation::setDuration(int duration) +{ + Q_D(QGLCameraAnimation); + d->duration = duration; +} + +/*! + \property QGLCameraAnimation::easingCurve + \brief the easing curve to use for the animation. The default + value is a linear easing curve. + + \sa duration() +*/ + +QEasingCurve QGLCameraAnimation::easingCurve() const +{ + Q_D(const QGLCameraAnimation); + return d->easingCurve; +} + +void QGLCameraAnimation::setEasingCurve(const QEasingCurve &easing) +{ + Q_D(QGLCameraAnimation); + d->easingCurve = easing; +} + +/*! + \internal +*/ +void QGLCameraAnimation::updateCurrentTime(int currentTime) +{ + Q_D(QGLCameraAnimation); + if (!d->camera) + return; // Nothing to do if no camera to modify. + if (currentTime >= d->duration) { + d->camera->setEye(d->endEye); + d->camera->setUpVector(d->endUpVector); + d->camera->setCenter(d->endCenter); + } else if (currentTime <= 0) { + d->camera->setEye(d->startEye); + d->camera->setUpVector(d->startUpVector); + d->camera->setCenter(d->startCenter); + } else { + // Derive the rotation parameters we need if arguments have changed. + if (d->dirty) { + d->dirty = false; + d->deriveRotations(); + } + + // Calculate the progress and modify it with the easing curve. + qreal progress = d->easingCurve.valueForProgress + (qreal(currentTime) / qreal(d->duration)); + + // Calculate the new eye and center locations. + QVector3D eye = d->startEye; + QVector3D center = d->startCenter; + QVector3D upVector = d->startUpVector; + if (d->pointOfRotation == QGLCameraAnimationPrivate::Center) { + QQuaternion q = QQuaternion::fromAxisAndAngle + (d->pointAxis, d->pointAngle * progress); + eye = q.rotatedVector(eye - d->startCenter); + if (d->lengthStart != d->lengthEnd) { + qreal length = (1.0f - progress) * d->lengthStart + + progress * d->lengthEnd; + eye = eye.normalized() * length; + } + eye += d->startCenter; + } else if (d->pointOfRotation == QGLCameraAnimationPrivate::CenterDual) { + if (progress < 0.5f) { + // First half of the animation - rotate the eye position. + QQuaternion q = QQuaternion::fromAxisAndAngle + (d->pointAxis, d->pointAngle * progress * 2.0f); + eye = q.rotatedVector(eye - d->startCenter); + if (d->lengthStart != d->lengthEnd) { + qreal length = (1.0f - progress) * d->lengthStart + + progress * d->lengthEnd; + eye = eye.normalized() * length; + } + eye += d->startCenter; + upVector = q.rotatedVector(upVector); + } else { + // Second half of the animation - rotate the up vector. + QQuaternion q = QQuaternion::fromAxisAndAngle + (d->upVectorAxis, + d->upVectorAngle * (progress - 0.5f) * 2.0f) * + QQuaternion::fromAxisAndAngle + (d->pointAxis, d->pointAngle); + eye = d->endEye; + upVector = q.rotatedVector(upVector); + } + } else if (d->pointOfRotation == QGLCameraAnimationPrivate::Eye) { + QQuaternion q = QQuaternion::fromAxisAndAngle + (d->pointAxis, d->pointAngle * progress); + center = q.rotatedVector(center - d->startEye); + if (d->lengthStart != d->lengthEnd) { + qreal length = (1.0f - progress) * d->lengthStart + + progress * d->lengthEnd; + center = center.normalized() * length; + } + center += d->startEye; + } else if (d->pointOfRotation == QGLCameraAnimationPrivate::EyeDual) { + if (progress < 0.5f) { + // First half of the animation - rotate the center position. + QQuaternion q = QQuaternion::fromAxisAndAngle + (d->pointAxis, d->pointAngle * progress * 2.0f); + center = q.rotatedVector(center - d->startEye); + if (d->lengthStart != d->lengthEnd) { + qreal length = (1.0f - progress) * d->lengthStart + + progress * d->lengthEnd; + center = center.normalized() * length; + } + center += d->startEye; + upVector = q.rotatedVector(upVector); + } else { + // Second half of the animation - rotate the up vector. + QQuaternion q = QQuaternion::fromAxisAndAngle + (d->upVectorAxis, + d->upVectorAngle * (progress - 0.5f) * 2.0f) * + QQuaternion::fromAxisAndAngle + (d->pointAxis, d->pointAngle); + center = d->endCenter; + upVector = q.rotatedVector(upVector); + } + } else { + eye += d->eyeTranslate * progress; + center += d->centerTranslate * progress; + } + + // Calculate the new up vector for single-step animations. + if (d->pointOfRotation != QGLCameraAnimationPrivate::CenterDual && + d->pointOfRotation != QGLCameraAnimationPrivate::EyeDual) { + if (d->upVectorAngle != 0.0f) { + QQuaternion q = QQuaternion::fromAxisAndAngle + (d->upVectorAxis, d->upVectorAngle * progress); + upVector = q.rotatedVector(upVector); + } + } + + d->camera->setEye(eye); + d->camera->setUpVector(upVector); + d->camera->setCenter(center); + } +} + +QT_END_NAMESPACE diff --git a/src/threed/viewing/qglcameraanimation.h b/src/threed/viewing/qglcameraanimation.h new file mode 100644 index 000000000..23172c66d --- /dev/null +++ b/src/threed/viewing/qglcameraanimation.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLCAMERAANIMATION_H +#define QGLCAMERAANIMATION_H + +#include <QtCore/qabstractanimation.h> +#include <QtCore/qeasingcurve.h> +#include <QtGui/qvector3d.h> +#include "qt3dglobal.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLCamera; +class QGLCameraAnimationPrivate; + +class Q_QT3D_EXPORT QGLCameraAnimation : public QAbstractAnimation +{ + Q_OBJECT + Q_PROPERTY(QGLCamera *camera READ camera WRITE setCamera) + Q_PROPERTY(QVector3D startEye READ startEye WRITE setStartEye) + Q_PROPERTY(QVector3D startUpVector READ startUpVector WRITE setStartUpVector) + Q_PROPERTY(QVector3D startCenter READ startCenter WRITE setStartCenter) + Q_PROPERTY(QVector3D endEye READ endEye WRITE setEndEye) + Q_PROPERTY(QVector3D endUpVector READ endUpVector WRITE setEndUpVector) + Q_PROPERTY(QVector3D endCenter READ endCenter WRITE setEndCenter) + Q_PROPERTY(int duration READ duration WRITE setDuration) + Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve) +public: + explicit QGLCameraAnimation(QObject *parent = 0); + ~QGLCameraAnimation(); + + QGLCamera *camera() const; + void setCamera(QGLCamera *camera); + + QVector3D startEye() const; + void setStartEye(const QVector3D &eye); + + QVector3D startUpVector() const; + void setStartUpVector(const QVector3D &upVector); + + QVector3D startCenter() const; + void setStartCenter(const QVector3D ¢er); + + QVector3D endEye() const; + void setEndEye(const QVector3D &eye); + + QVector3D endUpVector() const; + void setEndUpVector(const QVector3D &upVector); + + QVector3D endCenter() const; + void setEndCenter(const QVector3D ¢er); + + int duration() const; + void setDuration(int duration); + + QEasingCurve easingCurve() const; + void setEasingCurve(const QEasingCurve &easing); + +protected: + void updateCurrentTime(int currentTime); + +private: + QScopedPointer<QGLCameraAnimationPrivate> d_ptr; + + Q_DISABLE_COPY(QGLCameraAnimation) + Q_DECLARE_PRIVATE(QGLCameraAnimation) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/viewing/qglview.cpp b/src/threed/viewing/qglview.cpp new file mode 100644 index 000000000..044aca960 --- /dev/null +++ b/src/threed/viewing/qglview.cpp @@ -0,0 +1,1475 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglview.h" +#include "qglframebufferobject.h" +#include "qglsubsurface.h" +#include "qglmaskedsurface_p.h" +#include "qglwidgetsurface.h" +#include "qgldrawbuffersurface_p.h" +#include <QtGui/qevent.h> +#include <QtCore/qmap.h> +#include <QtGui/qapplication.h> +#include <QtCore/qtimer.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGLView + \brief The QGLView class extends QGLWidget with support for 3D viewing. + \since 4.8 + \ingroup qt3d + \ingroup qt3d::viewing + + \section1 Navigating + + Navigation in 3D space is possible under keyboard and mouse control. + + Holding down the left mouse button and dragging it will rotate the + camera() position around the object being viewed. Holding down the + left mouse button and the Shift key pans the view in a plane without + rotating the viewed object. + + Using the mouse wheel, the view can be zoomed in or out. If the + system does not have a mouse wheel, then holding down the left mouse + button and the Control key and moving the mouse up and down will + also zoom in and out. + + On the keyboard, the left, right, up, and down keys can also be used + to shift the camera() position around the object being viewed. Shift + and Control modify keys the same way they modify the left mouse + button above. Hitting the Home key will cause the camera position + to be reset to its original position. + + \section1 Stereo viewing support + + If the hardware supports stereo buffers, then each time the scene needs + to be painted, QGLView renders it twice: first from the perspective of + the left eye, and then from the perspective of the right eye. + The separation between the eye positions is specified by + QGLCamera::eyeSeparation(). If the eye separation is zero, + then stereo viewing is disabled and only a single image will + be rendered per frame. + + Three kinds of stereo viewing are possible: hardware stereo, + anaglyph stereo, and double image stereo. + + Hardware stereo relies upon specialized hardware that can render + the left and right eye images into separate buffers and then show + them independently to each eye through the use of polarized glasses + or similar technology. Hardware stereo is used if the \c{-stereo-hw} + command-line option is supplied or if the user explicitly requests + stereo buffers when the QGLView is constructed: + + \code + QGLFormat format(QGLFormat::defaultFormat()); + format.setOption(QGL::StereoBuffers); + QGLView view(format); + \endcode + + Anaglyph stereo is used when the hardware doesn't have specialized + stereo buffer support. The left eye image is masked by a red + filter and the right eye image is masked by a cyan filter. This makes + the resulting images suitable for viewing with standard red-cyan + anaglyph glasses. + + When using red-cyan anaglyphs, it is recommended that the + scene use non-primary object colors. Pure primary colors such + as red, green, and blue will only appear to one of the viewer's + eyes and will inhibit the 3D effect. Non-primary colors or + grayscale should be used to get the best effects. + + Red-cyan anaglyphs can be disorienting to some viewers. If hardware + stereo is not available and stereo viewing is not critical to + the application, then stereo can be disabled by setting + QGLCamera::eyeSeparation() to zero. + + Double image stereo involves drawing the left and right eye + images in a double-wide or double-high window, with the hardware + combining the images. Four different configurations are available: + LeftRight, RightLeft, TopBottom, + and BottomTop, according to the layout of the eye images. + Double image stereo is selected by calling setStereoType(). It is + the responsibility of the application to resize the window to + twice its normal size to accommodate the images. + + Ctrl-Left and Ctrl-Right can be used to make the eye separation + smaller or larger under keyboard control. + + A number of command-line options are available to select the + stereo mode of the QGLView so that the application does not + need to select the mode itself: + + \table + \row \o \c{-stereo-hw} \o \l Hardware. + \row \o \c{-stereo-lr} \o LeftRight. + \row \o \c{-stereo-rl} \o RightLeft. + \row \o \c{-stereo-tb} \o TopBottom. + \row \o \c{-stereo-bt} \o BottomTop. + \row \o \c{-stereo-stretched-lr} \o StretchedLeftRight. + \row \o \c{-stereo-stretched-rl} \o StretchedRightLeft. + \row \o \c{-stereo-stretched-tb} \o StretchedTopBottom. + \row \o \c{-stereo-stretched-bt} \o StretchedBottomTop. + \endtable + + The option can also be supplied in the \c{Quick3D_OPTIONS} environment + variable: + + \code + $ Quick3D_OPTIONS="-stereo-lr" ./cubehouse + \endcode + + If the application sets the stereo type with setStereoType(), + that will be used. Next is the command-line setting, and finally + the contents of the environment variable. + + \sa {Stereo Viewing Example} +*/ + +/*! + \enum QGLView::Option + This enum defines an option for QGLView. + + \value ObjectPicking Object picking is enabled. Disabled by default. + \value ShowPicking Objects are rendered with their pick colors instead + of their normal colors and materials. This can help debug + problems with object picking. Disabled by default. + \value CameraNavigation Camera navigation using the keyboard and mouse + is enabled. Enabled by default. + \omitvalue PaintingLog +*/ + +/*! + \enum QGLView::StereoType + This enum defines the type of stereo viewing technology being used by QGLView. + + \value Hardware Specialized stereo hardware is being used. + \value RedCyanAnaglyph Stereo is being simulated for viewing by + red-cyan anaglyph classes. + \value LeftRight The view is double-wide with the left eye + image on the left of the window. + \value RightLeft The view is double-wide with the left eye + image on the right of the window. + \value TopBottom The view is double-high with the left eye + image on the top of the window. + \value BottomTop The view is double-high with the left eye + image on the bottom of the window. + \value StretchedLeftRight Same as LeftRight, but with the + left and right eye images stretched to double their width. + \value StretchedRightLeft Same as RightLeft, but with the + left and right eye images stretched to double their width. + \value StretchedTopBottom Same as TopBottom, but with the + left and right eye images stretched to double their height. + \value StretchedBottomTop Same as BottomTop, but with the + left and right eye images stretched to double their height. +*/ + +class QGLViewPrivate +{ +public: + QGLViewPrivate(QGLView *parent) + : view(parent), mainSurface(parent) + { + options = QGLView::CameraNavigation; + fbo = 0; + leftSurface = 0; + rightSurface = 0; + + if (parent->format().stereo()) + stereoType = QGLView::Hardware; + else + stereoType = QGLView::RedCyanAnaglyph; + + pickBufferForceUpdate = true; + pickBufferMaybeInvalid = true; + updateQueued = false; + + pressedObject = 0; + pressedButton = Qt::NoButton; + enteredObject = 0; + + defaultCamera = new QGLCamera(parent); + camera = defaultCamera; + + panning = false; + startPan = QPoint(-1, -1); + lastPan = QPoint(-1, -1); + panModifiers = Qt::NoModifier; + + QObject::connect(defaultCamera, SIGNAL(projectionChanged()), + parent, SLOT(cameraChanged())); + QObject::connect(defaultCamera, SIGNAL(viewChanged()), + parent, SLOT(cameraChanged())); + + logTime.start(); + lastFrameTime.start(); + QByteArray env = qgetenv("Quick3D_LOG_EVENTS"); + if (env == "1") + options |= QGLView::PaintingLog; + } + ~QGLViewPrivate() + { + delete fbo; + delete leftSurface; + delete rightSurface; + } + + QGLView *view; + QGLView::Options options; + QGLView::StereoType stereoType; + QGLFramebufferObject *fbo; + QGLWidgetSurface mainSurface; + QGLAbstractSurface *leftSurface; + QGLAbstractSurface *rightSurface; + bool pickBufferForceUpdate; + bool pickBufferMaybeInvalid; + bool updateQueued; + QMap<int, QObject *> objects; + QObject *pressedObject; + Qt::MouseButton pressedButton; + QObject *enteredObject; + QGLCamera *defaultCamera; + QGLCamera *camera; + bool panning; + QPoint startPan; + QPoint lastPan; + QVector3D startEye; + QVector3D startCenter; + QVector3D startUpVector; + Qt::KeyboardModifiers panModifiers; + QTime logTime; + QTime enterTime; + QTime lastFrameTime; + + inline void logEnter(const char *message); + inline void logLeave(const char *message); + + void processStereoOptions(QGLView *view); + void processStereoOptions(QGLView *view, const QString &arg); + + QGLAbstractSurface *leftEyeSurface(const QSize &size); + QGLAbstractSurface *rightEyeSurface(const QSize &size); + QGLAbstractSurface *bothEyesSurface(); +}; + +inline void QGLViewPrivate::logEnter(const char *message) +{ + if ((options & QGLView::PaintingLog) == 0) + return; + int ms = logTime.elapsed(); + enterTime.start(); + int sinceLast = lastFrameTime.restart(); + qDebug("LOG[%d:%02d:%02d.%03d]: ENTER: %s (%d ms since last enter)", + ms / 3600000, (ms / 60000) % 60, + (ms / 1000) % 60, ms % 1000, message, sinceLast); +} + +inline void QGLViewPrivate::logLeave(const char *message) +{ + if ((options & QGLView::PaintingLog) == 0) + return; + int ms = logTime.elapsed(); + int duration = enterTime.elapsed(); + qDebug("LOG[%d:%02d:%02d.%03d]: LEAVE: %s (%d ms elapsed)", + ms / 3600000, (ms / 60000) % 60, + (ms / 1000) % 60, ms % 1000, message, duration); +} + +static QString qt_gl_stereo_arg() +{ + QStringList args = QApplication::arguments(); + foreach (QString arg, args) { + if (arg.startsWith(QLatin1String("-stereo-"))) + return arg; + } + QByteArray options(qgetenv("Quick3D_OPTIONS")); + args = QString::fromLocal8Bit + (options.constData(), options.size()).split(QLatin1Char(' ')); + foreach (QString arg, args) { + if (arg.startsWith(QLatin1String("-stereo-"))) + return arg; + } + return QString(); +} + +void QGLViewPrivate::processStereoOptions(QGLView *view) +{ + if (stereoType == QGLView::Hardware) + return; + QString arg = qt_gl_stereo_arg(); + if (!arg.isEmpty()) + processStereoOptions(view, arg); +} + +void QGLViewPrivate::processStereoOptions(QGLView *view, const QString &arg) +{ + // If the command-line contains an option that starts with "-stereo-", + // then convert it into options that define the size and type of + // stereo window to use for a top-level QGLView. Possible options: + // + // hw - use hardware stereo + // lr, rl, tb, bt - specify the eye order (default is left-right) + // stretched - used stretched versions of double wide/high modes. + // + QStringList opts = arg.mid(8).split(QLatin1Char('-')); + QGLView::StereoType stereoType; + bool stretched = opts.contains(QLatin1String("stretched")); + if (opts.contains(QLatin1String("rl"))) { + stereoType = stretched ? QGLView::StretchedRightLeft : QGLView::RightLeft; + } else if (opts.contains(QLatin1String("tb"))) { + stereoType = stretched ? QGLView::StretchedTopBottom : QGLView::TopBottom; + } else if (opts.contains(QLatin1String("bt"))) { + stereoType = stretched ? QGLView::StretchedBottomTop : QGLView::BottomTop; + } else { + stereoType = stretched ? QGLView::StretchedLeftRight : QGLView::LeftRight; + } + view->setStereoType(stereoType); +} + +class QGLViewSubsurface : public QGLSubsurface +{ +public: + QGLViewSubsurface(QGLAbstractSurface *surface, const QRect ®ion, + qreal adjust) + : QGLSubsurface(surface, region), m_adjust(adjust) {} + + qreal aspectRatio() const; + +private: + qreal m_adjust; +}; + +qreal QGLViewSubsurface::aspectRatio() const +{ + return QGLSubsurface::aspectRatio() * m_adjust; +} + +// Returns the surface to use to render the left eye image. +QGLAbstractSurface *QGLViewPrivate::leftEyeSurface(const QSize &size) +{ + QRect viewport; + qreal adjust = 1.0f; + switch (stereoType) { + case QGLView::Hardware: +#if defined(GL_BACK_LEFT) && defined(GL_BACK_RIGHT) + if (!leftSurface) { + leftSurface = new QGLDrawBufferSurface + (&mainSurface, + view->doubleBuffer() ? GL_BACK_LEFT : GL_FRONT_LEFT); + } + return leftSurface; +#endif + case QGLView::RedCyanAnaglyph: + if (!leftSurface) { + leftSurface = new QGLMaskedSurface + (&mainSurface, + QGLMaskedSurface::RedMask | QGLMaskedSurface::AlphaMask); + } + return leftSurface; + case QGLView::LeftRight: + viewport = QRect(0, 0, size.width() / 2, size.height()); + break; + case QGLView::RightLeft: + viewport = QRect(size.width() / 2, 0, size.width() / 2, size.height()); + break; + case QGLView::TopBottom: + viewport = QRect(0, 0, size.width(), size.height() / 2); + break; + case QGLView::BottomTop: + viewport = QRect(0, size.height() / 2, size.width(), size.height() / 2); + break; + case QGLView::StretchedLeftRight: + viewport = QRect(0, 0, size.width() / 2, size.height()); + adjust = 2.0f; + break; + case QGLView::StretchedRightLeft: + viewport = QRect(size.width() / 2, 0, size.width() / 2, size.height()); + adjust = 2.0f; + break; + case QGLView::StretchedTopBottom: + viewport = QRect(0, 0, size.width(), size.height() / 2); + adjust = 0.5f; + break; + case QGLView::StretchedBottomTop: + viewport = QRect(0, size.height() / 2, size.width(), size.height() / 2); + adjust = 0.5f; + break; + } + if (!leftSurface) { + if (adjust == 1.0f) + leftSurface = new QGLSubsurface(&mainSurface, viewport); + else + leftSurface = new QGLViewSubsurface(&mainSurface, viewport, adjust); + } else { + static_cast<QGLSubsurface *>(leftSurface)->setRegion(viewport); + } + return leftSurface; +} + +// Returns the surface to use to render the right eye image. +QGLAbstractSurface *QGLViewPrivate::rightEyeSurface(const QSize &size) +{ + QRect viewport; + qreal adjust = 1.0f; + switch (stereoType) { + case QGLView::Hardware: +#if defined(GL_BACK_LEFT) && defined(GL_BACK_RIGHT) + if (!rightSurface) { + rightSurface = new QGLDrawBufferSurface + (&mainSurface, + view->doubleBuffer() ? GL_BACK_RIGHT : GL_FRONT_RIGHT); + } + return rightSurface; +#endif + case QGLView::RedCyanAnaglyph: + if (!rightSurface) { + rightSurface = new QGLMaskedSurface + (&mainSurface, + QGLMaskedSurface::GreenMask | QGLMaskedSurface::BlueMask); + } + return rightSurface; + case QGLView::LeftRight: + viewport = QRect(size.width() / 2, 0, size.width() / 2, size.height()); + break; + case QGLView::RightLeft: + viewport = QRect(0, 0, size.width() / 2, size.height()); + break; + case QGLView::TopBottom: + viewport = QRect(0, size.height() / 2, size.width(), size.height() / 2); + break; + case QGLView::BottomTop: + viewport = QRect(0, 0, size.width(), size.height() / 2); + break; + case QGLView::StretchedLeftRight: + viewport = QRect(size.width() / 2, 0, size.width() / 2, size.height()); + adjust = 2.0f; + break; + case QGLView::StretchedRightLeft: + viewport = QRect(0, 0, size.width() / 2, size.height()); + adjust = 2.0f; + break; + case QGLView::StretchedTopBottom: + viewport = QRect(0, size.height() / 2, size.width(), size.height() / 2); + adjust = 0.5f; + break; + case QGLView::StretchedBottomTop: + viewport = QRect(0, 0, size.width(), size.height() / 2); + adjust = 0.5f; + break; + } + if (!rightSurface) { + if (adjust == 1.0f) + rightSurface = new QGLSubsurface(&mainSurface, viewport); + else + rightSurface = new QGLViewSubsurface(&mainSurface, viewport, adjust); + } else { + static_cast<QGLSubsurface *>(rightSurface)->setRegion(viewport); + } + return rightSurface; +} + +// Returns a surface that can be used to render a non-stereoscopic +// image into both eyes at the same time. Returns null if the eyes +// must be rendered one at a time. +QGLAbstractSurface *QGLViewPrivate::bothEyesSurface() +{ + switch (stereoType) { + case QGLView::Hardware: +#if defined(GL_BACK_LEFT) && defined(GL_BACK_RIGHT) + return 0; +#endif + case QGLView::RedCyanAnaglyph: + return &mainSurface; + default: + return 0; + } +} + +static QGLFormat makeStereoGLFormat(const QGLFormat& format) +{ +#if defined(GL_BACK_LEFT) && defined(GL_BACK_RIGHT) + QGLFormat fmt(format); + if (qt_gl_stereo_arg() == QLatin1String("-stereo-hw")) + fmt.setOption(QGL::StereoBuffers); + return fmt; +#else + QGLFormat fmt(format); + fmt.setOption(QGL::NoStereoBuffers); + return fmt; +#endif +} + +/*! + Constructs a new view widget and attaches it to \a parent. + + This constructor will request a stereo rendering context if + the hardware supports it. +*/ +QGLView::QGLView(QWidget *parent) + : QGLWidget(makeStereoGLFormat(QGLFormat::defaultFormat()), parent) +{ + d = new QGLViewPrivate(this); + setMouseTracking(true); + if (!parent) + d->processStereoOptions(this); +} + +/*! + Constructs a new view widget and attaches it to \a parent. + The \a format argument specifies the desired QGLFormat + rendering options. + + If \a format does not include the stereo option, then a stereo + viewing context will not be requested. +*/ +QGLView::QGLView(const QGLFormat& format, QWidget *parent) + : QGLWidget(format, parent) +{ + d = new QGLViewPrivate(this); + setMouseTracking(true); + if (!parent) + d->processStereoOptions(this); +} + +/*! + Destroys this view widget. +*/ +QGLView::~QGLView() +{ + delete d; +} + +/*! + Returns the options for this view. The default value is + CameraNavigation. + + \sa setOptions(), setOption() +*/ +QGLView::Options QGLView::options() const +{ + return d->options; +} + +/*! + Sets the options for this view to \a value. + + \sa options(), setOption() +*/ +void QGLView::setOptions(QGLView::Options value) +{ + d->options = value; +} + +/*! + Enables or disables \a option according to \a value. + + \sa options(), setOptions() +*/ +void QGLView::setOption(QGLView::Option option, bool value) +{ + if (value) + d->options |= option; + else + d->options &= ~option; +} + +/*! + Returns the type of stereo viewing technology that is in use. + + \sa setStereoType() +*/ +QGLView::StereoType QGLView::stereoType() const +{ + return d->stereoType; +} + +/*! + Sets the \a type of stereo viewing technology that is in use. + The request takes effect at the next repaint. + + The request is ignored stereoType() or \a type is Hardware, + because hardware stereo can only be enabled if the hardware + supports it, and then it can never be disabled. + + \sa stereoType() +*/ +void QGLView::setStereoType(QGLView::StereoType type) +{ + if (d->stereoType == Hardware || type == Hardware) + return; + if (d->stereoType == type) + return; + d->stereoType = type; + + // Destroy the current surface objects so that they will + // be re-generated the next time we paint the widget. + delete d->leftSurface; + delete d->rightSurface; + d->leftSurface = 0; + d->rightSurface = 0; +} + +/*! + Registers an \a object with this view to be notified when + \a objectId is selected with the mouse. The \a object must + persist for the lifetime of the QGLView, or until + deregisterObject() is called for \a objectId. + + \sa deregisterObject(), objectForPoint() +*/ +void QGLView::registerObject(int objectId, QObject *object) +{ + d->objects[objectId] = object; +} + +/*! + Deregisters the object associated with \a objectId. + + \sa registerObject() +*/ +void QGLView::deregisterObject(int objectId) +{ + d->objects.remove(objectId); +} + +/*! + Returns the camera parameters. The camera defines the projection + to apply to convert eye co-ordinates into window co-ordinates, + and the position and orientation of the viewer's eye. + + \sa setCamera() +*/ +QGLCamera *QGLView::camera() const +{ + return d->camera; +} + +/*! + Sets the camera parameters to \a value. The camera defines the + projection to apply to convert eye co-ordinates into window + co-ordinates, and the position and orientation of the viewer's eye. + + If \a value is null, then the default camera object will be used. + + This function will call update() to force the view to + update with the new camera parameters upon the next event loop. + + \sa camera() +*/ +void QGLView::setCamera(QGLCamera *value) +{ + if (!value) + value = d->defaultCamera; + + if (d->camera == value) + return; + + disconnect(d->camera, SIGNAL(projectionChanged()), + this, SLOT(cameraChanged())); + disconnect(d->camera, SIGNAL(viewChanged()), + this, SLOT(cameraChanged())); + + d->camera = value; + + connect(d->camera, SIGNAL(projectionChanged()), + this, SLOT(cameraChanged())); + connect(d->camera, SIGNAL(viewChanged()), + this, SLOT(cameraChanged())); + + cameraChanged(); +} + +/*! + Maps \a point from viewport co-ordinates to eye co-ordinates. + + The returned vector will have its x and y components set to the + position of the point on the near plane, and the z component + set to the inverse of the camera's near plane. + + This function is used for converting a mouse event's position + into eye co-ordinates within the current camera view. + + \sa QGLCamera::mapPoint() +*/ +QVector3D QGLView::mapPoint(const QPoint &point) const +{ + QSize viewportSize(size()); + qreal aspectRatio; + + // Get the size of the underlying paint device. + int width = viewportSize.width(); + int height = viewportSize.height(); + + // Use the device's DPI setting to determine the pixel aspect ratio. + int dpiX = logicalDpiX(); + int dpiY = logicalDpiY(); + if (dpiX <= 0 || dpiY <= 0) + dpiX = dpiY = 1; + + // Derive the aspect ratio based on window and pixel size. + if (width <= 0 || height <= 0) + aspectRatio = 1.0f; + else + aspectRatio = ((qreal)(width * dpiY)) / ((qreal)(height * dpiX)); + + // Map the point into eye co-ordinates. + return d->camera->mapPoint(point, aspectRatio, viewportSize); +} + +void QGLView::cameraChanged() +{ + // The pick buffer will need to be refreshed at the new camera position. + d->pickBufferForceUpdate = true; + + // Queue an update for the next event loop. + update(); +} + +/*! + \internal +*/ +void QGLView::initializeGL() +{ + d->logEnter("QGLView::initializeGL"); + QGLPainter painter; + painter.begin(); + + // Set the default depth buffer options. + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); +#if defined(QT_OPENGL_ES) + glDepthRangef(0.0f, 1.0f); +#else + glDepthRange(0.0f, 1.0f); +#endif + + // Set the default blend options. + if (painter.hasOpenGLFeature(QOpenGLFunctions::BlendColor)) + painter.glBlendColor(0, 0, 0, 0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (painter.hasOpenGLFeature(QOpenGLFunctions::BlendEquation)) + painter.glBlendEquation(GL_FUNC_ADD); + else if (painter.hasOpenGLFeature(QOpenGLFunctions::BlendEquationSeparate)) + painter.glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + + glDisable(GL_CULL_FACE); + initializeGL(&painter); + d->logLeave("QGLView::initializeGL"); +} + +/*! + \internal +*/ +void QGLView::resizeGL(int w, int h) +{ + // Set up the standard viewport for the new window size. + glViewport(0, 0, w, h); + + // We will need to regenerate the pick buffer. + d->pickBufferForceUpdate = true; +} + +/*! + \internal +*/ +void QGLView::paintGL() +{ + d->logEnter("QGLView::paintGL"); + // We may need to regenerate the pick buffer on the next mouse event. + d->pickBufferMaybeInvalid = true; + + // Paint the scene contents. + QGLPainter painter; + QGLAbstractSurface *surface; + painter.begin(); + if (d->options & QGLView::ShowPicking && + d->stereoType == QGLView::RedCyanAnaglyph) { + // If showing picking, then render normally. This really + // only works if we aren't using hardware or double stereo. + painter.setPicking(true); + painter.clearPickObjects(); + painter.setEye(QGL::NoEye); + earlyPaintGL(&painter); + painter.setCamera(d->camera); + paintGL(&painter); + painter.setPicking(false); + } else if (d->camera->eyeSeparation() == 0.0f && + (surface = d->bothEyesSurface()) != 0) { + // No camera separation, so render the same image into both buffers. + painter.pushSurface(surface); + painter.setEye(QGL::NoEye); + earlyPaintGL(&painter); + painter.setCamera(d->camera); + paintGL(&painter); + painter.popSurface(); + } else { + // Paint the scene twice, from the perspective of each camera. + QSize size(this->size()); + painter.setEye(QGL::LeftEye); + if (d->stereoType != QGLView::Hardware) + earlyPaintGL(&painter); // Clear both eyes at the same time. + painter.pushSurface(d->leftEyeSurface(size)); + if (d->stereoType == QGLView::Hardware) + earlyPaintGL(&painter); // Clear the left eye only. + earlyPaintGL(&painter); + painter.setCamera(d->camera); + paintGL(&painter); + if (d->stereoType == QGLView::RedCyanAnaglyph) + glClear(GL_DEPTH_BUFFER_BIT); + painter.setEye(QGL::RightEye); + painter.setSurface(d->rightEyeSurface(size)); + if (d->stereoType == QGLView::Hardware) + earlyPaintGL(&painter); // Clear the right eye only. + painter.setCamera(d->camera); + paintGL(&painter); + painter.popSurface(); + } + d->logLeave("QGLView::paintGL"); +} + +/*! + Initializes the current GL context represented by \a painter. + + \sa paintGL() +*/ +void QGLView::initializeGL(QGLPainter *painter) +{ + Q_UNUSED(painter); +} + +/*! + Performs early painting operations just after \a painter + is initialized but before the camera is set up. The default + implementation clears the color buffer and depth buffer. + + This function is typically overridden to draw scene backdrops + on the color buffer before the rest of the scene is drawn + by paintGL(). + + \sa paintGL() +*/ +void QGLView::earlyPaintGL(QGLPainter *painter) +{ + Q_UNUSED(painter); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +/*! + \fn void QGLView::paintGL(QGLPainter *painter) + + Paints the scene onto \a painter. The color and depth buffers + will have already been cleared, and the camera() position set. + + If QGLPainter::isPicking() is set for \a painter, then the + function should paint the scene onto \a painter in + "object picking mode". The scene will be rendered into a + background buffer using flat colors so that mouse events + can determine which object lies under the mouse pointer. + + The default implementation of picking will typically just + render the scene normally. However, some applications + may wish to render a simpler scene that omits unselectable + objects and uses simpler meshes for the selectable objects. + + \sa earlyPaintGL() +*/ + +/*! + Processes the mouse press event \a e. +*/ +void QGLView::mousePressEvent(QMouseEvent *e) +{ + QObject *object; + if (!d->panning && (d->options & QGLView::ObjectPicking) != 0) + object = objectForPoint(e->pos()); + else + object = 0; + if (d->pressedObject) { + // Send the press event to the pressed object. Use a position + // of (0, 0) if the mouse is still within the pressed object, + // or (-1, -1) if the mouse is no longer within the pressed object. + QMouseEvent event + (QEvent::MouseButtonPress, + (d->pressedObject == object) ? QPoint(0, 0) : QPoint(-1, -1), + e->globalPos(), e->button(), e->buttons(), e->modifiers()); + QCoreApplication::sendEvent(d->pressedObject, &event); + } else if (object) { + // Record the object that was pressed and forward the event. + d->pressedObject = object; + d->enteredObject = 0; + d->pressedButton = e->button(); + + // Send a mouse press event for (0, 0). + QMouseEvent event(QEvent::MouseButtonPress, QPoint(0, 0), + e->globalPos(), e->button(), e->buttons(), + e->modifiers()); + QCoreApplication::sendEvent(object, &event); + } else if ((d->options & QGLView::CameraNavigation) != 0 && + e->button() == Qt::LeftButton) { + d->panning = true; + d->lastPan = d->startPan = e->pos(); + d->startEye = d->camera->eye(); + d->startCenter = d->camera->center(); + d->startUpVector = d->camera->upVector(); + d->panModifiers = e->modifiers(); +#ifndef QT_NO_CURSOR + setCursor(Qt::ClosedHandCursor); +#endif + } + QGLWidget::mousePressEvent(e); +} + +/*! + Processes the mouse release event \a e. +*/ +void QGLView::mouseReleaseEvent(QMouseEvent *e) +{ + if (d->panning && e->button() == Qt::LeftButton) { + d->panning = false; +#ifndef QT_NO_CURSOR + unsetCursor(); +#endif + } + if (d->pressedObject) { + // Notify the previously pressed object about the release. + QObject *object = objectForPoint(e->pos()); + QObject *pressed = d->pressedObject; + if (e->button() == d->pressedButton) { + d->pressedObject = 0; + d->pressedButton = Qt::NoButton; + d->enteredObject = object; + + // Send the release event to the pressed object. Use a position + // of (0, 0) if the mouse is still within the pressed object, + // or (-1, -1) if the mouse is no longer within the pressed object. + QMouseEvent event + (QEvent::MouseButtonRelease, + (pressed == object) ? QPoint(0, 0) : QPoint(-1, -1), + e->globalPos(), e->button(), e->buttons(), e->modifiers()); + QCoreApplication::sendEvent(pressed, &event); + + // Send leave and enter events if necessary. + if (object != pressed) { + sendLeaveEvent(pressed); + if (object) + sendEnterEvent(object); + } + } else { + // Some other button than the original was released. + // Forward the event to the pressed object. + QMouseEvent event + (QEvent::MouseButtonRelease, + (pressed == object) ? QPoint(0, 0) : QPoint(-1, -1), + e->globalPos(), e->button(), e->buttons(), e->modifiers()); + QCoreApplication::sendEvent(pressed, &event); + } + } + QGLWidget::mouseReleaseEvent(e); +} + +/*! + Processes the mouse double click event \a e. +*/ +void QGLView::mouseDoubleClickEvent(QMouseEvent *e) +{ + if ((d->options & QGLView::ObjectPicking) != 0) { + QObject *object = objectForPoint(e->pos()); + if (object) { + // Simulate a double click event for (0, 0). + QMouseEvent event + (QEvent::MouseButtonDblClick, QPoint(0, 0), + e->globalPos(), e->button(), e->buttons(), e->modifiers()); + QCoreApplication::sendEvent(object, &event); + } + } + QGLWidget::mouseDoubleClickEvent(e); +} + +/*! + Processes the mouse move event \a e. +*/ +void QGLView::mouseMoveEvent(QMouseEvent *e) +{ + if (d->panning) { + QPoint delta = e->pos() - d->startPan; + if (e->modifiers() == d->panModifiers) { + d->camera->setEye(d->startEye); + d->camera->setCenter(d->startCenter); + d->camera->setUpVector(d->startUpVector); + } else { + d->startPan = d->lastPan; + delta = e->pos() - d->startPan; + d->startEye = d->camera->eye(); + d->startCenter = d->camera->center(); + d->startUpVector = d->camera->upVector(); + d->panModifiers = e->modifiers(); + } + d->lastPan = e->pos(); + if ((e->modifiers() & Qt::ControlModifier) != 0) + wheel(delta.y() * -60); + else if ((e->modifiers() & Qt::ShiftModifier) != 0) + pan(delta.x(), delta.y()); + else + rotate(delta.x(), delta.y()); + } else if ((d->options & QGLView::ObjectPicking) != 0) { + QObject *object = objectForPoint(e->pos()); + if (d->pressedObject) { + // Send the move event to the pressed object. Use a position + // of (0, 0) if the mouse is still within the pressed object, + // or (-1, -1) if the mouse is no longer within the pressed object. + QMouseEvent event + (QEvent::MouseMove, + (d->pressedObject == object) ? QPoint(0, 0) : QPoint(-1, -1), + e->globalPos(), e->button(), e->buttons(), e->modifiers()); + QCoreApplication::sendEvent(d->pressedObject, &event); + } else if (object) { + if (object != d->enteredObject) { + if (d->enteredObject) + sendLeaveEvent(d->enteredObject); + d->enteredObject = object; + sendEnterEvent(d->enteredObject); + } + QMouseEvent event + (QEvent::MouseMove, QPoint(0, 0), + e->globalPos(), e->button(), e->buttons(), e->modifiers()); + QCoreApplication::sendEvent(object, &event); + } else if (d->enteredObject) { + sendLeaveEvent(d->enteredObject); + d->enteredObject = 0; + } + } + QGLWidget::mouseMoveEvent(e); +} + +/*! + Processes the leave event \a e. +*/ +void QGLView::leaveEvent(QEvent *e) +{ + if (!d->pressedObject && d->enteredObject) { + sendLeaveEvent(d->enteredObject); + d->enteredObject = 0; + } + QGLWidget::leaveEvent(e); +} + +#ifndef QT_NO_WHEELEVENT + +/*! + Processes the wheel event \a e. +*/ +void QGLView::wheelEvent(QWheelEvent *e) +{ + if ((d->options & QGLView::CameraNavigation) != 0) + wheel(e->delta()); + QGLWidget::wheelEvent(e); +} + +#endif + +/*! + Processes the key press event \a e. +*/ +void QGLView::keyPressEvent(QKeyEvent *e) +{ + QGLCamera *camera; + qreal sep; + + if ((d->options & QGLView::CameraNavigation) == 0) { + QGLWidget::keyPressEvent(e); + return; + } + switch (e->key()) { + + case Qt::Key_Escape: + case Qt::Key_Q: + { + if (parentWidget() == 0) + close(); + } + + case Qt::Key_Left: + { + if ((e->modifiers() & Qt::ShiftModifier) != 0) { + pan(-10, 0); + } else if ((e->modifiers() & Qt::ControlModifier) != 0) { + camera = this->camera(); + sep = camera->eyeSeparation(); + sep -= (sep / 10.0f); + if (sep < 0.0f) + sep = 0.0f; + camera->setEyeSeparation(sep); + e->accept(); + return; + } else { + rotate(-10, 0); + } + } + break; + + case Qt::Key_Right: + { + if ((e->modifiers() & Qt::ShiftModifier) != 0) { + pan(10, 0); + } else if ((e->modifiers() & Qt::ControlModifier) != 0) { + camera = this->camera(); + sep = camera->eyeSeparation(); + sep += (sep / 10.0f); + camera->setEyeSeparation(sep); + e->accept(); + return; + } else { + rotate(10, 0); + } + } + break; + + case Qt::Key_Up: + { + if ((e->modifiers() & Qt::ControlModifier) != 0) + wheel(120); + else if ((e->modifiers() & Qt::ShiftModifier) != 0) + pan(0, -10); + else + rotate(0, -10); + } + break; + + case Qt::Key_Down: + { + if ((e->modifiers() & Qt::ControlModifier) != 0) + wheel(-120); + else if ((e->modifiers() & Qt::ShiftModifier) != 0) + pan(0, 10); + else + rotate(0, 10); + } + break; + } + QGLWidget::keyPressEvent(e); +} + +class QGLViewPickSurface : public QGLAbstractSurface +{ +public: + QGLViewPickSurface(QGLView *view, QGLFramebufferObject *fbo, + const QSize &areaSize); + + QPaintDevice *device() const; + bool activate(QGLAbstractSurface *prevSurface); + void deactivate(QGLAbstractSurface *nextSurface); + QRect viewportGL() const; + +private: + QGLView *m_view; + QGLFramebufferObject *m_fbo; + QRect m_viewportGL; +}; + +QGLViewPickSurface::QGLViewPickSurface + (QGLView *view, QGLFramebufferObject *fbo, const QSize &areaSize) + : QGLAbstractSurface(504) + , m_view(view) + , m_fbo(fbo) + , m_viewportGL(QPoint(0, 0), areaSize) +{ +} + +QPaintDevice *QGLViewPickSurface::device() const +{ + return m_view; +} + +bool QGLViewPickSurface::activate(QGLAbstractSurface *prevSurface) +{ + Q_UNUSED(prevSurface); + if (m_fbo) + m_fbo->bind(); + return true; +} + +void QGLViewPickSurface::deactivate(QGLAbstractSurface *nextSurface) +{ + Q_UNUSED(nextSurface); + if (m_fbo) + m_fbo->release(); +} + +QRect QGLViewPickSurface::viewportGL() const +{ + return m_viewportGL; +} + +/*! + Returns the registered object that is under the mouse position + specified by \a point. This function may need to regenerate + the contents of the pick buffer by repainting the scene + with paintGL(). + + \sa registerObject() +*/ +QObject *QGLView::objectForPoint(const QPoint &point) +{ + QPoint pt(point); + + // What is the size of the drawing area after correcting for stereo? + // Also adjust the mouse position to always be in the left half. + QSize areaSize = size(); + switch (d->stereoType) { + case QGLView::LeftRight: + case QGLView::RightLeft: + areaSize = QSize(areaSize.width() / 2, areaSize.height()); + if (pt.x() >= areaSize.width()) + pt.setX(pt.x() - areaSize.width()); + break; + case QGLView::TopBottom: + case QGLView::BottomTop: + areaSize = QSize(areaSize.width(), areaSize.height() / 2); + if (pt.y() >= areaSize.height()) + pt.setY(pt.y() - areaSize.height()); + break; + case QGLView::StretchedLeftRight: + case QGLView::StretchedRightLeft: { + int halfwid = areaSize.width() / 2; + if (pt.x() >= halfwid) + pt.setX((pt.x() - halfwid) * 2); + else + pt.setX(pt.x() * 2); + break; } + case QGLView::StretchedTopBottom: + case QGLView::StretchedBottomTop: { + int halfht = areaSize.height() / 2; + if (pt.y() >= halfht) + pt.setY((pt.y() - halfht) * 2); + else + pt.setY(pt.y() * 2); + break; } + default: break; + } + + // Check the area boundaries in case a mouse move has + // moved the pointer outside the window. + if (pt.x() < 0 || pt.x() >= areaSize.width() || + pt.y() < 0 || pt.y() >= areaSize.height()) + return 0; + + // Do we need to refresh the pick buffer contents? + QGLPainter painter(this); + if (d->pickBufferForceUpdate) { + // Initialize the painter, which will make the window context current. + painter.setPicking(true); + painter.clearPickObjects(); + + // Create a framebuffer object as big as the window to act + // as the pick buffer if we are single buffered. If we are + // double-buffered, then use the window back buffer. + bool useBackBuffer = doubleBuffer(); + if (!useBackBuffer) { + QSize fbosize = QGL::nextPowerOfTwo(areaSize); + if (!d->fbo) { + d->fbo = new QGLFramebufferObject(fbosize, QGLFramebufferObject::CombinedDepthStencil); + } else if (d->fbo->size() != fbosize) { + delete d->fbo; + d->fbo = new QGLFramebufferObject(fbosize, QGLFramebufferObject::CombinedDepthStencil); + } + } + + // Render the pick version of the scene. + QGLViewPickSurface surface(this, d->fbo, areaSize); + painter.pushSurface(&surface); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + painter.setEye(QGL::NoEye); + painter.setCamera(d->camera); + paintGL(&painter); + painter.setPicking(false); + painter.popSurface(); + + // The pick buffer contents are now valid, unless we are using + // the back buffer - we cannot rely upon it being valid next time. + d->pickBufferForceUpdate = useBackBuffer; + d->pickBufferMaybeInvalid = false; + } + + // Pick the object under the mouse. + if (d->fbo) + d->fbo->bind(); + int objectId = painter.pickObject(pt.x(), areaSize.height() - 1 - pt.y()); + QObject *object = d->objects.value(objectId, 0); + if (d->fbo) + d->fbo->release(); + + // Release the framebuffer object and return. + painter.end(); + doneCurrent(); + return object; +} + +void QGLView::sendEnterEvent(QObject *object) +{ + QEvent event(QEvent::Enter); + QCoreApplication::sendEvent(object, &event); +} + +void QGLView::sendLeaveEvent(QObject *object) +{ + QEvent event(QEvent::Leave); + QCoreApplication::sendEvent(object, &event); +} + +// Zoom in and out according to the change in wheel delta. +void QGLView::wheel(int delta) +{ + qreal scale = qAbs(viewDelta(delta, delta).x()); + if (delta < 0) + scale = -scale; + if (scale >= 0.0f) + scale += 1.0f; + else + scale = 1.0f / (1.0f - scale); + qreal fov = d->camera->fieldOfView(); + if (fov != 0.0f) + d->camera->setFieldOfView(d->camera->fieldOfView() / scale); + else + d->camera->setViewSize(d->camera->viewSize() / scale); +} + +// Pan left/right/up/down without rotating about the object. +void QGLView::pan(int deltax, int deltay) +{ + QPointF delta = viewDelta(deltax, deltay); + QVector3D t = d->camera->translation(delta.x(), -delta.y(), 0.0f); + + // Technically panning the eye left should make the object appear to + // move off to the right, but this looks weird on-screen where the user + // actually thinks they are picking up the object and dragging it rather + // than moving the eye. We therefore apply the inverse of the translation + // to make it "look right". + d->camera->setEye(d->camera->eye() - t); + d->camera->setCenter(d->camera->center() - t); +} + +// Rotate about the object being viewed. +void QGLView::rotate(int deltax, int deltay) +{ + int rotation = d->camera->screenRotation(); + if (rotation == 90 || rotation == 270) { + qSwap(deltax, deltay); + } + if (rotation == 90 || rotation == 180) { + deltax = -deltax; + } + if (rotation == 180 || rotation == 270) { + deltay = -deltay; + } + qreal anglex = deltax * 90.0f / width(); + qreal angley = deltay * 90.0f / height(); + QQuaternion q = d->camera->pan(-anglex); + q *= d->camera->tilt(-angley); + d->camera->rotateCenter(q); +} + +/*! + Converts \a deltax and \a deltay into percentages of the + view width and height. Returns a QPointF containing the + percentage values, typically between -1 and 1. + + This function is typically used by subclasses to convert a + change in mouse position into a relative distance travelled + across the field of view. + + The returned value is corrected for the camera() screen + rotation and view size. +*/ +QPointF QGLView::viewDelta(int deltax, int deltay) const +{ + int w = width(); + int h = height(); + bool scaleToWidth; + qreal scaleFactor, scaleX, scaleY; + QSizeF viewSize = d->camera->viewSize(); + if (w >= h) { + if (viewSize.width() >= viewSize.height()) + scaleToWidth = true; + else + scaleToWidth = false; + } else { + if (viewSize.width() >= viewSize.height()) + scaleToWidth = false; + else + scaleToWidth = true; + } + int rotation = d->camera->screenRotation(); + if (rotation == 90 || rotation == 270) { + scaleToWidth = !scaleToWidth; + qSwap(deltax, deltay); + } + if (rotation == 90 || rotation == 180) { + deltax = -deltax; + } + if (rotation == 180 || rotation == 270) { + deltay = -deltay; + } + if (scaleToWidth) { + scaleFactor = 2.0f / viewSize.width(); + scaleX = scaleFactor * ((qreal)h) / ((qreal)w); + scaleY = scaleFactor; + } else { + scaleFactor = 2.0f / viewSize.height(); + scaleX = scaleFactor; + scaleY = scaleFactor * ((qreal)w) / ((qreal)h); + } + return QPointF(deltax * scaleX / w, deltay * scaleY / h); +} + +/*! + \fn QPointF QGLView::viewDelta(const QPoint &delta) const + \overload + + Converts the x and y components of \a delta into percentages + of the view width and height. Returns a QPointF containing + the percentage values, typically between -1 and 1. +*/ + + +QT_END_NAMESPACE diff --git a/src/threed/viewing/qglview.h b/src/threed/viewing/qglview.h new file mode 100644 index 000000000..bd5934335 --- /dev/null +++ b/src/threed/viewing/qglview.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLVIEW_H +#define QGLVIEW_H + +#include "qglpainter.h" +#include "qglcamera.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3D) + +class QGLViewPrivate; + +class Q_QT3D_EXPORT QGLView : public QGLWidget +{ + Q_OBJECT +public: + explicit QGLView(QWidget *parent=0); + explicit QGLView(const QGLFormat& format, QWidget *parent=0); + ~QGLView(); + + enum Option + { + ObjectPicking = 0x0001, + ShowPicking = 0x0002, + CameraNavigation = 0x0004, + PaintingLog = 0x0008 + }; + Q_DECLARE_FLAGS(Options, Option) + + QGLView::Options options() const; + void setOptions(QGLView::Options value); + void setOption(QGLView::Option option, bool value); + + enum StereoType + { + Hardware, + RedCyanAnaglyph, + LeftRight, + RightLeft, + TopBottom, + BottomTop, + StretchedLeftRight, + StretchedRightLeft, + StretchedTopBottom, + StretchedBottomTop + }; + + QGLView::StereoType stereoType() const; + void setStereoType(QGLView::StereoType type); + + void registerObject(int objectId, QObject *object); + void deregisterObject(int objectId); + QObject *objectForPoint(const QPoint &point); + + QGLCamera *camera() const; + void setCamera(QGLCamera *camera); + + QVector3D mapPoint(const QPoint &point) const; + +protected: + void initializeGL(); + void resizeGL(int w, int h); + void paintGL(); + virtual void initializeGL(QGLPainter *painter); + virtual void earlyPaintGL(QGLPainter *painter); + virtual void paintGL(QGLPainter *painter) = 0; + + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void leaveEvent(QEvent *e); +#ifndef QT_NO_WHEELEVENT + void wheelEvent(QWheelEvent *e); +#endif + void keyPressEvent(QKeyEvent *e); + + QPointF viewDelta(int deltax, int deltay) const; + QPointF viewDelta(const QPoint &delta) const + { return viewDelta(delta.x(), delta.y()); } + +private Q_SLOTS: + void cameraChanged(); + +private: + QGLViewPrivate *d; + + static void sendEnterEvent(QObject *object); + static void sendLeaveEvent(QObject *object); + + void wheel(int delta); + void pan(int deltax, int deltay); + void rotate(int deltax, int deltay); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGLView::Options) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/threed/viewing/viewing.pri b/src/threed/viewing/viewing.pri new file mode 100644 index 000000000..60125f491 --- /dev/null +++ b/src/threed/viewing/viewing.pri @@ -0,0 +1,12 @@ +INCLUDEPATH += $$PWD +VPATH += $$PWD + +HEADERS += \ + qglcamera.h \ + qglcameraanimation.h \ + qglview.h + +SOURCES += \ + qglcamera.cpp \ + qglcameraanimation.cpp \ + qglview.cpp |