Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draco_encoder problem #67

Closed
Frankling opened this issue Feb 24, 2017 · 24 comments
Closed

draco_encoder problem #67

Frankling opened this issue Feb 24, 2017 · 24 comments

Comments

@Frankling
Copy link

Hi,is this encoder compress all mesh in one (e.g. one obj has same meshs), then decoder it just one mesh?

@ondys
Copy link
Collaborator

ondys commented Feb 25, 2017

Are you referring to sub-objects in the .obj file? If so, then yes, the current version of draco_encoder does convert them into a single mesh. Sub object ids are still stored in a GENERIC attribute though, but to separate the mesh into the sub-objects you would have to write your own logic on the client app. If that's what you want to do I can give you some pointers. We will add support for preserving sub-object for .OBJ files in future (when we add support for metadata).

@Frankling
Copy link
Author

@ondys ,yes,I want to refer to sub-objects in the .obj file.

@Frankling
Copy link
Author

@ondys my Email address is 815237345@qq.com

@ondys
Copy link
Collaborator

ondys commented Feb 28, 2017

I'll just write it here so other people can find it too. Each sub-object of the input OBJ is assigned a id that is equivalent to it's location in the .OBJ file (so the first sub-object has id == 0, second 1, third 2, etc...). No sub-object names are currently preserved. The sub-object ids are stored for every face of the mesh and to get them on the client, you need to get a generic attribute with custom_id() === 1 :

let subObjAttId = -1;
for (let i = 0; i < dracoGeometry.num_attributes(); i++) {
    const attribute = wrapper.GetAttribute(dracoGeometry, i)
    if (attribute.attribute_type() === Module.GENERIC && attribute.custom_id() === 1) {
      subObjAttId = i;
      break;
    }
}
let subObjAttribute = wrapper.GetAttribute(dracoGeometry, subObjAttId);

You can then get the sub-object id values as:

subObjAttributeData = new DracoModule.DracoFloat32Array();
          wrapper.GetAttributeFloatForAllPoints(dracoGeometry, subObjAttribute,   subObjAttributeData);

This array will store a sub-object id for every point of the mesh, but all points that belong to the same face are always going to be mapped to the same sub-object id value. You can then use these ids to split the mesh into different geometries.

@kappa-gooner
Copy link

@ondys
Rather surprising that I'm having the exact same requirement right now.
I notice however that the num_attributes() is always 2; and there are no attributes with type GENERIC.
Does it depend on the compression level cl or quantization parameters qp while encoding the file?

@ondys
Copy link
Collaborator

ondys commented Mar 1, 2017

@kappa-gooner The generic attribute is not going to be created if there is only one sub-object in the obj file. If that's not your case, can you please post a link to an .OBJ that shows the problem?

@kappa-gooner
Copy link

kappa-gooner commented Mar 1, 2017

@ondys Please scratch that request.
I was referring to sub-meshes and not sub-objects.
Is there any way we can retrieve individual sub-mesh info from the encoded file?

@ondys
Copy link
Collaborator

ondys commented Mar 1, 2017

@kappa-gooner Depends what sub-mesh info you are looking for. We store material ids in another generic attribute (with custom id 0). The file that you originally linked was also using g symbol for polygon groups and s symbol for smoothing groups. Both of these are currently ignored. We may add support for them later on if there is demand.

@kappa-gooner
Copy link

kappa-gooner commented Mar 2, 2017

@ondys
Thank you for looking into this.
Looks like most obj files I work with use g and s for polygon/smoothing groups.
Is there any way I can add support for these myself?
Also, even if I manage to retrieve the sub-mesh ids; would it be possible to get hold of the geometric information (say bounding box) of these groups?

@kappa-gooner
Copy link

@ondys
Ok, here's what I ended up doing. I followed your cue and replaced all the g in my obj file with o so that the polygon groups are represented as sub-objects.

I was able to pull out the subObjAttributeData as directed by you and ended up using that to first, map it to points and then to faces. My result is rather weird.
screen shot 2017-03-02 at 4 28 01 pm

  1. Any clues as to why this could be happening? Would it be possible for you to throw more light onto how this is achieved?

