|
20 | 20 | #include <cstring> |
21 | 21 | #include <memory> |
22 | 22 | #include <string> |
| 23 | +#include <vector> |
23 | 24 |
|
24 | 25 | #include <gmock/gmock.h> |
25 | 26 | #include <gtest/gtest.h> |
@@ -1588,6 +1589,77 @@ TEST_F(MjCTextureTest, CubeFromFileMustHaveThreeChannels) { |
1588 | 1589 | mj_deleteVFS(vfs.get()); |
1589 | 1590 | } |
1590 | 1591 |
|
| 1592 | +// the spec does not keep the pixels of its textures between compilations: a |
| 1593 | +// file, a builtin and a buffer texture compile to the same tex_data every time, |
| 1594 | +// with and without the asset cache |
| 1595 | +TEST_F(MjCTextureTest, PixelsSameAfterRecompile) { |
| 1596 | + // 1 x 1 RGB PNG file |
| 1597 | + static constexpr unsigned char pixel[] = { |
| 1598 | + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, |
| 1599 | + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, |
| 1600 | + 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, |
| 1601 | + 0x0c, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xf8, 0xdf, 0xc0, 0x00, |
| 1602 | + 0x00, 0x04, 0x01, 0x01, 0x80, 0xfb, 0xd7, 0xcb, 0xf1, 0x00, 0x00, 0x00, |
| 1603 | + 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82}; |
| 1604 | + static constexpr std::byte buffer[] = { |
| 1605 | + std::byte{1}, std::byte{2}, std::byte{3}, std::byte{11}, |
| 1606 | + std::byte{12}, std::byte{13}, std::byte{21}, std::byte{22}, |
| 1607 | + std::byte{23}, std::byte{31}, std::byte{32}, std::byte{33}}; |
| 1608 | + |
| 1609 | + auto vfs = std::make_unique<mjVFS>(); |
| 1610 | + mj_defaultVFS(vfs.get()); |
| 1611 | + mj_addBufferVFS(vfs.get(), "pixel.png", pixel, sizeof(pixel)); |
| 1612 | + |
| 1613 | + mjSpec* spec = mj_makeSpec(); |
| 1614 | + mjsTexture* file = mjs_addTexture(spec); |
| 1615 | + mjs_setName(file->element, "file"); |
| 1616 | + file->type = mjTEXTURE_2D; |
| 1617 | + mjs_setString(file->file, "pixel.png"); |
| 1618 | + mjsTexture* builtin = mjs_addTexture(spec); |
| 1619 | + mjs_setName(builtin->element, "builtin"); |
| 1620 | + builtin->type = mjTEXTURE_2D; |
| 1621 | + builtin->builtin = mjBUILTIN_CHECKER; |
| 1622 | + builtin->width = 4; |
| 1623 | + builtin->height = 4; |
| 1624 | + mjsTexture* buffered = mjs_addTexture(spec); |
| 1625 | + mjs_setName(buffered->element, "buffer"); |
| 1626 | + buffered->type = mjTEXTURE_2D; |
| 1627 | + buffered->width = 2; |
| 1628 | + buffered->height = 2; |
| 1629 | + buffered->nchannel = 3; |
| 1630 | + buffered->hflip = 1; |
| 1631 | + mjs_setBuffer(buffered->data, buffer, sizeof(buffer)); |
| 1632 | + |
| 1633 | + mjModel* m = mj_compile(spec, vfs.get()); |
| 1634 | + ASSERT_THAT(m, NotNull()) << mjs_getError(spec); |
| 1635 | + ASSERT_EQ(m->ntex, 3); |
| 1636 | + ASSERT_EQ(m->ntexdata, 3 + 48 + 12); |
| 1637 | + std::vector<mjtByte> expected(m->tex_data, m->tex_data + m->ntexdata); |
| 1638 | + EXPECT_EQ(expected[0], 255); // the pixel of the file is red |
| 1639 | + EXPECT_EQ(expected[m->tex_adr[2]], 11); // the buffer is flipped |
| 1640 | + |
| 1641 | + // recompile with a warm cache, then with a cold one, then compile again |
| 1642 | + for (int i = 0; i < 3; i++) { |
| 1643 | + if (i == 1) mj_clearCache(mj_getCache()); |
| 1644 | + if (i < 2) { |
| 1645 | + ASSERT_EQ(mj_recompile(spec, vfs.get(), m, nullptr), 0) |
| 1646 | + << mjs_getError(spec); |
| 1647 | + } else { |
| 1648 | + mj_deleteModel(m); |
| 1649 | + m = mj_compile(spec, vfs.get()); |
| 1650 | + ASSERT_THAT(m, NotNull()) << mjs_getError(spec); |
| 1651 | + } |
| 1652 | + ASSERT_EQ(m->ntexdata, expected.size()) << i; |
| 1653 | + EXPECT_EQ(std::vector<mjtByte>(m->tex_data, m->tex_data + m->ntexdata), |
| 1654 | + expected) |
| 1655 | + << i; |
| 1656 | + } |
| 1657 | + |
| 1658 | + mj_deleteModel(m); |
| 1659 | + mj_deleteSpec(spec); |
| 1660 | + mj_deleteVFS(vfs.get()); |
| 1661 | +} |
| 1662 | + |
1591 | 1663 | // ------------- test quaternion normalization---------------------------------- |
1592 | 1664 |
|
1593 | 1665 | using QuatNorm = MujocoTest; |
|
0 commit comments