void obj_parser_vntag(obj_parser_t *p, array_t *a) { // vn = "vn", whitespace, float, whitespace, float, whitespace, float float v[3]; for(uint64_t i = 0; i < 3; ++i) { obj_parser_expect(p, OBJ_FLOAT); v[i] = strtof(array_data(p->token.lexeme), NULL); } array_append(a, &v); }
void obj_parser_vttag(obj_parser_t *p, array_t *a) { // vt = "vt", whitespace, float, whitespace, float, [whitespace, float] float v[3]; for(uint64_t i = 0; i < 2; ++i) { obj_parser_expect(p, OBJ_FLOAT); v[i] = strtof(array_data(p->token.lexeme), NULL); } if(obj_parser_found(p, OBJ_FLOAT)) { // In case we get a 3d texture coordinate v[2] = strtof(array_data(p->token.lexeme), NULL); } else { v[2] = 0.0f; } // Blender's UV coordinate system is vertically flipped compared to OpenGL's v[1] = 1 - v[1]; array_append(a, &v); }
void obj_parser_vtag(obj_parser_t *p, array_t *a) { // v = 'v', whitespace, float, whitespace, float, whitespace, float // A vtag is followed by 3 floats float v[3]; for(uint64_t i = 0; i < 3; ++i) { obj_parser_expect(p, OBJ_FLOAT); v[i] = strtof(array_data(p->token.lexeme), NULL); } array_append(a, &v); }
void obj_parser_ftag(obj_parser_t *p, array_t *i_positions, array_t *i_texcoords, array_t *i_normals) { // f = 'f', whitespace, ((uint, whitespace, uint, whitespace, uint // | uint, separator, uint, whitespace, uint, separator, uint, whitespace, uint, separator, uint // | uint, separator, uint, separator, uint, whitespace, uint, separator, uint, separator, uint, whitespace, uint, separator, uint, separator, uint // | uint, separator, separator, uint, whitespace, uint, separator, separator, uint, whitespace, uint, separator, separator, uint) // 3 sets of indices for each vertex for(uint64_t i = 0; i < 3; ++i) { obj_parser_expect(p, OBJ_UINT); uint32_t index = (uint32_t)strtoul(array_data(p->token.lexeme), NULL, 10); array_append(i_positions, &index); if(obj_parser_found(p, OBJ_SEPARATOR)) { // Double separator means only normal index specified if(obj_parser_found(p, OBJ_SEPARATOR)) { obj_parser_expect(p, OBJ_UINT); index = (uint32_t)strtoul(array_data(p->token.lexeme), NULL, 10); array_append(i_normals, &index); } else { // One separator indicates a texcoord obj_parser_expect(p, OBJ_UINT); index = (uint32_t)strtoul(array_data(p->token.lexeme), NULL, 10); array_append(i_texcoords, &index); // If another separator is found then a normal is also specified if(obj_parser_found(p, OBJ_SEPARATOR)) { obj_parser_expect(p, OBJ_UINT); index = (uint32_t)strtoul(array_data(p->token.lexeme), NULL, 10); array_append(i_normals, &index); } } } } }
uint64_t obj_lexer_get_token(obj_parser_t *p) { obj_token_type_t prev_type = p->token.type; p->token.type = OBJ_UNKNOWN; // Skip comment lines while(p->fstring[p->c_index] == '#') { p->c_index += strcspn(&p->fstring[p->c_index], "\n"); while(isspace(p->fstring[p->c_index])) p->c_index++; } // Skip all the white spaces and newlines while(isspace(p->fstring[p->c_index])) p->c_index++; // Check if end of file has been reached if(p->c_index >= p->fsize) { p->token.type = OBJ_ENDOFFILE; return 1; } array_clear(p->token.lexeme); // Read characters until the next whitespace or separator while(!isspace(p->fstring[p->c_index]) && p->fstring[p->c_index] != '/') { char c = p->fstring[p->c_index++]; array_append(p->token.lexeme, &c); } size_t tok_len = array_size(p->token.lexeme); // If token length is zero, it must be the separator...right? if(tok_len == 0) { array_append(p->token.lexeme, &p->fstring[p->c_index++]); tok_len++; } char c = 0; array_append(p->token.lexeme, &c); // Get the actual data from the array char *lexeme = (char*)array_data(p->token.lexeme); // Check if it is a separator if(lexeme[0] == '/') { p->token.type = OBJ_ERROR; if(tok_len != 1) return 1; p->token.type = OBJ_SEPARATOR; return 0; } // Check if this token is an identifier depending on the previous type if(prev_type == OBJ_MTLLIBTAG || prev_type == OBJ_USEMTLTAG) { p->token.type = OBJ_IDENTIFIER; return 0; } // Check if the token is a tag: r'vn|vt|v|f' if(isalpha(lexeme[0])) { // Which kind of tag? if(strcmp(lexeme, "vn") == 0) p->token.type = OBJ_VNTAG; else if(strcmp(lexeme, "vt") == 0) p->token.type = OBJ_VTTAG; else if(strcmp(lexeme, "v") == 0) p->token.type = OBJ_VTAG; else if(strcmp(lexeme, "f") == 0) p->token.type = OBJ_FTAG; else if(strcmp(lexeme, "mtllib") == 0) p->token.type = OBJ_MTLLIBTAG; else if(strcmp(lexeme, "usemtl") == 0) p->token.type = OBJ_USEMTLTAG; else p->token.type = OBJ_ERROR; return (p->token.type == OBJ_ERROR) ? 1 : 0; } // Check if it is a floating point number: -?[0-9]+\.[0-9]+ if(strchr(lexeme, '.') != NULL && (lexeme[0] == '-' || isdigit(lexeme[0]))) { p->token.type = OBJ_ERROR; // Confirm that this is a correctly formatted float size_t index = 0; if(lexeme[index] == '-') index++; while(isdigit(lexeme[index++])); // Must be a period if(lexeme[(index-1)] != '.') return 1; // Continue confirming digits in the decimal portion while(isdigit(lexeme[index++])); // If index is the number as tok_len then we have successfully confirmed a floating point number if(--index != tok_len) return 1; p->token.type = OBJ_FLOAT; return 0; } // Check if it is a uint: [0-9]+ if(isdigit(lexeme[0])) { p->token.type = OBJ_ERROR; // Confirm that this is a correctly formatted uint size_t index = 0; while(isdigit(lexeme[index++])); // If index is the same number as tok_len then we have successfully confirmed a uint if(--index != tok_len) return 1; p->token.type = OBJ_UINT; return 0; } return 0; }
bool mesh_load(mesh_t *mesh, const char *objfile) { // Initialize the parser struct obj_parser_t p; if(obj_parser_init(&p, objfile) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load mesh: %s\n", objfile); return false; } // Initialize the arrays. mesh->vattributes = array_create(256, 3*sizeof(GLfloat)); mesh->indices = array_create(256, sizeof(GLuint)); mesh->mtl_grps = array_create(2, sizeof(material_group_t)); // Grab the vertex attribute data and place them in separate arrays array_t *uv = array_create(256, 3*sizeof(GLfloat)); array_t *normals = array_create(256, 3*sizeof(GLfloat)); array_t *mtllib = array_create(16, sizeof(char)); for(; p.token.type != OBJ_ENDOFFILE; obj_lexer_get_token(&p)) { switch(p.token.type) { case OBJ_VNTAG: // Parse the vertex normals obj_parser_vntag(&p, normals); break; case OBJ_VTTAG: // Parse the vertex texture coordinates obj_parser_vttag(&p, uv); break; case OBJ_VTAG: // Parse the vertex position coordinates obj_parser_vtag(&p, mesh->vattributes); // Now append zero vectors for the normal and texture coordinate attributes // The z value is initially set to -10.0f to indicate that the attribute is currently empty GLfloat u[3] = {0.0f, 0.0f, -10.0f}; array_append(mesh->vattributes, &u); array_append(mesh->vattributes, &u); break; case OBJ_MTLLIBTAG: // Parse the name of the mtllib file obj_parser_mtllibtag(&p, mtllib); // Get the mtllib filename array_prepend_str(mtllib, "resources/"); break; default: break; } } // If a mtllib file was specified, parse it array_t *mtl_list = array_create(2, sizeof(material_def_t)); if(array_size(mtllib) > 0) _mesh_load_material(mesh, array_data(mtllib), mtl_list); array_t *i_positions = array_create(4, sizeof(GLuint)); array_t *i_texcoords = array_create(4, sizeof(GLuint)); array_t *i_normals = array_create(4, sizeof(GLuint)); // Parse the indices as they are read in and place the vertex attributes in the correct index in the vertex attribute array for(p.c_index = 0, p.token.type = OBJ_UNKNOWN; p.token.type != OBJ_ENDOFFILE; obj_lexer_get_token(&p)) { switch(p.token.type) { case OBJ_FTAG: { // Parse the face indices array_clear(i_positions); array_clear(i_texcoords); array_clear(i_normals); obj_parser_ftag(&p, i_positions, i_texcoords, i_normals); mesh->num_faces++; // Create a material group if none exist // We might have obj files with only one group of faces and no materials if(array_size(mesh->mtl_grps) == 0) _mesh_create_material_group(mesh); // Add the indices to the last material group in the list material_group_t *grp = (material_group_t*)array_back(mesh->mtl_grps); // For each vertex attribute specified, add them to the material group's indices list duplicating the attribute if necessary for(uint64_t i = 0; i < 3; ++i) { // Indices start from 1 in the Wavefront OBJ format // Get all the indices for the each vertex attributes GLuint index = *((GLuint*)array_at(i_positions, i))-1; GLuint v_index = (array_size(i_texcoords) > 0) ? *((GLuint*)array_at(i_texcoords, i)) : 0; GLuint n_index = (array_size(i_normals) > 0) ? *((GLuint*)array_at(i_normals, i)) : 0; GLfloat v[3] = {0.0f, 0.0f, 0.0f}; GLfloat n[3] = {0.0f, 0.0f, 0.0f}; bool duplicate = false; // Make sure the texture coordinate was specified if(v_index != 0) { v_index--; memcpy(v, array_at(uv, v_index), 3*sizeof(GLfloat)); // Check if the vertex needs to be duplicated, if the texture coordinates are different for the same vertex GLfloat *u = array_at(mesh->vattributes, index*3+1); if((u[2] != -10.0f)&& (u[0] != v[0] || u[1] != v[1])) duplicate = true; } // Make sure a normal was specified if(n_index != 0) { n_index--; memcpy(n, array_at(normals, n_index), 3*sizeof(GLfloat)); // Check if the vertex needs to be duplicated, if the normals are different for the same vertex GLfloat *u = array_at(mesh->vattributes, index*3+2); if((u[2] != -10.0f) && (u[0] != n[0] || u[1] != n[1] || u[2] != n[2])) duplicate = true; } // Duplicate the vertex attribute if needed if(duplicate) { GLfloat l[3] = {0.0f, 0.0f, 0.0f}; memcpy(l, array_at(mesh->vattributes, index*3), 3*sizeof(GLfloat)); array_append(mesh->vattributes, l); index = ((GLuint)array_size(mesh->vattributes)-1)/3; array_append(mesh->vattributes, v); array_append(mesh->vattributes, n); } else { // Set the texture coordinate and normal in the vertex attribute array array_set(mesh->vattributes, index*3+1, v); array_set(mesh->vattributes, index*3+2, n); } // Append the index into the index array array_append(mesh->indices, &index); } // Increment the count of indices for the material group grp->count += 3; break; } case OBJ_USEMTLTAG: { array_t *mtl_name = array_create(8, sizeof(char)); obj_parser_usemtltag(&p, mtl_name); // Start a new material group using this material _mesh_create_material_group(mesh); // Load the material data for the material group // Have to find the material with the specified material name in the material definition list bool found_mtl = false; for(uint64_t i = 0; i < array_size(mtl_list); i++) { material_def_t *mtl_def = array_at(mtl_list, i); if(strcmp((char*)array_data(mtl_def->mtl_name), (char*)array_data(mtl_name)) == 0) { material_group_t *grp = (material_group_t*)array_back(mesh->mtl_grps); // Copy the material data memcpy(&grp->mtl, &mtl_def->mtl, sizeof(material_t)); found_mtl = true; break; } } if(!found_mtl) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find material: \'%s\'\n", (char*)array_data(mtl_name)); } array_delete(mtl_name); break; } default: break; } } // Cleanup temp arrays array_delete(uv); array_delete(normals); array_delete(mtllib); array_delete(i_positions); array_delete(i_texcoords); array_delete(i_normals); // Cleanup up the material definition list for(uint64_t i = 0; i < array_size(mtl_list); i++) { material_def_t *mtl_def = (material_def_t*)array_at(mtl_list, i); array_delete(mtl_def->mtl_name); } array_delete(mtl_list); // Delete the parser struct obj_parser_free(&p); // Generate and fill the OpenGL buffers _mesh_gen_buffers(mesh); // Print some stats SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Mesh loaded with %lu vertex attributes and %lu faces\n", array_size(mesh->vattributes)/3, mesh->num_faces); return true; }
static text_renderer_cell_p find_free_cell_or_revoke_unused_cell(text_renderer_p renderer, text_renderer_font_p font, uint32_t glyph_width, uint32_t glyph_height, uint32_t texture_width, uint32_t texture_height, size_t* line_idx, size_t* cell_idx) { // Padding between the glyphs const uint32_t padding = 1; // Keep track of the texture coordinates so we know where our cell is on the texture uint32_t x = 0, y = 0; // First search for a line with the matching height for(size_t i = 0; i < renderer->lines->length; i++) { text_renderer_line_p line = &array_data(renderer->lines, text_renderer_line_t)[i]; if (line->height == glyph_height) { // We found a line with matching height, now look if we can get our cell in there x = 0; for(size_t j = 0; j < line->cells->length; j++) { text_renderer_cell_p current_cell = array_elem_ptr(line->cells, j); x += current_cell->width + padding; } if (x + glyph_width <= texture_width) { // There is space left at the end of this line, add our cell here text_renderer_cell_p cell = array_append_ptr(line->cells); memset(cell, 0, sizeof(text_renderer_cell_t)); cell->x = x; cell->y = y; cell->width = glyph_width; *line_idx = i; *cell_idx = line->cells->length - 1; return cell; } // No space at end of line, search next line } y += line->height + padding; } // We haven't found a matching line or every matching line was full. Anyway add a new line // if there is space left at the bottom of the texture and put the cell in there. if (y + glyph_height + padding <= texture_height) { text_renderer_line_p line = array_append_ptr(renderer->lines); line->height = glyph_height; line->cells = array_of(text_renderer_cell_t); text_renderer_cell_p cell = array_append_ptr(line->cells); memset(cell, 0, sizeof(text_renderer_cell_t)); cell->x = x; cell->y = y; cell->width = glyph_width; *line_idx = renderer->lines->length - 1; *cell_idx = 0; return cell; } // We would like to add a new line but there is not enough free space at the bottom // to the texture. For now just give up. *line_idx = (size_t)-1; *cell_idx = (size_t)-1; return NULL; }
void _mesh_gen_buffers(mesh_t *mesh) { // Generate the name for the vertex array object (VAO) glGenVertexArrays(1, &mesh->vao); glBindVertexArray(mesh->vao); // Create names for the vertex buffer object (VBO) and the index buffer object glGenBuffers(1, &mesh->vbo); glGenBuffers(1, &mesh->ibo); // Copy the index data into the index buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(array_size(mesh->indices)*sizeof(GLuint)), array_data(mesh->indices), GL_STATIC_DRAW); // Copy the vertex data into the vertex buffer glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo); glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(array_size(mesh->vattributes)*3*sizeof(GLfloat)), array_data(mesh->vattributes), GL_STATIC_DRAW); // Set and enable the vertex attributes. 0 = vertex position, 1 = vertex texture coordinates, 2 = vertex normals glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), (GLvoid*)(3*sizeof(GLfloat))); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), (GLvoid*)(6*sizeof(GLfloat))); // Unbind VAO glBindVertexArray(0); }
bool _mesh_load_material(mesh_t *mesh, const char *mtl_filename, array_t *mtl_list) { // Initialize the parser struct mtl_parser_t p; if(mtl_parser_init(&p, mtl_filename) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load mtllib: %s\n", mtl_filename); return false; } for(; p.token.type != MTL_ENDOFFILE; mtl_lexer_get_token(&p)) { // Get the latest material definition material_def_t *mtl_def = (array_size(mtl_list) > 0) ? (material_def_t*)array_back(mtl_list): NULL; switch(p.token.type) { case MTL_NEWMTLTAG: { // Append a new material definition to the list mtl_parser_expect(&p, MTL_IDENTIFIER); material_def_t newmtl; newmtl.mtl_name = array_create(array_size(p.token.lexeme), sizeof(char)); array_copy(newmtl.mtl_name, p.token.lexeme); _mesh_init_material(&newmtl.mtl); array_append(mtl_list, &newmtl); break; } case MTL_NSTAG: { // Parse the shininess exponent if(mtl_parser_found(&p, MTL_FLOAT) || mtl_parser_found(&p, MTL_UINT)) { mtl_def->mtl.shininess = strtof(array_data(p.token.lexeme), NULL); } break; } case MTL_KATAG: { // Parse the ambient reflectivity for(uint64_t i = 0; i < 3; i++) { mtl_parser_expect(&p, MTL_FLOAT); mtl_def->mtl.ambient[i] = strtof(array_data(p.token.lexeme), NULL); } break; } case MTL_KDTAG: { // Parse the diffuse reflectivity for(uint64_t i = 0; i < 3; i++) { mtl_parser_expect(&p, MTL_FLOAT); mtl_def->mtl.diffuse[i] = strtof(array_data(p.token.lexeme), NULL); } break; } case MTL_KSTAG: { // Parse the specular reflectivity for(uint64_t i = 0; i < 3; i++) { mtl_parser_expect(&p, MTL_FLOAT); mtl_def->mtl.specular[i] = strtof(array_data(p.token.lexeme), NULL); } break; } case MTL_DTAG: { // Parse the dissolve/transparency value if(mtl_parser_found(&p, MTL_FLOAT) || mtl_parser_found(&p, MTL_UINT)) { mtl_def->mtl.transparency = strtof(array_data(p.token.lexeme), NULL); } break; } case MTL_MAPKDTAG: { // Parse the diffuse texture map mtl_parser_expect(&p, MTL_IDENTIFIER); array_t *texname = array_create(array_size(p.token.lexeme), sizeof(char)); array_copy(texname, p.token.lexeme); array_prepend_str(texname, "resources/"); // Load the texture from the file _mesh_load_texture(mesh, &mtl_def->mtl, array_data(texname)); array_delete(texname); break; } default: break; } } // Delete the parser struct mtl_parser_free(&p); return true; }
END_TEST START_TEST(axis_201_eachrow) { int dims[] = { 3, 4, 2 }; int32_t iq = array_scratch(LUX_INT32, 3, dims); iq = lux_indgen(1, &iq); ck_assert_msg(iq > 0, "Cannot create array"); int three = 3; int32_t axes = array_scratch(LUX_INT32, 1, &three); pointer axesp; axesp.v = array_data(axes); axesp.l[0] = 2; axesp.l[1] = 0; axesp.l[2] = 1; loopInfo srci; pointer srcp; ck_assert_int_eq(standardLoop(iq, axes, SL_EACHROW, LUX_INT32, &srci, &srcp, NULL, NULL, NULL), LUX_OK); ck_assert_int_eq(srci.ndim, sizeof(dims)/sizeof(*dims)); ck_assert_int_eq(srci.nelem, dims[0]*dims[1]*dims[2]); int i; for (i = 0; i < srci.ndim; i++) ck_assert_int_eq(srci.coords[i], 0); /* specified dimensions */ ck_assert_int_eq(srci.dims[0], dims[0]); ck_assert_int_eq(srci.dims[1], dims[1]); ck_assert_int_eq(srci.dims[2], dims[2]); ck_assert_int_eq(srci.singlestep[0], 1); /* one step along dimension 0 */ ck_assert_int_eq(srci.singlestep[1], dims[0]); /* along dimension 1 */ ck_assert_int_eq(srci.singlestep[2], dims[0]*dims[1]); /* along dim 2 */ /* specified axes */ ck_assert_int_eq(srci.naxes, 3); ck_assert_int_eq(srci.axes[0], 2); ck_assert_int_eq(srci.axes[1], 0); ck_assert_int_eq(srci.axes[2], 1); /* compressed/rearranged dimensions; we loop along dimension 2 first, and then along dimensions 0 and 1. */ ck_assert_int_eq(srci.rndim, 3); ck_assert_int_eq(srci.rdims[0], dims[2]); /* 2 */ ck_assert_int_eq(srci.rdims[1], dims[0]); /* 3 */ ck_assert_int_eq(srci.rdims[2], dims[1]); /* 4 */ ck_assert_int_eq(srci.rsinglestep[0], dims[0]*dims[1]); /* 12 */ ck_assert_int_eq(srci.rsinglestep[1], 1); ck_assert_int_eq(srci.rsinglestep[2], dims[0]); /* 3 */ /* how many elements to advance the pointer to go to the next item in the desired order? */ ck_assert_int_eq(srci.step[0], srci.rsinglestep[0]); /* 12 */ /* how many elements to advance the pointer additionally to go to the next item in the desired order when reaching the end of the first selected axis? Then we've advanced step[0] elements rdims[0] = dims[1] times, but want to end up having advanced rsinglestep[1] = 1 element. */ ck_assert_int_eq(srci.step[1], srci.rsinglestep[1] - srci.rdims[0]*srci.step[0]); /* 1 - 2*12 = -23 */ /* how many elements to advance the pointer additionally to go to the next item in the desired order when reaching the end of the second selected axis? Then we've advanced step[0] elements rdims[0]*rdims[1] times, and step[1] elements rdims[1] times, but want to end up having advanced rsinglestep[2] elements. */ ck_assert_int_eq(srci.step[2], srci.rsinglestep[2] - srci.step[0]*srci.rdims[0]*srci.rdims[1] - srci.step[1]*srci.rdims[1]); ck_assert_int_eq(srci.raxes[0], 2); ck_assert_int_eq(srci.raxes[1], 0); ck_assert_int_eq(srci.raxes[2], 1); ck_assert_int_eq(srci.iraxes[0], 1); ck_assert_int_eq(srci.iraxes[1], 2); ck_assert_int_eq(srci.iraxes[2], 0); ck_assert_int_eq(srci.axisindex, 0); ck_assert_int_eq(srci.advanceaxis, 1); int count = 0; /* advanceLoop returns 0 while inside the first specified axis, 1 when at the end of the first specified axis, 2 when at the end of the second specified axis, 3 when at the end of the third specified axis. */ /* axes 2,0,1 → dimension sizes 2,3,4 */ int expect_a[] = {1,1,2,1,1,2,1,1,2,1, 1, 3}; int expect_i[] = {0,1,2,3,4,5,6,7,8,9,10,11}; int ok; int size = dims[0]*dims[1]; do { fail_unless(count < size, "Out of bounds"); /* stay within bounds */ fail_unless(*srcp.l == expect_i[count], "Element bad; expect %d, got %d, count = %d", expect_i[count], *srcp.l, count); int c0 = 0; /* always 0 because of SL_EACHROW */ int c1 = count % srci.rdims[1]; int c2 = count/srci.rdims[1]; fail_unless(srci.coords[0] == c0, "Coordinate 0 bad; expect %d, got %d, count = %d", c0, srci.coords[0], count); fail_unless(srci.coords[1] == c1, "Coordinate 1 bad; expect %d, got %d, count = %d", c1, srci.coords[1], count); fail_unless(srci.coords[2] == c2, "Coordinate 2 bad; expect %d, got %d, count = %d", c2, srci.coords[2], count); /* we have to take care of advancing along the first axis ourselves, because of SL_EACHROW */ srcp.l += srci.rsinglestep[0]*srci.rdims[0]; ok = advanceLoop(&srci, &srcp); fail_unless(ok == expect_a[count], "advanceLoop return bad; expect %d, got %d, count = %d", expect_a[count], ok, count); ++count; } while (ok < srci.rndim); ck_assert_int_eq(count, size); }
END_TEST START_TEST(axis_102) { int dims[] = { 3, 4, 2 }; int32_t iq = array_scratch(LUX_INT32, 3, dims); iq = lux_indgen(1, &iq); ck_assert_msg(iq > 0, "Cannot create array"); int one = 1; int32_t axes = array_scratch(LUX_INT32, 1, &one); /* axis index 1 */ pointer axesp; axesp.v = array_data(axes); *axesp.l = 1; loopInfo srci; pointer srcp; ck_assert_int_eq(standardLoop(iq, axes, 0, LUX_INT32, &srci, &srcp, NULL, NULL, NULL), LUX_OK); ck_assert_int_eq(srci.ndim, sizeof(dims)/sizeof(*dims)); ck_assert_int_eq(srci.nelem, dims[0]*dims[1]*dims[2]); int i; for (i = 0; i < srci.ndim; i++) ck_assert_int_eq(srci.coords[i], 0); /* specified dimensions */ ck_assert_int_eq(srci.dims[0], dims[0]); ck_assert_int_eq(srci.dims[1], dims[1]); ck_assert_int_eq(srci.dims[2], dims[2]); ck_assert_int_eq(srci.singlestep[0], 1); /* one step along dimension 0 */ ck_assert_int_eq(srci.singlestep[1], dims[0]); /* along dimension 1 */ ck_assert_int_eq(srci.singlestep[2], dims[0]*dims[1]); /* along dim 2 */ /* specified axes */ ck_assert_int_eq(srci.naxes, 1); ck_assert_int_eq(srci.axes[0], 1); /* loop along axis 1 */ /* compressed/rearranged dimensions; we loop along dimension 1 first, and then along dimensions 0 and 2. */ ck_assert_int_eq(srci.rndim, 3); ck_assert_int_eq(srci.rdims[0], dims[1]); /* 4 */ ck_assert_int_eq(srci.rdims[1], dims[0]); /* 3 */ ck_assert_int_eq(srci.rdims[2], dims[2]); /* 2 */ ck_assert_int_eq(srci.rsinglestep[0], dims[0]); /* 3 */ ck_assert_int_eq(srci.rsinglestep[1], 1); ck_assert_int_eq(srci.rsinglestep[2], dims[0]*dims[1]); /* 12 */ /* how many elements to advance the pointer to go to the next item in the desired order? */ ck_assert_int_eq(srci.step[0], srci.rsinglestep[0]); /* 3 */ /* how many elements to advance the pointer additionally to go to the next item in the desired order when reaching the end of the first selected axis? Then we've advanced step[0] elements rdims[0] = dims[1] times, but want to end up having advanced rsinglestep[1] = 1 element. */ ck_assert_int_eq(srci.step[1], srci.rsinglestep[1] - srci.rdims[0]*srci.step[0]); /* 1 - 4*3 = -11 */ /* how many elements to advance the pointer additionally to go to the next item in the desired order when reaching the end of the second selected axis? Then we've advanced step[0] elements rdims[0]*rdims[1] times, and step[1] elements rdims[1] times, but want to end up having advanced rsinglestep[2] elements. */ ck_assert_int_eq(srci.step[2], srci.rsinglestep[2] - srci.step[0]*srci.rdims[0]*srci.rdims[1] - srci.step[1]*srci.rdims[1]); ck_assert_int_eq(srci.raxes[0], 1); ck_assert_int_eq(srci.raxes[1], 0); ck_assert_int_eq(srci.raxes[2], 2); ck_assert_int_eq(srci.iraxes[0], 1); ck_assert_int_eq(srci.iraxes[1], 0); ck_assert_int_eq(srci.iraxes[2], 2); ck_assert_int_eq(srci.axisindex, 0); ck_assert_int_eq(srci.advanceaxis, 0); int count = 0; /* advanceLoop returns 0 while inside the first specified axis, 1 when at the end of the first specified axis, 2 when at the end of the second specified axis, 3 when at the end of the third specified axis. */ /* axes 1,0,2 → dimension sizes 4,3,2 */ int expect_a[] = {0,0,0,1,0,0,0,1,0,0,0,2, 0,0,0,1,0,0,0,1,0,0,0,3}; int expect_i[] = { 0, 3, 6, 9, 1, 4, 7,10, 2, 5, 8,11, 12,15,18,21,13,16,19,22,14,17,20,23}; int ok; int size = array_size(iq); do { fail_unless(count < size, "Out of bounds"); /* stay within bounds */ fail_unless(*srcp.l == expect_i[count], "Element bad; expect %d, got %d, count = %d", expect_i[count], *srcp.l, count); int c0 = count % srci.rdims[0]; int c1 = (count/srci.rdims[0]) % srci.rdims[1]; int c2 = count/(srci.rdims[0]*srci.rdims[1]); fail_unless(srci.coords[0] == c0, "Coordinate 0 bad; expect %d, got %d, count = %d", c0, srci.coords[0], count); fail_unless(srci.coords[1] == c1, "Coordinate 1 bad; expect %d, got %d, count = %d", c1, srci.coords[1], count); fail_unless(srci.coords[2] == c2, "Coordinate 2 bad; expect %d, got %d, count = %d", c2, srci.coords[2], count); ok = advanceLoop(&srci, &srcp); fail_unless(ok == expect_a[count], "advanceLoop return bad; expect %d, got %d, count = %d", expect_a[count], ok, count); ++count; } while (ok < srci.rndim); ck_assert_int_eq(count, size); /* we did all elements */ }
uint64_t mtl_lexer_get_token(mtl_parser_t *p) { mtl_token_type_t prev_type = p->token.type; p->token.type = MTL_UNKNOWN; // Skip comment lines while(p->fstring[p->c_index] == '#') { p->c_index += strcspn(&p->fstring[p->c_index], "\n"); while(isspace(p->fstring[p->c_index])) p->c_index++; } // Skip all white spaces while(isspace(p->fstring[p->c_index])) p->c_index++; // Check if end of file has been reached if(p->c_index >= p->fsize) { p->token.type = MTL_ENDOFFILE; return 1; } array_clear(p->token.lexeme); // Read characters until the next whitespace or separator while(!isspace(p->fstring[p->c_index])) { char c = p->fstring[p->c_index++]; array_append(p->token.lexeme, &c); } size_t tok_len = array_size(p->token.lexeme); char c = 0; array_append(p->token.lexeme, &c); // Get the actual data from the array char *lexeme = (char*)array_data(p->token.lexeme); // Check if this token is an identifier depending on the the previous token type if(prev_type >= MTL_MAPKATAG && prev_type <= MTL_NEWMTLTAG) { p->token.type = MTL_IDENTIFIER; return 0; } // Check if the token is a tag if(isalpha(lexeme[0])) { // Which kind of tag if(strcmp(lexeme, "Ns") == 0) p->token.type = MTL_NSTAG; else if(strcmp(lexeme, "Ka") == 0) p->token.type = MTL_KATAG; else if(strcmp(lexeme, "Kd") == 0) p->token.type = MTL_KDTAG; else if(strcmp(lexeme, "Ks") == 0) p->token.type = MTL_KSTAG; else if(strcmp(lexeme, "d") == 0) p->token.type = MTL_DTAG; else if(strcmp(lexeme, "map_Ka") == 0) p->token.type = MTL_MAPKATAG; else if(strcmp(lexeme, "map_Kd") == 0) p->token.type = MTL_MAPKDTAG; else if(strcmp(lexeme, "map_Ks") == 0) p->token.type = MTL_MAPKSTAG; else if(strcmp(lexeme, "map_Bump") == 0) p->token.type = MTL_MAPBUMPTAG; else if(strcmp(lexeme, "map_d") == 0) p->token.type = MTL_MAPDTAG; else if(strcmp(lexeme, "newmtl") == 0) p->token.type = MTL_NEWMTLTAG; else p->token.type = MTL_ERROR; return (p->token.type == MTL_ERROR) ? 1 : 0; } // Check if it is a floating point number: -?[0-9]+\.[0-9]+ if(strchr(lexeme, '.') != NULL && (lexeme[0] == '-' || isdigit(lexeme[0]))) { p->token.type = MTL_ERROR; // Confirm that this is a correctly formatted float size_t index = 0; if(lexeme[index] == '-') index++; while(isdigit(lexeme[index++])); // Must be a period if(lexeme[(index-1)] != '.') return 1; // Continue confirming digits in the decimal portion while(isdigit(lexeme[index++])); // If index is the number as tok_len then we have successfully confirmed a floating point number if(--index != tok_len) return 1; p->token.type = MTL_FLOAT; return 0; } // Check if it is a uint: [0-9]+ if(isdigit(lexeme[0])) { p->token.type = MTL_ERROR; // Confirm that this is a correctly formatted uint size_t index = 0; while(isdigit(lexeme[index++])); // If index is the same number as tok_len then we have successfully confirmed a uint if(--index != tok_len) return 1; p->token.type = MTL_UINT; return 0; } return 0; }