This array will store a sub-object id for every point of the mesh, but all points that belong to the same face are always going to be mapped to the same sub-object id value. You can then use these ids to split the mesh into different geometries.

  1. I rather found it painful to map the subObjAttributeData to the points in the individual sub objects and then to the faces.

@ondys
Copy link
Collaborator

ondys commented Mar 2, 2017

@kappa-gooner

  1. Does the model look correct when you don't use the sub-object ids?
  2. If so, can you post an short snipped of your code where you use the sub-object ids to split the mesh into multiple geometries?

@kappa-gooner
Copy link

@ondys :

  1. Sorry, there are problems with this particular model when I don't use sub-object ids. That's got to do with the faces being polygons and not simple triangles as you pointed out in the other issue.
  2. But, regardless here's a simplified snippet of my code that splits sub-objects into meshes...
// First get subObjAttributeData by using your code above

// Build an object that holds every subobjectID as the key and a member count that indicates the number of points in each sub-object (This is achieved by simple looping and counting)

// Build subobjectArray that contains the points (Ignoring normals and colors for simplicity
const subObjectArray = [];
let pointCnt = 0;
for (let key in subObjInfo) {
	const numPts = subObjInfo[key].count;
	const numvert = numPts * 3;

	const geom = {
		vertices: new Float32Array(numvert),
		indices: [],
		minPtInd: pointCnt // The minPtIndex and MaxPtIndex are required to associtate the mesh faces to the sub-object points later-on 
	}

	for (let j=0, i=(pointCnt * 3); i< ( pointCnt*3 ) + numvert; j+= 3,i+= 3) {
		geom.vertices[j] = posAttributeData.GetValue(i);
		geom.vertices[j + 1] = posAttributeData.GetValue(i + 1);
		geom.vertices[j + 2] = posAttributeData.GetValue(i + 2);
	}
	pointCnt += numPts;
	geom.maxPtInd = pointCnt;
	subObjectArray.push(geom);
}

// Here's where I'm trying to associate the faces to the sub-objects
const ia = new dracoDecoder.DracoInt32Array();
for (let i = 0; i < numFaces; ++i) {
         wrapper.GetFaceFromMesh(dracoGeometry, i, ia);
            
	// COPYING INDICES
	for (let j=0; j<subObjectArray.length; j++) {
		const subObj = subObjectArray[j];
		if (ia.GetValue(0) >= subObj.minPtInd &&
			ia.GetValue(0) < subObj.maxPtInd) {
			subObj.indices.push(ia.GetValue(0) - subObj.minPtInd)
			subObj.indices.push(ia.GetValue(1) - subObj.minPtInd)
			subObj.indices.push(ia.GetValue(2) - subObj.minPtInd)
			break;
		}
	}
}

Any pointers that could help me out? :-/

@kappa-gooner
Copy link

@ondys : I figured out the problem.
The subObjAttributeIds are sequenced in an descending order...
For instance,

points = [0,1,2,3,4,5,6,7,8,9]
subObjId = [2,2,2,1,1,1,0,0,0]

So, point 0 ends up being subObjectId 250 which is counter-intuitive at first; but rather makes sense now.
It would be a good idea to have this information documented somewhere. Let me know if you need help with any of this...

And a big thank you for helping me over the last 2 days.

@ondys
Copy link
Collaborator

ondys commented Mar 4, 2017

I'm glad it worked for you but I would just like to warn you that there may be some potential issues with your solution. First, your proposed loader assumes that all positions and all indices are always stored consecutively in the loaded buffers .. e.g. this code:

for (let j=0, i=(pointCnt * 3); i< ( pointCnt*3 ) + numvert; j+= 3,i+= 3) {
		geom.vertices[j] = posAttributeData.GetValue(i);
		geom.vertices[j + 1] = posAttributeData.GetValue(i + 1);
		geom.vertices[j + 2] = posAttributeData.GetValue(i + 2);
	}

That is true only if the sub-objects are separate mesh components. If the sub-objects were defined on a single mesh component (faces connected together but belonging to different sub-objects), then this is not going to hold anymore.

The same is true for the subObjAttributeIds being stored in a descending order. It's going to be like that for sub-objects that are separate mesh components, but otherwise the subObjAttributeIds can be stored in an arbitrary order.

To solve this, you would have to do something like this:

  1. Count the number of points for each sub-object id the way you already do it
  2. Create a map between global point ids and local point ids in each sub-object (this can be done during the counting step, a simple Int32Array would be probably the best)
  3. Allocate geometries for each sub-object using the counted number of vertices in each sub-object
  4. Go over the points again and copy vertex positions to the sub-object geometries using the map between global and local point ids
  5. Go over all faces and copy indices from the global index buffer to sub-object index buffers using the map between global and local point ids

Overall, it shouldn't be more code than what you already have, but you will need the extra map between the global and local point indices

@kappa-gooner
Copy link

@ondys:
This works perfectly, but at a heavy performance cost.
Especially step 5; to do a lookup for every point index on every face against each sub-object's global-to-local point map is quite expensive (I'm dealing with large large models that have 1 million faces and a few thousand sub objects).

Do consider exposing an interface that provides the sub-object data in a more straight forward fashion that does not require such data manipulation at load time. 🥇

@ondys
Copy link
Collaborator

ondys commented Mar 9, 2017

@kappa-gooner Can you share your code? I would think that it should be relatively fast. Note that you need only one global-to-local point map that is common for all sub-objects (from your post it sounds like you may be using multiple maps).

What I'm thinking that we could do in future is to add more direct support for sub-meshes into Draco.. for example we could split the input mesh into multiple sub-meshes based on some per-face attribute (sub-object id, material id, or whatever) and then encode these sub-meshes separately. It may hurt compression somehow, but it would definitely make it much easier to handle these cases on client apps.

@Frankling
Copy link
Author

Frankling commented Mar 27, 2017

@ondys I found that most obj files I with use g and s for polygon/smoothing groups. Meanwhile,can DracoLoader return bufferGeometry which will contain all groups that the obj file owns not just one mesh?

@ondys
Copy link
Collaborator

ondys commented Mar 28, 2017

@Frankling I'm not sure if I understand the question. Draco currently does not parse polygon/smoothing groups so they are all contained within the same bufferGeometry. Do you want to have a separate geometry returned for each group?

@Frankling
Copy link
Author

@ondys
For example ,if i upload a obj file with objloader, I can get a groups that contains three mesh,as show below
ThreeSphereObjFile
|---children
|--Mesh[0]
|--Mesh[1]
|--Mesh[2]
..
so can dracoLoader return bufferGeometry that as a group contains children like objloader does;

@shubhamagarwal003
Copy link

@ondys there seems to be no attribute.custom_id() function in latest version 1.0.0. There is a unique_id() function but the condition (attribute.attribute_type() === dracoDecoder.GENERIC && attribute.unique_id() === 1) is not true for any attribute. Is there some changes in version 1.0.0?

@ondys
Copy link
Collaborator

ondys commented Aug 22, 2017

@shubhamagarwal003 That's correct. With the newly introduced support for metadata, we have decided to remove custom_id as it was redundant. If you parse .obj file with multiple generic attributes (sub objects + material ids) then I you can turn on metadata by passing --metadata flag to our draco_encoder app. The generic attributes can then be identified by metadata entries <"name", "material"> for the material attribute and <"name", "sub_obj"> for the sub-object attribute.

In javascript you can get the attribute id for a given metadata entry using this method:

const matt_att_id = decoder.GetAttributeIdByMetadataEntry(geometry, "name", "material");

Note that if you do the encoding directly using C++ api, you can still set the unique_id to whatever value you want and use that to identify the attributes.

@FrankGalligan
Copy link
Collaborator

I'm closing this issue. If you are still having problems please re-open.

@YouYue123
Copy link

Any update on making a direct support for JS encoder or DracoLoader to get sub-object information. Actually if you can add polygon groups (g) or smoothing groups(s) support in a more straight API it would be very helpful

@lednhatkhanh
Copy link

Any updates on adding support to preserve sub-objects?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants